How to get Google Guice working with JaxRS (Jersey) - java

I have a basic JAXRS service that I can expose easily, but for once I wish to use a dependency injection API and I suspect Google Guice will be one of the best. With this in mind, I have tried to integrate it, but the documentation is a little heavy going and I've been having to hunt around to try and find the right combination of
Web.xml
Context Listener (should I use ServletContainer or GuiceContainer)
Service
Whether to annotate the service with #Singleton or #Request or nothing (should I annotate with #Singleton - docs say I should but then says it defaults to request scope)
Whether to annotate the constructor parameters with #InjectParam
But currently I get errors from Google Guice and they change based on whether I use the #InjectParam annotation or not.
If I annotate with #InjectParam then I get
Mar 29, 2013 9:52:04 PM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: The class com.hillingar.server.dao.interfaces.UserDao is an interface and cannot be instantiated.
SEVERE: Missing dependency for constructor public com.hillingar.server.SessionUtility(com.hillingar.server.dao.interfaces.UserDao) at parameter index 0
If I don't annotate then I get
Mar 29, 2013 9:54:59 PM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 0
SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 1
This is my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">
<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>
<listener>
<listener-class>com.hillingar.server.ServletContextListener</listener-class>
</listener>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>
This is my ServletContextListener
package com.hillingar.server;
import java.util.logging.Logger;
import javax.servlet.ServletContextEvent;
import com.google.inject.Guice;
import com.google.inject.Singleton;
import com.hillingar.server.dao.jdbcImpl.UserJdbc;
import com.hillingar.server.dao.interfaces.UserDao;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import com.sun.jersey.spi.container.servlet.ServletContainer;
public class ServletContextListener implements javax.servlet.ServletContextListener {
Logger logger = Logger.getLogger(this.getClass().getName());
#Override
public void contextDestroyed(ServletContextEvent arg0) {
}
/*
* Covered in URL
* https://code.google.com/p/google-guice/wiki/ServletModule
*/
#Override
public void contextInitialized(ServletContextEvent arg0) {
// Note the user of JerseyServletModule instead of ServletModule
// otherwise the expected constructor injection doesn't happen
// (just the default constructor is called)
Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets() {
/*
* Note: Every servlet (or filter) is required to be a
* #Singleton. If you cannot annotate the class directly,
* you must bind it using bind(..).in(Singleton.class),
* separate to the filter() or servlet() rules.
* Mapping under any other scope is an error. This is to
* maintain consistency with the Servlet specification.
* Guice Servlet does not support the
* deprecated SingleThreadModel.
*/
bind(SecurityFilter.class).in(Singleton.class);
bind(ServletContainer.class).in(Singleton.class);
/*
* Filter Mapping
*
* This will route every incoming request through MyFilter,
* and then continue to any other matching filters before
* finally being dispatched to a servlet for processing.
*
*/
// SECURITY - currently disabled
// filter("/*").through(SecurityFilter.class);
/*
* Registering Servlets
*
* This registers a servlet (subclass of HttpServlet) called
* ServletContainer, the same one that I would have used in
* the web.xml file, to serve any web requests with the
* path /rest/* i.e. ...
*
<servlet>
<servlet-name>ServletAdaptor</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ServletAdaptor</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
*/
serve("/rest/*").with(ServletContainer.class); // JAX-RS
// Using this and it starts bitching about
// com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.
// So presumably wants an Application class that enumerates
// all my services?
//serve("/rest/*").with(GuiceContainer.class);
/*
* Bindings
*/
bind(UserDao.class).to(UserJdbc.class);
bind(SessionUtility.class);
}
});
}
}
This is my UserService
package com.hillingar.server.rest;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;
import com.hillingar.server.SessionUtility;
import com.hillingar.server.dao.interfaces.UserDao;
import com.hillingar.server.model.User;
import com.hillingar.server.model.dto.AuthenticationResponse;
#Path("/user")
#Produces("application/json")
#Consumes({"application/xml","application/json"})
#Singleton // <-- Added Singleton here
public class UserService {
private UserDao userDao;
private SessionUtility sessionManager;
/*
Error if I annotate with #InjectParam...
Mar 29, 2013 9:52:04 PM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: The class com.hillingar.server.dao.interfaces.UserDao is an interface and cannot be instantiated.
SEVERE: Missing dependency for constructor public com.hillingar.server.SessionUtility(com.hillingar.server.dao.interfaces.UserDao) at parameter index 0
Error If I don't annotate at all...
Mar 29, 2013 9:54:59 PM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 0
SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 1
(both output Initiating Jersey application, version 'Jersey: 1.13 06/29/2012 05:14 PM')
*/
#Inject
public UserService(UserDao userDao, SessionUtility sessionManager) {
this.userDao = userDao;
this.sessionManager = sessionManager;
}
#GET
public List<User> test(#Context HttpServletRequest hsr) {
// USER DAO IS ALWAYS NULL - CONSTRUCTOR INJECTION NOT WORKING
User loggedInUser = userDao.findBySessionId(hsr.getSession().getId());
...
return users;
}
}

