How to access Spring Bean from JerseyTest subclass - java

Here is my abstract class which starts Jersey with given Spring context:
public abstract class AbstractJerseyTest extends JerseyTest {
public void setUp() throws Exception {
super.setUp();
}
#AfterClass
public void destroy() throws Exception {
tearDown();
}
#Override
protected URI getBaseUri() {
return URI.create("http://localhost:9993");
}
#Override
protected Application configure() {
RestApplication application = new RestApplication();
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
properties.put("contextConfigLocation", "classpath:spring-context-test.xml");
application.setProperties(properties);
application.register(this);
return application;
}
}
So, the problem is that I need to access Spring bean from my test to populate database with some data.
Jersey version is 2.6
Also I found a similar question here
But it's related to Jersey 1.x so it doesn't work for Jersey 2.x
Could anyone point me in the right direction?

Solution was really simple.
I added:
#Autowired
private Repository repository;
to the AbstractJerseyTest and this field was automatically autowired during test startup. I don't know details about how it works, but it seems that when I register instance of the test in REST application
application.register(this);
it automatically autowires all beans in the test.

Normally in your case, I'd just say work with mocks, but there are cases where you may need to expose the services in the test class.
To do this without any "ugly hacks", you will need to get a handle on the ServiceLocator (which is analogous to Spring's ApplicationContext). When the Jersey app boots up, all the Spring services from the ApplicationContext are put into the ServiceLocator through HK2's Spring bridge.
The problem is JerseyTest does not expose the ServiceLocator in any way. The only way I can think of to get a hold of it, is to create your own TestContainerFactory, and create the ApplicationHandler, which exposes the ServiceLocator.
Trying to implement your own TestContainerFactory is not a walk in the park, if you don't know what you're doing. The easiest thing to do is just look at the source code for Jersey's InMemoryTestContainerFactory. If you look at the constructor for the inner class InMemoryTestContainer, you will see it creating the ApplicationHandler. This is how you can expose the ServiceLocator, through the appHandler.getServiceLocator().
So if you copied that class, and exposed the ServiceLocator, you could create your JerseyTest extension, and call the ServiceLocator.inject(Object) method to inject the test class.
public abstract class AbstractServiceLocatorAwareJerseyTest extends JerseyTest {
private final ServiceLocatorAwareInMemoryTestContainerFactory factory
= new ServiceLocatorAwareInMemoryTestContainerFactory();
private ServiceLocator locator;
#Override
public TestContainerFactory getTestContainerFactory() {
return factory;
}
#Before
#Override
public void setUp() throws Exception {
super.setUp();
this.locator = factory.getServiceLocator();
if (injectTestClass()) {
this.locator.inject(this);
}
}
public boolean injectTestClass() {
return true;
}
public ServiceLocator getServiceLocator() {
return locator;
}
}
And if for any reason you needed it, the ServiceLocator also has the ApplicationContext, which you could also expose to your test class if needed.
I put together a GitHub project, with a complete implementation, with tests if you want to take a look at it.
UPDATE
Though the OP's answer to this question works, I believe the fact that it works, is a bug. I originally deleted this answer, after the OP posted their answer, but after some testing, I believe that solution is a bug, so I've undeleted this post for anyone who doesn't like the warning1 you get when you use that solution
1. "WARNING: A provider SimpleTest registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider SimpleTest will be ignored."

Related

Unit testing JAX-RS/Jersey servlet with Guice Injections

I have an application that uses Jersey/JAX-RS for web services (annotations, etc) and Guice to inject service implementations. I don't really like the way Guice works with servlets directly, I prefer the Jersey way, so I had to do a bit of fussing to get the service injections to work since Guice wouldn't be creating my servlet classes, and I didn't want to deal with the HK2-Guice bridge. I did this by creating a listener class (called Configuration) that sets up the injectors in static fields upon application startup and then manually effecting the injections in each servlet class by creating a parent class that all my servlets extend with a constructor that contains the following:
public MasterServlet() {
// in order for the Guice #Inject annotation to work, we have to create a constructor
// like this and call injectMembers(this) on all our injectors in it
Configuration.getMyServiceInjector().injectMembers(this);
Configuration.getDriverInjector().injectMembers(this);
}
I know it's kind of hacky, but this works just fine in my servlets. I can use the Guice #Inject annotations on my services and switch between named implementations and so on. The problem comes when I go to set up my unit tests. I'm using JerseyTest to do my tests, but running a test against my servlets results in a 500 error with Guice saying the following:
com.google.inject.ConfigurationException: Guice configuration errors:
1) No implementation for com.mycompany.MyService was bound.
while locating com.mycompany.MyService
for field at com.mycompany.servlet.TestGetServlet.service(TestGetServlet.java:21)
while locating com.mycompany.servlet.TestGetServlet
The test looks like this:
public class TestServletTest extends JerseyTest {
#Test
public void testServletFunctional() {
final String response = target("/testget").request().get(String.class);
assertEquals("get servlet functional", response);
}
#Before
public void setup() {
Configuration configuration = new Configuration();
configuration.contextInitialized(null);
}
#Override
protected Application configure() {
return new ResourceConfig(TestGetServlet.class);
}
}
You'll notice in the setup method I am manually creating my Configuration class since I can't rely on the test container (Grizzly) to create it (I get NullPointerExceptions without those two lines). More about this below.
And here's the servlet being tested:
#Path("/testget")
public class TestGetServlet extends MasterServlet {
#Inject
MyService service;
#GET
#Produces({"text/plain", MediaType.TEXT_PLAIN})
public String testGet() {
//service = Configuration.getServiceInjector().getInstance(MyService.class);
return "get servlet functional";
}
}
Notice the commented line in the testGet() method? If I do that instead and remove the #Inject annotation above, everything works fine, which indicates that Grizzly is not creating my servlets the way I expect.
I think what's happening is that Grizzly doesn't know about Guice. Everything seems to suggest that Grizzly isn't seeing the Configuration class, despite the fact that by putting it in my test's #Before method it seems to be at least available to the classes that use it (see: the commented line in the TestGetServlet class). I just don't know how to fix it.
I'm still trying to figure this out but in the meantime I switched from Guice to HK2, which took a bit of doing but I figured this might be helpful for anyone who runs into this problem in the future.
I consider this an answer because truthfully my attempt to bypass the Guice-HK2 bridge but still use Guice with Jersey might not have been the best idea.
Switching from Guice to HK2 takes a bit of doing and there's no comprehensive guide out there with all the answers. The dependencies are really fussy, for example. If you try to use Jersey 2.27 you may run into the famous
java.lang.IllegalStateException: InjectionManagerFactory not found
error. Jersey 2.27 is not backwards compatible with previous versions due to HK2 itself. I am still working on getting that all to work, but in the meantime I had to downgrade all my Jersey dependencies to 2.26-b06 to get HK2 working properly.
Jersey thankfully already implements a bunch of HK2 boilerplate, so all you need to get injection working is proper use of #Contract, #Service (see HK2 docs for those), and then two new classes that look like this:
public class MyHK2Binder extends AbstractBinder {
#Override
protected void configure() {
// my service here is a singleton, yours might not be, so just omit the call to in()
// also, the order here is switched from Guice! very subtle!
bind(MyServiceImpl.class).to(MyService.class).in(Singleton.class);
}
}
And this:
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new MyHK2Binder());
packages(true, "com.mycompany");
}
}
Simple enough, but this only works for the application itself. The test container knows nothing about it, so you have to redo the Binder and ResourceConfig yourself in your test class, like this:
public class TestServletTest extends JerseyTest {
#Test
public void testServletFunctional() {
final String response = target("/testget").request().get(String.class);
assertEquals("get servlet functional", response);
}
#Before
public void setup() {
}
#Override
protected Application configure() {
return new TestServletBinder(TestGetServlet.class);
}
public class TestServletBinder extends ResourceConfig {
public TestServletBinder(Class registeree) {
super(registeree);
register(new MyHK2Binder());
packages(true, "com.mycompany");
}
}
}
Having to do this is actually fine because you can switch out the Binder for a test binder instead, in which you've bound your service to a mocked service instead or something. I haven't done that here but that's easy enough to do: replace new MyHK2Binder() in the call to register() with one that does a binding like this instead:
bind(MyTestServiceImpl.class).to(MyService.class).in(Singleton.class);
And voila. Very nice. Obviously you could achieve a similar result with Named bindings, but this works great and might even be simpler and more clear.
Hope this helps someone save the hours I spent screwing around to get this working.

