6/30/2006

Fix NameNotFoundException: Incorrect Lookup Name

javax.naming.NameNotFoundException is the most thrown exception I've seen in J2EE and JavaEE applications. In this series, I will explain some common causes and how to solve it.

Incorrect lookup names can take many forms. One of them is missing the standard prefix java:comp/env/when looking up resources or EJB in components' environment.

public class HelloServlet extends HttpServlet {
protected void processRequest(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = null;
try {
out = response.getWriter();
Context ic = new InitialContext();

//this line is wrong!
DataSource dataSource = (DataSource) ic.lookup("jdbc/default-datasource");
Connection connection = dataSource.getConnection();
out.println("Successfully looked up the default datasource: " + dataSource +
", and got the connection: " + connection);
} catch (NamingException e) {
throw new ServletException(e);
} catch (SQLException e) {
throw new ServletException(e);
}
}

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
And the web.xml is as follows:
<?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>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.foo.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>
<resource-ref>
<res-ref-name>jdbc/default-datasource</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
<mapped-name>jdbc/__default</mapped-name>
</resource-ref>
</web-app>
mapped-name subelement maps the local resource reference name jdbc/default-datasource used in Servlet code to the global JNDI name jdbc/__default configured in application server. mapped-name is a new way for mapping resource and EJB references in Java EE 5. jdbc/__default is the default datasource pre-configured in JavaEE SDK 5, Glassfish, and Sun Java System Application Server. Nothing is wrong with this configuration.

But when I deploy and run the above web app to JavaEE SDK 5, I get the following error:
javax.naming.NameNotFoundException
at com.sun.enterprise.naming.TransientContext.resolveContext(TransientContext.java:255)
at com.sun.enterprise.naming.TransientContext.lookup(TransientContext.java:178)
at com.sun.enterprise.naming.SerialContextProviderImpl.lookup(SerialContextProviderImpl.java:61)
at com.sun.enterprise.naming.LocalSerialContextProviderImpl.lookup(LocalSerialContextProviderImpl.java:98)
at com.sun.enterprise.naming.SerialContext.lookup(SerialContext.java:309)
at com.sun.enterprise.naming.NamingManagerImpl.lookup(NamingManagerImpl.java:885)
at com.sun.enterprise.naming.java.javaURLContext.lookup(javaURLContext.java:156)
at com.sun.enterprise.naming.SerialContext.lookup(SerialContext.java:307)
at javax.naming.InitialContext.lookup(InitialContext.java:351)
at com.foo.servlet.HelloServlet.processRequest(HelloServlet.java:29)
The root cause is the wrong lookup name is used in HelloServlet. To fix this bug, just change the lookup code to use absolute name:
DataSource dataSource = (DataSource)
ic.lookup("java:comp/env/jdbc/default-datasource");
Alternatively, you can also lookup the subcontext first, and then lookup the short resource reference name:
Context subcontext =
(Context) ic.lookup("java:comp/env");
DataSource dataSource = (DataSource)
subcontext.lookup("jdbc/default-datasource");

All posts in this series for NameNotFoundException:

Fix NameNotFoundException: Incorrect Lookup Name

Fix NameNotFoundException: Reference Not Declared

Fix NameNotFoundException: Wrong Mapping in Deployment Plan

6/25/2006

Don't Overload EJB 3 Lifecycle and Interceptor Methods: Part 3

In part 1 and part 2, I wrote about the error from overloading EJB 3 lifecycle and interceptor methods, and how to detect it. You may be wondering why there is such a restriction.

I don't have a clear answer. I guess the main purpose is to make it easier to implement this feature in EJB 3 containers. With this restriction, a container only needs to search (e.g., using reflection) for lifecycle and interceptor methods by their name, ignoring parameters. Processing method parameters may affect performance, though I'm not sure to what extent.

Variable arguments (varargs) introduced in JDK 5 adds more complexity to method parameter processing. I have yet to see any formal support of varargs in JavaEE technologies. So it seems convenient to just leave method parameters out.

Lifecycle and interceptor methods also need to be expressed in XML descriptor files, though it's less common in JavaEE 5. It's pretty verbose to uniquely represent a java method in XML format, and usually takes this form:

<method>
<class>com.javahowto.test.MyInterceptor</class>
<method-name>init</method-name>
<method-params>
<method-param>java.lang.String</method-param>
<method-param>int[]</method-param>
</method-params>
</method>
If we don't need to worry about all these method parameters, it will certainly make XML descriptors less clumsy.

So this is how a lifecycle method is defined in javaee_5.xsd:
<xsd:complexType name="lifecycle-callbackType">
<xsd:annotation>
<xsd:documentation>
The lifecycle-callback type specifies a method on a
class to be called when a lifecycle event occurs.
Note that each class may have only one lifecycle
callback method for any given event
and that the method may not be overloaded.

If the lifefycle-callback-class element is missing then
the class defining the callback is assumed to be the
component class in scope at the place in the descriptor
in which the callback definition appears.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="lifecycle-callback-class"
type="javaee:fully-qualified-classType"
minOccurs="0"/>
<xsd:element name="lifecycle-callback-method"
type="javaee:java-identifierType"/>
</xsd:sequence>
</xsd:complexType>
And this is how JavaEE interceptor method is defined in ejb-jar_3_0.xsd:
<xsd:complexType name="around-invokeType">
<xsd:annotation>
<xsd:documentation>
The around-invoke type specifies a method on a
class to be called during the around invoke portion of an
ejb invocation. Note that each class may have only one
around invoke method and that the method may not be
overloaded.

If the class element is missing then
the class defining the callback is assumed to be the
interceptor class or component class in scope at the
location in the descriptor in which the around invoke
definition appears.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="class"
type="javaee:fully-qualified-classType"
minOccurs="0"/>
<xsd:element name="method-name"
type="javaee:java-identifierType"/>
</xsd:sequence>
</xsd:complexType>
How about setter injection methods? Is it OK to overload JavaEE setter injection methods? There seems to be no clear specification, but it's reasonable to expect the same restriction to apply to setter injection methods. In XML descriptor, the corresponding element for a setter injection method is java-identifierType, as declared in javaee_6.xsd:
  <xsd:complexType name="java-identifierType">
<xsd:annotation>
<xsd:documentation>

The java-identifierType defines a Java identifier.
The users of this type should further verify that
the content does not contain Java reserved keywords.

</xsd:documentation>
</xsd:annotation>
<xsd:simpleContent>
<xsd:restriction base="javaee:string">
<xsd:pattern value="($|_|\p{L})(\p{L}|\p{Nd}|_|$)*"/>
</xsd:restriction>
</xsd:simpleContent>
</xsd:complexType>
There is no way for java-identifierType to distinguish between overloaded methods. I take it as another reason why setter injection methods should not be overloaded. For instance, the following is invalid:
private User user;

@EJB
public void setUser(User u) {
this.user = u;
}

// AVOID THIS
public void setUser(String uid) {
this.user = getUser(uid);
}

Don't Overload EJB 3 Lifecycle and Interceptor Methods: Part 2

In part one, I wrote about not to overload EJB 3 lifecycle and interceptor methods, which is another programming restriction in EJB. Under what circumstances are developers likely to fall into this trap?

1. Some method names are so good that we want to use it in multiple methods, such as init, initialize, initialise, etc. This is so natural in java and one will never expect them to be disallowed in EJB 3 lifecycle and interceptor methods.

2. When migrating some EJBs from 2.x to 3.0 and their bean classes already have overloaded methods. You annotate one of them to be lifecycle or interceptor method.

How can we detect such application errors? The good news is verifier will catch it. In JavaEE SDK 5/Glassfish/Sun Java System Application Server, it's install-dir/bin/verifier. In my NetBeans 5.5, I can also verify it by right-click the project node and choose verify. This will run the verifier, since my NetBeans has already been configured with Sun Java System Application Server 9.0.

C:\tmp > C:\Sun\AppServer\bin\verifier hello.ear
--------------
FAILED TESTS :
--------------

Test Name : tests.ejb.ejb30.CallbackMethodArgument
Test Assertion : Lifecycle callback interceptor methods defined on an interceptor class should have InvocationContext as argument. When defined on the bean class they should have empty argument. Please refer to EJB 3.0 "Core Contracts and Requirements" Specification Section #11.4 for further information.
Test Description : For [ hello#hello-ejb.jar#ResourceBean ]
Wrong PostConstruct interceptor method [ public void com.foo.ejb.ResourceBean.init(java.util.Properties) ]
This verifier is appserver-independent, and you can use it to verify any J2EE and JavaEE applications for potential errors/warnings, even if you are not using Glassfish. Other appservers and IDEs may or may not have a verifier program, and they may or may not catch such an error.

Continue to part 3 ...

6/23/2006

Don't Overload EJB 3 Lifecycle and Interceptor Methods

Overloading is a natural part of java language. With overloading, we are just declaring multiple, totally separate methods that happen to share the same name. But don't use overloading when it comes to EJB 3 lifecycle and interceptor methods. For example, the following bean and interceptor classes are all wrong:

Invalid EJB 1.

package com.foo.ejb;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.ejb.Stateless;

@Stateless
public class InvalidBean1 implements HelloRemote {
@PostConstruct
private void init() {
System.out.println("InvalidBean1 PostConstruct method called.");
}

public void init(Properties props) {
System.out.println("InvalidBean1 public init(Properties) method, not a PostConstruct method.");
}
To fix it, just rename init(Properties) to init2(Properties), and make sure init method is not overloaded in the current class.

Invalid EJB 2.
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

@Stateless
public class InvalidBean2 implements HelloRemote {
@AroundInvoke
private Object intercept(InvocationContext inv)
throws Exception {
System.out.println("InvalidBean2 AroundInvoke method called.");
return inv.proceed();
}

public void intercept(Properties props) {
System.out.println("InvalidBean2 public intercept(Properties) method, not an AroundInvoke method.");
}
}
To fix it, just rename intercept(Properties) to intercept2(Properties), and make sure intercept method is not overloaded in the current class.

Invalid interceptor class
.
import javax.interceptor.InvocationContext;
import javax.interceptor.AroundInvoke;

public class InvalidInterceptor {
@AroundInvoke
private Object intercept(InvocationContext inv)
throws Exception {
System.out.println("InvalidInterceptor AroundInvoke method called.");
return inv.proceed();
}

public void intercept(Properties props) {
System.out.println("InvalidInterceptor public intercept(Properties) method, not an AroundInvoke method.");
}
}
To fix it, just rename intercept(Properties) to intercept2(Properties), and make sure intercept method is not overloaded in the current class.

What will happen with these invalid apps? Since these overloaded methods are legal in java, so your apps will be able to build, maybe even deploy successfully. But when the EJB is instantiated, EJB container looks for a lifecycle or interceptor method only by name. It may or may not get the correct method, depending on container implementation, operation systems, and other unknown factors.

With JavaEE SDK 5/Glassfish/Sun Java System Application Server 9 on Sparc Solaris 10 , I was able to deploy and run my app successfully, and the correct lifecycle method is found and invoked. With the same appserver on Windows, accessing the EJB will always fail with the following error:
[#|2006-06-23T10:07:11.291-0400|INFO|sun-appserver-pe9.0|javax.enterprise.system.container.ejb|_ThreadID=16;_ThreadName=p: thread-pool-1; w: 3;|EJB5070: Exception creating stateless session bean : [{0}]
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at com.sun.ejb.containers.interceptors.BeanCallbackInterceptor$1.run(InterceptorManager.java:601)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.ejb.containers.interceptors.BeanCallbackInterceptor.intercept(InterceptorManager.java:595)
at com.sun.ejb.containers.interceptors.CallbackChainImpl.invokeNext(InterceptorManager.java:448)
at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:204)
at com.sun.ejb.containers.StatelessSessionContainer.createStatelessEJB(StatelessSessionContainer.java:546)
at com.sun.ejb.containers.StatelessSessionContainer.access$100(StatelessSessionContainer.java:96)
at com.sun.ejb.containers.StatelessSessionContainer$SessionContextFactory.create(StatelessSessionContainer.java:746)
at com.sun.ejb.containers.util.pool.NonBlockingPool.getObject(NonBlockingPool.java:186)
at com.sun.ejb.containers.StatelessSessionContainer._getContext(StatelessSessionContainer.java:469)
at com.sun.ejb.containers.BaseContainer.getContext(BaseContainer.java:1566)
at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1148)
at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:189)
at com.sun.ejb.containers.EJBObjectInvocationHandlerDelegate.invoke(EJBObjectInvocationHandlerDelegate.java:110)
...
It means on Windows, the appserver always gets the wrong, unannotated method by its overloaded name.

This is a bug in the application, not in the appserver. Unfortunately, it's a restriction we have to live with.

Continue to part 2 and part 3

6/20/2006

5 Ways to Get Resources in EJB 3

1. Use resource injection with runtime info mapping.
For example,

package com.foo.ejb;
import javax.ejb.Remote;

@Remote public interface ResourceRemote {
public void hello();
}

package com.foo.ejb;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;

@Stateless
public class ResourceBean implements ResourceRemote {
@Resource(name="jdbc/employee")
private DataSource employeeDataSource;
You don't need ejb-jar.xml. For portable applications, you will need appserver-specific deployment plan to map the logical name (jdbc/employee) to the actual DataSource configured in the target runtime environment. For JavaEE SDK 5, Glassfish, and Sun Java System Application Server 9, it's sun-ejb-jar.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 EJB 3.0//EN"
"http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
<sun-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>ResourceBean</ejb-name>
<jndi-name>ResourceBean</jndi-name>
<resource-ref>
<res-ref-name>jdbc/employee</res-ref-name>
<jndi-name>jdbc/__default</jndi-name>
</resource-ref>
</ejb>
</enterprise-beans>
</sun-ejb-jar>

2. Non-portable applications don't even need this appserver-specific deployment plan; they can just use mappedName() field in @Resource:
@Stateless
public class ResourceBean implements ResourceRemote {
@Resource(name="jdbc/employee",
mappedName="jdbc/__default")
private DataSource employeeDataSource;
If application portability is not a big concern, you don't need any descriptor in this example. mappedName() field maps the logical name jdbc/employee to its counterpart (jdbc/__default) in the target runtime server environment. Be aware that application servers are not required by JavaEE platform to support mappedName() field. So it may cause trouble when you later try to migration your applications to another appserver. Glassfish, JavaEE SDK, and SJSAS 9 support mappedName().


3. Yet another option is to use default mapping rules in some application servers, without using runtime deployment plan. This is not portable either and some appservers may not have this functionality at all. In Glassfish, JavaEE SDK, and SJSAS 9, basically if resource logical name (without prefix) is the same as its physical name, then they are mapped together even without sun-ejb-jar.xml. For example,
@Stateless
public class ResourceBean implements ResourceRemote {
@Resource(name="jdbc/__default")
private DataSource defaultDataSource;
You don't need any descriptor, and it just works thanks to the default resource mapping.


4. Use EJBContext.lookup(String name), a new convenience method in EJB 3. The name parameter is relative to java:comp/env. For example,
package com.foo.ejb;
import java.sql.Connection;
import java.sql.SQLException;
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;

@Stateless
public class ResourceBean implements ResourceRemote {
public void hello() {
DataSource
employeeDataSource =
(DataSource) sctx.lookup("jdbc/employee");
try {
Connection conn = employeeDataSource.getConnection();
} catch(SQLException ex) {
ex.printStackTrace();
}
}
ejb-jar.xml is needed to declare this resource reference. Appserver-specific deployment plan is also needed for mapping, unless you use the default mapping mechanism above.
<?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"
metadata-complete="false" 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>ResourceBean</ejb-name>
<resource-ref>
<res-ref-name>jdbc/employee</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
</session>
</enterprise-beans>
</ejb-jar>
sun-ejb-jar.xml is the same as in listing 1.

5. Use traditional JNDI lookup. This approach is basically the same as EJBContext.lookup(String name), except that JNDI lookup requires more lines of code, and uses an absolute reference name starting with java:comp/env or java:comp/
@Stateless
public class ResourceBean implements ResourceRemote {
@Resource private SessionContext sctx;

public void hello() {
DataSource ds = null;
try {
InitialContext ic = new InitialContext();
ds =
(DataSource) ic.lookup("java:comp/env/jdbc/employee");
} catch (NamingException ex){
throw new IllegalStateException(ex);
}
try {
Connection conn = ds.getConnection();
} catch(SQLException ex) {
throw new IllegalStateException(ex);
}
}
You need to declare this resource reference in ejb-jar.xml, and map it in sun-ejb-jar.xml, the same as in listing 4.

The biggest annoyance in JNDI lookup is that I have to try-catch javax.naming.NamingException, a checked exception. Since it's a low-level exception, it's not appropriate to just further throw it out. Probably for this reason, EJBContext.lookup(String name) just throws java.lang.IllegalArgumentException if name not found.

6/18/2006

Fix Javac java lang OutOfMemoryError

When javac is compiling a large number of java source files, it may fail with java.lang.OutOfMemoryError:

The system is out of resources.
Consult the following stack trace for details.
java.lang.OutOfMemoryError: Java heap space
It's no different than OutOfMemoryError in other java applications. When you run javac in Sun JDK, it's invoking com.sun.tools.javac.main.Main located in %JAVA_HOME%\lib\tools.jar.

If you are compiling with javac task in Apache Ant, set fork attribute to true, to run javac in a separate process with its own heap size settings. If fork is set to false, or not set (default is false), javac will run in the same process as Ant, which has a default maximum heap size of 64m. The following is a snippet from build.xml:
<javac fork="true"
srcdir="${basedir}/src"
destdir="${basedir}/build/classes"
classpath="${project.classpath}"
includeantruntime="false"
memoryinitialsize="256m"
memorymaximumsize="256m">
<compilerarg line="-endorseddirs ${env.CATALINA_BASE}/endorsed" />
</javac>
Setting fork to true will also limit any memory leaks in javac implementation to its own child process, without affecting the parent Ant process.

If setting fork, memoryInitialSize, and memoryMaximumSize still doesn't fix the problem, you can execute javac task several times, each javac compiling a subset of your source tree. But this should really be the last rescort, since you are now managing source code dependency, which should be javac's business. You will need to decide which modules get compiled first, and classes in certain modules cannot have direct references to classes in certain other modules, and so on. I'd rather increase the memoryMaximumSize to 2g.

If you don't want to modify existing build.xml files, another option is to increase the heap size for Ant JVM and still execute javac task in-process. You just need to set environment variable ANT_OPTS:
export ANT_OPTS="-Xms256m -Xmx256m"    (ksh/bash)
setenv ANT_OPTS="-Xms256m -Xmx256m" (tcsh/csh)
set ANT_OPTS=-Xms256m -Xmx256m (Windows)
A disadvantage of this approach is users will need to remember to set this environment variable, or use some sort of wrapper script on top of %ANT_HOME%\bin\ant.bat, or $ANT_HOME/bin/ant.

If you are invoking javac directly, you can also increase the heap size for the underlying JVM:
javac -d build/classes -classpath ... -J-Xms256m -J-Xmx256m java-source-files

6/17/2006

4 Ways to Get EJBContext in EJB 3

1. Use field injection in bean class. Those fields can have any access qualifiers (e.g., private, public, protected, package default).

package com.foo.ejb;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;

@Stateless
public class HelloBean implements HelloRemote {
@Resource
private SessionContext sctx;

public void hello() {
System.out.println("SessionContext from field injection: " + sctx);
}
2. Use setter method injection in bean class. You can use either method or field injection for a particular field or property, but not both. Those methods can have any access qualifiers (e.g., private, public, protected, package default).
@Stateless
public class HelloBean implements com.foo.ejb.HelloRemote {
private SessionContext sctx;

@Resource
private void setSessionContext(SessionContext sctx) {
this.sctx = sctx;
}
}
Or,
@Stateless
public class HelloBean implements com.foo.ejb.HelloRemote {
private SessionContext sctx;

@Resource
private void setSctx(SessionContext sctx) {
this.sctx = sctx;
}
}
3. Look up the injected resource based on the name() (or default name, if not specified) field of @Resource
@Stateless
public class HelloBean implements com.foo.ejb.HelloRemote {
@Resource(name="sessionContext")
private SessionContext sctx;

public void hello() {
try {
InitialContext ic = new InitialContext();
SessionContext sctxLookup =
(SessionContext) ic.lookup("java:comp/env/sessionContext");
System.out.println("look up injected sctx: " + sctxLookup);
} catch (NamingException ex) {
throw new IllegalStateException(ex);
}
}
Or using default name if the name field of @Resource is not specified. Note that the default name of an injected resource is: fully-qalified-class-name/variable-name:
@Stateless
public class HelloBean implements com.foo.ejb.HelloRemote {
@Resource
private SessionContext sctx;

public void hello() {
try {
InitialContext ic = new InitialContext();
SessionContext sctxLookup =
(SessionContext) ic.lookup("java:comp/env/com.foo.ejb.HelloBean/sctx");
System.out.println("look up injected sctx by default name: " + sctxLookup);
} catch (NamingException ex) {
throw new IllegalStateException(ex);
}
}
4. Look up by the standard name java:comp/EJBContext (note that there is no /env)
@Stateless
public class HelloBean implements com.foo.ejb.HelloRemote {
public void hello() {
try {
InitialContext ic = new InitialContext();
SessionContext sctxLookup =
(SessionContext) ic.lookup("java:comp/EJBContext");
System.out.println("look up EJBContext by standard name: " + sctxLookup);
} catch (NamingException ex) {
throw new IllegalStateException(ex);
}
}
The above examples use Stateless Session beans, and they also work with Stateful Session beans, Singleton Session beans (introduced in EJB 3.1), and message driven beans. EJBContext are a special kind of resource so you don't need to configure them in ejb-jar.xml, or any vendor-specific configuration files (sun-ejb-jar.xml, jboss.xml, etc). You can choose to also declare references to EJBContext/SessionContext/MessageDrivenContext in descriptors, but you are more likely to get javax.naming.NameNotFoundException.

These techniques are well defined in EJB 3 spec and should work in all JavaEE 5 compilant application servers. I tested above examples on JavaEE SDK 5/SJSAS 9/Glassfish.

6/15/2006

Set Classpath in Eclipse and NetBeans: a Short Comparison

How to set project classpath in Eclipse and NetBeans are similar: just right-click the project name, choose Properties to bring up the Properties Window. But there are some small variations between the two, mostly of the nature of ease-of-use, or style.

How to set classpath in NetBeans:

In NetBeans Project Properties Window, you click Libraries in the left panel, and in the right panel are 4 categories of classpath you can configure:

