ECF / JaxRSProviders

Remote Services distribution provider based upon JaxRS. Includes imples based upon Jersey and CXF.
Apache License 2.0
13 stars 19 forks source link

Jersey instantiates the JAX-RS service on request #5

Closed erdalkaraca closed 8 years ago

erdalkaraca commented 8 years ago

It seems that jersey re-instantiates the OSGi component on the first request to the alias URI. This means that if the OSGi component has any dependencies injected via Reference, then they will not be available.

Scott, do you have any ideas what I could check?

erdalkaraca commented 8 years ago

CXF behaves the same: the service class is instantiated twice (OSGi SCR and the JAX-RS impl). This means if this behavior is part of the specs, then using OSGi (ds) services with JAX-RS does not make sense, since I cannot use dependencies (via Reference annotation).

scottslewis commented 8 years ago

Hi Erdal. I need to look at jaxrs 2.0 more closely but afaik it doesn't support dynamics needed to add/remove services programmatically. But otoh doing so statically via reference annotation has to be implemented somehow so maybe it's a question of standardizing existing api.

I'm going to look at jerseys servlet implementation when possible...I'm travelling today so cannot.

One question: why would you favor the jaxrs reference annotation...it static...over the ds reference (dynamic, transitive deps etc)

erdalkaraca commented 8 years ago

Thanks, Scott! Does jaxrs also have a @Reference annotation?

I have this service:


@Component(...)
@Path()
class MyService {

@Reference
void setDependency(MyDependency dep){
}

@GET
@Path()
String getValue() {
   return dep.doSomething()
}
}

This is an OSGi component/service AND a JAX-RS service. Now, the class will be instantiated twice: by OSGi and JAX-RS. If getValue() is executed, then this is done on the instance that has not the dependencies set.

scottslewis commented 8 years ago

On Aug 11, 2016 11:35 AM, "Erdal Karaca" notifications@github.com wrote:

Thanks, Scott! Does jaxrs also have a @Reference annotation?

I don't think so. After sending I realized I probably misinderstood what you were saying.

I have this service:

@Component(...) @Path() class MyService {

@Reference void setDependency(MyDependency dep){ }

@GET @Path() String getValue() { return dep.doSomething() } }

This is an OSGi component/service AND a JAX-RS service. Now, the class will be instantiated twice: by OSGi and JAX-RS.

Ok. I didn't realize this is what you were doing.

If getValue() is executed, then this is done on the instance that has not the dependencies set.

It is possible with ds to define the Reference such that the Component isn't registered...and then exported as rs...until the dependency is satisfied.

I can't continue right now but let me know if this doesn't make sense and I will respond later.

— You are receiving this because you commented.

Reply to this email directly, view it on GitHub, or mute the thread.

erdalkaraca commented 8 years ago

Scott, I experimented a bit with Jax-rs/Jersey. The Application class has a getClasses() and getSingletons() method. We should implement getSingletons() instead of getClasses() and return the service implementation POJOs (from the OSGi registry):

JerseyServerDistributionProvider.createResourceConfig()

scottslewis commented 8 years ago

Hi Erdal. Thanks for the investigation.

We should implement getSingletons() instead of getClasses() and return the service implementation POJOs (from the OSGi registry):

Since OSGi services are dynamically registered will this work as desired? That is, the POJOs (service impls) can be added and removed dynamically...will this result in the Jersey servlet (i.e. org.glassfish.jersey.servlet.ServletContainer.ServletContainer(ResourceConfig) created and registered upon first remote service export) re-calling getSingletons()?

Thanks, we'll get this licked.

scottslewis commented 8 years ago

Hi Erdal,

I believe I've figured this out.

Jersey's ServletContainer class has a reload(ResourceConfig) method, that allows the ServletContainer to be reconfigured dynamically. I've tested this, and it allows multiple resources to be assigned to a single servlet.

So, currently for each uri..e.g. http://localhost:8282/jersey/is/good a separate ECF remote service container is created. This is as it should be I think, as each container manages a single servlet.

When I complete these changes, however, multiple jax-rs resources may be registered as OSGi services and as long as their Path is unique (e.g. studentservice and studentservice2) then they will be exposed by one servlet...e.g.:

http://localhost:8282/jersey/is/good/studentservice/students

and

http://localhost:8282/jersey/is/good/studentservice2/students

will be exposed via a single servlet.

And of course, it will be possible to have as many paths/servlets as desired as well.

The only limitation is that multiple instances of a single resource won't be possible....e.g. two separate instances of a single student service.

I'll be working on completing this Monday. I've already experimented with reload(ResourceConfig) to prove that it works as expected.

scottslewis commented 8 years ago

Hi Erdal,

I've just committed a fix for this that allows multiple resource classes to be used with a single servlet/ECF container. For example:

Let's say that a service is exported as follows:

props.put("service.exported.interfaces","*"); props.put("service.exported.configs","ecf.jaxrs.jersey.server"); props.put("ecf.jaxrs.jersey.server.uri","http://localhost:8282/jersey/is/good"); bc.registerService(StudentService.class,new StudentServiceImpl(),props);

...or via ds such as:

https://github.com/ECF/JaxRSProviders/blob/master/examples/com.mycorp.examples.student.remoteservice.host/src/com/mycorp/examples/student/remoteservice/host/StudentServiceImpl.java

This will result in a new ECF container being created (with ID http://localhost:8282/jersey/is/good ) and a servlet registered at /jersey/is/good via HttpService.

Then if another service instance is registered with the same uri...e.g.

https://github.com/ECF/JaxRSProviders/blob/master/examples/com.mycorp.examples.student.remoteservice.host/src/com/mycorp/examples/student/remoteservice/host/StudentServiceImpl2.java

Then this new resource instance will be dynamically added to the same servlet.

If the OSGi services are unregistered, then these will be dynamically removed from the servlet at /jersey/is/good, and if all are unregistered then the servlet will be unregistered as well.

Note that the @Path values for the two services are distinct...i.e. 'studentservice' and 'studentservice2'.

This has to be the case for this to work.

As well, the path prefix can be varied, and this will mean that multiple servlets/ECF containers will be created/configured to handle them.

This currently all only works with Jersey impl. I'm investigating how to implement this with CXF.

I'm going to close this issue as I think this deals with it pretty well. If you think it's necessary, please reopen.