package org.atricore.idbus.kernel.main.mediation;
import org.apache.activemq.command.ActiveMQObjectMessage;
import org.apache.activemq.util.ByteArrayInputStream;
import org.apache.activemq.util.ByteSequence;
import org.apache.activemq.util.JMSExceptionSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.atricore.idbus.kernel.common.support.osgi.OsgiBundlespaceClassLoader;
import org.atricore.idbus.kernel.main.mediation.osgi.ClassLoadingAwareObjectInputStream;
import org.osgi.framework.BundleContext;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.osgi.context.BundleContextAware;
import javax.jms.*;
import java.io.*;
import java.util.Enumeration;
import java.util.zip.InflaterInputStream;
/**
* @author <a href="mailto:sgonzalez@atricore.org">Sebastian Gonzalez Oyuela</a>
* @version $Id$
*/
public class ActiveMQMessageQueueManager implements MessageQueueManager, BundleContextAware, InitializingBean, DisposableBean {
private static transient Log log = LogFactory.getLog(MessageQueueManager.class);
private ArtifactGenerator artifactGenerator;
private ConnectionFactory connectionFactory;
private String jmsProviderDestinationName;
private Connection connection;
private Session session;
private Queue queue;
private boolean initialized = false;
private BundleContext bundleContext;
private ClassLoader osgiClassLoader;
private long receiveTimeout = 30000L;
private long artifactTTL = 600L;
public void afterPropertiesSet() throws Exception {
init();
}
public void destroy() throws Exception {
shutDown();
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
if (log.isDebugEnabled())
log.debug("Creating OSGi Bundlespace classloader using bundle " +
bundleContext.getBundle().getSymbolicName() + ":" +
bundleContext.getBundle().getBundleId());
osgiClassLoader = new OsgiBundlespaceClassLoader(
bundleContext,
Thread.currentThread().getContextClassLoader(),
bundleContext.getBundle());
}
/**
* @org.apache.xbean.Property alias="connection-factory"
*/
public ConnectionFactory getConnectionFactory() {
return connectionFactory;
}
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
/**
* @org.apache.xbean.Property alias="jms-provider-destination"
*/
public String getJmsProviderDestinationName() {
return jmsProviderDestinationName;
}
public void setJmsProviderDestinationName(String jmsProviderDestinationName) {
this.jmsProviderDestinationName = jmsProviderDestinationName;
}
public void init() throws Exception {
if (initialized)
return;
synchronized (this) {
if (initialized)
return;
log.debug("Initializing Message Queue Manager ... ");
connection = connectionFactory.createConnection();
connection.start();
log.trace("ReceiveTimeout:" + receiveTimeout);
log.trace("Artifact time to live: " + artifactTTL);
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
queue = session.createQueue(jmsProviderDestinationName);
initialized = true;
log.info("Initializing Message Queue Manager ... OK");
}
}
public void shutDown() throws Exception {
if (!initialized)
return;
try {
session.close();
session = null;
} finally {
try {
connection.stop();
connection.close();
connection = null;
} catch (Exception e) {
/**/
}
initialized = false;
}
}
public Object pullMessage(Artifact artifact) throws Exception {
init();
Object content = null;
MessageConsumer consumer;
String qry = "artifact = '" + artifact.getContent() + "'";
log.debug("Pulling Message with Selector ["+qry+"]");
consumer = session.createConsumer(queue, qry);
try {
Message message = pullMessageSync(artifact, consumer);
if (message == null) {
synchronized(this) {
try {
wait(100);
message = pullMessageSync(artifact, consumer);
} catch (InterruptedIOException e) {
/* */
}
}
}
// Workaround for OSGi classloader issues
ActiveMQObjectMessage amqMsg = (ActiveMQObjectMessage) message;
if (message == null) {
log.warn("No message received for ["+qry+"]");
return null;
}
if (amqMsg.getProperty("artifact") == null) {
throw new IdentityMediationException("Message does not contain 'artifact' property " + amqMsg);
}
if (!amqMsg.getProperty("artifact").equals(artifact.getContent())) {
throw new IdentityMediationException("Message 'artifact' invalid (received:" +
amqMsg.getProperty("artifact") + ", expected:" + artifact.getContent() + ")");
}
content = getObject(amqMsg);
} finally {
if (consumer != null) {
consumer.close();
}
}
return content;
}
protected Message pullMessageSync(Artifact artifact, MessageConsumer consumer) throws Exception {
ObjectMessage message = (ObjectMessage) consumer.receive(receiveTimeout);
// if (message == null) {
// int retry = 0;
// while(message == null && retry <= receiveRetries) {
// log.debug("Pull Message found NO message for [" + artifact + "]. Wait and retry ...");
// try { Thread.sleep(500); } catch (InterruptedException ie) { /*ignore it*/ }
// message = (ObjectMessage) consumer.receive(receiveTimeout);
// retry ++;
// }
// }
if (message != null) {
log.debug("Pull Message found message " + message);
} else {
log.debug("Pull Message found NO message for " + artifact);
}
return message;
}
public Object peekMessage(Artifact artifact) throws Exception {
init();
Object result = null;
String qry = "artifact = '" + artifact.getContent() + "'";
log.debug("Peeking message for ["+qry+"]");
QueueBrowser browser = session.createBrowser(queue, qry);
Enumeration elems = browser.getEnumeration();
if(elems.hasMoreElements()){
result = getObject((ActiveMQObjectMessage)elems.nextElement());
}
if (log.isDebugEnabled())
log.debug("Peek Message found " + (result != null ? result.getClass().getName() : "null") +
" content for artifact " + artifact.getContent());
return result;
}
public Artifact pushMessage(Object content) throws Exception {
if (content == null)
throw new NullPointerException("Message content cannot be null");
if (!(content instanceof Serializable))
throw new NotSerializableException(content.getClass().getName());
init();
MessageProducer producer = session.createProducer(queue);
ObjectMessage message = session.createObjectMessage((Serializable)content);
Artifact artifact = artifactGenerator.generate();
if (log.isDebugEnabled())
log.debug("Push Message "+content.getClass().getName()+" for artifact " + artifact.getContent());
message.setStringProperty("artifact", artifact.getContent());
producer.setTimeToLive(artifactTTL);
producer.send(message);
return artifact;
}
/**
* In OSGi, we need a differente way to unmarshall objects!
*/
protected Object getObject(ActiveMQObjectMessage msg) throws JMSException {
Object object;
try {
ByteSequence content = msg.getContent();
InputStream is = new ByteArrayInputStream(content);
if (msg.isCompressed()) {
is = new InflaterInputStream(is);
}
DataInputStream dataIn = new DataInputStream(is);
ClassLoadingAwareObjectInputStream objIn = new ClassLoadingAwareObjectInputStream(osgiClassLoader, dataIn);
try {
object = (Serializable)objIn.readObject();
} catch (ClassNotFoundException ce) {
log.error(ce.getMessage(), ce);
throw new IOException("Cannot read Object:" + ce.getMessage());
}
dataIn.close();
} catch (IOException e) {
throw JMSExceptionSupport.create("Failed to build body from bytes. Reason: " + e, e);
}
return object;
}
/**
* @org.apache.xbean.Property alias="artifact-generator"
*/
public ArtifactGenerator getArtifactGenerator() {
return artifactGenerator;
}
public void setArtifactGenerator(ArtifactGenerator artifactGenerator) {
this.artifactGenerator = artifactGenerator;
}
public long getReceiveTimeout() {
return receiveTimeout;
}
public void setReceiveTimeout(long receiveTimeout) {
this.receiveTimeout = receiveTimeout;
}
public long getArtifactTTL() {
return artifactTTL;
}
public void setArtifactTTL(long artifactTTL) {
this.artifactTTL = artifactTTL;
}
}