JAX-RS Application subclass injection

I'm writing custom JAX-RS 2.0 application (under Jersey 2.3.1) which holds some data for use by all the resources.
public class WebApp extends org.glassfish.jersey.server.ResourceConfig {
public WebApp() {
packages("my.resources.package");
}
}
(I could use API's javax.ws.rs.core.Application as well, the described result is the same)
Then I inject the object into a resource
#Path("test")
public class Test {
#Context
Application app;
#GET
#Path("test")
public String test() {
return "Application class: " + app.getClass();
}
}
However, the result of a call is
Application class: class org.glassfish.jersey.server.ResourceConfig$WrappingResourceConfig
which makes me use some ugly tricks like
if (app instanceof WebApp) {
return (WebApp) app;
} else if (app instanceof ResourceConfig) {
return (WebApp) ((ResourceConfig) app).getApplication();
}
My understanding of JAX-RS 2.0 spec section 9.2.1:
The instance of the application-supplied Application subclass can be injected into a class field or method parameter using the #Context annotation. Access to the Application subclass instance allows configuration information to be centralized in that class. Note that this cannot be injected into the Application subclass itself since this would create a circular dependency.
is that application-supplied Application subclass is mine WebApp, not JAX-RS implementation-specific wrapper.
Also, changing this fragment
#Context
Application app;
to this
#Context
WebApp app;
causes app to be null, due to ClassCastException during context injection, so the declared type doesn't matter.
Is it a bug in Jersey or my misunderstanding?
UPDATE: I checked the behaviour under RESTEasy 3.0. The injected object is my WebApp, without any wrappers. I'd call it a bug in Jersey.
This doesn't seem like a bug. According to JAX-RS 2.0 spec you can inject Application into your resource classes (for example) but it does not say anything about directly injecting custom extensions of the Application. Not sure what your use-case is but you can register custom HK2 binder that will allow you to inject directly WebApp into resources:
public class WebApp extends org.glassfish.jersey.server.ResourceConfig {
public WebApp() {
packages("my.resources.package");
register(new org.glassfish.hk2.utilities.binding.AbstractBinder() {
#Override
protected void configure() {
bind(WebApp.this);
}
});
}
}
I too have encountered this using Jersey 2.4.1.
FWIW: I agree it seems like a bug according to the spec para 8.2.1. The statement "The instance of the application-supplied Application subclass" seems perfectly clear.
I have an alternative workaround that doesn't involve glassfish.hk2 but still concentrates the Jersey-specific code in the Application-derived class.
public class MyApp extends ResourceConfig {
...
static MyApp getInstance( Application application) {
try {
// for a conformant implementation
return (MyApp) application;
} catch (ClassCastException e) {
// Jersey 2.4.1 workaround
ResourceConfig rc = (ResourceConfig) application;
return (MyApp) rc.getApplication();
}
}
...
}
public class MyResource {
...
#Context Application application;
...
SomeMethod() {
... MyApp.getInstance( application);
}
}
Hope this is useful.
This appears to be fixed in a later version og Jersey. The same approach works for me with Jersey 2.16 at least. My injected Application object is of the correct subclass without any wrapping whatsoever.
Edit: Or maybe the version is irrelevant after all. Please see the comments to this answer.

What is the proper replacement of the Resteasy 3.X PreProcessInterceptor?

I'm building rest service using an authentication/authorization mechanism as described in this tutorial: http://howtodoinjava.com/2013/06/26/jax-rs-resteasy-basic-authentication-and-authorization-tutorial/
Basically it uses the PreProcessInterceptor interface to scan the target method for annotations (from javax.annotation.security package) which describe the required roles to access that method. As the the authenticator here is an interceptor, it can cancel the target method invocation, returning a 401 (unauthorized) if needed.
The problem here is that the interface org.jboss.resteasy.spi.interception.PreProcessInterceptor is deprecated in the current RestEasy version (3.0.1), and I'm having problems trying to implement the same behaviour with the standard JAX-RS interfaces.
I'm using the javax.ws.rs.ext.ReaderInterceptor interface to intercept the call. But somehow the server never calls it: the interceptor is just ignored.
I'm registering the interceptors/resources the same way as I did with the former PreProcessInterceptor, and using the same #Provider and #ServerInterceptor annotations:
ServerApplication:
public class ServerApplication extends javax.ws.rs.core.Application {
private final HashSet<Object> singletons = new LinkedHashSet<Object>();
public ServerApplication() {
singletons.add(new SecurityInterceptor());
singletons.add( ... ); //add each of my rest resources
}
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>();
return set;
}
#Override
public Set<Object> getSingletons() {
return singletons;
}
}
SecurityInterceptor:
#Provider
#ServerInterceptor
public class SecurityInterceptor implements javax.ws.rs.ext.ReaderInterceptor {
#Override
public Object aroundReadFrom(ReaderInterceptorContext context){
//code that is never called... so lonely here...
}
}
Any insights about how can I solve this problem?
Thank you.
RESTEasy 3.x.x conforms to the JAX-RS 2.0 specification.
What you are trying to do could be accomplished (maybe better) with:
#Provider
public class SecurityInterceptor
implements javax.ws.rs.container.ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext){
if (not_authenticated){ requestContext.abortWith(response)};
}
}
since the ReaderInterceptor is invoked only if the underlying MessageBodyReader.readFrom is called by the standard JAX-RS pipeline, not fromthe application code.
The reason why your interceptor is not called, though, could be the #ServerInterceptor annotation, which is a RESTEasy extension.
The spec states at ยง6.5.2 that a interceptor is globally registered, unless the #Provider is annotated with a #NameBinding annotation, but I don't know if RESTEasy can handle a #ServerInterceptor if it's not explicitly registered as shown in RestEASY Interceptor Not Being Called
If you need to get access to the underlying java.lang.reflect.Method (like you used to be able to get by implementing AcceptedByMethod), you can do the following:
ResourceMethodInvoker methodInvoker = (ResourceMethodInvoker)
requestContext.getProperty("org.jboss.resteasy.core.ResourceMethodInvoker");
Method method = methodInvoker.getMethod();
I also wanted to get access to the underlying java.lang.reflect.Method and tried mtpettyp's answer with Resteasy 3.0.8, but that was returning null on the getProperty call. I am also using Spring and resteasy-spring although I don't believe that should impact this at all.
If you run into my situation and are implementing a Post Matching ContainerRequestFilter (you kind of have to if you were expecting to get the matched resource method anyway) then you can actually cast the ContainerRequestContext to the implementation Resteasy has for the Post Match scenario. The PostMatchContainerRequestContext has a reference to the ResourceMethodInvoker.
public void filter(ContainerRequestContext context) throws IOException {
PostMatchContainerRequestContext pmContext = (PostMatchContainerRequestContext) context;
Method method = pmContext.getResourceMethod().getMethod();
/* rest of code here */
}