Changed the ServletContextListener to
package com.hillingar.server;
import java.util.logging.Logger;
import javax.servlet.ServletContextEvent;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.google.inject.servlet.GuiceServletContextListener;
import com.hillingar.server.dao.jdbcImpl.UserJdbc;
import com.hillingar.server.dao.interfaces.UserDao;
import com.hillingar.server.rest.UserService;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import com.sun.jersey.spi.container.servlet.ServletContainer;
// (1) Extend GuiceServletContextListener
public class ServletContextListener extends GuiceServletContextListener {
Logger logger = Logger.getLogger(this.getClass().getName());
// (1) Override getInjector
#Override
protected Injector getInjector() {
return Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets() {
bind(SecurityFilter.class).in(Singleton.class);
bind(UserService.class);// .in(Singleton.class);
bind(ServletContainer.class).in(Singleton.class);
// (2) Change to using the GuiceContainer
serve("/rest/*").with(GuiceContainer.class); // <<<<---
bind(UserDao.class).to(UserJdbc.class);
bind(SessionUtility.class);
}
});
}
}

Related

Java: execute method when .war file is deployed

I want to execute some methods as soon as the .war file is deployed by Tomcat or JBoss, how can I do it?
I tried ServletContextListener but it's not working.
Thanks.
Have you tried adding your methods in a Servlet and then run it on startup
In your Web.xml:
<servlet>
<servlet-name>YourServlet</servlet-name>
<servlet-class>com.your.domain.YourServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
OK I resolved this, this works with JBoss:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Startup;
import javax.ejb.Singleton;
#Singleton
#Startup
public class InitializerEjb {
#PostConstruct
public void init() {
SMTPServer smtp_server = SMTPServer.getInstance();
smtp_server.start();
}
}
And this works with Tomcat:
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
#WebListener
public class Initializer implements ServletContextListener {
#Override
public final void contextInitialized(final ServletContextEvent sce) {
SMTPServer smtp_server = SMTPServer.getInstance();
smtp_server.start();
}
}

How to map all calls to *.html in a springboot controller?

I am trying to serve dynamic html pages with Thymeleaf template and springboot api. This is the scenario that I want to achieve.
When someone makes the following request: hostname/client then the application would return a Json object on the other hand if someone makes this request: hostname/client.html, this request is catch in a different controller so that I can manipulate the view that will be returned.
Client Controller
This class is working as expected, it is returning a Json Object
#RestController
public class ClientController {
#Autowired
public ClientService clientServiceImp;
#RequestMapping("/client")
public Client get(#RequestParam(value="name", defaultValue="World") String name){
return clientServiceImp.getClient(name);
}
}
Home Controller
This class's method does not map calls to *.html
#Controller
public class HomeController {
#RequestMapping(value={"/*.html"}, produces="text/html")
public String getIndex(Model model, HttpServletRequest request){
// I will set here the thymeleaf fragment location based on the resource requested.
return "index";
}
*This is the error I am receiving after calling hostname/client.html
Whitelabel Error Page
*This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Jan 25 16:04:56 BRST 2016
There was an unexpected error (type=Not Acceptable, status=406).
Could not find acceptable representation**
Springboot basic configuration
#SpringBootApplication(scanBasePackages = {"com.serviceira"})
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
It is important to point that I did not set any other configuration for the application. Thanks in advance for the help.
The reason ClientController is working properly is because you marked it as a #RestController, you are telling Spring to write the response to the html page directly.
However, your HomeController is not finding the mapping because you have not set up the servlet mapping yet.
If you are in Java config:
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("*.html"); // HERE YOU ARE SETTING THE .html mapping
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("your package name here");
return context;
}
}
If you are using XML, besides setting up your servelt, you need a mapping like this:
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/servlet-config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
Problem Solved
I solved the problem by adding different "produces" argument for for each controller's RequestMapping annotation, besides to change the value from the getIndex from /.html to .html.
HomeController
#RequestMapping(value={"*.html"}, produces="text/html")
ClientController
#RequestMapping(value="/client" produces="application/json")
Now, every request that contains *.html is received and treated by the getIndex method from the HomeController class and the others are handled by its own controller. For example:
request: GET: localhost/client
Controller
#RequestMapping(value="/client" produces="application/json")
public Client get(#RequestParam(value="name", defaultValue="World") String name){
return clientServiceImp.getClient(name);
}
request: GET: localhost/client.html
Controller
#RequestMapping(value={"*.html"}, produces="text/html")
public String getIndex(Model model, HttpServletRequest request){
// I will set here the thymeleaf fragment location based on the resource requested.
return "index";
}

