10/14/2009

A sample application-client.xml (Java EE 5)

Application client is another type of Java EE module, the least utilized one. It attempts to wrap up Java SE application, deploy it to application server, and make use of deployed EJB, platform services and resources.

The following application-client.xml declares an env-entry, ejb-ref and resource-ref.

<?xml version="1.0" encoding="UTF-8"?>
<application-client xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application-client_5.xsd">

<display-name>A sample application client</display-name>

<env-entry>
<description>admin email</description>
<env-entry-name>adminEmail</env-entry-name>
<env-entry-value>admin@example.x</env-entry-value>
</env-entry>

<ejb-ref>
<ejb-ref-name>ejb/testBean</ejb-ref-name>
<remote>test.TestRemoteInterface</remote>
<ejb-link>TestBean</ejb-link>
</ejb-ref>

<resource-ref>
<res-ref-name>HRDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<mapped-name>jdbc/__default</mapped-name>
</resource-ref>

</application-client>
The env-entry can be looked up with name "java:comp/env/adminEmail", or injected into the application client main class or its superclasses. For example:
@Resource(name="adminEmail")
//use static modifier in application client main class only
private static String adminEmail;
The ejb-ref and resource-ref can be looked up with names "java:comp/ejb/testBean" and "java:comp/env/HRDS", respectively.

The reason I include ejb-ref and resource-ref is to show how they fit in descriptors. In Java EE 5 and later, it is easier to use @EJB and @Resource to declare and inject them into component classes.

10/13/2009

A sample application.xml (Java EE 5 and Java EE 6)

The following is a sample application.xml that declares 3 modules (application client, ejb jar, and war), and a library directory. The library-directory element is redundant here since the default value is "lib".

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd">

<module>
<java>test-client.jar</java>
</module>

<module>
<ejb>test-ejb.jar</ejb>
</module>

<module>
<web>
<web-uri>test.war</web-uri>
<context-root>test</context-root>
</web>
</module>

<library-directory>lib</library-directory>
</application>

Java EE 6 application.xml:

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="6" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd">

<application-name>test-app</application-name>
<initialize-in-order>true</initialize-in-order>
<module>
<web>
<web-uri>test-web.war</web-uri>
<context-root>test</context-root>
</web>
</module>

<module>
<ejb>test-ejb.jar</ejb>
</module>
<library-directory>lib</library-directory>
</application>

10/12/2009

A sample ejb-jar.xml (EJB 3.0)

ejb-jar.xml is optional starting from EJB 3.0. Annotations are extensively used to declare metadata in place of descriptor elements. In certain cases, ejb-jar.xml is still necessary, and 2 such cases I can think of are the declaration of env-entry and default interceptor.

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<enterprise-beans>
<session>
<ejb-name>TestBean</ejb-name>
<ejb-ref>
<ejb-ref-name>ejb/fooremote</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<remote>test.FooRemoteIF</remote>
</ejb-ref>
<env-entry>
<description>admin email</description>
<env-entry-name>adminEmail</env-entry-name>
<env-entry-value>admin@example.x</env-entry-value>
</env-entry>
</session>
</enterprise-beans>

<interceptors>
<interceptor>
<interceptor-class>test.Interceptor1</interceptor-class>
</interceptor>
</interceptors>

<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>test.Interceptor1</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
This ejb-jar.xml assumes that TestBean is already annotated with either @Stateless or @Stateful. The descriptor adds a env-entry resource to this session bean's naming environment. This env-entry can be injected into TestBean class, or looked up in TestBean's naming environment.

The interceptor-binding element binds Interceptor1 to all EJBs packaged in the current ejb jar.

A minimal sample web.xml (servlet 2.5)

A very short web.xml (Servlet 2.5) that declares a servlet and its mapping. Some elements, like session-config and welcome-file-list have reasonable defaults provided by web containers.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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">

<servlet>
<display-name>Servlet1</display-name>
<servlet-name>Servlet1</servlet-name>
<servlet-class>test.Servlet1</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/Servlet1</url-pattern>
</servlet-mapping>

</web-app>

A sample web.xml (servlet 2.5)

