12/11/2007

Access Weblogic DataSource remotely from Java and JRuby client

In previous posts, I wrote how to access JBoss or Glassfish DataSource remotely from a java client (JBoss example and Glassfish example). Here is a simple program in Java and JRuby, how to do it in Weblogic 10, using the same appserver setup as Calling Weblogic EJB 3 from JRuby and java client.
package foo;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.ResultSet;

public class DataSourceClient {
public static void main(String[] args) throws Exception {
String sql = "SELECT id FROM MEDRECWLSTORE";
Context ic = new InitialContext();
DataSource dataSource =
(DataSource) ic.lookup("jdbc/MedRecGlobalDataSource");
System.out.println("lookup dataSource returned " + dataSource);
Connection connection = dataSource.getConnection();
System.out.println("Got connection: " + connection);

Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()) {
System.out.print(rs.getString(1) + " ");
}
}
}
To do the equivalent in JRuby with less code (testDataSourceWeblogic.rb):
require 'java'
include_class 'javax.naming.InitialContext'

sql = "SELECT id FROM MEDRECWLSTORE"
ic = InitialContext.new
data_source = ic.lookup("jdbc/MedRecGlobalDataSource")
puts "lookup DataSource returned #{data_source}"
connection = data_source.getConnection
puts "Got connection #{connection}"
stmt = connection.createStatement
rs = stmt.executeQuery(sql)
while rs.next
print rs.getString(1) + " "
end
Set environment variable CLASSPATH for JRuby. Currently this is the only way to pass java classpath to JRuby (jruby1.0.2). Since I need to set CLASSPATH anyway, I will use it for compiling and running java client as well.
set CLASSPATH=%WL_HOME%\server\lib\weblogic.jar;%WL_HOME%\server\lib\api.jar;C:\simple-ejb3\classes
prepare jndi.properties somewhere in client classpath, e.g., simple-ejb3\classes directory:
java.naming.factory.initial=weblogic.jndi.WLInitialContextFactory
java.naming.provider.url=t3://localhost:7011
7011 is the default port for t3, iiop, ldap and http for the sample MedRecServer. Your port number may be different.

Compile and run java client:
C:\simple-ejb3\classes > javac -d . ..\src\foo\DataSourceClient.java
C:\simple-ejb3\classes > java foo.DataSourceClient
Run JRuby client:
jruby testDataSourceWeblogic.rb
Both clients will produce the following output, if run successfully:
lookup DataSource returned ClusterableRemoteRef(-4637547633404831470S:localhost:[7011,7011,-1,-1,-1,-1,-1]:medrec:MedRecServer [-4637547633404831470S:localhost:[7011,7011,-1,-1,-1,-1,-1]:medrec:MedRecServer/286])/286
Got connection weblogic.jdbc.rmi.SerialConnection_weblogic_jdbc_rmi_internal_ConnectionImpl_weblogic_jdbc_wrapper_PoolConnection_com_pointbase_net_netJDBCConnection30_1001_WLStub@1
-1 1 2 3 4 5 6 7 8 9 10 134217727 268435454 402653181 536870908 671088635 805306362 939524089 1073741816 1207959543 1342177270 1476394997 1610612724 1744830451 1879048178 2013265905 214748363
Note that iiop protocol is not supported when accessing remote DataSource on Weblogic. Using iiop will cause the following failure:
Exception in thread "main" java.lang.ClassCastException: weblogic.jdbc.common.internal.ConnectionEnv cannot be cast to java.io.Serializable
at weblogic.iiop.InboundResponseImpl.unmarshalReturn(InboundResponseImpl.java:103)
at weblogic.rmi.cluster.ClusterableRemoteRef.invoke(ClusterableRemoteRef.java:338)
at weblogic.rmi.cluster.ClusterableRemoteRef.invoke(ClusterableRemoteRef.java:252)
at weblogic.jdbc.common.internal.RemoteDataSource_IIOP_WLStub.getConnection(Unknown Source)
at foo.DataSourceClient.main(DataSourceClient.java:12)
Caused by: java.lang.ClassCastException: weblogic.jdbc.common.internal.ConnectionEnv cannot be cast to java.io.Serializable
at weblogic.iiop.IIOPOutputStream.writeObject(IIOPOutputStream.java:2253)
Technorati Tags: , ,

12/07/2007

Calling Weblogic EJB 3 from JRuby and java client

This is a continuation of Calling EJB 3 from JRuby client and Calling JBoss EJB 3 from JRuby client. This post will show how to do it with Weblogic application server 10.

1. project directory structure:
C:\simple-ejb3> tree /A /F
+---classes
| \---foo
\---src
\---foo
Client.java
FooBean.java
FooRemote.java
2. create EJB remote business interface and bean class under src\foo:
package foo;
import javax.ejb.*;

@Remote
public interface FooRemote {
public String echo(String s);
}

package foo;
import javax.ejb.*;

@Stateless
public class FooBean implements FooRemote {
public String echo(String s) {
return s;
}
}
3 createJRuby script (testEJBWeblogic.rb) or java client, which is almost identical to the one for glassfish and JBoss, except the default jndi name.

Note that the jndi name I'm using below is not user-friendly, but I found this is the one that always worked. I also tried using the default jndi name for weblogic EJB3 but didn't find any in admin console's View JNDI Tree. Weblogic doesn't seem to fully honor mappedName either. When I specified mappedName="FooBean", the actual jndi name after deployment is FooBean/foo.FooRemote, with the remote business interface name appended to mappedName value.
package foo;
import javax.ejb.*;
import javax.naming.*;

public class Client {
public static void main(String[] args) throws Exception {
Context ic = new InitialContext();
Object obj = ic.lookup("_appsdir_foo-ejb_jarfoo-ejb_jarFooBean_FooRemote");

//the above jndi name is the default one used by weblogic server.
//You can check ejb jndi-name in
//admin console | environment | servers | MedRecServer(admin) | view JNDI tree
//You can also customize the jndi name with @Stateless(mappedName="myname")

FooRemote foo = (FooRemote) obj;
String s = foo.echo("Hello Foo!");
System.out.println(foo + " echo returned " + s);
}
}
==== testEJBWeblogic.rb ====

require 'java'
include_class 'javax.naming.InitialContext'

ic = InitialContext.new
foo = ic.lookup("_appsdir_foo-ejb_jarfoo-ejb_jarFooBean_FooRemote")
result = foo.echo("This is foo!")
puts "Calling the EJB 3 : #{foo} returned #{result}"
4. compile java src. An environment variable WL_HOME is set for convenience but it's not required.
C:\simple-ejb3\classes> set WL_HOME=C:\bea\wlserver_10.0
C:\simple-ejb3\classes> javac -d . -classpath %WL_HOME%\server\lib\weblogic.jar;%WL_HOME%\server\lib\api.jar;. ..\src\foo\*.java
5. start Weblogic server. I will use the sample medrec server to deploy the EJB:
%WL_HOME%\samples\domains\medrec\startWebLogic
6. package and autodeploy ejb-jar. We can combine the 2 steps in 1 by using %WL_HOME%\samples\domains\medrec\autodeploy as the destdir:
C:\simple-ejb3\classes> 
jar cvf %WL_HOME%\samples\domains\medrec\autodeploy\foo-ejb.jar
foo\FooBean.class foo\FooRemote.class
7. start the EJB3 in the admin console http://localhost:7011/console, login with username "weblogic" and password "weblogic". Select Deployments on the left, then _appsdir_foo-ejb_jar (autodeployed) on the right, and click start button.

Alternatively, you can deploy and start the ejb app with weblogic.Deployer class:
java -cp %WL_HOME%\server\lib\weblogic.jar weblogic.Deployer -adminurl t3://localhost:7011 -username weblogic -password weblogic -deploy foo-ejb.jar 
8. prepare jndi.properties somewhere in client classpath, e.g., simple-ejb3\classes directory:
java.naming.factory.initial=weblogic.jndi.WLInitialContextFactory
java.naming.provider.url=iiop://localhost:7011
Note that weblogic server listens on port 7011 for iiop, t3 and http protocols. So using t3://localhost:7011 works as well. If you use the sample wl_server instead of medrec server, the default port is 7001.

