12/31/2008

GlassFish MySql connection pool with empty password

The default mysql database server password is empty, which is convenient in development mode, but turns out to be trouble when creating a jdbc connection pool in GlassFish. There seems to be no way to specify an empty password either in admingui or asadmin.

One workaround is to use a temporary password (say, "xxx") when creating the connection pool, stop the domain, open $GLASSFISH_HOME/domains/domain1/config/domain.xml, and replace "xxx" with ""

This should work with all versions of GlassFish (v1, v2, and v3). To do it with script:

$GLASSFISH_HOME/bin/asadmin start-domain

$GLASSFISH_HOME/bin/asadmin create-jdbc-connection-pool --datasourceclassname com.mysql.jdbc.jdbc2.optional.MysqlXADataSource --property user=root:password=xxx:DatabaseName=test:ServerName=localhost:port=3306 mysql-pool
$GLASSFISH_HOME/bin/asadmin create-jdbc-resource --connectionpoolid mysql-pool jdbc/test

$GLASSFISH_HOME/bin/asadmin stop-domain

cd $GLASSFISH_HOME/domains/domain1/config
cat domain.xml | sed "s/xxx//" > domain.xml.replaced
mv domain.xml domain.xml.original
mv domain.xml.replaced domain.xml

$GLASSFISH_HOME/bin/asadmin start-domain
vi/vim is also well suited for this type of search-replace:
vim domain.xml
/xxx
dw
:wq

12/17/2008

Pass optional params with varargs

Varargs (introduced in Java SE 5) allows you to pass 0, 1, or more params to a method's vararg param. This reduces the need for overloading methods that do the similar things. For example,

package javahowto;

import org.apache.commons.lang.StringUtils;

public class HelloWorld {
public static void main(String[] args) {
hello();
hello("World");
hello("Santa", "Bye");

final String[] names = {"John", "Joe"};
hello(names);
}

public static void hello(String... names) {
final String defaultName = "Duke";
String msg = "Hello, ";
msg += ((names.length == 0) ? defaultName : StringUtils.join(names, ", "));
System.out.println(msg);
}
}
org.apache.commons.lang.StringUtils is in commons-lang-2.4.jar. Running this example gives the following output:
Hello, Duke
Hello, World
Hello, Santa, Bye
Hello, John, Joe
BUILD SUCCESSFUL (total time: 0 seconds)

12/07/2008

How to create jdbc connection pool and DataSource in GlassFish

Every GlassFish DataSource depends on a connection pool that specifies how to connect to database. DataSource is just a thin layer on top of a connection pool. This is different from several other appservers.

Usually the easiest way is to do it in admin GUI at http://localhost:4848/ , choose Create New JDBC Connection Pools from the Common Tasks list on the front page right after login. You may need to scroll down a little bit to see it. On my screen, it is always hidden underneath the panel bottom. So for a while, I didn't notice its existence and always go to the left panel and choose Resources | JDBC | Connection Pools, which does the same with more clicks. The rest steps are self-explanatory.

For repeated configuration tasks, I prefer using GlassFish command line tool asadmin ($GLASSFISH_HOME/bin/asadmin). For example, this is script for creating mysql connection pool and datasource:

cp $HOME/mysql-connector-java-5.1.5-bin.jar $GLASSFISH_HOME/domains/domain1/lib/ext

$GLASSFISH_HOME/bin/asadmin stop-domain
$GLASSFISH_HOME/bin/asadmin start-domain

$GLASSFISH_HOME/bin/asadmin create-jdbc-connection-pool --datasourceclassname com.mysql.jdbc.jdbc2.optional.MysqlDataSource --property user=root:password=test:DatabaseName=test:ServerName=localhost:port=3306 test-pool

$GLASSFISH_HOME/bin/asadmin create-jdbc-resource --connectionpoolid test-pool jdbc/test
To create a connection pool that supports distributed transaction, use com.mysql.jdbc.jdbc2.optional.MysqlXADataSource as datasourceclassname, and set --restype javax.sql.XADataSource option:
$GLASSFISH_HOME/bin/asadmin create-jdbc-connection-pool --restype javax.sql.XADataSource --datasourceclassname com.mysql.jdbc.jdbc2.optional.MysqlXADataSource --property user=root:password=test:DatabaseName=test:ServerName=localhost:port=3306 test-pool
Run asadmin ping-connection-pool test-pool to verify whether the created connection pool can connect to the database. The database server needs to be running.

12/02/2008

Disable NetBeans httpmonitor in GlassFish

I found my GlassFish v2 server.log file is littered with these warning messages:

WARNING *********** NetBeans HTTP Monitor ************
The request cannot be recorded most likely because the
NetBeans HTTP Server is not running....
Here is what I just did to disable and completely remove NetBeans httpmonitor from my GlassFish v2:

1. Open $GLASSFISH_HOME/domains/domain1/config/default-web.xml, delete or comment out the declaration and mapping for servlet filter HTTPMonitorFilter. That is, the following lines:
<filter>
<filter-name>HTTPMonitorFilter</filter-name>
<filter-class>org.netbeans.modules.web.monitor.server.MonitorFilter</filter-class>
<init-param>
<param-name>netbeans.monitor.ide</param-name>
<param-value>127.0.0.1:8082</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HTTPMonitorFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
2. Optionally, delete $GLASSFISH_HOME/domains/domain1/lib/org-netbeans-modules-web-httpmonitor.jar.
Since I don't need this httpmonitor, there is no reason to keep this jar.

3. Redeploy any web applications that was previously deployed when NetBeans HTTPMonitorFilter was in default-web.xml. This step is needed for apps already deployed, because the generated web.xml already contains HTTPMonitorFilter (inherited from the old default-web.xml), and it will not be regenerated even at server restart.

That should get rid of this warning messages. For more technical details, see NetBeans issue 139653 and GlassFish issue 3844.

10/20/2008

HelloWorld with EclipseLink and MySQL

