12/10/2006

Cygwin Setup Hangs at 00bash.sh

I tried to install the latest cygwin from cygwin.com. The setup window hangs forever at 98% while running 00bash.sh. When I click the cancel button, the popup window says something like the cygwin has been installed successfully. But I can't even start a bash terminal, which means the installation has not completed successfully.

I then googled with "cygwin install hang 00bash.sh", and the best result seems to be this link:
http://www.cygwin.com/ml/cygwin/2006-10/msg00675.html

It suggests running each step in 00bash.sh manually, which I did and got the same result.

I've been using cygwin for years and this is the first time I've seen this error. The next morning, I came to realize the culprit could be Norton Internet Security 2006. After I disable it, everything is working all right.

The bash program calls some network services (uname.exe, find.exe, hostname.exe), which are by default blocked by Norton Internet Security 2006 without giving user the options.

10/31/2006

Custom String Values for Enum

The default string value for java enum is its face value, or the element name. However, you can customize the string value by overriding toString() method. For example,

public enum MyType {
ONE {
public String toString() {
return "this is one";
}
},

TWO {
public String toString() {
return "this is two";
}
}
}
Running the following test code will produce this:
public class EnumTest {
public static void main(String[] args) {
System.out.println(MyType.ONE);
System.out.println(MyType.TWO);
}
}

this is one
this is two
Another interesting fact is, once you override toString() method, you in effect turn each element into an anonymous inner class. So after compiling the above enum class, you will see a long list of class files:
MyType.class
MyType$1.class
MyType$2.class

10/25/2006

Java Enum and Its Superclass

All java enum E implicitly extends java.lang.Enum<E>. Since java doesn't allow multiple inheritance, enum types can't have superclass. They can't even extend from java.lang.Enum, nor java.lang.Object. It also means enum A can't inherit or extend enum B.

For example, the following is an invalid enum declaration:

public enum MyType extends Object {
ONE, TWO
}
Compiler error:
MyType.java:3: '{' expected
public enum MyType extends Object {
MyType.java:6: identifier expected
2 errors
The correct form should be:
public enum MyType {
ONE, TWO
}
More posts about java enum:
Tags: ,

10/11/2006

Use library-directory in JavaEE 5 apps

Starting from JavaEE 5, you can specify a directory inside the an EAR file to hold jar files that can be used by all modules of that EAR. For example, if you've built an EAR called hello.ear:

hello.ear:
META-INF/application.xml
shared/util-1.jar
shared/util-2.jar
hello-ejb.jar
hello.war
The content of META-INF/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="5"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/application_5.xsd">

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

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

<library-directory>shared</library-directory>
</application>
All classes and resources in all jar files under shared directory (e.g., util-1.jar, util-2.jar) are accessible to hello-ejb.jar and hello.war. For instance, you may choose to put hello-ejb's business interfaces, component interfaces and home interfaces in util-1.jar, to avoid duplicates in ejb jar and war.

Better yet, you don't even need to have an application.xml to use this mechanism. Recall that all descriptors are optional in JavaEE 5. The default library-directory is a directory named lib inside the EAR. You just put your shared jar files here and they will be automatically made accessible for all components. However, these files do need to use *.jar extension.

9/29/2006

Inject EntityManagerFactory and EntityManagerFactory to Servlet

You can inject EntityManagerFactory and/or EntityManager into a Servlet, but be aware of the thread-safe issue. A servlet can service multiple concurrent requests using multiple threads. So all instance variables are shared by all these threads, which may cause undesired side-effect.

The good news is, all methods in EntityManagerFactory interface are thread-safe. Therefore, you can safely inject EntityManagerFactory into a servlet instance variable.

public class MyServlet extends HttpServlet {
//this is thread-safe
@PersistenceUnit(unitName="my-pu")
private EntityManagerFactory emf;
However, methods in EntityManager interface are not thread-safe, and may not be shared among multiple concurrent requests. Therefore, do not inject EntityManager into a servlet instance variable.
//this is not thread-safe, and avoid it
@PersistenceContext(unitName="my-pu")
private EntityManager em;
Having said that, you can still inject EntityManager at the servlet class type level, and look it up when needed during request processing.
@PersistenceContext(unitName="my-pu", name="persistence/em")
public class MyServlet extends HttpServlet {
protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {
InitialContext ic = new InitialContext();
EntityManager em =
(EntityManager) ic.lookup("java:comp/env/persistence/em");
} catch (NamingException ex) {
...
}
}
}

9/22/2006

Resource Injection in Servlet Filter

You can inject EJB or resources into servlet filters, in the same way as injecting into servlet classes. Things that can be injected include EJB 3 stateless and stateful session beans, EJB 2.1 Home, DataSource, ORB, UserTransaction, javamail session, JMS ConnectionFactory, JMS Queue, JMS Topic, etc. For example,

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import javax.ejb.EJB;
import javax.annotation.Resource;
import javax.sql.DataSource;

public class MyServletFilter implements Filter {
@EJB(beanName="HelloBean")
private HelloRemote helloBean;

@Resource(mappedName="jdbc/__default")
private DataSource ds;

private FilterConfig filterConfig;

public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
PrintWriter pw = response.getWriter();
helloBean.hello();

try {
chain.doFilter(request, response);
} catch(RuntimeException e) {
throw e;
} catch(Exception e) {
throw new ServletException(e);
}
}
public void destroy() {
}

public void init(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
}
}

9/20/2006

Default JNDI Name for Resource or EJB Injection

When a resource or EJB reference is injected to a component class, not only can you directly use this variable, you can also lookup with JNDI. For example,

package demo;
public class FooServlet extends HttpServlet {
@Resource(name="jdbc/mysql-ds")
private DataSource dataSource;

protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
Connection con = null;
Connection conn = null;
try {
InitialContext ic = new InitialContext();
DataSource ds =
(DataSource) ic.lookup("java:comp/env/jdbc/mysql-ds");

//the following 2 statements work the same:
conn = dataSource.getConnection();
con = ds.getConnection();
} catch (NamingException ex) {
throw new ServletException(ex);
} catch (SQLException ex) {
throw new ServletException(ex);
} finally {
//close con conn here
}
}
}
If the name() field is not specified for @EJB or @Resource, the default jndi name is not the variable name; instead, it's in the form of fully-qualified-classname/variable-name. Therefore, using default jndi name, the above example needs to be rewrite as follows:
package demo;
public class FooServlet extends HttpServlet {
@Resource
private DataSource dataSource;

protected void doGet(
HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
Connection con = null;
Connection conn = null;
try {
InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup(
"java:comp/env/demo.FooServlet/dataSource");

//the following 2 statements work the same:
conn = dataSource.getConnection();
con = ds.getConnection();
} catch (NamingException ex) {
throw new ServletException(ex);
} catch (SQLException ex) {
throw new ServletException(ex);
} finally {
//close con conn here
}
}
}

9/15/2006

Reverse a String with StringBuilder or StringBuffer

Both StringBuilder and StringBuffer class have a reverse method. They work pretty much the same, except that methods in StringBuilder are not synchronized. This is the signature of StringBuilder.reverse:

public StringBuilder reverse()
This method returns an instance of StringBuilder. The interesting thing is, the returned value is actually itself. So what this method does is, it reverses itself and returns itself. This is the source code for StringBuilder.reverse:
public StringBuilder reverse() {
super.reverse();
return this;
}
In the following example, the old and the reversed string will print the same content after calling reverse method:
public static void main(String[] args) {
StringBuilder old = new StringBuilder(args[0]);
StringBuilder newsb = old.reverse();
System.out.println("original string: " + old);
System.out.println("new string: " + newsb);
}
If you still need to access the original content of the StringBuilder, you will need to save it to string before invoking sb.reverse().

9/12/2006