  • Compile: Empty by default. Compile-time libraries are automatically propagated to other categories of classpath, so you don't need to repeat the same set of jar files in all 4 categories.

  • Run: By default includes everything in compile-time classpath, and compiled classes (e.g., build/classes).

  • Compile Tests: By default includes everything in compile-time classpath, compiled classes (e.g., build/classes), and JUnit.

  • Run Tests: By default includes classpath for compiling tests, and compiled tests.
The separation of the 4 categories of classpath offers a great deal of flexibility, and in most cases, common-sense default values are used to save configuration efforts. Depending on project types, you may see fewer categories of classpath in NetBeans. For instance, there is no Run classpath for web application project, since we don't directly run a war file.

How to set classpath in Eclipse:

Eclipse manages run classpath and build/compile classpath in different places. To configure run classpath, go to menu Run | Run ... to open up the Run Dialog Window. Your current project should already be selected, otherwise, expand the Java Application node in the left panel and select it. Click Classpath tab in the right panel. The default classpath for the current project is the output folder where all compiled classes reside. You can then click any of these buttons to modify run classpath: Add Projects, Add JARS, Add External JARS, Advanced, etc.

To configure build classpath, in Eclipse Project Properties Window, click Java Build Path in the left panel, and in the right panel choose Libraries tab. There are more classpath-related elements in Eclipse: JARs, External JARS, Variables, Libraries, Class Folders, and other Projects. Click Add External JARs (not Add JARS) if you want to add a jar file to classpath, and click Add Class Folders if you want to add a directory or folder to classpath.

It seems to me Add Variables, Add Libraries, and Add JARS all add collections of classpath elements that are defined inside Eclipse, but I don't know their differences. When I specify classpath in any IDE, I like to explicitly spell out Jar files and directories, without using any IDE-specific artifacts. I suppose the whole purpose of having them is to reuse them in other projects, but I find it's pretty unrealistic due to various jar version differences.

I don't care about the difference between a jar file and a directory, both of them are just an element in the classpath. I like the fact that NetBeans combine them into one single Add JAR/Folder button.

When I create a project in Eclipse 3.2, the default output folder is set to the same folder as source folder, which is not a good idea. At least for me, I don't like mixing class files along with java source files. Wouldn't that also slow down classloading when JVM searches classes, since there are twice as many files to scan. You can change the output folder in project property window | Java Build Path.

6/14/2006

Why We Need to Test with Logging Level FINEST

Does your team run a full test cycle with logging level FINEST? Probably not. From my experience in several projects, we are happy if all tests passed with default logging level. But there are good reasons why we need to include logging level FINEST in part of our testing:

  • Expose application erros such as NullPointerException, ClassCastException, etc. They are buried deep in code blocks that are only executed with logging level FINE, FINER, or FINEST. In production environment, customers or field engineers turn on FINE logging level for userful info, not these nasty exceptions. Catch them early in testing. For example,
    if(logger.isLoggable(Level.FINE)) {
    logger.log(Level.FINE, user.toString());
    }
  • Make sure sensitive data, such as password and account numbers are not logged anywhere at any levels.

  • Expose any thread deadlocks caused by more frequent logging. Several commonly used classes are heavily synchronized, such as java.util.Logger, java.lang.ClassLoader, java.util.ResourceBundle, java.io.PrintStream, etc. In a multi-threaded application, it's easy to get into thread deadlocks, especially when java.util.Logger is invoked more frequently at FINEST level. For example,

    Thread A holds a lock on its context classloader to load a class, and also tries to acquire a lock on logger to log some FINE messages : Loading class with Class.forName(classname).

    Thread B holds a lock on the same logger to do some logging, which invokes ResourceBundle.loadBundle(), which in turn invokes ClassLoader.loadClass(), which requires a lock on the class loader. Therefore, thread A is holding a lock on context classloader and waiting on a lock on logger; thread B is holding a lock on logger and waiting on a lock on the context classloader, hence the deadlock.
To fix the above bug, don't call any logging-related methods inside synchronized methods of a classloader.

6/12/2006

4 Ways to Traverse a Map

Usually I don't need to traverse a java.util.Map, which is meant to be looked up, not iterated through. I traverse a java.util.Map primarily for debugging purposem, to dump its content. In case you do need to, here are 4 ways I've tried, just as examples:

import static java.net.HttpURLConnection.*;
import java.util.*;

public class MapTest {
public static void main(String... args) {
traverseMap();
}

private static void traverseMap() {
Map<Integer, String> data = new HashMap<Integer, String>();
data.put(HTTP_OK, "HTTP_OK");
data.put(HTTP_FORBIDDEN, "HTTP_FORBIDDEN");
data.put(HTTP_NOT_FOUND, "HTTP_NOT_FOUND");

System.out.printf("%nUsing JDK 5 foreach and entry set:%n");
Set<Map.Entry<Integer, String>> entries = data.entrySet();
for(Map.Entry<Integer, String> entry : entries) {
Integer key = entry.getKey();
String value = entry.getValue();
System.out.printf("%s = %s%n", key, value);
}

System.out.printf("%nUsing Iterator<Map.Entry> and entry set:%n");
for(Iterator<Map.Entry<Integer, String>> it = entries.iterator(); it.hasNext();) {
Map.Entry<Integer, String> entry = it.next();
Integer key = entry.getKey();
String value = entry.getValue();
System.out.printf("%s = %s%n", key, value);
}

System.out.printf("%nUsing JDK 5 foreach and key set:%n");
for(Integer key : data.keySet()) {
String value = data.get(key);
System.out.printf("%s = %s%n", key, value);
}

System.out.printf("%nUsing traditional Iterator and key set%n");
for(Iterator<Integer> it = data.keySet().iterator(); it.hasNext();) {
Integer key = it.next();
String value = data.get(key);
System.out.printf("%s = %s%n", key, value);
}
}
}
You may have noticed I'm using primitive int numbers (200, 403, 404) as the map key. Thanks to auto-boxing in JDK 5, I can use primitive s and their wrapper types interchangeably. Another useful feature in JDK 5 used here is using static import to import all HTTP status constants from java.net.HttpURLConnection.

To compile and run the test:

$ javac MapTest.java
$ java MapTest

Using JDK 5 foreach and entry set:
200 = HTTP_OK
403 = HTTP_FORBIDDEN
404 = HTTP_NOT_FOUND

Using Iterator<Map.Entry> and entry set:
200 = HTTP_OK
403 = HTTP_FORBIDDEN
404 = HTTP_NOT_FOUND

Using JDK 5 foreach and key set:
200 = HTTP_OK
403 = HTTP_FORBIDDEN
404 = HTTP_NOT_FOUND

Using traditional Iterator and key set
200 = HTTP_OK
403 = HTTP_FORBIDDEN
404 = HTTP_NOT_FOUND
There is some discussion on entrySet vs keySet in the comment section of a previous post: Unnecessary Cast in Map. Basically, using entry set is faster than using key set in traversing a Map. I also learned to use entrySet from there.

6/11/2006

New Options in Javac 1.5/5: -classpath==-cp

4 new options were added to Javac in JDK 1.5/5:

-cp<path>    Specify where to find user class files
-version Version information
-X Print a synopsis of nonstandard options
-J<flag> Pass <flag> directly to the runtime system
I didn't notice them until recently, more than 2 years after Tiger was first released. The official Javac 1.5 docs page doesn't mention -cp and -version, either. I stumbled upon them when I looked at the output from javac -help.

It seems -cp was added in response to bug 4626998 (RFE) Allow "-cp" option to javac to set classpath.

What is the difference between -cp and -classpath options? Prior to JDK 1.5, -cp can be used on java command, but not on javac, whereas -classpath can be used with both java and javac. I tested Javac 1.4.2 with -cp option on both Windows and Solaris, and it's not recognized. Starting from JDK 1.5, -cp and -classpath are synonymous, and I confirmed that on Windows and Solaris.

There is no -version option in javac from 1.0 to 1.4.2. That's a fact I find hard to believe. To get version info from pre-5.0 javac, you will need to -J-version option to get the underlying JVM version, assuming the two versions are the same:
/usr/jdk/142/bin/javac -J-version Test.java
java version "1.4.2_06"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_06-b03)
Java HotSpot(TM) Client VM (build 1.4.2_06-b03, mixed mode)
You can use the same trick in JDK 1.5, but the new -version option is better:
C:\tools\jdk5\bin\javac -version -cp %JAVAEE_HOME%\lib\javaee.jar HelloWorldServlet.java
-J is not really a new option. It was just not listed by javac -help prior to 1.5. This option allows you to pass flags/options to the underlying JVM. Note it's not a standard javac option. For example,
javac -J-Xms512m -J-Xmx512m -J-showversion -classpath lib/foo.jar -d build/classes Test.java


6/10/2006

Why Servlet init Methods are Confusing

I wrote about servlet init methods in my previous post, A Common Mistake in Servlet init Methods. You may ask, what is the difference between servlet init() and init(ServletConfig), and why is it so confusing?

When servlet was invented, there was only one init method, the one with parameter ServletConfig. The no-arg init method was added around Servlet 2.3 timeframe, mainly to address this common mistake. This solution applies a Template Design Pattern to outline what needs to be done in init method and also keeps it extensible for concrete servlet classes.

init(ServletConfig) is the template method, which stores ServletConfig, and then delegates to init(). This is what javax.servlet.GenericServlet.init(ServletConfig) looks like:

public void init(ServletConfig config) throws ServletException {
this.config. = config;
init();
}
The real work of initialization is supposed to be done in init() method, which can be overrid by servlet subclasses. Its default implementation in javax.servlet.GenericServlet is merely a no-op.

From the above code snippet, when init() in your servlet class is invoked by the container, an instance of ServletConfig has already been saved. So you can safely call getServletConfig() inside of init() method.

Servlet classes rarely need to know about init(ServletConfig ) method. Only web containers need to invoke it. Ideally, this method should be declared as final or even private in GenericServlet. But that would break backward compatibility.

Having two overloaded init methods may also add to the confusion. Unless developers read the Javadoc carefully, their relationship (one calling the other) is unclear. So it's possible someone will override init() method like this:
@Override public void init() throws ServletException {
//do some initialization work first,
//then call init(ServletConfig)
init(null);
}
It will cause infinite loop and java.lang.StackOverflowError during servlet initialization, and the servlet will never be put into service. I hope I didn't make it more confusing. My previous post describes how to implement servlet init method.

6/09/2006

A Common Mistake in Servlet init Methods

Servlet init methods allows a servlet to perform one-time initialization prior to servicing requests. One common mistake when implementing init method is in this form:

public void init(ServletConfig config)
throws ServletException {
//do custom initialization ...
System.out.println(" init(ServletConfig config) invoked.");
...
}
This is wrong because it doesn't invoke super.init(ServletConfig). As a result, ServletConfig is not stored in the servlet instance, and subsequent calls to getServletConfig will return null.

Another variant of this mistake is to store the ServletConfig parameter in a class variable of the concrete servlet class:
private ServletConfig config;

public void init(ServletConfig config)
throws ServletException {
this.config = config;
System.out.println(" init(ServletConfig config) invoked.");
//do custom initialization ...
...
}
This is also wrong because config saved in the current servlet won't be available to its superclass, usually GenericServlet or HttpServlet. When the default implementation of getServletConfig() method looks for ServletConfig in superclasses, it's still null. The only way it can work is that you also override getServletConfig method to look in the concrete servlet class, which is unusual and unnecessary.

This is the error from Glassfish/SJSAS 9.0/JavaEE SDK 5, when running a servlet with those incorrect init methods:
java.lang.NullPointerException
test.HelloWorldServlet.doGet(HelloWorldServlet.java:14)
javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:73)
com.sun.enterprise.web.VirtualServerPipeline.invoke(VirtualServerPipeline.java:120)
org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:231)
To implement servlet init methods correctly, you have several options:
  • If you only need to save ServletConfig, do not override any init methods in servlet class. It's already implemented in servlet superclass GenericServlet.