This is a simple example of standalone java application using Java Persistence API (JPA), EclipseLink, 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 located at $HOME/NetBeansProjects/greeting/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"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="greetingPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>javahowto.Greeting</class>
<properties>
<property name="eclipselink.jdbc.password" value=""/>
<property name="eclipselink.jdbc.user" value="root"/>
<property name="eclipselink.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="eclipselink.jdbc.url" value="jdbc:mysql://localhost:3306/test"/>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
<property name="eclipselink.logging.level" value="INFO"/>
</properties>
</persistence-unit>
</persistence>
Entity class (generated by NetBeans 6.1):
package javahowto;
import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Greeting implements Serializable {
private static final long serialVersionUID = 1L;

@Id
private Long id;

@Basic
private String message;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}

@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Greeting)) {
return false;
}
Greeting other = (Greeting) object;
if ((this.id == null && other.id != null) ||
(this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}

@Override
public String toString() {
return "javahowto.Greeting[id=" + id + ", message='" + getMessage() +
"']";
}
}
Main class:
package javahowto;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {
private static final Logger logger = Logger.getLogger("javahowto.greeting");
private static final String PERSISTENCE_UNIT_NAME = "greetingPU";
private EntityManagerFactory emf;
private EntityManager em;

public static void main(String[] args) {
Main main = new Main();
main.initEntityManager();
main.createAndRead();
main.createAndRollback();
}

private void createAndRead() {
Greeting g = new Greeting();
g.setId(1L);
g.setMessage("hello, createAndRead");
em.getTransaction().begin();
em.persist(g);
em.getTransaction().commit();

//g should be written to database now.
//Read it from db (no transaction context needed for em.find method)
Greeting g2 = em.find(Greeting.class, g.getId());
logger.info("Greeting " + g.getId() + " from db: " + g2);
}

private void createAndRollback() {
Greeting g = new Greeting();
g.setId(2L);
g.setMessage("hello, createAndRollback");
em.getTransaction().begin();
em.persist(g);
em.getTransaction().rollback();

logger.info("Persisted " + g + ", but the transaction was rolled back.");
Greeting g2 = em.find(Greeting.class, g.getId());
logger.info("Greeting " + g.getId() + " from db: " + g2); //should be null
}

private void initEntityManager() {
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
em = emf.createEntityManager();
}
}
To compile the project,
/NetBeansProjects/greeting/src >
javac -d ../build/classes -cp $GLASSFISH_HOME/modules/javax.persistence.jar javahowto/*.java
Start MySql on localhost, and run the following command. No need to create the table since our persistence.xml tells EclipseLink to automatically drop and create it.
/home/javahowto/mysql > bin/mysqld_safe &

/NetBeansProjects/greeting/build/classes >
java -cp $HOME/tmp/mysql-connector-java-5.1.5-bin.jar:$GLASSFISH_HOME/modules/javax.persistence.jar:$GLASSFISH_HOME/modules/org.eclipse.persistence.jpa.jar:$GLASSFISH_HOME/modules/org.eclipse.persistence.core.jar:. javahowto.Main

[EL Info]: 2008.10.20 11:56:24.712--ServerSession(4744654)--EclipseLink, version: Eclipse Persistence Services - 1.0.1 (Build 20080905)
[EL Info]: 2008.10.20 11:56:25.163--ServerSession(4744654)--file:/home/javahowto/NetBeansProjects/greeting/build/classes/-greetingPU login successful
Oct 20, 2008 11:56:25 AM javahowto.Main createAndRead
INFO: Greeting 1 from db: javahowto.Greeting[id=1, message='hello, createAndRead']
Oct 20, 2008 11:56:25 AM javahowto.Main createAndRollback
INFO: Persisted javahowto.Greeting[id=2, message='hello, createAndRollback'], but the transaction was rolled back.
Oct 20, 2008 11:56:25 AM javahowto.Main createAndRollback
INFO: Greeting 2 from db: null
To stop MySQL server:
/home/javahowto/mysql > bin/mysqladmin -u root shutdown

10/09/2008

Solve java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy

I recently ran into this type of exceptions when experimenting with GlassFish V3. It turned out the root cause is that a class (HelloInterceptor) used in type-level @Interceptors annotation is missing in the WAR file. Once this class is packaged in the WAR file, it works.  In appservers that support EJB 3.1, EJB can be packaged inside a WAR file.

@Stateless
@Interceptors(HelloInterceptor.class)
public class HelloBean {
public String hello(String name) {
return "Hello, " + name;
}
}
ArrayStoreException has been in Java since JDK 1.0, but this is the first time I saw it. The javadoc says it is "[t]hrown to indicate that an attempt has been made to store the wrong type of object into an array of objects." So it's a low level exception that I would normally catch and shield it from client.

The stacktrace in server.log is pretty long, repeating the same stacktrace 3-4 times. In short it looks like this:
[#|2008-10-09T10:57:39.262-0400|SEVERE|GlassFish10.0|javax.enterprise.system.core|_ThreadID=17;_ThreadName=Thread-4;|
Exception while deploying the app
java.lang.RuntimeException: sun.reflect.annotation.TypeNotPresentExceptionProxy
at org.glassfish.javaee.core.deployment.JavaEEDeployer.loadMetaData(JavaEEDeployer.java:160)
at com.sun.enterprise.v3.server.ApplicationLifecycle.prepare(ApplicationLifecycle.java:436)
at com.sun.enterprise.v3.server.ApplicationLifecycle.deploy(ApplicationLifecycle.java:180)
at org.glassfish.deployment.admin.DeployCommand.execute(DeployCommand.java:314)
at com.sun.enterprise.v3.admin.CommandRunner$2.execute(CommandRunner.java:302)
at com.sun.enterprise.v3.admin.CommandRunner.doCommand(CommandRunner.java:312)
at com.sun.enterprise.v3.admin.CommandRunner.doCommand(CommandRunner.java:135)
at org.glassfish.deployment.autodeploy.AutoOperation.run(AutoOperation.java:122)
at org.glassfish.deployment.autodeploy.AutoDeployer.deploy(AutoDeployer.java:524)
at org.glassfish.deployment.autodeploy.AutoDeployer.deployAll(AutoDeployer.java:410)
at org.glassfish.deployment.autodeploy.AutoDeployer.run(AutoDeployer.java:342)
at org.glassfish.deployment.autodeploy.AutoDeployer.run(AutoDeployer.java:330)
at org.glassfish.deployment.autodeploy.AutoDeployService$1.run(AutoDeployService.java:200)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
Caused by: java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
at sun.reflect.annotation.AnnotationParser.parseClassArray(AnnotationParser.java:653)
at sun.reflect.annotation.AnnotationParser.parseArray(AnnotationParser.java:460)
at sun.reflect.annotation.AnnotationParser.parseMemberValue(AnnotationParser.java:286)
at sun.reflect.annotation.AnnotationParser.parseAnnotation(AnnotationParser.java:222)
at sun.reflect.annotation.AnnotationParser.parseAnnotations2(AnnotationParser.java:69)
at sun.reflect.annotation.AnnotationParser.parseAnnotations(AnnotationParser.java:52)
at java.lang.Class.initAnnotationsIfNecessary(Class.java:3072)
at java.lang.Class.getAnnotations(Class.java:3052)
at org.glassfish.apf.impl.AnnotationProcessorImpl.processAnnotations(AnnotationProcessorImpl.java:278)
at org.glassfish.apf.impl.AnnotationProcessorImpl.process(AnnotationProcessorImpl.java:188)
at org.glassfish.apf.impl.AnnotationProcessorImpl.process(AnnotationProcessorImpl.java:129)
at com.sun.enterprise.deployment.archivist.Archivist.processAnnotations(Archivist.java:476)
at com.sun.enterprise.deployment.archivist.Archivist.readAnnotations(Archivist.java:348)
at com.sun.enterprise.deployment.archivist.Archivist.readDeploymentDescriptors(Archivist.java:320)
at com.sun.enterprise.deployment.archivist.Archivist.open(Archivist.java:211)
at com.sun.enterprise.deployment.archivist.ApplicationFactory.openArchive(ApplicationFactory.java:143)
at org.glassfish.javaee.core.deployment.JavaEEDeployer.parseModuleMetaData(JavaEEDeployer.java:231)
at com.sun.enterprise.web.WebDeployer.parseModuleMetaData(WebDeployer.java:110)
Update on Mar 06, 2009: I ran into ArrayStoreException again. This time I used type-level @Remote and @Local on bean class but forgot to include the remote and local business interface classes into the WAR file:
@Remote(HelloRemote.class)
@Local(HelloLocal.class)
public class HelloBean {
public String hello(String name) {
return "Hello, " + name;
}

7/07/2008

Install MySql GUI tools (MySql Administrator and Query Browser)

The easiest way to install them on Ubuntu is to use Ubuntu package manager, either command line:

sudo apt-get install mysql-admin
sudo apt-get install mysql-query-browser
Or inside System | Administration | Synaptic Package Manager, search for "mysql-admin" and "mysql-query", mark them for install, and then apply the changes.

After installation is complete, open a terminal and run command mysql-admin or mysql-query-browser. If running them in an existing terminal, you may need to run rehash command first to include the new tools in PATH. Everything should just work.

I also tried installing the generic Linux tar bundle for MySql GUI tools, but had a couple of problems:

1. Download the generic Linux bundle (mysql-gui-tools-5.0r12-linux-i386.tar.gz) from http://dev.mysql.com/downloads/gui-tools/5.0.html . The bundle for Linux includes MySql Administrator and Query Browser (MySQL Migration Toolkit is currently only in Windows bundle).

2. To install the tools, just extract the archive. There are instructions at http://dev.mysql.com/doc/administrator/en/install-generic-tarball.html . Basically, execute this command to expand the downloaded file on Desktop to /opt:
cd /opt
sudo tar xzf $HOME/Desktop/mysql-gui-tools-5.0r12-linux-i386.tar.gz
3. To run MySql Administrator and Query Browser:
/opt/mysql-gui-tools-5.0/mysql-administrator
/opt/mysql-gui-tools-5.0/mysql-query-browser
You may also want to copy/move/link MySQLAdministrator.desktop and MySQLQueryBrowser.desktop to $HOME/Desktop, so you can double-click to launch them.

When running Query Browser, I ran into this issue: mysql-query-browser is freezing every way i try to pick, or click, a scheme. I tried removing everything under $HOME/.mysqlgui/ but still the same.

It's possible to install to directories other than /opt, but it may cause various troubles. I'd suggest stick to the default install directory /opt. I first tried to install under $HOME/mysql directory, to group all mysql-related programs under one umbrella. But I had this error when running ./mysql-administrator --update-paths:
(mysql-administrator-bin:11880): libglade-WARNING **: could not find glade file '/home/me/mysql/share/mysql-gui/common/preferences.glade'
terminate called after throwing an instance of 'MGGladeXML::Error'
Aborted
I then chose to install directly under $HOME so that mysql-gui-tools-5.0/ and mysql/ are at the same level. I also ran mysql-administrator --update-paths to update the paths. I was able to run MySql Administrator, but the GUI fonts are sort of blurred, probably because some GTK library cannot be found. This problem was gone once I changed to the default install location /opt/mysql-gui-tools-5.0.

In addition, the update-paths command doesn't seem to update path for Query Browser, since MySQLQueryBrowser.desktop file still has paths under /opt.

Configure MySql in NetBeans

NetBeans 6.1 has an interface to manage MySql, including start and stop MySql database server, connect/disconnect from databases, launch external MySql admin tool, execute queries, etc. Here are steps to configure MySql management inside NetBeans:

1. Create a wrapper script for mysqld_safe. The reason is that mysqld start script assumes the user is running the script from mysql base directory. An attempt to start mysqld from mysql/bin directory results in the following error:

/home/me/mysql/bin > ./mysqld_safe
./mysqld_safe: 199: my_print_defaults: not found
./mysqld_safe: 204: my_print_defaults: not found
The file /usr/local/mysql/bin/mysqld doesn't exist or is not executable
Please do a cd to the mysql installation directory and restart
this script from there as follows:
./bin/mysqld_safe.
See http://dev.mysql.com/doc/mysql/en/mysqld_safe.html for more information
So I created script /home/me/mysql/bin/mymysqld_safe, which can be called by NetBeans:
#! /bin/sh
cd /home/me/mysql/
./bin/mysqld_safe &
I had also tried changing mysqld_safe script to replace the hardcoded ./bin/ with `dirname $0`, but it caused other errors. So I figured the best option is to use a wrapper without touching mysql files.

2. Go to NetBeans Window | Services tab, and right-click DataBases node. Choose Properties.
Basic properties:
Server host name: localhost
Server port number: 3306
Administrator user name: root
Administrator password:

Admin properties:
Path/URL to admin tool: /usr/bin/mysql-admin
Path to start command: /home/me/mysql/bin/mymysqld_safe
Path to stop command: /home/me/mysql/bin/mysqladmin
Arguments to stop command: -u root shutdown

How to install MySql admin tool and Query Browser is posted in the previous post Install MySql GUI tools (MySql Administrator and Query Browser) .

6/19/2008

java pkg.Main or java pkg/Main?

Which format do you use to run a java application? I always pass the fully-qualified class name to java command, namely, java com.whatever.Main. In fact, either way is acceptable:

build/classes > java javahowto.Member
build/classes > java javahowto/Member
I've tried java command with both forms in JDK 1.4, JDK 5, and JDK 6 on Linux without any error. On Windows, you can use either pkg.Main, or pkg/Main, but not pkg\Main:
C:\javahowto\build\classes>java javahowto.Main
In Main.main(String[])
C:\javahowto\build\classes>java javahowto/Main
In Main.main(String[])
C:\javahowto\build\classes>java javahowto\Main
Exception in thread "main" java.lang.NoClassDefFoundError: javahowto\Main (wrong name: javahowto/Main)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
In any event, do not include .class in the class name. When running from the root of class directory on Unix, one can use the shell auto-completion to fill the class name, instead of typing the FQN. That's a big plus for the the second format. On Windows, auto-complete doesn't help in this case, since \ is class name is not accepted. I guess from now on, I will be using the slash form more often.

Unlike java command, javac operates on java source files and always takes file paths as arguments.

6/08/2008

Java generics examples -- parameterized class

Not only can Java generics be used in fields and methods, it can also be used in class definitions to produce parameterized classes. Since JDK 1.5, many built-in Java classes have been retrofitted to parameterized forms. An example is java.lang.Class, which is declared to be public final class Class<T>, the parameter type <T> represents the actual type modeled by this Class object. One benefit is that some of its methods can take or return specific types, avoiding casting while enforcing type-check.

It's also easy to write your own parameterized class. The following is a Member class whose Id field can be parameterized to String, Integer, etc:

package javahowto;
public class Member<T> {
private T id;
public Member(T id) {
this.id = id;
}
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
public static void main(String[] args) {
Member<String> mString = new Member<String>("id1");
mString.setId("id2");
System.out.printf("id after setting id: %s%n", mString.getId());
//output: id after setting id: id2

Member<Integer> mInteger = new Member<Integer>(1);
mInteger.setId(2);
System.out.printf("id after setting id: %d%n", mInteger.getId());
//output: id after setting id: 2
}

5/26/2008

Java generics examples -- use generics in collection

Here are some most simple and common use of generics with collection.

Example 1:

List<String> names = new ArrayList<String>();
names.add("John");
System.out.printf("List<String> names: %s%n", names);
In the above example, names is a List of String. When retrieving elements from the List, the return value is of type String. So no need for casting, which is a big advantage over the old, non-parameterized collection.

Example 2:
Map<Integer, String> idToName = new HashMap<Integer, String>();
idToName.put(0, "John");
System.out.printf("Map<Integer, String> idToName: %s%n", idToName);
In the above example, idToName is a Map with a Integer key and String value. The output is:
Map<Integer, String> idToName: {0=John}

Example 3:
List<List<String>> listOfList = new ArrayList<List<String>>();
List<String> sublist1 = new ArrayList<String>();
sublist1.add("A String inside sublist1");
listOfList.add(sublist1);

List<String> sublist2 = new LinkedList<String>();
sublist2.add("A String inside sublist2");
listOfList.add(sublist2);
System.out.printf("List<List<String>> listOfList: %s%n", listOfList);
The above example shows a List whose elements are of type List, i.e., a List of List. The inner List declares that it can only hold String elements. The first inner list is an ArrayList of String, and the second is a LinkedList of String. Running this code snippet prints:
List<List<String>> listOfList: [[A String inside sublist1], [A String inside sublist2]]

Example 4:
private static <T> List<T> extractElements(List bag, Class<T> type) {
List<T> result = new ArrayList<T>();
for(Object e : bag) {
//if(e instanceof T) can't use instanceof
if(type.isAssignableFrom(e.getClass())) {
result.add((T) e);
}
}
return result;
}
This method takes a List of mixed elements and extracts those elements of the desired type. The following shows how to call this method:
List bag = new ArrayList();
bag.add(new Integer(0));
bag.add(new Integer(1));
bag.add(new Double(2008.5));
bag.add("a string");
List<Number> numbersInBag = extractElements(bag, Number.class);
System.out.printf("All elements in bag: %s%nNumber elements in bag: %s%n",
bag, numbersInBag);
List<Integer> integersInBag = extractElements(bag, Integer.class);
System.out.printf("All elements in bag: %s%nInteger elements in bag: %s%n",
bag, integersInBag);
-------- output -----------

All elements in bag: [0, 1, 2008.5, a string]
Number elements in bag: [0, 1, 2008.5]
All elements in bag: [0, 1, 2008.5, a string]
Integer elements in bag: [0, 1]

5/16/2008

Copy plugins between NetBeans installations

NetBeans installer does not migrate plugins, and with every new installation, your old plugins are gone. This makes sense because the new NetBeans installation may not support the old plugins. So you will need to re-install these plugins through update centers or manually installing downloaded *.nbm files.

Another option is to copy the plugin jar and configuration files, from the old .netbeans directory to the new .netbeans directory. For example, this is how I copied whichelement plugin from NetBeans 6.0 to 6.1:
cd $HOME/.netbeans/6.0/modules
cp org-netbeans-modules-whichelement.jar $HOME/.netbeans/6.1/modules/

cd $HOME/.netbeans/6.0/config/Modules/
cp org-netbeans-modules-whichelement.xml $HOME/.netbeans/6.1/config/Modules/
Restart NetBeans and you will see the new plugin is enabled. These copied plugins will always appear installed in Tools | Plugins window, and grayed out. So there is no way to deactivate or uninstall them, since they were not installed with the wizard in the first place. But I guess you can always configure them by editing their config files under .netbeans/6.1/config/Modules. The following is the config file for whichelement (org-netbeans-modules-whichelement.xml):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//NetBeans//DTD Module Status 1.0//EN"
"http://www.netbeans.org/dtds/module-status-1_0.dtd">
<module name="org.netbeans.modules.whichelement">
<param name="autoload">false</param>
<param name="eager">false</param>
<param name="enabled">true</param>
<param name="jar">modules/org-netbeans-modules-whichelement.jar</param>
<param name="reloadable">false</param>
<param name="specversion">1.5</param>
</module>
This is not the recommended approach in most cases and may not work for some plugins. However, it can be useful if you can't find it in update centers or in *.nbm form.

Update on 12/07/2008:
I've copied whichelement plugin from my NetBeans 6.1 installation to 6.5. It works fine so far.

5/15/2008

How to customize NetBeans look and feel

NetBeans by default uses the platform native look and feel: windows look and feel on Windows, and Gtk look and feel on Linux and Solaris where Gtk 2 is available. While the windows look and feel is fine and pretty close to native windows applications, NetBeans doesn't look the best with Gtk look and feel. Here are some options to adjust them, in order of my preference:

1. Plastic looks from JGoodies. Go to JGoodies download page, choose "JGoodies Looks" among the list of download links. Runs with Java 1.4 or greater.

Unzip the download file (looks-2.1.4.zip) to somewhere, e.g., /home/xxx. The zip file contains a top-level root directory so no need to make a temp dir for unzipping.

Edit NetBeans-dir/etc/netbeans.conf file, appending the following options to netbeans_default_options (You may want to copy and comment out the original line for safety):

--cp:p /home/xxx/looks-2.1.4/looks-2.1.4.jar --laf com.jgoodies.looks.plastic.Plastic3DLookAndFeel
Note that netbeans_default_options value is quoted so after your editing, it should still end with ". Restart NetBeans to see the new look. You can also pass these options in command line when you start NetBeans and command line options will override netbeans_default_options in netbeans.conf file:

NetBeans-dir/bin/netbeans --cp:p /home/xxx/looks-2.1.4/looks-2.1.4.jar --laf com.jgoodies.looks.windows.WindowsLookAnd
JGoodies looks comes with the following look and feels:
com.jgoodies.looks.plastic.Plastic3DLookAndFeel
com.jgoodies.looks.plastic.PlasticLookAndFeel
com.jgoodies.looks.plastic.PlasticXPLookAndFeel
com.jgoodies.looks.windows.WindowsLookAndFeel
All of them are cross-platform, which means you can also use com.jgoodies.looks.windows.WindowsLookAndFeel on Linux. But it's not much different from swing metal look and feel. While trying Plastic3DLookAndFeel, PlasticLookAndFeel and PlasticXPLookAndFeel on my Linux laptop, I didn't see any noticeable difference among them.

2. Use the classic swing metal look and feel. Although not visually appealing, it has some benefits: (1) no additional jar on classpath; (2) consistent look and feel across platform; and (2) maybe a bit faster. To use metal, edit NetBeans-dir/etc/netbeans.conf:
netbeans_default_options="... --laf javax.swing.plaf.metal.MetalLookAndFeel"
Since NetBeans always sets look and feel whether you specify --laf option or not, the swing default look and feel does to take effect. Adding a file in JAVA_HOME/jre/lib/swing.properties has no effect either. It is not possible to use swing Windows look and feel on non-Windows platform.

3. If NetBeans is running on JDK 6 or greater, Install NetBeans Substance plugin. This is listed # 3, though recommended at NetBeans wiki page. You can download the plugin as a *.nbm file at NetBeans Plugin Portal, and install it following menu Tools | Plugins | Downloaded tab | Add Plugins... button. Once installed, a wide variety of skins can be chosen from menu View | Skins. You can change skin at any time without restarting NetBeans.

I tried all skins and they seem to be very similar, differing only in colors used. The good ones (good to my eyes) are "Moderate" and "Business *". One thing I don't like is that they all auto-hide the plus sign in front of the folder icon inside File and Favorite panel, which I found is very annoying and distracting. When the mouse moves on the left panel, the on-focus elements changes background color. I find it confusing since the current selected element is also highlighted.

After trying it for a couple of days, I decide to unininstall it (Tools | Plugins | Installed tab, select the plugin and click Deactivate on the right, or click Uninstall button on the bottom).

4. Napkin look and feel, making you feel like coding on a napkins. I don't quite like its informal style but others may. Links: overview & snapshots, download, instructions.

5. Nimbus look and feel in JDK 6 update 10 (beta as of 5/15/2008). See here for a comparison to metal. To use it with NetBeans, install JDK 6 update 10, set netbeans_jdkhome to JDK 6u10, and netbeans_default_options to include "--laf com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel" in NetBeans-dir/etc/netbeans.conf.

Wtih Nimbus, I can only see the selected items on my left panel (File, Favorite, or Project views). Other elements all have the same color as the white background so are invisible. Will try it again when the final JDK 6u10 is out.

4/17/2008

A sample servlet that dynamically creates images

This is a sample servlet that dynamically generates JPEG images. Currently all image properties are hard-coded, but it should be trivial to provide a jsp form to collect them (e.g., color, font, dimensions, messages). The generated image contains dynamic data such as the appserver name, and OS name.
package javahowto;

import com.sun.image.codec.jpeg.JPEGCodec;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.*;

public class ImageServlet extends HttpServlet {
private static final int WIDTH = 450;
private static final int HEIGHT = 200;
private static final Color BACKGROUND_COLOR = new Color(100,149,237);
private static final Color COLOR = new Color(0,0,139);
private static final Font FONT = new Font("Times New Roman", Font.BOLD, 46);
private static final Font FOOT_FONT = new Font("Courier", Font.ITALIC, 14);
private static final Color FOOT_COLOR = Color.BLACK;

@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("image/jpg");
ServletOutputStream out = response.getOutputStream();
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_BYTE_INDEXED);
Graphics graphics = image.getGraphics();
graphics.setColor(BACKGROUND_COLOR);
graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
graphics.setColor(COLOR);
graphics.setFont(FONT);
graphics.drawString("Hello World!", 10, HEIGHT/2);
graphics.setFont(FOOT_FONT);
graphics.setColor(FOOT_COLOR);
graphics.drawString("Created by " + getServletContext().getServerInfo(), 10, HEIGHT - 30);
graphics.drawString("for http://javahowto.blogspot.com/ on " + System.getProperty("os.name"), 10, HEIGHT - 10);
JPEGCodec.createJPEGEncoder(out).encode(image);
}
}
The web.xml file is as simple as declaring and mapping a servlet:
<?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>image</servlet-name>
<servlet-class>javahowto.ImageServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>image</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Build the project and deploy the image.war to any web server or appserver. Enter the URL http://localhost:8080/image/ to view the generated image. Here we are using the .war file base name as the default context-root and a / servlet path. I tested it on Glassfish v2 update 1, Tomcat 6, and JBoss-5.0.0.Beta4 successfully, except some redeployment errors on JBoss, which was resolved by deleting the .war and restarting server.

If the appserver is running on a headless server, you may need to set the system property java.awt.headless to true. Also note that a Sun-specific encoder class (com.sun.image.codec.jpeg.JPEGCodec) is used. It is in rt.jar of Sun's JDK, but may not be available in other vendors' JDK like IBM's JDK or BEA's (now Oracle's) JRockit. If a non-Sun Java is used, you will need to use the appropriate encoder class. Unfortunately, there seems to be no portable JPEG encoder class as of JDK 6.



4/13/2008

Java enum examples

Simple enum. The ; after the last element is optional, when this is the end of enum definition.
public enum Color {
 WHITE, BLACK, RED, YELLOW, BLUE;  //; is optional
}
Enum embedded inside a class. Outside the enclosing class, elements are referenced as Outter.Color.RED, Outter.Color.BLUE, etc.
public class Outter {
 public enum Color {
   WHITE, BLACK, RED, YELLOW, BLUE
 }
}
Enum that overrides toString method. A semicolon after the last element is required to be able to compile it. More details on overriding enum toString method can be found here.
public enum Color {
 WHITE, BLACK, RED, YELLOW, BLUE;  //; is required here.

 @Override public String toString() {
   //only capitalize the first letter
   String s = super.toString();
   return s.substring(0, 1) + s.substring(1).toLowerCase();
 }
}
Enum with additional fields and custom constructor. Enum constructors must be either private or package default, and protected or public access modifier is not allowed. When custom constructor is declared, all elements declaration must match that constructor.
public enum Color {
 WHITE(21), BLACK(22), RED(23), YELLOW(24), BLUE(25);

 private int code;

 private Color(int c) {
   code = c;
 }

 public int getCode() {
   return code;
 }
Enum that implements interfaces. Enum can implement any interfaces. All enum types implicitly implements java.io.Serializable, and java.lang.Comparable.
public enum Color implements Runnable {
 WHITE, BLACK, RED, YELLOW, BLUE;

 public void run() {
   System.out.println("name()=" + name() +
       ", toString()=" + toString());
 }
}
A sample test program to invoke this run() method:
for(Color c : Color.values()) {
 c.run();
}
Or,
for(Runnable r : Color.values()) {
 r.run();
}
A more complete example with custom fields, constructors, getters, lookup method, and even a main method for quick testing:
import java.util.HashMap;
import java.util.Map;

public enum Status {
    PASSED(1, "Passed", "The test has passed."),
    FAILED(-1, "Failed", "The test was executed but failed."),
    DID_NOT_RUN(0, "Did not run", "The test did not start.");

    private int code;
    private String label;
    private String description;

    /**
     * A mapping between the integer code and its corresponding Status to facilitate lookup by code.
     */
    private static Map<Integer, Status> codeToStatusMapping;

    private Status(int code, String label, String description) {
        this.code = code;
        this.label = label;
        this.description = description;
    }

    public static Status getStatus(int i) {
        if (codeToStatusMapping == null) {
            initMapping();
        }
        return codeToStatusMapping.get(i);
    }

    private static void initMapping() {
        codeToStatusMapping = new HashMap<Integer, Status>();
        for (Status s : values()) {
            codeToStatusMapping.put(s.code, s);
        }
    }

    public int getCode() {
        return code;
    }

    public String getLabel() {
        return label;
    }

    public String getDescription() {
        return description;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("Status");
        sb.append("{code=").append(code);
        sb.append(", label='").append(label).append('\'');
        sb.append(", description='").append(description).append('\'');
        sb.append('}');
        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println(Status.PASSED);
        System.out.println(Status.getStatus(-1));
    }
}
To run the above example:
java Status