System getenv() reinstated in JDK 1.5

In this previous post, I wrote about using System.getenv(key) to get OS environment variable. Actually, System.getenv(key) is not new. It was introduced at the very beginning of JDK, deprecated in JDK 1.2, and reinstated in JDK 1.5 (aka JDK 5).

The reason why it was deprecated, according to JDK 1.3 javadoc:

Deprecated. The preferred way to extract system-dependent information is the system properties of the java.lang.System.getProperty methods and the corresponding getTypeName methods of the Boolean, Integer, and Long primitive types.
Then bug 4199068 was submitted shortly after, requesting this method to be reinstated. Due to popular demand, this method was eventually reinstated in JDK 1.5.
Tags: , ,

9/08/2006

Is a String Empty?

Prior to JDK 6, we can check if a string is empty in 2 ways:

  • if(s != null && s.length() == 0)
  • if(("").equals(s))
Checking its length is more readable and may be a little faster. Starting from JDK 6, String class has a new convenience method isEmpty():
boolean isEmpty()
Returns true if, and only if, length() is 0.
It is just a shorthand for checking length. Of course, if the String is null, you will still get NullPointerException. I don't see much value in adding this convenience method. Instead, I'd like to see a static utility method that also handle null value:
public static boolean notEmpty(String s) {
return (s != null && s.length() > 0);
}

9/03/2006

A Bad Way of Getting Java System Property

I don't like code like this:

String flag1 = System.getProperties().getProperty("flag1.key");
String flag2 = System.getProperties().getProperty("flag2.key");
Why not just call System.getProperty("flag1.key")? In most cases, the two produces the same result. But the former is unnecessarily verbose.

You may be able to save some security check overhead by caching the system properties inside your application, if you need to get a series of properties, and if SecurityManager is enabled. For example,
Properties sysProps = System.getProperties();
String flag1 = sysProps.getProperty("flag1.key");
String flag2 = sysProps.getProperty("flag2.key");
...
The security check is only performed once in the above block. But it requires both read and write permission on all system properties: permission java.util.PropertyPermission "*", "read,write";

With System.getProperty("key"), security check is performed for every call. But it only requires read permission on the target property: permission java.util.PropertyPermission "key", "read";

The savings may be negligible, if any. The only time I will use System.getProperties() is when the property key is unknown, and the application needs to iterate through all system properties based on some criteria. For example,
Properties myProps = new Properties();
Properties sysProps = System.getProperties();
Set<Map.Entry<String, String>> entrySet =
sysProps.entrySet();
while(entrySet.hasNext()) {
Map.Entry entry = entrySet.next();
String key = entry.getKey();
if(key.startsWith("foo.")) {
myProps.setProperty(key, entry.getValue(key));
}
}

8/30/2006

How to Remove a System Property

I thought I could remove a system property by setting its value to null, but it turns out that System.setProperty(key, val) will throw NullPointerException when either key or value is null. Here are 2 ways to remove or clear a system property:

1. clearProperty(String key) introduced in JDK 1.5.

System.clearProperty("key");
2.
Properties sysProps = System.getProperties();
sysProps.remove("key");
When java security manager is enabled, Both methods need specific permissions to succeed. Approach 1 requires PropertyPermission "key", "read,write". Approach 2 requires a much wider permission: PropertyPermission "*", "read,write", because you are free to modify/remove all system properties with the returned object from System.getProperties()

8/28/2006

What is the Superclass of StringBuilder and StringBuffer

Their javadoc page shows they both extend from Object:

public final class StringBuilder
extends Object
implements Serializable, CharSequence

public final class StringBuffer
extends Object
implements Serializable, CharSequence
Is Object really the direct superclass of StringBuilder and StringBuffer? Since the two string-related classes are so similar (see my previous post for a short comparison), doesn't it make sense for them to have a common superclass?

