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.