Status{code=1, label='Passed', description='The test has passed.'}
Status{code=-1, label='Failed', description='The test was executed but failed.'}

Is the main method inherited?

Is the public static void main method on the superclass inherited by subclasses? Yes, it can be accessed by all subclasses directly. So it is possible to have a main method just on the superclass, and be able to run each subclass that doesn't itself declare the main method. For example:
public class Main {
public static void main(String[] args) {
System.out.println("In Main.main(String[])");
}
}

public class Main2 extends Main {
public void m2() {
main(null); //access the main method in superclass
}
}

public class Main3 extends Main2 {}
Running Main, Main2, Main3 have the same effect:
C:\tmp> java Main
In Main.main(String[])

C:\tmp> java Main2
In Main.main(String[])

C:\tmp> java Main3
In Main.main(String[])
When running Main2 and Main3 inside NetBeans 6.1 beta, I got the error Class "Main2" does not have a main method. Note this error is not from java runtime. It seems NetBeans is doing some validation and being too restrictive.

3/30/2008

A servlet-EJB3 sample on JBoss AS 5 beta

This is a simple project illustrating how to deploy and run an EAR that contains an ejb-jar and .war on JBoss 5 beta 4. This project is completely portable and contains no JBoss-specific configurations. It should also work on any other JavaEE application servers (Glassfish v1, v2, WebLogic 10, etc).