Yes, they do share a common abstract superclass, java.lang.AbstractStringBuilder, as revealed in their source code:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence {
/** use serialVersionUID for interoperability */
static final long serialVersionUID = 4383685877147921099L;
...
AbstractStringBuilder is an abstract class with package access type:
abstract class AbstractStringBuilder
implements Appendable, CharSequence {

8/25/2006

StringBuilder vs StringBuffer

StringBuilder was introduced in JDK 1.5. What's the difference between StringBuilder and StringBuffer? According to javadoc, StringBuilder is designed as a replacement for StringBuffer in single-threaded usage. Their key differences in simple term:

  • StringBuffer is designed to be thread-safe and all public methods in StringBuffer are synchronized. StringBuilder does not handle thread-safety issue and none of its methods is synchronized.

  • StringBuilder has better performance than StringBuffer under most circumstances.

  • Use the new StringBuilder wherever possible.
Other than that, the two classes are remarkably similar with compatible API. It seems the author just copied StringBuffer.java to StringBuilder.java, removing all occurrences of "synchronized". Here is a little trace left in StringBuilder.readObject method:
/**
* readObject is called to restore the state of the
* StringBuffer from a stream.
*/
private void readObject(java.io.ObjectInputStream s)
Note the above javadoc still refers to StringBuffer where it should be StringBuilder.
2 interesting methods in StringBuilder or StringBuffer are reverse() and equals(Object):
  • reverse() method modifies the current this StringBuilder/StringBuffer, and also returns itself. More details in Reverse a String with StringBuilder or StringBuffer

  • equals(Object) method is not implemented (overridden) in StringBuilder/StringBuffer. So comparing 2 instances of the same content will return false. Also there is no equalsIgnoreCase() method on StringBuilder/StringBuffer. However, toString() is implemented to return a string representing the data in this StringBuilder/StringBuffer.

8/23/2006

To Use or not to Use Jar Index

Starting from JDK 1.3, you can generate a index file for jar files. The purpose is to speed up classloading, just as indexed columns in database tables do to queries. Running this jar command generates a META-INF/INDEX.LIST for an existing jar file:

/cygdrive/c/tmp > dir main.jar
main.jar
/cygdrive/c/tmp > jar i main.jar
/cygdrive/c/tmp > jar tf main.jar
META-INF/INDEX.LIST
...
Note that you cannot generate index while you are creating or updating the jar file. It has to be a separate step. You can only use the i options, not to be combined with other options like vf. For a complete documentations, see here.

Sounds like a great feature. Then I checked all jars in JDK 5, just to find none of the JDK jars has INDEX.LIST. I would think big jars like rt.jar would benefit from this feature. The only drawback I can think of is, when the jar file is subsequently updated, INDEX.LIST should also be updated to reflect any changes in class names. But normally users won't update rt.jar.

In JBoss 4.0.4, while most jar files do not have INDEX.LIST either, quite a few library and service archives do:
client/jacorb.jar
server/default/deploy/jbossweb-tomcat55.sar/jasper-compiler-jdt.jar
server/default/deploy/jbossweb-tomcat55.sar/servlets-webdav.jar
...
What factors keep people from using jar index file?

8/21/2006

Set JVM Options in JBuilder

First, you can view the current settings by running jbuilder executable with -info option, which dumps all JVM options and system properties before starting IDE. For JVM settings, look for output launcher.config.params

You can backup and edit install-dir/bin/jbuilder.config to modify the IDE JVM settings, among other things. The entries of interest are:

# Tune this VM to provide enough headroom to work on large
# applications
vmmemmin 32m
vmmemmax 75%
vmparam -XX:MaxPermSize=128m
I found these property names and value formats a little awkward. As their names suggests, vmmemmin 32m maps to JVM option -Xms32m, and vmmemmax 75% maps to JVM option -Xmx750m. The latter is confusing and seems to be a result of 1000m (physical memory) * 75%. It turns out you may also use the absolute amount like vmmemmax 750m.

But it seems I can't use the percentage format for initial heap size. For example, vmmemmin 12% would result in the wrong JVM option -Xmx120m.

If you want to specify additional JVM options, you need to add more vmparam entries (they are additive). For example, if you want to specify space for PermGen, in addition to heap size:
vmparam -XX:MaxPermSize=128m
vmparam -XX:PermSize=64m
Including them into the same entry value would result in syntax error. The following line is wrong:
vmparam -XX:MaxPermSize=128m -XX:PermSize=64m
Unrecognized VM option 'MaxPermSize=128m -XX:PermSize=64m'
Unable to create JVM.

8/18/2006

jps: JVM Process Status Tool

jps was introduced in JDK 1.5 on all platforms. I used to run ps -ef | grep java all the time to get the pid of java programs, or just to see if my appserver is running or not. Now with jps, I no longer need this ps+grep, or windows task manager.

# list pid and short java main class name
C:\>jps
2008 Jps
2020 Bootstrap

# list pid and fully-qualified java main class
C:\>jps -l
2160 sun.tools.jps.Jps
2020 org.apache.catalina.startup.Bootstrap

# pid, full main class name, and application arguments
C:\>jps -lm
2152 org.apache.catalina.startup.Bootstrap start
2024 sun.tools.jps.Jps -lm

# pid and JVM options
C:\>jps -v
1320 Jps -Dapplication.home=C:\tools\jdk5 -Xms8m
2152 Bootstrap -Djava.endorsed.dirs=c:\tools\jakarta-tomcat-5\common\endorsed
-Dcatalina.base=c:\tools\jakarta-tomcat-5
-Dcatalina.home=c:\tools\jakarta-tomcat-5
-Djava.io.tmpdir=c:\tools\jakarta-tomcat-5\temp
So jps -lvm gives the similar result to /usr/ucb/ps -xxxwww.

It's a little annoying to see jps itself is included in the output. Running jps with no options just lists the short name of the java main class, which isn't very useful in most cases. You can't tell org.netbeans.Main from another.Main.

However, if you are running Tomcat 6.x or 7.x, and using JDK 1.6.0_23 or 1.6.0_24, Tomcat process will not show up in the jps output, or jvisualvm screen. This is because Tomcat uses a custom java.io.tmpdir (-Djava.io.tmpdir=$CATALINA_BASE/temp) but jps and jvisualvm in JDK 1.6.0_23 or 1.6.0_24 always looks for java processes under the default java.io.tmpdir(the file hsperfdata_$USER). For more details, see https://issues.apache.org/bugzilla/show_bug.cgi?id=50518

It's about jps, not jsp. For more details, see jps docs here.

8/16/2006

Save Exception StackTrace to a String

How to convert the stacktrace of an exception to string? Sometimes I need to save it as a string so I can fit it into certain API. The following is how I would do it. The resulted string contains the same information you would normally see when the stacktrace is printed. All nested causal exceptions should also be included.

public String stackTraceToString(Throwable e) {
String retValue = null;
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
e.printStackTrace(pw);
retValue = sw.toString();
} finally {
try {
if(pw != null) pw.close();
if(sw != null) sw.close();
} catch (IOException ignore) {}
}
return retValue;
}

8/11/2006

Access Glassfish DataSource Remotely from Java Applications

My previous post describes how to access a MySql datasource in JBoss from within a standalone java application. This is also supported by Glassfish and Sun Java System Application Server:

1. start MySQL server. If a test database is not present, create it (inside mysql console, run create database test;)

2. put MySQL Connector/J jar (mysql-connector-java-5.0.3-bin.jar) in %SJSAS_HOME%\domains\domain1\lib\ext, and restart Glassfish server.

3. create a jdbc-connection-pool and a jdbc-resource in admin web GUI, which is usually at http://localhost:4848. Setting allow-non-component-callers to true on the jdbc-connection-pool tells Glassfish to open this datasource for remote access, and its JNDI name will be used for remote lookup without java:comp/env prefix. Note that for GlassFish 3.x, allow-non-component-callers property is not needed for standalone client any more.

Alternatively, you can also do it in command line. Edit a file \tmp\mysql-ds.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//Sun Microsystems Inc.//DTD Application Server 9.0 Domain//EN"
"file:///C:/Sun/AppServer/lib/dtds/sun-resources_1_2.dtd">
<resources>
<jdbc-connection-pool allow-non-component-callers="true"
name="mysql-pool"
datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"
non-transactional-connections="false"
res-type="javax.sql.DataSource" >
<property name="datasourceName" value="mysql"/>
<property name="user" value="root"/>
<property name="password" value="your-password"/>
<property name="port" value="3306"/>
<property name="databaseName" value="test"/>
<property name="serverName" value="localhost"/>
</jdbc-connection-pool>
<jdbc-resource enabled="true" pool-name="mysql-pool"
jndi-name="jdbc/mysql"/>
</resources>
Then run this command to create datasource:
%SJSAS_HOME%\bin\asadmin add-resources \tmp\mysql-ds.xml
4. java client is the same as the one for JBoss, and compile it with the same command.

5. run it with JDK 6 so that I can use wildcard (*) in classpath and don't have to figure out which SJSAS jars to include (see my previous post on this feature):
java -cp C:\ws\nb\scrap\build\classes;%SJSAS_HOME%\lib\*;%SJSAS_HOME%\imq\lib\*;%SJSAS_HOME%\domains\domain1\lib\mysql-java\mysql-connector-java-3.1.13-bin.jar
com.foo.DataSourceTest
Query 'select version()' returned 5.0.18
With JDK 1.5 or earlier, you need to explicitly list all jars in classpath. You need at least javaee.jar, appserv-rt.jar, and a jms-related jar in %SJSAS_HOME%\imq\lib. appserv-rt.jar contains a jndi.properties that configures the client-side naming service provider, and is equivalent to build\classes\jndi.properties in JBoss example. I couldn't find out which jms jar right away, so I use the shortcut classpath format in JDK 6.

If the client and Glassfish server are not on the same host, or Glassfish naming service is not on the default port (3700), then add the two system properties to the above java command:
-Dorg.omg.CORBA.ORBInitialHost=remote-server-host-name
-Dorg.omg.CORBA.ORBInitialPort=server-naming-service-port
GlassFish 3.x provides a gf-client.jar, which is basically empty itself but references (via its META-INF/MANIFEST.MF) ALL GlassFish jar files. In GlassFish 3.1, it's in $GLASSFISH_HOME/lib/gf-client.jar; in GlassFish 3.0.1, it's $GLASSFISH_HOME/modules/gf-client.jar. One can use it in client classpath to include all GlassFish jars. The following is examples with GlassFish 3.1:

javac -cp $GLASSFISH_HOME/lib/gf-client.jar:$GLASSFISH_HOME/domains/domain1/lib/ext/mysql-connector-java-5.1.15-bin.jar:. test/DataSourceTest.java

java -cp $GLASSFISH_HOME/lib/gf-client.jar:$GLASSFISH_HOME/domains/domain1/lib/ext/mysql-connector-java-5.1.15-bin.jar:. test.DataSourceTest

8/08/2006

Access JBoss DataSource Remotely from Java Applications

Sometimes people need to access appserver datasource remotely from applications outside J2EE containers. JBoss adds this feature as of 4.0.0, according to JBoss Wiki page. Here are the steps to remotely access a mysql datasource deployed in JBoss 4.0.1:

1. start MySQL server. If a test database is not present, create it (inside mysql console, run create database test;)

2. put MySQL Connector/J jar (mysql-connector-java-5.0.3-bin.jar) in %JBOSS_HOME%\server\default\lib

3. back up and edit %JBOSS_HOME%\docs\examples\jca\mysql-ds.xml

...
<jndi-name>jdbc/mysql</jndi-name>
<use-java-context>false</use-java-context>
<connection-url>jdbc:mysql://localhost:3306/test</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password></password>
...
Set use-java-context to false. This tells JBoss to open this datasource for remote access, and its JNDI name will be used for remote lookup without using java:/ prefix.

4. copy modified mysql-ds.xml to %JBOSS_HOME%\server\default\deploy

5. code java client:
package com.foo;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class DataSourceTest {
public static void main(String[] args) throws Exception {
testDataSource();
}

private static void testDataSource()
throws NamingException, SQLException {
final String sql = "select version()";
InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup("jdbc/mysql");
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
con = ds.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery(sql);
while(rs.next()) {
System.out.println("Query '" + sql + "' returned " + rs.getString(1));
}
} finally {
if(rs != null) rs.close();
if(stmt != null) stmt.close();
if(con != null) con.close();
}
}
}
To compile it:
C:\ws\nb\scrap\src\com\foo>
javac -d C:\ws\nb\scrap\build\classes DataSourceTest.java
6. create a jndi.properties and put it in classpath (C:\ws\nb\scrap\build\classes):
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=jnp://localhost:1099
7. run it with JDK 6 so that I can use wildcard (*) in classpath and don't have to figure out which jboss jars to include (see my previous post on this feature):
java -cp C:\ws\nb\scrap\build\classes;%JBOSS_HOME%\lib\*;%JBOSS_HOME%\server\default\lib\* com.foo.DataSourceTest
Query 'select version()' returned 5.0.18