Get ServletContext in Application

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();
}
}

Share variables between JAX-RS requests

I have what I think is a very basic question about JAX-RS but I somehow can't easily find the answer.
I am trying to refactor a REST service which uses a "standard" Javax servlet -- routing requests to methods by hand -- into an "cleaner" JAX-RS implementation. The current application sets some variables during the servlet init(). It assigns those as attributes of the HttpServlet class so they are available during each doGet() and can be passed as parameters to request processing methods. For clarity, one of these is a ConcurentHashMap that acts as a cache.
Now, with JAX-RS, I can extend Application to set my resource classes. I can also use the #Context annotation in each resource implementation to inject things like ServletContext when processing a request. But I do not know how to similarly inject variables set during application initialization.
I am using the Apache Wink 1.3.0 implementation of JAX-RS.
You can use a listener for init the cache and set to the context as attribute before the web application start. something like the following:
package org.paulvargas.shared;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class CacheListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
Map<String, String> dummyCache = new HashMap<String, String>();
dummyCache.put("greeting", "Hello Word!");
ServletContext context = sce.getServletContext();
context.setAttribute("dummyCache", dummyCache);
}
public void contextDestroyed(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
context.removeAttribute("dummyCache");
}
}
This listener is configured in the web.xml.
<listener>
<listener-class>org.paulvargas.shared.CacheListener</listener-class>
</listener>
<servlet>
<servlet-name>restSdkService</servlet-name>
<servlet-class>
org.apache.wink.server.internal.servlet.RestServlet
</servlet-class>
<init-param>
<param-name>applicationConfigLocation</param-name>
<param-value>/WEB-INF/application</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>restSdkService</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
You can use the #Context annotation for inject the ServletContext and retrieving the attribute.
package org.apache.wink.example.helloworld;
import java.util.*;
import javax.servlet.ServletContext;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import org.apache.wink.common.model.synd.*;
#Path("/world")
public class HelloWorld {
#Context
private ServletContext context;
public static final String ID = "helloworld:1";
#GET
#Produces(MediaType.APPLICATION_ATOM_XML)
public SyndEntry getGreeting() {
Map<String, String> dummyCache =
(Map<String, String>) context.getAttribute("dummyCache");
String text = dummyCache.get("greeting");
SyndEntry synd = new SyndEntry(new SyndText(text), ID, new Date());
return synd;
}
}

Instantiate value only once in a Bean

I'm new to using Servlets so please forgive me if I use incorrect terminology. I have an Object called "Provider" in JSF Bean Class "Detector" which needs to be instantiated once and then can be used for all other requests. I've done some searching and found the ServletContextListener interface which seems to do what I need. Ive mentioned it in my web.xml file like so:
<listener>
<listener-class>
p1.ContextListener
</listener-class>
</listener>
and the class looks like this:
package p1;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ContextListener implements ServletContextListener{
#Override
public void contextInitialized(ServletContextEvent sce) {
Detector.startProvider();
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
Provider.dispose();
}
}
And here is my Detector Class:
package p1;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
#ManagedBean
public class Detector{
private static Provider p;
FacesContext context;
String userAgent;
public Detector() {
context = FacesContext.getCurrentInstance();
}
public String getValue() {
return p.getValue();
}
public String getUserAgent() {
return ((HttpServletRequest) context.getExternalContext().getRequest()).getHeader("User-Agent");
}
public static void startProvider(){
p = Creater.create();
}
}
My code all works, but the only way that seems right to me is to have the Provider Object as a static but that seems like a bad idea in an Bean that will be used for different requests. My question is whether it is right to have the Provider Object as a static?
Using "static" is a bad idea. If you want an object in your servlet to be shared between all the HTTP requests processed by this servlet then simply made it a field of your servlet class. The best place for initialization of that field variable is init() method.
public class MyServlet extends HttpServlet {
private MyProdiver provider;
public void init() throws ServletException {
this.provider = new MyProdiver();
// do init
}
}
Unless your servlet class implements SingleThreadModel there is only one servlet instance per servlet declaration in your deployment descriptor (web.xml)
I found the answer I need on this question JSF initialize application-scope bean when context initialized. I set the Provider Object as an attribute of the ServletContextEvent in my "ContextListener" and retrieved it in my Detector class from my FacesContext Object "context". (This is shown in more detail in the accepted answer of the link provided)

Categories