9. run JRuby client. Before running the script, we need to set the environment variable CLASSPATH, which is of course different from the one for glassfish and JBoss.
set CLASSPATH=%WL_HOME%\server\lib\weblogic.jar;%WL_HOME%\server\lib\api.jar;C:\simple-ejb3\classes
jruby testEJB.rb
The test output:
Calling the EJB 3 : Delegate(602878) [weblogic.iiop.IOR[RMI:foo.FooBean_my4bwg_FooRemoteIntf:0000000000000000] @localhost:7011, <337, null>] returned This is foo!
The command to run java client (no -cp nor -classpath is needed since we already set CLASSPATH:
java foo.Client
Technorati Tags: , ,

12/04/2007

Enum wrapper for javax.transaction.Status

javax.transaction.Status contains a list of int constants representing transaction status. It suffers many drawbacks like lack of type-check and unreadable output. Here is my attempt to create a wrapper enum type:
package javahowto;

import java.util.HashMap;
import java.util.Map;
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);
}

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

private TransactionStatusEnum(int statusCode) {
this.statusCode = statusCode;
}
}
All 10 transaction status code are mirrored in the enum type. You can get the enum instance based on javax.transaction.Status code. The toString method of the enum is also overridden to print out both the string literal and the status code. This is a sample test program to illustrate these usage. Comments are welcome. Another implementation without using static mapping is described in this post (Enum wrapper for javax.transaction.Status (2))

Below is a testcase that prints the 2-way association:
package javahowto;
import java.util.EnumSet;

public class TestTransactionStatusEnum {
public static void main(String[] args) {
System.out.println("status code -> enum:");
for(int i = 0; i < TransactionStatusEnum.values().length; i++) {
System.out.println(TransactionStatusEnum.getEnumFor(i));
}

System.out.println("enum -> status code:");
for(TransactionStatusEnum e : EnumSet.allOf(TransactionStatusEnum.class)) {
System.out.println(e.toString() + "\t" + e.getStatusCode());
}
}
}
In the above test, I use 2 ways to get all elements of an enum type:
  • values() is a built-in public static method in all enum type, and returns an array of this type of enum.
  • allOf(Class<E> elementType) is a public static method in java.util.EnumSet, which returns a set of this type of enum.
Technorati Tags: ,

11/28/2007

Calling JBoss EJB 3 from JRuby client

In Calling EJB 3 from JRuby client, I wrote how to call EJB3 deployed to glassfish from a JRuby script. This post will show how to do it with JBoss EJB3, reusing the same EJB3 as the previous post (A simple EJB 3 on JBoss application server). Steps 1-7 (creating EJB3 classes, starting JBoss server, deploying EJB, preparing jndi.properties and log4j.properties) are exactly the same as that post. Here I will show the client script code and how to run it.

The JRuby script (testEJB.rb) is almost identical to the one for glassfish, except the default jndi name. TODO: pass it in as parameter instead of hardcoding it.
require 'java'
include_class 'javax.naming.InitialContext'

ic = InitialContext.new
foo = ic.lookup("FooBean/remote")
result = foo.echo("This is foo!")
puts "Calling the EJB 3 : #{foo} returned #{result}"
Before running the script, we need to set the environment variable CLASSPATH, which is of course different from the one for glassfish.
set CLASSPATH=%JBOSS_HOME%\lib\*;%JBOSS_HOME%\client\*;%JBOSS_HOME%\server\default\lib\*;C:\simple-ejb3\classes
jruby testEJB.rb
The above classpath value works only with JDK 6 or later. If you are stuck with JDK 5, then:
set CLASSPATH=%JBOSS_HOME%\lib\jboss-aop-jdk50.jar;
%JBOSS_HOME%\client\jbossall-client.jar;
%JBOSS_HOME%\server\default\lib\jbosssx.jar;
C:\simple-ejb3\classes
Technorati Tags: , ,

11/27/2007

A simple EJB 3 on JBoss application server

This is a simple tutorial on how to run EJB 3 app on Jboss application server. The version I'm using is JBossAS-5.0.0 beta2. The following app works with JBoss AS 5 & 6. AS 7 introduced significant changes to remote JNDI, and I will write a new post(Standalone Java Client for JBoss AS 7.1.1) to cover it.

Note that JBossAS-5.0.0 beta2 does not work with JDK 6 (see my previous post). So you will need to set environment variable JAVA_HOME to a JDK 5 location (e.g., set JAVA_HOME=C:\jdk5) for JBoss server. This also requires us to compile EJB classes with JDK 5 for the class file to be compatible with server JVM. However, it is perfectly OK to use JDK 6 for the client.

1. project directory structure:

C:\simple-ejb3> tree /A /F
+---classes
| \---foo
\---src
\---foo
Client.java
FooBean.java
FooRemote.java
2. create java src files under src\foo:
FooRemote.java:

package foo;
import javax.ejb.*;

@Remote
public interface FooRemote {
public String echo(String s);
}
FooBean.java:

package foo;
import javax.ejb.*;

@Stateless
public class FooBean implements FooRemote {
public String echo(String s) {
return s;
}
}
Client.java

package foo;
import javax.ejb.*;
import javax.naming.*;

public class Client {
public static void main(String[] args) throws Exception {
//JBoss' default remote jndi: <ejb-name>/remote
final String jndiName = "FooBean/remote";
Context ic = new InitialContext();
System.out.println("about to look up jndi name " + jndiName);
Object obj = ic.lookup(jndiName);
System.out.println("lookup returned " + obj);

FooRemote foo = (FooRemote) obj;
String s = foo.echo("Hello Foo on JBoss!");
System.out.println(foo + " echo returned " + s);
}
}
3. Compile java src. An environment variable JBOSS_HOME is set for convenience but it's not required.
C:\simple-ejb3\classes> set JBOSS_HOME=C:\jboss
C:\simple-ejb3\classes> javac -d . -classpath %JBOSS_HOME%\client\jbossall-client.jar;. ..\src\foo\*.java
4. start JBoss default config:
set JAVA_HOME=C:\jdk5
C:\simple-ejb3\classes> %JBOSS_HOME%\bin\run.bat
5. package and autodeploy ejb-jar. We can combine the 2 steps in 1 by using %JBOSS_HOME%\server\default\deploy as the destdir:
C:\simple-ejb3\classes>
jar cvf %JBOSS_HOME%\server\default\deploy\foo-ejb.jar
foo\FooBean.class foo\FooRemote.class
6. Prepare jndi.properties and log4j.properties somewhere in client classpath, e.g., simple-ejb3\classes directory:

# jndi.properties:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099

# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=INFO, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n
7. run the standalone java client. Note that I use JDK 6 on the client to use the wildcard (*) in classpath. JBoss client needs a sleuth of jars scattered in several JBoss server directories. With * in classpath I don't need to figure out which jars to include.
C:\simple-ejb3\classes> java -cp %JBOSS_HOME%\lib\*;%JBOSS_HOME%\client\*;%JBOSS_HOME%\server\default\lib\*;. foo.Client

about to look up jndi name FooBean/remote
lookup returned jboss.j2ee:jar=foo-ejb.jar,name=FooBean,service=EJB3
jboss.j2ee:jar=foo-ejb.jar,name=FooBean,service=EJB3 echo returned Hello Foo on JBoss!
If you have to run client with JDK 5, this is the detailed classpath:
%JBOSS_HOME%\lib\jboss-aop-jdk50.jar;
%JBOSS_HOME%\client\jbossall-client.jar;
%JBOSS_HOME%\server\default\lib\jbosssx.jar;.
This is the classpath necessary to run this simple client. Other more complex client may need additional jars. And who knows what will change in the next JBoss release? So it's always a good idea to use the wildcard classpath with JDK 6.

8. undeploy the ejb module:
del %JBOSS_HOME%\server\default\deploy\foo-ejb.jar
Tags: , ,

11/26/2007

JBossAS 5.0.0 beta2 cannot start with JDK 6

I wanted to run some EJB3 applications on JBoss, and so I installed the most recent version of JBoss (JBossAS-5.0.0.Beta2). When starting the server with JDK 6 (build 1.6.0_02-b06), I immediately got ClassNotFoundException.
===============================================================================

JBoss Bootstrap Environment

JBOSS_HOME: C:\tools\jboss

JAVA: C:\jdk6\bin\java

JAVA_OPTS: -Dprogram.name=run.bat -server -Xms128m -Xmx512m -XX:MaxPermSize=256m -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000

CLASSPATH: C:\jdk6\lib\tools.jar;C:\tools\jboss\bin\run.jar

