Saturday, August 16, 2008

EJB Programming Restrictions

In order for the container to properly carry out its duties, provide appropriate services, and manage the components within its environment, a number of restrictions apply to the design and creation of EJBs. These rules often cause much confusion and gnashing of teeth among developers, but it is best to understand and abide by them to avoid problems in your beans and applications.

"This section describes the programming restrictions that a Bean Provider must follow to ensure that the enterprise bean is portable and can be deployed in any compliant EJB 2.0 Container. The restrictions apply to the implementation of the business methods...

* An enterprise Bean must not use read/write static fields. Using read-only static fields is allowed. Therefore, it is recommended that all static fields in the enterprise bean class be declared as final.
* An enterprise Bean must not use thread synchronization primitives to synchronize execution of multiple instances.
* An enterprise Bean must not use the AWT functionality to attempt to output information to a display, or to input information from a keyboard.
* An enterprise bean must not use the java.io package to attempt to access files and directories in the file system.
* An enterprise bean must not attempt to listen on a socket, accept connections on a socket, or use a socket for multicast.
* The enterprise bean must not attempt to query a class to obtain information about the declared members that are not otherwise accessible to the enterprise bean because of the security rules of the Java language. The enterprise bean must not attempt to use the Reflection API to access information that the security rules of the Java programming language make unavailable.
* The enterprise bean must not attempt to create a class loader; obtain the current class loader; set the context class loader; set security manager; create a new security manager; stop the JVM; or change the input, output, and error streams.
* The enterprise bean must not attempt to set the socket factory used by ServerSocket, Socket, or the stream handler factory used by URL.
* The enterprise bean must not attempt to manage threads. The enterprise bean must not attempt to start, stop, suspend, or resume a thread; or to change a thread's priority or name. The enterprise bean must not attempt to manage thread groups.
* The enterprise bean must not attempt to directly read or write a file descriptor.
* The enterprise bean must not attempt to obtain the security policy information for a particular code source.
* The enterprise bean must not attempt to load a native library.
* The enterprise bean must not attempt to gain access to packages and classes that the usual rules of the Java programming language make unavailable to the enterprise bean.
* The enterprise bean must not attempt to define a class in a package.
* The enterprise bean must not attempt to access or modify the security configuration objects (Policy, Security, Provider, Signer, and Identity).
* The enterprise bean must not attempt to use the subclass and object substitution features of the Java Serialization Protocol.
* The enterprise bean must not attempt to pass this as an argument or method result. The enterprise bean must pass the result of SessionContext.getEJBObject(), SessionContext. getEJBLocalObject(), EntityContext.getEJBObject(), or EntityContext.getEJBLocalObject() instead."

Sunday, May 25, 2008

Enhanced Exception Handling

Exception wrapping has a couple of disadvantages

• Exception wrapping may result in very long stack traces consisting of one stack trace for each exception in the wrapping hierarchy. Most often only the root stack trace is interesting. The rest of the stack traces are then just annoying.
• The messages of the exceptions are spread out over the stack traces. The message of an exception is typically printed above the stack trace. When several exceptions wrap each other in a hierarchy, all these messages are spread out in between the stack traces. This makes it harder to determine what went wrong, and what the program was trying to do when the error happened. In other words, it makes it hard to determine in what context the error occurred. The error might have occurred in a PersonDao class, but was it called from a servlet or from a web service when it failed?

Exception enhancement is an alternative to Exception wrapping
In exception enhancement we do not wrap exceptions; instead you add contextual information to the original exception and re-throw it. Re-throwing an exception does not reset the stack trace embedded in the exception.
Here is an example:
------------------------------------------------------------------------------------

public void addItem() throws EnhanceableException{
try{
add();
} catch(EnhanceableException e){
e.addExceptionMetadata("An error occurred when trying to ...");
throw e;
}
}
public void add() throws EnhanceableException {
if(...) throw new EnhanceableException( "Original error message");
}

---------------------------------------------------------------------------------------
As per the above example we can see the add() method throws an EnhanceableException which is a superclass for Enhanceable exceptions.
NOTE:-
This is not a standard Java exception. So we will have to create it yourself.
In addition to add() method, we also have addItems() method which calls the addExceptionMetadata() method on the caught EnhanceableException, and re-throw it afterwards. As the exception propagates up the call stack, each catch block can add relevant information to the exception if necessary.
Using this simple technique we can only get a single stack trace, and still get any relevant contextual information necessary to investigate the cause of the exception.

Unique Error Codes
If there is a requirement that each error raised in an application is identified by a unique error code. This can be a bit problematic since some errors are raised inside components that are reused throughout the application. Therefore an exception may seem the same to the component throwing it, but the context in which the exception occurs is different.

