Showing posts with label Java EE. Show all posts
Showing posts with label Java EE. Show all posts

Tuesday, October 18, 2011

SingleThreadModel : why you should not implement it and what happens if you do

According to the Servlet specifications, the SingleThreadModel interface is deprecated since the 2.4 version of the specs. The reason why it has been deprecated is that it cannot actually guarantee thread-safety, as its name suggests. But do you know what would the consequences be if you still have a servlet implement SingleThreadModel?

The side-effects of having a servlet implement SingleThreadModel are vendor-specific. Having worked with some legacy code deployed on a Tomcat 4.x container which contains a servlet that implements SingleThreadModel, I've noticed the following behavior : the container can processed a maximum of 20 simultaneous POST requests.

Explanations

Since true thread-safety cannot be achieved by implementing SingleThreadModel, each servlet container will have 2 possibilities for treating SingleThreadModel type servlets :

  • the container manages a servlet pool (2 simultaneous requests to a same servlet will actually be handled by 2 distinct instances of the requested servlet).
  • the container synchronizes the service( ) method of the requested servlet
Use-case : SingleThreadModel servlet deployed on Tomcat
 
Here's the code for the servlet we'll deployed onto any version of Tomcat for our test purpose :


public class TestServlet extends HttpServlet implements SingleThreadModel {
 private static int requestCounter = 0;
 
