Jersey Endpoint+OSGi Dependency, Keeping Track - java

I have a Jersey endpoint which uses a custom OSGi Service ExceptionManager Service.
#Path("service")
public class ServiceFacade {
private volatile ExceptionManager exceptionManager;
public ServiceFacade() {
BundleContext bC = FrameworkUtil.getBundle(ServiceFacade.class).getBundleContext();
ServiceReference<ExceptionManager> sR = bC.getServiceReference(ExceptionManager.class);
if (sR != null)
this.exceptionManager = bC.getService(sR);
}
#GET
#Consumes({MediaType.APPLICATION_JSON})
#Produces(MediaType.APPLICATION_JSON)
public Response sayHello() {
try {
if (exceptionManager == null)
return Response.status(Status.SERVICE_UNAVAILABLE).build();
// Do some work...
} catch (Exception e) {
exceptionManager.handle(e);
}
}
}
This Jersey class is added to the Jersey Application as a simple class, that means that every time a user hits this endpoint, a new instance of this class is created to handle the request. As you can see, the class contains a constructor which initializes the ExceptionManager Service. My question is, isn't there a simplified way of retrieving the service without going to BundleContext?
I have seen DependencyManager, but this bundle seems to only add the dependencies to the class (ServiceFacade in this case) during the Activation process, but that dependency resolution is too early this has to be done during run-time, every time an instance is created. Bellow is an approximation with DependencyManager but is not a solution for this:
public class Activator extends DependencyActivatorBase {
#Override
public void init(BundleContext bundleContext, DependencyManager dependencyManager) throws Exception {
dependencyManager.add(createComponent()
.setImplementation(ServiceFacade.class)
.add(createServiceDependency()
.setService(ExceptionManager.class)
.setRequired(true));
}
}
Thanks.-

You can obtain the reference to an OSGi service without accessing to BundleContext by using Declarative Services. A tutorial can be found here.

You can make the endpoint a singleton resource. This way you can let the dependency manager create a single instance and inject services and then add that instance to the Jersey application.
There are a few limitations, like Jersey's field or constructor injection does not work. You also have to be careful about concurrency when using fields of the resource.

Related

How to access configuration in a Lagom service at startup?

I am migrating my current app in Spring/J2EE to Lagom. I am working in Java. I need to read variables from the configuration (application.conf in resources folder). In the implementation module, I try to inject configuration as a class variable like this
#Inject
private Configuration config
but when I access this config object in the constructor, it gives null pointer exception.
The whole code is like this
import play.Configuration;
public class SomeServiceImpl implements SomeService {
#Inject
private Configuration config;
public SomeServiceImpl() {
//getting configuration from application.conf
// gives exception as config is null.
String key = config.getString(“key”);
}
#Override
public ServiceCall<Request, Response> send() {
//works here, does not give exception
String key = config.getString(“key”);
}
}
Sorry, I should have been clear from the beginning. I have edited the original question. I get null pointer exception when I try to read from configuration object in constructor but I am able to use it in service call implementation. I want some way in which I can access the configuration in application.conf at startup and possibly store in some config class which can be accessed anywhere later.
In Java, when an object is instantiated, the first thing that happens (before anything else can possibly happen) is the constructor is invoked. After that, frameworks like Guice (which Lagom uses) are free to inject things, but they can't do it until the constructor has been invoked. So, all your #Inject annotated fields will be null when the constructor is invoked, there is nothing you can do to work around that.
So, don't use field injection, use constructor injection, eg:
import play.Configuration;
public class SomeServiceImpl implements SomeService {
private final Configuration config;
#Inject
public SomeServiceImpl(Configuration config) {
this.config = config;
String key = config.getString("key");
}
#Override
public ServiceCall<Request, Response> send() {
String key = config.getString("key");
}
}
Constructor injection is not just recommended for this use case, you should be using it everywhere, it avoids all these potential issues.

Injecting a dependency into Guice Module

I have a module that acquires and holds an API token (simplified):
#Singleton
public class KeyHolderModule extends AbstractModule {
// This doesn't seem to be injected
private #Inject TokenConnector connector;
private DateTime keyLastRefreshed;
private String key;
private Credentials creds = config.getCreds();
#Override protected void configure() {
this.key = connector.getToken(creds);
this.keyLastRefreshed = DateTime.now();
}
#Provides #Named("apiKey") public String getKey() {
// logic to check key last refreshed and handle generating a new one
return this.key;
}
}
I get a null pointer error on the line where I try to access the connector (this.key = connector.getToken(creds);), so the connector is obviously not getting wired up correctly.
I've tried creating a constructor and using #Inject there, but I'm manually adding these modules via new to a list in my app bootstrap class, so that's sort of out.
Obviously I'm missing something here -- I could probably just new up a TokenConnector in this case since it doesn't have any dependencies itself, but that wouldn't fix my fundamental failure to grasp what's happening here. So if you want to see (simplified) other pieces of code, or less simplified pieces of this code, let me know.
Though you can't use #Inject for a Module (unless you get the Module from another Injector, which I recommend strongly against), you can easily inject into a #Provides method.
public class KeyHolderModule extends AbstractModule {
private DateTime keyLastRefreshed;
private String key;
private Credentials creds = config.getCreds();
#Override protected void configure() {}
#Provides #Named("apiKey") public String getKey(
TokenConnector connector) {
// logic to check key last refreshed and handle generating a new one
this.key = connector.getToken(creds);
this.keyLastRefreshed = DateTime.now();
return this.key;
}
}
The trick here is that a Module is typically manually instantiated at injector creation time, but #Provides methods are invoked when the dependencies they provide are needed. Consequently, the Injector isn't ready to provide anything when the Module is constructed, but #Provides methods invoked throughout the application lifecycle have access to whatever other injector-provided dependencies they might need. When configure is run the Injector is not yet created, the best you can do is call getProvider (though you can't call get on those until the Injector is ready).
I wrote up a variety of other in-Module injection techniques as this SO answer.