===============================================================================

11:24:31,328 INFO [ServerImpl] Starting JBoss (Microcontainer)...
11:24:31,328 INFO [ServerImpl] Release ID: JBoss [Morpheus] 5.0.0.Beta2 (build: SVNTag=JBoss_5_0_0_Beta2 date=200704060017)
11:24:31,328 INFO [ServerImpl] Home Dir: C:\tools\jboss
11:24:31,328 INFO [ServerImpl] Home URL: file:/C:/tools/jboss/
11:24:31,328 INFO [ServerImpl] Library URL: file:/C:/tools/jboss/lib/
11:24:31,328 INFO [ServerImpl] Patch URL: null
11:24:31,328 INFO [ServerImpl] Server Name: default
11:24:31,328 INFO [ServerImpl] Server Home Dir: C:\tools\jboss\server\default
11:24:31,328 INFO [ServerImpl] Server Home URL: file:/C:/tools/jboss/server/default/
11:24:31,328 INFO [ServerImpl] Server Data Dir: C:\tools\jboss\server\default\data
11:24:31,328 INFO [ServerImpl] Server Temp Dir: C:\tools\jboss\server\default\tmp
11:24:31,343 INFO [ServerImpl] Server Config URL: file:/C:/tools/jboss/server/default/conf/
11:24:31,343 INFO [ServerImpl] Server Library URL: file:/C:/tools/jboss/server/default/lib/
11:24:31,343 INFO [ServerImpl] Root Deployment Filename: jboss-service.xml
11:24:31,359 INFO [ServerImpl] Starting Microcontainer, bootstrapURL=file:/C:/tools/jboss/server/default/conf/bootstrap-beans.xml
11:24:32,109 INFO [ProfileImpl] Using profile root:C:\tools\jboss\server\default
11:24:33,078 ERROR [AbstractKernelController] Error installing to Instantiated: name=DeploymentFilter state=Described
java.lang.IllegalStateException: Class not found: [Ljava.lang.String;
at org.jboss.metadata.spi.signature.Signature.stringsToClasses(Signature.java:174)
at org.jboss.metadata.spi.signature.Signature.stringsToClasses(Signature.java:125)
at org.jboss.metadata.spi.signature.Signature.getParametersTypes(Signature.java:292)
at org.jboss.metadata.plugins.loader.reflection.AnnotatedElementMetaDataLoader.getComponentMetaDataRetrieval(AnnotatedElementMetaDataLoader.java:138)
at org.jboss.metadata.plugins.context.AbstractMetaDataContext.getComponentMetaDataRetrieval(AbstractMetaDataContext.java:280)
at org.jboss.metadata.spi.retrieval.MetaDataRetrievalToMetaDataBridge.getComponentMetaData(MetaDataRetrievalToMetaDataBridge.java:159)
at org.jboss.aop.microcontainer.integration.AOPConstructorJoinpoint.methodHasAnnotations(AOPConstructorJoinpoint.java:202)
at org.jboss.aop.microcontainer.integration.AOPConstructorJoinpoint.hasMethodMetaData(AOPConstructorJoinpoint.java:172)
at org.jboss.aop.microcontainer.integration.AOPConstructorJoinpoint.hasInstanceOrJoinpointMetaData(AOPConstructorJoinpoint.java:152)
at org.jboss.aop.microcontainer.integration.AOPConstructorJoinpoint.dispatch(AOPConstructorJoinpoint.java:99)
at org.jboss.kernel.plugins.dependency.KernelControllerContextAction.dispatchJoinPoint(KernelControllerContextAction.java:103)
at org.jboss.kernel.plugins.dependency.InstantiateAction.installActionInternal(InstantiateAction.java:52)
at org.jboss.kernel.plugins.dependency.KernelControllerContextAction.installAction(KernelControllerContextAction.java:197)
at org.jboss.kernel.plugins.dependency.KernelControllerContextAction.install(KernelControllerContextAction.java:136)
at org.jboss.dependency.plugins.AbstractControllerContextActions.install(AbstractControllerContextActions.java:51)
at org.jboss.dependency.plugins.AbstractControllerContext.install(AbstractControllerContext.java:233)
at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:724)
at org.jboss.dependency.plugins.AbstractController.incrementState(AbstractController.java:445)
at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:555)
at org.jboss.dependency.plugins.AbstractController.resolveContexts(AbstractController.java:489)
at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:289)
at org.jboss.dependency.plugins.AbstractController.install(AbstractController.java:192)
at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.deployBean(AbstractKernelDeployer.java:302)
at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.deployBeans(AbstractKernelDeployer.java:272)
at org.jboss.kernel.plugins.deployment.AbstractKernelDeployer.deploy(AbstractKernelDeployer.java:119)
at org.jboss.kernel.plugins.deployment.BasicKernelDeployer.deploy(BasicKernelDeployer.java:64)
at org.jboss.kernel.plugins.deployment.xml.BasicXMLDeployer.deploy(BasicXMLDeployer.java:76)
at org.jboss.kernel.plugins.deployment.xml.BasicXMLDeployer.deploy(BasicXMLDeployer.java:146)
at org.jboss.system.server.profileservice.ProfileServiceBootstrap.deploy(ProfileServiceBootstrap.java:295)
at org.jboss.system.server.profileservice.ProfileServiceBootstrap.bootstrap(ProfileServiceBootstrap.java:222)
at org.jboss.kernel.plugins.bootstrap.AbstractBootstrap.run(AbstractBootstrap.java:89)
at org.jboss.system.server.profileservice.ServerImpl.doStart(ServerImpl.java:403)
at org.jboss.system.server.profileservice.ServerImpl.start(ServerImpl.java:342)
at org.jboss.Main.boot(Main.java:210)
at org.jboss.Main$1.run(Main.java:522)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.ClassNotFoundException: [Ljava.lang.String;
A search showed this JBoss bug JBAS-4491. As mentioned by one user in the report, one workaround is to use JDK 5. I don't like keeping multiple versions of JDK in my machine, and I have been running my other java applications with JDK 6 for some time. I will wait for JBossAS-5.0.0.Beta3.

Update on 10/24/2008 Friday: with more recent releases, e.g., jboss-5.0.0.CR2 & jboss-5.0.0.CR1, JBoss AS runs fine on JDK 6.

Technorati Tags:

11/22/2007

Calling EJB 3 from JRuby client

Calling EJB 3 from JRuby client is very similar to calling EJB 3 from a java standalone client, with fewer lines of code. In this post, I had a sample showing how to call EJB 3 from a standalone java client. The following JRuby test client uses the same business interface and bean class.
require 'java'
include_class 'javax.naming.InitialContext'

ic = InitialContext.new
foo = ic.lookup("foo.FooRemote")
result = foo.echo("This is foo!")
puts "Calling the EJB 3 : #{foo} returned #{result}"
This is the exact command line how to call an EJB deployed on Glassfish v2:
set CLASSPATH=%GLASSFISH_HOME%\lib\javaee.jar;%GLASSFISH_HOME%\lib\appserv-rt.jar;C:\simple-ejb3\dist\foo-client-view.jar
jruby testEJB.rb
Calling the EJB 3 : foo._FooRemote_Wrapper@e9738564 returned This is foo!
Noteworthy points:

1. My JRuby is ruby 1.8.5 (2007-11-01 rev 4810) [x86-jruby1.0.2]. It only supports setting classpath in environment variable, though I prefer to set classpath via -cp or -classpath.

2. The classpath is different for other application servers. At a minimum, you need to include the JavaEE API jar, typically named javaee.jar, and jars for application server naming service and other resources. To invoke more complex EJBs even in glassfish, you may need to include more glassfish jars.

3. No need to use a Properties to instantiate the initialContext. It saves us a couple of lines of code in both JRuby and Java clients.

Technorati Tags: , ,

11/16/2007

When to forget about constructors in JavaEE

Sometimes I see people do some sort of validation inside the constructors of a JavaEE component class, like EJB or servlet class. They do this mostly for testing or debugging purpose, but always surprised to see certain fields have not been injected. For example, the following validation in the constructor will shows the SessionContext is null!
@Stateless(mappedName="FooBean")
public class FooBean implements FooRemote {
@Resource
private SessionContext sctx;

//DO NOT DO THIS
public FooBean() {
System.out.println("Inside constructor, Injected SessionContext: "
+ sctx);
if(sctx == null) {
throw new IllegalStateException
("SessionContext not injected! Something is wrong!!");
}
}
//business methods...
}
This is an application bug and nothing is wrong with the application server. The right way to validate injection fields is the @PostConstruct method. In the @PostConstruct method, all injections are guaranteed to have completed.
@Stateless(mappedName="FooBean")
public class FooBean implements FooRemote {
@Resource
private SessionContext sctx;

@PostConstruct
private void validate() {
System.out.println("Inside @PostConstruct, Injected SessionContext: "
+ sctx);
}
//business methods...
}
Technorati Tags: , ,

11/14/2007

Shortcut key changes in NetBeans 6.0

There are a lot of good things to say about NetBeans 6.0. But if you are upgrading from earlier versions, be aware of these changes in keyboard shortcuts.

TasksNetBeans 5.xNetBeans 6.0
Expand code template on ...SpacTab
Format codeCtrl-Shift-F Alt-Shift-F
Fix importsAlt-Shift-FAlt-Shift-I
Go to typeAlt-Shift-OCtrl-O
Toggle java commentsCtrl-Shift-T Alt-Shift-W in beta 2,
Ctrl-Slash in RC 1
Toggle bookmarkCtrl-F2Supposed to be Ctrl-Shift-M,
but not working.
No such shortcut in the menu, either
Go to next bookmarkF2Ctrl-Shift-Period
Go to previous bookmarkShift-F2Ctrl-Shift-Comma


These are what I have noticed so far. Now I will have to change my keyboard shortcut habits. It is inconvenient, to say the least. Some of these changes will only cause confusion. Why would one think Tab, rather than the old Space, should be pressed for code completion? I have been using Space for code completion in all IDE, even including VIM. The only place I use Tab for completion is in tcsh or bash. Maybe this idea was borrowed from tcsh or bash? Thankfully, they didn't choose Esc.

Another source of confusion, Ctrl-Shift-F is pretty much the standard shortcut for code formatting. It works this way in NetBeans releases prior to 6.0, and Eclipse as well. Now NetBeans 6.0 slated Ctrl-Shift-F for a new feature called Find in Projects. To format code in NetBeans 6.0? one need to press Alt-Shift-F, which in NetBeans 5.x stands for Fix Imports.

Technorati Tags: ,

11/13/2007

Glassfish asadmin command auto suggestions

In previous post, I mentioned Glassfish asadmin command auto suggestions. When you enter an invalid command, glassfish can suggest how to fix it. It has been very handy for me, given that most asadmin commands are quite verbose with multiple words concatenated with "-." More examples of using asadmin auto-correction:

Say I want to work with glassfish cluster but don't know what commands are available:
C:\glassfish\bin> asadmin cluster
Closest matching command(s):
configure-ha-cluster
create-cluster
delete-cluster
list-clusters
remove-ha-cluster
start-cluster
stop-cluster
CLI001 Invalid Command, cluster.
To do something with glassfish server jvm options:
C:\glassfish\bin> asadmin jvm
Closest matching command(s):
create-jvm-options
delete-jvm-options
generate-jvm-report
CLI001 Invalid Command, jvm.
For database operations:
C:\glassfish\bin>asadmin database
Closest matching command(s):
start-database
stop-database
CLI001 Invalid Command, database.
How to manage jdbc-related tasks:
C:\glassfish\bin>asadmin jdbc
Closest matching command(s):
create-jdbc-connection-pool
create-jdbc-resource
delete-jdbc-connection-pool
delete-jdbc-resource
list-jdbc-connection-pools
list-jdbc-resources
CLI001 Invalid Command, jdbc.
How to do jms stuff:
C:\glassfish\bin>asadmin jms
Closest matching command(s):
create-jms-host
create-jms-resource
create-jmsdest
delete-jms-host
delete-jms-resource
delete-jmsdest
flush-jmsdest
jms-ping
list-jms-hosts
list-jms-resources
list-jmsdest
CLI001 Invalid Command, jms.
Anything to do with glassfish connection-pool or thread-pool:
C:\glassfish\bin>asadmin pool
Closest matching command(s):
create-connector-connection-pool
create-jdbc-connection-pool
create-threadpool
delete-connector-connection-pool
delete-jdbc-connection-pool
delete-threadpool
list-connector-connection-pools
list-jdbc-connection-pools
list-threadpools
ping-connection-pool
CLI001 Invalid Command, pool.
Technorati Tags: ,

11/12/2007

List jndi entries to debug JNDI lookup problems

What will you do if you have a problem with JNDI lookup? Maybe something you think should be there but a JNDI lookup throws NamingNotFoundException instead? I've seen people add the following debug code:
InitialContext ic = new InitialContext();
System.out.println(ic.list("java:comp/env"));
There is a better way of doing that, without adding these debugging statements. In GlassFish, you can use the command "asadmin list-jndi-entries":
C:\>%GLASSFISH_HOME%\bin\asadmin list-jndi-entries
Jndi Entries for server within root context:

foo.FooRemote: javax.naming.Referencecustom:
com.sun.enterprise.naming.TransientContextfoo.FooRemote__3_x_Internal_RemoteBusinessHome__: javax.naming.Reference
jdo: com.sun.enterprise.naming.TransientContext
mail: com.sun.enterprise.naming.TransientContext
foo.FooRemote#foo.FooRemote: javax.naming.Reference
jdbc: com.sun.enterprise.naming.TransientContext
UserTransaction: com.sun.enterprise.distributedtx.UserTransactionImpl
ejb: com.sun.enterprise.naming.TransientContext

Command list-jndi-entries executed successfully.
C:\>
list-jndi-entries is not recursive and does not list the content of any subcontext. To drill down to a subcontext, specify a --context param:
C:\>%GLASSFISH_HOME%\bin\asadmin list-jndi-entries --context ejbmgmt:
com.sun.enterprise.naming.impl.TransientContext
Command list-jndi-entries executed successfully.

C:\>%GLASSFISH_HOME%\bin\asadmin list-jndi-entries --context ejb/mgmt
MEJB: org.glassfish.kernel.javaee.MEJBNamingObjectProxy

Command list-jndi-entries executed successfully.
What if you don't remember the exact command and subcommand? GlassFish can help you find what you mean, as long as you give it a hint. There is never a need to remember these asadmin subcommands in glassfish. For example, this is how I ran the above command:
C:\>%GLASSFISH_HOME%\bin\asadmin jndi
Closest matching command(s): create-jndi-resource
delete-jndi-resource
list-jndi-entries
list-jndi-resources
CLI001 Invalid Command, jndi.

C:\>%GLASSFISH_HOME%\bin\asadmin list-jndi-entries

11/02/2007

How to get container-managed EntityManager in servlets

In this previous post, I wrote about the danger of using field/setter injection to get EntityManager in servlets A follow-up post compares container-managed EntityManager vs application-managed EntityManager.

What if I need a container-managed EntityManager in my servlet class? There are 2 ways to do that:

1. type-level injection + JNDI lookup


@PersistenceContext(unitName="my-pu", name="persistence/em")
public class FooServlet extends HttpServlet {
@Resource private UserTransaction ut;

protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
EntityManager em = null;
try {
Context ic = new InitialContext();
em = (EntityManager) ic.lookup
("java:comp/env/persistence/em");
} catch (NamingException e) {
throw new ServletException(e);
}
//call EntityManager methods inside transaction
try {
ut.begin();
Employee employee = new Employee(...);
em.persist(employee);
ut.commit();
} catch (Exception e) {
throw new ServletException(e);
}
}
}