Tomcat WebSocketServlet and Google Guice

I have a webapp which requires the usage of Tomcat 7 web sockets.
In this webapp all standard Servlets (those extending javax.servlet.http.HttpServlet) work nicely (and correctly) with Google Guice. To make my Servlet work with Guice handlers I simply:
decorate the servlet with #Singleton
declare private Provider for MyHandler instance & generate a setter which is marked for injection
decorate the Servlet's constructor with #Inject
Example to demonstrate points above:
#Singleton
public class MyServlet extends HttpServlet {
private Provider<MyHandler> myHandler;
#Inject
MyServlet() {
}
#Override
protected void service(..) throws ServletException { ... }
#Inject
public void setMyHandler(Provider<MyHandler> myHandler) {
this.myHandler = myHandler;
}
...
}
How can one call the same Guice handler, above called myHandler from a WebSocketServlet?
I can't adopt the same style as in the standard servlet use case because, rather than having a Singleton servlet as in the case of the standard servlets, each WebSocket communication results in an instance extending MessageInbound; then the appropriate method that would call MyHandler is called from a method (e.g. onOpen or onClose) within the MessageInbound instance; not from a method within an HttpServlet instance as MyServlet above.
What did I try? I did try some (conceptually wrong) solutions such as calling the websocket-servlet's handlers from within the MessageInbound instance; that of course results in scoping problems lower down the Guice stack trace. What is the conceptually correct way of doing this?
Update after looking at GitHub example:
How you use Guice is just fine. Since there is just one particular usage of MessageInbound any sugar like with the AbstractGuiceWebSocketServlet is unnecessary. Provider chatLogHdlr and then doing manual construction is OK. But you lose AOP support. If that is needed you might want to do Assisted Inject. But for now this is fine.
On a side note, use construction injection instead of setter injection.
I saw immediately what is the problem. It is not Guice but rather how you use Guice-Persist. I didn't used GP a lot and still use the venerable Warp-persist. But I see 2 problems with how you use Guice-persist in your code:
You need to inject the PersistService to start Guice-Persist. It is explained in the WIKI e.g.
public class PocWebApp extends GuiceServletContextListener {
#Inject
PersistService ps;
#Override
protected Injector getInjector() {
Injector injector = Guice.createInjector(new ServletModule() {
#Override
protected void configureServlets() {
install(new JpaPersistModule("DesktopPU"));
serve("/socket/main/").with(MainSocket.class);
}
});
injector.injectMembers(this);
return injector;
}
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
super.contextInitialized(servletContextEvent);
ps.start();
}
}
The PersistFilter is useless as only the first time WebSocket will go trough filter but all subsequent communication will not go trough the filter. Using the txn just around #Transactional (Session-per-transaction) is the way to go.
Off-topic:
How many users do you intend to support? If this is going to be a hardcore chat server I'd use Netty instead but it is somewhat more involved. Googling found this:
http://comoyo.github.com/blog/2012/07/30/integrating-websockets-in-netty/
Original answer:
So this is a question about style?
WebSockets != Servlets. There is nothing wrong if they require a slightly different style. I'd even prefer to be reminded I am not dealing with vanilla servlets.
Some observations:
WebSocketServlet is nothing special. You can easily use it with Guice-Servlet. E.g.:
#Singleton
public class FooGuiceWebSocketServlet extends WebSocketServlet {...}
And then refernce it as
serve("/foo").with(FooGuiceWebSocketServlet.class);
Now, MessageInbound that is special as is all handled by Tomcat as you explained. The MessageInbound is WebSocket scoped. Now Guice has no idea about this scope and it might make sense to leave it that way.
For starters I'd make sure MessageInbound is created by Guice. Something along this lines:
#Singleton
public class ExampleWebSocketServlet extends AbstractGuiceWebSocketServlet {
#Override
public Class<? extends StreamInbound> serveWith() {
return Foo.class;
}
public static class Foo extends MessageInbound {
#Inject GuiceCreatedAndInjected bar;
#Override
protected void onBinaryMessage(ByteBuffer byteBuffer) throws IOException {
// do nothing
}
#Override
protected void onTextMessage(CharBuffer charBuffer) throws IOException {
// this getSwOutbonds() is not very friendly to testing
getWsOutbound().writeTextMessage(bar.echo(charBuffer));
}
}
}
Where
public abstract class AbstractGuiceWebSocketServlet extends WebSocketServlet {
#Inject Injector injector;
#Override
protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) {
return injector.getInstance(serveWith());
}
public abstract Class<? extends StreamInbound> serveWith();
}
You can go from here to higher abstractions and/or scopings as needed. I don't particularly like #getWsOutbound() as it hinders testing.
Just keep on improving the style until you are satisfied. Say if you need more help (will modify answer).
I didn't quite understand what you're trying to accomplish. I would look for AOP support in Guice. It seems that you need that MyHandler is set (inject) before being used in some methods of MessageInbound subclass' instance. An aspect could do that.
However, there is one question you need to ask: where is the (instantialization) control? If there's some way to add some kind of delegation in your Tomcat application configuration, Guice could "enhance" the MessageInbound instances which, in turn, would have the proper MyHandler set before use.