How to inject callback classes in Spring context?

Is it possible to use callbacks with Spring to that they are managed by application context?
My problem is when a service is used from outer by #Autowired, but within that service there is a callback defined using new operator.
The following example executes a method that is worth retrying. Spring offers a RetryCallback for this case (I know this could be acchieved differently, but just to illustrate my callback problem).
#Service
class MyService {
//main method invoked
void run(DataVO dataVO) {
//new operator not usable in spring context
RetryCallback<Object> retryCallback = new RetryCallback<Object>() {
#Override
public Object doWithRetry(RetryContext context) throws Exception {
return createBooking(dataVO);
}
};
}
private Object createBooking(DataVO dataVO) {
//creates the booking, worth retry on specific failures
//uses further injected/autowired services here
}
}
Is it possible to refactor this snippet so that the callback is managed by spring/injected/autowired?
Make your service implement the callback interface :
#Service
class MyService implements RetryCallback<Object> {
//main method invoked
void run(DataVO dataVO) {
}
#Override
public Object doWithRetry(RetryContext context) throws Exception {
return createBooking(dataVO);
}
private Object createBooking(DataVO dataVO) {
//creates the booking, worth retry on specific failures
//uses further injected/autowired services here
}
}

Mapping of custom exception in JAX-RS via #WebFault annotation

I am developing a Client-Server app with JAX-RS / Apache CXF, JSON
I would like Apache CXF to handle my exception transparently on both ends : Which means transforming the exception into a bean, serializing it with my Jackson Serializer (JSON) and then doing the over way around on client side.
I have seen several confusing posts/answers on this subject and came up with using the #WebFault annotation :
#WebFault(name=CODE, faultBean="foo.bar.FaultBean")
public class DuplicateRuleNameFault extends Exception {
static final public String CODE = "DUPLICATE_RULE_NAME";
private FaultBean faultBean;
public DuplicateRuleNameFault(String msg) {
super(msg);
this.faultBean = new FaultBean(msg);
}
public DuplicateRuleNameFault() {
}
public FaultBean getFaultBean() {
return faultBean;
}
public void setFaultBean(FaultBean faultBean) {
this.faultBean = faultBean;
}
}
With no success ... Currently, CXF seems to happily ignore the annotation on the Exception and handle it as an unknown exception : 500 status error and no response body generated on the server side.
Is there something specific I have to configure in the "" server element of my Spring context ? I already have Spring scanning my Exception/FaultBean classes (is it even needed BTW ?).
I would appreciate if you could point me at some working example.
Thanks.
#WebFault's are not part of the JAX-RS specification. You will want to read up on section 3.3.4 of the specification, which describes the different ways you can accomplish what you are trying to do.
Option 1
Design your resource classes to throw WebApplicationException's. Set the response property of these exceptions to be a valid JAX-RS response containing the fault beans you want to send to the client.
Option 2
Define exception mapping providers. You can create a hierarchy of these to handle all the common exceptions your application will throw. Or you can create a top level exception with an embedded bean and an exception handler for it. And then derive several specific exceptions from the top level one.
public abstract class MyApplicationException<T> extends Exception {
private T faultBean;
// Constructors, setters/getters
}
#Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException<?>> {
// Implementation
}
One way of doing this is by using the javax.ws.rs.core.Response object like so :
#GET
#Path("/")
public Response getBlah()
{
try {
return Response.status(Response.Status.OK)
.entity(<Object you want to return>).build();
}
catch (final DuplicateRuleNameFault e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(e.getFaultBean().getMsg()).build();
}
}

Unit Testing gwt-dispatch