2. web.xml + JNDI lookup

public class FooServlet extends HttpServlet {
@Resource private UserTransaction ut;
//the rest is the same as listed in bullet 1

The persistence-context-ref is declared 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>FooServlet</servlet-name>
<servlet-class>com.foo.abc.FooServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FooServlet</servlet-name>
<url-pattern>/foo</url-pattern>
</servlet-mapping>
<persistence-context-ref>
<persistence-context-ref-name>persistence/em</persistence-context-ref-name>
<persistence-unit-name>my-pu</persistence-unit-name>
</persistence-context-ref>
</web-app>

10/24/2007

Mind thread-safety when injecting EntityManager (2)

Continuing from the post Mind thread-safety when injecting EntityManager, here is a second part: container-managed EntityManager vs application-managed EntityManager.

There are important differences between the injected EntityManager, and the EntityManager created from an injected EntityManagerFactory. Basically, injected EntityManager is container-managed, meaning all of its lifecycle is controlled by the container (web container or EJB container). Application code cannot close it, or otherwise interfere with its life.

In addition, for a container-managed EntityManager, its associated PersistenceContext is automatically propagated along with the underlying JTA, from servlet A to servlet B, from servlet to EJB, from EJB a to EJB B, and so on. As such, EntityManager of the same configuration injected into various classes can share the same PersistenceContext in a call stack.

On the other hand, EntityManager created from EntityManagerFactory is application-managed EntityManager. Application code is responsible for managing its whole lifecycle. And there is no PersistenceContext propagation for application-managed EntityManager.

Are all EntityManager's obtained from EntityManagerFactory application-managed EntityManager? Yes.

Is it possible to get a container-managed EntityManager from EntityManagerFactory? No.

You may have read about the method EntityManagerFactory.getEntityManager(), which returns a container-managed EntityManager. This method was considered and included in the early draft version of Java Persistence API, but was eventually removed from its final release.

10/19/2007

Mind thread-safety when injecting EntityManager

As I wrote in post Why we need type-level injections in JavaEE, injecting EJB 3 stateful beans into servlet instance fields is not thread-safe. Along the same line, injecting EntityManager with @PersistenceContext into servlet instance variables is not thread-safe, either. EntityManager is just not designed to be thread-safe.

For example, the following code snippet of a servlet class is incorrect:

public class FooServlet extends HttpServlet {
//This field injection is not thread-safe.
//FIXME
@PersistenceContext
private EntityManager em;
...
}
One way to fix this is to inject EntityManagerFactory instead. EntityManagerFactory is guaranteed to be thread-safe. For example:
public class FooServlet extends HttpServlet {
//This field injection is thread-safe
@PersistenceUnit
private EntityManagerFactory emf;

protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
EntityManager em = emf.createEntityManager();
//work with em
}
}