8/07/2006

New IOException Constructors in JDK 6

In JDK 1.5 or earlier, IOException only has 2 constructors: IOException() and IOException(String s). So you can't pass a cause exception or throwable to these constructors. To do that, you will need to do something like this:

ZipException ze = new ZipException("Nested ZipException");
IOException e = new IOException("IOException with nested ZipException");
e.initCause(ze);
throw e;
In JDK 6, 2 additional constructors are added:
IOException(String s, Throwable cause)
IOException(Throwable cause)
So the following can compile and run fine in JDK 6 and later:
ZipException ze = new ZipException("Nested ZipException");
IOException e = new IOException(ze);
throw e;
but will fail to compile in JDK 1.5 or earlier:
/cygdrive/c/tmp > javac -version A.java
javac 1.5.0_06
A.java:15: cannot find symbol
symbol : constructor IOException(java.util.zip.ZipException)
location: class java.io.IOException
IOException e = new IOException(ze);
^
1 error
See IOException javadoc in JDK 1.5 and JDK 6.

8/04/2006

Another Way to Access Environment Variables from Java Applications

How to pass environment variables to java applications? The traditional way is to pass them as system properties when starting the JVM (e.g., java -DCVSROOT=$CVSROOT ...), and then your java class can access it via System.getProperty("CVSROOT")

Starting from JDK 1.5, os environment variables can be directly accessed from within java. 2 new methods in java.lang.System are introduced for this purpose. Actually, getenv() first appeared in JDK 1, was then deprecated, and reinstated in JDK 5 (See this blog entry).

Before you go ahead testing them in your Foo.java, remember the method name is getenv, not getEnv.

I made the mistake when testing them:
C:\tmp > javac A.java
A.java:4: cannot find symbol
symbol : method getEnv(java.lang.String)
location: class java.lang.System
System.out.println(System.getEnv("CVSROOT"));
^
1 error

7/28/2006

How to Set Ant Options in NetBeans

Sometimes I need to ask NetBeans to run ant with -verbose or -debug options, when building or running a project. It can be set at Tools | Options | Misc | Ant Options

This took me a while to find out. I first thought it must be somewhere in the project property, similar to how I set java compiler option and java runtime options.

As it turns out, ant options in NetBeans are global, and any changes will affect all projects. I'm wondering if it makes sense to allow it to be set at project level as well, with project-wide setting overriding IDE-wide setting.

Why I don't Need toplink-essentials.jar in classpath

I use TopLink bundled inside Glassfish. Compiling and running java persistence application is easy, even when done from command line, as you can see from the previous post.

At compile time, I only have toplink-essentials-agent.jar in classpath. This jar only contains a java agent class:
oracle/toplink/essentials/internal/ejb/cmp3/JavaSECMPInitializerAgent.clasBut its META-INF/MANIFEST.MF contains a Class-Path entry , in addition to a Premain-Class entry:

Premain-Class: oracle.toplink.essentials.internal.ejb.cmp3.JavaSECMPIn
itializerAgent
Class-Path: toplink-essentials.jar
<other entries omitted here>
So the class loader will follow this reference and load toplink-essentials.jar in the same directory. toplink-essentials.jar contains all the Java Persistence API and implementation classes.

At runtime, I don't even have any toplink jar in classpath. Because we use -javaagent:toplink-essentials-agent.jar, and any agent jars are automatically appended to the classpath. I wrote this post on -javaagent: option. So runtime classpath only needs to include application classes and jdbc driver jar.

In NetBeans 5.5, I found I do need to include both toplink-essentials-agent.jar and toplink-essentials.jar in project library. Otherwise, NetBeans can't find JPA classes.

HelloWorld with JPA, Hibernate and MySql

An earlier entry shows how to write a simple java application using Java Persistence API (JPA), Toplink and MySql. Now let's try Hibernate Entity Manager.

Entity class and main class remain the same. persistence.xml is different since we will need to specify Hibernate-specific properties:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="hello-world" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>com.foo.Greeting</class>
<properties>
<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test"/>
<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
<property name="hibernate.connection.password" value=""/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
</properties>
</persistence-unit>
</persistence>
Compile classpath is different, since we need to use Hibernate jars. Strictly speaking, javac only needs JPA API classes in the classpath. But just to be consistent with runtime, it does't hurt to include all 17 Hibernate jar files.

To save some typing, I decided to use * wildcard in classpath, a new feature in JDK 6 (see my previous post):
C:\ws\nb\scrap\src\com\foo> javac
-d C:\ws\nb\scrap\build\classes
-classpath "C:\tools\hibernate-em\*"
Greeting.java HelloWorld.java
Note that the "" around the classpath element is needed. Otherwise, you will get javac error: invalid flag.

You also need to have a log4j.properties file in classpath. I have it in build/classes/log4j.properties:
log4j.rootCategory=WARN, dest1
log4j.appender.dest1=org.apache.log4j.ConsoleAppender
log4j.appender.dest1.layout=org.apache.log4j.PatternLayout
Unlike TopLink, Hibernate doesn't use -javaagent: option. First start MySql on localhost, then
C:\ws\nb\scrap\build\classes> java -cp
C:\tools\hibernate-em\*;C:\tools\mysql-java\mysql-connector-java-3.1.13-bin.jar;.
com.foo.HelloWorld
createDDL.jdbcO-:-Ofalse
dropDDL.jdbcO-:-Ofalse
log4j.propertiesO-:-Ofalse
META-INF/persistence.xmlO-:-Ofalse
No configuration found. Configuring ehcache from ehcache-failsafe.xml found in
the classpath: jar:file:/C:/tools/hibernate-em/ehcache-1.2.jar!/ehcache-failsafe.xml
Query returned: Greeting id=1, message=hello world, language=en
The 17 jars used by Hibernate Entity Manager are:
antlr-2.7.6.jar, commons-collections-2.1.1.jar, hibernate-annotations.jar, jta.jar, asm-attrs.jar, commons-logging-1.0.4.jar, hibernate-entitymanager.jar, log4j-1.2.11.jar, asm.jar, dom4j-1.6.1.jar, hibernate3.jar, c3p0-0.9.0.jar, ehcache-1.2.jar, javassist.jar, cglib-2.1.3.jar, ejb3-persistence.jar, jboss-archive-browsing.jar


This blog talks about doing all these in NetBeans 5.5 beta 2, very helpful.

