/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, 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.multiplexer;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.TreeCacheMBean;
import org.jgroups.JChannel;
import org.jgroups.JChannelFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Utility class that can associate a cache with a JMX server and a
* a JGroups JChannelFactory.
*
* @author <a href="brian.stansberry@jboss.com">Brian Stansberry</a>
* @version $Revision: 2825 $
*/
public class MultiplexerTestHelper
{
private static final Log log = LogFactory.getLog(MultiplexerTestHelper.class);
public static final String MUX_STACK = "jbc-test";
private static final String FACTORY_OBJECT_NAME_BASE = "jboss.cache:service=MuxChannelFactory,count=";
private MBeanServer mbeanServer;
private final Set factories = Collections.synchronizedSet(new HashSet());
private final Set caches = Collections.synchronizedSet(new HashSet());
public MultiplexerTestHelper()
{
mbeanServer =
MBeanServerFactory.createMBeanServer();
}
/**
* Configures the given cache to get its JChannel from a
* multiplexer-enabled JChannelFactory. The JChannelFactory will
* produce MuxChannels configured with the same protocol stack as
* whatever the provided cache is configured with.
*
* @param cache
* @throws Exception
*/
public void configureCacheForMux(TreeCacheMBean cache) throws Exception
{
synchronized (caches)
{
ObjectName on = createMuxChannelFactory(cache);
cache.setMultiplexerService(on.getCanonicalName());
cache.setMultiplexerStack(MUX_STACK);
ObjectName cacheON = new ObjectName("jboss.cache:service=TestCache,count=" + caches.size());
mbeanServer.registerMBean(cache, cacheON);
caches.add(cacheON);
}
}
/**
* Creates a JChannelFactory and binds it to this object's
* internal MBeanServer. The JChannelFactory will
* produce MuxChannels configured with the same protocol stack as
* whatever the provided cache is configured with.
*
* @param cache the cache from which the protocol stack config should
* be obtained
*
* @return the ObjectName of the channel factory.
* @throws Exception
*/
public ObjectName createMuxChannelFactory(TreeCacheMBean cache) throws Exception
{
String props = cache.getClusterProperties();
props = (props == null ? JChannel.DEFAULT_PROTOCOL_STACK : props);
return createMuxChannelFactory(getClusterConfigElement(props));
}
/**
* Creates a JChannelFactory and binds it to this object's
* internal MBeanServer. The JChannelFactory will
* produce MuxChannels configured with according to the given
* protocol stack(s).
*
* @param muxConfig Element that looks like the root element
* of a multiplexer stacks.xml file.
*
* @return the ObjectName of the channel factory.
* @throws Exception
*/
public ObjectName createMuxChannelFactory(Element muxConfig) throws Exception
{
synchronized (factories)
{
JChannelFactory factory = new JChannelFactory();
factory.setDomain("jbc.mux.test");
factory.setExposeChannels(false);
MuxChannelFactory muxFactory = new MuxChannelFactory(factory, muxConfig);
// Give the factory a unique ObjectName
ObjectName on = new ObjectName(FACTORY_OBJECT_NAME_BASE + factories.size());
mbeanServer.registerMBean(muxFactory, on);
factories.add(on);
muxFactory.create();
muxFactory.start();
return on;
}
}
/**
* Converts an old-style JGroups protocol stack config string to an Element
* that looks like the root element of a multiplexer stacks.xml file.
*
* @param clusterConfig
* @return
* @throws Exception
*/
public static Element getClusterConfigElement(String clusterConfig) throws Exception
{
clusterConfig = clusterConfig.trim();
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.newDocument();
Element top = doc.createElement("protocol_stacks");
doc.appendChild(top);
Element stack = doc.createElement("stack");
stack.setAttribute("name", MUX_STACK);
top.appendChild(stack);
Element config = doc.createElement("config");
StringTokenizer outer = new StringTokenizer(clusterConfig, ":");
while (outer.hasMoreTokens())
{
String protocol = outer.nextToken();
String protName = protocol;
String attribs = null;
int nameEnd = protocol.indexOf('(');
if (nameEnd > 0)
{
protName = protocol.substring(0, nameEnd);
attribs = protocol.substring(nameEnd + 1, protocol.length() -1);
}
Element element = doc.createElement(protName);
if (attribs != null && attribs.length() > 0)
{
StringTokenizer inner = new StringTokenizer(attribs, ";");
while (inner.hasMoreTokens())
{
String attrib = inner.nextToken();
int eq = attrib.indexOf('=');
String name = attrib.substring(0, eq);
String value = attrib.substring(eq +1);
element.setAttribute(name, value);
}
}
config.appendChild(element);
}
stack.appendChild(config);
return top;
}
/**
* Performs cleanup work, most importantly unregistering the
* internal MBeanServer. Once this method is invoked, this
* object should no longer be used.
*/
public void tearDown()
{
try
{
for (Iterator it = caches.iterator(); it.hasNext(); )
{
try
{
mbeanServer.unregisterMBean((ObjectName) it.next());
}
catch (Exception e)
{
log.error(e);
}
}
for (Iterator it = factories.iterator(); it.hasNext(); )
{
try
{
mbeanServer.unregisterMBean((ObjectName) it.next());
}
catch (Exception e)
{
log.error(e);
}
}
}
catch (Exception e)
{
log.error(e);
}
finally
{
factories.clear();
caches.clear();
if (mbeanServer != null)
{
MBeanServerFactory.releaseMBeanServer(mbeanServer);
mbeanServer = null;
}
}
}
/**
* Makes sure the internal MBeanServer is unregistered.
*/
protected void finalize() throws Throwable
{
if (mbeanServer != null)
{
MBeanServerFactory.releaseMBeanServer(mbeanServer);
}
super.finalize();
}
/**
* Tests creation of a channel factory.
*
* @param args
*/
public static void main(String[] args)
{
MultiplexerTestHelper helper = new MultiplexerTestHelper();
try
{
helper.createMuxChannelFactory(getClusterConfigElement(JChannel.DEFAULT_PROTOCOL_STACK));
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
helper.tearDown();
}
}
}