R7 Postges Example Doesn't Work Without JsonpConvertingPlugin

52 views
Skip to first unread message

robert.p...@leidos.com

unread,
Feb 26, 2018, 2:49:23 PM2/26/18
to bndtools-users
Hi,

I've been working with the R7 Postgres example.  I have a rest service and a JPA DAO like in the example.  We need to be able to return JAX-RS Response objects from our REST service method calls.  So, I took out JsonpConvertingPlugin and created my own JAX-RS deployment class that extends javax.ws.rs.core.Application and I annotated it with @ApplicationPath("/myservices").  This should work in a normal Java web container, but it doesn't work in OSGi.  When I start my OSGi container, none of my components activate (they have methods annotated with @Activate), and I get a 404 error.

How can I get this working in the standard way?

Thanks.

robert.p...@leidos.com

unread,
Feb 26, 2018, 2:57:13 PM2/26/18
to bndtools-users
Here is my JAX-RS deployment class:

package my.package;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;


@ApplicationPath("/myservices")
public class MyApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> empty = new HashSet<Class<?>>();

public MyApplication() {
//singletons.add(new MyResource());
}

@Override
public Set<Class<?>> getClasses() {
//return empty;
HashSet<Class<?>> set = new HashSet<Class<?>>();
set.add(MyResource.class);
return set;
}

@Override
public Set<Object> getSingletons() {
return singletons;
}

}

Here is my REST service class:
Message has been deleted

robert.p...@leidos.com

unread,
Feb 26, 2018, 3:14:50 PM2/26/18
to bndtools-users
package my.package;

import org.apache.johnzon.mapper.Mapper;
import org.apache.johnzon.mapper.MapperBuilder;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.ws.rs.GET;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;

import my.package.MyDao;
import my.package.MyDto;


@Component(service=MyResource.class)
@JaxrsResource
@Path("/myresourcepath")
//@Produces(MediaType.APPLICATION_JSON)
@JSONRequired
public class MyResource {

    @Reference
    private MyDao dao;

    @Activate
    void activate(Map<String, Object> props) {
        System.out.println("MyResource activated.");
    }

    @GET
    @Path("/items")
    @Produces(MediaType.APPLICATION_JSON)
    public List<MyDto> getItems() {
        return dao.list();
    }

    @GET
    @Path("/itemlist")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getItemList() throws Exception {    
        try {
            List<MyDto> result = dao.list();
            Mapper mapper = new MapperBuilder().build();
            String json = mapper.writeArrayAsString(result); 
            ResponseBuilder builder = Response.ok(json);
            return builder.build();
        }
        catch(Exception ex) {
            System.out.println(ex.getMessage());
            return Response.status(Status.INTERNAL_SERVER_ERROR).build();
        }
    }

}

On Monday, February 26, 2018 at 2:49:23 PM UTC-5, robert.p...@leidos.com wrote:

robert.p...@leidos.com

unread,
Feb 26, 2018, 3:33:21 PM2/26/18
to bndtools-users
I changed the @Component annotation on MyResource to @Component(scope=PROTOTYPE), and my components wire and activate.  But, I still get a 404 error.


On Monday, February 26, 2018 at 2:49:23 PM UTC-5, robert.p...@leidos.com wrote:

Tim Ward

unread,
Feb 27, 2018, 6:47:53 AM2/27/18
to bndtool...@googlegroups.com
Hi Robert,

From the code snippets that you’ve sent it looks like there are a few misunderstandings about how a whiteboard works, and about how selection filters and service properties ensure that things work at runtime.

Firstly - you’ve created a custom JAX-RS application. There’s not enough context for me to understand why you feel that this is needed,  but if you do want the application to be picked up by the whiteboard then it must be registered as an OSGi service and define the necessary service property to that the whiteboard should process it. The easiest way to do this is using Declarative Services:

@Component(service=Application.class)
@JaxrsApplicationBase("/myservices”)
public class MyApplication extends Application {
    …
}

At this point your application will be recognised however the MyApplication as you’ve written it won’t behave in the way that you want. You’ve added MyResource.class as a resource in the application, but haven’t taken account of the lifecycle of the resource at all. If the MyApplication is used then the JAX-RS container will instantiate a MyResource, but none of the referenced services will be injected, nor any lifecycle methods called. In general adding classes to the application is the wrong way to include resources. Instead what you should be doing is targeting the whiteboard resource at the whiteboard application. The easiest way to do this is via naming the application:


@Component(service=Application.class)
@JaxrsApplicationBase("/myservices”)
@JaxrsName(“myAppplication”)
public class MyApplication extends Application {
    …
}

@Component(service=MyResource.class)
@JaxrsResource
@JaxrsApplicationSelect(“(osgi.jaxrs.name=myApplication)”)
@Path("/myresourcepath”)
@JSONRequired
public class MyResource {
    …
}

This way the application will include the resource service when it gets registered, but the resource service instance(s) will be managed by DS. You can still use scope=PROTOTYPE if you like.

Secondly, you’ve removed the JsonpConvertingPlugin, which is fine, but your resource still says that it @Produces JSON and you’ve also still got the @JSONRequired annotation on your component. This will actively prevent your resource from being registered with the whiteboard unless a target application or extension provides the JSON media type. This is why the JsonpConvertingPlugin had the annotation @JaxrsMediaType(APPLICATION_JSON).

If you don’t want to use the JsonpConvertingPlugin then you will need to make sure that something else is providing JSON support. This could be a whiteboard extension (just like the JsonpConvertingPlugin) or it could be included as a static extension inside your custom application, either way you need to declare that the application or extension service provides this (for example by stating @JaxrsMediaType(APPLICATION_JSON)).

I hope that this helps you to make some more forward progress.

Regards,

Tim

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

robert.p...@leidos.com

unread,
Feb 27, 2018, 11:45:32 AM2/27/18
to bndtools-users
Hi Tim,

Thanks for the fast response.

I'm going to try out your suggestions to make it all work with OSGi annotations.  Will let you know how it goes.

I chose JAX-RS, because it is a Java EE standard.  We would like to use Java EE standards for both HTTP request handling and for persistence.  I am trying to figure out how to make our web service classes and CRUD service classes work with Java EE standards.  Is there any way to achieve this?

Thanks,
Bob


On Monday, February 26, 2018 at 2:49:23 PM UTC-5, robert.p...@leidos.com wrote:

Tim Ward

unread,
Feb 27, 2018, 12:06:31 PM2/27/18
to bndtool...@googlegroups.com
Hi Bob,

JAX-RS is obviously a standard defined as part of Java EE, but an OSGi framework is not a Java EE application server; it does not have the same classloading structure, and it has a much more dynamic lifecycle. You can absolutely get a “pure” Java EE solution running by packaging the application and server components into one giant module, but at that point you’re really not using OSGi any more.

The “glue” that makes JAX-RS work well in OSGi is the JAX-RS whiteboard specification. This lets you write JAX-RS resources, just like you would in Java EE, but lets you dynamically assemble them into an application. As a result multiple modules can collaborate to provide the application. This way of assembling applications takes advantage of the service-based approach of OSGi, but still supports all the things you would expect from JAX-RS. 

The end result of this is that you can (if you’re careful) write a JAX-RS whiteboard application that would run in Java EE, but in doing so you lose access to OSGi features. As you use more of OSGi’s features your application will become more modular and loosely coupled, but as a result you have to start assembling your application in a more OSGi-centric way.

I hope that this helps,

Tim


robert.p...@leidos.com

unread,
Feb 28, 2018, 8:08:15 AM2/28/18
to bndtools-users
I have it working now.  I can process a GET request, return a result from my database, return a JSON object to the browser.  Thanks!

I have noticed, though, that if I declare my JAX-RS resource class as PROTOTYPE, it gets created and activated twice (I wrote a constructor and an @Activate method.  Have I done something wrong?

Here is my application class:

package my.package.rest;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationBase;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsMediaType;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsName;


@Component(service=Application.class)
@JaxrsApplicationBase("/myservices")
@JaxrsName("myservices")
@JaxrsMediaType(MediaType.APPLICATION_JSON)
public class MyApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> empty = new HashSet<Class<?>>();

public MyApplication() {
System.out.println("MyApplication created.");
}

@Override
public Set<Class<?>> getClasses() {
return empty;
}

@Override
public Set<Object> getSingletons() {
return singletons;
}

}

Here is my resource class:

package my.package.rest;

import org.apache.johnzon.mapper.Mapper;
import org.apache.johnzon.mapper.MapperBuilder;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JSONRequired;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsApplicationSelect;
import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;

import static org.osgi.service.component.annotations.ServiceScope.PROTOTYPE;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Response.Status;

import my.package.MyDao;
import my.package.MyDto;


@Component(service=MyResource.class, scope=PROTOTYPE)
@JaxrsResource
@JaxrsApplicationSelect("(osgi.jaxrs.name=myservices)")
@Path("/myresource")
@JSONRequired
//@Produces(MediaType.APPLICATION_JSON)
public class MyResource {

@Reference
private MyDao dao;

public MyResource() {
System.out.println("MyResource created.");
}

@Activate
void activate(Map<String, Object> props) {
System.out.println("MyResource activated.");
}

@GET
@Path("/items")
@Produces(MediaType.APPLICATION_JSON)
public List<MyDto> getItems() {
return dao.list();
}

@GET
@Path("/itemlist")
@Produces(MediaType.APPLICATION_JSON)
public Response getItemList() throws Exception {
try {
List<MyDto> result = dao.list();
Mapper mapper = new MapperBuilder().build();
String json = mapper.writeArrayAsString(result); 
ResponseBuilder builder = Response.ok(json);
return builder.build();
}
catch(Exception ex) {
System.out.println(ex.getMessage());
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
}

}

On Monday, February 26, 2018 at 2:49:23 PM UTC-5, robert.p...@leidos.com wrote:

Raymond Auge

unread,
Feb 28, 2018, 9:39:11 AM2/28/18
to bndtool...@googlegroups.com
This could probably be handled better.

Could you file an issue with Apache Aries JAX-RS [1]?

- Ray

[1] https://issues.apache.org/jira/browse/ARIES-1785?jql=project%20%3D%20ARIES%20AND%20component%20%3D%20jax-rs-whiteboard

--
You received this message because you are subscribed to the Google Groups "bndtools-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bndtools-users+unsubscribe@googlegroups.com.

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



--
Raymond Augé (@rotty3000)
Senior Software Architect Liferay, Inc. (@Liferay)
Board Member & EEG Co-Chair, OSGi Alliance (@OSGiAlliance)

Tim Ward

unread,
Feb 28, 2018, 10:19:07 AM2/28/18
to bndtool...@googlegroups.com
Hi Bob,

This actually sounds as though it’s working correctly. When you make the service PROTOTYPE scoped then it is treated as a request-scoped resource. This means a new instance will be created, injected, activated, called and destroyed for every incoming request. If you don’t make the service PROTOTYPE scope then it is treated as a singleton and will be reused for each call. This is the same thing that JAX-RS does with Application#getSingletons() and Application#getClasses().

Note that there will also be an additional retrieval of the service when it is first attached to a whiteboard application. This is necessary to validate the resource and to populate DTO information.

Regards,

Tim

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

Raymond Auge

unread,
Feb 28, 2018, 10:22:37 AM2/28/18
to bndtool...@googlegroups.com
While I do agree with everything Tim said, I do think there's one extra call for an instance at the very start which, I believe, results in that instance being only used only for class introspection and then throw away.

- Ray
Reply all
Reply to author
Forward
0 new messages