  • If you have custom initialization work to do, override the no-arg init() method, and forget about init(ServletConfig). Is it ok to call getServletConfig() method inside the no-arg init() method? Yes, an instance of ServletConfig has already been saved by superclass GenericServlet. See Why Servlet init Methods are Confusing for more details.

  • If you really want to override init(ServletConfig), make sure you invoke super.init(ServletConfig);, usually as the first line, though it's not strictly required.

6/07/2006

6 Common Errors in Setting Java Heap Size

Two JVM options are often used to tune JVM heap size: -Xmx for maximum heap size, and -Xms for initial heap size. Here are some common mistakes I have seen when using them:

  • Missing m, M, g or G at the end (they are case insensitive). For example,
    java -Xmx128 BigApp
    java.lang.OutOfMemoryError: Java heap space
    The correct command should be: java -Xmx128m BigApp. To be precise, -Xmx128 is a valid setting for very small apps, like HelloWorld. But in real life, I guess you really mean -Xmx128m

  • Extra space in JVM options, or incorrectly use =. For example,
    java -Xmx 128m BigApp
    Invalid maximum heap size: -Xmx
    Could not create the Java virtual machine.

    java -Xmx=512m HelloWorld
    Invalid maximum heap size: -Xmx=512m
    Could not create the Java virtual machine.
    The correct command should be java -Xmx128m BigApp, with no whitespace nor =. -X options are different than -Dkey=value system properties, where = is used.

