/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.camel.component.sjms; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import javax.jms.Connection; import javax.jms.MessageProducer; import javax.jms.Session; import org.apache.camel.AsyncCallback; import org.apache.camel.Endpoint; import org.apache.camel.Exchange; import org.apache.camel.component.sjms.jms.ConnectionResource; import org.apache.camel.component.sjms.tx.SessionTransactionSynchronization; import org.apache.camel.impl.DefaultAsyncProducer; import org.apache.camel.util.ObjectHelper; import org.apache.commons.pool.BasePoolableObjectFactory; import org.apache.commons.pool.impl.GenericObjectPool; /** * Base SjmsProducer class. */ public abstract class SjmsProducer extends DefaultAsyncProducer { /** * The {@link MessageProducerResources} pool for all {@link SjmsProducer} * classes. */ protected class MessageProducerResourcesFactory extends BasePoolableObjectFactory<MessageProducerResources> { @Override public MessageProducerResources makeObject() throws Exception { return doCreateProducerModel(createSession()); } @Override public void destroyObject(MessageProducerResources model) throws Exception { if (model.getMessageProducer() != null) { model.getMessageProducer().close(); } if (model.getSession() != null) { try { if (model.getSession().getTransacted()) { try { model.getSession().rollback(); } catch (Exception e) { // Do nothing. Just make sure we are cleaned up } } model.getSession().close(); } catch (Exception e) { // TODO why is the session closed already? } } } } private GenericObjectPool<MessageProducerResources> producers; private ExecutorService executor; private Future<?> asyncStart; public SjmsProducer(Endpoint endpoint) { super(endpoint); } @Override protected void doStart() throws Exception { super.doStart(); this.executor = getEndpoint().getCamelContext().getExecutorServiceManager().newDefaultThreadPool(this, "SjmsProducer"); if (getProducers() == null) { setProducers(new GenericObjectPool<MessageProducerResources>(new MessageProducerResourcesFactory())); getProducers().setMaxActive(getProducerCount()); getProducers().setMaxIdle(getProducerCount()); getProducers().setLifo(false); if (getEndpoint().isPrefillPool()) { if (getEndpoint().isAsyncStartListener()) { asyncStart = getEndpoint().getComponent().getAsyncStartStopExecutorService().submit(new Runnable() { @Override public void run() { try { fillProducersPool(); } catch (Throwable e) { log.warn("Error filling producer pool for destination: " + getDestinationName() + ". This exception will be ignored.", e); } } @Override public String toString() { return "AsyncStartListenerTask[" + getDestinationName() + "]"; } }); } else { fillProducersPool(); } } } } private void fillProducersPool() throws Exception { while (producers.getNumIdle() < producers.getMaxIdle()) { producers.addObject(); } } @Override protected void doStop() throws Exception { super.doStop(); if (asyncStart != null && !asyncStart.isDone()) { asyncStart.cancel(true); } if (getProducers() != null) { if (getEndpoint().isAsyncStopListener()) { getEndpoint().getComponent().getAsyncStartStopExecutorService().submit(new Runnable() { @Override public void run() { try { getProducers().close(); setProducers(null); } catch (Throwable e) { log.warn("Error closing producers on destination: " + getDestinationName() + ". This exception will be ignored.", e); } } @Override public String toString() { return "AsyncStopListenerTask[" + getDestinationName() + "]"; } }); } else { getProducers().close(); setProducers(null); } } if (this.executor != null) { getEndpoint().getCamelContext().getExecutorServiceManager().shutdownGraceful(this.executor); } } @Override public SjmsEndpoint getEndpoint() { return (SjmsEndpoint) super.getEndpoint(); } protected MessageProducerResources doCreateProducerModel(Session session) throws Exception { MessageProducerResources answer; try { MessageProducer messageProducer = getEndpoint().getJmsObjectFactory().createMessageProducer(session, getEndpoint()); answer = new MessageProducerResources(session, messageProducer, getCommitStrategy()); } catch (Exception e) { log.error("Unable to create the MessageProducer", e); throw e; } return answer; } protected Session createSession() throws Exception { ConnectionResource connectionResource = getOrCreateConnectionResource(); Connection conn = connectionResource.borrowConnection(); try { return conn.createSession(isEndpointTransacted(), getAcknowledgeMode()); } catch (Exception e) { log.error("Unable to create the Session", e); throw e; } finally { connectionResource.returnConnection(conn); } } protected interface ReleaseProducerCallback { void release(MessageProducerResources producer) throws Exception; } protected class NOOPReleaseProducerCallback implements ReleaseProducerCallback { public void release(MessageProducerResources producer) throws Exception { /* no-op */ } } protected class ReturnProducerCallback implements ReleaseProducerCallback { public void release(MessageProducerResources producer) throws Exception { getProducers().returnObject(producer); } } public abstract void sendMessage(Exchange exchange, AsyncCallback callback, MessageProducerResources producer, ReleaseProducerCallback releaseProducerCallback) throws Exception; @Override public boolean process(final Exchange exchange, final AsyncCallback callback) { if (log.isDebugEnabled()) { log.debug("Processing Exchange.id:{}", exchange.getExchangeId()); } try { MessageProducerResources producer = null; ReleaseProducerCallback releaseProducerCallback = null; if (isEndpointTransacted() && isSharedJMSSession()) { Session session = exchange.getIn().getHeader(SjmsConstants.JMS_SESSION, Session.class); if (session != null && session.getTransacted()) { // Join existing transacted session - Synchronization must have been added // by the session initiator producer = doCreateProducerModel(session); releaseProducerCallback = new NOOPReleaseProducerCallback(); } else { // Propagate JMS session and register Synchronization as an initiator producer = getProducers().borrowObject(); releaseProducerCallback = new ReturnProducerCallback(); exchange.getIn().setHeader(SjmsConstants.JMS_SESSION, producer.getSession()); exchange.getUnitOfWork().addSynchronization(new SessionTransactionSynchronization(producer.getSession(), producer.getCommitStrategy())); } } else { producer = getProducers().borrowObject(); releaseProducerCallback = new ReturnProducerCallback(); if (isEndpointTransacted()) { exchange.getUnitOfWork().addSynchronization(new SessionTransactionSynchronization(producer.getSession(), producer.getCommitStrategy())); } } if (producer == null) { exchange.setException(new Exception("Unable to send message: connection not available")); } else { if (!isSynchronous()) { if (log.isDebugEnabled()) { log.debug(" Sending message asynchronously: {}", exchange.getIn().getBody()); } final MessageProducerResources finalProducer = producer; final ReleaseProducerCallback finalrpc = releaseProducerCallback; getExecutor().execute(new Runnable() { @Override public void run() { try { sendMessage(exchange, callback, finalProducer, finalrpc); } catch (Exception e) { ObjectHelper.wrapRuntimeCamelException(e); } } }); } else { if (log.isDebugEnabled()) { log.debug(" Sending message synchronously: {}", exchange.getIn().getBody()); } sendMessage(exchange, callback, producer, releaseProducerCallback); } } } catch (Exception e) { if (log.isDebugEnabled()) { log.debug("Processing Exchange.id:{}", exchange.getExchangeId() + " - FAILED"); } if (log.isDebugEnabled()) { log.trace("Exception: " + e.getLocalizedMessage(), e); } exchange.setException(e); } log.debug("Processing Exchange.id:{}", exchange.getExchangeId() + " - SUCCESS"); return isSynchronous(); } /** * @deprecated use {@link #getOrCreateConnectionResource()} */ @Deprecated protected ConnectionResource getConnectionResource() { return getEndpoint().getConnectionResource(); } protected ConnectionResource getOrCreateConnectionResource() { ConnectionResource answer = getEndpoint().getConnectionResource(); if (answer == null) { answer = getEndpoint().createConnectionResource(this); } return answer; } /** * Gets the acknowledgment mode for this instance of DestinationProducer. * * @return int */ public int getAcknowledgeMode() { return getEndpoint().getAcknowledgementMode().intValue(); } /** * Gets the synchronous value for this instance of DestinationProducer. * * @return true if synchronous, otherwise false */ public boolean isSynchronous() { return getEndpoint().isSynchronous(); } /** * Gets the replyTo for this instance of DestinationProducer. * * @return String */ public String getReplyTo() { return getEndpoint().getNamedReplyTo(); } /** * Gets the destinationName for this instance of DestinationProducer. * * @return String */ public String getDestinationName() { return getEndpoint().getDestinationName(); } /** * Sets the producer pool for this instance of SjmsProducer. * * @param producers A MessageProducerPool */ public void setProducers(GenericObjectPool<MessageProducerResources> producers) { this.producers = producers; } /** * Gets the MessageProducerPool value of producers for this instance of * SjmsProducer. * * @return the producers */ public GenericObjectPool<MessageProducerResources> getProducers() { return producers; } /** * Test to verify if this endpoint is a JMS Topic or Queue. * * @return true if it is a Topic, otherwise it is a Queue */ public boolean isTopic() { return getEndpoint().isTopic(); } /** * Test to determine if this endpoint should use a JMS Transaction. * * @return true if transacted, otherwise false */ public boolean isEndpointTransacted() { return getEndpoint().isTransacted(); } /** * Test to determine if this endpoint should share a JMS Session with other SJMS endpoints. * * @return true if shared, otherwise false */ public boolean isSharedJMSSession() { return getEndpoint().isSharedJMSSession(); } /** * Returns the named reply to value for this producer * * @return true if it is a Topic, otherwise it is a Queue */ public String getNamedReplyTo() { return getEndpoint().getNamedReplyTo(); } /** * Gets the producerCount for this instance of SjmsProducer. * * @return int */ public int getProducerCount() { return getEndpoint().getProducerCount(); } /** * Gets consumerCount for this instance of SjmsProducer. * * @return int */ public int getConsumerCount() { return getEndpoint().getConsumerCount(); } /** * Gets the executor for this instance of SjmsProducer. * * @return ExecutorService */ public ExecutorService getExecutor() { return executor; } /** * Gets the ttl for this instance of SjmsProducer. * * @return long */ public long getTtl() { return getEndpoint().getTtl(); } /** * Gets the boolean value of persistent for this instance of SjmsProducer. * * @return true if persistent, otherwise false */ public boolean isPersistent() { return getEndpoint().isPersistent(); } /** * Gets responseTimeOut for this instance of SjmsProducer. * * @return long */ public long getResponseTimeOut() { return getEndpoint().getResponseTimeOut(); } /** * Gets commitStrategy for this instance of SjmsProducer. * * @return TransactionCommitStrategy */ protected TransactionCommitStrategy getCommitStrategy() { if (isEndpointTransacted()) { return getEndpoint().getTransactionCommitStrategy(); } return null; } }