CORS-Compliant REST API with Jersey and ContainerResponseFilter

Posted: février 27th, 2012 | Author: | Filed under: Dev, Java, Java EE, JAX-RS, Jersey, Tools, Tutorial | Tags: , , , , , , , | 20 Comments »

I recently had to consume a REST API with a cross-domain call and i’ve found a solution in this excellent blog post from Kdecherf.

The problem with this implementation is that you need to call a method for each of your api’s methods.

So with the ContainerResponseFilter interface, you could do this for ALL of your methods … transparently.

1 – Configure web.xml

You just have to add an init-param to your Jersey Servlet

<init-param>
	<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
	<param-value>com.ezakus.api.web.security.ResponseCorsFilter</param-value>
</init-param>

2 – ResponseCorsFilter

package com.ezakus.api.web.security;

import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;

public class ResponseCorsFilter implements ContainerResponseFilter {

	@Override
	public ContainerResponse filter(ContainerRequest req, ContainerResponse contResp) {

		ResponseBuilder resp = Response.fromResponse(contResp.getResponse());
		resp.header("Access-Control-Allow-Origin", "*")
	      		.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");

		String reqHead = req.getHeaderValue("Access-Control-Request-Headers");

		if(null != reqHead && !reqHead.equals("")){
			resp.header("Access-Control-Allow-Headers", reqHead);
		}

		contResp.setResponse(resp.build());
        	return contResp;
	}

}

And that’s all !

Thanks again to Kdecherf for his war upon JSONP and for sharing those headers :D


Using Jsp in a Jersey JAX-RS RESTful application

Posted: décembre 29th, 2011 | Author: | Filed under: Dev, Glassfish, Java, Java EE, JAX-RS, Jersey, Tools, Tutorial | Tags: , , , , , , , , , , , | 21 Comments »

So, ok we could easily produce some RESTful applications with Jersey.
But sometimes, the output could be very big to put in a method and a template could be useful.

Jersey provides MVC support for JSP pages.
There is a JSP template processor that resolves absolute template references to processable template references that are JSP.


1 – Configure web.xml

<filter>
	<filter-name>jersey</filter-name>
	<filter-class>com.sun.jersey.spi.container.servlet.ServletContainer</filter-class>
	<init-param>
		<param-name>com.sun.jersey.config.property.packages</param-name>
		<param-value>com.ezakus.web</param-value>
	</init-param>
	<init-param>
		<param-name>com.sun.jersey.config.property.JSPTemplatesBasePath</param-name>
		<param-value>/WEB-INF/jsp</param-value>
	</init-param>
	<init-param>
		<param-name>com.sun.jersey.config.property.WebPageContentRegex</param-name>
		<param-value>/(resources|(WEB-INF/jsp))/.*</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>jersey</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

Instead of a servlet mapping you need to have a filter.

With the JSPTemplatesBasePath param, you choose your jsp folder

	<param-name>com.sun.jersey.config.property.JSPTemplatesBasePath</param-name>
	<param-value>/WEB-INF/jsp</param-value>

And with the WebPageContentRegex you are able to serve static resources.
In the previous example, static resources are on the /resources/ or /WEB-INF/jsp/ path but you can put what you want :

	<param-name>com.sun.jersey.config.property.WebPageContentRegex</param-name>
	<param-value>/(resources|js|css|images)/.*</param-value>

2 – Return Viewable or Response

Now you can use the Viewable class with your jsp path

package com.ezakus.web;

import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

import com.sun.jersey.api.view.Viewable;

@Stateless
@Path("/")
public class MyController {

    @GET
    @Produces("text/html")
    public Viewable index() {
    	return new Viewable("/index");
    }

}

Note : /index assume that you have a /WEB-INF/jsp/index.jsp on your path

You can also use the Response class

package com.ezakus.web;

import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import com.sun.jersey.api.view.Viewable;

@Stateless
@Path("/")
public class MyController {

    @GET
    @Produces("text/html")
    public Response index() {
    	return Response.ok(new Viewable("/index")).build();
    }

}

3 – Using Viewable’s model

The Viewable object could be created with a model :

package com.ezakus.web;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ejb.Stateless;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import com.sun.jersey.api.view.Viewable;

@Stateless
@Path("/")
public class MyController {

    @GET
    @Produces("text/html")
    public Response index() {
    	Map<String, Object> map = new HashMap<String, Object>();
        map.put("user", "usul");
        List<String> l = new ArrayList<String>();
        l.add("light saber");
        l.add("fremen clothes");
        map.put("items", l);
    	return Response.ok(new Viewable("/cart", map)).build();
    }

}