I will take advantage of the sample projects bundled inside NetBeans 6.1 beta (earlier versions should also have them, though I haven't checked). Download the NetBeans bundle that includesJavaEE and Glassfish.

1. Inside NetBeans, press Ctrl-Shift-N to create a new project. In the popup window, under Categories choose Samples | Enterprise, then under Projects on the right panel choose Servlet Stateless.

2. On the next window, accept defaults for Name and Location, then click Finish.

3. Under Project tab, you will see a new enterprise project called ServletStateless, which contains 2 sub-projects: ServletStateless-ejb and ServletStateless-war. Everything for this project is already in place.

4. Right click the project ServletStateless (the parent project) and choose build. You will see this output: Building jar: C:\javahowto\ServletStateless\dist\ServletStateless.ear

5. Start JBoss AS all config (not default config). You can also do it inside NetBeans if JBoss has been set as the target server for this project.
cd %JBOSS_HOME%\bin
run.bat -c all
6. Deploy the EAR to JBoss AS. Or inside NetBeans, right click the project node and choose Undeploy and Deploy
cd %JBOSS_HOME%\server\all\deploy
copy C:\javahowto\ServletStateless\dist\ServletStateless.ear .
Make sure it's correctly deployed, checking the output on JBoss console:
22:27:15,859 INFO  [MCKernelAbstraction] installing bean: jboss.j2ee:ear=ServletStateless.ear,jar=ServletState
less-ejb.jar,name=StatelessSessionBean,service=EJB3 with dependencies:
22:27:15,859 INFO [MCKernelAbstraction] and demands:
22:27:15,859 INFO [MCKernelAbstraction] jboss.ejb:service=EJBTimerService
22:27:15,859 INFO [MCKernelAbstraction] and supplies:
22:27:15,859 INFO [MCKernelAbstraction] Class:enterprise.servlet_stateless_ejb.StatelessSession
22:27:16,843 INFO [EJBContainer] STARTED EJB: enterprise.servlet_stateless_ejb.StatelessSessionBean ejbName:
StatelessSessionBean
22:27:16,984 INFO [TomcatDeployment] deploy, ctxPath=/ServletStateless-war, vfsUrl=ServletStateless.ear/Servl
etStateless-war.war
7. Enter the following URL in the browser. Or inside NetBeans right click project node and choose Run.
http://localhost:8080/ServletStateless-war/servlet, you will see this:
 Servlet2Stateless:: Please enter your name
Some notes:

1. Deploying this simple EAR to JBoss default config doesn't work. I had these errors:
22:22:27,937 INFO  [EJBContainer] STARTED EJB: enterprise.servlet_stateless_ejb.StatelessSessionBean ejbName:
StatelessSessionBean
22:22:28,171 WARN [HDScanner] Failed to process changes
org.jboss.deployers.client.spi.IncompleteDeploymentException: Summary of incomplete deployments (SEE PREVIOUS
ERRORS FOR DETAILS):

*** CONTEXTS MISSING DEPENDENCIES: Name - Dependency{Required State:Actual State}

jboss.web.deployment:war=/ServletStateless-war
- jboss.cache:service=TomcatClusteringCache{Start:** NOT FOUND **}
- jboss.cache:service=TomcatClusteringCache{Create:** NOT FOUND **}


*** CONTEXTS IN ERROR: Name - Error

jboss.cache:service=TomcatClusteringCache - ** NOT FOUND **


at org.jboss.deployers.plugins.deployers.DeployersImpl.checkComplete(DeployersImpl.java:576)
at org.jboss.deployers.plugins.main.MainDeployerImpl.checkComplete(MainDeployerImpl.java:559)
at org.jboss.system.server.profileservice.hotdeploy.HDScanner.scan(HDScanner.java:291)
at org.jboss.system.server.profileservice.hotdeploy.HDScanner.run(HDScanner.java:221)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:317)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:150)
Maybe because JBoss 5 is still at beta 4?

