I'm new to Guice and already stuck :)
I pretty much copied classes GuiceConfig, OfyFactory and slightly modified Ofy from Motomapia project (which you can browse) using it as s sample.
I created GuiceServletContextListener which looks like this
public class GuiceConfig extends GuiceServletContextListener
{
static class CourierServletModule extends ServletModule
{
#Override
protected void configureServlets()
{
filter("/*").through(AsyncCacheFilter.class);
}
}
public static class CourierModule extends AbstractModule
{
#Override
protected void configure()
{
// External things that don't have Guice annotations
bind(AsyncCacheFilter.class).in(Singleton.class);
}
#Provides
#RequestScoped
Ofy provideOfy(OfyFactory fact)
{
return fact.begin();
}
}
#Override
public void contextInitialized(ServletContextEvent servletContextEvent)
{
super.contextInitialized(servletContextEvent);
}
#Override
protected Injector getInjector()
{
return Guice.createInjector(new CourierServletModule(), new CourierModule());
}
}
I added this listener into my web.xml
<web-app>
<listener>
<listener-class>com.mine.courierApp.server.GuiceConfig</listener-class>
</listener>
<!-- GUICE -->
<filter>
<filter-name>GuiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GuiceFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<!-- My test servlet -->
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.mine.courierApp.server.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app>
OfyFactory looks like this
#Singleton
public class OfyFactory extends ObjectifyFactory
{
Injector injector;
#Inject
public OfyFactory(Injector injector)
{
this.injector = injector;
register(Pizza.class);
register(Ingredient.class);
}
#Override
public <T> T construct(Class<T> type)
{
return injector.getInstance(type);
}
#Override
public Ofy begin()
{
return new Ofy(super.begin());
}
}
Ofy doesn't have any Guice annotations at all...
public class Ofy extends ObjectifyWrapper<Ofy, OfyFactory>
{
// bunch of helper methods here
}
And finally test servlet where I'm trying to use injected field looks like this
public class TestServlet extends HttpServlet
{
#Inject Ofy ofy;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
ofy.save(new Pizza());
}
}
Ofy ofy is always null. It's never injected. And it's not injected because OfyFactory is never instantiated, its constructor is never called.
Could you please point what I'm doing wrong? Why my singleton is never created?
Thanks a lot.
Instead of defining TestServlet in the web.xml file, try deleting its mapping from web.xml and adding this line in the configureServlets() method:
serve("/test").with(TestServlet.class);
You may also need to bind TestServlet as a Singleton either by annotating the class with #Singleton or by adding a
bind(TestServlet.class).in(Singleton.class);
line to one of the modules.
What's happening is that Guice is not actually creating your servlet so it isn't able to inject the Ofy object. Guice will only create servlets if it is instructed to do so using a serve(...).with(...) binding. Any servlets defined in the web.xml are outside of Guice's control.
Related
There are REST web service based on Jersey 2.23.2 & Guice 3.0. To use Guice it is necessary to adjust the hk2-guice bridge (I'm using 2.5.0-b07). Everything works fine until I have tried to test the service using Jersey Test Framework. Can't configure hk2-guice bridge for tests.
My test:
public class SomeTest extends JerseyTestNg.ContainerPerClassTest {
#Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return new GrizzlyWebTestContainerFactory();
}
#Override
protected Application configure() {
return super.configure(); // cant't configure
}
#Test
public void test() {
Assert.assertEquals(1, 1);
}
}
I can't configure test in SomeTest.configure() just returning new JerseyConfiguration() (see below) because JerseyConfiguration's constructor expected ServiceLocator's object.
Even if it were possible to return an object of class `JerseyConfiguration` - I'm not sure that my test would work because some filters and listener are defined in the web.xml file.
How to configure the test taking into account all filters, listener and hk2-guice bridge?
Web service details
Dependencies section from build.gradle:
def jerseyVersion = '2.23.2'
def hk2Version = '2.5.0-b07'
def giuceVersion = '3.0'
dependencies {
compile "javax.servlet:javax.servlet-api:3.1.0"
//jersey
compile "org.glassfish.jersey.core:jersey-server:${jerseyVersion}"
compile "org.glassfish.jersey.containers:jersey-container-servlet:${jerseyVersion}"
//hk2
compile "org.glassfish.hk2:guice-bridge:${hk2Version}"
//guice
compile "com.google.inject:guice:${giuceVersion}"
compile "com.google.inject.extensions:guice-servlet:${giuceVersion}"
}
File web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>REST API App</display-name>
<listener>
<listener-class>com.example.core.JerseyGuiceServletContextListener</listener-class>
</listener>
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>jerseyFilter</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.example.core.JerseyConfiguration</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>jerseyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Class JerseyGuiceServletContextListener:
public class JerseyGuiceServletContextListener extends GuiceServletContextListener {
static Injector injector;
#Override
protected Injector getInjector() {
injector = Guice.createInjector(new JerseyServletModuleConfig());
return injector;
}
}
Class JerseyServletModuleConfig:
class JerseyServletModuleConfig extends ServletModule {
#Override
protected void configureServlets() {
bind(HeyResource.class).in(Scopes.SINGLETON);
}
}
Class JerseyConfiguration:
package com.example.core;
import com.google.inject.Injector;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.jersey.server.ResourceConfig;
import org.jvnet.hk2.guice.bridge.api.GuiceBridge;
import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;
import javax.inject.Inject;
import javax.servlet.ServletContext;
class JerseyConfiguration extends ResourceConfig {
#Inject
public JerseyConfiguration(ServiceLocator serviceLocator, ServletContext servletContext) {
packages("com.example.ws");
GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(JerseyGuiceServletContextListener.injector);
}
}
just overrode configureDeployment() & modified configure() methods in test
public class SomeTest extends JerseyTestNg.ContainerPerClassTest {
#Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return new GrizzlyWebTestContainerFactory();
}
#Override
protected Application configure() {
return new ResourceConfig()
.packages("com.example.ws")
.register(GensonCustomResolver.class);
}
#Override
protected DeploymentContext configureDeployment() {
Application application = configure();
return ServletDeploymentContext.builder(application)
.addListener(JerseyGuiceServletContextListener.class)
.addFilter(GuiceFilter.class, "guiceFilter")
.addFilter(ServletContainer.class, "jerseyFilter", new HashMap<String, String>(){{
put("javax.ws.rs.Application", JerseyConfiguration.class.getCanonicalName());
}})
.build();
}
#Test
public void test() {
Assert.assertEquals(1, 1);
}
}
& add web.xml to test classpath.
I build a web application with JSPs and in my servlet I have:
public class MyServlet extends HttpServlet {
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
init();
HttpSession session = request.getSession(true);
//more code...
}
}
Till now my serlvet is called, when the JSP page calls it like <a href="MyServlet..">. What I want is whenever the application starts, the servlet to be executed as well. I could have a button in my 1st page like "START" and there to call the servlet.. But, can I avoid this?
Whatever you want done on startup should be done by a class implementing ServletContextListener, so you should write such a class, for example:
public class MyContextListener
implements ServletContextListener{
#Override
public void contextDestroyed(ServletContextEvent arg0) {
//do stuff
}
#Override
public void contextInitialized(ServletContextEvent arg0) {
//do stuff before web application is started
}
}
Then you should declare it in web.xml:
<listener>
<listener-class>
com.whatever.MyContextListener
</listener-class>
</listener>
You can configure it in Tomcat's web.xml (or corresponding configuration files in similar servers), like below using the tag <load-on-startup> :
<servlet>
<servlet-name>MyOwnServlet</servlet-name>
<servlet-class>MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
In my point of view, a good way is to implement a Servlet Context Listener. It listens to application startup and shutdown.
public class YourListener implements javax.servlet.ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
}
public void contextDestroyed(ServletContextEvent sce) {
}
}
And then, you configure the listener in your web.xml () or with the #WebServletContextListener annotation.
I am pretty new to spring. I have dynamic web application project. Inside i have a servlet which is receive request. The request comes with a request no. Based on the no i will create a new object for appropriate request class and serve the request. Now i need to integrate with spring. i applied the below configuration,
WEB.XML (To load spring context)
<servlet>
<servlet-name>spring-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/docspring.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
In servlet
package com.receve;
#Controller
#WebServlet("/Recever")
public class Recever extends HttpServlet {
#Autowired
private ClassOne one;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
//request parameters receved and
if(req==1){
one.print();
}
}
public ClassOne getOne() {
return One;
}
public void setOne(ClassOne one) {
this.one = one;
}
}
In ClassOne
package com.operations;
#Component
public Class ClassOne{
public void print(){
//some statement;
}
}
In spring.xml
<context:annotation-config />
<context:component-scan base-package="com" />
But while running the application i am getting NullPointerexception while calling one.print() method. what is the proper way(configuration) to obtain this?
Thanks.
Remove extends HttpServlet and Autowiring is turned off by default, so the default autowiring mode for a bean is 'no'.
Could you possibly explain how I can get the ServletContext instance in my Application's sub-class? Is it possible? I have tried to do it like in the following snippet but it does not seem to work - the ctx is not set:
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
//...
#ApplicationPath("/")
public class MainApplication extends Application {
#Context ServletContext ctx;
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
//...
return classes;
}
}
web.xml:
<web-app ...>
<context-param>
<param-name>environment</param-name>
<param-value>development</param-value>
</context-param>
<filter>
<filter-name>jersey-filter</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>my.MainApplication</param-value>
</init-param>
</filter>
...
</web-app>
The problem is that I need to get context parameters from it. If there is another way, I would be grateful if somebody gave a hint.
I understand that Context annotation might not be purposed for this. Actually, I do not need ServletContext itself. If only I could get context params from web.xml, I would be absolutely happy.
Here is an example of what I really need:
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
public class MainApplication extends Application {
#Context ServletContext ctx;
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<Object>();
final String environment = ctx.getInitParameter("environment");
//final String environment = ... get context parameter from web xml
set.add(new AbstractBinder() {
#Override
protected void configure() {
bind(new BaseDataAccess(environment)).to(DataAccess.class);
}
});
//...
return set;
}
}
Thanks.
Since Jersey 2.5, ServletContext can be injected directly in constructor:
https://java.net/jira/browse/JERSEY-2184
public class MyApplication extends ResourceConfig {
public MyApplication(#Context ServletContext servletContext) {
// TODO
}
}
#Context can be made available on ResoureConfig by injecting it as a constructor parameter using #Context. Another way to access it is through an event handler.
Try the below code.
#ApplicationPath("...")
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(StartupHandler.class);
}
private static class StartupHandler extends AbstractContainerLifecycleListener {
#Context
ServletContext ctx;
#Override
public void onStartup(Container container) {
// You can put code here for initialization.
}
}
// ...
Injection happens when you enter service method. Check if this is a problem.
There is interesting statement in documentation for Jersey version 1.18 for class
com.sun.jersey.spi.container.servlet.ServletContainer
The servlet or filter may be configured to have an initialization
parameter "com.sun.jersey.config.property.resourceConfigClass" or
"javax.ws.rs.Application" and whose value is a fully qualified name of
a class that implements ResourceConfig or Application. If the concrete
class has a constructor that takes a single parameter of the type Map
then the class is instantiated with that constructor and an instance
of Map that contains all the initialization parameters is passed as
the parameter.
If my understanding is correct the following constructor must be invoced with "an instance of Map that contains all the initialization parameters"
public class ExampleApplication extends Application {
public ExampleApplication(Map initParams) {
}
...
}
Here is appropriate part of web.xml:
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>experiment.service.ExampleApplication</param-value>
</init-param>
</servlet>
But somehow it failed for me with the following message:
SEVERE: Missing dependency for constructor public
experiment.service.ExampleApplication(java.util.Map) at parameter
index 0
And for current version of Jersey (2.5.1) there are no such statement in documentstion:
https://jersey.java.net/apidocs/latest/jersey/org/glassfish/jersey/servlet/ServletContainer.html
You can use the ApplicationEventListener interface to get the ServletContext. After initialization has finished, you can 'catch' an ApplicationEvent and use the injected ServletContext to work with.
Works fine with: org.glassfish.jersey : 2.12
For additional versions, pls use comments - i dont know, sry.
Jersey Docs - 20.1.2. Event Listeners
Your MainApplication:
#ApplicationPath("/")
public class MainApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> set = new HashSet<Class<?>>();
set.add(MainApplicationListener.class);
return classes;
}
}
... or alternative MainResourceConfig (I prefer to use this one):
public class MainResourceConfig extends ResourceConfig {
public MainResourceConfig() {
register(MainApplicationListener.class);
}
}
And the ApplicationEventListener:
public class MainApplicationListener implements ApplicationEventListener {
#Context
private ServletContext ctx; //not null anymore :)
#Override
public void onEvent(ApplicationEvent event) {
switch (event.getType()) {
case INITIALIZATION_FINISHED:
// do whatever you want with your ServletContext ctx
break;
}
#Override
public RequestEventListener onRequest(RequestEvent requestEvent) {
return null;
}
}
Don't use #Context in your Application but in a Resource class.
#Path("/foos")
public class FooResource {
#Context
ServletContext ctx;
#GET
public Response getFoos() {
return Response.ok().build();
}
}
Is it possible to use Guice AOP to intercept an annotated method on a Jersey resource?
I have a successfully configured Guice integration working with Jersey with respect to Dependency Injection without any problems, however my configured Interceptor is not intercepting my annotated method at all.
web.xml
<listener>
<listener-class>my.package.GuiceConfig</listener-class>
</listener>
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
GuiceConfig configuration module
public class GuiceConfig extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets() {
bindInterceptor(Matchers.any(),
Matchers.annotatedWith(RequiredAuthority.class),
new AuthorisationInterceptor());
Map<String, String> params = new HashMap<String, String>();
params.put(JSP_TEMPLATES_BASE_PATH, "/WEB-INF/jsp");
params.put(FEATURE_FILTER_FORWARD_ON_404, "true");
params.put(PROPERTY_PACKAGES, "my.service.package");
filter("/*").through(GuiceContainer.class, params);
}
});
}
}
RequiredAuthority annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface RequiredAuthority {
String value();
}
AuthorisationInterceptor aspect
public class AuthorisationInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// Allow invocation to process or throw an appropriate exception
}
}
TempResource JAX-RS resource class
#Path("/temp")
public class TempResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
#RequiredAuthority("PERMISSION")
public String getTemp() {
// Return resource normally
}
}
Looks like configureServlets() isn't calling:
bind(TempResource.class);