Injecting objects into servlet classes with JAX-RS - java

I am pretty new to this whole servlet thing, so please correct me if I mix or use incorrect/confusing terms for things. I am however writing a blog ping server in Java using JAX-RS (Jersey), and I'm facing a problem where I have one servlet class that accepts REST input, and another servlet class that lists the same content.
Right now they are sharing their objects through a class that looks like this:
public class BlogPingUtils {
private static final String LIST_OF_CHANGES = "listOfChanges";
public static List<PingInfo> getListOfChanges(ServletContext context) {
List<PingInfo> listOfChanges = (List<PingInfo>)context.getAttribute(LIST_OF_CHANGES);
if(listOfChanges == null) listOfChanges = new ArrayList<PingInfo>();
return listOfChanges;
}
public static void setListOfChanges(ServletContext context, List<PingInfo> listOfChanges) {
context.setAttribute(LIST_OF_CHANGES, listOfChanges);
}
}
This works in a small-scale development environment, but it feels dirty and probably wouldn't work in a heavy-duty production environment because of concurrency issues and such. Also it's not very flexible. What I would like to do is have an interface that would have methods for reading and writing data. Then I would inject an object of a class that implements this interface into these two servlets, so that they are actually using the same object for interacting with the data. This would then be backed by a synchronized list or database transactions or something.
What would be the preferred way to do this? Is it possible? My web.xml is very simple right now and looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<display-name>Blog Ping</display-name>
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.vrutberg.blogping</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

Rather than a static class, I would design an Interface with your getters and setters, than create an implementation class.
I would then register the singleton (or pool depending on your needs) implementation class with a thread-safe central repository visible by both servlets, such as JNDI.
The servlets query the central repository and 'cast' to the interface.

Related

Intercept JAX-RS Request: Register a ContainerRequestFilter with tomcat

I am trying to intercept a request to my JAX-RS webservice by a ContainerRequestFilter. I want to use it with a custom annotation, so I can decorate certain methods of the webservice. This should enable me to handle requests to this methods based on the information whether they are made on a secure channel
or not, before the actual method is executed.
I tried different approaches, searched several posts and then implemented mostly based on the answer by Alden in this post.
But I can't get it working.
I have a method test in my webservice decorated with my custom annotation Ssl.
#POST
#Path("/test")
#Ssl
public static Response test(){
System.out.println("TEST ...");
}
The annotation looks like this:
#NameBinding
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
public #interface Ssl {}
Then I setup a filter implementation
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
#Ssl
#Provider
public class SslInterceptor implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
System.out.println("Filter executed.");
}
}
But the filter is never executed nor there occur any error messages or warnings. The test method runs fine anyway.
To resolve it, I tried to register the filter in the web.xml as described here.
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
<param-value>com.sun.jersey.api.core.PackagesResourceConfig</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.my.packagewithfilter</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.my.packagewithfilter.SslInterceptor</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.my.packagewithfilter</param-value>
</init-param>
</servlet>
But that also doesn't work. What am I missing? Any ideas how to make that filter work? Any help is really appreciated!
You're using JAX-RS 2.0 APIs (request filters, name binding, ...) in your classes but Jersey 1 proprietary init params in your web.xml (package starting with com.sun.jersey, Jersey 2 uses org.glassfish.jersey). Take a look at this answer and at these articles:
Registering Resources and Providers in Jersey 2
Binding JAX-RS Providers to Resource Methods
Just compiling the answer from Michael Gajdos to help someone who do not want open more tabs:
When you are using Jersey-2 you must use the follow configuration to register your filter into the web.xml
jersey.config.server.provider.classnames
instead of
com.sun.jersey.spi.container.ContainerRequestFilters (jersey-1x)
<!-- This is the config needed -->
<servlet>
//...
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>com.your_package_path.yourClassFilter</param-value>
</init-param>
//...
</servlet>
Have a look at this blog post for the more 'classical' approaches (without using the annotation)

Multiple endpoints with Resteasy

