Monday, September 28, 2009

How to Use EJB 3.0 Session Bean in the Web Application

A sample that uses EJB 3.0 stateless session bean is described here. It is from the ejb3 example in weblogic 10.

Overview of the Whole Picture

Scenario #1: Remote Session Bean
The web.xml file has the following:
<ejb-ref>
    <ejb-ref-name>ejb/ReviewManager</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <remote>examples.ejb.ejb30.service.ReviewService</remote>
    <ejb-link>domain.jar#ReviewManagerBean</ejb-link>
    </ejb-ref>
Here domain.jar is the jar file that contains the ReviewManagerBean.class and the file persistence.xml. The jar is the ejb module. The jsp and servlet files below are in a war file that is the web module. The domain.jar and the web module are in the same ear file. The ejb module domain.jar does not have the descriptor file ejb-jar.xml because it uses ejb annotation. The ReviewManagerBean class is the following:
package examples.ejb.ejb30.session;
import examples.ejb.ejb30.mdb.ReviewListener;
import examples.ejb.ejb30.service.ReviewServiceImpl;
import examples.ejb.ejb30.service.ReviewService;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

/**
 * A Stateless Session Bean operating on a persistent domain model.
 * The EJB container registers the bean in JNDI global namespace.
 * The JNDI name for the bean is the simple name (i.e.
 * stripped of package name) of the business interface it implements.
 * For this example, the JNDI name will be ReviewService
 * which is specified by the annotation element mappedName="ReviewService".

* NOTE:This policy is ambiguous when two beans implements the same * business interface.

* The EJB contain also injects the bean with a instance of * {@link javax.persistence.EntityManager EntityManager} (or rather a proxy). * This EntityManager is used to interact with the persistent EJB domain * model. */ @Stateless(mappedName="ReviewService") @Remote(ReviewService.class) public class ReviewManagerBean extends ReviewServiceImpl implements ReviewService { @PersistenceContext(unitName = "reviewSession") private EntityManager em; @EJB ReviewListener listener; @PostConstruct public void init(){ // inject the EnityManager. injectEntityManager(em); addReviewAddedListener(listener); } }


In the jsp file, the bean is used as follows:
<%
private ReviewService serviceImpl;
String SESSION_BEAN_JNDI_NAME = "java:comp/env/ejb/ReviewManager";
InitialContext ctx = new InitialContext();
serviceImpl = (ReviewService) ctx.lookup(SESSION_BEAN_JNDI_NAME);

Artist artist = serviceImpl.newArtist(name);
%>  
In summary, the web component uses JNDI and InitialContext to get the session bean. A session bean is a service. It is injected an EntityManager instance by the J2EE container using the unitName. Each business method call is a transaction unit. The business method can just use the EntityManager to perform database activity without using any transaction maintence code such as beginTransaction, commit, or rollback. The container will take care of that.
Scenario #2: Local Session Bean
In this scenario, the session bean is local. No configuration is needed in web.xml. The session bean is the following:
@Stateless
@Local(Process.class)
public class ProcessBean {
//reference name and type inferred from variable.
@EJB ReviewListener listener;
@PersistenceContext(unitName = "reviewSession")
private EntityManager em;

public Book getRandomBook() {
List books = em.createQuery("select i from Book i").getResultList();
int size = books.size();
return size == 0 ? null : books.get(new Random().nextInt(size));
}

public void addReview(int uid, String reviewerName, int rating, String comments) {
Book book = em.find(Book.class, uid);
if (book == null)
throw new ObjectNotFoundException("Can't find book with id ["+uid+"]");
Reviewer reviewer = em.find(Reviewer.class, reviewerName);
if (reviewer == null) {
reviewer = new Reviewer(reviewerName, Person.Gender.MALE);
}
Review review = reviewer.review(book, rating, comments);
em.persist(reviewer);
listener.reviewAdded(review);
}
It is injected an EntityManager. In the web module, the servlet will use it as in the follows:
public class EJBRefSampleClientServlet extends HttpServlet {
@EJB
private examples.ejb.ejb30.ejbref.Process service_;
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
......
service_.addReview(uid, "guest", rate, comments);
......
}
We can see that here the servlet uses dependency injection to get the EJB. Local clients can use JNDI lookup as well as in the using remote EJB case, but dependency injection results in simpler code. See[4] for some examples.

Integration with JPA

In both of the scenarios above, the JSP or the Servlet does not use the JPA directly. Instead, the JSP/Servlet just uses a session bean and calls the business mehtod of the bean. The session bean implements the service by using the injected EntityManager from the J2EE container. But can the JSP/Servlet use the JPA directly? The answer is yes. But be very careful here. Since EntityManager is not thread-safe, the servlet should be injected an EntityManager and use it directly. There are two ways.
  1. Use @PersistenceUnit to inject an EntityManagerFactory into the servlet. This is to use application managed entity manager. In the service() method, you can then use the code like the following:
    EntityManager em = emf.createEntityManager();
    try{
      beginTransaction();
      em.persist(......)
      commitTransaction();
    }catch(Exception e){
      //rollback code
    }finally{
    em.close();
    }
    
  2. Use @PersistenceContext to declare a dependency on an EntityManager in the servlet and use JNDI to look up the EntityManager. You'll also need to write code to begin/commit/rollback the transaction.

How to Write a Session Bean

To create a remote session bean, use one of the following:
  1. Decorate the business interface with @Remote:
    @Remote
    public interface InterfaceName{...}
  2. Decorate the bean class with @Remote, specifying the business interface or interfaces:
    @Remote(InterfaceName.class)
    public class BeanName implements InterfaceName{...}

To create a local session bean, use one of the following:
  1. Create the business interface and the bean class without the @Remote or @Local annotation.
  2. Decorate the business interface with @Local:
    @Local
    public interface InterfaceName{...}
  3. Decorate the bean class with @Local, specifying the business interface or interfaces:
    @Local(InterfaceName.class)
    public class BeanName implements InterfaceName{...}
Although it is uncommon, it is possible for a session bean to allow both remote and local access. In this case, you can do one of the following:
  1. Use two business interfaces, one is decorated with @Remote and the other is decorated with @Local. Let the bean implement both these two interfaces
  2. The bean class explicitly designates the business interfaces by using @Remote and @Local.
Note that the same business interface cannot be both a local and remote business interface. See [5]. To create a stateless session bean for web service client, there are a few requirements. The bean is now an endpoint implementation class. Some requirements are:
  • The class must be annotated with either the javax.jws.WebService or javax.jws.WebServiceProvider.
  • The endpoint class must be annotated @Stateless.
  • The class must have a default public constructor.
  • Business methods that are exposed to web service clients must be annotated @WebMethod
The following is a sample from Java EE5 tutorial:
@Stateless
  @WebService
  public class HelloServiceBean{
    private String message = "Hello";
    public void HelloServiceBean(){}

    @WebMethod
    public String sayHello(String name){ 
      return message + name + ".";
    }
  }
There are some requirements on business methods.
  • If the bean allows remote access through a remote business interface, the arguemnts and return types must be legal types for the Java RMI API.
  • If the bean is a web service endpoint, the arguments and return types for the methods annotated @WebMethod must be legal types for JAX-WS.
  • The modifier must not be static or final.
The code of the ReviewManagerBean is the following:
@Stateless(mappedName="ReviewService")
@Remote(ReviewService.class)
public class ReviewManagerBean extends ReviewServiceImpl
implements ReviewService {
@PersistenceContext(unitName = "reviewSession")
private EntityManager em;
@EJB ReviewListener listener;

@PostConstruct
public void init(){
  // inject the EnityManager.
  injectEntityManager(em);
  addReviewAddedListener(listener);
}
The code contains the ReviewListener. This is not relevent to our topic now. It is used for other purpose. The important thing is how the session bean uses the JPA. The ReviewManagerBean lets the container to manage the transaction. The unitName is from persistence.xml. Notice that the transaction-type is JTA.
<?xml version="1.0"?>

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">

<persistence-unit name="reviewSession" transaction-type="JTA">
<class>examples.ejb.ejb30.domain.Artist</class>
......
<properties>
<property name="kodo.ConnectionURL" value="jdbc:pointbase:server://localhost:9092/demo"/>
<property name="kodo.ConnectionDriverName" value="com.pointbase.jdbc.jdbcUniversalDriver"/>
<property name="kodo.ConnectionUserName" value="examples"/>
<property name="kodo.ConnectionPassword" value="examples"/>
<property name="kodo.jdbc.SynchronizeMappings" value="refresh"/>
</properties>
</persistence-unit>
</persistence>
ReviewManagerBean is injected an EntityManager instead of an EntityManagerFactory. The init() method calls injectEntityManager(em). But this is actually not needed. The reason why the ejb3 example code does this is that it wants to utilize the code ReviewServiceImpl which is for both container managed and application managed service. If you look at the code, you can see that the injected private instance variable em of ReviewManagerBean is not used directly. It will first get injected when the bean is created. And then the injectEntityManager method passes this instance to the parent class ReviewServiceImpl where the em is assigned to the private instance variable _em of ReviewServiceImpl. So if ReviewServiceImpl is not used, then the injection of em is enough. There would be no need to call injectEntityManager.
public void injectEntityManager(EntityManager em) {
_em = em;
}
The business method newArtist can be the following:
public Artist newArtist(String name) {
  if (name == null || name.trim().length() == 0)
   throw new RuntimeException("[" + name + "] can not be an Artist's name");
  EntityManager em = _em
  Artist artist = getPerson(Artist.class, name);
  if (artist == null) {
    artist = new Artist(name);
    em.persist(artist);
  } else {
    throw new ObjectExistedException("Artist named [" + name + "] exists");
  }
  return artist;
}

public  T getPerson(Class personType, String name) {
  return _em.find(personType, name);
}

Notes on Different Name Attributes in Anotation

  1. In @Stateless, @name() is the annotation equivalent of <ejb-name> in ejb-jar.xml. If no @Stateless name() is specified, it defaults to the unqualified bean class name.
  2. In @Stateless, mappedName() is used to assign a global JNDI name to the bean's remote interface.
  3. The mappedName only applies to Remote interface. If you're resolving a local interface the caller must be in the same application. For that, the beanName()/ejb-link should be used.
  4. There is a portability issue about mappedName. If a vendor doesn't support is you can resolve the EJB dependency using the vendor's config file without changing the source code of the standard .xml.
  5. If the .war file is not in the same .ear file as the ejb-jar, you have to use the Remote interface.
  6. beanName() and ejb-link only apply if the target remote ejb is defined within the same application as the referencing component.
See references [1] and [2] for more details.

References

[1] http://forums.sun.com/thread.jspa?threadID=5332558
[2] https://glassfish.dev.java.net/javaee5/ejb/EJB_FAQ.html#EJB_ejb-ref_ejb_local_ref
[3] http://weblogs.java.net/blog/ss141213/archive/2005/12/dont_use_persis_1.html
[4] http://www.developer.com/java/ejb/article.php/10931_3650661_5/EJB-3-Session-Beans.htm
[5] The Java EE5 Tutorial from Sun Microsystems.

How to Use Java Persistence API without other EJB Stuff

In EJB 3.0, the Entity beans are replaced by the Java Persistence API entities. An application can use JPA without touching any EJB stuff. The following is such an example about how to use JPA in a web application.

JPA Configuration

The JPA needs a file persistence.xml. Note that the transaction-type is "RESOURCE_LOCAL"
<?xml version="1.0"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
 version="1.0">
 <persistence-unit name="myService"
  transaction-type="RESOURCE_LOCAL">
  <class>examples.ejb.ejb30.domain.Item</class>
  <properties>
   <property name="xyz.ConnectionURL" value="..." />
   <property name="xyz.ConnectionDriverName" value="..." />
   <property name="xyz.ConnectionUserName" value="examples" />
   <property name="xyz.ConnectionPassword" value="examples" />
   <property name="xyz.jdbc.SynchronizeMappings"
    value="refresh" />
  </properties>
 </persistence-unit>
</persistence>

Web Module Configuration

In web.xml, a listener is needed:
<listener>
  <listener-class>example.ejb30.web.MyContextInitializer</listener-class>
</listener>
When initialized, the initializer will create an EntityManagerFactory and put it into the ServletContext. The code can be like the following. Notice that there is no object injection and JNDI lookup here.
public class MyContextInitializer implements ServletContextListener {
  private EntityManagerFactory emf;

  public void contextInitialized(ServletContextEvent servletContextEvent) {
    emf = Persistence.createEntityManagerFactory("myService");
    ......
    ServletContext context = servletContextEvent.getServletContext();
    context.setAttribute("coolService",emf);
}

  public void contextDestroyed(ServletContextEvent servletContextEvent) {
    //close EMF to free these resources maintained by EntityManagerFactory instances.
    emf.close();
  }
}

Using the JPA in Application Code

In a jsp file, it can be used as follows:
<%
EntityManagerFactory emf = (EntityManagerFactory) application.getAttribute("coolService");
MyServiceImpl service = new MyServiceImpl(emf);

service.doSomeBusiness();

%>
The method doSomeBusiness() needs to manage the transaction. The following is a sample method:
public Artist newArtist(String name) {
  if (name == null || name.trim().length() == 0)
   throw new RuntimeException("[" + name + "] can not be an Artist's name");
  EntityManager em = getEntityManager();
  beginTransaction();
  Artist artist = getPerson(Artist.class, name);
  if (artist == null) {
    artist = new Artist(name);
    em.persist(artist);
    commit();
  } else {
    rollback();
    throw new ObjectExistedException("Artist named [" + name + "] exists");
  }
  return artist;
}

protected final EntityManager beginTransaction() {
  EntityManager em = getEntityManager();
  if (!_isManaged && !em.getTransaction().isActive())
    em.getTransaction().begin();
  return em;
}

protected final void commit() {
  if (_isManaged)
    return;
  EntityManager em = getEntityManager();
  if (em.getTransaction().isActive())
    em.getTransaction().commit();
}

protected final void rollback() {
  if (_isManaged)
    return;
  EntityManager em = getEntityManager();
  if (em.getTransaction().isActive())
    em.getTransaction().rollback();
}
The tricky thing is how the method getEntityManager() is implemented by MyServiceImpl. The
weblogic 10 ejb3 example uses the following:
private final EntityManager getEM() {
  if (_em != null)
    return _em;
  if (_thread == null)
    _thread = new ThreadLocal<EntityManager>();
  EntityManager em = _thread.get();
  if (em != null)
    return em;
  if (_emf == null)
    return null;
  em = _emf.createEntityManager();
  _thread.set(em);
  return em;
}
Notice that ThreadLocal is used here. I think this is to ensure that only one EntityManager is used in the thread. And this is to resolve the thread safety issue for EntityManager because EntityManager is not thread-safe.

Tuesday, September 22, 2009

Various Springframework contexts in the J2EE application

A J2EE application can have EJB modules and web modules. These modules can integrate with the Springframework.

Integration of EJB and Springframework

Spring uses injection. The first obvious question is: How do the EJBs use the beans configured in Spring?
The answer seems to be that each Spring EJB bean has a private
instance variable beanFactoryLocator. This variable will store all the bean definitions from the various Spring xml files. (Note: It is a waste if there are many Spring EJBs. The javadoc below of AbstractEnterpriseBean says there is an option to use of a shared ApplicationContext between multiple EJBs.)
As a result of this bean configuration, the ejb-jar.xml will typically have the following for the EJB to indicate the path to the xml files to load the spring beans from:
<message-driven>
 <ejb-name>mdb1</ejb-name>
 <ejb-class>com.foo.Mdbean1</ejb-class>
 ......
 <env-entry>
  <env-entry-name>ejb/BeanFactoryPath</env-entry-name>
  <env-entry-type>java.lang.String</env-entry-type>
  <env-entry-value>
   classpath*:spring/fooContext.xml
  </env-entry-value>
 </env-entry>
 ......
</message-driven>
In the above, the value "ejb/BeanFactoryPath" should be written as is. It is hard-coded in the Spring class AbstractEnterpriseBean:
public static final String BEAN_FACTORY_PATH_ENVIRONMENT_KEY = "java:comp/env/ejb/BeanFactoryPath";
But this key might be able to be customized.

Now there is a house to store those beans. The outside code will need to interact with this house. So the next question is: How will the business logic use the beans in this house?
The answer seems to be that a bean in the house will be looked up using the bean factory and the bean name. The bean factory here refers to that bean "house". The following can be some code in some method of Mdbean1:
this.proxy = this.getBeanFactory().getBean(String
.valueOf(this.fooObjectLocator.getObject())));
The locator is a JndiObjectFactoryBean and is created in the constructor of the EJB. When the locator is created, you also set a jndi name. The following is some code from the constructor of Mdbean1.
if (this.fooObjectLocator == null)
    {
      this.fooObjectLocator = new JndiObjectFactoryBean();
      this.fooObjectLocator.setJndiName(FOO_OBJECT_PATH_ENVIRONMENT_KEY);
      try
      {
        this.fooObjectLocator.afterPropertiesSet();
      }
      catch (NamingException e)
      {
        LOGGER.fatal("OBJECT NOT FOUND[" + e.getMessage() + "]", e);
      }
    }
The spring EJBs inherit the following class:
public abstract class AbstractEnterpriseBean implements EnterpriseBean
{
  public static final String BEAN_FACTORY_PATH_ENVIRONMENT_KEY = "java:comp/env/ejb/BeanFactoryPath";

  /**
   * Helper strategy that knows how to locate a Spring BeanFactory (or
   * ApplicationContext).
   */
  private BeanFactoryLocator beanFactoryLocator;

  /** factoryKey to be used with BeanFactoryLocator */
  private String beanFactoryLocatorKey;

   ......

  void loadBeanFactory() throws BeansException
  {
    if (this.beanFactoryLocator == null)
    {
      this.beanFactoryLocator = new ContextJndiBeanFactoryLocator();
    }
    if (this.beanFactoryLocatorKey == null)
    {
      this.beanFactoryLocatorKey = BEAN_FACTORY_PATH_ENVIRONMENT_KEY;
    }

    this.beanFactoryReference = this.beanFactoryLocator
        .useBeanFactory(this.beanFactoryLocatorKey);

    // We cannot rely on the container to call ejbRemove() (it's skipped in
    // the case of system exceptions), so ensure the the bean factory
    // reference is eventually released.
    WeakReferenceMonitor.monitor(this,
        new BeanFactoryReferenceReleaseListener(this.beanFactoryReference));
  }
   .....
}
The javadoc of this class has the following:

* Provides a standard way of loading a Spring BeanFactory. Subclasses act as a
* facade, with the business logic deferred to beans in the BeanFactory. Default
* is to use a {@link org.springframework.context.access.ContextJndiBeanFactoryLocator},
* which will initialize an XML ApplicationContext from the class path (based on a JNDI
* name specified). For a different locator strategy, setBeanFactoryLocator
* may be called (before your EJB's ejbCreate method is invoked,
* e.g. in setSessionContext). For use of a shared ApplicationContext between
* multiple EJBs, where the container class loader setup supports this visibility, you may
* instead use a {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator}.
* Alternatively, {@link #setBeanFactoryLocator} may be called with a custom implementation
* of the {@link org.springframework.beans.factory.access.BeanFactoryLocator} interface.


Various Spring contexts for EJB module, Web module, and Servlet, and their relationship

1. The ejb Mdbean1 has its Spring Context(the bean factory). For Spring message driven bean and stateless session bean, this bean factory is created in the ejb life cycle method ejbCreate(). Both the Spring class AbstractMessageDrivenBean and AbstractStatelessSessionBean have the following method:
public void ejbCreate() throws CreateException {
  loadBeanFactory();
  onEjbCreate();
}
The xml files that contain the Spring beans are defined in the ejb-jar.xml file:
<env-entry>
<env-entry-name>ejb/BeanFactoryPath</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>
classpath*:spring/fooContext.xml
</env-entry-value>
</env-entry>
This Spring context is attached to Mdbean1 as an instance variable of type BeanFactoryLocator.

2. The web module has three Spring contexts. These contexts have nothing to do with the Spring context in the ejb module. The three contexts are:

(1) the parent context of the root web application context. It is created via the Spring ContextLoaderListener and the context parameters in the web.xml:
<context-param>
<param-name>locatorFactorySelector</param-name>
<param-value>classpath*:spring/beanRefContext.xml</param-value>
</context-param>
<context-param>
<param-name>parentContextKey</param-name>
<param-value>fooID</param-value>
</context-param>
......
......
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
Note that the param-name "locatorFactorySelector" is exactly the hard-coded value
LOCATOR_FACTORY_SELECTOR_PARAM in the code
servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);
And the param-name "parentContextkey" is exactly the hard-coded value LOCATOR_FACTORY_KEY_PARAM
in the code
servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);
The two hard-coded values are defined in the class org.springframework.web.context.ContextLoader. There might be customized way to specify the key and the selector.
This context is created at the same time when the root web application context is created. And it is saved to the instance variable of the root web application context as its parent conetxt.
ApplicationContext parent = loadParentContext(servletContext);
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
Note: if the param "parentContextKey" is not specified in web.xml, the context will not be created. It will be null.
The actual method that creates the context is the API method from the javax interface ServletContextListener that the Spring class ContextLoaderListener implements:
public void contextInitialized(ServletContextEvent event) {
  this.contextLoader = createContextLoader(); 
  this.contextLoader.initWebApplicationContext(event.getServletContext());
}
(2) the root web application context. It is created via the same ContextLoaderListener that creates the parent context in (1) and the following parameter in the web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/foo1.xml /WEB-INF/foo-2.xml</param-value>
</context-param>
As shown by the code of ContextLoader in (1), it uses the context in (1) as its parent context. The context is attached to the ServletContext using a specific attribute.

(3) the context created by the Spring DispatcherServlet. It is created via the following in the web.xml file:
<servlet>
<servlet-name>xyz</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
The DispatcherServlet uses the servlet life cycle method init() to create the context and saves it to the private variable of type WebApplicationContext. This context uses the context created in (2) as its parent context. It gets the parent context using the ServletContext and the specific attribute. It uses the namespace to load the bean xml files. The servlet name in web.xml is "xyz". So it sets the namespace to "xyz-servlet". And by default, it will load the bean xml files from WEB-INF/xyz-servlet.xml.

How is the parent context used?

In the cases of the root web application context and the servlet Spring context, they have parent context and store it in the instance variable. What is the use of this parent context? I have not done any research on this. But I guess that when they look up a bean, they will first try to find it from the beans loaded through their own xml files. If they can not find the bean, then they will use the parent context to get it.

Some Important Beans in the Spring XML Configuration File

Beans for Transaction Management
Spring has its own transaction management mechanism. The following is a sample configuration in the bean xml file.
<bean id="myTransactionManager"
 class="org.springframework.transaction.jta.JtaTransactionManager">
</bean>
<bean id="matchAllWithPropReq"
 class="org.springframework.transaction.interceptor.MatchAlwaysTransactionAttributeSource">
 <property name="transactionAttribute">
  <value>PROPAGATION_REQUIRED,-MyCheckedException</value>
 </property>
</bean>

<bean id="matchAllTxInterceptor"
 class="org.springframework.transaction.interceptor.TransactionInterceptor">
 <property name="transactionManager">
  <ref bean="myTransactionManager" />
 </property>
 <property name="transactionAttributeSource">
  <ref bean="matchAllWithPropReq" />
 </property>
</bean>
The xml bean file that contains the business objects can have the following:
<bean id="autoProxyCreator"
 class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
 <property name="interceptorNames">
  <list>
   <idref bean="matchAllTxInterceptor" />
   <idref bean="other interceptors, can be user defined" />
  </list>
 </property>
 <property name="beanNames">
  <list>
   <idref local="foo1Bo" />
   <idref local="foo2Bo" />
   ......
   <idref local="foonBo" />
  </list>
 </property>
</bean>
Beans for View
The following bean is an example bean that maps the URLs to spring controllers in the Spring MVC framework.
<bean id="myUrlMapping"
 class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
 <property name="mappings">
  <props>
   <prop key="/foo1.htm">bar1Controller</prop>
   <prop key="/foo2.htm">bar2Controller</prop>
   ......
  </props>
 </property>
</bean>
The following is an example bean for message bundle.
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>messages</value>
</property>
</bean>
The following is an example bean used for view resolver.
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename">
<value>views</value>
</property>
</bean>
In the WEB-INF/classes directory, you put two files messages.properties and views.properties. They can look like the following.
messages.properties:
messageId1=default message for Id1.
messageWithArguments=this message has arg1 {0} and arg2 {1}.
views.properties:
viewName1.(class)=org.springframework.web.servlet.view.InternalResourceView
viewName1.url=/jsp/view1.jsp
viewName2.(class)=org.springframework.web.servlet.view.InternalResourceView
viewName2.url=/jsp/view2.jsp

Wednesday, September 16, 2009

Configuration of Java Logger

Which logger is selected?

A commonly used logger is Apache Log4J. But there are also other loggers. And there are many default configuration which make the whole thing mysterious. Let’s take a look. In this analysis, we are using Apache commons-logging-1.0.4.jar and log4j-1.2.8.jar.

A typical first call to the logger is the following as in a class BatchJob.java:

public class BatchJob
{
  private static Log logger = LogFactory.getLog(BatchJob.class);
  ...
}

Most of the secrets are implemented in the following class
org.apache.commons.logging.impl.LogFactoryImpl extends LogFactory.
The class LogFactory is in the same package. The javadoc of the class LogFactoryImpl reveals how the logging implementation is selected. The javadoc is the following:

Concrete subclass of LogFactory that implements the following algorithm to dynamically select a logging implementation class to instantiate a wrapper for.
  1. Use a factory configuration attribute named org.apache.commons.logging.Log to identify the requested implementation class.
  2. Use the org.apache.commons.logging.Log system property to identify the requested implementation class.
  3. If Log4J is available, return an instance of org.apache.commons.logging.impl.Log4JLogger.
  4. If JDK 1.4 or later is available, return an instance of org.apache.commons.logging.impl.Jdk14Logger.
  5. Otherwise, return an instance of org.apache.commons.logging.impl.SimpleLog.
If the selected Log implementation class has a setLogFactory() method that accepts a LogFactory parameter, this method will be called on each newly created instance to identify the associated factory. This makes factory configuration attributes available to the Log instance, if it so desires. This factory will remember previously created Log instances for the same name, and will return them on repeated requests to the getInstance() method. This implementation ignores any configured attributes.

The selection algorithm will become clearer if you look at the actual code in the class LogFactoryImpl:

/**
* Return the fully qualified Java classname of the  Log 
* implementation we will be using.
*/
protected String getLogClassName() {
// Return the previously identified class name (if any)
if (logClassName != null) {
   return logClassName;
}
logClassName = (String) getAttribute(LOG_PROPERTY);
if (logClassName == null) { // @deprecated
   logClassName = (String) getAttribute(LOG_PROPERTY_OLD);
}
if (logClassName == null) {
  try {
    logClassName = System.getProperty(LOG_PROPERTY);
  } catch (SecurityException e) {;}
}
if (logClassName == null) { // @deprecated
  try {
    logClassName = System.getProperty(LOG_PROPERTY_OLD);
  } catch (SecurityException e) {;}
}
if ((logClassName == null) && isLog4JAvailable()) {
  logClassName = "org.apache.commons.logging.impl.Log4JLogger";
}
if ((logClassName == null) && isJdk14Available()) {
  logClassName = "org.apache.commons.logging.impl.Jdk14Logger";
}
if ((logClassName == null) && isJdk13LumberjackAvailable()) {
  logClassName = 
     "org.apache.commons.logging.impl.Jdk13LumberjackLogger";
}
if (logClassName == null) {
  logClassName = "org.apache.commons.logging.impl.SimpleLog";
}
return (logClassName);
}

In the test case that I executed with the batch job, if you put the log4j-1.2.8.jar in the classpath, the logger class used will be org.apache.commons.logging.impl.Log4JLogger. Otherwise, the logger class is org.apache.commons.logging.impl.Jdk14Logger.

How to configure log attributes.

One mistake that I made was to put something like the following in the command line:
-Dlog4j.configuration=file:/c:/workspaces/appName/src/config/jobs/job1.properties
Notice: In eclipse, the -Dlog4j.configuration must be set as VM arguments, not the Program arguments.
The purpose is for the logger to use the configuration in the job1.properties file. But it does not work! Finally I found that this is because I did not put log4j-1.2.8.jar on the classpath. Obviously the log4j.configuration option only works for Log4JLogger in log4j-1.2.8.jar, not other logger class such as Jdk14Logger !

Besides the command line option for specifying log attributes, a more familiar way is to use the log4j.properties file. From my test results, if you use Log4JLogger, you can put log4j.properties on the classpath, and it will be picked up by the logger. But what if you also specify the commandline –Dlog4j.configuration parameter? The answer is that the command line property file will override the log4j.properties file. This is not a surprise. I believe this is a convention of Java. For example, if you specify classpath using –cp option in the command line, it will override the environment variable CLASSPATH.

Advanced class loader stuff

Now the next thing that is interesting is how the LogFactoryImpl determines if log4j is available to use. The actual method is the following:

/**
* Is a Log4J implementation available?
*/
protected boolean isLog4JAvailable() {
  try {
    loadClass("org.apache.log4j.Logger");
    loadClass("org.apache.commons.logging.impl.Log4JLogger");
    return (true);
  } catch (Throwable t) {
    return (false);
  }
}
As a comparison, the following method determines if the jdk logger is available:

/**
* Return true if JDK 1.4 or later logging
* is available.  Also checks that the Throwable class
* supports getStackTrace(), which is required by
* Jdk14Logger.
*/
protected boolean isJdk14Available() {
  try {
    loadClass("java.util.logging.Logger");
    loadClass("org.apache.commons.logging.impl.Jdk14Logger");
    Class throwable = loadClass("java.lang.Throwable");
    if (throwable.
         getDeclaredMethod("getStackTrace", null) == null) {
      return (false);
    }
  return (true);
  } catch (Throwable t) {
    return (false);
  }
}.
In all cases, the essential method called is loadClass(), which is the following:

private static Class loadClass( final String name )
throws ClassNotFoundException
{
  Object result = AccessController.doPrivileged(
    new PrivilegedAction() {
      public Object run() {
        ClassLoader threadCL = getContextClassLoader();
        if (threadCL != null) {
          try {
            return threadCL.loadClass(name);
          } catch( ClassNotFoundException ex ) {
            // ignore
          }
        }
        try {
          return Class.forName( name );
        } catch (ClassNotFoundException e) {
          return e;
        }
      }
    });
  if (result instanceof Class)
    return (Class)result;
  throw (ClassNotFoundException)result;
}
Notice that a general programming rule is that we should not use exception for decision making. But in the special case here, we are using the ClassNotFoundException to determine if the logger implementation class is available.

The above method uses the following method, which is also in LogFactoryImpl:

/**
* Return the thread context class loader if available.
* Otherwise return null.
*
* The thread context class loader is available for JDK 1.2
* or later, if certain security conditions are met.
*
* @exception LogConfigurationException if a suitable class 
* loader cannot be identified.
*/
protected static ClassLoader getContextClassLoader()
throws LogConfigurationException
{
  ClassLoader classLoader = null;
  try {
    // Are we running on a JDK 1.2 or later system?
    Method method = Thread.class.
           getMethod("getContextClassLoader", null);
    // Get the thread context class loader (if there is one)
    try {
      classLoader = (ClassLoader)method.
           invoke(Thread.currentThread(), null);
    } catch (IllegalAccessException e) {
      throw new LogConfigurationException
            ("Unexpected IllegalAccessException", e);
    } catch (InvocationTargetException e) {
  /**
   * InvocationTargetException is thrown by 
   * 'invoke' when the method being invoked 
   * (getContextClassLoader) throws an exception.
   *
   * getContextClassLoader() throws 
   * SecurityException when the context class 
   * loader  isn't an ancestor of the calling class's 
   * class loader, or if security permissions are 
   * restricted.
   *
   * In the first case (not related), we want to ignore
   * and keep going.  We cannot help but also ignore 
   * the  second with the logic below, but other calls 
   * elsewhere (to obtain a class loader) will trigger 
   * this exception where we can make a distinction.
   */
       if (e.getTargetException() instanceof 
            SecurityException) {
         ;  // ignore
       } else {
   // Capture 'e.getTargetException()' exception for 
   // details alternate: log 'e.getTargetException()', 
   // and pass back 'e'.
         throw new LogConfigurationException
            ("Unexpected InvocationTargetException", 
              e.getTargetException());
       }
    }
  } catch (NoSuchMethodException e) {
    // Assume we are running on JDK 1.1
    classLoader = LogFactory.class.getClassLoader();
  }
  // Return the selected class loader
  return classLoader;
}

Configuration of java.util.logging.Logger

Some applications use java.util.logging.Logger. For example, the reference implementation of JSF from sun jsf-impl-1.2.jar uses this logger in the source code. How to configure it?
By default, this logger uses the configuration file in %JAVA_HOME%/jre/lib/logging.properties. In order to customize this, you can create a properties file, say C:\workshop\foo\logs\logging.properties. Then you need pass this as a parameter when invoking java:
-Djava.util.logging.config.file=C:\workshop\foo\logs\logging.properties 

The following is a sample file logging.properties:
#logging.properties
handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
.level=FINE
java.util.logging.FileHandler.pattern = C:/workshop/foo/logs/java%u.log
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter

This property file will cause the log to go to the file C:/workshop/foo/logs/java%u.log and the log level is FINE. Here %u is a count number. It will be 0, etc. See http://docs.oracle.com/javase/1.4.2/docs/api/java/util/logging/FileHandler.html for details about how to configure the location of this file.

Merge java.util.log into log4j

Say you are using Log4j in your application. And the application uses a third party library that uses java.util.logging. You can configure for each log. But the log messages will go to separate log files. You would like these log messages to go to the same log file in the time order that they are generated. How to do that? A web application using jsf-impl-1.2.jar of Sun is such an application because jsf-impl-1.2.jar uses java.util.logging. The following is one solution.
You can delegate the log from java.util.log to log4j. The website http://wiki.apache.org/myfaces/Trinidad_and_Common_Logging has a class JavaLoggingToCommonLoggingRedirector.java. To use this class in the web application. You can do the following.
  1. Copy the class JavaLoggingToCommonLoggingRedirector.java into your project. For example, you can have the following:
    package com.foo.example.util;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.logging.Handler;
    import java.util.logging.Level;
    import java.util.logging.LogManager;
    import java.util.logging.LogRecord;
    import java.util.logging.Logger;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    /**
     * Writes JDK log messages to commons logging.
     */
    public class JavaLoggingToCommonLoggingRedirector {
            static JDKLogHandler activeHandler;
    
            /**
             * Activates this feature.
             */
            public static void activate() {
                    try {
                            Logger rootLogger = LogManager.getLogManager().getLogger("");
                            // remove old handlers
                            for (Handler handler : rootLogger.getHandlers()) {
                                    rootLogger.removeHandler(handler);
                            }
                            // add our own
                            activeHandler = new JDKLogHandler();
                            activeHandler.setLevel(Level.ALL);
                            rootLogger.addHandler(activeHandler);
                            rootLogger.setLevel(Level.ALL);
                            // done, let's check it right away!!!
    
                            Logger.getLogger(JavaLoggingToCommonLoggingRedirector.class.getName()).info("activated: sending JDK log messages to Commons Logging");
                    } catch (Exception exc) {
                            LogFactory.getLog(JavaLoggingToCommonLoggingRedirector.class).error("activation failed", exc);
                    }
            }
    
            public static void deactivate() {
                    Logger rootLogger = LogManager.getLogManager().getLogger("");
                    rootLogger.removeHandler(activeHandler);
    
                    Logger.getLogger(JavaLoggingToCommonLoggingRedirector.class.getName()).info("dactivated");
            }
    
            protected static class JDKLogHandler extends Handler {
                    private Map<String, Log> cachedLogs = new ConcurrentHashMap<String, Log>();
    
                    private Log getLog(String logName) {
                            Log log = cachedLogs.get(logName);
                            if (log == null) {
                                    log = LogFactory.getLog(logName);
                                    cachedLogs.put(logName, log);
                            }
                            return log;
                    }
    
                    @Override
                    public void publish(LogRecord record) {
                            Log log = getLog(record.getLoggerName());
                            String message = record.getMessage();
                            Throwable exception = record.getThrown();
                            Level level = record.getLevel();
                            if (level == Level.SEVERE) {
                                    log.error(message, exception);
                            } else if (level == Level.WARNING) {
                                    log.warn(message, exception);
                            } else if (level == Level.INFO) {
                                    log.info(message, exception);
                            } else if (level == Level.CONFIG || level == Level.FINE ) {
                                    log.debug(message, exception);
                            } else {
                                    log.trace(message, exception);
                            }
                    }
    
                    @Override
                    public void flush() {
                            // nothing to do
                    }
    
                    @Override
                    public void close() {
                            // nothing to do
                    }
            }
    }
    Note that I modified the class a little bit. I added the code "|| level == Level.FINE" to log FINE messages as DEBUG messages.
  2. In web.xml, add the following:
    <listener>
      <listener-class>
    com.foo.example.util.LogUtilListener</listener-class>
     </listener>
    
    The listener class is the following:
    package com.foo.example.util;
    
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    
    public class LogUtilListener implements ServletContextListener {
    
     public void contextInitialized(ServletContextEvent arg0) {
      JavaLoggingToCommonLoggingRedirector.activate();
     }
    
     public void contextDestroyed(ServletContextEvent arg0) {
      JavaLoggingToCommonLoggingRedirector.deactivate();
    
     }
    }
    
  3. Modify the log4j.properties to log the message from the third-party library. In our case, the library is jsf-impl-1.2.jar. Assume that you want to see the log messages from the package com.sun.faces.lifecycle in jsf-impl-1.2.jar. You can add the following:
    log4j.logger.javax.enterprise.resource.webcontainer.jsf=DEBUG,fileout
    
    Note that if you use
    log4j.logger.com.sun.faces=DEBUG,fileout 
    then it won't work even though the class LifecycleImpl is in the package com.sun.faces.lifecycle. This is surprising. But after looking at the source code of LifecycleImpl.java, you can see the reason. In that class, the logger is defined as follows:
    private static Logger logger = Util.getLogger(Util.FACES_LOGGER 
                + Util.LIFECYCLE_LOGGER);
    
    And Util.FACES_LOGGER has the value "javax.enterprise.resource.webcontainer.jsf" ! That is why we need to use this value in log4j.properties. You can also use the complete name "Util.FACES_LOGGER + Util.LIFECYCLE_LOGGER" which is equal to "javax.enterprise.resource.webcontainer.jsf.lifecycle". But since "javax.enterprise.resource.webcontainer.jsf" is a "parent" logger name of it, so that will cover the subpackages.
In the above example, I am not sure why LifecycleImpl uses "javax.enterprise.resource.webcontainer.jsf" instead of its package name as the logger name.
On the other hand, if you just use java.util.logging and check the log file, you can see that the log messages contain the package name and even the method name! The following is the sample:
Jan 30, 2012 3:03:07 PM com.sun.faces.lifecycle.LifecycleImpl execute
FINE: Skipping rest of execute() because of a reload
Jan 30, 2012 3:03:09 PM com.sun.faces.lifecycle.LifecycleImpl render
FINE: render(com.icesoft.faces.context.BridgeFacesContext@d0da01)
Jan 30, 2012 3:03:09 PM com.sun.faces.lifecycle.LifecycleImpl phase
FINE: phase(RENDER_RESPONSE 6,com.icesoft.faces.context.BridgeFacesContext@d0da01)
Jan 30, 2012 3:03:09 PM com.sun.faces.lifecycle.RenderResponsePhase execute
FINE: Entering RenderResponsePhase
Jan 30, 2012 3:03:09 PM com.sun.faces.lifecycle.RenderResponsePhase execute
FINE: About to render view /home.jspx
The corresponding log code in java in the class LifeCycleImpl.java is as follows:
public class LifecycleImpl extends Lifecycle {
...
  public void execute(FacesContext context) throws FacesException {
     ......
    for (int i = 1; i < phases.length; i++) { // Skip ANY_PHASE placeholder

            if (context.getRenderResponse() ||
                context.getResponseComplete()) {
                break;
            }

            phase((PhaseId) PhaseId.VALUES.get(i), phases[i], context);

            if (reload((PhaseId) PhaseId.VALUES.get(i), context)) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.fine("Skipping rest of execute() because of a reload");
                }
                context.renderResponse();
            }
        }

    }
...
}
As the comparison, the log messages from log4j after the log merge are as follows:
2012-02-01 16:29:33,219 DEBUG [lifecycle]: Skipping rest of execute() because of a reload
2012-02-01 16:29:33,797 DEBUG [lifecycle]: render(com.icesoft.faces.context.BridgeFacesContext@8673f8)
2012-02-01 16:29:33,797 DEBUG [lifecycle]: phase(RENDER_RESPONSE 6,com.icesoft.faces.context.BridgeFacesContext@8673f8)
2012-02-01 16:29:33,797 DEBUG [lifecycle]: Entering RenderResponsePhase
2012-02-01 16:29:33,797 DEBUG [lifecycle]: About to render view /home.jspx
If you want to see the actual class name and method name of the log message from java.util.logging, you can modify the JDKLogHandler class:
public void publish(LogRecord record) {
  Log log = getLog(record.getLoggerName());
  String message = record.getMessage();
  Throwable exception = record.getThrown();
  String completeClassName = record.getSourceClassName();
  int pos = completeClassName.lastIndexOf(".");
  String className = completeClassName.substring(pos + 1);
  String methodName = record.getSourceMethodName();
  message = className + ":" + methodName + " " + message;
  Level level = record.getLevel();
              ...
}
Here we make use of the class java.util.logging.LogRecord. This class has the API that gives the class name and method name.

Note that the strategy used above to delegate java util log to log4j works for a single web application. If there are multiple web applications on the same server, the java util log in different web applications will be delegated to the same log file. To avoid this, you have to do something different. Reference[1] gives a solution for this.

References

  1. http://blog.mycila.com/2011/01/jdk-logging-per-webapp.html
  2. http://www.slf4j.org

Saturday, September 12, 2009

Blog Start

Today is Saturday. I created this google blog account. Toady's weather is mild. There is no rain. But it is sometimes cloudy and sometimes the sunshine come through the sky. I drove out in the morning and found that the top of some trees had already turned yellow or red. The fall season is coming.