I'm trying to write some unit tests for a gwt-dispatch service with JUnit. I'm getting the following error when stepping through the test with my debugger:
Error in custom provider, com.google.inject.OutOfScopeException: Cannot access scoped object. Either we are not currently inside an HTTP Servlet request, or you may have forgotten to apply com.google.inject.servlet.GuiceFilter as a servlet filter for this request.
I'm going to simplify the code a bit here -- hopefully I'm not stripping out anything necessary.
import junit.framework.TestCase;
import net.customware.gwt.dispatch.client.standard.StandardDispatchService;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.ServletModule;
...
public class LoggedInServiceTest extends TestCase {
Injector i;
StandardDispatchService service;
protected com.google.inject.Injector getInjector() {
return Guice.createInjector(new ServletModule(),
new TestServletModule(),
new ActionsHandlerModule(),
new TestDispatchModule(),
new OpenIdGuiceModule());
}
public void setUp() throws Exception {
i = getInjector();
service = i.getInstance(StandardDispatchService.class);
}
public void testNotLoggedIn() {
try {
GetProjectsResult result = (GetProjectsResult) service.execute(new GetProjectsAction());
result.getSizeOfResult();
} catch (Exception e) {
fail();
}
}
}
The service request is indeed supposed to be going through a GuiceFilter, and it looks like that filter is not being set.
Any ideas on what other setup needs to be done to register the filter?
The problem is just what it states. You are trying to access a scoped object, but you are not currently in the scope. Most likely, your test is asking the injector for a RequestScoped object or an object that has a RequestScoped object in the injection dependency tree, but the test didn't do anything to enter the scope.
Binding the GuiceFilter in the test doesn't help, because your test isn't trying to send an HttpServletRequest through GuiceFilter to a servlet.
The best option would be to unit test your code. Create your classes in isolation, injecting mocks.
Assuming you want to do some kind of integration test, you have three options:
Have your test install a test module that called bindScope(RequestScoped.class, new FakeScope). The FakeScope class would implement Scope and have methods to enter and exit the scope. You may have to "seed" the scope with fake implementations of objects you depend on. See the Guice CustomScopes wiki page. This is the best option for integration tests, IMHO
Use ServletScopes.scopeRequest (Javadoc) to run part of the test code inside of a simulated request scope. This gets a bit ugly since you need to pass a Callable.
Do a full end-to-end test. Start your server and send it requests using Selenium. It's really hard to get good coverage this way, so I would leave this to things that you really need a browser to test.
Things might get a bit messy if the class you are testing depends indirectly on HttpServletRequest or HttpServletResponse. These classes can be challenging to setup correctly. Most of your classes should not depend on the servlet classes directly or indirectly. If that is not the case, you are either doing something wrong or you need to find a good action framework that allows you have most of your code not depend on these classes.
Here's an example of approach 1, using SimpleScope from the Guice CustomScopes wiki page:
public class LoggedInServiceTest extends TestCase {
private final Provider<StandardDispatchService> serviceProvider;
private final SimpleScope fakeRequestScope = new SimpleScope();
private final HttpServletRequest request = new FakeHttpServletRequest();
protected Injector createInjector() {
return Guice.createInjector(new FakeRequestScopeModule(),
new LoggedInServiceModule();
}
#Override
protected void setUp() throws Exception {
super.setUp();
Injector injector = createInjector();
scope.enter();
serviceProvider = injector.getProvider(StandardDispatchService.class);
}
#Override
protected void tearDown() throws Exception {
fakeRequestScope.exit()
super.tearDown();
}
public void testNotLoggedIn() {
fakeRequestScope.enter();
// fill in values of request
fakeRequestScope.seed(FakeHttpServletRequest.class, request);
StandardDispatchService service = serviceProvider.get();
GetProjectsAction action = new GetProjectsAction();
try {
service.execute(action);
fail();
} catch (NotLoggedInException expected) {
}
}
private class FakeRequestScopeModule extends AbstractModule() {
#Override
protected void configure() {
bind(RequestScoped.class, fakeRequestScope);
bind(HttpServletRequest.class)
.to(FakeHttpServletRequest.class)
.in(RequestScoped.class)
}
}
}
Write an AppSession interface and two implementations: HttpAppSession and MockAppSession.
Make your server-side handlers depend on AppSession and not on HttpSession directly.
Use Guice to inject HttpSession into HttpAppSession. That's the one you'll use in production, and for actually running your app. within a real servlet container.
The MockAppSession should not depend on HttpSession, nor HttpServletRequest, nor any other Guice Http scope. That's the one you'll use during testing.
Now, your Guice module should inject an AppSession implementation as follows:
bind(AppSession.class).to(MockAppSession.class)
bind(MockAppSession.class).in(Singleton.class)
That'll sort you out.

Categories