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