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