This is a sample web.xml based on Servlet 2.5 (part of Java EE 5) that declares common elements. All top-level elements are optional, and can be in any order.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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">

<context-param>
<param-name>debug</param-name>
<param-value>false</param-value>
</context-param>

<session-config> <!-- 10 minutes -->
<session-timeout>10</session-timeout>
</session-config>

<servlet>
<display-name>Servlet1</display-name>
<servlet-name>Servlet1</servlet-name>
<servlet-class>test.Servlet1</servlet-class>
<init-param>
<param-name>sleep-time-in-seconds</param-name>
<param-value>10</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/Servlet1</url-pattern>
</servlet-mapping>

<env-entry>
<description>admin email</description>
<env-entry-name>adminEmail</env-entry-name>
<env-entry-value>admin@example.x</env-entry-value>
</env-entry>

<resource-ref>
<res-ref-name>HRDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<mapped-name>jdbc/__default</mapped-name>
</resource-ref>

<filter>
<display-name>Filter1</display-name>
<filter-name>Filter1</filter-name>
<filter-class>Filter1</filter-class>
</filter>

<filter-mapping>
<filter-name>Filter1</filter-name>
<url-pattern>/Filter1</url-pattern>
</filter-mapping>

<filter-mapping>
<filter-name>Filter1</filter-name>
<servlet-name>Servlet1</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

<listener>
<listener-class>Listener1</listener-class>
</listener>

<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>

</web-app>

10/09/2009

Servlet without web.xml

One of the new features in Servlet 3.0 is the introduction of annotation to define a servlet, thus making web.xml optional. The following is a simple web app that only contains a servlet:

1. create project directory structure:

mkdir nowebxml
cd nowebxml/
mkdir -p src/test build/WEB-INF/classes
2. create TestServlet.java in src/test
cd src/test
-------------------

package test;

import java.io.IOException;
import java.io.PrintWriter;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns = "/TestServlet", loadOnStartup = 1)
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}

protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter writer = response.getWriter();
writer.println("A servlet without web.xml: " + getServletName());
}
}
3. compile and package
javac -cp "$GLASSFISH_HOME/modules/*" -d ../../build/WEB-INF/classes/ TestServlet.java

cd ../../build/

jar cvf nowebxml.war WEB-INF/
4. deploy
cp nowebxml.war $GLASSFISH_HOME/domains/domain1/autodeploy/
5. run TestServlet
curl http://localhost:8080/nowebxml/TestServlet
A servlet without web.xml: test.TestServlet
GlassFish V3 is used for deploy and running the test web app, but this web app is portable and should work with any container that supports Servlet 3.0.

My javac command uses a wildcard in classpath values, which is only supported in Java SE 6 or later. Classpath wildcard is very convenient, especially in testing. In addition, Servlet 3.0 is part of Java EE 6, which itself requires Java SE 6.

Print array content with Arrays.toString or Arrays.asList

When directly printing an array, it gives its element type and hashcode, which is not the desired result in most cases. A better way is to use java.util.Arrays.toString(), or Arrays.asList() method to print the string representation of the array.

This test method tries 2 ways of printing the content of a string array:

public final void testPrintArray() {
   final String[] names = {"Linux", "Mac OS X", "Windows", "Solaris", null};
   System.out.println("print names array directly: " + names);
   System.out.println("print names with Arrays.toString(): " + Arrays.toString(names));
   System.out.println("print names with Arrays.asList(): " + Arrays.asList(names));
   //but String.valueOf() won't print array content
   System.out.printf("print names with String.valueOf(): %s%n", String.valueOf(names));
}
Test output:
print names array directly: [Ljava.lang.String;@5122cdb6
print names with Arrays.toString(): [Linux, Mac OS X, Windows, Solaris, null]
print names with Arrays.asList(): [Linux, Mac OS X, Windows, Solaris, null]
print names with String.valueOf(): [Ljava.lang.String;@293e86f
Between the 2 methods, I find toString() is more natural. Arrays.toString() was introduced in Java 5, somewhat later than Arrays.asList().

Also note that a List can take null as element value, and its string representation is "null".