2. In my NetBeans, I set Glassfish as my default target appserver. Your NetBeans should have one target appserver, and I remember NetBeans also supports JBoss, Webspere, WebLogic with plugins. In my projects I also see various deployment descriptors auto-generated by NetBeans. For this simple project you only need web.xml (to specify servlet mapping) and other *.xml files are all redundant.

3. This is the line that injects bean reference to servlet class:

@EJB private StatelessSession sless;

3/24/2008

Install FindBugs plugin in NetBeans 6

To install FindBugs in NetBeans 6, the basic idea is to add an updatecenter configuration to NetBeans plugin manager. It will then periodically keep your installed plugin updated. These are the steps that worked for me. Note that these steps will install FindBugs + PMD + CheckStyle co-bundled plugins.

1, Go to NetBeans Tools -> plugins, click Settings tab. It shows a list of existing update centers like 3rd Party Plugins, NetBeans, NetBeans Beta, Plugin Portal.

2, Click Add button on the right side. You will see a Update Center Customizer popup, asking for Name and URL. Enter the following values. You may also enable Check for updates automatically.

Name: SQE Update Center
URL: https://sqe.dev.java.net/updatecenters/sqe/updates.xml

3, Click OK and you will see it is added the the list of update centers. Otherwise, most likely your network proxy is not properly configured. You can also manually verify the above URL in the browser.