JDK 6 Supports * in Classpath But Be Aware of This

Finally JDK 6 (Mustang) lets me use wildcard (*) in classpath. I no longer have to enumerate all jar files in a lib directory. But with this added feature, it's easier to get errors like "javac: invalid flag:" For example, I want to complile a simple servlet, including all jar files in C:\Sun\AppServer\lib:

C:\tmp> javac -cp C:\Sun\AppServer\lib\*
HelloWorldServlet.java
javac: invalid flag: C:\Sun\AppServer\lib\addons
Usage: javac <options> <source>
use -help for a list of possible options
What's happening here is, the OS expands * to all files/directories in C:\Sun\AppServer\lib, since * has special meaning for the OS. So the real command resolves to:
javac -cp C:\Sun\AppServer\lib\activation.jar
C:\Sun\AppServer\lib\addon
C:\Sun\AppServer\lib\admin-cli.jar
C:\Sun\AppServer\lib\ant ... HelloWorldServlet.java
As all files/directories are expanded alphabetically, the first one (activation.jar) is treated as classpath element, and everything else starting with addon are treated as separate JVM options. Of course these are invalid JVM options, hence javac: invalid flag:

Note: if you are using javaw.exe instead of java.exe, * wildcard expansion in -cp or -classpath doesn't work, due to JDK 6 bug 6510337

So here are some tricks how to use * in classpath without getting burned:
  • "*" and "*.jar" have the same effect when used in classpath.
  • Quote it, if your classpath has a single element that uses *. For example,
    javac -cp "C:\Sun\AppServer\lib\*" HelloWorldServlet.java
  • This problem doesn't exist if you have more than one element in classpath, even though they do not exist. When in doubt, always quote your classpath value that uses wildcard.
    javac -cp C:\Sun\AppServer\lib\*;\nosuchdir HelloWorldServlet.java
  • This problem exists for all JDK tools that takes classpath option, including java. The error is different but solution is the same as above.
    java -cp C:\temp\* A
    Exception in thread "main" java.lang.NoClassDefFoundError: C:\temp\b/jar
  • Of course, if the directory contains 1 jar file, it will be expanded correctly, and you don't need to quote it. But using * doesn't save you much typing:
    java -cp C:\single-jar-file-folder\* A
    this is A
  • If on Unix, whether to quote or not quote wildcard classpath depends on which shell you are using. For example, tcsh or csh may fail with the following error: "java: No match." But the exact same command works under bash:
    java -cp tmp:$HOME/modules/*.jar Test abc

7/27/2006

HelloWorld with JPA, TopLink and MySql

This is a simple example of standalone java application using Java Persistence API (JPA), TopLink persistence engine, and MySql. It consists of an entity class, a main class, and a persistence.xml file. No need to create tables, as they are created and dropped automatically.

META-INF/persistence.xml must be at the root of the persistence unit. In my example project, it's C:\ws\nb\scrap\build\classes\META-INF\persistence.xml.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence">
<persistence-unit name="hello-world" transaction-type="RESOURCE_LOCAL">

<!-- comment out to use the default provider
<provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
-->

<class>com.foo.Greeting</class>
<properties>
<property name="toplink.jdbc.url" value="jdbc:mysql://localhost:3306/test"/>
<property name="toplink.jdbc.user" value="root"/>
<property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="toplink.jdbc.password" value=""/>
<property name="toplink.ddl-generation" value="drop-and-create-tables"/>
</properties>
</persistence-unit>
</persistence>
Entity class:
package com.foo;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Greeting implements Serializable {
@Id @GeneratedValue private int id;
@Basic private String message;
@Basic private String language;

public Greeting() {}
public Greeting(String message, String language) {
this.message = message;
this.language = language;
}

public String toString() {
return "Greeting id=" + id + ", message=" + message + ", language=" + language;
}
}
Main class:
package com.foo;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class HelloWorld {
private EntityManagerFactory emf;
private EntityManager em;
private String PERSISTENCE_UNIT_NAME = "hello-world";

public static void main(String[] args) {
HelloWorld hello = new HelloWorld();
hello.initEntityManager();
hello.create();
hello.read();
hello.closeEntityManager();
}

private void initEntityManager() {
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
em = emf.createEntityManager();
}

private void closeEntityManager() {
em.close();
emf.close();
}

private void create() {
em.getTransaction().begin();
Greeting g_en = new Greeting("hello world", "en");
Greeting g_es = new Greeting("hola, mundo", "es");
Greeting[] greetings = new Greeting[]{g_en, g_es};
for(Greeting g : greetings) {
em.persist(g);
}
em.getTransaction().commit();
}

private void read() {
Greeting g = (Greeting) em.createQuery(
"select g from Greeting g where g.language = :language")
.setParameter("language", "en").getSingleResult();
System.out.println("Query returned: " + g);
}
}
To compile the project,
C:\ws\nb\scrap\src\com\foo>
javac -d C:\ws\nb\scrap\build\classes
-classpath C:\Sun\AppServer\lib\toplink-essentials-agent.jar
Greeting.java HelloWorld.java
To run it, start MySql on localhost, and run
C:\ws\nb\scrap\build\classes>
java -javaagent:C:\Sun\AppServer\lib\toplink-essentials-agent.jar
-cp .;C:\tools\mysql-java\mysql-connector-java-3.1.13-bin.jar
com.foo.HelloWorld

[TopLink Info]: 2006.07.27 01:34:26.484--ServerSession(20003078)--TopLink, version: Oracle TopLink Essentials - 2006.4 (Build 060412)
[TopLink Info]: 2006.07.27 01:34:27.686--ServerSession(20003078)--file:/C:/ws/nb/scrap/build/classes-hello-world login successful
[TopLink Warning]: 2006.07.27 01:34:27.926--ServerSession(20003078)--Exception [TOPLINK-4002] (Oracle TopLink Essentials - 2006.4 (Build 060412)): oracle.toplink.essentials.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: Table 'sequence' already existsError Code: 1050
Call:CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(38), PRIMARY KEY (SEQ_NAME))
Query:DataModifyQuery()
Query returned: Greeting id=1, message=hello world, language=en
[TopLink Info]: 2006.07.27 01:34:28.447--ServerSession(20003078)--file:/C:/ws/nb/scrap/build/classes-hello-world logout successful

7/24/2006

The -javaagent: Option

The -javaagent: is introduced in JDK 5, and it may be late to talk about any new features in JDK 5, while JDK 6 is just around the corner. I started to use it recently but at first couldn't find any good documentation on this option.

java -help shows a brief message:

-javaagent:<jarpath>[=<options>]
load Java programming language agent, see java.lang.instrument
JDK tools doc page doesn't give much more info. The official one is at the Javadoc page for java.lang.instrument, as suggested by java -help

Here is my quick summary with comments:

1. An agent is just an interceptor in front of your main method, executed in the same JVM and loaded by the same system classloader, and governed by the same security policy and context.

The name is misleading, since the word agent usually suggests something working remotely and separately from the primary entity. But it turns out the java agent as used in -javaagent: is much simpler than that.

How to write a java agent? Just implement this method:
public static void premain(String agentArgs, Instrumentation inst);
2. Agent classes must be packaged in jar file format whose META-INF/MANIFEST.MF contains at least one additional attribute: Premain-Class. An example of MANIFEST.MF:
Manifest-Version: 1.0
Premain-Class: javahowto.JavaAgent
Created-By: 1.6.0_06 (Sun Microsystems Inc.)
Once you have the custom MANIFEST.MF file, run jar command with cvfm option to create the agent jar:
/projects/Hello/build/classes $
jar cvfm ../../myagent.jar ../../mymanifest.mf javahowto/MyAgent.class
3. All these agent jars are automatically appended to the classpath. So no need to add them to classpath, unless you want to reorder classpath elements.

4. One java application may have any number of agents by using -javaagent: option any number of times. Agents are invoked in the same order as specified in options.

5. Each agent may also take String-valued args. I guess that's the reason why we have to use this option multiple times for multiple agents. Otherwise, we could've just done something like: -javaagent agent1.jar:agent2.jar, which is incorrect.

6. It's convenient for java application integration. Now I can enhance/modify the behavior of an application without changing its source code.

7. JavaEE 5 has many similar construts, such as interceptors in EJB 3, and EntityListener in Java Persistence API. In JavaEE, they are managed by some sort of containers, so their semantics is much richer than javaagent.

7/22/2006

7 Things I don't Like in Co-workers' Computers

I shouldn't care about anything in my co-workers' computers. But once in a while, I need to sit down in front of their machines to help debug something, I find these things annoying and slowing things down. For me, it's just a few times, no big deal. But for owners of these machines, I don't know how many person-months are wasted on typing and keystrokes.

1. vim not in path, or not installed anywhere in the current machine. There may be some network locations with vim executable, but it's hard to remember where they are. So I have to use /bin/vi.

2. csh is used, rather than tcsh, bash, zsh, etc. If they really love csh, why can't they just upgrade to tcsh, which is totally csh-compatible. I guess /bin/csh is the default login shell and they never bother to change it.

3. Current path is not displayed as part of the prompt, and users are more likely to run commands in the wrong directories. I have set prompt="%/ > " in my $HOME/.tcsh, to always include `pwd` in the prompt. I don't remember the last time I run pwd command.

4. File/path-completion doesn't work, or not smart. For example, csh uses the hard-to-reach escape key for its limited file-completion, and doesn't support case-insensitive match, possible matches listing, nor hyphen-underscore auto switch. I usually don't type a complete path. I don't trust my typing, even though I'm a pretty good typist.

5. $JAVA_HOME is not defined, or $JAVA_HOME/bin is not in the path. As a result, commands like java, javac, jar, etc are not recognized.

6. Up and Down keys don't show command history. Without these two history keys, I will have to run history command all the time to get the previous commands. Slightly better, use shortcuts like !java, but that is not very accurate and may give you a javac -classpath ... command.

7. Shell background is not black or dark gray. It's just a matter of personal preference.

To alleviate some of these pains, I created a $HOME/.common file, and every time I go to another workstation, I just run tcsh and source /home/me/.common.

Tags: , , ,

7/18/2006

NullPointerException and autoboxing

I recently came across a NullPointerException that is not evident from the line number. To reduce it to a simple testcase:

package com.javahowto.test;

public class Project {
private Integer version;

public static void main(String[] args) {
Project project = new Project();
System.out.println(project.toString());
//NPE in the next line
int version = project.getVersion();
}

private Integer getVersion() {
return version;
}
}
In that line, it seems the only suspect is the variable project. But if project was null, the constructor, or project.toString() should've failed, and the execution shouldn't reach project.getVersion().

You may have noticed it's the autoboxing, the implicit cast from Integer to int that has caused NPE. This line
int version = project.getVersion();
is really this at runtime:
int version = project.getVersion().intValue();
project.getVersion() returned null and caused NPE. Unlike primitives, Integer and other wrapper types don't default to 0 when used as instance or class variables. With the autoboxing in JDK 5, primitives and their wrapper types are almost but not completely interchangeable.

7/15/2006

Don't Close Servlet OutputStream or PrintWriter

I don't think web app developers need to close ServletOutputStream or PrintWriter in servlet classes. They are created and managed by the web container, and web components should not interfere with its lifecycle. If you do close them inside your servlet service/doGet/doPost methods, it won't cause trouble for a fault-tolerant container.

When NetBeans generates a new servlet, it does close the PrintWriter in processRequest method:


protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
/* TODO output your page here
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet MyServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Servlet MyServlet at "
+ request.getContextPath () + "</h1>");
out.println("</body>");
out.println("</html>");
*/
out.close();
}
If NetBeans insists on closing ServletOutputStream, the safe way is to close it in a finally block.