Unit testing Jersey Resources with Guice injected fields

I have a Jersey Resource that I want to test with JUnit. The resource uses Guice Providers to inject certain fields:
#Path("/example/")
class ExampleResource {
#Inject
Provider<ExampleActionHandler> getMyExampleActionHandlerProvider;
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<ExamplePojo> getExampleList() {
ExampleActionHandler handler = getMyExampleActionHandlerProvider.get();
handler.doSomething();
...
This all works beautifully when using a real server to serve the API, however testing it is problematic.
My test class currently looks something like:
public class ApiTest extends JerseyTest {
public ApiTest() throws Exception {
super();
ApplicationDescriptor appDescriptor = new ApplicationDescriptor();
appDescriptor.setContextPath("/api");
appDescriptor.setRootResourcePackageName("com.my.package.name");
super.setupTestEnvironment(appDescriptor);
}
#Test
public void testHelloWorld() throws Exception {
String responseMsg = webResource.path("example/").get(String.class);
Assert.assertEquals("{}", responseMsg);
}
}
Clearly, Guice isn't getting the opportunity to initialize the fields in ExampleResource so that the handler.doSomething() call doesn't result in a NullPointerException.
Is there a way to tell Jersey to instantiate the ExampleResource class using Guice so that the Provider works?
One way to do it is to break the tests to few steps. You need to create the injector you're configuring the service with and test that injector (see Testing Guice Servlet bindings and Testing Guice can init servlets). Using these tests you make sure you have the right bindings in place.
Once you have the injector, get the ApplicationDescriptor object from it with
ExampleResource exampleResource = injector.getInstance(ExampleResource.class);
Assert.assertEquals(myList, getExampleList());

Categories