/**
*
* Copyright 2004 Hiram Chirino
*
* 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.codehaus.activemq.ra;
import EDU.oswego.cs.dl.util.concurrent.Latch;
import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.ActiveMQConnectionConsumer;
import org.codehaus.activemq.ActiveMQSession;
import org.codehaus.activemq.message.ActiveMQMessage;
import org.codehaus.activemq.message.ActiveMQQueue;
import org.codehaus.activemq.message.ActiveMQTopic;
import javax.jms.*;
import javax.resource.ResourceException;
import javax.resource.spi.endpoint.MessageEndpoint;
import javax.resource.spi.work.*;
import javax.transaction.xa.XAResource;
/**
* @version $Revision: 1.5 $ $Date: 2004/08/01 02:21:15 $
*/
public class ActiveMQPollingEndpointWorker extends ActiveMQBaseEndpointWorker implements Work {
private static final Log log = LogFactory.getLog(ActiveMQPollingEndpointWorker.class);
private static final int MAX_WORKERS = 10;
private SynchronizedBoolean started = new SynchronizedBoolean(false);
private SynchronizedBoolean stopping = new SynchronizedBoolean(false);
private Latch stopLatch = new Latch();
private ActiveMQConnectionConsumer consumer;
private CircularQueue workers;
static WorkListener debugingWorkListener = new WorkListener() {
//The work listener is useful only for debugging...
public void workAccepted(WorkEvent event) {
}
public void workRejected(WorkEvent event) {
log.warn("Work rejected: " + event, event.getException());
}
public void workStarted(WorkEvent event) {
}
public void workCompleted(WorkEvent event) {
}
};
/**
* @param adapter
* @param key
* @throws ResourceException
*/
public ActiveMQPollingEndpointWorker(ActiveMQResourceAdapter adapter, ActiveMQEndpointActivationKey key) throws ResourceException {
super(adapter, key);
}
public void start() throws WorkException, ResourceException {
ActiveMQActivationSpec activationSpec = endpointActivationKey.getActivationSpec();
boolean ok = false;
try {
workers = new CircularQueue(MAX_WORKERS, stopping);
for( int i=0; i < workers.size(); i++) {
ActiveMQSession session = (ActiveMQSession) adapter.getPhysicalConnection().createSession(transacted,Session.AUTO_ACKNOWLEDGE);
XAResource xaresource=null;
if( session instanceof XASession ) {
if( !transacted )
throw new ResourceException("You cannot use an XA Connection with a non transacted endpoint.");
xaresource = ((XASession)session).getXAResource();
}
MessageEndpoint endpoint = endpointFactory.createEndpoint(xaresource);
workers.returnObject(new InboundEndpointWork(session,endpoint, workers));
}
Destination dest = null;
if ("javax.jms.Queue".equals(activationSpec.getDestinationType())) {
dest = new ActiveMQQueue(activationSpec.getDestinationName());
} else if ("javax.jms.Topic".equals(activationSpec.getDestinationType())) {
dest = new ActiveMQTopic(activationSpec.getDestinationName());
} else {
throw new ResourceException("Unknown destination type: " + activationSpec.getDestinationType());
}
if (emptyToNull(activationSpec.getDurableSubscriptionName()) != null) {
consumer = (ActiveMQConnectionConsumer) adapter.getPhysicalConnection().createDurableConnectionConsumer((Topic)dest,activationSpec.getDurableSubscriptionName(), emptyToNull(activationSpec.getMessageSelector()), null, 0);
} else {
consumer = (ActiveMQConnectionConsumer) adapter.getPhysicalConnection().createConnectionConsumer(dest, emptyToNull(activationSpec.getMessageSelector()), null, 0);
}
ok = true;
log.debug("Started");
workManager.scheduleWork(this, WorkManager.INDEFINITE, null, debugingWorkListener);
ok = true;
} catch (JMSException e) {
throw new ResourceException("Could not start the endpoint.", e);
} finally {
// We don't want to leak sessions on errors. Keep them around only if
// there were no errors.
if (!ok) {
safeClose(consumer);
}
}
}
private String emptyToNull(String value) {
if ("".equals(value)) {
return null;
}
return value;
}
/**
*
*/
public void stop() throws InterruptedException {
stopping.set(true);
workers.notifyWaiting();
if (started.compareTo(true) == 0) {
stopLatch.acquire();
}
safeClose(consumer);
}
/**
* @see javax.resource.spi.work.Work#release()
*/
public void release() {
}
/**
* The WorkManager has started up and we now need to pull message off
* the destination and push them to an endpoint.
*
* @see java.lang.Runnable#run()
*/
public void run() {
started.set(true);
try {
while (!stopping.get()) {
ActiveMQMessage message = consumer.receive(500);
if (message != null) {
InboundEndpointWork worker = (InboundEndpointWork) workers.get();
// Did we get stopped?
if( worker == null ) {
break;
}
worker.message = message;
workManager.scheduleWork(worker, WorkManager.INDEFINITE, null, debugingWorkListener);
}
}
// Try to collect the workers so that none are running.
workers.drain();
} catch (Throwable e) {
log.info("dispatcher: ", e);
} finally {
stopLatch.release();
}
}
public static class InboundEndpointWork implements Work{
private final ActiveMQSession session;
private final MessageEndpoint endpoint;
private final CircularQueue workers;
ActiveMQMessage message;
/**
* @param session
* @param endpoint
* @param workers
* @throws JMSException
*/
public InboundEndpointWork(ActiveMQSession session, MessageEndpoint endpoint, CircularQueue workers) throws JMSException {
this.session = session;
this.endpoint = endpoint;
this.workers = workers;
session.setMessageListener((MessageListener) endpoint);
}
public void release() {
}
/**
* @see java.lang.Runnable#run()
*/
public void run() {
try {
endpoint.beforeDelivery(ON_MESSAGE_METHOD);
try {
session.dispatch(message);
session.run();
} finally {
endpoint.afterDelivery();
}
} catch (NoSuchMethodException e) {
log.info("worker: ", e);
} catch (ResourceException e) {
log.info("worker: ", e);
} finally {
workers.returnObject(this);
}
}
}
}