Note : Jersey will assign the model instance to the attribute « it » in the jsp. (Yes, life is hard)

<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <title>Welcome!</title>
</head>
<body>
  <h1>Welcome ${it.user}!</h1>
  <p>
  	items in your cart :<br />
    <c:forEach var="item" items="${it.items}">
    	${item}<br />
    </c:forEach>
  </p>
</body>
</html>

How to change the Glassfish server log level from command line

Posted: décembre 28th, 2011 | Author: | Filed under: Admin, asadmin, Glassfish, Java, Java EE, Tools, Tutorial | Tags: , , , , , , , , , , , | 1 Comment »

When deploying applications with glassfish, you have sometimes (just sometimes) to read the server log :) You can change, mostly on the fly (no restart needed), the log levels, that could be very useful. First, list the log levels :

asadmin list-log-levels

Don’t forget to give instance name if you want those for this instance

asadmin list-log-levels instance1

Here is an example :

asadmin list-log-levels instance1

ShoalLogger	<CONFIG>
com.sun.enterprise.server.logging.GFFileHandler	<ALL>
java.util.logging.ConsoleHandler	<FINEST>
javax.enterprise.resource.corba	<INFO>
javax.enterprise.resource.javamail	<INFO>
javax.enterprise.resource.jdo	<INFO>
javax.enterprise.resource.jms	<INFO>
javax.enterprise.resource.jta	<INFO>
javax.enterprise.resource.resourceadapter	<INFO>
javax.enterprise.resource.sqltrace	<FINE>
javax.enterprise.resource.webcontainer.jsf.application	<INFO>
javax.enterprise.resource.webcontainer.jsf.config	<INFO>
javax.enterprise.resource.webcontainer.jsf.context	<INFO>
javax.enterprise.resource.webcontainer.jsf.facelets	<INFO>
javax.enterprise.resource.webcontainer.jsf.lifecycle	<INFO>
javax.enterprise.resource.webcontainer.jsf.managedbean	<INFO>
javax.enterprise.resource.webcontainer.jsf.renderkit	<INFO>
javax.enterprise.resource.webcontainer.jsf.resource	<INFO>
javax.enterprise.resource.webcontainer.jsf.taglib	<INFO>
javax.enterprise.resource.webcontainer.jsf.timing	<INFO>
javax.enterprise.system.container.cmp	<INFO>
javax.enterprise.system.container.ejb	<INFO>
javax.enterprise.system.container.ejb.mdb	<INFO>
javax.enterprise.system.container.web	<INFO>
javax.enterprise.system.core.classloading	<INFO>
javax.enterprise.system.core.config	<INFO>
javax.enterprise.system.core	<INFO>
javax.enterprise.system.core.naming	<INFO>
javax.enterprise.system.core.security	<INFO>
javax.enterprise.system.core.selfmanagement	<INFO>
javax.enterprise.system.core.transaction	<INFO>
javax.enterprise.system	<INFO>
javax.enterprise.system.ssl.security	<INFO>
javax.enterprise.system.tools.admin	<INFO>
javax.enterprise.system.tools.backup	<INFO>
javax.enterprise.system.tools.deployment	<INFO>
javax.enterprise.system.util	<INFO>
javax.enterprise.system.webservices.registry	<INFO>
javax.enterprise.system.webservices.rpc	<INFO>
javax.enterprise.system.webservices.saaj	<INFO>
javax	<INFO>
javax.org.glassfish.persistence	<INFO>
org.apache.catalina	<INFO>
org.apache.coyote	<INFO>
org.apache.jasper	<INFO>
org.eclipse.persistence.session	<INFO>
org.glassfish.admingui	<INFO>
org.jvnet.hk2.osgiadapter	<INFO>
Command list-log-levels executed successfully.

After that, you just have to set it, for example :

asadmin set-log-levels javax.enterprise.system.container.ejb=<level>
# Here with the instance
asadmin set-log-levels <instance-name> javax.enterprise.system.container.ejb=<level>

As you could see in the asadmin documentation :
Log level values are SEVERE, WARNING, INFO, CONFIG, FINE, FINER, and FINEST.
The default setting is INFO.

When you perform this, i think you are watching the logs.
So you could view the changes applied, if not, you have to restart the instance (or server).
Hope it would be useful for you


Java EE 6 Glassfish 3.1.1 Cluster with Load-Balancer Installation

