/*
* 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.mobicents.servlet.sip;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.StringTokenizer;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import org.apache.catalina.Container;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.loader.StandardClassLoader;
import org.apache.catalina.security.SecurityClassLoad;
import org.apache.catalina.security.SecurityConfig;
import org.apache.catalina.startup.CatalinaProperties;
import org.apache.coyote.http11.Http11Protocol;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
import org.apache.tomcat.util.IntrospectionUtils;
import org.mobicents.servlet.sip.core.SipApplicationDispatcherImpl;
import org.mobicents.servlet.sip.core.session.SipStandardManager;
import org.mobicents.servlet.sip.startup.SipContextConfig;
import org.mobicents.servlet.sip.startup.SipHostConfig;
import org.mobicents.servlet.sip.startup.SipProtocolHandler;
import org.mobicents.servlet.sip.startup.SipStandardContext;
import org.mobicents.servlet.sip.startup.SipStandardEngine;
import org.mobicents.servlet.sip.startup.SipStandardService;
/**
* This class is emulating an embedded tomcat configured with sip servlets extension
* to allow deployment of sip servlets apps to it. It is for the test suite purposes only for now...
*
* @author Jean Deruelle
* @author Vladimir Ralev
*/
public class SipEmbedded {
private static transient Logger log = Logger.getLogger(SipEmbedded.class);
private String loggingFilePath = null;
private String darConfigurationFilePath = null;
private String path = null;
private SipStandardService sipService = null;
private StandardHost host = null;
private String serviceFullClassName;
private String serverName;
/**
* Default Constructor
*
*/
public SipEmbedded(String serverName, String serviceFullClassName) {
this.serviceFullClassName = serviceFullClassName;
this.serverName = serverName;
}
/**
* Basic Accessor setting the value of the context path
*
* @param path -
* the path
*/
public void setPath(String path) {
this.path = path;
}
/**
* Basic Accessor returning the value of the context path
*
* @return - the context path
*/
public String getPath() {
return path;
}
/**
* Init the tomcat server
* @param tomcatBasePath the base path of the server
* @throws Exception
*/
public void initTomcat(String tomcatBasePath) throws Exception {
setPath(tomcatBasePath);
// Set the home directory
// System.setProperty("CATALINA_HOME", getPath());
// System.setProperty("CATALINA_BASE", getPath());
// System.setProperty("catalina.home", getPath());
// System.setProperty("catalina.base", getPath());
//logging configuration
System.setProperty("java.util.logging.config.file", loggingFilePath + "logging.properties");
DOMConfigurator.configure(loggingFilePath + "log4j.xml");
// BasicConfigurator.configure();
// PropertyConfigurator.configure(loggingFilePath);
//Those are for trying to make it work under mvn test command
// don't know why but some jars aren't loaded
setSecurityProtection();
initDirs();
initNaming();
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
/*
* <Service className="org.mobicents.servlet.sip.startup.SipStandardService"
* darConfigurationFileLocation="file:///E:/sip-serv/sip-servlets-impl/docs/dar.properties"
* name="Catalina"
* sipApplicationDispatcherClassName="org.mobicents.servlet.sip.core.SipApplicationDispatcherImpl"
* sipApplicationRouterClassName="org.mobicents.servlet.sip.router.DefaultApplicationRouter">
*/
// Create an embedded server
sipService = (SipStandardService) Class.forName(serviceFullClassName).newInstance();
sipService.setName(serverName);
sipService.setSipApplicationDispatcherClassName(SipApplicationDispatcherImpl.class.getName());
// sipService.setSipApplicationRouterClassName(DefaultApplicationRouter.class.getName());
sipService.setDarConfigurationFileLocation(darConfigurationFilePath);
sipService.setCongestionControlCheckingInterval(30000);
sipService.setAdditionalParameterableHeaders("additionalParameterableHeader");
// sipService.setBypassRequestExecutor(true);
// sipService.setBypassResponseExecutor(true);
// Create an engine
SipStandardEngine engine = new SipStandardEngine();
engine.setName(serverName);
engine.setBaseDir(getPath());
engine.setDefaultHost("localhost");
engine.setService(sipService);
// Install the assembled container hierarchy
sipService.setContainer(engine);
sipService.init();
// Create a default virtual host
// host = (StandardHost) embedded.createHost("localhost", getPath() + "/webapps");
host = new StandardHost();
host.setAppBase(getPath() + "/webapps");
host.setName("localhost");
host.setConfigClass(StandardContext.class.getName());
host.setAppBase("webapps");
host.addLifecycleListener(new SipHostConfig());
host.setAutoDeploy(false);
host.setDeployOnStartup(false);
engine.addChild(host);
}
/**
* @throws Exception
*/
public void addHttpConnector(int port) throws Exception {
Connector httpConnector = new Connector(
Http11Protocol.class.getName());
Http11Protocol httpProtocolHandler = (Http11Protocol) httpConnector
.getProtocolHandler();
httpProtocolHandler.setPort(port);
httpProtocolHandler.setDisableUploadTimeout(true);
httpProtocolHandler.setMaxHttpHeaderSize(8192);
// httpProtocolHandler.setMaxSpareThreads(75);
// httpProtocolHandler.setMinSpareThreads(75);
httpProtocolHandler.setMaxThreads(150);
sipService.addConnector(httpConnector);
}
/**
* This method Starts the Tomcat server.
*/
public void startTomcat() throws Exception {
// Start the embedded server
sipService.start();
}
/**
* @throws Exception
*/
public void addSipConnector(String connectorName, String ipAddress, int port, String transport) throws Exception {
Connector udpSipConnector = new Connector(
SipProtocolHandler.class.getName());
SipProtocolHandler udpProtocolHandler = (SipProtocolHandler) udpSipConnector
.getProtocolHandler();
udpProtocolHandler.setPort(port);
udpProtocolHandler.setIpAddress(ipAddress);
udpProtocolHandler.setSignalingTransport(transport);
udpProtocolHandler.setUsePrettyEncoding(true);
// udpProtocolHandler.setSipStackPropertiesFile("file:///" + loggingFilePath + "mss-sip-stack.properties");
sipService.addConnector(udpSipConnector);
}
/**
*
* @return
*/
public Connector[] findConnectors() {
return sipService.findConnectors();
}
/**
*
* @param connector
*/
public void removeConnector(Connector connector) {
sipService.removeConnector(connector);
}
/**
* This method Stops the Tomcat server.
*/
public void stopTomcat() {
// Stop the embedded server
if(sipService != null) {
try {
sipService.stop();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
}
/**
* Deploy a context to the embedded tomcat container
* @param contextPath the context Path of the context to deploy
*/
public boolean deployContext(String docBase, String name, String path) {
SipStandardContext context = new SipStandardContext();
context.setDocBase(docBase);
context.setName(name);
context.setPath(path);
context.setParent(host);
context.addLifecycleListener(new SipContextConfig());
context.setManager(new SipStandardManager());
host.addChild(context);
return context.getAvailable();
}
public boolean deployContext(SipStandardContext context) {
context.setParent(host);
host.addChild(context);
return context.getAvailable();
}
public void undeployContext(Container context) {
host.removeChild(context);
}
public String getDarConfigurationFilePath() {
return darConfigurationFilePath;
}
public void setDarConfigurationFilePath(String darConfigurationFilePath) {
this.darConfigurationFilePath = darConfigurationFilePath;
}
public String getLoggingFilePath() {
return loggingFilePath;
}
public void setLoggingFilePath(String loggingFilePath) {
this.loggingFilePath = loggingFilePath;
}
/**
* Is naming enabled ?
*/
protected boolean useNaming = true;
protected static final String CATALINA_HOME_TOKEN = "${catalina.home}";
protected static final String CATALINA_BASE_TOKEN = "${catalina.base}";
protected static final Integer IS_DIR = Integer.valueOf(0);
protected static final Integer IS_JAR = Integer.valueOf(1);
protected static final Integer IS_GLOB = Integer.valueOf(2);
protected static final Integer IS_URL = Integer.valueOf(3);
protected ClassLoader commonLoader = null;
protected ClassLoader catalinaLoader = null;
protected ClassLoader sharedLoader = null;
private void initClassLoaders() throws Exception {
commonLoader = createClassLoader(serverName + "common", null);
if( commonLoader == null ) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader=this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader(serverName + "server", commonLoader);
sharedLoader = createClassLoader(serverName + "shared", commonLoader);
}
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
ArrayList repositoryLocations = new ArrayList();
ArrayList repositoryTypes = new ArrayList();
int i;
StringTokenizer tokenizer = new StringTokenizer(value, ",");
while (tokenizer.hasMoreElements()) {
String repository = tokenizer.nextToken();
// Local repository
boolean replace = false;
String before = repository;
while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) {
replace=true;
if (i>0) {
repository = repository.substring(0,i) + getCatalinaHome()
+ repository.substring(i+CATALINA_HOME_TOKEN.length());
} else {
repository = getCatalinaHome()
+ repository.substring(CATALINA_HOME_TOKEN.length());
}
}
while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) {
replace=true;
if (i>0) {
repository = repository.substring(0,i) + getCatalinaBase()
+ repository.substring(i+CATALINA_BASE_TOKEN.length());
} else {
repository = getCatalinaBase()
+ repository.substring(CATALINA_BASE_TOKEN.length());
}
}
if (replace && log.isDebugEnabled())
log.debug("Expanded " + before + " to " + replace);
// Check for a JAR URL repository
try {
URL url=new URL(repository);
repositoryLocations.add(repository);
repositoryTypes.add(IS_URL);
continue;
} catch (MalformedURLException e) {
// Ignore
}
if (repository.endsWith("*.jar")) {
repository = repository.substring
(0, repository.length() - "*.jar".length());
repositoryLocations.add(repository);
repositoryTypes.add(IS_GLOB);
} else if (repository.endsWith(".jar")) {
repositoryLocations.add(repository);
repositoryTypes.add(IS_JAR);
} else {
repositoryLocations.add(repository);
repositoryTypes.add(IS_DIR);
}
}
String[] locations = (String[]) repositoryLocations.toArray(new String[0]);
Integer[] types = (Integer[]) repositoryTypes.toArray(new Integer[0]);
ClassLoader classLoader = createClassLoader
(locations, types, parent);
// Retrieving MBean server
MBeanServer mBeanServer = null;
if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
mBeanServer =
(MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
} else {
mBeanServer = MBeanServerFactory.createMBeanServer();
}
// Register the server classloader
ObjectName objectName =
new ObjectName(serverName+ ":type=ServerClassLoader,name=" + name);
if(!mBeanServer.isRegistered(objectName))
mBeanServer.registerMBean(classLoader, objectName);
return classLoader;
}
/**
* Get the value of the catalina.home environment variable.
*/
public String getCatalinaHome() {
// return System.getProperty("catalina.home",
// System.getProperty("user.dir"));
return getPath();
}
/**
* Get the value of the catalina.base environment variable.
*/
public String getCatalinaBase() {
// return System.getProperty("catalina.base", getCatalinaHome());
return getPath();
}
/**
* Create and return a new class loader, based on the configuration
* defaults and the specified directory paths:
*
* @param locations Array of strings containing class directories, jar files,
* jar directories or URLS that should be added to the repositories of
* the class loader. The type is given by the member of param types.
* @param types Array of types for the members of param locations.
* Possible values are IS_DIR (class directory), IS_JAR (single jar file),
* IS_GLOB (directory of jar files) and IS_URL (URL).
* @param parent Parent class loader for the new class loader, or
* <code>null</code> for the system class loader.
*
* @exception Exception if an error occurs constructing the class loader
*/
public static ClassLoader createClassLoader(String locations[],
Integer types[],
ClassLoader parent)
throws Exception {
if (log.isDebugEnabled())
log.debug("Creating new class loader");
// Construct the "class path" for this class loader
ArrayList list = new ArrayList();
if (locations != null && types != null && locations.length == types.length) {
for (int i = 0; i < locations.length; i++) {
String location = locations[i];
if ( types[i].equals(IS_URL) ) {
URL url = new URL(location);
if (log.isDebugEnabled())
log.debug(" Including URL " + url);
list.add(url);
} else if ( types[i].equals(IS_DIR) ) {
File directory = new File(location);
directory = new File(directory.getCanonicalPath());
if (!directory.exists() || !directory.isDirectory() ||
!directory.canRead())
continue;
URL url = directory.toURL();
if (log.isDebugEnabled())
log.debug(" Including directory " + url);
list.add(url);
} else if ( types[i].equals(IS_JAR) ) {
File file=new File(location);
file = new File(file.getCanonicalPath());
if (!file.exists() || !file.canRead())
continue;
URL url = file.toURL();
if (log.isDebugEnabled())
log.debug(" Including jar file " + url);
list.add(url);
} else if ( types[i].equals(IS_GLOB) ) {
File directory=new File(location);
if (!directory.exists() || !directory.isDirectory() ||
!directory.canRead())
continue;
if (log.isDebugEnabled())
log.debug(" Including directory glob "
+ directory.getAbsolutePath());
String filenames[] = directory.list();
for (int j = 0; j < filenames.length; j++) {
String filename = filenames[j].toLowerCase();
if (!filename.endsWith(".jar"))
continue;
File file = new File(directory, filenames[j]);
file = new File(file.getCanonicalPath());
if (!file.exists() || !file.canRead())
continue;
if (log.isDebugEnabled())
log.debug(" Including glob jar file "
+ file.getAbsolutePath());
URL url = file.toURL();
list.add(url);
}
}
}
}
// Construct the class loader itself
URL[] array = (URL[]) list.toArray(new URL[list.size()]);
if (log.isDebugEnabled())
for (int i = 0; i < array.length; i++) {
log.debug(" location " + i + " is " + array[i]);
}
StandardClassLoader classLoader = null;
if (parent == null)
classLoader = new StandardClassLoader(array);
else
classLoader = new StandardClassLoader(array, parent);
return (classLoader);
}
/**
* Set the security package access/protection.
*/
protected void setSecurityProtection(){
SecurityConfig securityConfig = SecurityConfig.newInstance();
securityConfig.setPackageDefinition();
securityConfig.setPackageAccess();
}
/** Initialize naming - this should only enable java:env and root naming.
* If tomcat is embeded in an application that already defines those -
* it shouldn't do it.
*
* XXX The 2 should be separated, you may want to enable java: but not
* the initial context and the reverse
* XXX Can we "guess" - i.e. lookup java: and if something is returned assume
* false ?
* XXX We have a major problem with the current setting for java: url
*/
protected void initNaming() {
// Setting additional variables
if (!useNaming) {
log.info( "Catalina naming disabled");
System.setProperty("catalina.useNaming", "false");
} else {
System.setProperty("catalina.useNaming", "true");
String value = "org.apache.naming";
String oldValue =
System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
if (oldValue != null) {
value = value + ":" + oldValue;
}
System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
if( log.isDebugEnabled() )
log.debug("Setting naming prefix=" + value);
value = System.getProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY);
if (value == null) {
System.setProperty
(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
} else {
log.debug( "INITIAL_CONTEXT_FACTORY alread set " + value );
}
}
}
protected void initDirs() {
String catalinaHome = getPath();//System.getProperty("catalina.home");
if (catalinaHome == null) {
// Backwards compatibility patch for J2EE RI 1.3
String j2eeHome = System.getProperty("com.sun.enterprise.home");
if (j2eeHome != null) {
catalinaHome=System.getProperty("com.sun.enterprise.home");
} else if (System.getProperty("catalina.base") != null) {
catalinaHome = System.getProperty("catalina.base");
} else {
// Use IntrospectionUtils and guess the dir
catalinaHome = IntrospectionUtils.guessInstall
("catalina.home", "catalina.base", "catalina.jar");
if (catalinaHome == null) {
catalinaHome = IntrospectionUtils.guessInstall
("tomcat.install", "catalina.home", "tomcat.jar");
}
}
}
// last resort - for minimal/embedded cases.
if(catalinaHome==null) {
catalinaHome=System.getProperty("user.dir");
}
if (catalinaHome != null) {
File home = new File(catalinaHome);
if (!home.isAbsolute()) {
try {
catalinaHome = home.getCanonicalPath();
} catch (IOException e) {
catalinaHome = home.getAbsolutePath();
}
}
System.setProperty("catalina.home", catalinaHome);
}
if (System.getProperty("catalina.base") == null) {
System.setProperty("catalina.base",
catalinaHome);
} else {
String catalinaBase = System.getProperty("catalina.base");
File base = new File(catalinaBase);
if (!base.isAbsolute()) {
try {
catalinaBase = base.getCanonicalPath();
} catch (IOException e) {
catalinaBase = base.getAbsolutePath();
}
}
System.setProperty("catalina.base", catalinaBase);
}
String temp = System.getProperty("java.io.tmpdir");
if (temp == null || (!(new File(temp)).exists())
|| (!(new File(temp)).isDirectory())) {
log.error("no temp directory"+ temp);
}
}
/**
* @return the sipService
*/
public SipStandardService getSipService() {
return sipService;
}
}