 @Override
 protected void doPost(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {
  
  doGet(req, resp);
 }
 
 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse resp)
   throws ServletException, IOException {
  
  try {
   System.out.println("Start processing request with servlet " 
                        + instance : " + this + "[CPT : " + ++requestCounter + "]");
   Thread.sleep(1000000); // simulate a uber long request
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
}

How to run the test?

Simply send 21 POST requests to the servlet and you'll see that the 21st request and the following ones aren't not processed by the container :



Where is this limit of 20 servlet instances configured?





Conclusion


Having a servlet implement SingleThreadModel is definitely a conception flaw that could lead to performance breakdown in the best case scenario, and to a deadlock in the worse case scenario.



Sunday, June 5, 2011

EJB 3 example - How to use Timer service (JBoss AS 6)

Through this post, I wanna show you how easy it is to use the timer service provided by EJB 3 compliant application server. The timer service is a very convenient feature that helps you schedule tasks that must be ran once or repeatedly. Besides, timers created with the timer service are persisted so that they're kept running when the server is restarted (useful for crash-recovery). As stated in the title, I use JBoss AS 6 to deploy my example but you could deploy yours on any other EJB 3 compliant application server.

Description of my example

In my example, I've implemented a stateless session bean that manage user subscriptions to a website. At each subscription, I'll start a timer that will check, every once in a while, whether the subscribed user is still active (for that purpose, every user record in my database will contain a column that contains the last connection date).

Java EE timer service : annotation or deployment descriptor?

As almost every service available in Java EE, you could configure timer service either with annotations or with the deployment descriptor. But you'll typically configure it with annotation because configuring it via the description descriptor will make you loose some flexibility : using annotation, you'll be able to start, cancel, define timer delay, and so on, from within your code.

Where to use timer service?

As for EJB 3 specifications, timer can be created only for stateless session bean and for message-driven bean.

How to use timer service ?

1° In order to create a timer, you'll need to get hold of an instance of TimerService. To do so, there are 3 possibilities : 
  • through dependency injection :  @resource private TimerService timerService;
  • through JNDI lookup
  • through EJBContext : @resource private EJBContext context; context.getTimerService( );
2° Specify the method within your bean, that will be executed by the timer. To do so, there are 2 possibilities
  • Implement a method with the following signature : public/protected void methodName (Timer timer) and annotate it with @Timeout.
  • Have your bean implement the TimedObject interface. You'll  then have to implement the public void ejbTimeout(Timer timer) method
3° Finally, you'll have to create a timer from within the bean. To do so, you can call methods such as createTimer(...) and createCalendarTimer(...) on the TimerServiceInstance.

Code example
package beans.userManagement;

import java.io.Serializable;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Date;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

import tools.StringManager;
import beans.dao.UserDao;
import beans.emailManagement.EmailManagement;
import dto.User;
import exceptions.EmailException;
import exceptions.UserManagementException;

/**
 * Session Bean implementation class UserSubscriptionManagementBean
 */
@Stateless
public class UserSubscriptionManagementBean implements UserSubscriptionManagementLocal, UserSubscriptionManagementRemote{
    private static final long NB_DAYS_BEFORE_REMOVAL = 30L;
    private static final long NB_DAYS_BEFORE_WARNING = 15L;
    private static final long MAXIMUM_INACTIVE_PERIOD_BEFORE_REMOVAL = 1000L * 3600L * 24L * NB_DAYS_BEFORE_REMOVAL; // maximum inactivity period = 30 days 
    private static final long MAXIMUM_INACTIVE_PERIOD_BEFORE_WARNING = 1000L * 3600L * 24L * NB_DAYS_BEFORE_WARNING;

    @EJB
    private UserDao userDao;
    @EJB
    private EmailManagement emailManager;
    @Resource
    private TimerService timerService;

    private User user;

    /**
     * Default constructor. 
     */
    public UserSubscriptionManagementBean() {}

    @Override
    public void subscribe(String email, String nickname, String password, 
            String firstName, String lastName, Date birthdate, Date subscriptionDate) throws UserManagementException {

        // insert new User in database
        ...
        
        userDao.insert(user);
        // starting a timer to cleanup inactive user / send warning
        createTimers();
    }

    private void createTimers(){
        timerService.createTimer(MAXIMUM_INACTIVE_PERIOD_BEFORE_WARNING, user.getEmail());
        timerService.createTimer(MAXIMUM_INACTIVE_PERIOD_BEFORE_REMOVAL, user);
    }

    @Timeout
    public void cleanupInactiveUsers(Timer timer){
        Serializable info = timer.getInfo();

        if(info != null){
            if(info instanceof User){ // deleting inactive user
                User userToCheck = userDao.find(((User)info).getEmail());

                if(userToCheck == null){
                    return;
                }
                
                Long inactivityPeriod = System.currentTimeMillis() - userToCheck.getLastConnectionDate().getTime();

                if(inactivityPeriod >= MAXIMUM_INACTIVE_PERIOD_BEFORE_REMOVAL){
                    userDao.delete(userToCheck);
                    StringBuilder object = new StringBuilder("Automatic unsubscription");
                    StringBuilder message = new StringBuilder();
                    message.append("Your Crowd Freighting account has been deleted due to " + NB_DAYS_BEFORE_REMOVAL + " days inactivity");
                    message.append("\n\nKind regards, \nThe Crowd Freighting Crew.");

                    try {
                        emailManager.sendEmail(new String[]{userToCheck.getEmail()}, 
                                               null, 
                                               null, 
                                               object.toString(), 
                                               message.toString());                    
                    } catch (EmailException e) {
                        e.printStackTrace();
                    }
                }else{
                    timerService.createTimer(MAXIMUM_INACTIVE_PERIOD_BEFORE_REMOVAL - inactivityPeriod, userToCheck);
                }
            }else if(info instanceof String){ // send warning to user
                User userToCheck = userDao.find((String) info);
                Long inactivityPeriod = System.currentTimeMillis() - userToCheck.getLastConnectionDate().getTime();

                if(inactivityPeriod >= MAXIMUM_INACTIVE_PERIOD_BEFORE_WARNING){
                    StringBuilder object = new StringBuilder("Subscription warning");
                    StringBuilder message = new StringBuilder();
                    message.append("Your Crowd Freighting account has been inactive for " + NB_DAYS_BEFORE_WARNING + " days.");
                    message.append("\nIt will be removed from our database when it reached " + NB_DAYS_BEFORE_REMOVAL + " days of inactvity");
                    message.append("\n\nKind regards, \nThe Crowd Freighting Crew.");
                    
                    try {
                        emailManager.sendEmail(new String[]{userToCheck.getEmail()}, 
                                               null, 
                                               null, 
                                               object.toString(), 
                                               message.toString());                    
                    } catch (EmailException e) {
                        e.printStackTrace();
                    }
                }else{
                    timerService.createTimer(MAXIMUM_INACTIVE_PERIOD_BEFORE_REMOVAL - inactivityPeriod, userToCheck);
                }
            }
        }
    }
} 

Troubeshooting for JBoss AS 6


When using timer service with JBoss AS 6, you may run into the following exception : 

21:40:29,665 WARN  [com.arjuna.ats.arjuna] ARJUNA-12140 Adding multiple last resources is disallowed.
Current resource is com.arjuna.ats.internal.arjuna.abstractrecords.LastResourceRecord@1bc46c
21:40:29,665 WARN  [org.hibernate.util.JDBCExceptionReporter] SQL Error: 0, SQLState: null
21:40:29,665 ERROR [org.hibernate.util.JDBCExceptionReporter] Could not enlist in transaction
on entering meta-aware object!; - nested throwable:
(javax.transaction.SystemException: java.lang.Throwable: Unabled to enlist resource,
see the previous warnings. 
....

The explanation is that since JBoss AS 6, the server default behavior seems to allow only one datasource at a time. The thing is timer service already use a datasource to persist timer ... To solve this issue, you'll need to edit the %JBOSS_HOME%\server\%YOUR_SERVER_PROFILE%\deploy\transaction-jboss-beans.xml file and add the following property : 

<property name="allowMultipleLastResources">true</property>

in the following bean : 

<bean name="CoreEnvironmentBean" class="com.arjuna.ats.arjuna.common.CoreEnvironmentBean">

Wednesday, May 4, 2011

Configuring JBoss mail service to use Gmail as SMTP server

In this post, I'll show how to configure the JBoss mail service to use Gmail as an SMTP server, so that you'll be able to send emails using a valid Gmail account, from within an application that has been deployed in JBoss. 

1° Environment / Prerequisites

  • As in the other posts, I am running JBoss AS 6
  • Any valid Gmail account
2° Mail service configuration


In the deploy directory of your JBoss server, you'll find a file name mail-service.xml (note that this file only exists in the deploy directory of the following server profiles : ALL, DEFAULT and standard). Simply edit this file as shown below :



 
 
 

 
  java:/GMail

  
  someuser@gmail.com

  
  myPassword

  
   
    
    
    
    
   
  
  jboss:service=Naming
 

3° Sending mail : example

On the personal project I am currently working at, mails are sent from a business method within a stateless session bean. Here's the code excerpt that does send the mail : 


Context initCtx;
  try {
   initCtx = new InitialContext();
   Session session = (Session) initCtx.lookup("java:/GMail");
   Message message = new MimeMessage(session);
   
   InternetAddress to[] = new InternetAddress[1];
   to[0] = new InternetAddress("destination@gmail.com");  
   message.setRecipients(Message.RecipientType.TO, to);
   
   message.setSubject("How to send a mail with GMAIL from JBoss AS");
   message.setContent("Hello world", "text/plain");
   Transport.send(message);
  } catch (NamingException e) {
   e.printStackTrace();
  } catch (AddressException e) {
   e.printStackTrace();
  } catch (MessagingException e) {
   e.printStackTrace();
  }
Now, let's take a look at the recipient mailbox :