Scanning singletons from a custom Application subclass

113 views
Skip to first unread message

Chris Prokopiak

unread,
Feb 27, 2015, 1:44:26 PM2/27/15
to swagger-sw...@googlegroups.com
Hi,

I am trying to use and configure Swagger in a REST component for a Felix OSGI project. The project uses JAX-RS and CXF. I am using Swagger 1.3.12 for Scala 2.10. Below is the startup method for my REST component.

@Override
    public void startup() throws Exception
    {
        patientEndpoint = new PatientEndpointImpl(patientDataProvider, encounterDataProvider);
        encounterEndpoint = new EncounterEndpointImpl(encounterDataProvider);
        
        ObjectMapper mapper = new ObjectMapper();
        mapper.getSerializationConfig().setDateFormat(new SimpleDateFormat("MM/dd/yyyy"));
        JAXRSApplication app = new JAXRSApplication();
        
        app.addClass(ApiListingResource.class);
        app.addClass(ApiDeclarationProvider.class);
        app.addClass(ApiListingResourceJSON.class);
        app.addClass(ResourceListingProvider.class);
        //app.addClass(PatientEndpoint.class);
        //app.addClass(EncounterEndpoint.class);
        app.addSingletons(patientEndpoint, encounterEndpoint);        
        app.addSingleton(new JacksonJsonProvider(mapper));
        app.addSingleton(new AccessControlResponseFilter(true));        
        service.registerApplication(ALIAS, app);        
        
        BeanConfig beanConfig = new BeanConfig();
        beanConfig.setVersion("0.0.1");
        beanConfig.setBasePath(ALIAS);
        beanConfig.setResourcePackage("com.lexmark.healthcare.lhcdb.endpoint.rs");
        beanConfig.setDescription("Healthcare Database resources");
        beanConfig.setTitle("Healthcare Database REST API");
        beanConfig.setScan(true);
        
        ScannerFactory.setScanner(new DefaultJaxrsScanner());
        //ScannerFactory.setScanner(new ReflectiveJaxrsScanner());
        ClassReaders.setReader(new DefaultJaxrsApiReader());
    }

The JAXRSApplication is a custom subclass of Application. I add the Swagger resources as classes to the JAXRSApplication, while I add my own resources as singletons. Using this setup, when I go to my api-docs endpoint, I don't get any information for either of my endpoints.

{"apiVersion":"0.0.1","swaggerVersion":"1.2","info":{"title":"Healthcare Database REST API","description":"Healthcare Database resources"}}

However, if I change the configuration so that I add my own resources as classes, I get a list of apis back from the api-docs endpoint.

{"apiVersion":"0.0.1","swaggerVersion":"1.2","apis":[{"path":"/patients","description":"Operations about patients"},{"path":"/encounters","description":"Operations about encounters"}],"info":{"title":"Healthcare Database REST API","description":"Healthcare Database resources"}}

The resource implementations extend interfaces that contain the Swagger Annotations. For example, the Patient resource:

@Path("/patients")
@Api(value="/patients", description="Operations about patients")
@Produces(MediaType.APPLICATION_JSON)

public interface PatientEndpoint {

@GET
@Path("/{facilityId}/{mrn}")
@ApiOperation(value="Find patient by Facility ID and MRN",
notes="Requires both the Facility ID and MRN to return a patient.")
@ApiResponses(value={@ApiResponse(code=400, message="Invalid ID combination supplied"), @ApiResponse(code=404, message="Patient not found")})
//TODO: Fix later for facilityId
public Patient getPatient(@ApiParam(value="MRN of patient that needs to be fetched", required=true) @PathParam("mrn") String mrn, @ApiParam(value="Facility ID of patient that needs to be fetched", required=true) @PathParam("facilityId") String facilityId);

...

}


and

public class PatientEndpointImpl implements PatientEndpoint
{    
    private static SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("MM/dd/yyyy");
private PatientDataProvider patientDataProvider;
private EncounterDataProvider encounterDataProvider;
public PatientEndpointImpl (PatientDataProvider patientDataProvider, 
EncounterDataProvider encounterDataProvider)
{
this.patientDataProvider = patientDataProvider;
this.encounterDataProvider = encounterDataProvider;
}
    Logger logger = LoggerFactory.getLogger(PatientEndpointImpl.class);

    @Override
    public Patient[] getPatients(String firstName, String middleName, String lastName, String dob, int maxResults) 
{
        ...
    }

...

}

I saw that there used to be an issue in Swagger where the Annotations were not inherited from an interface, but it seemed like that was supposed to have been resolved in Swagger 1.3.10.

I'm not sure if this is a problem with my endpoint/implementation/annotations, incorrect configuration, or with scanning singletons in the Application subclass.

Another thread I read mentioned using the ReflectiveJaxrsScanner (before Annotation inheritance was fixed in 1.3.10). I've tried to use the ReflectiveJaxrsScanner, but get the following exception:

