12/31/2010

Simple webapp with BASIC authentication

To enable BASIC authentication in a webapp, one needs to
(1) configure roles and other security aspects in web.xml;

(2) administratively create the users in application server; and

(3) map the roles declared in step 1 to users created in step 2, with appserver-specific descriptor.

The following is a simple webapp that only contains 3 files: TestServlet class, web.xml, sun-web.xml.

package test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class TestServlet extends HttpServlet {
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();
out.println("Hello from " + getServletName());
}

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

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

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>test.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/TestServlet</url-pattern>
</servlet-mapping>

<security-constraint>
<web-resource-collection>
<web-resource-name>secure</web-resource-name>
<url-pattern>/TestServlet</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>tester</role-name>
</auth-constraint>
</security-constraint>

<login-config>
<auth-method>BASIC</auth-method>
<realm-name>file</realm-name>
</login-config>

<security-role>
<role-name>tester</role-name>
</security-role>
</web-app>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD GlassFish Application Server 3.0 Servlet 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_3_0-0.dtd">
<sun-web-app>
<security-role-mapping>
<role-name>tester</role-name>
<principal-name>joe</principal-name>
<group-name>user</group-name>
</security-role-mapping>
</sun-web-app>
To create the user in GlassFish:
$ $GLASSFISH_HOME/bin/asadmin create-file-user --group user joe
Compile TestServlet class and jar up *.class and *.xml into a test.war:
WEB-INF/classes/test/TestServlet.class
WEB-INF/sun-web.xml
WEB-INF/web.xml
Copy it to $GLASSFISH_HOME/domains/domain1/autodeploy directory to deploy it. To run it go to the url http://localhost:8080/test/TestServlet. After entering the username and password, the following response is displayed:
Hello from TestServlet

12/12/2010

vnc notes on Linux

To start vncserver: $ vncserver

To stop vncserver: $ vncserver -kill :1

To make the vnc view screen larger, edit /usr/bin/vncserver script:

sudo chmod a+w /usr/bin/vncserver
vim !$

#$geometry = "1024x768";
$geometry = "1500x1050";
A sample $HOME/.vnc/xstartup file:
#!/bin/sh

# Uncomment the following two lines for normal desktop:
unset SESSION_MANAGER
exec /etc/X11/xinit/xinitrc

[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
xterm -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &
twm &

11/05/2010

Programmatically bind global JNDI resources

Starting from Java EE 6, an application can configure JNDI resources in global, application, module, and component namespaces. Usually this is done with @Resource annotations and deployment descriptors. It is also possible to bind global JNDI resources programmatically in a Java EE application, as shown in the following servlet.

package test;

import java.io.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import javax.naming.*;
import javax.annotation.*;

@WebServlet(urlPatterns = "/*")
public class TestServlet extends HttpServlet {

private static final String name = "java:global/env/foo";
private static final String value = "FOO";

protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter out = response.getWriter();

try {
InitialContext ic = new InitialContext();
try {
Object obj = ic.lookup(name);
out.println("found " + name + " : " + obj);
} catch (NamingException ne) {
ic.rebind(name, value);
out.println("not found: " + name + ", rebind: " + value);
}
} catch (NamingException e) {
out.println(e);
}
}

@PreDestroy
private void preDestroy() {
try {
InitialContext ic = new InitialContext();
ic.unbind(name);
System.out.println("unbound " + name);
} catch (NamingException e) {
System.out.println("failed to unbind " + name);
}
}

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
}
Compile TestServlet.java, package it in test.war:WEB-INF/classes/test/TestServlet.class, cp it to $GLASSFISH_HOME/domains/domain1/autodeploy/, and run it at http://localhost:8080/test/

Result from the first run:
not found: java:global/env/foo, rebind: FOO
Result from the subsequent run:
found java:global/env/foo : FOO
You can also verify the global JNDI resource with GlassFish asadmin command:
$ asadmin list-jndi-entries --context java:global/env
foo: java.lang.String
This resource is available server-wide to all deployed apps. It will be removed after test.war is undeployed thanks to TestServlet.preDestroy. To retain it beyond the life of test.war, do not unbind it in TestServlet.preDestroy.

10/22/2010

glassfish-application.xml and sun-application.xml examples

The following glassfish-application.xml and sun-application.xml have the same content. glassfish-application.xml (the new name for sun-application.xml) is the file for specifying additional application (EAR) configuration in GlassFish 3.1.

sun-application.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-application PUBLIC "-//Sun Microsystems, Inc.//DTD GlassFish Application Server 3.0 Java EE Application 6.0//EN" "http://www.sun.com/software/appserver/dtds/sun-application_6_0-0.dtd">
<sun-application>
<web>
<web-uri>hello.war</web-uri>
<context-root>hi</context-root>
</web>
<security-role-mapping>
<role-name>admin</role-name>
<principal-name>js</principal-name>
<group-name>manager</group-name>
</security-role-mapping>
</sun-application>
glassfish-application.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-application PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Java EE Application 6.0//EN" "http://glassfish.org/dtds/glassfish-application_6_0-1.dtd">
<glassfish-application>
<web>
<web-uri>hello.war</web-uri>
<context-root>hi</context-root>
</web>
<security-role-mapping>
<role-name>admin</role-name>
<principal-name>js</principal-name>
<group-name>manager</group-name>
</security-role-mapping>
</glassfish-application>

glassfish-application-client.xml and sun-application-client.xml examples

The following glassfish-application-client.xml and sun-application-client.xml have the same content. glassfish-application-client.xml (the new name for sun-application-client.xml) is the file for specifying additional application client (application-client.jar) configuration in GlassFish 3.1.

sun-application-client.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-application-client PUBLIC "-//Sun Microsystems, Inc.//DTD GlassFish Application Server 3.0 Application Client 6.0//EN" "http://www.sun.com/software/appserver/dtds/sun-application-client_6_0-0.dtd">
<sun-application-client>
<ejb-ref>
<ejb-ref-name>ejb/BarBean</ejb-ref-name>
<jndi-name>java:global/app-name/module-name/FooBean!com.test.xxx.FooRemote</jndi-name>
</ejb-ref>

<resource-ref>
<res-ref-name>queueConnectionFactory</res-ref-name>
<jndi-name>jms/QueueConnectionFactory</jndi-name>
<default-resource-principal>
<name>guest</name>
<password>guest</password>
</default-resource-principal>
</resource-ref>
</sun-application-client>
glassfish-application-client.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-application-client PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Java EE Application Client 6.0//EN" "http://glassfish.org/dtds/glassfish-application-client_6_0-1.dtd">
<glassfish-application-client>
<ejb-ref>
<ejb-ref-name>ejb/BarBean</ejb-ref-name>
<jndi-name>java:global/app-name/module-name/FooBean!com.test.xxx.FooRemote</jndi-name>
</ejb-ref>