  • Only setting -Xms JVM option and its value is greater than the default maximum heap size, which is 64m. The default minimum heap size seems to be 0. For example,
    java -Xms128m BigApp
    Error occurred during initialization of VM
    Incompatible initial and maximum heap sizes specified
    The correct command should be java -Xms128m -Xmx128m BigApp. It's a good idea to set the minimum and maximum heap size to the same value. In any case, don't let the minimum heap size exceed the maximum heap size.

  • Heap size is larger than your computer's physical memory. For example,
    java -Xmx2g BigApp
    Error occurred during initialization of VM
    Could not reserve enough space for object heap
    Could not create the Java virtual machine.
    The fix is to make it lower than the physical memory: java -Xmx1g BigApp

  • Incorrectly use mb as the unit, where m or M should be used instead.
    java -Xms256mb -Xmx256mb BigApp
    Invalid initial heap size: -Xms256mb
    Could not create the Java virtual machine.
  • The heap size is larger than JVM thinks you would ever need. For example,
    java -Xmx256g BigApp
    Invalid maximum heap size: -Xmx256g
    The specified size exceeds the maximum representable size.
    Could not create the Java virtual machine.
    The fix is to lower it to a reasonable value: java -Xmx256m BigApp

  • The value is not expressed in whole number. For example,
    java -Xmx0.9g BigApp
    Invalid maximum heap size: -Xmx0.9g
    Could not create the Java virtual machine.
    The correct command should be java -Xmx928m BigApp
PS:

How to set java heap size in Tomcat?
Stop Tomcat server, set environment variable CATALINA_OPTS, and then restart Tomcat. Look at the file tomcat-install/bin/catalina.sh or catalina.bat for how this variable is used. For example,
set CATALINA_OPTS=-Xms512m -Xmx512m  (Windows, no "" around the value)
export CATALINA_OPTS="-Xms512m -Xmx512m" (ksh/bash, "" around the value)
setenv CATALINA_OPTS "-Xms512m -Xmx512m" (tcsh/csh, "" around the value)
In catalina.bat or catallina.sh, you may have noticed CATALINA_OPTS, JAVA_OPTS, or both can be used to specify Tomcat JVM options. What is the difference between CATALINA_OPTS and JAVA_OPTS? The name CATALINA_OPTS is specific for Tomcat servlet container, whereas JAVA_OPTS may be used by other java applications (e.g., JBoss). Since environment variables are shared by all applications, we don't want Tomcat to inadvertently pick up the JVM options intended for other apps. I prefer to use CATALINA_OPTS.

How to set java heap size in JBoss?
Stop JBoss server, edit $JBOSS_HOME/bin/run.conf, and then restart JBoss server. You can change the line with JAVA_OPTS to something like:
JAVA_OPTS="-server -Xms128m -Xmx128m"
How to set java heap size in Eclipse?
You have 2 options:
1. Edit eclipse-home/eclipse.ini to be something like the following and restart Eclipse.
-vmargs
-Xms64m
-Xmx256m
2. Or, you can just run eclipse command with additional options at the very end. Anything after -vmargs will be treated as JVM options and passed directly to the JVM. JVM options specified in the command line this way will always override those in eclipse.ini. For example,
eclipse -vmargs -Xms64m -Xmx256m
How to set java heap size in NetBeans?
Exit NetBeans, edit the file netbeans-install/etc/netbeans.conf. For example,
netbeans_default_options="-J-Xms512m -J-Xmx512m -J-XX:PermSize=32m -J-XX:MaxPermSize=128m -J-Xverify:none
How to set java heap size in Apache Ant?
Set environment variable ANT_OPTS. Look at the file $ANT_HOME/bin/ant or %ANT_HOME%\bin\ant.bat, for how this variable is used by Ant runtime.
set ANT_OPTS=-Xms512m -Xmx512m  (Windows)
export ANT_OPTS="-Xms512m -Xmx512m" (ksh/bash)
setenv ANT_OPTS "-Xms512m -Xmx512m" (tcsh/csh)
How to set java heap size in jEdit?
jEdit is a java application, and basically you need to set minimum/maximum heap size JVM options when you run java command. jEdit by default runs with a default maximum heap size 64m. When you work on large files, you are likely to get these errors:
java.lang.OutOfMemoryError: Java heap space
at java.lang.String.concat(String.java:2001)
at org.gjt.sp.jedit.buffer.UndoManager.contentInserted(UndoManager.java:160)
at org.gjt.sp.jedit.Buffer.insert(Buffer.java:1139)
at org.gjt.sp.jedit.textarea.JEditTextArea.setSelectedText(JEditTextArea.java:2052)
at org.gjt.sp.jedit.textarea.JEditTextArea.setSelectedText(JEditTextArea.java:2028)
at org.gjt.sp.jedit.Registers.paste(Registers.java:263)

How to fix it? If you click a desktop icon, or Start menu item to start jEdit: right-click the icon or menu item, view its property, and you can see its target is something like:
C:\jdk6\bin\javaw.exe -jar "C:\jedit\jedit.jar"
You can change that line to:
C:\jdk6\bin\javaw.exe -Xmx128m -Xms128m -jar "C:\jedit\jedit.jar"
If you run a script to start jEdit: just add these JVM options to the java line inside the script file:
java -Xmx128m -Xms128m -jar jedit.jar
If you start jEdit by running java command: just add these JVM options to your java command:
java -Xmx128m -Xms128m -jar jedit.jar
Note that when you run java with -jar option, anything after -jar jar-file will be treated as application arguments. So you should always put JVM options before -jar. Otherwise, you will get error:
C:\jedit>java -jar jedit.jar -Xmx128m
Unknown option: -Xmx128m
Usage: jedit [options] [files]
How to set java heap size in JavaEE SDK/J2EE SDK/Glassfish/Sun Java System Application Server?
Stop the application server, edit
$GLASSFISH_HOME/domains/domain1/config/domain.xml, search for XML element name java-config and jvm-options. For example,
<java-config suffix="...">
<jvm-options>-Xmx512m</jvm-options>
<jvm-options>-XX:NewRatio=2</jvm-options>
<jvm-options>-XX:MaxPermSize=128m</jvm-options>
...</java-config>
You can also change these settings in the web-based admin console, typically at http://localhost:4848/, or https://localhost:4848/. Go to Application Server near the top of the left panel, and then on the right panel, click JVM Settings | JVM Options, and you will see a list of existing JVM options. You can add new ones and modify existing ones there.

Yet another option is to use its Command Line Interface (CLI) tool command, such as:
./asadmin help create-jvm-options
./asadmin help delete-jvm-options
They may be a bit hard to use manually, but are well suited for automated scripts.

6/06/2006

3 Ways to Traverse a List

There are primarily 3 ways I can think of to traverse a java.util.List:

  • Using a traditional for loop;
  • Using a simplified for loop, or "foreach" statement in JDK 5 ;
  • Using java.util.Iterator
 public static void traverse(List data) {
System.out.println("Using simplified for loop/foreach:");
for(Object obj : data) {
System.out.println(obj);
}

System.out.println("Using for loop:");
for(int i = 0, n = data.size(); i < n; i++) {
System.out.println(data.get(i));
}

System.out.println("Using Iterator:");
for(Iterator it = data.iterator(); it.hasNext();) {
System.out.println(it.next());
}
}
I always use the for loop and JDK 5 enhanced for loop, and avoid using Iterator to traverse List. If you see other ways, feel free to add them in comment section.

PS:
Is there a foreach statement in JDK 1.5? Yes, but the word foreach is not a keyword in java, and I doubt it will ever be. Java foreach statement uses a simplified, or enhanced for loop (see examples above). Therefore, JDK 1.5 doesn't need to introduce new keywords like foreach, forEach, or for each, and foreach/forEach can continue to be used as valid identifier/variable names.

6/05/2006

A Bug's Life (11/2001 -- )

From time to time, I need to find and replace some string across the whole project. It hasn't been easy in NetBeans 3.x, 4.x, or 5.x. Last time I got stuck with this, I used JEdit to perform the task. Why is it not implemented in NetBeans while the same feature is already a natural part of Eclipse, JBuilder, or JEdit? Just look at this bug: Ability to search and replace text across multiple files and/or directories. I'm amazed by the its long and eventful life:

First opened in 11/2001, it has been delayed from 3.4, to TBD, 4.0, 4.1, 5.0, 5.5, and TBD again. It sports 57 votes by users, maybe the highest among all NetBeans bugs.

During this period, many duplicate bugs have been filed: 30166, 33075, 10021, 34044, and 72089.

pdxlooie put it best in its comments on March 1, 2006:

What I would like to know is, why has the NebBeans project management team refused to implement this feature? A specific answer to that question would allow us to address specific concerns. Otherwise, we're just like a dog chasing its tail...Finally, at some point, if this feature is just not going to be implemented, the project managers should declare "as designed," close this bug and reject all future bugs filed on this issue. Put a note in the FAQ and be done with it. As things stand presently, I easily can see this conversation continuing to 2010.
The most recent activity is a comment on 3/2006. Looks like NetBeans team has overcome the analysis paralysisis and got serious about implementing it. As a faithful NetBeans user, I look forward to the end of the bug's life.

NetBeans 5.5 is out (10/31/2006), but the fix is still not in. Now this bug is targeted to 6.0.

6/04/2006

NetBeans Keyboard Shortcuts on Refrigerator Door

I printed 2 copies of NetBeans Keyboard Shortcuts, and placed one above my desk in office, and the other on my refrigerator door at home. You can get the shortcut cheatsheet (PDF file) from withint NetBeans: Help | Keyboard Shortcuts. You can also directly open this PDF file in nb-install-dir/nb5.0/shortcuts.pdf.

Among the numerous shortcuts in NetBeans, I only use a small fraction, maybe 10%. From time to time when I stand in front of the frig, I will find some good shortcuts I didn't know, or I knew before but haven't used for a while. Here are just a few that I use in NetBeans Java editors, and some in XML editor as well:

Ctrl-L/K: insert next/previous matched word
Ctrl-E: delete current line
Ctrl-Del: delete the next word
Ctrl-Backspace: delete the previous word
Ctrl-Mouse Hover: make the current element a hyperlink

Missing from the default shortcuts for Save All, usually Ctrl-Shift-S. Although I can customize all shortcuts in NetBeans, I would like to see this out of the box. To customise NetBeans 5 keyboard shortcuts, go to Tools | Options, click keymap in the left panel, and choose from the list of keymaps (Eclipse, Emacs, NetBeans). In NetBeans 4.x, you need to open Tools | Keyboard Shortcuts.

A couple of times, I pressed Ctrl-Shift-S to save all files, without realizing this shortcut has not been defined. Then I couldn't figure out why my changes didn't show up when running the modified code. Even worse, custom keyboard shortcuts in my NetBeans 5.0 are not imported into NetBeans 5.5 beta. Are you telling me I need to recreate those shortcuts, or manually copy a config file?

6/03/2006

6 Ways of Setting Java Classpath

How to set Java classpath? List as many ways as you can. This can be an interesting Java job interview question. Here are what I came up with:

  1. Use -classpath JVM option:
    java -classpath C:\hello\build\classes com.javahowto.test.HelloWorld
  2. Use -cp JVM option, a short form of -classpath:
    java -cp C:\hello\build\classes com.javahowto.test.HelloWorld
  3. Use -Djava.class.path system property:
    java -Djava.class.path=C:\hello\build\classes com.javahowto.test.HelloWorld
  4. Use CLASSPATH environment variable:
    set CLASSPATH=C:\hello\build\classes;
    java com.javahowto.test.HelloWorld
  5. Use current directory as the default classpath:
    cd C:\hello\build\classes;
    java com.javahowto.test.HelloWorld
  6. Package all classes into a self-containing jar file that has this in its META-INF/MANIFEST.MF.
    Main-Class: com.javahowto.test.HelloWorld
    java -jar hello-world.jar
    Note: when you run java with -jar option, any -classpath, or -cp options, or CLASSPATH environment variable are ignored, as JVM thinks all classes are already contained inside the jar file, or referenced via the Class-Path entry in its META-INF/MANIFEST.MF.
Among all those options, I usually use option 1, and occasionally 6. I don't like my Java apps have dependency on environment settings like CLASSPATH, which is totally unpredictable. Using current directory as the default classpath is not a good idea either, since your classes may be scattered in several directories, folders and jar files.

Can I set classpath at java runtime dynamically and programmatically? No. Although you can set the system property java.class.path in your application, but its new value doesn't affect the system classloader. If you need to reset classpath, it's time to consider using a custom classloader as the child loader of the system classloader.

With a custom classloader, you have full control where to load class files, from local file system, remote url, or even database. Classes loaded by such a custom loader are only visible by this loader and its child loaders, but not to its parent loader nor the system classloader.

For the same reason, you can't dynamically set minimum/maximum heap size of a JVM. These JVM options are all set once when JVM is started and unchanged throughout its life.

JDK 6 now support * in classpath, so you can include all jar files in a directory to the classpath easily. But you may run into problems described in http://javahowto.blogspot.com/2006/07/jdk-6-supports-in-classpath-but-be.html


6/01/2006

Don't Show Me the Wrapper; Show Me the Inside

Are you frustrated with how exceptions and errors are reported in some Java applications? I know I am when I see some simple errors are wrapped and disguised multiple times. It seems as if Java exception has been used as a way to hide root causes from end users.

I've seen this in quite a few Java software, including some otherwise great ones. Take Glassfish (JavaEE SDK, Sun Java System Application Server 9.0) for example.

My JSP failed with the following exceptions when trying to open a connection on a DataSource:

javax.servlet.ServletException:
java.sql.SQLException:
Error in allocating a connection.
Cause: Connection could not be allocated because:
java.security.PrivilegedActionException :
Error opening socket to server localhost on port 1527 with message : null
org.apache.jasper.runtime.PageContextImpl.doHandlePageException(PageContextImpl.java:930)
org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:863)
org.apache.jsp.index_jsp._jspService(index_jsp.java:103)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:111)
javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:353)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:412)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:318)
javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:73)
com.sun.enterprise.web.VirtualServerPipeline.invoke(VirtualServerPipeline.java:120)
org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:231)
com.sun.enterprise.web.connector.grizzly.ProcessorTask.invokeAdapter(ProcessorTask.java:667)
com.sun.enterprise.web.connector.grizzly.ProcessorTask.processNonBlocked(ProcessorTask.java:574)
com.sun.enterprise.web.connector.grizzly.ProcessorTask.process(ProcessorTask.java:844)
com.sun.enterprise.web.connector.grizzly.ReadTask.executeProcessorTask(ReadTask.java:325)
com.sun.enterprise.web.connector.grizzly.ReadTask.doTask(ReadTask.java:250)
If you also get this error, I suggest you check if Derby/JavaDB database is started or not. I know it must be a user error on my part, since it's a pretty simple JSP and worked before. The stacktrace here shows 3 layers of nested exceptions:

1. javax.servlet.ServletException
2. java.sql.SQLException
3. java.security.PrivilegedActionException


But that's no problem, I said to myself. I sort of got used to it and usually go directly to the inner-most root cause. It would be java.security.PrivilegedActionException in this case. So I naturally thought I might have missed some socket-related security permission in my server.policy.

Wait, the SecurityManager is off by default, and my web app was running with no SecurityManager. How come I got this security exception? Well, the real reason was, I forgot to start Derby database. But this is not obvious anywhere in the output or server.log. I only see "with message : null" where the root cause should've been shown.

Below are just a few thoughts I have after this mini-adventure:
  • Connecting to database is executed inside a doPriviledged block, even when SecurityManager is off. Maybe we can optimize it away?

  • Calling PrivilegedActionException.getMessage() will almost always return a null, since PrivilegedActionException is just a wrapper exception, and usually constructed with no message. So any method that receives an instance of PrivilegedActionException should really peel if off to get the real cause, then either further throw out, or wrap up the real cause in another higher-level exception.

  • The same error message (not the real cause) was logged 3 times in a row in server.log. When it's not clear which component is responsible for logging errors, all parties do it. IMO, these logging entries are mostly redundant at INFO level, as the error message is already part of the exception. Less is better.

PS: To start the Derby/JavaDB database server bundled in Glassfish/J2EESDK/SJSAS, just run this command:
glassfish-install-dir/bin/asadmin start-database [--dbhost 0.0.0.0] [--dbport 1527]  [--dbhome
current_directory] [--echo=false] [--terse=false]
To stop the database server, run this command:
glassfish-install-dir/bin/asadmin stop-database [--dbhost 0.0.0.0] [--dbport 1527]
For more info, run the help command for the two sub-commands:
./asadmin help start-database
./asadmin help stop-database
Alternatively, you can also directly run Derby/JavaDB's own start scripts in derby-or-JavaDB/frameworks/NetworkServer/bin, but you are more likely to get ksh script errors. I have two suggestions if you can't start derby/javaDB:
  • Make sure you set environment variables JAVA_HOME and DERBY_INSTALL. For example,
    set JAVA_HOME=C:\jdk5
    set DERBY_INSTALL=C:\Sun\Appserver\derby
  • On Solaris or Linux, do not directly run startNetworkServer.ksh, stopNetworkServer.ksh, or other ksh scripts. Instead, run ksh command with these scripts as parameter. For example,
    ksh setNetworkServerCP.ksh
    ksh ij.ksh
    Why? because ksh in your system may be in /usr/bin/ksh, or /bin/ksh, but Derby ksh scripts don't the usual first line like #! /bin/ksh, or #! /usr/bin/ksh. When derby has trouble finding the correct ksh, it reports some totally confusing error:
    CLASSPATH: Undefined variable.
    This is because Derby is using some ksh script (setNetworkClientCP.ksh) to set the environment variabel $CLASSPATH, and failed. So it complains CLASSPATH: Undefined variable. Any way, the lesson is to always run derby/javadb ksh scripts with ksh command.

A Noteworthy Javac Bug Fix in JDK 5.0 Update 7

JDK 5.0 update 7 (JDK 1.5.0_07) was released last week. As I skim through the release note and bug fixes, I noticed this Javac bug:

6295519: javac throws ZipException when you have invalid files in classpath

The root cause is that Javac assumes all files in the classpath are jar files or zip files, and tries to open them using java.util.zip API, which will fail if the classpath contains other types of files, such as *.class, *.so, etc. From the bug description, it was a regression from JDK 1.4.0_01, and exists from 1.4.0_02 to 1.5.0_06. Note that it is NOT a Java bug, but a Javac bug. I did a quick test on my 5.0_06 Javac, and was able to reproduce the same error:

error: error reading /appserver/lib/LauncherBootstrap.class;
java.util.zip.ZipException: error in opening zip file
1 error
It's pretty common to fall victim to this bug. For example, suppose you wan to include all files under /appserver/lib in Javac classpath, so you would write compile target like:
<target name="compile">
<javac srcdir="src" destdir="build/classes" includes="**/*.java">
<classpath>
<fileset dir="/appserver/lib" includes="*"/>
</classpath>
</javac>
</target>
Your intention is to include all *.jar and *.zip files under /appserver/lib directory. But this directory may also include *.so files and even *.class files, which will cause this bug. To work around this bug, you will need to be more specific: includes="*.jar, *.zip".