Caused by: java.lang.NullPointerException
	at org.eclipse.osgi.internal.loader.BundleLoader.findResources(BundleLoader.java:661)
	at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.findResources(DefaultClassLoader.java:162)
	at java.lang.ClassLoader.getResources(Unknown Source)
	at org.reflections.util.ClasspathHelper.forResource(ClasspathHelper.java:108)
	at org.reflections.util.ClasspathHelper.forPackage(ClasspathHelper.java:86)
	at com.wordnik.swagger.jaxrs.config.ReflectiveJaxrsScanner.classesFromContext(ReflectiveJaxrsScanner.scala:35)
	at com.wordnik.swagger.jaxrs.listing.ApiListingCache$$anonfun$listing$1$$anonfun$apply$1$$anonfun$apply$2.apply(ApiListing.scala:42)
	at com.wordnik.swagger.jaxrs.listing.ApiListingCache$$anonfun$listing$1$$anonfun$apply$1$$anonfun$apply$2.apply(ApiListing.scala:40)
	at scala.Option.map(Option.scala:145)
	at com.wordnik.swagger.jaxrs.listing.ApiListingCache$$anonfun$listing$1$$anonfun$apply$1.apply(ApiListing.scala:40)
	at com.wordnik.swagger.jaxrs.listing.ApiListingCache$$anonfun$listing$1$$anonfun$apply$1.apply(ApiListing.scala:39)
	at scala.Option.map(Option.scala:145)
	at com.wordnik.swagger.jaxrs.listing.ApiListingCache$$anonfun$listing$1.apply(ApiListing.scala:39)
	at com.wordnik.swagger.jaxrs.listing.ApiListingCache$$anonfun$listing$1.apply(ApiListing.scala:37)
	at scala.Option.orElse(Option.scala:257)
	at com.wordnik.swagger.jaxrs.listing.ApiListingCache$.listing(ApiListing.scala:37)
	at com.wordnik.swagger.jaxrs.listing.ApiListingResource.resourceListing(ApiListing.scala:85)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:180)
	at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96)
	... 37 more

This is probably some issue with OSGi that I haven't figured out yet.

Any help would be appreciated. If you need more information, let me know.

Ron Ratovsky

unread,
Feb 27, 2015, 1:48:49 PM2/27/15
to swagger-sw...@googlegroups.com
Is com.lexmark.healthcare.lhcdb.endpoint.rs the package of your implementations, interfaces or both?

--
You received this message because you are subscribed to the Google Groups "Swagger" group.
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
-----------------------------------------
http://swagger.io
https://twitter.com/SwaggerApi
-----------------------------------------

Chris Prokopiak

unread,
Feb 27, 2015, 2:16:24 PM2/27/15
to swagger-sw...@googlegroups.com
My interfaces are in the package com.lexmark.healthcare.lhcdb.endpoint.rs.api, the implementations are in com.lexmark.healthcare.lhcdb.endpoint.rs.impl. My REST component (with the startup() function mentioned above) is located in com.lexmark.healthcare.lhcdb.endpoint.rs.

I had originally set the beanConfig.setResourcePackage to use the endpoint.rs package to see if that would help at all. I've tried setting it to both endpoint.rs.api, and endpoint.rs.impl also. None have worked while adding my resources as singletons.
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsub...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Ron Ratovsky

unread,
Feb 27, 2015, 2:54:57 PM2/27/15
to swagger-sw...@googlegroups.com
Are the Swagger code and JAX-RS endpoints loaded via separate OSGi modules?

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Chris Prokopiak

unread,
Feb 27, 2015, 3:06:02 PM2/27/15
to swagger-sw...@googlegroups.com
No. Currently, my endpoint bundle is embedding the Swagger jars and their dependencies as Referenced Libraries. So the code is all loaded at the same time.

Ron Ratovsky

unread,
Feb 27, 2015, 3:19:23 PM2/27/15
to swagger-sw...@googlegroups.com
Can you please clarify what you meant here:

However, if I change the configuration so that I add my own resources as classes, I get a list of apis back from the api-docs endpoint.

Chris Prokopiak

unread,
Feb 27, 2015, 3:32:18 PM2/27/15
to swagger-sw...@googlegroups.com
Sure.

In the startup function, it's this section of code:

      JAXRSApplication app = new JAXRSApplication();
       
       app.addClass(ApiListingResource.class);
       app.addClass(ApiDeclarationProvider.class);
       app.addClass(ApiListingResourceJSON.class);
       app.addClass(ResourceListingProvider.class);
       
//app.addClass(PatientEndpoint.class);
       //app.addClass(EncounterEndpoint.class);      
       app.addSingletons(patientEndpoint, encounterEndpoint);

You can see that right now, I am adding instances of patientEndpoint and encounterEndpoint as singletons to the JAXRSApplication, while the Swagger resources are added as classes. This currently does not work.

However, pay attention to the two lines that I have commented out. If I change to code so that I'm adding my resources using addClass instead of addSingletons, like below: 

      JAXRSApplication app = new JAXRSApplication();
     
      app.addClass(ApiListingResource.class);
      app.addClass(ApiDeclarationProvider.class);
      app.addClass(ApiListingResourceJSON.class);
      app.addClass(ResourceListingProvider.class);
      app
.addClass(PatientEndpoint.class);
      app.addClass(EncounterEndpoint.class);      
      //app.addSingletons(patientEndpoint, encounterEndpoint);

With this setup I am able to get Swagger to properly scan and return my API. However, the functionality of my endpoints is now broken. As it stands, my endpoint resources need to be added to the JAXRSApplication as singletons.

From what I can tell, the scanners should be checking singletons added to the Application subclass. However, I haven't been able to attach the swagger sources in my Eclipse project to verify via debugging.

Hopefully that helps to clarify.
To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggersocket+unsubscri...@googlegroups.com.

Ron Ratovsky

unread,
Feb 27, 2015, 3:43:47 PM2/27/15
to swagger-sw...@googlegroups.com
I could be wrong, but I think that functionality is currently missing from the swagger-core implementation.
Can you open an issue on the project with the relevant details as described here?

To unsubscribe from this group and stop receiving emails from it, send an email to swagger-swaggers...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Chris Prokopiak

unread,
Feb 27, 2015, 4:07:39 PM2/27/15
to swagger-sw...@googlegroups.com
Sure thing. Thanks.
Reply all
Reply to author
Forward
0 new messages