Here is an example:
----------------------------------------------------------------------------------------

public void addItemService() throws EnhanceableException{
try{
add();
} catch(EnhanceableException e){
e.addExceptionMetadata("An error occurred when trying to ..."); throw e;
}
}
public void addItem() throws EnhanceableException{
try{
add();
} catch(EnhanceableException e){
e.addExceptionMetadata("An error occurred when trying to ..."); throw e;
}
}
public void add() throws EnhanceableException {
if(...) throw new EnhanceableException( "ERROR1", "Original error message"); }

----------------------------------------------------------------------------------------------------
In the above example we have add() method which adds the code "ERROR1" to the thrown EnhanceableException to uniquely identify that error cause.
But, notice too that add() method is called from both addItem() and addItemService() methods.
Though the error may seem the same to add() method no matter which of addItem() and addItemServce() method that called it, this may important to know for the developer investigating the error. The error code "ERROR1" is enough to determine where the error occurred, but not in what context it occurred.
A solution to this problem is to add unique context error codes to the exception the same way the other contextual information is added. Here is an example where we modify the addExceptionMetadata() method has been changed to accommodate this:
---------------------------------------------------------------------------------------------------

public void addItemService() throws EnhanceableException{
try{
add();
} catch(EnhanceableException e){
e.addExceptionMetadata("addItemService", "ERROR1", "An error occurred when trying to ..."); throw e;
}
}


public void addItem() throws EnhanceableException{
try{
add();
} catch(EnhanceableException e){
e.addExceptionMetadata("addItem", "ERROR1", "An error occurred when trying to ..."); throw e;
}
}


public void add() throws EnhanceableException {
if (---){
throw new EnhanceableException( "add", "ERROR1",
"Original error message");
}
}

--------------------------------------------------------------------------------------------------


We can add two new parameters to the addExceptionMetadata() method and the constructor of the EnhanceableException.
• The first parameter is a code identifying the context in which the error occurred.
• The second parameter is a unique error code within that context.
An error identification for an exception thrown by add() method when called from addItem() method will now look like this:
[addItem:ERROR1][add:ERROR1]
And error identification for an exception thrown by add() method when called from addItemService() method will now look like this:
[addMethodService:ERROR1][add:ERROR1]
Now it is possible to distinguish an exception thrown from add() method via addItem() method from the same exception thrown from add() method via addItemservice() method.
NOTE:-
This is only required if we have a Requirement where we need to have extra Contextual Error code.

Wrapping Non-Enhanceable Exceptions
We may not always be able to avoid exception wrapping. If a component in our application throws a checked exception that is not Enhanceable, we may have to wrap it in an Enhanceable exception. Here is an example where add() method catches a non-Enhanceable exception and wraps it in an Enhanceable exception, and throws the Enhanceable exception:

public void method1() throws EnhanceableException {
try{
... call some method that throws an IOException ...
} catch(IOException ioexception) {
throw new EnhanceableException( ioexception, "METHOD1", "ERROR1", "Original error message");
}
}

Unchecked EnhanceableException
After reading the below article, It should be a good idea to have EnhanceableException as unchecked, by having it extend RuntimeException.
Refer below link
Would You Like Checked or Unchecked Exceptions With That?
Exception Enhancement and Pluggable Exception Handlers
Like with any other exception type it is possible to use pluggable exception handlers with Enhanceable exceptions. If we use the unique error codes described earlier these codes must be added as paremeters to the exception handler interface. Here is an example exception handler interface supporting these unique error codes:

public interface ExceptionHandler{

public void handle(String contextCode, String errorCode,
String errorText, Throwable t)

public void raise(String contextCode, String errorCode,
String errorText);

}

Exceptions caught in the program will be passed to the handleException() which will decide what concrete exception to throw instead. In this case an EnhanceableException is thrown. If the EnhanceableException is unchecked it is not necessary to declare it in the handleException() method.

An Example EnhanceableException
Below is an example of an Enhanceable exception that we can use as a template or create our own Enhanceable exceptions based on this template.
The class definition can be modified to suit the requirements. The exception is designed to use unique error codes as described earlier.
Below the code is an example application that uses the EnhanceableException, and a stack trace generated from this application.

ExceptionMetadata class

