Pages

Sunday, March 25, 2012

JEE example - part 2: Asynchronous EJB call


In this entry I will discuss the use of EJB asynchronous call in JEE applications.

By default, session bean invocations through the Remote, Local, and no-interface views are synchronous. The client that made the call blocks for the duration of the invocation and is returned control only after all invocation processing has completed. Since EJB 3.1 clients can achieve asynchronous invocation behavior (for instance on long time processing tasks). A session bean class or a method can be marked with the annotation javax.ejb.Asynchronous. When using such designated methods, control returns to the client before the container dispatches the invocation to a bean instance; the container continues processing the invocation on a separate thread of execution. It is to be noted that asynchronous method invocation semantics only apply to the no-interface, Local business, and Remote business client views. As stated in the EJB 3.1 specification, an asynchronous method must have return type void or java.util.concurrent.Future<V>, where V is the result value type. Here is an example that completes the interface from previous post:

package org.example.jee.service;
import java.util.concurrent.Future;

/**
 * Service Interface
 * 
 * @author fracaru
 * 
 */
public interface IService {

 /**
  * Greetings 
  * @return greetings
  */
 String sayHello();
           
        /**
  * Asynchronous Greetings 
  * @return greetings
  */
 Future sayHelloAsynchronously();
}


And the EJB implementation:

package org.example.jee.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.Future;

import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Remote;
import org.example.jee.service.IService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author fracaru
 * 
 */
@Remote(IService.class)
@Stateless
public class HelloServiceBean implements IService {

 @Override
 public String sayHello() {
  return "Hello World";
 }