10/11/2007

Resource Injection and Findbugs

Findbugs should have a place in every Java developer's toolbox. But it doesn't seem to understand Java EE resource injection yet, giving some false alarms.

If I run findbugs on this simple JavaEE application client code:

public class AppClientMain {
@EJB
protected static CalculatorRemote calc;

public static void main(String[] args) {
AppClientMain main = new AppClientMain();
main.calculate();
}

public void calculate() {
calc.calculate();
}
}
FindBugs will emit at least 2 false alarms:
1. Class AppClientMain(Priority 1, Category MALICIOUS_CODE)
MS_SHOULD_BE_FINAL: AppClientMain.calc isn't final but should be

A mutable static field could be changed by malicious code or by
accident from another package. The field could be made final to
avoid this vulnerability.
The injection field in application client main class must be declared static, though it can use any access modifier like public, protected, package private, or private. That's the rule. If I change the field to final, as suggested by findbugs, the container (application client container) won't be able to inject anything into it. However, once I change the field to private, findbugs is happy.

BTW, just to be clear, injection fields in all components other than application client main class or its superclass must not be static. In all cases, injection fields must not be final. But I don't think findbugs need to complain about non-final instance fields.
2. Class AppClientMain(Priority 2, Category CORRECTNESS)
NP_UNWRITTEN_FIELD: Read of unwritten field calc in
AppClientMain.calculate()

The program is dereferencing a field that does not seem to ever
have a non-null value written to it. Dereferencing this value
will generate a null pointer exception.
Another variant of this warning is:
UWF_UNWRITTEN_FIELD: Unwritten field:...This field is never written.
All reads of it will return the default value. Check for errors
(should it have been initialized?), or remove it if it is useless.
No, this shouldn't result in NPE. If you do see NPE at runtime, it can only mean one of the following:

1. Your application is not properly configured. For instance, the @EJB reference cannot be resolved to a deployed EJB. Then you need to make sure the @EJB reference is correctly mapped via @EJB(mappedName) attribute, mapped-name, ejb-link, or jndi-name elements in descriptors and deployment plans. Most likely, your server should already reported a NameNotFoundException in server log.

2. A bug in your application server, which is much less likely than (1) but still possible.

10/09/2007

Why we need type-level injections in JavaEE?

As I wrote in the previous post, JavaEE type-level injection is more verbose than field and setter injections. So why don't we just all use the simple field or setter injections? Well, there are some times a type-level injection is more appropriate

1. You want to lazily initialize a field such as a data source or bean. Having the container to initialize a field via field injection or setter injection has its overhead. With type-level injection, you have the resource and EJB reference fully configured without descriptors, but defer the actual initialization. Whenever you need the resource, just look it up.

Type-level injection also works well if you want to reference the injected resource in some non-component utility classes that are in the same naming context. Since only JavaEE component classes can take injections, you can't inject into POJO utility classes. For example, you can

@Resource(type=javax.sql.DataSource.class, 
name="jdbc/defaultDataSource,
mappedName="jdbc/__default")

public class FooServlet extends HttpServlet {
//the service method, or doGet, doPost, etc, references ServletUtil
}

public class ServletUtil {
private void help() throws ServletException {
//This is how to use the resource injected at type-level in FooServlet
Connection con = null;
try {
InitialContext ic = new InitialContext();
DataSource ds =
(DataSource) ic.lookup("java:comp/env/jdbc/defaultDataSource");
con = ds.getConnection();
} catch (NamingException ex) {
throw new ServletException(ex);
} catch (SQLException ex) {
throw new ServletException(ex);
} finally {
//close connection here
}
}
The above example will also if we instead use field or setter injection. But the instance filed in FooServlet class that takes the injection may not be used at all. Another approach is to pass the data source reference injected into FooServlet as method parameters to ServletHelper. But it pollutes the API. It appears more natural, at least to me, to use type-level injection in this case.

2. You want to restrict the scope or sharing of the variable that references the resource. Field and setter injections both use instance variables, which are accessible to all methods and all threads of the current component instance. Instance variables represent shared states. With type-level injections, you typically assign the resource to a local variable and thus is thread-safe.

This is the reason why you shouldn't inject an EJB 3 stateful session bean into a servlet class variable. In so doing, this stateful bean variable will be shared by all request-processing threads running inside the servlet's service method. Servlet is stateless, so it should not keep state (the stateful session bean reference).

10/08/2007

Type-level injection in JavaEE

There are 3 types of resource injections in JavaEE: field injection, setter injection, and type-level injection, in the order of usage frequency.

Unlike field and setter injections, type-level injections are not really injection. It does not inject anything into anywhere; it just declares a resource reference in a descriptor-free manner.

To use resource injected at type-level, you will need to do a traditional JNDI lookup. What we have saved here, compared to pre-JavaEE-5, is there is no need for deployment descriptors.

A servlet example in glassfish:

@Resource(type=javax.sql.DataSource.class, 
name="jdbc/defaultDataSource,
mappedName="jdbc/__default")

public class FooServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
//This is how to use the resource injected at type-level
Connection con = null;
try {
InitialContext ic = new InitialContext();
DataSource ds =
(DataSource) ic.lookup("java:comp/env/jdbc/defaultDataSource");
con = ds.getConnection();
} catch (NamingException ex) {
throw new ServletException(ex);
} catch (SQLException ex) {
throw new ServletException(ex);
} finally {
//close connection here
}
}
Type-level injections are inherently verbose, compared to field and setter injections, where a bare @EJB or @Resource is sufficient in some cases. Field and setter injections can use the field type and parameter type to infer the resource type, respectively. Type-level injections can't. All the necessary attributes must be present.

For @Resource, you must specify at least name and type. For @EJB, you must have at least beanInterface and name.

9/19/2007

Line tools NetBeans plugin

I don't like overloading my IDE with a bunch of plugins, but this is a must-have for my NetBeans (currently version 5.5.1).

[Update] Those features in line tool has been integrated into NetBeans proper 6.x. So if you are running NetBeans 6.1 (the latest as of 5/2008), line tool plugin is no longer needed.

I use it everyday to copy and paste lines of text without using CTRL-C CTRL-V. This is its official description:

Line Tools Support following operations in text editors:

1. Move Line (or lines spanned by selection) Up

2. Move Line (or lines spanned by selection) Down

3. Copy Line (or lines spanned by selection) Up

4. Copy Line (or lines spanned by selection) Down

5. Sort lines spanned by selection in ascending order

