2 Replies Latest reply on Apr 3, 2009 10:23 AM by Rich Bankroft

    Message-driven bean cleanup issue in Seam

    Rich Bankroft Newbie

      Hi all,


      I am using a message-driven bean to receive JMS messages with Seam 2.1.0.SP1 and JBoss 4.2.3.GA. The message-driven bean injects a stateless bean to process the message. My problem is that, for each message received, a new stateless bean is instantiated and never completely garbage collected. Looking into it with a profiler, it looks like a reference to the stateless bean remains. After 10 messages I have 10 references, after 100 I have 100, after 1000 I have 1000 etc.   with the result that after several hours the app runs out of memory.


      I have posted some code below, using a message processor (doNothingHandler) that does nothing but logging a message but even that is never cleaned up completely:


      The message-driven bean:



      |@MessageDriven(name = "TrackingRequestDispatcher", activationConfig = {
                @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
                @ActivationConfigProperty(propertyName = "destination", propertyValue = "TrackingRequestQueue"),
                @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
      @TransactionManagement(TransactionManagementType.BEAN)
      @Name("trackingDispatcher")
      @Scope(ScopeType.STATELESS)
      public class TrackingRequestDispatcher extends BaseMessageListener {
      
           @In(value = "nothingHandler", create = true)
           private RequestHandler nothingHandler;
      
           @Override
           protected void processMessage(String messageType, Document xmlmsg) {
                // we normally call different handlers here depending
                // on the message type
             nothingHandler.handleRequest(xmlmsg);
           }
      }|



      BaseMessageListener looks like this:



      public abstract class BaseMessageListener implements MessageListener {
           @Logger
           protected Log log;
      
           @Resource
           protected MessageDrivenContext mdc;
      
           protected DocumentBuilderFactory dbFactory;
      
           private static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
           private static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
           private static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
      
           protected abstract void processMessage(String messageType, Document xmlmsg);
      
           public void onMessage(Message inMessage) {
                TextMessage msg = null;
                UserTransaction transaction = mdc.getUserTransaction();
      
                try {
                     if (!(inMessage instanceof TextMessage)) {
                          log.warn("Message of wrong type: {0}", inMessage.getClass().getName());
                          throw new RequestHandlerException();
                     }
      
                     if (dbFactory == null) {
                          dbFactory = DocumentBuilderFactory.newInstance();
                     }
      
                     msg = (TextMessage) inMessage;
                     String msgText = msg.getText();
                     if (null == msgText || msgText.isEmpty()) {
                          log.error("Message contains no text. Aborting.");
                          throw new RequestHandlerException();
                     }
                     String msgType = msg.getStringProperty("messageType");
                     log.info("Request received: {0}", msg.getText());
                     StringReader inStream = new StringReader(msg.getText());
                     InputSource inSource = new InputSource(inStream);
      
                     if (msgType != null) {
                          URL url = this.getClass().getResource("xsd/" + msgType + ".xsd");
                          if (null != url) {
                               File f = new File(url.toURI());
                               if (f.exists()) {
                                    InputStream schemaFile = this.getClass().getResourceAsStream("xsd/" + msgType + ".xsd");
                                    dbFactory.setNamespaceAware(true);
                                    dbFactory.setValidating(true);
                                    dbFactory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
                                    dbFactory.setAttribute(JAXP_SCHEMA_SOURCE, schemaFile);
                               }
                          }
                     }
                     DocumentBuilder parser = dbFactory.newDocumentBuilder();
                     parser.setErrorHandler(new SaxParserErrorHandler(log));
                     Document xmlmsg = parser.parse(inSource);
      
                     transaction.begin();
                     processMessage(msgType, xmlmsg);
                     transaction.commit();
                }
                catch (JMSException e) {
                     log.warn("Could not extract text from message");
                     e.printStackTrace();
                }
                catch (ParserConfigurationException e) {
                     log.error("Could not instantiate an XML parser. Giving up.");
                     e.printStackTrace();
                }
                catch (RequestHandlerException e) {
                     log.error("Could not process messages");
                }
                catch (Throwable te) {
                     te.printStackTrace();
                }
                finally {
                     dbFactory = null;
                     try {
                          if (transaction.getStatus() != Status.STATUS_NO_TRANSACTION) {
                               transaction.rollback();
                          }
                     }
                     catch (Exception e) {
                          e.printStackTrace();
                     }
                }
           }
      
      }



      The doNothingHandler looks like this:


      @Name("nothingHandler")
      @Stateless
      @Scope(ScopeType.STATELESS)
      public class DoNothingHandler implements RequestHandler {
      
           @Logger
           private Log log;
           
      
           public void handleRequest(Document xmlmsg) {
                log.info("I am the doNothingHandler, I am doing nothing.");
           }
      
      }



      after receiving, for example, 100 messages, I have exactly 1 reference to TrackingRequestDispatcher (which is OK) but 100 references to doNothingHandler which never get cleared up. The profiler does unfortunately not tell me who exactly is holding these references.  How can these be cleared up?
      Any helps and hints would be greatly appreciated. 


      Thanks a lot folks,
      Rich.