/* * Copyright 2004,2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.axis2.transport.jms; import org.apache.axis2.AxisFault; import org.apache.axis2.Constants; import org.apache.axis2.description.AxisService; import org.apache.axis2.description.Parameter; import org.apache.axis2.transport.base.AbstractTransportListenerEx; import org.apache.axis2.transport.base.BaseConstants; import org.apache.axis2.transport.base.ManagementSupport; import org.apache.axis2.transport.base.event.TransportErrorListener; import org.apache.axis2.transport.base.event.TransportErrorSource; import org.apache.axis2.transport.base.event.TransportErrorSourceSupport; /** * The revamped JMS Transport listener implementation. Creates {@link ServiceTaskManager} instances * for each service requesting exposure over JMS, and stops these if they are undeployed / stopped. * <p> * A service indicates a JMS Connection factory definition by name, which would be defined in the * JMSListner on the axis2.xml, and this provides a way to reuse common configuration between * services, as well as to optimize resources utilized * <p> * If the connection factory name was not specified, it will default to the one named "default" * {@see JMSConstants.DEFAULT_CONFAC_NAME} * <p> * If a destination JNDI name is not specified, a service will expect to use a Queue with the same * JNDI name as of the service. Additional Parameters allows one to bind to a Topic or specify * many more detailed control options. See package documentation for more details * <p> * All Destinations / JMS Administered objects used MUST be pre-created or already available */ public class JMSListener extends AbstractTransportListenerEx<JMSEndpoint> implements ManagementSupport, TransportErrorSource { public static final String TRANSPORT_NAME = Constants.TRANSPORT_JMS; /** The JMSConnectionFactoryManager which centralizes the management of defined factories */ private JMSConnectionFactoryManager connFacManager; private final TransportErrorSourceSupport tess = new TransportErrorSourceSupport(this); @Override protected void doInit() throws AxisFault { connFacManager = new JMSConnectionFactoryManager(getTransportInDescription()); log.info("JMS Transport Receiver/Listener initialized..."); } @Override protected JMSEndpoint createEndpoint() { return new JMSEndpoint(this, workerPool); } /** * Listen for JMS messages on behalf of the given service * * @param service the Axis service for which to listen for messages */ @Override protected void startEndpoint(JMSEndpoint endpoint) throws AxisFault { ServiceTaskManager stm = endpoint.getServiceTaskManager(); stm.start(); for (int i=0; i<3; i++) { // Check the consumer count rather than the active task count. Reason: if the // destination is of type topic, then the transport is only ready to receive // messages if at least one consumer exists. This is of not much importance, // except for automated tests. if (stm.getConsumerCount() > 0) { log.info("Started to listen on destination : " + stm.getDestinationJNDIName() + " of type " + JMSUtils.getDestinationTypeAsString(stm.getDestinationType()) + " for service " + stm.getServiceName()); return; } try { Thread.sleep(1000); } catch (InterruptedException ignore) {} } log.warn("Polling tasks on destination : " + stm.getDestinationJNDIName() + " of type " + JMSUtils.getDestinationTypeAsString(stm.getDestinationType()) + " for service " + stm.getServiceName() + " have not yet started after 3 seconds .."); } /** * Stops listening for messages for the service thats undeployed or stopped * * @param service the service that was undeployed or stopped */ @Override protected void stopEndpoint(JMSEndpoint endpoint) { ServiceTaskManager stm = endpoint.getServiceTaskManager(); if (log.isDebugEnabled()) { log.debug("Stopping listening on destination : " + stm.getDestinationJNDIName() + " for service : " + stm.getServiceName()); } stm.stop(); log.info("Stopped listening for JMS messages to service : " + endpoint.getServiceName()); } /** * Return the connection factory name for this service. If this service * refers to an invalid factory or defaults to a non-existent default * factory, this returns null * * @param service the AxisService * @return the JMSConnectionFactory to be used, or null if reference is invalid */ public JMSConnectionFactory getConnectionFactory(AxisService service) { Parameter conFacParam = service.getParameter(JMSConstants.PARAM_JMS_CONFAC); // validate connection factory name (specified or default) if (conFacParam != null) { return connFacManager.getJMSConnectionFactory((String) conFacParam.getValue()); } else { return connFacManager.getJMSConnectionFactory(JMSConstants.DEFAULT_CONFAC_NAME); } } // -- jmx/management methods-- /** * Pause the listener - Stop accepting/processing new messages, but continues processing existing * messages until they complete. This helps bring an instance into a maintenence mode * @throws AxisFault on error */ @Override public void pause() throws AxisFault { if (state != BaseConstants.STARTED) return; try { for (JMSEndpoint endpoint : getEndpoints()) { endpoint.getServiceTaskManager().pause(); } state = BaseConstants.PAUSED; log.info("Listener paused"); } catch (AxisJMSException e) { log.error("At least one service could not be paused", e); } } /** * Resume the lister - Brings the lister into active mode back from a paused state * @throws AxisFault on error */ @Override public void resume() throws AxisFault { if (state != BaseConstants.PAUSED) return; try { for (JMSEndpoint endpoint : getEndpoints()) { endpoint.getServiceTaskManager().resume(); } state = BaseConstants.STARTED; log.info("Listener resumed"); } catch (AxisJMSException e) { log.error("At least one service could not be resumed", e); } } /** * Stop processing new messages, and wait the specified maximum time for in-flight * requests to complete before a controlled shutdown for maintenence * * @param millis a number of milliseconds to wait until pending requests are allowed to complete * @throws AxisFault on error */ @Override public void maintenenceShutdown(long millis) throws AxisFault { if (state != BaseConstants.STARTED) return; try { long start = System.currentTimeMillis(); stop(); state = BaseConstants.STOPPED; log.info("Listener shutdown in : " + (System.currentTimeMillis() - start) / 1000 + "s"); } catch (Exception e) { handleException("Error shutting down the listener for maintenence", e); } } public void addErrorListener(TransportErrorListener listener) { tess.addErrorListener(listener); } public void removeErrorListener(TransportErrorListener listener) { tess.removeErrorListener(listener); } void error(AxisService service, Throwable ex) { tess.error(service, ex); } }