6. Sort lines spanned by selection in descending order

7. Remove duplicate lines while sorting. Maintain the caret position, the selection and the order of start and end of the selection when moving/copying the lines.


This is the first plugin I will install for every new NetBeans release. Unfortunately, NetBeans can't automatically migrate my installed plugins.

Oddly, this plugin doesn't show up in the official NetBeans plugin portal. The place to go is the author's (Sandip Chitale) blog.

You can choose either download the *.nbm file and manually install it, or add a new update center (Sandip Chitale's update center) to your NetBeans. After adding the update center, you will able to download a lot more good stuffs by Sandip Chitale.

9/15/2007

JAVA overloaded

JAVA is now the stock symbol for Sun, replacing SUNW. This word JAVA has become more overloaded, and can mean at least 3 things: (1) the Java programming language; (2) the Java Island in Indonesia; and (3) the stock symbol for Sun Microsystems.

So every time you google "java", you will have more mixed results to filter out. Unfortunately, the first match by google and msn is the stock chart of Sun Microsystems, Inc. That is a little inconvenient for java developers, but seems a good way to bring attention to Sun's stock.

Tags:

8/11/2007

Java class extends clause comes before implements clause

The following class failed to compile with either JDK 5 or 6:

import java.io.Serializable;

public class ImplementsExtendsOrder implements Serializable extends Object {
}

\jdk6\bin\javac ImplementsExtendsOrder.java
ImplementsExtendsOrder.java:7: '{' expected
public class ImplementsExtendsOrder implements Serializable extends Object {
^
1 error
It turned out when declaring a java class, the extends clause has to come before implements clause. The error message about missing {did lead me to check matching {} many times without finding anything.

The correct way to declare such a class is to put implements clause before extends clause:
import java.io.Serializable;

public class ImplementsExtendsOrder extends Object implements Serializable {
}
Tags: ,

7/09/2007

A simple EJB 3 + Servlet application

In previous post, I wrote a simple EJB3 bean invoked from a standalone java client. Standalone java client is convenient for prototyping and testing purpose. The most common way to invoke EJB is from a web app. Here I will add such a simple web app to invoke the foo-ejb module written in previous post.

Steps 1 - 5 are the same as in previous post, except in step 2, foo/Client.java is not needed here.

6. Servlet class

package foo;
import java.io.*;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;

public class FooServlet extends HttpServlet {
@EJB(mappedName="foo.FooRemote")
private FooRemote foo;

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Servlet FooServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>FooRemote.echo returned: " + foo.echo("From FooServlet") + "</h1>");
out.println("</body>");
out.println("</html>");
}

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
7. 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>FooServlet</servlet-name>
<servlet-class>foo.FooServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FooServlet</servlet-name>
<url-pattern>/FooServlet</url-pattern>
</servlet-mapping>
</web-app>

8. Make sure foo.FooRemote.class is in the webapp's classpath. For example, C:\simple-ejb\build\classes directory is included in the webapp's classpath.

9. Build the webapp to produce a simple-web.war with the following content:
index.jsp
WEB-INF/web.xml
WEB-INF/classes/foo/FooServlet.class
WEB-INF/classes/foo/FooRemote.class
10. Make sure the ejb jar is already deployed (in step 5). Start glassfish server and deploy the war file:
set JAVAEE_HOME=C:\glassfish
%JAVAEE_HOME%\bin\asadmin.bat start-domain
C:\simple-web\dist> copy simple-web.war %JAVAEE_HOME%\domains\domain1\autodeploy
Another way to deploy the war is using asadmin command:

C:\simple-web\dist> %JAVAEE_HOME%\bin\asadmin deploy simple-web.war
11. Run it by entering the following url in browser:
http://localhost:8080/simple-web/FooServlet

Update: Starting from EJB 3.1, EJB classes can be packaged in WAR files. So if the target server supports EJB 3.1, the ejb jar file in this example can be merged into WAR (typically by putting all EJB classes under WEB-INF/classes).

6/26/2007

A simple EJB 3 application, hand-made

This is a simple EJB 3 app that is written, deployed and run from command line and vim, with no IDE, no ant, no maven, no admin console. The target appserver is glassfish.

1. project directory structure:

C:\simple-ejb3> tree /A /F
+---classes
| \---foo
\---src
\---foo
Client.java
FooBean.java
FooRemote.java
2. create java src files under src\foo:
package foo;
import javax.ejb.*;

@Remote
public interface FooRemote {
public String echo(String s);
}

package foo;
import javax.ejb.*;

@Stateless
public class FooBean implements FooRemote {
public String echo(String s) {
return s;
}
}

package foo;
import javax.ejb.*;
import javax.naming.*;

public class Client {
public static void main(String[] args) throws Exception {
Context ic = new InitialContext();
Object obj = ic.lookup(FooRemote.class.getName());
System.out.println("lookup returned " + obj);

FooRemote foo = (FooRemote) obj;
String input = (args.length > 0) ? args[0] :
"No application arg specified.";
String s = foo.echo(input);
System.out.println("foo.echo returned " + s);
}
}
3. Compile java src. An environment variable JAVAEE_HOME is set for convenience but it's not required.
C:\simple-ejb3\classes> set JAVAEE_HOME=C:\glassfish
C:\simple-ejb3\classes> javac -d . -classpath %JAVAEE_HOME%\lib\javaee.jar;. ..\src\foo\*.java
4. start the appserver:
C:\simple-ejb3\classes> %JAVAEE_HOME%\bin\asadmin.bat start-domain
5. package and autodeploy ejb-jar. We can combine the 2 steps in 1 by using %JAVAEE_HOME%\domains\domain1\autodeploy as the destdir:
C:\simple-ejb3\classes> jar cvf %JAVAEE_HOME%\domains\domain1\autodeploy\foo-ejb.jar foo\FooBean.class foo\FooRemote.class
6. run the standalone java client:
C:\simple-ejb3\classes> java -cp %JAVAEE_HOME%\lib\javaee.jar;%JAVAEE_HOME%\lib\appserv-rt.jar;. foo.Client

lookup returned foo._FooRemote_Wrapper@e9738564
foo.echo returned No application arg specified.
7. undeploy the ejb module:
del %JAVAEE_HOME%\domains\domain1\autodeploy\*.jar
It's pretty easy, isn't it? No deployment descriptors, no client stubs, no jndi.properties file.

6/16/2007

Centralize exception handling in java methods (2)

In a previous post, I wrote about how to centralize java exception handling inside one method by saving the exception to a local variable. Some of you might ask, "why not put exception logic into a separate private method?" I would avoid this approach for the following reasons:

1. When the exception-handling logic is specific to the current method, I like to scope it to the current method. Even if we have a private void handleException, it can still be called by other methods in the current class.

2. With a private exception-handling method, you will need to pass in all the info needed for handling the exception, in addition to the Exception. In addition, what info is needed may change overtime. This may lead to methods like this:

private void handleException(Exception e, String name, String info)
throws AppException

6/15/2007

Where to put persistence.xml in web app?

In all cases, persistence.xml always resides in {root-of-persistence-unit}/META-INF/ directory. For example,

foo.war:
WEB-INF/classes/META-INF/persistence.xml //good
WEB-INF/classes/com/foo123/jpa/Project.class
WEB-INF/web.xml
index.jsp
You may also package entity classes and persistence.xml inside a library jar, which is packaged inside the war: WEB-INF/lib
foo.war:
WEB-INF/lib/my-entities.jar
WEB-INF/web.xml
index.jsp

my-entities.jar:
META-INF/persistence.xml //good
com/foo123/jpa/Project.class
For comparison, some invalid configurations are:
foo.war:
WEB-INF/persistence.xml //invalid
WEB-INF/web.xml
WEB-INF/classes/com/foo123/jpa/Project.
index.jsp
-----------------------------------------------
foo.war:
META-INF/persistence.xml //invalid
WEB-INF/classes/com/foo123/jpa/Project.class
WEB-INF/web.xml
index.jsp
-----------------------------------------------
foo.war:
persistence.xml //invalid
WEB-INF/classes/com/foo123/jpa/Project.class
WEB-INF/web.xml
index.jsp
-----------------------------------------------
foo.war:
META-INF/persistence.xml //invalid
com/foo123/jpa/Project.class
WEB-INF/web.xml
index.jsp
In all these invalid configurations, EntityManager lookup will fail with javax.naming.NameNotFoundException, or @PersistenceContext injection will be silently skipped and the em variable is always null, and hence NullPointerException when referenced.

6/13/2007

Use ant NoBannerLogger and -emacs options for better output (2)

In my previous post, I wrote about using ant options -emacs -logger org.apache.tools.ant.NoBannerLogger to strip out empty targets and task names. Here are more tips to save some typing:

1. define an shell-level alias to include these options.

In bash, add to $HOME/.bashrc:
alias ant='ant -emacs -logger org.apache.tools.ant.NoBannerLogger'


In tcsh, add to $HOME/.tcshrc:
alias ant 'ant -emacs -logger org.apache.tools.ant.NoBannerLogger'


On windows, create a ant.bat under a directory (e.g., C:\bin) that is in the PATH before %ANT_HOME%\bin:

@echo off
%ANT_HOME%\bin\ant.bat -emacs -logger org.apache.tools.ant.NoBannerLogger %*
2. If you don't like using alias, another option is to modify $ANT_HOME/bin/ant or %ANT_HOME%\bin\ant.bat to include these options. Change
ant_exec_args=

To
ant_exec_args="-emacs -logger org.apache.tools.ant.NoBannerLogger"


The drawback is, every time you reinstall ant, it's gone. Also note that you should only use either 1 or 2, but not both. Otherwise, ant will complain it can't use 2 loggers.

3. Yet another option is to define a shell variable to hold the ant options:
bash-3.00$ export anto="-emacs -logger org.apache.tools.ant.NoBannerLogger"
bash-3.00$ ant $anto test

6/12/2007

Centralize exception handling in java methods

Oftentimes we need to handle a series of java exceptions in one method in a similar manner. So it's desirable to centralize the exception logic to avoid duplication. For example, the following method looks up UserTransaction and then performs the transaction. You will need to handle at least 6 exceptions. I find it handy to first save the exception to handle it at the end.

    public void transaction2() throws AppException {
Exception savedException = null;
//1. lookup UserTransaction
UserTransaction ut = null;
try {
Context ic = new InitialContext();
ut = (UserTransaction) ic.lookup("java:comp/UserTransaction");
} catch (NamingException ex) {
savedException = ex;
}

//2. perform transaction
if(ut != null) {
try {
ut.begin();
//...
ut.commit();
} catch (NotSupportedException ex) {
savedException = ex;
} catch (IllegalStateException ex) {
savedException = ex;
} catch (SecurityException ex) {
savedException = ex;
} catch (HeuristicMixedException ex) {
savedException = ex;
} catch (SystemException ex) {
savedException = ex;
} catch (HeuristicRollbackException ex) {
savedException = ex;
} catch (RollbackException ex) {
savedException = ex;
}
}
if(savedException != null) {
//log it, etc, etc
throw new AppException(savedException);
}
}

6/11/2007

How to set prompt in tcsh and bash

Wherever I go, I like to set prompt to display the current directory. I mean the complete path of current directory, not just the last element, and they all must be in one single line.

This is good:

/usr/bin/X11 >
Avoid this:

bash-3.2$ cd /usr/bin/X11
bash-3.2$
localhost$ cd /usr/bin
localhost$ /usr/bin
For tcsh, I have this in my $HOME/.tcshrc:
set prompt="%/ > "
For bash, include this in $HOME/.bashrc:
PS1="\w > "
, Or you can also run export PS1="\w > " from a bash terminal.

The benefits?

1. I don't need to type pwd 100s times a day. I don't even remember when was the last time I typed pwd.

2. I can easily copy the current working directory and pasted it elsewhere. Notice the trailing space in the value of prompt and PS1? With them I can simply double-click the path to select and copy it.

Tags: , ,

6/06/2007

Use ant NoBannerLogger and -emacs options for better output

It can be hard to get useful data from a huge ant output file. So I like to use two options when running ant:

1. -logger org.apache.tools.ant.NoBannerLogger to strip out any empty targets.
2. -emacs to strip out leading [taskname]

Here is a comparison of running a simple build.xml target:

  <target name="test" depends="test2, test3, test4">
<echo message="Running target test"/>
</target>

<target name="test2"/>
<target name="test3"/>
<target name="test4"/>
/home/javahowto > ant test
Buildfile: build.xml

test2:

test3:

test4:

test:
[echo] Running target test
/home/javahowto > ant -logger org.apache.tools.ant.NoBannerLogger test
Buildfile: build.xml

test:
[echo] Running target test
/home/javahowto > ant -emacs -logger org.apache.tools.ant.NoBannerLogger test
Buildfile: build.xml

test:
Running target test

6/01/2007

Start java web start app from command line

When I need to start a Java webstart app, I usually do it from command line, instead of using a browser. There are 2 slightly different ways to do that:

1. Run javaws with the url to the jnlp file:

%JAVA_HOME%\bin\javaws http://www.foo123.com/bar/buz.jnlp
2. Save the jnlp file locally and run javaws command with the local copy. If you enter this jnlp url in the browser's address bar, it will run the app instead of downloading the file. So here I use wget to download the file.
cd %HOMEPATH%\Desktop
del buz.jnlp
wget http://www.foo123.com/bar/buz.jnlp
%JAVA_HOME%\bin\javaws %HOMEPATH%\Desktop\buz.jnlp
Option 2 is a bit faster since it doesn't retrieve the jnlp file every time, and can still work even in case of network problem. But the downside is you may not get the latest version of the application. This auto-update is supposed to be the main benefit of jnlp. If your applications don't update frequently, or you don't care running an obsolete version, option 2 is fine.

To make the command shorter, I created a batch file buz.bat:
@echo off
%JAVA_HOME%\bin\javaws %HOMEPATH%\Desktop\buz.jnlp
rem %JAVA_HOME%\bin\javaws http://www.foo123.com/bar/buz.jnlp

5/18/2007

When to join threads

Let's say I need to spawn multiple threads to do the work, and continue to the next step only after all of them complete. I will need to tell the main thread to wait. The key point is to use Thread.join() method. For example,

package foo;
import java.util.Vector;
public class ThreadTest {
private Vector<String> threadNames = new Vector<String>();
public static void main(String[] args) {
ThreadTest test = new ThreadTest();
test.threadTest(Integer.parseInt(args[0]));
System.out.println(test.threadNames);
}
private void threadTest(int numOfThreads) {
Thread[] threads = new Thread[numOfThreads];
for (int i = 0; i < threads.length; i++) {
threads[i] = new foo.ThreadTest.MyThread();
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
try {
threads[i].join();
} catch (InterruptedException ignore) {}
}
}
class MyThread extends Thread {
public void run() {
for (int i = 0; i < 1000000; i++) {
i = i + 0;
}
threadNames.add(getName());
}
}
}
The output when running this program with 10 threads:
[Thread-1, Thread-3, Thread-0, Thread-5, Thread-7, Thread-6, Thread-9, Thread-2, Thread-8, Thread-4]
The order in which the threads are executed is random, which is expected.

Also note that we use two for-loops, the first to create and start each thread, and the second loop to join each thread. If each thread is joined right after start, the effect is these threads are executed sequentially, without the desired concurrency. For example, the following code snippet results in serial execution:
// The following is NOT concurrent:
//
private void threadTest(int numOfThreads) {
Thread[] threads = new Thread[numOfThreads];
for (int i = 0; i < threads.length; i++) {
threads[i] = new foo.ThreadTest.MyThread();
threads[i].start();
try {
threads[i].join();
} catch (InterruptedException ignore) {
}
}
}
Output:

[Thread-0, Thread-1, Thread-2, Thread-3, Thread-4, Thread-5, Thread-6, Thread-7, Thread-8, Thread-9]
If we don't use any join at all, threadNames, when printed, may be empty, or partially filled, since the main thread will just move on when it gets the chance. The main thread will still wait, at the very last step, for all threads to complete, before exiting the JVM. The output for running 10 threads may be:
[Thread-0, Thread-1, Thread-2, Thread-3]
The following is a general-purpose code skeleton for running concurrency test using anonymous Runnable inner-class:

Thread[] threads = new Thread[numOfThreads];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
System.out.println("xxx");
}
});
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
}
}
Another version using java.util.concurrent.CountDownLatch: When to join threads with CountDownLatch