Posted: décembre 21st, 2011 | Author: | Filed under: Dev, Java, Java EE, Tutorial | Tags: , , , , , , , , , , , , | 12 Comments »

In this tutorial we’ll see how to install a Glassfish cluster with this architecture

 
architecture
 

Assumptions : there’s nothing on servers (either java, xauth or whatever you want)

 

Prepare Node 2 and Node 1


 

Add the glassfish user (with the same password on each) and choose the installation path

adduser glassfish mkdir /opt/glassfish3
chown glassfish:glassfish
/opt/glassfish3

Add non-free in sources.list for sun jdk installation

# vi /etc/apt/sources.list
deb http://mirror.ovh.net/debian/ squeeze main non-free
deb-src http://mirror.ovh.net/debian/ squeeze main non-free

Install sun jdk

apt-get update apt-get install sun-java6-jdk

 

Prepare LB


 

Add the glassfish user (still with the same password)

adduser glassfish

 

On Node 1


  • Install xauth

The simpliest way is to install glassfish in graphical mode so we need xauth for x forwarding with ssh.

apt-get install xauth

Logout and Log in with -X option.

  • Glassfish install

Download the GlassFish Server 3.1.1 Open Source Edition Full Platform

wget http://download.java.net/glassfish/3.1.1/release/glassfish-3.1.1-unix.sh
chmod +x glassfish-3.1.1-unix.sh ./glassfish-3.1.1-unix.sh

Choose custom, install and configure, choose the installation path (/opt/glassfish3 for us)

chown -R glassfish:glassfish 

Add the path to <installation path>/bin to glassfish user’s PATH (.bashrc .profile …)
With this, glassfish user could use asadmin command line tool

PATH="/opt/glassfish3/glassfish/bin:$PATH"
  • Glassfish config

To know what we could do with asadmin

asadmin list-commands

So let’s go

asadmin login
asadmin enable-secure-admin asadmin restart-domain
#If you forget the domain name : <strong>asadmin list-domains
</strong>
  • Installing Node 2 from Node 1

asadmin setup-ssh --generatekey=true node-2 lb-1
asadmin install-node node-2
  •  Create cluster

First, create the cluster config (based on the existing one),  all cluster nodes will share the same config.

asadmin copy-config default-config cluster-config

Now we could create the nodes

asadmin create-node-ssh --nodehost localhost node-1-ssh
asadmin create-node-ssh --sshuser glassfish --sshkeyfile /home/glassfish/.ssh/id_rsa --nodehost node-2 node-2-ssh

And finally the cluster and the instances

asadmin create-cluster --config cluster-config usul-cluster
asadmin create-instance --cluster usul-cluster --node node-1-ssh gf-1
asadmin create-instance --cluster usul-cluster --node node-2-ssh gf-2
# Verifying
asadmin list-instances --long=true
# Start the cluster
asadmin start-cluster usul-cluster

Load-Balancer setup


  • Install xauth because lb installer needs GUI

Edit : As John said in the comments : The Load Balancer installer requires a GUI on first install, but you can save the LB configuration to a file and subsequent installs can be done in a headless manner.

yum install xauth

We also need the JDK, so download java 6 sdk bin installer on java.sun.com (the bin one, not rpm.bin)

wget http://download.oracle.com/otn-pub/java/jdk/6u30-b12/jdk-6u30-linux-x64.bin

chmod +x jdk-6u30-linux-x64.bin
./jdk-6u30-linux-x64.bin
mv jdk1.6.0_30 /usr/local && cd /usr/local && ln -s jdk1.6.0_30 jdk
# Install in the system
# 1 because there's no java on the server, put 2 or 3 if you already have another java version)
# and launch alternatives --config
java alternatives --install /usr/bin/java java /usr/local/jdk/bin/java 1

# Verifying
java -version
  • Install and prepare Apache

yum install httpd mod_ssl openssl

For cert generation, be careful to put your server name in Common Name

# Generate private key
openssl genrsa -out ca.key 1024
# Generate CSR
openssl req -new -key ca.key -out ca.csr
# Generate Self Signed Key
openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt
# Copy the files to the correct locations
cp ca.crt /etc/pki/tls/certs
cp ca.key /etc/pki/tls/private/ca.key
cp ca.csr /etc/pki/tls/private/ca.csr

Edit /httpd/conf/ssl.conf and set the correct paths

SSLCertificateFile /etc/pki/tls/certs/ca.crt
SSLCertificateKeyFile /etc/pki/tls/private/ca.key