I have two separate handfuls of REST services in one application. Let's say a main "people" service and a secondary "management" service. What I want is to expose them in separate paths on the server. I am using JAX-RS, RESTEasy and Spring.
Example:
#Path("/people")
public interface PeopleService {
// Stuff
}
#Path("/management")
public interface ManagementService {
// Stuff
}
In web.xml I currently have the following set-up:
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/public</param-value>
</context-param>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/public/*</url-pattern>
</servlet-mapping>
The PeopleService and ManagementService implementations are just Spring beans.
Above web.xml configuration will expose them both on /public (so having /public/people and /public/management respectively).
What I want to accomplish is to expose the PeopleService on /public, so that the full path would become /public/people and expose the ManagementService on /internal, so that its full path would become /internal/management.
Unfortunately, I cannot change the value of the #Path annotation.
How should I do that?
actually you can. After few hours of debugging I came up with this:
1) Declare multiple resteasy servlets in your web.xml (two in my case)
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/openrest</param-value>
</init-param>
<init-param>
<param-name>resteasy.resources</param-name>
<param-value>com.mycompany.rest.PublicService</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>private-resteasy-servlet</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/protectedrest</param-value>
</init-param>
<init-param>
<param-name>resteasy.resources</param-name>
<param-value>com.mycompany.rest.PrivateService</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>private-resteasy-servlet</servlet-name>
<url-pattern>/protectedrest/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/openrest/*</url-pattern>
</servlet-mapping>
Please pay attention to the fact that we initialize personal resteasy.servlet.mapping.prefix and resteasy.resources for each our servlet.
Please don't forget to NOT include any botstrap classes as filters or servlets! And disable autoscan as well.
2) Create a filter that cleans up application from the RESTeasy's global information that it saves in context:
public class ResteasyCleanupFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
request.getServletContext().setAttribute(ResteasyProviderFactory.class.getName(), null);
request.getServletContext().setAttribute(Dispatcher.class.getName(), null);
chain.doFilter(request, response);
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
}
Register it for any request to your services (here I used it for all requests for simplisity):
<filter>
<filter-name>CleanupFilter</filter-name>
<filter-class>com.mycompany.ResteasyCleanupFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CleanupFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Thats it!Now you have two different REST services which lays under different prefixes : /openrest which meant to service all public requests and /protectedrest that takes care about all the private stuff in the app.
So why does it work (or why it does not work otherwise)?
When you call openrest instance for the first time it tries to initalize itself and when done saves the state in the global servletContext like this :
servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory());
servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher());
And if you will let it be your call to your second /protectedrest will get the SAME configuration! That is why you need to clean up this information some where. That is why we used our CleanupFilter which empty the context so brand new rest servlet could initialize itself with all the init parameters we declared.
This is a hack, but it does the trick.
This solution was tested for RESTEasy 2.3.6
EDITED
Works with 3.0.9.final as well!
AFAIK, you cannot have multiple servlet mappins for your JAX-RS implementation.
What you could do is: map RESTEasy to '/' (or '/api' for example if your application has other resources to serve and you don't want the JAX-RS part to interfere), then have the following #Path annotations:
#Path("/public/people")
public interface PeopleService {
// Stuff
}
#Path("/internal/management")
public interface ManagementService {
// Stuff
}

Jersey doesn't honour required=true

I have following class annotated with JAX-RS:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Blub {
#XmlElement (required = true)
private String author;
with getter/and setter. I am using this object as parameter to a method:
#Path("/createBlub")
#POST
public ReplyObject createBlub(Blub blub) {
try {
...
//process here
return ReplyObject.success("blub", result);
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
I am expecting Jersey to throw an Exception if in the parameter blub object the field author is not set. However, Jersey doesn't seem to care for the required attribute. I remember that it worked in other projects, but don't see the difference.
I am using jersey 1.12 without anything else:
<servlet>
<servlet-name>JerseyServletContainerAdmin</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>xxx.yyy.zzz.admin</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.config.feature.DisableXmlSecurity
</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JerseyServletContainerAdmin</servlet-name>
<url-pattern>/admin/*</url-pattern>
</servlet-mapping>
thanks in advance
regards
Leon
Jersey is using JAXB for marshaling and unmarshalling which just means transforming the data into a Java object. If you want validation you have to do it yourself.
Proposed solutions for that include creating a custom MessageBodyReader to add the validation on unmarshalling or creating a more reusable implementation by writing a custom ContextResolver as described in this post: Jersey JAX-RS and JAXB Schema Validation.
JSR303 support would have been ideal for this sort of thing (working nicely with JSON data) but looks like that will be available only in 2.x. It should be possible though to adapt the solution from the above post and use JSR303.
And if the implementation gets too complicated you can always let Jersey create the object and then first-thing-first call some validation method of yours on the object, which normally shouldn't be more than a one liner, something like:
#Path("/createBlub")
#POST
public ReplyObject createBlub(Blub blub) {
ValidationUtils.<Blub>validate(blub);
...

CORS support for a Jersey app with WADL

I have a Java Application in Eclipse. Using Jersey, it successfully creates a webpage locally (localhost).
The problem is I have a javascript on another server trying to access that webpage and it throws a CORS error.
Is there any built-in CORS support for Jersey/WADL applications?
I've tried creating a Jersey filter that supports CORS - following these instructions. No luck. :( I'm looking into this, but am unsure if it is the right choice for what I want to do.
Essentially, my header is currently this:
But I want it to look something like this:
Thanks all!
EDIT:
As per jgm's suggestion, I have created a filter (called CORSFilter) and added the requesite dependencies. Am I registering the filter correctly in the web.xml file?
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>createJPA</display-name>
<servlet>
<servlet-name>Jersey Root REST Service</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Root REST Service</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class>model.producer.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Here is a simple CORS filter for jersey, adapted from the Weald Technology utilities:
import com.google.inject.Inject;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
import com.wealdtech.jersey.CORSConfiguration;
/**
* Filter to handle cross-origin resource sharing.
*/
public class CORSFilter implements ContainerResponseFilter
{
private static final String ACAOHEADER = "Access-Control-Allow-Origin";
private static final String ACRHHEADER = "Access-Control-Request-Headers";
private static final String ACAHHEADER = "Access-Control-Allow-Headers";
private static final String ACAMHEADER = "Access-Control-Allow-Methods";
private static final String ACACHEADER = "Access-Control-Allow-Credentials";
private final transient CORSConfiguration configuration;
#Inject
public CORSFilter(final CORSConfiguration configuration)
{
this.configuration = configuration;
}
#Override
public ContainerResponse filter(final ContainerRequest request, final ContainerResponse response)
{
response.getHttpHeaders().add(ACAOHEADER, this.configuration.getOrigin());
final String requestHeaders = request.getHeaderValue(ACRHHEADER);
response.getHttpHeaders().add(ACAHHEADER, requestHeaders);
response.getHttpHeaders().add(ACAMHEADER, this.configuration.getAllowedMethods());
response.getHttpHeaders().add(ACACHEADER, this.configuration.allowCredentials());
return response;
}
}
For the configuration you can either use the existing configuration setup or incorporate it in to your own system. Note that this uses Google Guice to inject the configuration, if you aren't using Guice you'll need to do this manually.