<resource-ref>
<res-ref-name>queueConnectionFactory</res-ref-name>
<jndi-name>jms/QueueConnectionFactory</jndi-name>
<default-resource-principal>
<name>guest</name>
<password>guest</password>
</default-resource-principal>
</resource-ref>
</glassfish-application-client>

glassfish-web.xml and sun-web.xml examples

The following glassfish-web.xml and sun-web.xml have the same content. glassfish-web.xml (the new name for sun-web.xml) is the file for specifying additional web app (WAR) configuration in GlassFish 3.1.

sun-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD GlassFish Application Server 3.0 Servlet 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_3_0-0.dtd">
<sun-web-app>
<context-root>different-name</context-root>

<security-role-mapping>
<role-name>admin</role-name>
<principal-name>js</principal-name>
<group-name>manager</group-name>
</security-role-mapping>

<ejb-ref>
<ejb-ref-name>ejb/helloBean</ejb-ref-name>
<jndi-name>HelloBean</jndi-name>
</ejb-ref>

<resource-ref>
<res-ref-name>jdbc/dataSource</res-ref-name>
<jndi-name>jdbc/__default</jndi-name>
<default-resource-principal>
<name>guest</name>
<password>guest</password>
</default-resource-principal>
</resource-ref>
<class-loader delegate="true"/>
</sun-web-app>
glassfish-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
<context-root>different-name</context-root>

<security-role-mapping>
<role-name>admin</role-name>
<principal-name>js</principal-name>
<group-name>manager</group-name>
</security-role-mapping>

<ejb-ref>
<ejb-ref-name>ejb/helloBean</ejb-ref-name>
<jndi-name>HelloBean</jndi-name>
</ejb-ref>

<resource-ref>
<res-ref-name>jdbc/dataSource</res-ref-name>
<jndi-name>jdbc/__default</jndi-name>
<default-resource-principal>
<name>myname</name>
<password>password</password>
</default-resource-principal>
</resource-ref>
<class-loader delegate="true"/>
</glassfish-web-app>

glassfish-ejb-jar.xml and sun-ejb-jar.xml examples

The following glassfish-ejb-jar.xml and sun-ejb-jar.xml have the same content. glassfish-ejb-jar.xml (the new name for sun-ejb-jar.xml) is the file for specifying additional EJB jar configuration in GlassFish 3.1.

sun-ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 9.0 EJB 3.0//EN" "http://www.sun.com/software/appserver/dtds/sun-ejb-jar_3_0-0.dtd">
<sun-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>FooBean</ejb-name>
<jndi-name>FooBean</jndi-name>
<ejb-ref>
<ejb-ref-name>ejb/BarBean</ejb-ref-name>
<jndi-name>java:global/app-name/module-name/FooBean!com.test.xxx.FooRemote</jndi-name>
</ejb-ref>
<ejb-ref>
<ejb-ref-name>ejb/BuzBean</ejb-ref-name>
<jndi-name>corbaname:iiop:otherserver:3700#BuzBean</jndi-name>
</ejb-ref>
<resource-ref>
<res-ref-name>jdbc/employee</res-ref-name>
<jndi-name>jdbc/__default</jndi-name>
</resource-ref>
</ejb>
</enterprise-beans>
</sun-ejb-jar>
glassfish-ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-ejb-jar PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 EJB 3.1//EN" "http://glassfish.org/dtds/glassfish-ejb-jar_3_1-1.dtd">
<glassfish-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>FooBean</ejb-name>
<jndi-name>FooBean</jndi-name>
<ejb-ref>
<ejb-ref-name>ejb/BarBean</ejb-ref-name>
<jndi-name>java:global/app-name/module-name/FooBean!com.test.xxx.FooRemote</jndi-name>
</ejb-ref>
<ejb-ref>
<ejb-ref-name>ejb/BuzBean</ejb-ref-name>
<jndi-name>corbaname:iiop:otherserver:3700#BuzBean</jndi-name>
</ejb-ref>
<resource-ref>
<res-ref-name>jdbc/employee</res-ref-name>
<jndi-name>jdbc/__default</jndi-name>
</resource-ref>
</ejb>
</enterprise-beans>
</glassfish-ejb-jar>

9/28/2010

java -agentlib:jdwp for attaching debugger

In Java SE 5 or newer, one can use a new standard JVM option to attach a remote debugger. For example,

-agentlib:jdwp=transport=dt_socket,address=localhost:9009,server=y,suspend=y
In Sun JDK 1.4 or earlier, one still uses this old options:
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009
An additional benefits of using the new -agentlib args is, it doesn't contain any whitespace, so you don't need to worry if you need to quote it or not. But if you do want to use the old flags, be careful about when to quote the value and when to not quote.

When using it directly in java command (Windows or Unix), do not quote it:

java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009 test.Main
java "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009" test.Main

When specifying it as an environment variable on Linux, or Unix, quote it:

export VMARGS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009"
export VMARGS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009

When specifying it as an environment variable on Windows, do not quote it:

set VMARGS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009
set VMARGS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009"

The following is the usage from running this java command:
$ java -agentlib:jdwp=help

Java Debugger JDWP Agent Library
--------------------------------

