/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.cache.invalidation.bridges;
import java.io.Serializable;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.jboss.cache.invalidation.BatchInvalidation;
import org.jboss.cache.invalidation.InvalidationBridgeListener;
import org.jboss.cache.invalidation.InvalidationManager;
import org.jboss.system.ServiceMBeanSupport;
/**
* JMS implementation of a cache invalidation bridge
*
* Based on previous code of Bill Burke based on interceptors
*
* @see org.jboss.cache.invalidation.InvalidationManagerMBean
*
* @author <a href="mailto:sacha.labourey@cogito-info.ch">Sacha Labourey</a>.
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>.
* @version $Revision: 81030 $
*
* <p><b>Revisions:</b>
*
* <p><b>28 septembre 2002 Sacha Labourey:</b>
* <ul>
* <li> First implementation </li>
* </ul>
*/
public class JMSCacheInvalidationBridge
extends ServiceMBeanSupport
implements JMSCacheInvalidationBridgeMBean,
InvalidationBridgeListener,
MessageListener
{
// Constants -----------------------------------------------------
public static final String JMS_CACHE_INVALIDATION_BRIDGE = "JMS_CACHE_INVALIDATION_BRIDGE";
// Attributes ----------------------------------------------------
// JMX Attributes
//
protected org.jboss.cache.invalidation.InvalidationManagerMBean invalMgr = null;
protected org.jboss.cache.invalidation.BridgeInvalidationSubscription invalidationSubscription = null;
protected String invalidationManagerName = InvalidationManager.DEFAULT_JMX_SERVICE_NAME;
protected boolean publishingAuthorized = false;
protected String connectionFactoryName = "java:/ConnectionFactory";
protected String topicName = "topic/JMSCacheInvalidationBridge";
protected boolean transacted = true;
protected int acknowledgeMode = TopicSession.AUTO_ACKNOWLEDGE; // AUTO_ACK by default
protected int propagationMode = JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION; // IN_OUT by default
protected java.rmi.dgc.VMID serviceId = new java.rmi.dgc.VMID();
protected TopicConnection conn = null;
protected TopicSession session = null;
protected Topic topic = null;
protected TopicSubscriber subscriber = null;
protected TopicPublisher pub = null;
protected String providerUrl = null;
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
public JMSCacheInvalidationBridge () { super (); }
// Public --------------------------------------------------------
// *MBean implementation ----------------------------------------------
public String getInvalidationManager ()
{
return this.invalidationManagerName;
}
public void setInvalidationManager (String objectName)
{
this.invalidationManagerName = objectName;
}
public String getConnectionFactoryName ()
{
return this.connectionFactoryName;
}
public void setConnectionFactoryName (String factoryName)
{
this.connectionFactoryName = factoryName;
}
public String getTopicName ()
{
return this.topicName;
}
public void setTopicName (String topicName)
{
this.topicName = topicName;
}
public String getProviderUrl ()
{
return providerUrl;
}
public void setProviderUrl (String providerUrl)
{
this.providerUrl = providerUrl;
}
public boolean isTransacted ()
{
return this.transacted;
}
public void setTransacted (boolean isTransacted)
{
this.transacted = isTransacted;
}
public int getAcknowledgeMode ()
{
return this.acknowledgeMode;
}
public void setAcknowledgeMode (int ackMode)
{
if (ackMode > 3 || ackMode < 1)
throw new RuntimeException ("Value AcknowledgeMode must be between 1 and 3");
switch (ackMode)
{
case 1: this.acknowledgeMode = TopicSession.AUTO_ACKNOWLEDGE; break;
case 2: this.acknowledgeMode = TopicSession.CLIENT_ACKNOWLEDGE; break;
case 3: this.acknowledgeMode = TopicSession.DUPS_OK_ACKNOWLEDGE; break;
}
}
public int getPropagationMode ()
{
return this.propagationMode;
}
public void setPropagationMode (int propMode)
{
if (propMode > 3 || propMode < 1)
throw new RuntimeException ("Value PropagationMode must be between 1 and 3");
this.propagationMode = propMode;
}
// MessageListener implementation ----------------------------------------------
public void onMessage(Message msg)
{
// just to make sure we are in the good mode
//
if (this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION ||
this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_ONLY_BRIDGE_PROPAGATION)
{
try
{
ObjectMessage objmsg = (ObjectMessage)msg;
if (!objmsg.getJMSType().equals(JMS_CACHE_INVALIDATION_BRIDGE)) return;
JMSCacheInvalidationMessage content = (JMSCacheInvalidationMessage)objmsg.getObject();
// Not very efficient as the whole message must be unserialized just to check
// if we were the emitter. Maybe wrapping this in a byte array would be more efficient
//
if (!content.emitter.equals (this.serviceId))
{
if(content.invalidateAllGroupName != null)
{
invalidationSubscription.invalidateAll(content.invalidateAllGroupName);
}
else
{
invalidationSubscription.batchInvalidate (content.getInvalidations ());
}
}
}
catch (Exception ex)
{
log.warn(ex.getMessage());
}
}
}
// InvalidationBridgeListener implementation ----------------------------------------------
public void batchInvalidate (BatchInvalidation[] invalidations, boolean asynchronous)
{
if ( (this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION ||
this.propagationMode == JMSCacheInvalidationBridgeMBean.OUT_ONLY_BRIDGE_PROPAGATION)
&& this.publishingAuthorized)
{
JMSCacheInvalidationMessage msg = new JMSCacheInvalidationMessage (this.serviceId, invalidations);
this.sendJMSInvalidationEvent (msg);
}
}
public void invalidate (String invalidationGroupName, Serializable[] keys, boolean asynchronous)
{
if ( (this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION ||
this.propagationMode == JMSCacheInvalidationBridgeMBean.OUT_ONLY_BRIDGE_PROPAGATION)
&& this.publishingAuthorized)
{
JMSCacheInvalidationMessage msg = new JMSCacheInvalidationMessage (
this.serviceId,
invalidationGroupName,
keys);
this.sendJMSInvalidationEvent (msg);
}
}
public void invalidate (String invalidationGroupName, Serializable key, boolean asynchronous)
{
if ( (this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION ||
this.propagationMode == JMSCacheInvalidationBridgeMBean.OUT_ONLY_BRIDGE_PROPAGATION)
&& this.publishingAuthorized)
{
JMSCacheInvalidationMessage msg = new JMSCacheInvalidationMessage (
this.serviceId,
invalidationGroupName,
new Serializable[] {key} );
this.sendJMSInvalidationEvent (msg);
}
}
public void invalidateAll(String groupName, boolean asynchronous)
{
if ( (this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION ||
this.propagationMode == JMSCacheInvalidationBridgeMBean.OUT_ONLY_BRIDGE_PROPAGATION)
&& this.publishingAuthorized)
{
JMSCacheInvalidationMessage msg = new JMSCacheInvalidationMessage(
this.serviceId,
groupName
);
this.sendJMSInvalidationEvent (msg);
}
}
public void newGroupCreated (String groupInvalidationName)
{
// we don't manage groups dynamically, so we don't really care...
//
}
public void groupIsDropped (String groupInvalidationName)
{
// we don't manage groups dynamically, so we don't really care...
//
}
// ServiceMBeanSupport overrides ---------------------------------------------------
protected void startService () throws Exception
{
log.info("Starting JMS cache invalidation bridge");
// Deal with the InvalidationManager first..
//
this.invalMgr = (org.jboss.cache.invalidation.InvalidationManagerMBean)
org.jboss.system.Registry.lookup (this.invalidationManagerName);
this.invalidationSubscription = invalMgr.registerBridgeListener (this);
// deal with JMS next
//
InitialContext iniCtx = getInitialContext ();
Object tmp = iniCtx.lookup(this.connectionFactoryName);
TopicConnectionFactory tcf = (TopicConnectionFactory) tmp;
conn = tcf.createTopicConnection();
topic = (Topic) iniCtx.lookup(this.topicName);
session = conn.createTopicSession(this.transacted,
this.acknowledgeMode);
conn.start();
// Are we publisher, subscriber, or both?
//
if (this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION ||
this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_ONLY_BRIDGE_PROPAGATION)
{
this.subscriber = session.createSubscriber(topic);
this.subscriber.setMessageListener(this);
}
if (this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION ||
this.propagationMode == JMSCacheInvalidationBridgeMBean.OUT_ONLY_BRIDGE_PROPAGATION)
{
this.pub = session.createPublisher(topic);
this.publishingAuthorized = true;
}
}
protected void stopService ()
{
log.info ("Stoping JMS cache invalidation bridge");
try
{
if (this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION ||
this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_ONLY_BRIDGE_PROPAGATION)
{
subscriber.close();
}
if (this.propagationMode == JMSCacheInvalidationBridgeMBean.IN_OUT_BRIDGE_PROPAGATION ||
this.propagationMode == JMSCacheInvalidationBridgeMBean.OUT_ONLY_BRIDGE_PROPAGATION)
{
this.publishingAuthorized = false;
pub.close();
}
conn.stop();
session.close();
conn.close();
}
catch (Exception ex)
{
log.warn("Failed to stop JMS resources associated with the JMS bridge: ", ex);
}
}
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
protected synchronized TopicSession getSession()
{
return this.session;
}
protected synchronized TopicPublisher getPublisher()
{
return this.pub;
}
protected void sendJMSInvalidationEvent(JMSCacheInvalidationMessage invalidationMsg)
{
try
{
if (log.isTraceEnabled ())
log.trace("sending JMS message for cache invalidation" + invalidationMsg);
try
{
ObjectMessage msg = getSession().createObjectMessage();
msg.setJMSType(JMS_CACHE_INVALIDATION_BRIDGE);
msg.setObject(invalidationMsg);
getPublisher().publish(msg);
}
catch (JMSException ex)
{
log.debug("failed to publish seppuku event: ", ex);
}
}
catch (Exception ex)
{
log.warn("failed to do cluster seppuku event: " , ex);
}
}
protected InitialContext getInitialContext()
throws NamingException
{
if (providerUrl == null)
{
return new InitialContext();
}
else
{
log.debug("Using Context.PROVIDER_URL: " + providerUrl);
java.util.Properties props = new java.util.Properties(System.getProperties());
props.put(Context.PROVIDER_URL, providerUrl);
return new InitialContext(props);
}
}
// Private -------------------------------------------------------
// Inner classes -------------------------------------------------
}