/** This class would be used for keeping the Exception information **/
protected class ExceptionMetadata{
public String errorContext = null;
public String errorCode = null;
public String errorText = null;
public ExceptionMetadata(String contextCode, String errorCode,
String errorText){

this.errorContext = contextCode;
this.errorCode = errorCode;
this.errorText = errorText;
}

ExceptionHandler Class
public class ExceptionHandler{
public void handle(String errorContext, String errorCode,
String errorText, Throwable t){

if(! (t instanceof EnhanceableException)){
throw new EnhanceableException(
errorContext, errorCode, errorText, t);
} else {
((EnhanceableException) t).addExceptionMetadata(
errorContext, errorCode, errorText);
}
}

public void raise(String errorContext, String errorCode,
String errorText){
throw new EnhanceableException(
errorContext, errorCode, errorText);
}
}

EnhanceableException Class

import java.util.ArrayList;
import java.util.List;

public class EnhanceableException extends RuntimeException {
public static final long serialVersionUID = -1;

protected List exceptionMetadatas =
new ArrayList();

public EnhanceableException(String errorContext, String errorCode,
String errorMessage){
addExceptionMetadata(errorContext, errorCode, errorMessage);
}

public EnhanceableException(String errorContext, String errorCode,
String errorMessage, Throwable cause){
super(cause);
addExceptionMetadata(errorContext, errorCode, errorMessage);
}

public EnhanceableException addExceptionMetadata(
String errorContext, String errorCode, String errorText){
this.exceptionMetadatas.add(
new ExceptionMetadata(errorContext, errorCode, errorText));
return this;
}

//This create the String consisting of all the contextual error
// codes
public String getCode(){
StringBuilder builder = new StringBuilder();

//Iterating over the exception meta data info list
for(int i = this.exceptionMetadatas.size()-1 ; i >=0; i--){
ExceptionMetadata exceptionMetadata = this.ExceptionMetadatas.get(i);
builder.append('[');
builder.append(exceptionMetadata.errorContext);
builder.append(':');
builder.append(exceptionMetadata.errorCode);
builder.append(']');
}

return builder.toString();
}

public String toString(){
StringBuilder builder = new StringBuilder();

builder.append(getCode());
builder.append('\n');


//append additional context infomation
for(int i = this.exceptionMetadatas.size()-1 ; i >=0; i--){
ExceptionMetadata exceptionMetadata = this.ExceptionMetadatas.get(i);
builder.append('[');
builder.append(exceptionMetadata.errorContext);
builder.append(':');
builder.append(exceptionMetadata.errorCode);
builder.append(']');
builder.append(exceptionMetadata.errorText);
if(i>0) builder.append('\n');
}

//append root causes and text from this exception first.
if(getMessage() != null) {
builder.append('\n');
if(getCause() == null){
builder.append(getMessage());
} else if(!getMessage().equals(getCause().toString())){
builder.append(getMessage());
}
}
appendException(builder, getCause());

return builder.toString();
}

private void appendException(
StringBuilder builder, Throwable throwable){
if(throwable == null) return;
appendException(builder, throwable.getCause());
builder.append(throwable.toString());
builder.append('\n');
}


Here Is the Test Class for EnhanceableException

public class ExceptionTest {

protected ExceptionHandler exceptionHandler = new ExceptionHandler();

public static void main(String[] args){
ExceptionTest test = new ExceptionTest();
try{
test.method1();
} catch(Exception e){
e.printStackTrace();
}
}

public void method1(){
try{
method2();
} catch (EnhanceableException e){
this.exceptionHandler.handle(
"M1", "E1", "Error in method 1, calling method 2", e);
throw e;
}
}

public void method2(){
try{
method3();
} catch (EnhanceableException e){
this.exceptionHandler.handle(
"M2", "E2", "Error in method 2, calling method 3", e);
throw e;
}
}

public void method3(){
try{
method4();
} catch(Exception e){
this.exceptionHandler.handle(
"M3", "E3", "Error at method 3", e);
}
}

public void method4(){
throw new IllegalArgumentException("incorrect argument passed");
}

}




Test Case Output:-

[M1:E1][M2:E2][M3:E3]
[M1:E1]Error in method 1, calling method 2
[M2:E2]Error in method 2, calling method 3
[M3:E3]Error at method 3
java.lang.IllegalArgumentException: incorrect argument passed

at exception.ExceptionTest$1.handle(ExceptionTest.java:8)
at exception.ExceptionTest.method3(ExceptionTest.java:49)
at exception.ExceptionTest.method2(ExceptionTest.java:38)
at exception.ExceptionTest.method1(ExceptionTest.java:29)
at exception.ExceptionTest.main(ExceptionTest.java:21)
Caused by: java.lang.IllegalArgumentException: incorrect argument passed
at exception.ExceptionTest.method4(ExceptionTest.java:54)
at exception.ExceptionTest.method3(ExceptionTest.java:47)
... 3 more

That all !!!

If you want to read more on Java Exception, below are some good links
http://dev2dev.bea.com/lpt/a/541