4, Click Available Plugins tab, and then click the Reload Catalog button beneath the tab. After a short while, you will see a long list (close to 100) plugins. Plugins that are already installed are not included here.

5, Click Category column head to sort them. Notice 3 plugins under Quality category: SQE Java, SQE Update Center, SQE Platform.

6, Select all 3 plugins and click Install button. The rest is just clicking a few OK buttons. At the end, you will see 4 new icons on your tool bar: Check Quality, Run FindBugs, Run PMD, Run CheckStyle.

sqe (Software Quality Environment) is a java.net project. It also has instructions for integrate these tools with NetBeans here. But this page is slightly dated, with an invalid updatecenter URL (https://sqe.dev.java.net/updatecenters/sqe/catalog.xml). That page consists largely of screen shots and no way to copy that URL, which is the most critical data.

In sqe home page under Announcement section, it does mention that:
There is a new updatecenter url for NetBeans6 Beta 1 or later compatible development builds (pre-alpha) https://sqe.dev.java.net/updatecenters/sqe/updates.xml.This should allow the PluginManager to detect SQE as as 3 plugins (all other modules are hidden)!

3/23/2008

Enum wrapper for javax.transaction.Status (2)

In this previous post, I wrote about a enum type wrapper around javax.transaction.Status. It uses a static map to hold the mapping between integer status code and enum type. Here is a slightly shorter (better) implementation without using a static mapping. The commented lines are original code:
package javahowto;
import java.util.EnumSet;
import javax.transaction.Status;

public enum TransactionStatusEnum {
STATUS_ACTIVE (Status.STATUS_ACTIVE),
STATUS_COMMITTED (Status.STATUS_COMMITTED),
STATUS_COMMITTING (Status.STATUS_COMMITTING),
STATUS_MARKED_ROLLBACK (Status.STATUS_MARKED_ROLLBACK),
STATUS_NO_TRANSACTION (Status.STATUS_NO_TRANSACTION),
STATUS_PREPARED (Status.STATUS_PREPARED),
STATUS_PREPARING (Status.STATUS_PREPARING),
STATUS_ROLLEDBACK (Status.STATUS_ROLLEDBACK),
STATUS_ROLLING_BACK (Status.STATUS_ROLLING_BACK),
STATUS_UNKNOWN (Status.STATUS_UNKNOWN);

// private static Map<Integer, TransactionStatusEnum> codeToEnums
// = new HashMap<Integer, TransactionStatusEnum>();
//
// static {
// codeToEnums.put(Status.STATUS_ACTIVE, STATUS_ACTIVE);
// codeToEnums.put(Status.STATUS_COMMITTED, STATUS_COMMITTED);
// codeToEnums.put(Status.STATUS_COMMITTING, STATUS_COMMITTING);
// codeToEnums.put(Status.STATUS_MARKED_ROLLBACK, STATUS_MARKED_ROLLBACK);
// codeToEnums.put(Status.STATUS_NO_TRANSACTION, STATUS_NO_TRANSACTION);
// codeToEnums.put(Status.STATUS_PREPARED, STATUS_PREPARED);
// codeToEnums.put(Status.STATUS_PREPARING, STATUS_PREPARING);
// codeToEnums.put(Status.STATUS_ROLLEDBACK, STATUS_ROLLEDBACK);
// codeToEnums.put(Status.STATUS_ROLLING_BACK, STATUS_ROLLING_BACK);
// codeToEnums.put(Status.STATUS_UNKNOWN, STATUS_UNKNOWN);
// }

private Integer statusCode;

public Integer getStatusCode() {
return statusCode;
}

public static TransactionStatusEnum getEnumFor(Integer i) {
// return codeToEnums.get(i);
for(TransactionStatusEnum t : EnumSet.allOf(TransactionStatusEnum.class)) {
if(t.statusCode == i) {
return t;
}
}
throw new IllegalArgumentException("Invalid transaction status code: " + i);
}

@Override public String toString() {
return super.toString() + "(" + statusCode + ")";
}

private TransactionStatusEnum(int statusCode) {
this.statusCode = statusCode;
}
}
The output from the testcase in the previous post:
status code -> enum:
STATUS_ACTIVE(0)
STATUS_MARKED_ROLLBACK(1)
STATUS_PREPARED(2)
STATUS_COMMITTED(3)
STATUS_ROLLEDBACK(4)
STATUS_UNKNOWN(5)
STATUS_NO_TRANSACTION(6)
STATUS_PREPARING(7)
STATUS_COMMITTING(8)
STATUS_ROLLING_BACK(9)

enum -> status code:
STATUS_ACTIVE(0) 0
STATUS_COMMITTED(3) 3
STATUS_COMMITTING(8) 8
STATUS_MARKED_ROLLBACK(1) 1
STATUS_NO_TRANSACTION(6) 6
STATUS_PREPARED(2) 2
STATUS_PREPARING(7) 7
STATUS_ROLLEDBACK(4) 4
STATUS_ROLLING_BACK(9) 9
STATUS_UNKNOWN(5) 5
A third approach is to combine the above 2 approaches by building the mapping inside getEnumFor method so that subsequent calls may not need to iterate over all elements. This can be useful if the enum type has many elements. getEnumFor can be rewritten as:
public static synchronized TransactionStatusEnum getEnumFor(Integer i) {
TransactionStatusEnum en = codeToEnums.get(i);
if (en == null) {
for (TransactionStatusEnum t : values()) {
if (t.statusCode == i) {
en = t;
codeToEnums.put(i, t);
break;
}
}
}
if (en == null) {
throw new IllegalArgumentException("Invalid transaction status code: " + i);
}
return en;
}

3/11/2008

How to initialize a list when declaring it

With array, we can easily declare and initialize it at the same time:

String[] favorites = new String[] {"EJB", "JPA", "GlassFish"};
Or even simpler:
String[] favorites = {"EJB", "JPA", "GlassFish"};
We can do the same with a List using java.util.Arrays.asList method. For example:
package javahowto;

import java.util.Arrays;
import java.util.List;

public class ListTest {
public static final List<String> favorites =
Arrays.asList("EJB", "JPA", "GlassFish");

public static void main(String[] args){
System.out.println("favorites: " + favorites);
}
}

3/09/2008

UnsupportedOperationException and OperationNotSupportedException

For some unimplemented methods, I want to throw some exception instead of leaving it blank. I know there is already such a class in Java but don't know its exact name. So I typed "Operation" and then Ctrl-Space to let NetBeans to complete it. But only javax.naming.OperationNotSupportedException showed up, which is not what I want. After some search, it turns out the right class is java.lang.UnsupportedOperationException. Description from its javadoc:
java.lang.Object
extended by java.lang.Throwable
extended by java.lang.Exception
extended by java.lang.RuntimeException
extended by java.lang.UnsupportedOperationException
All Implemented Interfaces:
Serializable
Direct Known Subclasses:
HeadlessException, ReadOnlyBufferException

public class UnsupportedOperationException
extends RuntimeException

Thrown to indicate that the requested operation is not supported.

This class is a member of the Java Collections Framework.

Since:
1.2