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;
}
}
Related
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";
}
I'm trying to implement an application with several uis - vaadin, jsp and etc.
It was working with simple jsp but then I decided to use vaadin as ui.
I've created vaadin servlet(and spring servlet left too).
My web.xml looks like this
<?xml version="1.0"?>
<web-app 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"
version="2.5">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/pmc-web-context.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>another-pmc-servlet</servlet-name>
<servlet-class>com.vaadin.server.VaadinServlet</servlet-class>
<init-param>
<param-name>UI</param-name>
<param-value>com.xxxx.app.pmc.vaadin.PmcUi</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>pmc-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>another-pmc-servlet</servlet-name>
<url-pattern>/VAADIN/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>pmc-servlet</servlet-name>
<url-pattern>/JSP/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>/WEB-INF/jsp/*</url-pattern>
</servlet-mapping>
</web-app>
I've created vaadin table component and adjusted it for my needs. I used autowiring for service.
package com.xxxx.app.pmc.vaadin.components;
import com.xxxx.app.pmc.model.Project;
import com.xxxx.app.pmc.service.project.ProjectService;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.ui.Table;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
#Component("projectTable")
public class ProjectTable extends Table {
private static final String CAPTION = "Projects";
private static final String[] headers = { "Project name", "Project owner", "ID" };
#Autowired
private ProjectService projectService;
public Table createTable() {
this.setContainerDataSource(projectDatasource());
this.setVisibleColumns(headers);
this.setSelectable(true);
this.setImmediate(true);
return this;
}
public IndexedContainer projectDatasource() {
IndexedContainer indexedContainer = new IndexedContainer();
for(String header: headers) {
indexedContainer.addContainerProperty(header, String.class, "");
}
List<Project> projects = projectService.findAllProjects();
for(int i = 0; i < projects.size(); i++) {
Object id = indexedContainer.addItem();
Project item = projects.get(i);
indexedContainer.getContainerProperty(id, headers[0]).setValue(item.getProjectName());
indexedContainer.getContainerProperty(id, headers[1]).setValue(item.getProjectOwner());
indexedContainer.getContainerProperty(id, headers[1]).setValue(item.getProjectId());
}
return indexedContainer;
}
}
ProjectService is a spring bean too.
#Service("projectService")
public class ProjectService {
#Autowired
private ProjectRepository projectRepository;
public void insertProject(Project project) {
projectRepository.store(project);
}
public List<Project> findAllProjects() {
return projectRepository.getAllItems();
}
public Project getProject(String id) {
return projectRepository.get(id);
}
}
ProjectRepository is another spring bean. It uses SqlSessionTemplate bean from MyBatis.
#Repository("projectRepository")
public class ProjectRepository implements IRepository<Project> {
private static final String STORE_PROJECT = "Project.insertProject";
private static final String GET_PROJECT_BY_ID = "Project.getProjectById";
private static final String GET_PROJECT_LIST = "Project.getProjectList";
#Autowired
private SqlSessionTemplate sqlSessionTemplate;
#Override
public void store(Project object) {
sqlSessionTemplate.insert(STORE_PROJECT, object);
}
#Override
public Project get(String id) {
return sqlSessionTemplate.selectOne(GET_PROJECT_BY_ID, id);
}
#Override
public List<Project> getAllItems() {
return sqlSessionTemplate.selectList(GET_PROJECT_LIST);
}
}
When I wrote an application using spring controller(using JSP) - it was working fine.
But when I added vaadin - JSP stopped working and vaadin application throws NullPointerException for ProjectService, ProjectRepository... all the beans I use.
What is the problem?
All my context xml context files are simple.
<import resource="classpath:com/xxxx/app/pmc/pmc-service-context.xml"/>
<context:component-scan base-package="com.xxx.app.pmc"/>
And my pmc-web-context.xml has this lines too.
<mvc:annotation-driven/>
<context:annotation-config/>
It was working fine with JSP so I think the problem is not with spring declarations itself but with integration of spring into vaadin of mine.
How to resolve it?
When I for example created ProjectTable object manually - it throws NullPointerException for ProjectService. When I create ProjectService manually - it throws NullPointerException for ProjectRepository and so on. It seems autowiring simply doesn't work.
P.S forgot to add my UI code
package com.xxxx.app.pmc.vaadin;
import com.xxxx.app.pmc.vaadin.components.ProjectTable;
import com.vaadin.annotations.Title;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import org.springframework.stereotype.Component;
#Title("PMC")
#Component("pmcVaadin")
public class PmcUi extends UI {
#Override
protected void init(VaadinRequest request) {
VerticalLayout mainLayout = new VerticalLayout();
ProjectTable projectTable = new ProjectTable();
mainLayout.addComponent(projectTable.createTable());
mainLayout.setSizeFull();
setContent(mainLayout);
}
}
Like you suspected your Spring beans are created, but in application and Spring's servlet contexts. Your Vaadin servlet has no access to any of those.
For detailed (manual) solution with code either check Vaadin's wiki or search for a proper Vaadin's add ons that will do the job for you (personally I recommend SpringVaadinIntegration).
The typical idea is to programmatically pass Vaadin servlet to one of Spring's utility classes (eg.: WebApplicationContextUtils) and retrieve application context (loaded by ContextLoaderListener). If you need servlet context, then from code examples I have seen, you do the same as above, but additionally manually read context (eg.: using XmlWebApplicationContext) and set application context as it's parent.
Pretty easy option is to use Configurable support. It'll do some aspect magic and all java objects annotated by #Configurable will be automatically integrated into Spring. You can find more details in Spring documentation. Also please note that to represent context information in GUI you will have to use session beans. That causes the problem with session size, and in bigger applications makes it impossible to cluster.
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();
}
}
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);
}
});
}
}
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)