In short, don't close servlet OutputStream or PrintWriter, just as you won't close System.out.

7/08/2006

NetBeans Now Knows How to Indent Java Annotations

NetBeans 4 is the first java IDE to support JDK 1.5 language features, and it has been working for me very well. One little annoyance in NetBeans 4.1 and 5 is it doesn't know how to indent java annotations. For example, it would indent and format the following method like this:

@Override
public String toString() {
return super.toString();
}
After typing @Override and return, the cursor in the new line is already indented 8 spaces. I have to manually correct it. I have to remember not to press CTRL-SHIFT-F to format code, since it will ruin my carefully and manually formated code and format java annotations in its own unique way.

Finally, it was fixed in NetBeans 5.5 beta and formats it the way I (I guess most people) like:
@Override
public String toString() {
return super.toString();
}
This is also how jEdit and Eclipse 3.2 formats java annotations. Note that @Override doesn't need to be in its own line, but it looks cleaner to me.

7/07/2006

Java Annotations with No Target

Perhaps you would expect every Java annotation to have one or multiple targets, indicating where it can be applied, either on a class, method, field, or some combination. It's correct for the most part, but it also turns out some java annotations have no target. Their target() field is an empty array. For example,

  • javax.ejb.ActivationConfigProperty:

    @Target(value={})
    @Retention(value=RUNTIME)
    public @interface ActivationConfigProperty
  • javax.persistence.EntityResult

    @Target(value={})
    @Retention(value=RUNTIME)
    public @interface EntityResult
Where can we use these targetless annotations? They are to be used as nested annotations inside other enclosing annotations. Maybe we need a new target value NESTED? For example, @ActivationConfigProperty can only be used inside @javax.ejb.MessageDriven to specify properties for the Message-Driven Bean (MDB):
@MessageDriven(name="MessageBean",
activationConfig = {
@ActivationConfigProperty(propertyName="destinationType",
propertyValue="javax.jms.Topic"),
@ActivationConfigProperty(propertyName="acknowledgeMode",
propertyValue="Auto-acknowledge"),
@ActivationConfigProperty(propertyName="subscriptionDurability",
propertyValue="Durable"),
@ActivationConfigProperty(propertyName="messageSelector",
propertyValue="name='java' AND bugs < 1")
})
public class MessageBean implements MessageListener {
...
}
@javax.persistence.EntityResult can only be used as nested annotation inside @javax.persistence.SqlResultSetMapping. Here is a more complex example from its java doc, with 3 nested annotations at 3 different levels:
@SqlResultSetMapping(name="OrderResults",
entities={
@EntityResult(entityClass=com.acme.Order.class, fields={
@FieldResult(name="id", column="order_id"),
@FieldResult(name="quantity", column="order_quantity"),
@FieldResult(name="item", column="order_item")})},
columns={
@ColumnResult(name="item_name")}
)

7/06/2006

To Use or Not to Use Cygwin

Yes, if you just want to use those neat Unix utilities, like grep, head, touch, tail, more, etc, and if you are patient enough to manage Cygwin installation. For anything beyond that, you should really consider Solaris x86 or Linux.

Cygwin installer is my worst install experience. After using Cygwin for years, I still find it confusing and tiresome to check/uncheck these little source/binary/remove icons.

I wouldn't run any serious apps, not even Apache Ant, from within a Cygwin shell. I don't need more dll mismatches, or path configuration problems. I don't want to duplicate all my environment variables (JAVA_HOME, ANT_HOME, MAVEN_HOME, CATALINA_HOME, JBOSS_HOME, SJSAS_HOME, etc) in both Windows Control Panel and .profile/.bash/.tcsh. I don't want to rewrite my convenience batch files to tcsh alias or bourne shell scripts.

