/*
* 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.deployment.spi;
import java.net.InetAddress;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.enterprise.deploy.shared.ModuleType;
import javax.enterprise.deploy.spi.TargetModuleID;
import javax.enterprise.deploy.spi.exceptions.TargetException;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.jboss.logging.Logger;
/**
* A Target that deploys using the JMX adaptor to communicate with the
* MainDeployer using file URLs to the deployments. This target is selected
* by including a targetType=jmx param in the DeploymentManager deployURI.
*
* @author Thomas.Diesler@jboss.org
* @author Scott.Stark@jboss.com
* @version $Revision: 81011 $
*/
public class JMXTarget implements JBossTarget
{
private static final Logger log = Logger.getLogger(JMXTarget.class);
public static final String DESCRIPTION = "JBoss JMX deployment target";
/**
* The default RMIAdaptor JNDI location
*/
private static final String DEFAULT_ADAPTOR_PATH = "/jmx/invoker/RMIAdaptor";
/** The JSR88 deployment manager default and uri option name */
private static final String JSR88_MBEAN = "jboss.management.local:type=JSR88DeploymentManager,name=DefaultManager";
private static final String JSR88_MBEAN_OPT = "jsr88MBean";
/** The deployment target uri */
private URI deployURI;
/** The JNDI properties used to locate the MBeanServer */
private Properties jndiEnv;
private ObjectName jsr88MBean;
/**
* @todo merge the query parameter parsing for jndi and mbean names and
* test proper escaping of unsafe url chars.
*
* @param deployURI
*/
public JMXTarget(URI deployURI)
{
log.debug("new JMXTarget: " + deployURI);
try
{
String localHostName = InetAddress.getLocalHost().getHostName();
String scheme = deployURI.getScheme();
String host = deployURI.getHost();
int port = deployURI.getPort();
String path = deployURI.getPath();
String query = deployURI.getRawQuery();
String uri = deployURI.toASCIIString();
if (uri.startsWith(DeploymentManagerImpl.DEPLOYER_URI))
{
// Using JNDI defaults
scheme = "jnp";
host = localHostName;
port = 1099;
path = DEFAULT_ADAPTOR_PATH;
try
{
InitialContext iniCtx = new InitialContext();
Hashtable env = iniCtx.getEnvironment();
String providerURL = (String)env.get(InitialContext.PROVIDER_URL);
if (providerURL != null)
{
// In case there is no schema returned
if (providerURL.indexOf("://") < 0)
providerURL = "jnp://" + providerURL;
URI providerURI = new URI(providerURL);
scheme = providerURI.getScheme();
host = providerURI.getHost();
port = providerURI.getPort();
}
}
catch (NamingException e)
{
log.error(e);
}
}
StringBuffer tmp = new StringBuffer(scheme + "://");
tmp.append(host != null ? host : localHostName);
tmp.append(port > 0 ? ":" + port : "");
tmp.append(path != null && path.length() > 0 ? path : DEFAULT_ADAPTOR_PATH);
tmp.append(query != null ? "?" + query : "");
deployURI = new URI(tmp.toString());
log.debug("URI changed to: " + deployURI);
this.deployURI = deployURI;
// Parse the query for options
parseQuery();
}
catch (Exception e)
{
log.error(e);
}
}
/**
* Get the target's description
* @return the description
*/
public String getDescription()
{
return DESCRIPTION;
}
/**
* Get the target's name
* @return the name
*/
public String getName()
{
return deployURI.toString();
}
/**
* Get the target's host name
*/
public String getHostName()
{
return deployURI.getHost();
}
/**
* Deploy a given module
*/
public void deploy(TargetModuleID targetModuleID) throws Exception
{
TargetModuleIDImpl moduleID = (TargetModuleIDImpl)targetModuleID;
SerializableTargetModuleID smoduleID = new SerializableTargetModuleID(moduleID);
MBeanServerConnection server = getMBeanServerConnection();
String url = targetModuleID.getModuleID();
Object[] args = { smoduleID };
String[] sig = { smoduleID.getClass().getName() };
log.info("Begin deploy: " + url);
server.invoke(jsr88MBean, "deploy", args, sig);
log.info("End deploy");
}
/**
* Start a given module
*/
public void start(TargetModuleID targetModuleID) throws Exception
{
MBeanServerConnection server = getMBeanServerConnection();
URL url = new URL(targetModuleID.getModuleID());
Object[] args = { url };
String[] sig = { URL.class.getName() };
log.debug("Start: " + url);
args = new Object[] { url.toExternalForm() };
sig = new String[] { String.class.getName() };
log.info("Begin start: " + url);
server.invoke(jsr88MBean, "start", args, sig);
log.info("End start");
}
/**
* Stop a given module
*/
public void stop(TargetModuleID targetModuleID) throws Exception
{
MBeanServerConnection server = getMBeanServerConnection();
URL url = new URL(targetModuleID.getModuleID());
Object[] args = { url };
String[] sig = { URL.class.getName() };
log.debug("Stop: " + url);
args = new Object[] { url.toExternalForm() };
sig = new String[] { String.class.getName() };
log.info("Begin stop: " + url);
server.invoke(jsr88MBean, "stop", args, sig);
log.info("End stop");
}
/**
* Undeploy a given module
*/
public void undeploy(TargetModuleID targetModuleID) throws Exception
{
MBeanServerConnection server = getMBeanServerConnection();
String url = targetModuleID.getModuleID();
Object[] args = { url };
String[] sig = { String.class.getName() };
log.info("Begin undeploy: " + url);
server.invoke(jsr88MBean, "undeploy", args, sig);
log.info("End undeploy");
}
/**
* Retrieve the list of all J2EE application modules running or not running
* on the identified targets.
*/
public TargetModuleID[] getAvailableModules(ModuleType moduleType) throws TargetException
{
try
{
List list = new ArrayList();
MBeanServerConnection server = getMBeanServerConnection();
Object[] args = { new Integer(moduleType.getValue()) };
String[] sig = { int.class.getName() };
SerializableTargetModuleID[] modules = (SerializableTargetModuleID[])server.invoke(jsr88MBean, "getAvailableModules", args, sig);
for (int n = 0; n < modules.length; n++)
{
SerializableTargetModuleID id = modules[n];
String moduleID = id.getModuleID();
boolean isRunning = id.isRunning();
ModuleType type = ModuleType.getModuleType(id.getModuleType());
TargetModuleIDImpl tmid = new TargetModuleIDImpl(this, moduleID, null, isRunning, type);
convertChildren(tmid, id);
list.add(tmid);
}
TargetModuleID[] targetModuleIDs = new TargetModuleID[list.size()];
list.toArray(targetModuleIDs);
return targetModuleIDs;
}
catch (Exception e)
{
TargetException tex = new TargetException("Failed to get available modules");
tex.initCause(e);
throw tex;
}
}
private void convertChildren(TargetModuleIDImpl parent, SerializableTargetModuleID parentID)
{
SerializableTargetModuleID[] children = parentID.getChildModuleIDs();
int length = children != null ? children.length : 0;
for (int n = 0; n < length; n++)
{
SerializableTargetModuleID id = children[n];
String moduleID = id.getModuleID();
boolean isRunning = id.isRunning();
ModuleType type = ModuleType.getModuleType(id.getModuleType());
TargetModuleIDImpl child = new TargetModuleIDImpl(this, moduleID, parent, isRunning, type);
parent.addChildTargetModuleID(child);
convertChildren(child, id);
}
}
// Get MBeanServerConnection
private MBeanServerConnection getMBeanServerConnection() throws NamingException
{
Properties env = buildJNDIEnv();
String lookupPath = deployURI.getPath();
log.debug("JNDI lookup: " + lookupPath);
log.trace("Creating InitialContext with env: " + env);
InitialContext ctx = new InitialContext(env);
MBeanServerConnection server = (MBeanServerConnection)ctx.lookup(lookupPath);
return server;
}
/**
* Parse the query portion of the deployment URI to look for options:
* jsr88MBean : specifies the JSR88 mbean service that provides the
* deployment manager deploy/start/stop/undeploy operations.
*/
private void parseQuery() throws Exception
{
String query = deployURI.getRawQuery();
log.debug("DeployURI.rawQuery: " + query);
Properties params = new Properties();
if (query != null)
{
/* Break the raw query into the name=value pairs. This processing is
too fragile to how the URI was built as it expects that only
the name/value portions of the query were encoded.
*/
String[] options = query.split("[&=]");
for (int n = 0; n < options.length; n += 2)
{
String name = URLDecoder.decode(options[n], "UTF-8");
String value = URLDecoder.decode(options[n + 1], "UTF-8");
params.setProperty(name, value);
}
}
String name = params.getProperty(JSR88_MBEAN_OPT, JSR88_MBEAN);
jsr88MBean = new ObjectName(name);
}
private Properties buildJNDIEnv()
{
if (jndiEnv == null)
{
jndiEnv = new Properties();
// Parse the query string for name=value pairs to put into the env
String query = deployURI.getQuery();
if (query != null)
{
log.debug("Parsing query string: " + query);
StringTokenizer tokenizer = new StringTokenizer(query, "=&");
while (tokenizer.hasMoreTokens())
{
String name = tokenizer.nextToken();
String value = tokenizer.nextToken();
jndiEnv.setProperty(name, value);
}
}
// Set defaults for missing properties
if (jndiEnv.getProperty(Context.INITIAL_CONTEXT_FACTORY) == null)
{
jndiEnv.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
}
if (jndiEnv.getProperty(Context.PROVIDER_URL) == null)
{
String host = deployURI.getHost();
if (host == null)
{
try
{
host = InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException e)
{
host = "localhost";
}
}
int port = deployURI.getPort();
if (port <= 0)
{
port = 1099;
}
String jnpURL = "jnp://" + host + ':' + port;
jndiEnv.setProperty(Context.PROVIDER_URL, jnpURL);
}
if (jndiEnv.getProperty(Context.OBJECT_FACTORIES) == null)
{
jndiEnv.setProperty(Context.OBJECT_FACTORIES, "org.jboss.naming");
}
}
return jndiEnv;
}
}