Now we need to tweak this installation because the load balancer installer needs some files and paths

# Folder for the glassfish DAS certificate
mkdir /etc/httpd/conf/ssl.crt
# Installer will look for files in $apache_home/conf/extra
ln-s /etc/httpd/conf.d /etc/httpd/conf/extra
touch /etc/httpd/conf/extra/httpd-ssl.conf
touch /etc/httpd/conf/extra/httpd-vhosts.conf
touch/etc/httpd/conf/extra/httpd-mpm.conf
# Installer will look for bin/apachectl and bin/envvars
mkdir /etc/httpd/bin
touch /etc/httpd/bin/envvars
touch /etc/httpd/bin/apachectl

Edit Mpm config file

PidFile "logs/httpd.pid"
LockFile "logs/accept.lock"

<IfModule mpm_prefork_module>
 StartServers 5
 MinSpareServers 5
 MaxSpareServers 10
 MaxClients 150
 MaxRequestsPerChild 0
</IfModule>

<IfModule mpm_worker_module>
 StartServers 2
 MaxClients 150
 MinSpareThreads 25
 MaxSpareThreads 75
 ThreadsPerChild 25
 MaxRequestsPerChild 0
</IfModule>

Edit apachectl

#!/bin/bash
/usr/sbin/apachectl -v

 

chmod 755 /etc/httpd/bin/apachectl
  • Export the DAS certificate

The load balancer needs an xml file to be updated : loadbalancer.xml By putting the certificate on the LB, we could update it from the DAS with apply-http-lb-changes

#Export crt
keytool \
 -export \
 -rfc \
 -alias s1as \
 -keystore <installPath>/glassfish/domains/usul/config/keystore.jks \
 -file ./glassfish.crt \
 -storepass changeit

# Send it on the LB
scp glassfish.crt root@lb-1:/etc/httpd/conf/

# The installer will put the crt into ssl.crt later
  • Install the load-balancer

Download loadbalancer : http://www.oracle.com/technetwork/middleware/glassfish/downloads/index.html

# Don't forget to ssh -X because of GUI intall
java -jar glassfish-lbconfigurator-3_1_1.jar

Choose apache and put the installation dir (/etc/httpd)

Choose the DAS cert in /etc/httpd/conf/

When finished, edit /etc/init.d/httpd and add

 if [ -f /etc/httpd/bin/envvars]; then
 . /etc/httpd/bin/envvars
 fi
  • Configure the ssl access

keytool -printcert -file /etc/httpd/conf/ssl.cert/glassfish.crt

Copy Serial number (UPPER CASE !!!) and Organization (O) and Organization Unit (OU)
Edit /etc/httpd/conf/extra/httpd-ssl.conf and edit both SSLRequire at the end of file with O OU and Serial (in Upper Case)
You will have something like this

 SSLVerifyClient require
 SSLVerifyDepth 1
 SSLRequireSSL
 SSLCACertificateFile /etc/httpd/conf/ssl.crt/glassfish.crt
 SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)-/ \
 and %{SSL_CLIENT_S_DN_O} eq "Oracle Corporation" \
 and %{SSL_CLIENT_S_DN_OU} eq "GlassFish" \
 and %{SSL_CLIENT_M_SERIAL} eq "4EEB6172" )

 SSLVerifyClient require
 SSLVerifyDepth 1
 SSLRequireSSL
 SSLCACertificateFile /etc/httpd/conf/ssl.crt/glassfish.crt
 SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)-/ \
 and %{SSL_CLIENT_S_DN_O} eq "Oracle Corporation" \
 and %{SSL_CLIENT_S_DN_OU} eq "GlassFish" \
 and %{SSL_CLIENT_M_SERIAL} eq "4EEB6172" )

Edit /etc/httpd/conf/extra/httpd-vhosts.conf

#Be sure to have :
NameVirtualHost *:80
<VirtualHost *:80>
 ServerName lb-1
 DocumentRoot "/var/www"
 #  The document root is not important, it must exist to avoid warnings or errors in logs but this is not used
</VirtualHost>

Raise the MaxClients in /etc/httpd/conf/extra/httpd-mpm.conf because installer put 1 that is not enough

chown -R apache:apache /etc/httpd
# just check before that apache is the user for apache in httpd.conf
/etc/init.d/httpd restart

Create the load-balancer in the DAS

asadmin create-http-lb --devicehost lb-1 --deviceport 443 --target usul-cluster lb-1

asadmin apply-http-lb-changes lb-1