I wouldn't build my projects using any part of Cywin. I wouldn't require team members to have Cygwin in their Windows box. Of course, I would never ask end users of my software to first install Cygwin, or some other Unix clone on Windows. If any software product have such requirement, I have doubt on its quality and maintainability.

If my development environment is Windows, I would hire a release engineer good at batch files, or even better, an Ant expert.

Something You May Not Know about Jar Command: Part 3

8. jar command options can start with optional -. jar tvf a.jar is the same as jar -tvf a.jar

9. You would usually use relative paths for source files, relative to the current directory. These relative paths will be preserved inside the target jar file. For example, jar cvf \tmp\b.jar com\javahowto\test\ will create the directory tree com\javahowto\test\ inside the target jar, and include all files under <current-dir>\com\javahowto\test\.

If you use absolute paths for source files, jar will copy the absolute paths inside the jar file, which is not what you want. For example,

C:\tmp > jar cvf a.jar C:\tmp\A.class
added manifest
adding: C:/tmp/A.class(in = 405) (out= 279)(deflated 31%)
10. If source files are not located in the current directory, you can use -C option to tell jar command to implicitly change to another directory and then include files there. For example,
C:\ws\nb\scrap\dist > jar cvf hello-world.jar -C ..\build\classes com\javahowto\test
11. If you use -C ../build/classes option and want to include all files under ../build/classes, use . (dot) to represent all files there. Note that you can't use *, which will resolve by OS to all files to the current directory. For example,
C:\ws\nb\scrap\dist > jar cvf hello-world.jar -C ..\build\classes .
The following example (using -C and *) will ignore -C option and instead include all files in the current directory, which is not what we want:
C:\ws\nb\scrap\dist > jar cvf hello-world.jar -C ..\build\classes *
..\build\classes\com\hello-world.jar : no such file or directory
added manifest
adding: scrap.jar(in = 20487) (out= 7472)(deflated 63%)
12. There are 3 types of paths in jar command:
  • path to the destination jar file, either relative or absolute path is fine. In fact, any format is ok as long as it can be correctly resolved by the OS

  • path to the manifest file, if m option is present. Either relative or absolute path is fine. The same as destination file.

  • multiple paths to source files. They should be relative to the current directory, unless -C option is present. In that case, all source files should be relative to the value of -C option.
If you don't like using -C option, I'd suggest you always cd into the parent directory of all source files, and then run jar command.

13. It is not possible to include source files from multiple different parent directories. But you can always first copy them into a common parent directory. Jar task in Apache Ant is more flexible and can accommodate almost all use cases.

14. In JDK 6 or newer version, you can use e option to specify an entry-point class (Main-Class in META-INF/MANIFEST.MF) for self-contained applications packaged in a jar file. See this post for details.

7/05/2006

Something You May Not Know about Jar Command: Part 2

4. You can choose not to have manifest file when creating a jar file, using M option:

jar cvfM no-meta.jar A.class
adding: A.class(in = 405) (out= 279)(deflated 31%)
Note: it's upper-case M. Lower-case m has a different meaning.

5. You can specify your own manifest file when creating a jar file, using m option:
jar cvfm my-meta.jar my-meta-inf\my.mf A.class
added manifest
adding: A.class(in = 405) (out= 279)(deflated 31%)
The option used here cvfm tells the jar command that the destination file (f) will come next and then custom manifest file (m). I can also specify them in a different order:
jar cvmf my-meta-inf\my.mf my-meta.jar A.class
added manifest
adding: A.class(in = 405) (out= 279)(deflated 31%)
It's lower-case m. Upper-case M has a different meaning.

6.META-INF/MANIFEST.MF file in source files is always ignored.
jar cvf ignore.jar A.class META-INF
added manifest
adding: A.class(in = 405) (out= 279)(deflated 31%)
ignoring entry META-INF/
adding: META-INF/LICENSE.txt(in = 2657) (out= 1185)(deflated 55%)
ignoring entry META-INF/MANIFEST.MF
When it comes to manifest files for new jar file, you only have 3 options:
  • Do not specify any manifest-related options and use the default MANIFEST.MF

  • Use option M not to include a manifest file

  • Use option m to use a custom manifest file. Inside the jar file, this file will always be named MANIFEST.MF under META-INF directory. Outside of the jar file, this custom manifest file can be anywhere and have any name.
7. If you unjar an archive, do nothing, and then jar it up again, the resulted new archive may not equal to the original one. And applications using this new archive may not work correctly because of the wrong manifest file. The original jar may have a custom manifest file, which is expaned into META-INF/MANIFEST.MF. But when you jar these files up again without using cvfm option, this custom manifest file is ignored and a default MANIFEST.MF is included.

Something You May Not Know about Jar Command: Part 1

1. jar command can also operate on zip files. I usually run jar tvf hello.zip to quickly view its content, without starting up the WinZip program. jar xvf hello.zip should also be able to expand the target zip files. I find it hard to memorize Unix zip/unzip command line options, so I just use jar tvf/jar xvf instead. For example:

C:\tmp > jar tvf eclipse-SDK-3.2RC7-win32.zip
2. jar tvf can selectively list table of contents for archive. I used to run jar tvf j2ee.jar | grep javax/servlet/http to search for servlet classes in j2ee.jar. Replace grep with findstr on Windows. In fact, I don't need grep or findstr; I can just run this command:
jar tvf j2ee.jar javax/servlet/http
Note that the search criteria are matched against the beginning of all entries in jar file. It uses String.startsWith(what) rather than String.contains(what). So this command jar tvf j2ee.jar ejb will not return any matching entries, though jar tvf j2ee.jar javax/ejb will return all ejb classes. The search is also case-sensitive.

If you want case-insensitive search, or match by any parts (not just the beginning) of entries, you still need to use jar tvf my.jar | grep -i aNynAmE

3. You can extract selected entries from a jar file. For instance, if you only want to view the meta-inf/manifest.mf file, you can
C:\Sun\AppServer\lib > jar xvf j2ee.jar META-INF/MANIFEST.MF
inflated: META-INF/MANIFEST.MF
Or using a backslash instead of a forward slash:
C:\Sun\AppServer\lib > jar xvf j2ee.jar META-INF\MANIFEST.MF
inflated: META-INF/MANIFEST.MF
The entry names are case sensitive, and so the following will not extract anything:
C:\Sun\AppServer\lib > jar xvf j2ee.jar meta-inf/manifest.mf
Of course, you can always double-click the entry to view it in WinZip, fileroller, or other tools.

A New Option in Jar Command in JDK 6

JDK 6 adds a new feature to the jar command: e. Other options of the jar command are still the same. This is the partial usage from JDK 6 beta 2:

C:\tools\jdk6\bin > jar
Usage: jar {ctxui}[vfm0Me] [jar-file] [manifest-file] [entry-point] [-C dir] files ...
Options:
...
-e specify application entry point for stand-alone application
bundled into an executable jar file
...
For example, I run jar command to package a self-contained application in a jar file:
C:\ws\nb\scrap\build\classes > C:\tools\jdk6\bin\jar cvfe  ..\..\dist\hello-world.jar com.javahowto.test.HelloWorld com
added manifest
adding: com/(in = 0) (out= 0)(stored 0%)
adding: com/javahowto/(in = 0) (out= 0)(stored 0%)
adding: com/javahowto/test/(in = 0) (out= 0)(stored 0%)
adding: com/javahowto/test/HelloWorld.class(in = 572) (out= 348)(deflated 39%)
Then I can distribute hello-world.jar to users, who can run hello-world app like this:
C:\download\hello > java -jar hello-world.jar
Hello world!
Users can run it with any version of java; it doesn't have to be JDK 6. What the extra e option does is simply adding a Main-Class entry in the jar's META-INF\MANIFEST.MF:
Manifest-Version: 1.0
Created-By: 1.6.0-beta2 (Sun Microsystems Inc.)
Main-Class: com.javahowto.test.HelloWorld
This new feature is helpful when packaging simple self-contained apps, and application client modules in J2EE/JavaEE, both of them require a Main-Class entry in MANIFEST.MF. So I don't have to create one beforehand, or have Apache Ant generate one.

