home.


Tagged: servlet


Java Servlet: Servering static content

If you want to server static content – like PNGs, Javascript, etc – you need to explicitly tell your server this in its web.xml

Edit: I’ve changed this article due to an error and infelicitiy:

Add this file somewhere, and, voila, all the files in your res/ directory (in src/main/webapp/res in the gradle directory structure, that is) will be served.

@WebServlet("res/*")
public class ResourcesServlet extends HttpServlet {
  private static final long serialVersionUID = 1L;

  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    RequestDispatcher rd = getServletContext().getNamedDispatcher("default");
    HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
      public String getServletPath() {
        return "/res/";
      }
    };
    rd.forward(wrapped, resp);
  }
}
java java-servlet java-servlet-static-content

Servlets: Forwarding and Redirecting

If you want a 302 redirect, where res is a HttpServletResponse:

    res.sendRedirect("/YourContextPath/YourServletPath/PathToServlet");

The first part of the path will be the name your site has in the container. If you uploaded SomeWar file, and Tomcat give it that name to request, your context path will be that.

The next part is either blank or the path to your servlet. For example, if you url-pattern in your web.xml is /somepath/* then this will be ‘somepath’.

The final part is the path to your servlet.

If you want to transparently forward to another servlet, keeping the existing objects in your HttpServletRequest:

    RequestDispatcher disp = httpServletRequest.getRequestDispatcher("/YourServletPath/PathToServlet");
    disp.forward(req, res);

The url here is the same as before but without the context path.

servlet servlet-forwarding

Tomcat 7: Error pages for exceptions and error codes

You can specify an error page in your web folder for either an exception or a standard error code.

The below makes error404.html be shown each time an array out of bounds error occurs.

    <error-page>
       <!-- <error-code>404</error-code> -->
       <exception-type>java.lang.ArrayIndexOutOfBoundsException</exception-type>      
       <location>/error404.html</location>
    </error-page>  
servlet servlet-error-pages

Servlets: filtering

You can filter all servlet requests, including REST ones with Jersey, with a filter:

 public class HelloFilter implements Filter {
  @Override
  public void destroy() {
  }
  @Override
  public void doFilter(ServletRequest req, ServletResponse resp, 
           FilterChain chain) throws IOException, ServletException {
    chain.doFilter(req, resp);
  }
  @Override
  public void init(FilterConfig arg0) throws ServletException {
  }
 }

In the doFilter() method you call the rest of the filters on the chain to continue. Alternatively, you can just bail out there with something like:

  HttpServletResponse r = (HttpServletResponse) resp;
  r.setStatus(HttpServletResponse.SC_GONE);

The filter allows you do look at the request and response, do various things with the servletcontext or httpservletrequest the latterly called doGet or REST method will see such changes.

You can register this in your web.xml like so:

  <filter>
    <filter-name>Filter</filter-name>
    <filter-class>
      org.denevell.tomcat.filters.HelloFilter
    </filter-class>
  </filter>
  <filter-mapping>
    <filter-name>Filter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
servlet servlet-filters

Tomcat 7: REST services with Jersey

First download Apache Jersey. I downloaded the archive with the follow jars in them and placed them in /usr/share/tomcat7/lib and restarted tomcat.

 asm-3.1.jar
 jackson-core-asl-1.9.2.jar
 jackson-jaxrs-1.9.2.jar
 jackson-mapper-asl-1.9.2.jar
 jackson-xc-1.9.2.jar
 jersey-client-1.17.jar
 jersey-core-1.17.jar
 jersey-json-1.17.jar
 jersey-server-1.17.jar
 jersey-servlet-1.17.jar
 jettison-1.1.jar
 jsr311-api-1.1.1.jar

I had to increase Tomcat’s memory size to avoid out of memory errors. I put this line at the top of /usr/share/tomcat7/bin/catalina.sh

JAVA_OPTS=‘-Xmx512m’

Then you can create a simple POJO with the @XMLRootElement annotation. This will allow it to be converted into JSON for the REST services (Yes, it does say XMLRoot…)

 @XmlRootElement
 public class JsonObject {
   private String str;
   public String getStr() {
      return str;
   }
   public void setStr(String str) {
      this.str = str;
   }
 }

Next you can create your REST class by creating a class like the following:

 @Path("/rest")
 public class Rest {
   @Context
   UriInfo info;
   @Context
   Request request;
   @Context
   ServletContext context;
   ...
   @PostConstruct
   public void init() {
   }
 }

This defines a new rest api that will have the ‘/rest/’ prefix. The @Context annotation injects UriInfo and details of the actual request that you may want to use. You can access these methods in a method annotated with @PostConstruct

The real logic comes in the method signatures:

 @GET
 @Path("/r")
 @Produces(MediaType.TEXT_PLAIN)
 public String sayRest() {
   return "rest";
 }

 @GET
 @Path("/r")
 @Produces(MediaType.TEXT_HTML)
 public String sayRest() {
   return "html text";
 }

This will produce different responses, depending on whether the client is asking for plain text or html. The path is /rest/r.

This produces and consumes the POJO object we created above:

 @PUT
 @Path("/j")
 @Produces(MediaType.APPLICATION_JSON)
 @Consumes(MediaType.APPLICATION_JSON)
 public JsonObject intakeJson(JsonObject intake) {
   intake.setStr("intaken: " + intake.getStr());
   return intake;
 }

You can either pass in query parameters or path parameters:

 @GET
 @Path("/r")
 @Produces(MediaType.TEXT_HTML)
 public String sayRestInHtml(@QueryParam("q") String q) {
   return "rest: "+q;
 }  

 @GET
 @Path("/r/{q}")
 @Produces(MediaType.TEXT_HTML)
 public String sayRestInHtmlWithPath(@PathParam("q") String q) {
   return "rest with param: "+q;
 }

You’d access these via /rest/r?q=sdfsdf and /rest/r/sdfsdf.

You can craft HTTP responses using:

 Response.created(info.getAbsolutePath()).build();

This creates a CREATED HTTP response with the absolute path gained from the UriInfo via the @Context annotation above.

You now need to edit your web.xml to activate this servlet:

  <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>org.denevell.tomcat.rest</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>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>

This is a normal servlet mapping except the servlet-class is a Jersey class, and the init-param state where the REST class is. And the com.sun.jersey.api.json.POJOMappingFeature ensures you use Jackson instead of the standard Java implementation JaxB (which you don’t want).

You final urls will be such as

 /rest/rest/r
jersey java REST servlet tomcat

Page 1 of 2
Next