Tags: ,

5/03/2007

My glassfish password file


AS_ADMIN_MASTERPASSWORD=changeit
AS_ADMIN_PASSWORD=adminadmin
AS_ADMIN_USERPASSWORD=xxx
password.txt contains the above password values. Glassfish application server doesn't allow you to specify password as command line arguments (or at least it gives warning). Instead, it uses the --passwordfile argument to get passwords from a plain text file, which can only be accessed by the owner.

Nobody can memorize these keys, and several times I forgot where I saved this file. So keep a copy here for future copy-paste.

If the keys in the password file are not what glassfish expected, it gives this warning:
Options specified in passwordfile are either invalid or deprecated. Passwordfile can only be used to specify values for password options. Use of passwordfile to specify values for other options is deprecated. Passwordfile should contain the following format AS_ADMIN_=.

4/05/2007

Sun certification exam retake promotion

Sun is offering a certification exam retake promotion with Priority Code WW47CX2 in most countries. It can be used for various java certifications like SCJP, SCJD, SCWCD, SCBCD, SCEA, among many other certifications. You must purchase the voucher between March 21 and June 23, 2007. Please see the above link for the complete terms and conditions.

3/12/2007

Who set my CLASSPATH?

I never set CLASSPATH environment variable. Instead I always use the explicit -classpath or -cp options when running java, javac, javap, etc. The other day I was surprised to see a CLASSPATH environment variable is set:

C:\tmp> set CLASSPATH
CLASSPATH=.;C:\Program Files\Java\jre1.5.0_09\lib\ext\QTJava.zip

C:\tmp> dir "C:\Program Files\Java\jre1.5.0_09\lib\ext\QTJava.zip"
06/12/2006 09:57 AM 1,180,476 QTJava.zip
It seems when I installed apple's quicktime plugin, it automatically set this CLASSPATH in my environment. I don't like this. I always assume a clean environment when running any java applications. So I removed it:
C:\tmp> set CLASSPATH=

C:\tmp> set CLASSPATH
Environment variable CLASSPATH not defined
I can tolerate an installer modifying my PATH, but not CLASSPATH.

3/06/2007

A proposed README file in auto-loaded directories

In my previous post, I suggested not to put unused jar/zip files (*.jar, *.zip) in those directories that are automatically scanned and loaded. But how do we tell such a directory from regular ones? I think we need a quick way to know it without reading official documentations, if there is any.

I propose these magic directories contain a README file that looks like this:

All jar files (*.jar) in this directory are automatically loaded by the application server common classloader. The application server scans this directory in every 5 seconds by default. This scanning interval may be configured in config/server.xml. Note that this scan is non-recursive and any subdirectories are ignored.

WARNING: please do not put unused jar files in this directory to avoid class file conflicts.

For more information, please see the official application server documentation:
http://docs.my-application-server.com/classloader

2/27/2007

Don't copy jar files in auto-loaded directories

Jar files in some directories are automatically loaded into JVM in some applications. Some examples of these magic directories are, common/lib and shared/lib in Tomcat, $JAVA_HOME/jre/lib/endorsed, $JAVA_HOME/jre/lib/ext, etc. This can ease the task of classpath management, to some extent. But we should also be very careful while doing anything in these directories. Specifically, do not put any unused *.jar files in these directories, since they will also be loaded by java runtime and may cause conflict.

Sometimes I need to try a different version of a jar. I rename the old jar to foo-02272007.jar and copy the new jar over. In an auto-loaded directory, this may cause nasty random errors due to conflict between 2 versions of the same jar. I should really back up the old jar as foo.jar.20202007 without a .jar extension to exclude it from auto-loading.

2/10/2007

Comment out xml file fragments in java IDE

It's easy to comment out and uncomment lines of Java code in any Java IDE, using shortcut keys like Ctrl-/ (in Eclipse), or Ctrl-Shift-T (in NetBeans). But I haven't found such features for XML commenting. I have to always manually add <!-- and --> even when editing those XML files inside Eclipse or NetBeans.

Why is it missing in major Java IDEs? Nowadays almost all Java projects have some XML files, like ant build files, deployment descriptors, etc. And both NetBeans and Eclipse have nice XML editing features, including syntax highlighting, element and attribute auto-completion, and element tree view. All these seem to suggest Java IDEs are serious in supporting XML development. One would think commenting/uncommenting is one of the basic requirements. Why can't I select some lines, press Ctrl-/ or Ctrl-Shift-T to comment out them?

Maybe one reason is that XML doesn't allow nested comments. So if my selection already contains XML comments, I can't add an outer comment. But even in this case, the IDE can either issue a waring, or add 2 smaller comments, one before the existing comment and the other after.

2/06/2007

Override equals and hashCode methods (part 2)

Eclipse can generate equals and hashCode methods for any class that has state fields (see post). What if you are writing code outside Eclipse, e.g., using a plain text editor? I usually copy java.lang.String.equals(Object) and modify it:

C:\jdk5> jar xvf src.zip java/lang/String.java
inflated: java/lang/String.java

C:\jdk5> gvim java/lang/String.java
It basically looks like this:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
xxx anotherString = (xxx)anObject;
//compare the states of two objects
}
return false;
}
I then save this template in C:\tmp\equals.txt. Next time I need it inside vim, I just read it into vim with :r /equals.txt.

1/31/2007

Project-wide find and replace in NetBeans 6

I am so glad to notice that NetBeans 6 (development build) now supports project-wide find and replace. See my previous post about this bug that plagued NetBeans since 2001. This bug fix is long overdue, but it's great to see NetBeans now has the real find-replace.

So in NetBeans 6, I can click on a project node, or package node, or any directory node in my project, and find. The pop-up window lets you specify the replacement string and options (case-sensitive, match whole word, regular expression, etc).

What's really nice is, it then opens up output windows where you can review, select, and de-select matches for replace:

1/29/2007

Why we have to use -jar option when running a self-contained jar

We can run a self-contained jar file with a command like this:

java -jar foo.jar
,where foo.jar contains a main class that is specified in META-INF/MANIFEST.MF with a Main-Class entry. What will happen if running if without -jar option? java will read foo.jar as a main class named jar in package foo:
C:\tmp>java foo.jar
Exception in thread "main" java.lang.NoClassDefFoundError: foo/jar
I guess that's the reason why -jar option is necessary to tell JVM to look for a jar file rather than a class file foo/jar.class. But realistically, how often people name their main class jar? Why can't we disallow this naming and then omit -jar option when running a jar file? JVM should be able to figure it out by .jar extension.

The first step of installing Jboss and Glassfish appserver

Both Jboss and Glassfish application server provide an installer in the form of a self-contained jar file. Users can just download this jar file and run java command to install it in any platform.

To install Jboss appserver 4.0.5:

java -jar jems-installer-1.2.0.GA.jar
To install Glassfish, according to the instruction:
java -Xmx256m -jar glassfish.jar
Jboss jar is around 71M and glassfish zip 60M in size, and I think they are roughly comparable. But notice the difference in JVM options. I don't like having to use -jar option to run a jar file (see my previous post). Glassfish installer needs yet another more advanced JVM option. There is a big difference in usability between running it without options and running it with one or more options. The more options, the more chances for errors.

To be fair to glassfish, I tried java -jar glassfish.jar without -Xmx256m to see if I get OutOfMemoryError. The installation completes in a couple of seconds without any problem, with JDK 1.4.2, JDK 5, or JDK 6 on Windows XP. So it seems safe to just run java -jar glassfish.jar to install it.

[update 1/1/2007]: In Glassfish V2 update 1 released in 12/2007, you do need -Xmx256m option to install it. Glassfish V2U1 may have added more stuff that takes more memory.

[update 11/24/2008]: Changed -Xmx256 to -Xmx256m. Reader Bartek pointed out this error in the comment.

1/26/2007

Override equals and hashCode methods

If you want to override equals method in your class, you should also override hashCode method as well. There are a lot of ways the two methods can be implemented incorrectly.

I found the easiest way is to use the wizard in Eclipse, just running Source | Generate hashCode() and equals()... either from menu or context menu. For example, this is what Eclipse generated for a simple class Scrap with a instance variable number:


/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + number;
return result;
}

/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Scrap other = (Scrap) obj;
if (number != other.number)
return false;
return true;
}
I don't feel I need to modify anything in the 2 generated methods. I especially like the @Override annotation being used in the generated code. It clearly documents the method overriding and enforces the compile-time check.