7/04/2006

When and Where not to Use java:comp/env

In this post, I wrote about the need to use the prefix java:comp/env when looking up ejb/resource in the current naming environment. Equally important is to know where/when not to use it.

1. Don't use java:comp/env in standard deployment descriptors, such as web.xml, ejb-jar.xml, and application-client.xml. I can't think of any elements in these descriptors that contain java:comp/env.

2. Don't use java:comp/env in appserver-specific deployment plan files.

3. Don't use java:comp/env in any fields of resource and/or ejb injection, whether it's field or method injection.

4. Don't use java:comp/env when looking up resource/ejb using javax.ejb.EJBContext.lookup(String name). This is a new method in javax.ejb.EJBContext in EJB 3. The lookup name in this case is always relative to java:comp/env. For more details, please see 5 Ways to Get Resources in EJB 3.

5. Don't use java:comp/env when looking up certain standard J2EE and JavaEE resources. They reside directly under java:comp/, with no /env. For instance:

  • java:comp/UserTransaction

  • java:comp/EJBContext

  • java:comp/ORB

  • java:comp/TransactionSynchronizationRegistry
6. Don't use java:comp/env when looking up global resources in a server-dependent way. Some application servers let you look up resources by their global JNDI name. In such case, their lookup name should not contain java:comp/env. For example, java:/defaultDS in jboss. In JavaEE SDK 5/Glassfish/Sun Java System Application Server 9, I can also directly look up jdbc/__default (the default datasource) without configuring it in any descriptors. Note that this style of lookup is not portable. It ties your apps to specifc runtime server environment, and should really be avoided.

7. Don't use java:comp/env in the name of any physical resources inside an application server, like jdbc-resource, jdbc-pool, JMS queue or topic, EJB JNDI name, persistence manager, etc.

7/03/2006

Fix NameNotFoundException: Wrong Mapping in Deployment Plan

In previous posts, I wrote about two common causes of javax.naming.NameNotFoundException: incorrect lookup name and reference not declared. This post covers a third cause: wrong mapping of EJB/resource in appserver-specific deployment plans.

This sample project consists of a simple EJB3 stateless session bean HelloEJBBean, and an application client with main class hello.Main. A reference to HelloEJBBean's remote business interface is injected into the client main class.

package hello.ejb;
import javax.ejb.Remote;

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

package hello.ejb;
import javax.ejb.Stateless;

@Stateless
public class HelloEJBBean implements HelloEJBRemote {
public void hello() { }
}
Application client main class:
package hello;
import hello.ejb.HelloEJBRemote;
import javax.ejb.EJB;

public class Main {
@EJB(beanName="HelloEJBBean")
private static HelloEJBRemote helloEJB;

public static void main(String[] args) {
helloEJB.hello();
}
}
For the above sample app to work, we do not need any deployment descriptors or deployment plan. Why? because all metadata have been provided with annotations, or have defaults, or can be figured out by appserver one way or another.

But some IDEs still generate unnecessary deployment descriptors and deployment plans, which may have wrong mapping info. This happens without you knowing it. If you delete these unnecessary and wrong deployment plans, the next time you rebuild project, they will be regenerated.

For instance, NetBeans 5.5 beta generates the following sun-application-client-jar.xml, which is unnecessary and contains the wrong mapping data:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-application-client PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Application Client 5.0//EN"
"http://www.sun.com/software/appserver/dtds/sun-application-client_5_0-0.dtd">
<sun-application-client>
<ejb-ref>
<ejb-ref-name>helloEJB</ejb-ref-name>
<jndi-name>ejb/helloEJB</jndi-name>
</ejb-ref>
</sun-application-client>
Both the ejb reference name and ejb JNDI name are wrong. If the IDE really thinks a sun-application-client.xml is helpful for whatever reason, the ejb-ref element should be:
<ejb-ref>
<ejb-ref-name>hello.Main/helloEJB</ejb-ref-name>
<jndi-name>hello.ejb.HelloEJBRemote</jndi-name>
</ejb-ref>
because the default ejb reference name for an injected ejb is the of the format: <fully-qualified-class-name of the injection target class>/field-name. The default ejb JNDI name depends on appserver implementation, and for EJB3 in JavaEE SDK 5/Glassfish/Sun Java System Application Server, it's the fully qualified class name of the remote business interface.

IDE's best effort to generate deployment artifacts is still not good enough. With wrong mapping info in deployment plan, your app may fail to deploy, if the appserver validates the ejb reference at deployment time. Or even if it is deployed, it will fail at request time.

All posts in this series for NameNotFoundException:

Fix NameNotFoundException: Incorrect Lookup Name

Fix NameNotFoundException: Reference Not Declared

Fix NameNotFoundException: Wrong Mapping in Deployment Plan


Tags: , , , , , , , , , , , ,

7/02/2006

Fix NameNotFoundException: Reference Not Declared

J2EE 1.4 and earlier versions require all resource and ejb references be declared in the standard deployment descriptors, such as ejb-jar.xml, web.xml, and application-client.xml. These elements -- resource-ref, ejb-ref, ejb-local-ref -- register entries in the current component's private environment context. These declarations are needed, even when the reference name is the same as the global JNDI of the target resource or EJB.

For example, HelloServlet looks up the default DataSource in JavaEE SDK 5, whose global JNDI name is jdbc/__default:

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();
DataSource dataSource = (DataSource) ic.lookup("java:comp/env/jdbc/__default");
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);
}
}
Any portable applications must also declare a resource-ref in web.xml:

<?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</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
</web-app>
Without this resource-ref element in web.xml, the app fails with this error in JavaEE SDK 5/Glassfish/Sun Java System Application Server:

javax.naming.NameNotFoundException: No object bound to name java:comp/env/jdbc/__default
at com.sun.enterprise.naming.NamingManagerImpl.lookup(NamingManagerImpl.java:751)
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:30)
We are not done yet. We also need to map the resource reference name (jdbc/__default) to the global JNDI name of the target resource (jdbc/__default). This step is appserver-dependent, usually editing an appserver-specific deployment plan file. For example, sun-ejb-jar.xml, sun-web.xml, sun-application-client.xml, or jboss.xml. The following shows how this is mapped in sun-web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 Servlet 2.5//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_5-0.dtd">
<sun-web-app error-url="">
<context-root>/webapp14</context-root>
<resource-ref>
<res-ref-name>jdbc/__default</res-ref-name>
<jndi-name>jdbc/__default</jndi-name>
</resource-ref>
<class-loader delegate="true"/>
</sun-web-app>
When the local reference name and the global JNDI name are the same, some application servers may also have some default mapping rules to eliminate this step. For instance, in JavaEE SDK 5 and Sun Java System Application Server 9, this step is not needed when the two names are the same.

JavaEE 5 has introduced a series of annotations to greatly simplify configurations, such as @EJB, @EJBs, @Resource and @Resources. Using resource injections, applications no longer need to use ejb-ref, ejb-local-ref, nor resource-ref, and they may get rid of standard deployment descriptors altogether. But some sort of mapping mechanisms (default mapping rules, or appserver-specific deployment plans) are still needed.

More info on how to inject/lookup resources in JavaEE 5 and EJB 3, please refer to 5 Ways to Get Resources in EJB 3

All posts in this series for NameNotFoundException:

Fix NameNotFoundException: Incorrect Lookup Name

Fix NameNotFoundException: Reference Not Declared

Fix NameNotFoundException: Wrong Mapping in Deployment Plan