 @Override
 @Asynchronous
 public Future sayHelloAsynchronously() {
  // do some processing stuff here
  if (ctx.wasCancelCalled()) {
   return new AsyncResult(
     "sayHello Asynchronous Call Cancelled");
  }
  // continue long processing stuff here
  for (int i = 0; i < 10000; i++) {
   try {
    File f = File.createTempFile("process", "tmp");
    FileOutputStream fos = new FileOutputStream(f);
    fos.write(i);
    fos.flush();
    fos.close();
    if (i % 1000 == 0) {
     System.out.println("Processing asynchronous call ...");
    }
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
  return new AsyncResult("Asynchronous Hello World");
 }

}

What is the processing on the client side? When using the Future return type, the client can retrieve the invocation result value, discover any invocation exception, or attempt to cancel the asynchronous invocation. When a client calls the get method, it blocks, waiting the processing to complete, and then retrieves the result, if available. It is to remark that the JEE specification recommends to clients to process the result of an asynchronous EJB call as the server will try to return this result.

@Test
 public void testService() {
 IService ejb = (IService) ctx.lookup(remoteBean);
 Future result = ejb.sayHelloAsynchronously();
 
 System.out.println("Return from asynchronous call ...");
 // process result call
 String message = result.get();
    Assert.assertEquals("Asynchronous Hello World", message);
 }

Before EJB 3.1, the only possibility to build asynchronous JEE architecture was to use JMS and MDB. JMS is a Message Oriented Middleware (MOM) that allows application JEE applications to create, send, receive, and read messages. You have to create and manage JMS queues where a client publishes messages. The processing is handled in the MDB that consumes the message. It is to be noted that JMS provides a more robust and reliable mechanism to achieve asynchronous calls than asynchronous session bean methods. In a JMS / MDB architecture, messages are persisted, and only on successful processing in the MDB they are removed from queue only on successful processing in the MDB. Moreover, using JMS allows components or applications to be to be loosely coupled and provides allows delivery guarantees. In conclusion, the use of javax.ejb.Asynchronous annotation is recommended when oneyou just wants to call a method asynchronously, with no need of complex mechanisms.

Wednesday, February 22, 2012

JEE example - part 1: remote EJB invocation using JBoss AS7


JBoss AS 7 7.1.0.Final (codename “Thunder”) the well-known JEE application server was released last week and it can be downloaded here.
 This version is certified JEE 6 full profile, while the previous 7.0 was certified JEE Web profile only. Therefore, this blog entry will present in how to create, deploy and test a JEE 6 application using remote EJBs.

First off all download the JBoss archive (zip or tar.gz) and decompress it on your disk. Optionally, you may set up the JBOSS_HOME environment variable. All you need now to do is to launch the server using scripts in /bin folder. It is to remark one of the new features of JBoss AS7: the possibility to manage multiple instances (a collection of such instances are referred as members of “domain”) from a single control point. In order to have such coordinated production multi server management you may use domain.sh or domain.bat scripts. In our examples, we will use a single server instance and launch standalone.bat or standalone.sh scripts

============
21:47:35,369 INFO  [org.jboss.modules] JBoss Modules version 1.1.1.GA
21:47:35,509 INFO  [org.jboss.msc] JBoss MSC version 1.0.2.GA
21:47:35,556 INFO  [org.jboss.as] JBAS015899: JBoss AS 7.1.0.Final "Thunder" starting
21:47:36,320 INFO  [org.jboss.as.server] JBAS015888: Creating http management service using socket-binding (management-http)
21:47:36,320 INFO  [org.xnio] XNIO Version 3.0.3.GA
21:47:36,336 INFO  [org.xnio.nio] XNIO NIO Implementation Version 3.0.3.GA
21:47:36,351 INFO  [org.jboss.remoting] JBoss Remoting version 3.2.2.GA
21:47:36,351 INFO  [org.jboss.as.logging] JBAS011502: Removing bootstrap log handlers
21:47:36,351 INFO  [org.jboss.as.configadmin] (ServerService Thread Pool -- 32) JBAS016200: Activating ConfigAdmin Subsystem
21:47:36,367 INFO  [org.jboss.as.jacorb] (ServerService Thread Pool -- 38) JBAS016300: Activating JacORB Subsystem
21:47:36,367 INFO  [org.jboss.as.clustering.infinispan] (ServerService Thread Pool -- 37) JBAS010280: Activating Infinispan subsystem.
21:47:36,367 INFO  [org.jboss.as.osgi] (ServerService Thread Pool -- 49) JBAS011940: Activating OSGi Subsystem
21:47:36,383 INFO  [org.jboss.as.webservices] (ServerService Thread Pool -- 58) JBAS015537: Activating WebServices Extension
21:47:36,398 INFO  [org.jboss.as.connector] (MSC service thread 1-3) JBAS010408: Starting JCA Subsystem (JBoss IronJacamar 1.0.7.Final)
21:47:36,414 INFO  [org.jboss.as.security] (ServerService Thread Pool -- 54) JBAS013101: Activating Security Subsystem
21:47:36,414 INFO  [org.jboss.as.naming] (ServerService Thread Pool -- 48) JBAS011800: Activating Naming Subsystem
21:47:36,539 INFO  [org.jboss.as.naming] (MSC service thread 1-6) JBAS011802: Starting Naming Service
21:47:36,539 INFO  [org.jboss.as.security] (MSC service thread 1-6) JBAS013100: Current PicketBox version=4.0.6.final
21:47:36,570 INFO  [org.jboss.as.jaxr] (MSC service thread 1-6) Binding JAXR ConnectionFactory: java:jboss/jaxr/ConnectionFactory
21:47:36,570 INFO  [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 33) JBAS010403: Deploying JDBC-compliant driver class org.h2.Driver (version 1.3)
21:47:36,601 INFO  [org.jboss.as.connector.subsystems.datasources] (ServerService Thread Pool -- 33) JBAS010404: Deploying non-JDBC-compliant driver class org.postgresql.Driver (version 8.4)
21:47:36,617 INFO  [org.jboss.as.mail.extension] (MSC service thread 1-6) JBAS015400: Bound mail session [java:jboss/mail/Default]
21:47:36,663 INFO  [org.jboss.ws.common.management.AbstractServerConfig] (MSC service thread 1-2) JBoss Web Services - Stack CXF Server 4.0.1.GA
21:47:36,913 WARN  [org.jboss.as.messaging] (MSC service thread 1-1) JBAS011600: AIO wasn't located on this platform, it will fall back to using pure Java NIO. If your platform is Linux, install LibAIO
21:47:36,913 INFO  [org.jboss.as.server.deployment.scanner] (MSC service thread 1-3) JBAS015012: Started FileSystemDeploymentService for directory D:\servers\jboss-as-7.1.0.Final\standalone\deployments
21:47:36,929 INFO  [org.apache.coyote.http11.Http11Protocol] (MSC service thread 1-2) DÚmarrage de Coyote HTTP/1.1 sur http-127.0.0.1-127.0.0.1-8080
21:47:36,929 INFO  [org.jboss.as.remoting] (MSC service thread 1-7) JBAS017100: Listening on 127.0.0.1/127.0.0.1:4447
21:47:36,944 INFO  [org.hornetq.core.server.impl.HornetQServerImpl] (MSC service thread 1-1) live server is starting with configuration HornetQ Configuration (clustered=false,backup=false,sharedStore=tr
21:47:37,116 INFO  [org.hornetq.core.server.impl.HornetQServerImpl] (MSC service thread 1-1) Waiting to obtain live lock
21:47:37,131 INFO  [org.hornetq.core.persistence.impl.journal.JournalStorageManager] (MSC service thread 1-1) Using NIO Journal
21:47:37,131 INFO  [org.jboss.as.remoting] (MSC service thread 1-2) JBAS017100: Listening on /127.0.0.1:9999
21:47:37,194 INFO  [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-6) JBAS010400: Bound data source [java:jboss/datasources/ExampleDS]
21:47:37,194 INFO  [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-5) JBAS010400: Bound data source [java:/PersonDS]
21:47:37,194 INFO  [org.hornetq.core.server.impl.FileLockNodeManager] (MSC service thread 1-1) Waiting to obtain live lock
21:47:37,194 INFO  [org.hornetq.core.server.impl.FileLockNodeManager] (MSC service thread 1-1) Live Server Obtained live lock
21:47:37,272 WARN  [jacorb.codeset] (MSC service thread 1-5) Warning - unknown codeset (Cp1252) - defaulting to ISO-8859-1
21:47:37,412 INFO  [org.jboss.as.jacorb] (MSC service thread 1-5) JBAS016330: CORBA ORB Service started
21:47:37,490 INFO  [org.jboss.as.jacorb] (MSC service thread 1-4) JBAS016328: CORBA Naming Service started
21:47:37,771 INFO  [org.hornetq.core.remoting.impl.netty.NettyAcceptor] (MSC service thread 1-1) Started Netty Acceptor version 3.2.5.Final-a96d88c 127.0.0.1:5445 for CORE protocol
21:47:37,787 INFO  [org.hornetq.core.remoting.impl.netty.NettyAcceptor] (MSC service thread 1-1) Started Netty Acceptor version 3.2.5.Final-a96d88c 127.0.0.1:5455 for CORE protocol
21:47:37,787 INFO  [org.hornetq.core.server.impl.HornetQServerImpl] (MSC service thread 1-1) Server is now live
21:47:37,787 INFO  [org.hornetq.core.server.impl.HornetQServerImpl] (MSC service thread 1-1) HornetQ Server version 2.2.11.Final (HQ_2_2_11_FINAL_AS7, 122) [cda4651d-5c69-11e1-ad91-30a420524153]) starte
21:47:37,787 INFO  [org.hornetq.core.server.impl.HornetQServerImpl] (MSC service thread 1-5) trying to deploy queue jms.topic.testTopic
21:47:37,802 INFO  [org.jboss.as.messaging] (MSC service thread 1-5) JBAS011601: Bound messaging object to jndi name java:/topic/test
21:47:37,802 INFO  [org.jboss.as.messaging] (MSC service thread 1-5) JBAS011601: Bound messaging object to jndi name java:jboss/exported/jms/topic/test
21:47:37,818 INFO  [org.jboss.as.messaging] (MSC service thread 1-7) JBAS011601: Bound messaging object to jndi name java:/ConnectionFactory
21:47:37,818 INFO  [org.jboss.as.messaging] (MSC service thread 1-8) JBAS011601: Bound messaging object to jndi name java:jboss/exported/jms/RemoteConnectionFactory
21:47:37,833 INFO  [org.jboss.as.messaging] (MSC service thread 1-8) JBAS011601: Bound messaging object to jndi name java:/RemoteConnectionFactory
21:47:37,833 INFO  [org.hornetq.core.server.impl.HornetQServerImpl] (MSC service thread 1-3) trying to deploy queue jms.queue.testQueue
21:47:37,833 INFO  [org.jboss.as.messaging] (MSC service thread 1-3) JBAS011601: Bound messaging object to jndi name java:/queue/test
21:47:37,833 INFO  [org.jboss.as.messaging] (MSC service thread 1-3) JBAS011601: Bound messaging object to jndi name java:jboss/exported/jms/queue/test
21:47:37,833 INFO  [org.jboss.as.deployment.connector] (MSC service thread 1-6) JBAS010406: Registered connection factory java:/JmsXA
21:47:37,849 INFO  [org.hornetq.ra.HornetQResourceAdapter] (MSC service thread 1-6) HornetQ resource adaptor started
21:47:37,849 INFO  [org.jboss.as.connector.services.ResourceAdapterActivatorService$ResourceAdapterActivator] (MSC service thread 1-6) IJ020002: Deployed: file://RaActivatorhornetq-ra
21:47:37,849 INFO  [org.jboss.as.deployment.connector] (MSC service thread 1-6) JBAS010401: Bound JCA ConnectionFactory [java:/JmsXA]
21:47:37,865 INFO  [org.jboss.as] (Controller Boot Thread) JBAS015874: JBoss AS 7.1.0.Final "Thunder" started in 2715ms - Started 172 of 249 services (75 services are passive or on-demand)



One of most impressive features of you can rapidly observe JBoss AS7 is the startup time, less than 3s. This is achieved among others by a modular architecture and a lazy loading mechanism.

Let’s create now our simple application. In the first time create an interface with one method

package org.example.jee.service;

import java.util.concurrent.Future;

/**
 * Service Interface
 * 
 * @author fracaru
 * 
 */
public interface IService {

 /**
  * Greetings 
  * @return greetings
  */
 String sayHello();
}

Now, we will create a stateless bean that implements the previous interface. In order to invoke this service remotely, you have to declare the expose the view of bean using @Remote annotation.

package org.example.jee.service.impl;

import javax.ejb.Remote;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;

import org.example.jee.service.IService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @author fracaru
 * 
 */
@Remote(IService.class)
@Stateless
public class HelloServiceBean implements IService {

 /* the context used to illustrate asynchronous calls */
 @Resource
 SessionContext ctx;

 /* the logger */
 private final Logger logger = LoggerFactory
   .getLogger(HelloServiceBean.class);

 /*
  * (non-Javadoc)
  * 
  * @see org.example.jee.service.IService#sayHello()
  */
 @Override
 public String sayHello() {
  return "Hello World";
 }
}

I used maven to package the interface in a jar called project-common-service-1.0-SNAPSHOT.jar and the bean in a jar called project-server-ejb-1.0-SNAPSHOT.jar and created an ear archive project-jee-example.ear. In order to deploy the ear on the server we have several options:

  • Use maven plug-ins
  •  Use a CLI interface
  • Copy the ear to the deployment folder
  • Use the new improved web administration interface. Note that for this version the security is activated by default for the server so you need to create an admin user (using add-user scripts in the bin folder) in order to use the web interface.

Once our application installed, you can check the server log (or the interface notification) to verify that the ear was correctly deployed:

22:04:50,551 INFO  [org.jboss.as.server.deployment] (MSC service thread 1-6) JBAS015876: Starting deployment of "project-server-ejb-1.0-SNAPSHOT.jar"
22:04:50,941 INFO  [org.jboss.weld.deployer] (MSC service thread 1-2) JBAS016002: Processing weld deployment project-jee-example.ear
22:04:50,988 INFO  [org.jboss.weld.deployer] (MSC service thread 1-1) JBAS016002: Processing weld deployment project-server-ejb-1.0-SNAPSHOT.jar
22:04:50,988 INFO  [org.jboss.as.ejb3.deployment.processors.EjbJndiBindingsDeploymentUnitProcessor] (MSC service thread 1-1) JNDI bindings for session bean named HelloServiceBean in deployment unit subdeployment "project-server-ejb-1.0-SNAPSHOT.jar" of deployment "project-jee-exampl

        java:global/project-jee-example/project-server-ejb-1.0-SNAPSHOT/HelloServiceBean!org.example.jee.service.IService
        java:app/project-server-ejb-1.0-SNAPSHOT/HelloServiceBean!org.example.jee.service.IService
        java:module/HelloServiceBean!org.example.jee.service.IService
        java:jboss/exported/project-jee-example/project-server-ejb-1.0-SNAPSHOT/HelloServiceBean!org.example.jee.service.IService
        java:global/project-jee-example/project-server-ejb-1.0-SNAPSHOT/HelloServiceBean
        java:app/project-server-ejb-1.0-SNAPSHOT/HelloServiceBean
        java:module/HelloServiceBean


Now that the application is deployed we will create a remote client that accesses the exposed service. In order to work the client need on the one hand the project-common-service-1.0-SNAPSHOT.jar and the JBoss client dependencies. For the 7.1.0 version, JBoss provided a jboss-client-7.1.0.Final.jar in the bin/client directory which contains a minimal set of jars required for remote JMS and EJB usage. This jar is to be used for non-maven clients; for maven users JBoss recommends to not use this jar, but use the following BOM dependencies instead:

        
            org.jboss.as
            jboss-as-ejb-client-bom
            pom
        
        
            org.jboss.as
            jboss-as-jms-client-bom
            pom
        
    

Here is my client code, inspired from the JBoss documentation:

private static IService service;

 @BeforeClass
 public static void setUpTests() throws NamingException {
  System.out.println("Init tests...");
  Hashtable envs = new Hashtable();
        envs.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
        InitialContext context = new InitialContext(envs);

  // The app name is the application name of the deployed EJBs. This is
  // typically the ear name
  // without the .ear suffix. However, the application name could be
  // overridden in the application.xml of the
  // EJB deployment on the server.
  final String appName = "project-jee-example";
  // This is the module name of the deployed EJBs on the server. This is
  // typically the jar name of the
  // EJB deployment, without the .jar suffix, but can be overridden via
  // the ejb-jar.xml
  // In this example, we have deployed the EJBs in a
  // jboss-as-ejb-remote-app.jar, so the module name is
  // jboss-as-ejb-remote-app
  final String moduleName = "project-server-ejb-1.0-SNAPSHOT";
  // AS7 allows each deployment to have an (optional) distinct name. We
  // haven't specified a distinct name for
  // our EJB deployment, so this is an empty string
  final String distinctName = "";
  // The EJB name which by default is the simple class name of the bean
  // implementation class
  final String beanName = "HelloServiceBean";
  // the remote view fully qualified class name
  final String viewClassName = "org.example.jee.service.IService";
  // let's do the lookup
  String lookUpJNDI = "ejb:" + appName + "/" + moduleName + "/"
    + distinctName + "/" + beanName + "!" + viewClassName;

  service = (IService) context.lookup(lookUpJNDI);

 }

@Test
 public void testService() {
  String message = service.sayHello();
  System.out.println("Message = " + message);
  Assert.assertEquals("Hello World", message);
 }

Several things are to be discussed before testing the client. First off all,  the JNDI name used (the comments in code are self explanatory):
"ejb:project-jee-example/project-server-ejb-1.0-SNAPSHOT//HelloServiceBean!org.example.jee.service.IService"

Personally, I would prefer to have here the global portable JNDI name for EJB3.1, ie java:global/[<application-name>]/<module-name>/<bean-name>!<fully-qualified-bean-interface-name>.instead of ejb:// and JBoss specific name.

The second thing is the necessity to provide in the client classpath a file called jboss-ejb-client.properties containing EJB client context properties. A different file may be indicated using the command
-Djboss.ejb.client.properties.file.path=/home/fracaru/jbossas7/client/custom-jboss-ejb-client.properties



####################################################################
endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false

remote.connections=default

remote.connection.default.host=localhost
remote.connection.default.port = 4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=true

remote.connection.default.username=user
remote.connection.default.password=userpassword
####################################################################


Note in this file the server host address and port number (by default JBoss AS7 is using the 4447 for remote EJB invocation). It also to remark user / password properties provided in the properties file which must be set because the security-realm is enabled for the subsystem remoting. They must be set by using the command bin/add-user.sh (or.bat). If you do not need the security, just remove the attribute security-realm in the configuration (standalone.xml).


And when launching the client, you should see the next output log.

févr. 21, 2012 10:51:09 PM org.jboss.ejb.client.EJBClient <clinit>
INFO: JBoss EJB Client version 1.0.2.Final
Lookup OK ! Proxy for remote EJB StatelessEJBLocator{appName='project-jee-example', moduleName='project-server-ejb-1.0-SNAPSHOT', distinctName='', beanName='HelloServiceBean', view='interface org.example.jee.service.IService'}
févr. 21, 2012 10:51:09 PM org.xnio.Xnio <clinit>
INFO: XNIO Version 3.0.3.GA
févr. 21, 2012 10:51:09 PM org.xnio.nio.NioXnio <clinit>
INFO: XNIO NIO Implementation Version 3.0.3.GA
févr. 21, 2012 10:51:09 PM org.jboss.remoting3.EndpointImpl <clinit>
INFO: JBoss Remoting version 3.2.2.GA
févr. 21, 2012 10:51:10 PM org.jboss.ejb.client.remoting.VersionReceiver handleMessage
INFO: Received server version 1 and marshalling strategies [river]
févr. 21, 2012 10:51:10 PM org.jboss.ejb.client.remoting.RemotingConnectionEJBReceiver associate
INFO: Successful version handshake completed for receiver context EJBReceiverContext{clientContext=org.jboss.ejb.client.EJBClientContext@15a64e6, receiver=Remoting connection EJB receiver [connection=Remoting connection <1f17060>,channel=jboss.ejb,nodename=ak8820]} on channel Channel ID c063cdac (outbound) of Remoting connection 00e2b745 to localhost/127.0.0.1:4447
févr. 21, 2012 10:51:10 PM org.jboss.ejb.client.remoting.ChannelAssociation$ResponseReceiver handleMessage
WARN: Unsupported message received with header 0xffffffff
Message = Hello World



I was very delighted to discover this JBoss AS7 7.1.0 release certified JEE 6 full profile.  A new and improved architecture, fast boot time and new features are in the menu. It’s time to use them …
Thanks. In a second blog entry, I will enhance this example with stateful EJB, asynchronous ejb call, JPA and REST features. …

Wednesday, February 15, 2012

Eclipse RCP P2 self-update


I have been working last time in order to add update mechanism to Eclipse RCP application we have developed in my team. I must confess I struggled for some time with the P2 mechanism and finally I succeeded to obtain the expected result: an RCP based application with an automatic self-update mechanism, using headless build and testable with Jenkins.


Adding self-update to an application is not a ordinary or easy task. There are few examples here, here, or here
I tried different solution and there were always some scenarios that didn’t work properly (headless build did not work as expected,  problems with dependencies in eclipse.equinox.p2.user.ui, plug-in org.eclipse.equinox.ds not started and the most frustrating the updates were not detected). Therefore, my colleagues agreed all that P2 mechanism is "unnecessary complicated".  


Here are steps that I followed in order to have the provisioning mechanism working.
  • Create a feature-based product (a simple RCP Application to which we will add features)
  •  Export the product
  •  Export features to be included in the application
  • Install features to the result application. You may add them manually using the Install New Software menu or use a command line or script. For instance you may use:

%eclipse_home%/eclipsec.exe -application org.eclipse.equinox.p2.director -repository <list_of_comma_separated_repositories>  -installIU <your_feature_group> -destination <location_of_targeted_product>  -profile <profile>
  • Modify your bundles and features version number
  • Export again the features and deploy them optionally on a web server
  • Start your application and check for updates (update sites may be added at he runtime, in the code or using p2.inf file to have pre-defined locations): it should detect the updated features. Additionally, I added headless update at startup as described here

I hope this will help you to have your P2 working. I will publish soon the source code and scripts for this application.

Thursday, December 29, 2011

Exceptions: Best Practices


Last days I had a discussion with few colleagues about best practices on handling exception in the code (more precisely in the JEE application server). As we exposed each one his point of view, we arrived to point the performance issue that may arrive. At that moment, I remembered an older entry on Xebia French blog that illustrated such performance issue related to exception management.

First off all, the author explains that it was not obvious to find the source of its performance problem. Fortunately, he was using an open source framework and he was able to isolate the code causing the problem. In fact there was a method that threw a NoClassDefFoundError at each call. This method body was looking something like that:
try {
 variable = new SomeClass();
} catch (Throwable t) {
    try {
        variable = new SomeNewClass();
    } catch (Throwable t2) {
        variable = new SomeOtherClass();
    }
}
 
I must confess I spent a whole day to find a bug in one of our projects, caused by a similar code in an external jar file that for some reason threw an exception:
try {
 // doing something
} catch (Throwable t) {
};

Another subject that the author illustrates (and the one of the users argued and proved) is that comparing two method calls, one with nominal execution and the other one throwing an exception, there were significant differences on the execution time between the two cases. This is caused, on the one hand by the object creation and on the other hand by the synchronized fillInStackTrace method in Throwable class.

In conclusion, what we have learn from these examples when using exceptions:
  • exceptions must be used in order to handle errors, an exceptional event that disrupts the normal execution of the program; do not use exceptions in order to transfer values, return parameters from methods etc; 
  • if you catch an exception, log an appropriate message or do something that help the debugging
  • do not catch Throwable, Error or RuntimeException (well there may be some exceptions here  :p)

Wednesday, November 16, 2011

To Agile or not to Agile, that is the challenge


Last week I read one of Nicole Bellios articles on true Agile stories. I found this article and the comments very interesting and confirmed me the idea that I have on Agile.

Indeed, Agile has obvious advantages: ability to respond to changing requirements, collaboration with the stakeholder, team interaction. In my company, agile has also brought us more white boards in the rooms and work area :p. Agile is a MUST in some projects, and it proved the added value for the client and for us. Consequently, we have several agile success stories.

On the other hand, we also have a lot of successful projects that followed a classical process. Agile is not always the right answer. Agile does not fit well in all environments (and this is a postulate from Agile defenders). There are many ways to do agile. Or better, we can successfully apply Agile concepts to projects that are not conducted in a pure agile way: continuous integration, intensive tests, stand-up (even if we were sited) meetings etc.

Moreover, there are people thinking that Agile is focusing on team and result forgetting the main actors, the individuals. Not fully sticking with Agile is OK. Agile or not Agile, there are advantages and disadvantages, constraints and benefits, best practices and individuals beats processes and tools, the success of a project is made by motivated people.
/* */