(see http://java.sun.com/products/jpda for more information)

jdwp usage: java -agentlib:jdwp=[help]|[<option>=<value>, ...]

Option Name and Value Description Default
--------------------- ----------- -------
suspend=y|n wait on startup? y
transport=<name> transport spec none
address=<listen/attach address> transport spec ""
server=y|n listen for debugger? n
launch=<command line> run debugger on event none
onthrow=<exception name> debug on throw none
onuncaught=y|n debug on any uncaught? n
timeout=<timeout value> for listen/attach in milliseconds n
mutf8=y|n output modified utf-8 n
quiet=y|n control over terminal messages n

Obsolete Options
----------------
strict=y|n
stdalloc=y|n

Examples
--------
- Using sockets connect to a debugger at a specific address:
java -agentlib:jdwp=transport=dt_socket,address=localhost:8000 ...
- Using sockets listen for a debugger to attach:
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y ...

Notes
-----
- A timeout value of 0 (the default) is no timeout.

Warnings
--------
- The older -Xrunjdwp interface can still be used, but will be removed in
a future release, for example:
java -Xdebug -Xrunjdwp:[help]|[<option>=<value>, ...]

8/25/2010

My subversion svn notes

Non-comment lines of $HOME/.subversion/config (%APPDATA%\Subversion\config on Windows):

global-ignores = *.ipr *.iml *.iws .project .classpath .settings .*.swp out.txt todo.txt *.save *.my *.class *.org #*# .*.rej *.rej .*~ *~ .#* .DS_Store
enable-auto-props = yes
*.java = svn:eol-style=native;svn:keywords=Date Author Id Revision HeadURL
To view the usage of a subcommand, for example, propedit:
svn help propedit
To view the details of a commit with rev number, including changed files and log messages (Without -v option, changed files are omitted in the output):
svn log -v -c 1234 Foo.java

Date and time in log are in local timezone of the current client. For example,
r526 | user1 | 2012-02-25 09:32:00 -0500 (Sat, 25 Feb 2012) | 3 lines

-0500 indicates the timezone EST (US/Eastern Standard Time), 5 hours behind UTC

r536 | user2 | 2012-04-26 13:44:59 -0400 (Thu, 26 Apr 2012) | 2 lines
-0400 indicates the timezone EDT (US/Eastern Daylight Time), 4 hours behind UTC

In contrast, git log displays the date in the committer's timezone with offset to UTC.  git log --date=local displays it in the git client local time without showing any timezone value.

To get the date and time in UTC (the ending Z is for UTC):
svn propget --revprop -r 537 svn:date
2012-05-01T02:08:21.396811Z


# shows all changes made by rev 1234 compared to previous rev
svn diff -c 1234 Foo.java To get, or set end-of-line property (One of 'native', 'LF', 'CR', 'CRLF'):
svn pg      svn:eol-style Utils.java
svn pget    svn:eol-style Utils.java
svn propget svn:eol-style Utils.java

svn propset svn:eol-style LF Utils.java
svn pset    svn:eol-style LF Utils.java
svn ps      svn:eol-style LF Utils.java
To view the svn:externals or svn:ignore for the current directory:
svn propget svn:externals .
svn pg svn:externals .
svn pg svn:ignore .
To delete the svn:ignore for the current directory:
svn pd svn:ignore .
To edit the externals for the current directory:
svn --editor-cmd vim propedit svn:externals .
svn --editor-cmd vim pe svn:externals .
If any one of these environment variables SVN_EDITOR, VISUAL or EDITOR is set, you can omit the "--editor-cmd vim" part:
setenv EDITOR vim (in csh/tcsh)
export EDITOR=vim (in bash)
svn pe svn:externals .
To compare two different versions of a file:
svn diff -r r99:r100 build.xml
svn diff -r 99:100 build.xml
svn diff -c 100
It's easier to copy r99 r100 from svn log output, so I prefer the first form. The option "-c 100" is the same as "-r 99:100", and shows all changes made by rev 100.

To skip any differences in deleted files:
svn diff --no-diff-deleted
svn diff will not included newly added files, or files that are moved to target directory. To view colored diff with vim:
svn diff | view -
I also have a shell script wrapper (diffv) for the above:
#! /bin/sh
svn diff -x -wub $@ | view -
# use -x -wub to ignore whitespace changes
To get detailed info like URL, repository URL, revision, last changed date, etc:
svn info .
To relocate a loca workspace after the server repository has moved:
cd root-of-current-workspace
svn switch --relocate http://old http://new .
To revert all modified files under current directory:
svn revert *
svn revert util/*
Note that "svn revert ." does not revert changes in sub-directories. For recursive revert,
svn revert --depth infinity util
svn revert -R util
svn revert --recursive util
When newly-added files (with svn add or svn move) are reverted, they will stay in the file system, even though removed from version control. This can also happen when you apply someone else's patch to your local workspace and then revert them. As a consequence, when building the workspace these left-over files are still included in project artifacts, causing mysterious errors.

To downgrade svn client version:
cd {to the directory with local changes};
python $HOME/bin/change-svn-wc-format.py . 1.5
The above command will downgrade the working copy format to svn 1.5. This is to fix the annoying svn version error:
svn: This client is too old to work with working copy '...'.  You need
to get a newer Subversion client, or to downgrade this working copy.
See http://subversion.tigris.org/faq.html#working-copy-format-change
for details.
To apply a patch (.diff file), ignoring all parent directories in diff index:
cd {working directory containing the target file}
patch -i $HOME/Desktop/a.diff
svn patch $HOME/Desktop/a.diff .  # for subversion client 1.7 or later

To apply a patch (.diff file), preserving all parent directories in diff index:
cd {parent directory of the whole directory structure in diff index}
patch -p0 -i /tmp/a.diff
This is useful when target files do not fall under the same directory. -p means how many levels of directories to prune. patch is a Unix (not svn) command, usually resolves to /usr/bin/patch.

If you've copied the diff (e.g., from email received from coworker), you can let patch command take input from console:
patch
patch -p0
After pasting diff content, Ctrl-D to end.

If a patch contains moved files between different directories, /usr/bin/patch or svn patch doesn't know how to create the destination files. You will need to 'svn copy' these files to their destinations before running patch.


To make directories and parent directories together (the equivalent of OS command mkdir -p),
svn mkdir --parents com/a/b/c
To move a single file to another directory (--parents option will make any intermediate directories if not already exist):
svn mv --parents Utils.java ../common/a/b/c/Utils.java
# note that both SRC and DST must be file.  If DST is a ../common/a/b/c,
# c will actually be treated as a file to hold the content of Utils.java

To move MULTIPLE files to another directory (--parents option will make any intermediate directories if not already exist):
cd <source directory>
svn mv --parents *.java ../common/a/b/c

To move the whole directory to another location:
svn mv impl ../common
After this command, if you do a "svn st", you will see all the deleted directories and files are listed, but under the DST directory, only the top-level directory (../common/impl) is listed as Added. Even in the comment editor window of "svn ci", still only the top-level directory listed as Added. How about files under the top-level directory, have they been copied over? But have no fear, everything from the SRC along with revision history were indeed moved to the destination. "svn ci" will commit these changes to the repository.

With TortoiseSVN (Windows only), all these move operations can be done in Windows Explorer: right-click the SRC, hold and drag it to the DST, and confirm your operation in the pop-up menu.

To remove a directory (there is no svn rmdir command),
svn rm a/
To merge some changes from trunk to branch, branch to trunk, or branch 1 to branch 2:
cd <source directory of changes>
svn log . | head -25
copy and paste the start rev and end rev, e.g., r123:r129
svn info; and copy the source URL, e.g., http://host:port/my-project-svn/a/b
cd <dest directory>
svn merge --dry-run -r r123:r129 http://host:port/my-project-svn/a/b
To make real changes, omit --dry-run option.

To merge ALL changes in Foo.java from trunk to branch:
cd <source directory of changes>
svn info Foo.java; and copy the complete source URL, e.g., http://host:port/.../Foo.java
cd <dest directory>
svn merge http://host:port/.../Foo.java ./Foo.java

To merge rev 567 in Foo.java from trunk to branch:
cd <source directory of changes>
svn info Foo.java; and copy the complete source URL, e.g., http://host:port/.../Foo.java
cd <dest directory>
svn merge -c 567 http://host:port/.../Foo.java ./Foo.java

To quickly undo or revert a bad commit (rev 99) with a reverse merge (notice the negative rev number is used. It must be a number; args like -c -r99 is rejected.):
svn merge -c -99 .
svn ci -m 'revert my changes in rev 99.'

To create a changelist with a list of files:

svn cl user.changelist src/main/java/com/xxx/User.java src/main/java/com/xxx/Person.java


To create a changelist from a file created by svn st command, need to first cut off the leading characters like "M", "A", deleting all lines starting with "?", and then use --targets option (in plural):

svn cl user.changelist --targets /tmp/d


To diff only those files associated with user.changelist:

svn diff --cl user.changelist


To remove a changelist, you don't remove the changelist itself, instead remove the association of all files. You can either list the files in command line, or include these lines in a file. After that, the changelist will simply disappear:

svn cl --remove src/main/java/com/xxx/User.java src/main/java/com/xxx/Person.java
svn cl --remove --targets /tmp/d


To revert changes in a changelist, you need to specify both the changelist and path:

svn revert --cl user.changelist -R user/src/main

Changelist is helpful when working on multiple tasks in the same workspace. You cannot check in changes for task1 due to some other dependency, but would like to start working on task2. But be very careful when juggling with multiple tasks in the same workspace, as some java files will stay in the file systems even after they are reverted and be built into the artifacts.

If you have locally modified a file, and an incoming update will delete or move the same file, its status after the update will be Tree Conflict (delete upon update). What I will do is to first back it up, revert local changes, and then resolve this conflict

cp Method.java $tmp/Method.java
svn revert Method.java
svn resolved Method.java

If the file (Method.java) is moved to some other directory, apply your local changes to Method.java in its new directory. If you run "svn resolved" with local modifications, this file will show up as newly added file that contains your local changes, which is probably not what you want.

To view svn status, excluding untracked local-only files (filtering out lines like "?    config/server.xml.bak"):
svn st | grep -v "^?"
To view locally modified files only:
svn st | grep "^M"

7/16/2010

String.contains and String.indexOf

To check if a string contains a substring, I usually use:

if(s.indexOf(sub) >= 0)
For JDK 5 or later, a contains method can also be used, which seems to be a little more readable:
if(s.contains(sub))
The signature of contains method is:
public boolean contains(java.lang.CharSequence s);
Note that CharSequence is a super-interface of String, StringBuffer, StringBuilder, java.nio.CharBuffer, javax.swing.text.Segment. So you can pass any of the 5 types to contains method.

The current implementation of contains method just convert the param to String and calls indexOf.

7/14/2010

Java debug options in ant build.xml

To add debug options to java (Sun's JDK) command line:

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9009
If the java process runs and terminates itself, change suspend=y, so you can have a chance to attach the remote debugger.

To add the same debug options to ant build.xml java task:
<target name="run">
 <java fork="on"
       failonerror="true"
       classpath="xxx"
       classname="xxx">
     <jvmarg line="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=9009" />
     <arg line="--arg1 arg2 --arg3 arg4"/>
  </java>
</target>
Or, with the newer -agentlib option:
<target name="test" depends="compile">
    <junit printsummary="yes" haltonerror="no" haltonfailure="no" fork="true">
      <formatter type="plain" usefile="false"/>
      <formatter type="xml"/>
      <test name="FooBeanTest"
          todir="xxx"/>
      <jvmarg line="-agentlib:jdwp=transport=dt_socket,address=localhost:9009,server=y,suspend=y" />
      <classpath>
        <path refid="test.compile.classpath"/>
        <path refid="javaee.classpath"/>
        <pathelement path="${build.dir}"/>
        <pathelement path="${junit.jar}"/>
      </classpath>
    </junit>
  </target>
Note: <arg> and <jvmarg> take either line or value attribute, but value attributes are treated as one single argument that may contain spaces. So in our case line attribute is used to specify multiple arguments separated by spaces. If value attribute was used instead of line attribute, the debug options will not take effect.
The same element can also be used on ant testng task to debug test run:
<testng mode="testng" dumpCommand="true" workingDir="." failureProperty="tests.failed" outputdir="results">
    <classpath>
        <fileset dir="lib">
            <include name="*.jar" />
        </fileset>
    </classpath>
    <xmlfileset dir="." includes="testng-suite.xml" />
    <jvmarg line="-agentlib:jdwp=transport=dt_socket,address=localhost:9009,server=y,suspend=y" />
</testng>

7/04/2010

Some basic steps using Derby 10.5

The following is some basic steps of using derby bundled with GlassFish.

1. start derby database server:

$GLASSFISH_HOME/bin/asadmin start-database
or start it on a non-default port number:

$GLASSFISH_HOME/bin/asadmin start-database --dbport 1528
2. make sure environment variable DERBY_HOME is set:
$ echo $DERBY_HOME
/Users/root/v3/glassfish/../javadb
3. start derby ij (or ij.bat), connect to derby server:
$ $DERBY_HOME/bin/ij
ij version 10.5
ij> connect 'jdbc:derby://localhost:1527/sun-appserv-samples;user=APP;password=APP;create=true';

ij> select count(*) COUNT from sys.systables;
COUNT
-----------
24

1 row selected

ij> select OWNERID from EJB__TIMER__TBL;
ij> delete from EJB__TIMER__TBL;

ij> show tables;
TABLE_SCHEM |TABLE_NAME |REMARKS
-------------------------------------------
SYS |SYSALIASES |
SYS |SYSCHECKS |
SYS |SYSCOLPERMS |
SYS |SYSCOLUMNS |
SYS |SYSCONGLOMERATES |
SYS |SYSCONSTRAINTS |
SYS |SYSDEPENDS |
SYS |SYSFILES |
SYS |SYSFOREIGNKEYS |
SYS |SYSKEYS |
SYS |SYSROLES |
SYS |SYSROUTINEPERMS |
SYS |SYSSCHEMAS |
SYS |SYSSTATEMENTS |
SYS |SYSSTATISTICS |
SYS |SYSTABLEPERMS |
SYS |SYSTABLES |
SYS |SYSTRIGGERS |
SYS |SYSVIEWS |
SYSIBM |SYSDUMMY1 |
APP |LEAGUE |
APP |PLAYER |
APP |TEAM |
APP |TEAMPLAYER |

24 rows selected

ij> describe player;
COLUMN_NAME |TYPE_NAME|DEC&|NUM&|COLUM&|COLUMN_DEF|CHAR_OCTE&|IS_NULL&
------------------------------------------------------------------------------
PLAYER_ID |VARCHAR |NULL|NULL|255 |NULL |510 |NO
NAME |VARCHAR |NULL|NULL|255 |NULL |510 |YES
POSITION |VARCHAR |NULL|NULL|255 |NULL |510 |YES
SALARY |DOUBLE |NULL|2 |52 |NULL |NULL |NO

4 rows selected

ij> CREATE TABLE FOO (FOO_ID BIGINT, FOO_NAME VARCHAR(25), DESCRIPTION VARCHAR(64));
0 rows inserted/updated/deleted

ij> insert into foo values(1, 'foo1', 'foo1, that is.');
1 row inserted/updated/deleted

ij> select * from foo;
FOO_ID |FOO_NAME |DESCRIPTION
---------------------------------------------------------------------------------------------------------------
1 |foo1 |foo1, that is.

1 row selected

ij> update foo set foo_name='foo1 new name' where foo_id=1;
1 row inserted/updated/deleted

ij> delete from foo where foo_id=1;
1 row inserted/updated/deleted

ij> select count(*) count from foo;
COUNT
-----------
0

1 row selected

ij> drop table foo;
0 rows inserted/updated/deleted

To start ij and connect to a database in single step:
java -classpath $GLASSFISH_HOME/../javadb/lib/derbyrun.jar -Dij.connection.sample="jdbc:derby://localhost:1527/sun-appserv-samples;user=APP;password=APP;create=true" org.apache.derby.tools.ij
When starting derby with GlassFish "asadmin start-database" command, the default host is localhost, the default port number is 1527, and the default derby system home is set to $GLASSFISH_HOME/databases. This is also the directory where derby.log and database files will be stored. To overwrite the defaults with the following options:
asadmin start-database --dbhost dell-01 --dbport 3333 --terse=true --dbhome $HOME/derby

A sample web.xml (servlet 3.0) with ejb-ref and ejb-local-ref

This is a sample web.xml based on servlet 3.0 (part of Java EE 6).

<web-app 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_3_0.xsd"
version="3.0">

<servlet>
<display-name>Servlet1</display-name>
<servlet-name>Servlet1</servlet-name>
<servlet-class>test.Servlet1</servlet-class>
<init-param>
<param-name>sleep-time-in-seconds</param-name>
<param-value>10</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/Servlet1</url-pattern>
</servlet-mapping>

<ejb-ref>
<ejb-ref-name>ejb/fooremote</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<remote>test.FooRemoteIF</remote>
</ejb-ref>

<ejb-local-ref>
<ejb-ref-name>ejb/foo</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<local>test.FooIF</local>
<ejb-link>FooBean</ejb-link>
</ejb-local-ref>
</web-app>

6/25/2010

GlassFish rotate-log

GlassFish has a command to rotate or reset server.log file:

$GLASSFISH_HOME/bin/asadmin rotate-log
After running this command the logs directory looks like this:
/glassfish-3.1/glassfish/domains/domain1/logs > ll
total 200
-rw-r--r-- 1 root 0 Jun 25 10:11 server.log
-rw-r--r-- 1 root 10780 Jun 25 10:11 server.log_2010-06-25T10-09-48
-rw-r--r-- 1 root 16384 Jun 25 10:10 jvm.log
drwxr-xr-x 3 root 102 Jun 25 10:02 server/
This command renames server.log file to a timestamped backup file, and leaves the current server.log file empty. I usually run this command before starting a test run to make sure the current server.log only contains logs from this test run.

For convenience, I also have an alias for it (in tcsh syntax):
alias rlog '$GLASSFISH_HOME/bin/asadmin rotate-log'
For more details, see official docs, or view the online help:
$GLASSFISH_HOME/bin/asadmin rotate-log --help

5/24/2010

Double click in NetBeans 6.5 & 6.8 editor

NetBeans 6.x editors handle double-click differently from other applications. In firefox, thunderbird, eclipse, or even terminal, I can doulble-click anywhere in a word to select the whole word, keep the mouse button pressed, and slide forward to expand the selection.

In NetBeans, doing the same will drag the selected word, just like moving the word elsewhere. While it seems cool to be able to drag-and-drop, I don't see any practical value. In java editor, I always avoid drag-and-drop anything, since the selected element often lands in the wrong place when your mouse/pad reaches its end.

Update: It has been fixed in NetBeans 7.1 according to this bug. I installed 7.1 beta just to get this fix.

5/06/2010

Example of EJB 3.1 Stateful Session Bean and Servlet

Stateful session bean class:

package test;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.ejb.Stateful;
import javax.ejb.StatefulTimeout;

@Stateful
@StatefulTimeout(unit = TimeUnit.MINUTES, value = 30)
public class StatefulTestBean {
private static final Logger logger = Logger.getLogger("test");

private String clientInfo;

@SuppressWarnings("unused")
@PrePassivate
private void prePassivate() {
logger.info("In PrePassivate method");
}

@SuppressWarnings("unused")
@PostActivate
private void postActivate() {
logger.info("In PostActivate method");
}

public String getBeanInfo() {
return this.toString();
}

public String getClientInfo() {
return clientInfo;
}

public void setClientInfo(String clientInfo) {
this.clientInfo = clientInfo;
}
}
Servlet class:
package test;
import java.io.IOException;
import java.io.PrintWriter;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet(urlPatterns = "/TestServlet")
public class TestServlet extends HttpServlet {
private static final String STATEFUL_TEST_BEAN_KEY = "STATEFUL_TEST_BEAN_KEY";

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter writer = response.getWriter();
StatefulTestBean statefulTestBean = getStatefulTestBean(request);
writer.println("clientInfo: " + statefulTestBean.getClientInfo());
writer.println("beanInfo: " + statefulTestBean.getBeanInfo());
}

private StatefulTestBean getStatefulTestBean(HttpServletRequest request)
throws ServletException {
HttpSession httpSession = request.getSession(true);
StatefulTestBean statefulTestBean = (StatefulTestBean)
httpSession.getAttribute(STATEFUL_TEST_BEAN_KEY);
if (statefulTestBean == null) {
try {
InitialContext ic = new InitialContext();
statefulTestBean = (StatefulTestBean) ic.lookup("java:module/StatefulTestBean");
httpSession.setAttribute(STATEFUL_TEST_BEAN_KEY, statefulTestBean);

String userAgent = request.getHeader("User-Agent");
String userName = request.getParameter("userName");
statefulTestBean.setClientInfo(userAgent + ", " + userName);
} catch (NamingException e) {
throw new ServletException(e);
}
}
return statefulTestBean;
}
}
To compile the above 2 classes:
javac -cp "$GLASSFISH_HOME/modules/*" TestServlet.java StatefulTestBean.java
Copy *.class files under a WEB-INF directory, and package the above 2 classes into a WAR file, stateful.war, that contains the following:
WEB-INF/classes/test/StatefulTestBean.class
WEB-INF/classes/test/TestServlet.class
To deploy it, copy stateful.war to appserver autodeploy directory.

I did the following to test the stateful session behavior:

1, open the following url in firefox:
http://localhost:8080/stateful/TestServlet?userName=nobody

will get the following response:
clientInfo: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3, nobody
beanInfo: test._StatefulTestBean_Serializable@3af78ba5

2, In the same firefox window, enter url without userName:
http://localhost:8080/stateful/TestServlet

will still get the same response, since the second request share the same http session and stateful session bean with the first request.

3, Open a new firefox window and repeat step 2, will still get the same response, since the http session is tied to the firefox browser.

4, Open a chrome window and repeat step 2, will get a different response due to a new http session and new stateful session bean instance were created.

Since both http sessin and stateful session bean are used to manage user session, their timeout value should be in kept in sync.

This app is programmed to JavaEE 6 and EJB 3.1, using some new features such as @StatefulTimeout and a portable module-scope jndi name. I've tested with GlassFish 3.0, and it should also work, without any modification, in any JavaEE-6-compliant appserver. To run with JavaEE 5 appserver, some minor modifications are needed.

The stateful bean reference is looked up, instead of using field injection. This is necessary to keep it thread safe and avoid being accessed by different users.

4/26/2010

restart-domain in GlassFish

Restarting an appserver is a common operation after manually editing server config, or adding new library jars, or just to be safe. I hava an alias to restart GlassFish, and all it does is: stop domain, sleep 3 seconds, and start domain.

In GlassFish v3, this can be done with a single asadmin command: restart-domain.

First, I tried restart-domain while the server is not running, which is equivalent to start-domain command:

./asadmin restart-domain
Server is not running, will attempt to start it...
Waiting for DAS to start .....................
Started domain: domain1
Domain location: /v3/glassfish/domains/domain1
Log file: /v3/glassfish/domains/domain1/logs/server.log
Admin port for the domain: 4848
Command restart-domain executed successfully.
Then, try it while the server is running. This command takes a few seconds to finish since it involves stopping the domain first, possibly pausing a bit, and then starting the domain:
./asadmin restart-domain
Successfully restarted the domain
Command restart-domain executed successfully.

4/21/2010

Example of EJB Interceptor class

EJB 3.x interceptor class can contain either EJB lifecycle callback methods (e.g., @PostConstruct, @PreDestroy, @PrePassivate, @PostActivate), or business interceptor methods (i.e., @AroundInvoke), or any combination of these methods. The following is a simple example of EJB interceptor class:

package test;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.PrePassivate;
import javax.ejb.PostActivate;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class Interceptor1 {
@PostConstruct
protected void myPostConstruct(InvocationContext ctx) {

}

@PreDestroy
protected void myPreDestroy(InvocationContext ctx) {

}

@PostActivate
private void postActivate(InvocationContext ctx) {

}

@PrePassivate
private void prePassivate(InvocationContext ctx) {

}

@AroundInvoke
public Object businessIntercept(InvocationContext ctx)
throws Exception {
Object result = null;
try {
// PreInvoke: do something before handing control to the next in chain
result = ctx.proceed();
return result;
} finally {
// PostInvoke: do something (cleanup, etc) after the main processing is done
}
}
}
The interceptor class is attached to EJB bean class (or business method in EJB bean class) with @Interceptors annotation, or specified in ejb-jar.xml.

4/20/2010

@Resource.lookup and classpath

JavaEE 6 added a new lookup attribute to @javax.annotation.Resource. So one can inject resources like this:

@Resource(lookup = "java:module/env/rate")
private String rate;
Compile the classes:
javac -cp "$GLASSFISH_HOME/modules/*" *Bean.java

EmailBean.java:18: cannot find symbol
symbol : method lookup()
location: @interface javax.annotation.Resource
@Resource(lookup = "java:module/env/rate")
^
1 error
In GlassFish, all JavaEE classes are in various jars under $GLASSFISH_HOME/modules and its subdirectories. It failed because version 1.0 of javax.annotation.Resource class (and other javax.annotation.* classes) are included in Java SE 6. The use of lookup attribute requires version 1.1 of Common Annotation jar, which is included in JavaEE 6.

To fix it, simply add a -Djava.endorsed.dirs sysprop to override those classes in Java SE 6:
javac -cp "$GLASSFISH_HOME/modules/*" -Djava.endorsed.dirs=$GLASSFISH_HOME/modules/endorsed  *Bean.java
In Eclipse IDE, you just need to put $GLASSFISH_HOME/modules/endorsed/javax.annotation.jar before JRE System Library when configuring Java Build Path.

This should also work with any compliant appserver, though actual jar name and location may vary. The same -Djava.endorsed.dirs sysprop should also be used when running the app.

4/19/2010

2 @ManagedBean in JavaEE 6

There are 2 @ManagedBean annotations in JavaEE 6:

javax.annotation.ManagedBean (javadoc)
javax.faces.bean.ManagedBean (javadoc)

Looks like we are running out of names.

A third type of managed bean is CDI managed bean, introduced in Java EE 6.

4/17/2010

Env-entry enhancement in JavaEE 6

JavaEE 6 adds 2 additional types to the list of env-entry types: java.lang.Class and any enum type. So there are now 11 portable env-entry types that must be supported by all compliant application servers:

       java.lang.Class
any enum type
java.lang.Boolean
java.lang.Byte
java.lang.Character
java.lang.String
java.lang.Short
java.lang.Integer
java.lang.Long
java.lang.Float
java.lang.Double
For example, to use java.lang.Class and enum env-entry-type in a web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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_3_0.xsd">
<env-entry>
<description>example of java.lang.Class env-entry-type</description>
<env-entry-name>java:app/env/myImplClass</env-entry-name>
<env-entry-type>java.lang.Class</env-entry-type>
<env-entry-value>test.MyServiceProvider</env-entry-value>
</env-entry>

<env-entry>
<description>example of enum env-entry-type</description>
<env-entry-name>java:app/env/day</env-entry-name>
<env-entry-type>com.examplexxx.util.Day</env-entry-type>
<env-entry-value>SUNDAY</env-entry-value>
</env-entry>
</web-app>
To inject the above 2 env-entry into fields in a servlet, or EJB bean class, or any other components in the EAR:
@Resource(lookup="java:app/env/myImplClass")
@SuppressWarnings("unchecked")
private Class myImplClass;

@Resource(lookup="java:app/env/day")
private com.examplexxx.util.Day day;
For java.lang.Class type, only the raw type, not the generic type, can be declared as the field type, hence @SuppressWarnings("unchecked").

4/16/2010

DataSourceDefinition Examples in JavaEE 6

The usual way to create a DataSource is through the appserver's administration, CLI or GUI tool. Then applications can declare resource-ref mapped to the DataSource. This involves both application developer and server administrator, and sometimes can be too much for simple apps. JavaEE 6 added 2 annotations to ease this task: javax.annotation.sql.DataSourceDefinition and javax.annotation.sql.DataSourceDefinitions.

With the 2 annotations, applications can take control of DataSource creation, and choose to expose the DataSource in various scopes: component-, module-, application, and global-scope. For simple apps, all this can be done without any server configuration or XML descriptors.

Example 1: a servlet declares how to create a derby DataSource, and injects a reference to this DataSource into a field. This DataSource is scoped to the whole module (the entire webapp).

@DataSourceDefinition(name="java:module/env/inventory",
className="org.apache.derby.jdbc.ClientDataSource",
portNumber=1527,
serverName="localhost",
databaseName="inventory",
user="user1",
password="password1")
@WebServlet(urlPatterns = "/InventoryServlet")
public class InventoryServlet extends HttpServlet {
@Resource(lookup="java:module/env/inventory")
private DataSource inventoryds;
Example 2: a Stateless session bean creates an application-scope Oracle DataSource, and injects its resource-ref. This DataSource is visible to the entire application (EAR).
@DataSourceDefinition(name="java:app/env/inventory",
className="oracle.jdbc.pool.OracleDataSource",
portNumber=1521,
serverName="localhost",
databaseName="inventory",
user="user1",
password="password1",
properties={"driverType=thin"}
)
@Stateless
public class InventoryBean {
@Resource(lookup="java:app/env/inventory")
private DataSource inventoryds;
For more details, see JavaEE 6 javadoc page for @DataSourceDefinition

4/14/2010

Message Driven Bean Example with Servlet Client

This is a simple example of JMS Queue message-driven bean (MDB), with a servlet client. MessageServlet sends a text message to the queue that the MDB is bound to, the MDB then consumes the incoming message. This example requires EJB 3.1 and Servlet 3.0, both are in Java EE 6.

First, the MDB class:

package test;

import java.util.logging.Logger;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;

@MessageDriven(mappedName="testQueue2",
activationConfig = { @ActivationConfigProperty(
propertyName="destinationType", propertyValue="javax.jms.Queue")})
public class MessageBean implements MessageListener {
Logger logger = Logger.getLogger("test");

public void onMessage(Message msg) {
try {
String name = msg.getStringProperty("name");
logger.info("Received msg " + msg + ", from " + name);
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
}
The servlet class:
package test;

import java.io.IOException;
import javax.annotation.Resource;
import javax.jms.*;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet(urlPatterns = "/MessageServlet")
public class MessageServlet extends HttpServlet {
@Resource(mappedName = "testQueue2")
private Queue queue;

@Resource(mappedName = "jms/QueueConnectionFactory")
private QueueConnectionFactory queueConnectionFactory;

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
QueueConnection queueConnection = null;
try {
queueConnection = queueConnectionFactory.createQueueConnection();
queueConnection.start();
QueueSession queueSession = queueConnection.createQueueSession(false,
Session.AUTO_ACKNOWLEDGE);
QueueSender sender = queueSession.createSender(queue);

TextMessage msg = queueSession.createTextMessage();
msg.setText("A message from MessageServlet");
msg.setStringProperty("name", "MessageServlet");

sender.send(msg);
} catch (JMSException e) {
throw new RuntimeException(e);
} finally {
try {
if (queueConnection != null) {
queueConnection.close();
}
} catch (JMSException e) { //ignore
}
}
}
}
After compiling, package the two classes into a WAR file, say, mdb.war:
WEB-INF/classes/test/MessageBean.class
WEB-INF/classes/test/MessageServlet.class
Create the 2 JMS resources (testQueue2 & jms/QueueConnectionFactory) in the target application server. In GlassFish, you can do it in Admin GUI, or via asadmin CLI:
./asadmin create-jms-resource --restype javax.jms.Queue testQueue2
./asadmin create-jms-resource --restype javax.jms.QueueConnectionFactory jms/QueueConnectionFactory
Deploy mdb.war by copying it to server autodeploy directory.

Enter the URL in browser to invoke the servlet:
http://localhost:8080/mdb/MessageServlet

You will see the following log in server.log as a result of MDB processing:
[#|2010-04-14T17:28:49.900-0400|INFO|glassfishv3.0|test|_ThreadID=31;_ThreadName=Thread-1;|Received msg com.sun.messaging.jms.ra.DirectTextPacket@3ce177de, from MessageServlet|#]

4/08/2010

GlassFish set-web-env-entry

env-entry elements can be used to configure a webapp without recompiling. Usually one still needs to update them in web.xml, and redeploy the WAR for the new config to take effect. I just noticed GlassFish v3 now supports in-place, dynamic updating web env-entry via its powerful asadmin command. I guess it can also be done in GlassFish admin GUI.

1, download a sample webapp, hello.war, from GlassFish web site, to $GLASSFISH_HOME/domains/domain1/autodeploy/ directory:

wget http://glassfish.java.net/downloads/quickstart/hello.war
2, verify hello.war has been successfully deployed:
cd $GLASSFISH_HOME/bin
./asadmin list-components
3,
./asadmin set-web-env-entry --name=contact-email --value=info@examplexxx.com --type=java.lang.String hello
./asadmin set-web-env-entry --name=debug --value=true --type=java.lang.Boolean hello
4, verify the 2 new env-entry elements:
./asadmin list-web-env-entry hello
Reported 2 env-entry settings

contact-email (java.lang.String) = info@examplexxx.com ignoreDescriptorItem=false //(description not specified)
debug (java.lang.Boolean) = true ignoreDescriptorItem=false //(description not specified)
You can also verify them by viewing $GLASSFISH_HOME/domains/domain1/domain.xml:
<application context-root="/hello" location="${com.sun.aas.instanceRootURI}/applications/hello/" name="hello" object-type="user">
<property name="defaultAppName" value="hello" />
<module name="hello">
<engine sniffer="security" />
<engine sniffer="web">
<web-module-config>
<env-entry>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>info@examplexxx.com</env-entry-value>
<env-entry-name>contact-email</env-entry-name>
</env-entry>
<env-entry>
<env-entry-type>java.lang.Boolean</env-entry-type>
<env-entry-value>true</env-entry-value>
<env-entry-name>debug</env-entry-name>
</env-entry>
</web-module-config>
</engine>
</module>
</application>

To learn more about set-web-env-entry subcommand, try one of the following:
./asadmin help set-web-env-entry
./asadmin set-web-env-entry --help
To list and unset env-entry, use the following asadmin commands:

list-web-env-entry
unset-web-env-entry
The same feature also exists for web context-param, via the following commands:

list-web-context-param
set-web-context-param
unset-web-context-param

4/01/2010

EJB 3.1 Timer Simple Example

This is a simple webapp that polls a web site to check if a product is available. It uses a stateless session bean and calendar-based timer.

package test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.util.logging.Logger;

import javax.ejb.Schedule;
import javax.ejb.Stateless;

@Stateless
public class InventoryCheckBean {
private static final String PRODUCT_URL = "http://www.amazon.com/dp/B002BSA298/";
private static final String IN_STOCK = "In Stock";

@SuppressWarnings("unused")
@Schedule(dayOfWeek = "0-5", hour = "0/2", minute = "0/20", timezone = "America/Los_Angeles")
private void checkInventory() {
BufferedReader br = null;
try {
String line = null;
URL url = new URI(PRODUCT_URL).toURL();
br = new BufferedReader(new InputStreamReader(url.openStream()));
while ((line = br.readLine()) != null) {
if(line.indexOf(IN_STOCK) >= 0) {
//email notify
Logger.getLogger("").info(line);
break;
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if(br != null) {
try {
br.close();
} catch (IOException e) { //ignore
}
}
}
}
}
After compiling, simply package test/InventoryCheckBean.class into a WAR file under WEB-INF/classes. No other files are needed.
/tmp > jar tf $owd/inventory.war
META-INF/
META-INF/MANIFEST.MF
WEB-INF/
WEB-INF/classes/
WEB-INF/classes/test/
WEB-INF/classes/test/InventoryCheckBean.class
Deploy this war to any application server that is Java EE 6 compliant. The timer is activated upon successful deployment, and starts to check the specified site at the specified intervals. This is so-called automatic timer.

In the server log file, you will notice these logs:
[#|2010-04-01T10:50:01.644-0400|INFO|glassfishv3.0||_ThreadID=26;_ThreadName=Thread-1;|<span class="availGreen">In Stock.</span><br/ > Ships from and sold by <b>Amazon.com</b>. Gift-wrap available.|#]
Note:
(1) This WAR file contains only an EJB bean class.

(2) There is no business method in this EJB; only 1 private timer method, which is only invoked by the container after deployment. So @SuppressWarnings is used to mute any warnings that this method is never used.

(3) No client action is needed. Right after deployment, the timer is activated and starts the processing specified in the timer method.

(4) timezone is optionals and defaults to that of the hosting machine.

(5) This timer goes off every 20 minutes of every other hour starting from 0 o'clock from Sunday to Friday, and the time is based on America/Los_Angeles timezone.

Update on 3/4/2011, I deployed the same inventory.war to JBoss AS 6.0, by copying it to $JBOSS_HOME/server/default/deploy, it also worked. No need to change anything.

3/17/2010

Simple Asynchronous methods in EJB 3.1

EJB 3.1 introduced a simple mechanism for asynchronous invocation. This post presents such an example with a Singleton session bean and its JUnit test case. 2 styles of asynchronous EJB invocations are used:

(1) fire-and-forget asynchronous methods having void return type.

(2) retrieve-result-later asynchronous methods having Future<?> return type.

package test;

import java.util.concurrent.Future;
import javax.ejb.Asynchronous;
import javax.ejb.AsyncResult;
import javax.ejb.Singleton;

@Singleton
@Asynchronous
public class AsyncBean {
@Asynchronous //this line can be omitted
public void ignoreResult(int a, int b) {
// the client doesn't care what happens here
}

@Asynchronous //this line can be omitted
public Future<Integer> longProcessing(int a, int b) {
return new AsyncResult<Integer>(a * b);
}
}
Since the class is annotated with @Asynchronous, all business methods are asynchronous. The method-level @Asynchronous is therefore redundant, and kept there for illustration purpose.
package test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import junit.framework.TestCase;

public class AsyncBeanTest extends TestCase {
private EJBContainer container;
private Context namingContext;
private AsyncBean asyncBean;

@Override
protected void setUp() throws Exception {
super.setUp();
container = EJBContainer.createEJBContainer();
namingContext = container.getContext();
asyncBean = (AsyncBean) namingContext.lookup("java:global/testApp/AsyncBean");
}

@Override
protected void tearDown() throws Exception {
super.tearDown();
container.close();
}

public final void testIgnoreResult() {
asyncBean.ignoreResult(0, 0); // fire and forget
System.out.println("Proceed without waiting for the async method result.");
}

public final void testLongProcessing() throws InterruptedException, ExecutionException {
Future<Integer> futureResult = asyncBean.longProcessing(8, 9);
System.out.println("Proceed to other tasks and check async method result later.");
Integer intResult = futureResult.get();
System.out.println("The prior async method returned " + intResult);
}
}
I've tested this example with GlassFish V3 in its embedded mode. It should work with any Java EE 6 application servers as well. The configureation (classpath, outputDir, etc) and how to run it are the same as EJB Lite testing with JUnit and embeddable container.

3/10/2010

How to trim ant property string value

When writing ant build files, I often need to trim the string value of an ant property. But I couldn't find a simple task to do that. So I wrote the following ant macrodef for this purpose. In short, it saves the property value into a temp file, trims lines while moving the file, and loads the file back into a new property.

<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="trim" name="test">
<target name="trim">
<trim input="${text}" property="text.trimmed" />
<echo message="original text='${text}'" />
<echo message="trimmed text='${text.trimmed}'" />
</target>

<macrodef name="trim">
<attribute name="input" />
<attribute name="property" />
<sequential>
<tempfile property="temp.file" />
<echo file="${temp.file}" message="@{input}" />
<move file="${temp.file}" tofile="${temp.file}.2">
<filterchain>
<trim/>
</filterchain>
</move>
<loadfile property="@{property}" srcFile="${temp.file}.2" />
<delete file="${temp.file}.2" failonerror="false" />
</sequential>
</macrodef>

</project>
To run this target:
/tmp > ant -Dtext=' a b c '
Buildfile: build.xml

trim:
[move] Moving 1 file to /tmp
[delete] Deleting: /tmp/null1355243589.2
[echo] original text=' a b c '
[echo] trimmed text='a b c'

BUILD SUCCESSFUL
Total time: 0 seconds
The ant macro named trim declares the trim operation, while the trim target shows an example of calling the trim macro.

There are other solutions to achive the same result, e.g., write a custom ant task, or use ant-contrib tasks. But the advantage of this sample is that it only uses built-in ant tasks and no other library jars are needed.