/*
* Created on Nov 8, 2004
*
* Copyright 2005 CafeSip.org
*
* 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.cafesip.jiplet;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.ObjectName;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import org.cafesip.jiplet.config.jip.ContextMapping;
import org.cafesip.jiplet.config.jip.JipletContextMappings;
import org.cafesip.jiplet.config.jip.ObjectFactory;
import org.cafesip.jiplet.config.server.JipConnector;
import org.cafesip.jiplet.config.server.JipRealm;
import org.cafesip.jiplet.config.server.JipRealmParam;
import org.cafesip.jiplet.config.server.JipRealms;
import org.cafesip.jiplet.config.server.JipServer;
import org.cafesip.jiplet.jmxbeans.ConnectorElement;
import org.cafesip.jiplet.jmxbeans.ContextElement;
import org.cafesip.jiplet.jmxbeans.RealmElement;
import org.cafesip.jiplet.utils.FileUtils;
import org.xml.sax.InputSource;
/**
* This class represents the Jiplet Container. The jiplet container hosts
* multiple contexts. Each context is a separate application that has its own
* class loader and therefore completely isolated from each other. Each context
* may contain one or more jiplets. The container exposes a JMX management
* interface for managing the contexts. Using this interface, a context may be
* added or removed from the container. Alternatively, a container may be
* deployed by simply adding the content of the context to the deploy directory
* of the container and re-starting the jilet container application. Unlike most
* servlet container and J2EE server applications, the container does not
* periodically check its deploy directory to determine if a new context has
* been added.
*
* <p>
* The contexts may be deployed either in the "spr" format or in "exploded"
* format. The spr format consist of a single file that packs all the classes,
* data files and descriptors beloging to a context. The files are packed using
* the jar/zip utility. A custom ant task has been provided to zip the content
* of a context directory. A spr is equivalent to a "war" file in the
* servlet/JSP world.
*
* The jiplet container server application can either run as a Java standalone
* application similar to Apache Tomcat. But it can also be deployed as a JBOSS
* service (incidentally Tomcat can also be run as a JBOSS service). When
* running as a JBOSS service, it can be managed from the JBOSS management
* console and it also supports deployment of spr files using the JBOSS
* deployment infrastructure.
*
* @author Becky McElroy & Amit Chatterjee
*
*/
public class JipletContainer extends NotificationBroadcasterSupport implements
JipletContainerMBean
{
private static final int REALM_DEPLOYED = 0;
private static final int REALM_J2EE = 1;
private static JipletContainer instance = null;
private static File deployDir;
private static File confDir;
private JipServer config;
private HashMap connectors = new HashMap();
private String defaultConnectorName;
private HashMap deployedContexts = new HashMap();
private static MBeanServer jmxAgent;
private ArrayList realms = new ArrayList();
private HashMap dirRealmsMap = new HashMap();
// key = path, value=Realm object
private HashMap realmsDirMap = new HashMap();
// key= realm name, value = path
public static ObjectName MBEAN_NAME = null;
static
{
try
{
MBEAN_NAME = new ObjectName(
"org.cafesip.jiplet:type=JipletContainer");
}
catch (MalformedObjectNameException e)
{
// should not happen
}
}
private long seqNum = 0;
private String defaultRealm;
private long authCachePeriod = 0L;
private boolean authOnLogout = true;
private JipletContextMappings contextMappings = null;
private static VendorDescriptorFactory vendorDeploymentFactory = null;
/**
* Constructor for the class.
*/
public JipletContainer() throws JipletException
{
if (instance != null)
{
throw new JipletException(
"Jiplet container is already instantiated. Use getInstance() instead");
}
instance = this;
}
public void init() throws Exception
{
JipletLogger.info("Jiplet container started");
// parse the server.xml
File serverxml = new File(confDir, "server.xml");
FileInputStream istream = new FileInputStream(serverxml);
config = (JipServer) JAXBContext.newInstance(
"org.cafesip.jiplet.config.server",
this.getClass().getClassLoader()).createUnmarshaller()
.unmarshal(istream);
istream.close();
File mappingxml = new File(confDir, "mapping.xml");
if (mappingxml.exists() == true)
{
istream = new FileInputStream(mappingxml);
contextMappings = (JipletContextMappings) JAXBContext.newInstance(
"org.cafesip.jiplet.config.jip",
this.getClass().getClassLoader()).createUnmarshaller()
.unmarshal(istream);
istream.close();
}
// initialize the SIP Connectors from server.xml
initConfiguredSipConnectors();
// initialize the realms from server.xml
initRealms();
// next, initialize the contexts present in the deploy
// directory
initContexts();
// finally, initialize the JMX services
initJmxServices();
}
private void initRealms() throws Exception
{
// first, configure the realms based on the server.xml config file.
JipRealms realms = config.getRealms();
if (realms == null)
{
return;
}
int cache_period = realms.getAuthCachePeriod().intValue();
if (cache_period > 0)
{
authCachePeriod = cache_period * 1000L;
}
authOnLogout = realms.isAuthOnLogout();
Iterator iter = realms.getRealm().iterator();
while (iter.hasNext() == true)
{
JipRealm realm = (JipRealm) iter.next();
startRealm(realm, null, null);
}
// next initiate the SRR realms based on the deployment directory
if (deployDir.exists() == false)
{
return;
}
File[] files = deployDir.listFiles(new FilenameFilter()
{
public boolean accept(File dir, String name)
{
if ((name.equals(".") == true) || (name.equals("..") == true))
{
return false;
}
File f = new File(dir, name);
if (FileUtils.validDeployableRealmDir(f.getAbsolutePath()) == true)
{
// if the srr exists, then return false because the srr will
// be found
File srr = new File(f.getAbsolutePath()
+ FileUtils.SRR_EXTENSION);
if (FileUtils.validDeployableSrr(srr.getAbsolutePath()) == true)
{
return false;
}
}
else if (FileUtils.validDeployableSrr(f.getAbsolutePath()) == false)
{
return false;
}
return true;
}
});
for (int i = 0; i < files.length; i++)
{
String err = deployRealm(files[i].getAbsolutePath(),
REALM_DEPLOYED, null);
if (err.length() > 0)
{
JipletLogger.error("Error deploying realm "
+ files[i].getAbsolutePath() + ". Error message: "
+ err);
continue;
}
}
}
private void initJmxServices() throws InstanceAlreadyExistsException,
MBeanRegistrationException, NotCompliantMBeanException
{
if (jmxAgent != null)
{
JipletLogger.info("Registering MBEAN JipletContainer");
jmxAgent.registerMBean(this, MBEAN_NAME);
}
else
{
JipletLogger
.info("No JMX agent found. JMX services will not be initialized");
}
}
private void initConfiguredSipConnectors() throws Exception
{
List list = config.getConnectors().getConnector();
Iterator iter = list.iterator();
while (iter.hasNext() == true)
{
JipConnector c = (JipConnector) iter.next();
initSipConnector(c);
}
}
private void initContexts()
{
if (deployDir.exists() == false)
{
return;
}
File[] files = deployDir.listFiles(new FilenameFilter()
{
public boolean accept(File dir, String name)
{
if ((name.equals(".") == true) || (name.equals("..") == true))
{
return false;
}
File f = new File(dir, name);
if (FileUtils.validDeployableJipletDir(f.getAbsolutePath()) == true)
{
// if the spr exists, then return false because the spr will
// be found
File spr = new File(f.getAbsolutePath()
+ FileUtils.SPR_EXTENSION);
if (FileUtils.validDeployableSpr(spr.getAbsolutePath()) == true)
{
return false;
}
}
else if (FileUtils.validDeployableSpr(f.getAbsolutePath()) == false)
{
return false;
}
return true;
}
});
for (int i = 0; i < files.length; i++)
{
String err = deployContext(files[i].getAbsolutePath(),
JipletContext.CONTEXT_DEPLOYED, null, null);
if (err.length() > 0)
{
JipletLogger.error("Error deploying context "
+ files[i].getAbsolutePath() + ". Error message: "
+ err);
}
}
}
private void startContext(String name, String path, int type,
ClassLoader loader) throws Exception
{
JipletLogger.info("Initializing context: " + name);
JipletContext context = new JipletContext(name, new File(path), type,
loader);
context.init();
synchronized (deployedContexts)
{
deployedContexts.put(name, context);
}
}
/**
* This method returns a context given its name.
*
* @param name
* name of the context.
* @return the JipletContext object, null if the context is not found.
*/
private JipletContext findContext(String name)
{
synchronized (deployedContexts)
{
return (JipletContext) deployedContexts.get(name);
}
}
private void initSipConnector(JipConnector c) throws Exception
{
SipConnector sc = new SipConnector(c);
sc.init(c);
synchronized (connectors)
{
connectors.put(c.getName(), sc);
}
if (c.isDefault() == true)
{
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger.debug("Connector " + c.getName()
+ " being set to default");
}
defaultConnectorName = c.getName();
}
else if (defaultConnectorName == null)
{
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger.debug("Connector " + c.getName()
+ " being set to default");
}
defaultConnectorName = c.getName();
}
}
/**
* The JipletContainer is a singleton class. This static method is used to
* get the instance of the class. If the class has not already been
* instantiated, the class is instantiated inside this method and the
* reference is returned.
*
* @return Returns the instance.
*/
public static JipletContainer getInstance() throws Exception
{
if (instance == null)
{
new JipletContainer();
}
return instance;
}
/**
* Sets the deploy directory. By default, the deploy directory is located
* under the home directory of the container. However, an application
* instantiating this class can set a different directory.
*
* @param dir
*/
public static void setDeployDir(File dir)
{
deployDir = dir;
}
/**
* Returns a list of the connectors. Each element of the returned HashMap
* object contains a key-value pair. The key is the connector name and the
* value is an object of type SipConnector.
*
* @see SipConnector for more details.
*
* @return Returns the connectors.
*/
public HashMap getConnectors()
{
return connectors;
}
/**
* @return Returns the defaultConnectorName.
*/
public String getDefaultConnectorName()
{
return defaultConnectorName;
}
/**
* Returns a SipConnector object of a given connector name.
*
* @param name
* name of the connector
* @return a SipConnector object, null if the name does not match.
*/
public SipConnector findConnector(String name)
{
synchronized (connectors)
{
return (SipConnector) connectors.get(name);
}
}
/**
* This method is called when the application disposes off the container.
*/
public void destroy()
{
if (jmxAgent != null)
{
try
{
jmxAgent.unregisterMBean(MBEAN_NAME);
}
catch (Exception e)
{
JipletLogger
.error("Error occured while unregistering JipletContainer MBEAN: "
+ e.getClass().getName()
+ ": "
+ e.getMessage()
+ "\n"
+ JipletLogger.getStackTrace(e));
}
}
// first, shutdown all the contexts
synchronized (deployedContexts)
{
Iterator iter = deployedContexts.entrySet().iterator();
while (iter.hasNext() == true)
{
Map.Entry element = (Map.Entry) iter.next();
JipletContext context = (JipletContext) element.getValue();
JipletLogger.info("Destroying jiplet context: "
+ (String) element.getKey());
context.destroy();
iter.remove();
}
}
// next shutdown all the connectors
synchronized (connectors)
{
Iterator iter = connectors.entrySet().iterator();
while (iter.hasNext() == true)
{
Map.Entry element = (Map.Entry) iter.next();
SipConnector connector = (SipConnector) element.getValue();
JipletLogger.info("Destroying connector: "
+ (String) element.getKey());
connector.destroy();
iter.remove();
}
}
// next shutdown the realms
synchronized (realms)
{
Iterator iter = realms.iterator();
while (iter.hasNext() == true)
{
Realm realm = (Realm) iter.next();
JipletLogger.info("Destroying realm " + realm.getRealmName());
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
realm.destroy();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
iter.remove();
}
}
}
private boolean deployDirExists(File deploy, String path)
{
try
{
File f = new File(path);
if (deploy.exists() == false)
{
return false;
}
if (deploy.compareTo(f.getParentFile()) == 0)
{
return true;
}
}
catch (Exception ex)
{
return false;
}
return false;
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#startContext(java.lang.String,
* java.lang.String)
*/
public String createContext(String path, String overriddenName)
{
JipletLogger
.info("Received a request from JMX agent to add a context with path: "
+ path
+ ((overriddenName != null) ? (", name: " + overriddenName)
: ""));
String err = deployContext(path, JipletContext.CONTEXT_DEPLOYED,
overriddenName, null);
if (err.length() > 0)
{
return err;
}
String name = FileUtils.parseContextNameFromSpr(new File(path));
if (overriddenName != null)
{
name = overriddenName;
}
JipletLogger.info("Context " + name + " created based on JMX request");
Notification n = new Notification("Container", this.getClass()
.getName(), seqNum++, System.currentTimeMillis(), "Context: "
+ name + " added with path: " + path);
sendNotification(n);
return "";
}
private String deployContext(String path, int type, String overriddenName,
ClassLoader loader)
{
String name = null;
File f = new File(path);
try
{
boolean internal = deployDirExists(deployDir, path);
boolean spr = false;
if (FileUtils.validDeployableSpr(path) == true)
{
if (overriddenName != null)
{
if (internal == true)
{
// This is because when the system is re-started, the
// container will try to
// deploy the spr with the parsed name instead of the
// overridden name
return "This context with path " + path
+ " is internal. The name cannot be overidden";
}
name = overriddenName;
}
else
{
name = FileUtils.parseContextNameFromSpr(f);
}
spr = true;
}
else if (FileUtils.validDeployableJipletDir(path) == true)
{
name = f.getName();
spr = false;
}
else
{
return "The context could not be created because the path does not have a valid signature";
}
if (findContext(name) != null)
{
return "The context could not be created because a context with the same name already exists";
}
FileUtils ant = new FileUtils();
File exploded = new File(deployDir, name);
boolean extr = false;
if ((spr == true) || (internal == false))
{
if (FileUtils.validDeployableJipletDir(exploded
.getAbsolutePath()) == true)
{
if (f.lastModified() > exploded.lastModified())
{
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger
.info("Found older exploded context entry "
+ exploded.getAbsolutePath()
+ " while creating the context "
+ name + ". Deleting the directory");
}
ant.deleteDir(exploded.getAbsolutePath());
extr = true;
}
}
else
{
if (exploded.isDirectory() == true)
{
ant.deleteDir(exploded.getAbsolutePath());
}
extr = true;
}
}
if (extr == true)
{
if (spr == true)
{
if (ant.extractSpr(f.getAbsolutePath(), deployDir
.getAbsolutePath(), name) == false)
{
JipletLogger.error("Error extracting context " + name
+ " from the corresponding spr file");
return "The zipped context could not be extracted";
}
}
else
// exploded
{
if (ant.copy(path, deployDir.getAbsolutePath()) == false)
{
JipletLogger.error("Error copying exploded context "
+ name + " file: " + path
+ " to deploy directory");
return "The exploded context could not be copied to the deploy dir";
}
}
}
startContext(name, exploded.getAbsolutePath(), type, loader);
}
catch (Exception ex)
{
JipletLogger.error("Error creating context: "
+ ex.getClass().getName() + ": " + ex.getMessage() + "\n"
+ JipletLogger.getStackTrace(ex));
return "Context: " + name + " could not be created. Message: "
+ ex.getMessage() + ". See log for details";
}
return "";
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#deleteContext(java.lang.String)
*/
public String deleteContext(String name)
{
JipletLogger
.info("Received a request from JMX agent to delete a context: "
+ name);
synchronized (deployedContexts)
{
JipletContext context = findContext(name);
if (context == null)
{
return "The context " + name + " does not exist";
}
if (context.getContextType() == JipletContext.CONTEXT_J2EE)
{
return "The context " + name
+ " is a J2EE-deployed context. It cannot be deleted";
}
context.destroy();
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger.debug("Removing entry for context " + name
+ " from deploy directory");
}
deployedContexts.remove(name);
FileUtils ant = new FileUtils();
File exploded = new File(deployDir, name);
// if a spr file exists, delete the spr
File spr_file = new File(deployDir, name + FileUtils.SPR_EXTENSION);
// Delete the contexts so that the contexts wont be started during
// server re-start
if (spr_file.exists() == true)
{
JipletLogger.info("Deleting deployment spr file "
+ spr_file.getAbsolutePath());
if (spr_file.delete() == false)
{
// this can happen in a windoze environment because the file
// may be in use
JipletLogger.error("The context spr file "
+ spr_file.getAbsolutePath()
+ ". could not be deleted.");
}
}
JipletLogger.info("Deleting deployment directory "
+ exploded.getAbsolutePath());
if (ant.deleteDir(exploded.getAbsolutePath()) == false)
{
JipletLogger
.warn("The context directory "
+ exploded.getAbsolutePath()
+ ". Could not be deleted. We will try again during server restart");
// this can happen in a windoze environment because the file may
// be in use
markContextAsDeleted(exploded);
}
}
JipletLogger.info("Context " + name + " deleted based on JMX request");
Notification n = new Notification("Container", this.getClass()
.getName(), seqNum++, System.currentTimeMillis(), "Context: "
+ name + " deleted");
sendNotification(n);
return "";
}
private void markContextAsDeleted(File dir)
{
File jip = new File(dir, "JIP-INF/jip.xml");
if (jip.exists() == true)
{
File rename = new File(jip.getAbsolutePath() + ".deleted");
jip.renameTo(rename);
}
}
/*
* @see org.cafesip.jiplet.JipletContainerMBean#listContexts()
*/
public String[] listContexts()
{
JipletLogger.info("Received a JMX request for listing contexts");
synchronized (deployedContexts)
{
String[] ret = new String[deployedContexts.size()];
deployedContexts.keySet().toArray(ret);
return ret;
}
}
/**
* Returns the JMX MBeanServer object. Jiplet applications can call this
* method to retrieve the MBeanServer object. This object can then be used
* to provide management interface for the jiplet application.
*
* @return Returns the jmxAgent.
*/
protected static MBeanServer getJmxAgent()
{
return jmxAgent;
}
/**
* @param jmxAgent
* The jmxAgent to set.
*/
public static void setJmxAgent(MBeanServer jmxAgent)
{
JipletContainer.jmxAgent = jmxAgent;
}
/*
* @see org.cafesip.jiplet.JipletContainerMBean#getContextProperty()
*/
public ContextElement getContextProperty(String name)
{
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger
.debug("Received a request from JMX agent to view the context: "
+ name);
}
JipletContext context = findContext(name);
if (context == null)
{
if (JipletLogger.isDebugEnabled() == true)
{
JipletLogger.debug("Context: " + name + " was not found");
}
return null;
}
ContextElement element = new ContextElement();
ArrayList criteria = context.getContextSelectionCriteria();
ArrayList scriteria = new ArrayList(criteria.size());
for (int i = 0; i < criteria.size(); i++)
{
Pair p = (Pair) criteria.get(i);
String connector = (String) p.getFirst();
String exp = (String) p.getSecond();
scriteria.add(connector + ":" + exp);
}
element.setSelectionCriteria(scriteria);
element.setName(name);
element.setDisplayName(context.getDisplayName());
element.setPath(context.getContextRoot().getAbsolutePath());
element.setTypeDescription(context.getContextTypeDescription());
return element;
}
/*
* @see org.cafesip.jiplet.JipletContainerMBean#getDeployDir()
*/
public String getDeployDir()
{
return deployDir.getAbsolutePath();
}
/**
* Create a J2EE context. The Jboss wrapper application for the jiplet
* container uses this method to register spr files deployed in the JBOSS
* deploy directory.
*
* @param path
* the path of the SPR file
* @param loader
* - the J2EE classloader
* @return
*/
public String createJ2eeContext(String path, ClassLoader loader)
{
JipletLogger
.info("Received a request from J2EE deployer to add a context with path: "
+ path);
String err = deployContext(path, JipletContext.CONTEXT_J2EE, null,
loader);
if (err.length() > 0)
{
return err;
}
String name = FileUtils.parseContextNameFromSpr(new File(path));
JipletLogger.info("Context " + name
+ " created based on J2EE deployer request");
Notification n = new Notification("Container", this.getClass()
.getName(), seqNum++, System.currentTimeMillis(), "Context: "
+ name + " added with path: " + path);
sendNotification(n);
return "";
}
/**
* The JBOSS wrapper application uses this method to delete a context when
* it has been removed from the JBOSS deploy directory.
*
* @param path
* @return
*/
public String deleteJ2eeContext(String path)
{
String name = null;
File f = new File(path);
if (path.endsWith(FileUtils.SPR_EXTENSION) == true)
{
name = FileUtils.parseContextNameFromSpr(f);
}
else
{
name = f.getName();
File deploy = new File(deployDir, name);
if (deploy.isDirectory() == false)
{
return "The context could not be removed because the deployment does not exist";
}
}
JipletLogger
.info("Received a request from the J2ee server to delete a context: "
+ name);
synchronized (deployedContexts)
{
JipletContext context = findContext(name);
if (context == null)
{
return "The context " + name + " does not exist";
}
if (context.getContextType() != JipletContext.CONTEXT_J2EE)
{
return "The context "
+ name
+ " is not a J2EE-deployed context. It cannot be deleted";
}
context.destroy();
deployedContexts.remove(name);
}
JipletLogger.info("Context " + name
+ " deleted based on J2EE deployer request");
Notification n = new Notification("Container", this.getClass()
.getName(), seqNum++, System.currentTimeMillis(), "Context: "
+ name + " deleted");
sendNotification(n);
return "";
}
/**
* @return Returns the realms.
*/
protected ArrayList getRealms()
{
return realms;
}
public Realm findRealm(String name)
{
synchronized (realms)
{
Iterator iter = realms.iterator();
while (iter.hasNext() == true)
{
Realm r = (Realm) iter.next();
String rname = null;
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(
r.getClass().getClassLoader());
rname = r.getRealmName();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
if (rname.equals(name) == true)
{
return r;
}
}
}
return null;
}
/**
* @return Returns the defaultRealm.
*/
public String getDefaultRealm()
{
return defaultRealm;
}
/**
* @return Returns the authCachePeriod.
*/
protected long getAuthCachePeriod()
{
return authCachePeriod;
}
/*
* @see org.cafesip.jiplet.JipletContainerMBean#listRealms()
*/
public String[] listRealms()
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
JipletLogger.info("Received a JMX request for listing realms");
String[] ret = null;
synchronized (realms)
{
int size = realms.size();
ret = new String[size];
for (int i = 0; i < size; i++)
{
Realm realm = (Realm) realms.get(i);
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
ret[i] = realm.getRealmName();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
}
return ret;
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#createRealmUser(java.lang.String,
* java.lang.String, java.lang.String, java.lang.String[])
*/
public String createRealmUser(String name, String user, String password,
String[] roles)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
JipletLogger.info("Received a JMX request for creating user " + user
+ " for domain " + name);
synchronized (realms)
{
int size = realms.size();
for (int i = 0; i < size; i++)
{
Realm realm = (Realm) realms.get(i);
String realm_name = null;
boolean supports_prov = false;
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
realm_name = realm.getRealmName();
supports_prov = realm.supportsProvisioning();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
if (name.equals(realm_name) == true)
{
if (supports_prov == false)
{
return "This realm does not support provisioning";
}
boolean ret;
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
ret = realm.addUser(user, password, roles);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
if (ret == false)
{
return "The user already exists";
}
Notification n = new Notification("Container", this
.getClass().getName(), seqNum++, System
.currentTimeMillis(), "Added user " + user
+ " to realm " + name);
sendNotification(n);
return "";
}
}
return "The realm could not be found";
}
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#modifyRealmUser(java.lang.String,
* java.lang.String, java.lang.String, java.lang.String[])
*/
public String modifyRealmUser(String name, String user, String password,
String[] roles)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
JipletLogger.info("Received a JMX request for modifying user " + user
+ " for domain " + name);
synchronized (realms)
{
int size = realms.size();
for (int i = 0; i < size; i++)
{
Realm realm = (Realm) realms.get(i);
String realm_name = null;
boolean supports_prov = false;
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
realm_name = realm.getRealmName();
supports_prov = realm.supportsProvisioning();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
if (name.equals(realm_name) == true)
{
if (supports_prov == false)
{
return "This realm does not support provisioning";
}
boolean ret;
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
ret = realm.modifyUser(user, password, roles);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
if (ret == false)
{
return "The user already exists";
}
if (ret == false)
{
return "The user could not be found";
}
Notification n = new Notification("Container", this
.getClass().getName(), seqNum++, System
.currentTimeMillis(), "Modified user " + user
+ " belonging to realm " + name);
sendNotification(n);
return "";
}
}
return "The realm could not be found";
}
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#deleteRealmUser(java.lang.String,
* java.lang.String)
*/
public String deleteRealmUser(String name, String user)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
JipletLogger.info("Received a JMX request for deleting user " + user
+ " from domain " + name);
synchronized (realms)
{
int size = realms.size();
for (int i = 0; i < size; i++)
{
Realm realm = (Realm) realms.get(i);
String realm_name = null;
boolean supports_prov = false;
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
realm_name = realm.getRealmName();
supports_prov = realm.supportsProvisioning();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
if (name.equals(realm_name) == true)
{
if (supports_prov == false)
{
return "This realm does not support provisioning";
}
boolean ret;
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
ret = realm.deleteUser(user);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
if (ret == false)
{
return "The user does not exist";
}
Notification n = new Notification("Container", this
.getClass().getName(), seqNum++, System
.currentTimeMillis(), "User " + user
+ " deleted from realm " + name);
sendNotification(n);
return "";
}
}
return "The realm could not be found";
}
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#getRealmUser(java.lang.String,
* java.lang.String)
*/
public String[] getRealmUser(String name, String user)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
synchronized (realms)
{
int size = realms.size();
for (int i = 0; i < size; i++)
{
Realm realm = (Realm) realms.get(i);
String realm_name = null;
boolean supports_prov = false;
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
realm_name = realm.getRealmName();
supports_prov = realm.supportsProvisioning();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
if (name.equals(realm_name) == true)
{
if (supports_prov == false)
{
return null;
}
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
return realm.getRoles(user);
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
}
return null;
}
}
protected boolean isAuthOnLogout()
{
return authOnLogout;
}
private String deployRealm(String path, int type, ClassLoader cl)
{
File f = new File(path);
String name = null;
try
{
boolean srr = false;
if (FileUtils.validDeployableSrr(path))
{
srr = true;
name = FileUtils.parseRealmDirNameFromSrr(f);
}
else if (FileUtils.validDeployableRealmDir(path))
{
srr = false;
name = f.getName();
}
else
{
return "The realm could not be created because the path does not have a valid signature";
}
FileUtils ant = new FileUtils();
File exploded = new File(deployDir, name);
boolean extr = false;
if (srr)
{
if (FileUtils.validDeployableRealmDir(exploded
.getAbsolutePath()))
{
if (f.lastModified() > exploded.lastModified())
{
if (JipletLogger.isDebugEnabled())
{
JipletLogger
.info("Found older exploded realm entry "
+ exploded.getAbsolutePath()
+ " while creating the realm on directory "
+ name + ". Deleting the directory");
}
ant.deleteDir(exploded.getAbsolutePath());
extr = true;
}
}
else
{
if (exploded.isDirectory())
{
ant.deleteDir(exploded.getAbsolutePath());
}
extr = true;
}
}
// new code
boolean internal = deployDirExists(deployDir, path);
if (srr || !internal)
{
if (FileUtils.validDeployableRealmDir(exploded
.getAbsolutePath()))
{
if (f.lastModified() > exploded.lastModified())
{
if (JipletLogger.isDebugEnabled())
{
JipletLogger
.info("Found older exploded context entry "
+ exploded.getAbsolutePath()
+ " while creating the realm "
+ name + ". Deleting the directory");
}
ant.deleteDir(exploded.getAbsolutePath());
extr = true;
}
}
else
{
if (exploded.isDirectory())
{
ant.deleteDir(exploded.getAbsolutePath());
}
extr = true;
}
}
// new code ends
if (extr)
{
if (srr)
{
if (!ant.extractSpr(f.getAbsolutePath(), deployDir
.getAbsolutePath(), name))
{
JipletLogger
.error("Error extracting realm with directory "
+ name
+ " from the corresponding srr file");
return "The zipped context could not be extracted";
}
}
else
// exploded
{
if (!ant.copy(path, deployDir.getAbsolutePath()))
{
JipletLogger.error("Error copying exploded realm "
+ name + " file: " + path
+ " to deploy directory");
return "The exploded realm could not be copied to the deploy dir";
}
}
}
File realmxml = new File(exploded, "META-INF/realm.xml");
FileInputStream istream = new FileInputStream(realmxml);
JipRealm realm = (JipRealm) JAXBContext.newInstance(
"org.cafesip.jiplet.config.server",
this.getClass().getClassLoader()).createUnmarshaller()
.unmarshal(istream);
istream.close();
if (findRealm(realm.getName()) != null)
{
return "The realm could not be created because a realm with the same name already exists";
}
// create a classloader for this realm
// add the classes directory
File c = new File(exploded, "classes");
if (!c.isDirectory())
{
// does not exist or is not a directory
c = null;
}
// next, add all the jars under the lib directory
File j = new File(exploded, "lib");
if (!j.isDirectory())
{
j = null;
}
ClassLoader pcl = getClass().getClassLoader();
if (cl != null)
{
pcl = cl;
}
ClassLoader loader = JipletClassLoader.getClassLoader(pcl, c, j);
startRealm(realm, loader, exploded.getAbsolutePath());
}
catch (Exception e)
{
JipletLogger.error("Error creating realm: "
+ e.getClass().getName() + ": " + e.getMessage() + "\n"
+ JipletLogger.getStackTrace(e));
return "Realm: " + name + " could not be created. Message: "
+ e.getMessage() + ". See log for details";
}
return "";
}
private void startRealm(JipRealm realm, ClassLoader loader,
String deployPath) throws Exception
{
Iterator i = realm.getRealmParams().getRealmParam().iterator();
HashMap map = new HashMap();
while (i.hasNext() == true)
{
JipRealmParam param = (JipRealmParam) i.next();
map.put(param.getRealmParamName(), param.getRealmParamValue());
}
if (loader == null)
{
loader = this.getClass().getClassLoader();
}
Class cl = Class.forName(realm.getClassname(), true, loader);
Class[] interfaces = cl.getInterfaces();
boolean found = false;
for (int j = 0; j < interfaces.length; j++)
{
if (interfaces[j].equals(Realm.class) == true)
{
found = true;
break;
}
}
if (found == false)
{
throw new JipletException("Realm " + realm.getName()
+ " does not implement the interface "
+ Realm.class.getName());
}
Realm r = null;
ClassLoader tcl = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(loader);
r = (Realm) cl.newInstance();
r.init(realm.getName(), confDir, map, realm.isDefault());
}
finally
{
Thread.currentThread().setContextClassLoader(tcl);
}
JipletLogger.info("Realm " + realm.getName() + " started");
if (realm.isDefault() == true)
{
defaultRealm = realm.getName();
}
if (defaultRealm == null)
{
defaultRealm = realm.getName();
}
synchronized (this.realms)
{
this.realms.add(r);
}
if (deployPath != null)
{
synchronized (dirRealmsMap)
{
dirRealmsMap.put(deployPath, r);
realmsDirMap.put(r.getRealmName(), deployPath);
}
}
}
/**
* Create a J2EE realm. The Jboss wrapper application for the jiplet
* container uses this method to register srr files deployed in the JBOSS
* deploy directory.
*
* @param path
* @param loader
* @return
*/
public String createJ2eeRealm(String path, ClassLoader loader)
{
JipletLogger
.info("Received a request from J2EE deployer to add a realm with path: "
+ path);
String err = deployRealm(path, REALM_J2EE, loader);
if (err.length() > 0)
{
JipletLogger.error("Error creating J2EE realm: " + err);
return err;
}
String name = FileUtils.parseRealmDirNameFromSrr(new File(path));
JipletLogger.info("Realm " + name
+ " created based on J2EE deployer request");
Notification n = new Notification("Container", this.getClass()
.getName(), seqNum++, System.currentTimeMillis(), "Realm: "
+ name + " added with path: " + path);
sendNotification(n);
return "";
}
/**
* The JBOSS wrapper application uses this method to delete a realm when it
* has been removed from the JBOSS deploy directory.
*
* @param path
* @return
*/
public String deleteJ2eeRealm(String path)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
String name = null;
File f = new File(path);
if (path.endsWith(FileUtils.SRR_EXTENSION) == true)
{
name = FileUtils.parseRealmDirNameFromSrr(f);
}
else
{
name = f.getName();
}
File deploy = new File(deployDir, name);
if (deploy.isDirectory() == false)
{
return "The realm could not be remove because the deployment does not exist";
}
JipletLogger
.info("Received a request from the J2EE server to delete a realm with directory name: "
+ name);
Realm realm = null;
synchronized (dirRealmsMap)
{
realm = (Realm) dirRealmsMap.get(deploy.getAbsolutePath());
if (realm == null)
{
return "The realm with deployment directory "
+ deployDir.getAbsolutePath()
+ " does not have an entry in the realms map. Aborting.";
}
synchronized (deployedContexts)
{
Iterator i = deployedContexts.entrySet().iterator();
while (i.hasNext() == true)
{
Map.Entry entry = (Map.Entry) i.next();
JipletContext context = (JipletContext) entry.getValue();
context.getSecurityManager().removeRealm(realm);
}
}
String realm_name = null;
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
realm_name = realm.getRealmName();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
dirRealmsMap.remove(deploy.getAbsolutePath());
realmsDirMap.remove(realm_name);
synchronized (realms)
{
realms.remove(realm);
}
// tell the realm to clean up
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
realm.destroy();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
}
JipletLogger.info("Realm " + realm.getRealmName()
+ " deleted based on J2EE deployer request");
Notification n = new Notification("Container", this.getClass()
.getName(), seqNum++, System.currentTimeMillis(), "Realm: "
+ realm.getRealmName() + " deleted");
sendNotification(n);
return "";
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#createRealm(java.lang.String)
*/
public String createRealm(String path)
{
JipletLogger
.info("Received a request from the JMX agent to add a realm with path: "
+ path);
String err = deployRealm(path, REALM_DEPLOYED, null);
if (err.length() > 0)
{
return err;
}
String name = FileUtils.parseContextNameFromSpr(new File(path));
JipletLogger.info("Realm with directory " + name
+ " created based on JMX request");
Notification n = new Notification("Container", this.getClass()
.getName(), seqNum++, System.currentTimeMillis(),
"Context with directory name: " + name + " added with path: "
+ path);
sendNotification(n);
return "";
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#deleteRealm(java.lang.String)
*/
public String deleteRealm(String realmName)
{
JipletLogger
.info("Received a request from the JMX agent to delete a realm with name: "
+ realmName);
synchronized (dirRealmsMap)
{
String path = (String) realmsDirMap.get(realmName);
if (path == null)
{
return "The path was not found for the given realm name."
+ " Either the realm does not exist or the realm is not a deployed realm (is a part of the server.xml)";
}
Realm realm = (Realm) dirRealmsMap.get(path);
if (realm == null)
{
return "The realm " + realmName
+ " could not be mapped to a realm object. Aborting.";
}
synchronized (deployedContexts)
{
Iterator i = deployedContexts.entrySet().iterator();
while (i.hasNext() == true)
{
Map.Entry entry = (Map.Entry) i.next();
JipletContext context = (JipletContext) entry.getValue();
context.getSecurityManager().removeRealm(realm);
}
}
dirRealmsMap.remove(path);
realmsDirMap.remove(realmName);
synchronized (realms)
{
realms.remove(realm);
}
// tell the realm to clean up
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
realm.destroy();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
// remove the directories and files
File spr_file = new File(path + FileUtils.SPR_EXTENSION);
if (spr_file.exists() == true)
{
if (spr_file.delete() == false)
{
JipletLogger
.error("While deleting realm "
+ realmName
+ ", the realm file "
+ spr_file.getAbsolutePath()
+ " could not be deleted. On re-start, the realm will get started up.");
}
}
FileUtils futils = new FileUtils();
if (futils.deleteDir(path) == false)
{
JipletLogger.warn("While deleting realm " + realmName
+ ", the realm directory " + path
+ " could not be deleted.");
// mess up the deployment descriptor name so that it does not
// get recorgnized
// as a valid srr deployment during startup.
File realmxml = new File(path, "META-INF/realm.xml");
File mess_name = new File(path, "META-INF/realm.xml.deleted");
if (realmxml.renameTo(mess_name) == false)
{
JipletLogger
.error("While deleting realm "
+ realmName
+ ", the realm directory "
+ path
+ " could not be mangled. On re-start, the realm will get started up.");
}
}
}
JipletLogger.info("Realm " + realmName
+ " deleted based on JMX request");
Notification n = new Notification("Container", this.getClass()
.getName(), seqNum++, System.currentTimeMillis(), "Realm: "
+ realmName + " deleted");
sendNotification(n);
return "";
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#getRealmDirName(java.lang.String)
*/
public String getRealmDirName(String realmName)
{
synchronized (dirRealmsMap)
{
String path = (String) realmsDirMap.get(realmName);
if (path == null)
{
return null;
}
File f = new File(path);
return f.getName();
}
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#getRealmProperty(java.lang.String
* )
*/
public RealmElement getRealmProperty(String realmName)
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
synchronized (realms)
{
Iterator i = realms.iterator();
while (i.hasNext() == true)
{
Realm realm = (Realm) i.next();
String rname = null;
try
{
Thread.currentThread().setContextClassLoader(
realm.getClass().getClassLoader());
rname = realm.getRealmName();
}
finally
{
Thread.currentThread().setContextClassLoader(cl);
}
if (rname.equals(realmName) == true)
{
RealmElement element = new RealmElement();
element.setName(realmName);
element.setDefaultRealm(realm.isDefaultRealm());
element.setParams(realm.getParams());
element.setDeployDir((String) realmsDirMap.get(realmName));
return element;
}
}
}
return null;
}
protected ArrayList getContextMapping(String context)
{
ArrayList list = new ArrayList();
if (contextMappings == null)
{
return list;
}
Iterator i = contextMappings.getContextMapping().iterator();
while (i.hasNext() == true)
{
ContextMapping cmap = (ContextMapping) i.next();
if (cmap.getContext().equals(context) == true)
{
list.add(cmap);
}
}
return list;
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#addContextMapping(java.lang.String
* )
*/
public String addContextMapping(String context, String mapping)
{
try
{
ObjectFactory factory = new ObjectFactory();
if (contextMappings == null)
{
contextMappings = factory.createContextMappings();
}
synchronized (contextMappings)
{
// next, unmarshall the mapping object
JipletContextMappings rmappings = (JipletContextMappings) JAXBContext
.newInstance("org.cafesip.jiplet.config.jip",
this.getClass().getClassLoader())
.createUnmarshaller().unmarshal(
new InputSource(new StringReader(mapping)));
// next remove any references to the context left from earlier
// deployment
Iterator i = contextMappings.getContextMapping().iterator();
while (i.hasNext() == true)
{
ContextMapping cmap = (ContextMapping) i.next();
if (cmap.getContext().equals(context) == true)
{
i.remove();
}
}
// copy the received map object into the cloned map object
i = rmappings.getContextMapping().iterator();
while (i.hasNext() == true)
{
ContextMapping m = (ContextMapping) i.next();
contextMappings.getContextMapping().add(m);
}
// everything looks good. Marshall the content to the
// mapping.xml file.
saveMappingXml();
}
return "";
}
catch (Exception e)
{
JipletLogger.error("Exception " + e.getClass().getName()
+ " occured while adding context mapping: "
+ e.getMessage() + "\n" + JipletLogger.getStackTrace(e));
return e.getClass().getName()
+ " occured while adding context mapping: "
+ e.getMessage();
}
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#removeContextMapping(java.lang
* .String)
*/
public String removeContextMapping(String context)
{
try
{
if (contextMappings == null)
{
return "";
}
boolean removed = false;
synchronized (contextMappings)
{
Iterator i = contextMappings.getContextMapping().iterator();
while (i.hasNext() == true)
{
ContextMapping cmap = (ContextMapping) i.next();
if (cmap.getContext().equals(context) == true)
{
i.remove();
removed = true;
}
}
}
if (removed == true)
{
// everything looks good. Marshall the content to the
// mapping.xml file.
saveMappingXml();
}
return "";
}
catch (Exception e)
{
JipletLogger.error("Exception " + e.getClass().getName()
+ " occured while removing context mapping: "
+ e.getMessage() + "\n" + JipletLogger.getStackTrace(e));
return e.getClass().getName()
+ " occured while removing context mapping: "
+ e.getMessage();
}
}
private void saveMappingXml() throws JAXBException, IOException
{
File f = new File(confDir, "mapping.xml");
FileOutputStream os = new FileOutputStream(f);
Marshaller m = JAXBContext.newInstance("org.cafesip.jiplet.config.jip",
this.getClass().getClassLoader()).createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, new Boolean(true));
m.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
"context-mappings_2.0.xsd");
m.marshal(contextMappings, os);
os.close();
}
/*
* @see org.cafesip.jiplet.JipletContainerMBean#listConnectors()
*/
public String[] listConnectors()
{
JipletLogger.info("Received a JMX request for listing connectors");
ArrayList list = new ArrayList();
synchronized (connectors)
{
Iterator i = connectors.keySet().iterator();
while (i.hasNext() == true)
{
String name = (String) i.next();
list.add(name);
}
String[] ret = new String[list.size()];
list.toArray(ret);
return ret;
}
}
/*
* @see
* org.cafesip.jiplet.JipletContainerMBean#getConnectorProperty(java.lang
* .String)
*/
public ConnectorElement getConnectorProperty(String connectorName)
{
JipletLogger
.info("Received a JMX request for getting connector property for "
+ connectorName);
synchronized (connectors)
{
SipConnector connector = (SipConnector) connectors
.get(connectorName);
if (connector == null)
{
return null;
}
ConnectorElement element = new ConnectorElement();
element.setName(connectorName);
element.setDefaultConnector(defaultConnectorName
.equals(connectorName));
element.setPorts(connector.getPortInfo());
element.setStack(connector.getStackImpl());
element.setProperties(connector.getProperties());
element.setMaxThreads(connector.getMaxThreadCount());
element.setMinThreads(connector.getMinThreadCount());
return element;
}
}
/**
* @return Returns the confDir.
*/
public static File getConfDir()
{
return confDir;
}
/**
* @param confDir
* The confDir to set.
*/
public static void setConfDir(File confDir)
{
JipletContainer.confDir = confDir;
}
/**
* @return Returns the vendorDeploymentFactory.
*/
public static VendorDescriptorFactory getVendorDeploymentFactory()
{
return vendorDeploymentFactory;
}
/**
* @param vendorDeploymentFactory
* The vendorDeploymentFactory to set.
*/
public static void setVendorDeploymentFactory(
VendorDescriptorFactory vendorDeploymentFactory)
{
JipletContainer.vendorDeploymentFactory = vendorDeploymentFactory;
}
}