Start class right after deployment, not at session start for JSF

For a web application I make use of JSF 1.2 and Facelets.
The problem is that we now do the initialisation via a singleton pattern and that takes about 5-15 seconds because it read in data files (we are not using a database). This happens when the first user browses to the corresponding web page (the 2nd and other users don't have this delay).
I would like to have this singleton initialised right after deployment. How can I do this? I've tried to add an application bean but it does not get called. I've also tried to add a servlet as followings:
<servlet>
<description>MyApplicationContextListener Servlet</description>
<display-name>MyApplicationContextListener Servlet</display-name>
<servlet-name>MyApplicationContextListener</servlet-name>
<servlet-class>mydomain.beans.MyApplicationContextListener</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<listener>
<listener-class>mydomain.beans.MyApplicationContextListener</listener-class>
</listener>
with the following code:
package mydomain.beans;
import javax.servlet.ServletContextEvent;
public class MyApplicationContextListener {
public void contextInitialized(ServletContextEvent event) {
System.out.println("MyApplicationContextListener.contextInitialized started");
}
public void contextDestroyed(ServletContextEvent event) {
System.out.println("MyApplicationContextListener.contextInitialized stopped");
}
}
An example including changes needed in web.xml and/or faces-config.xml would be nice!
How about using a ServletContextListener ? Its contextInitialized(..) method will be called at the moment the context is initialized. It's mapped in web.xml like this:
<listener>
<listener-class>com.example.MyServletContextListener</listener-class>
</listener>
Also, (not sure this would work), you can configure your faces-servlet to be loaded on startup.:
<load-on-startup>1</load-on-startup>
Clarification: For the listener approach, your listener must implement the ServletContextListener:
public class MyServletContextListener implements ServletContextListener { .. }

Categories

Resources