All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mobicents.servlet.sip.startup.SipContextConfig Maven / Gradle / Ivy

There is a newer version: 4.0.12
Show newest version
/*
 * TeleStax, Open Source Cloud Communications  Copyright 2012. 
 * and individual contributors
 * by the @authors tag. See the copyright.txt 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.mobicents.servlet.sip.startup;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import javax.servlet.ServletContext;

import org.apache.catalina.Context;
import org.apache.catalina.Host;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.deploy.WebXml;
import org.apache.catalina.startup.Constants;
import org.apache.catalina.startup.ContextConfig;
import org.apache.catalina.startup.DigesterFactory;
import org.apache.catalina.startup.ExpandWar;
import org.apache.catalina.util.ContextName;
import org.apache.log4j.Logger;
import org.apache.naming.resources.DirContextURLConnection;
import org.apache.naming.resources.FileDirContext;
import org.apache.naming.resources.ResourceAttributes;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;
import org.apache.tomcat.util.bcel.classfile.ClassFormatException;
import org.apache.tomcat.util.bcel.classfile.ClassParser;
import org.apache.tomcat.util.bcel.classfile.JavaClass;
import org.apache.tomcat.util.digester.Digester;
import org.mobicents.servlet.sip.annotations.AnnotationVerificationException;
import org.mobicents.servlet.sip.annotations.ClassFileScanner;
import org.mobicents.servlet.sip.catalina.CatalinaSipContext;
import org.mobicents.servlet.sip.catalina.SipDeploymentException;
import org.mobicents.servlet.sip.catalina.SipEntityResolver;
import org.mobicents.servlet.sip.catalina.SipRuleSet;
import org.mobicents.servlet.sip.core.SipContext;
import org.xml.sax.EntityResolver;

/**
 * Startup event listener for a the SipStandardContext that configures
 * the properties of that Context, and the associated defined servlets.
 * it extends the regular tomcat context config to be able to load sip
 * servlet applications.
 * 
 * @author Jean Deruelle
 * 
 */
public class SipContextConfig extends ContextConfig {	

	private static transient final Logger logger = Logger.getLogger(SipContextConfig.class);
	//Issue 77: http://code.google.com/p/sipservlets/issues/detail?id=77
	//commenting out to avoid double parsing of annotations if there is some  
//	private Boolean hasWebAnnotations = false;


	/**
	 * {@inheritDoc}
	 */
	public void lifecycleEvent(LifecycleEvent event) {
		try {
			super.lifecycleEvent(event);
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	@Override
	protected synchronized void configureStart() {
		super.configureStart();
		if(context instanceof SipContext) {
			if(logger.isDebugEnabled()) {
				logger.debug("starting sipContextConfig");
			}
			ServletContext servletContext = context.getServletContext();
			
			// commenting out to avoid double parsing of annotations if there is some and calling 
			// super.configureStart at the beginning of the method to make sure we always parse annotations 
			
			// calling start on the parent to initialize web resources of the web
			// app if any. That mean that this is a converged application.
//			InputStream webXmlInputStream = servletContext
//				.getResourceAsStream(Constants.ApplicationWebXml);
//			context.setWrapperClass(StandardWrapper.class.getName());						
//			if (webXmlInputStream != null) {
//				if(logger.isDebugEnabled()) {
//					logger.debug(Constants.ApplicationWebXml + " has been found, calling super.start() !");
//				}
//				super.configureStart();
//			} else {
//				//Java Servlets 3.0 makes web.xml optional if the servlet is using annotations.
//				//So here we check for web servlet annotations.
//				checkWebAnnotations();
//			}
//
//			if(hasWebAnnotations){
//				super.configureStart();
//			}

			context.setWrapperClass(org.mobicents.servlet.sip.catalina.SipServletImpl.class.getName());

			//annotations scanning
			ClassFileScanner scanner = new ClassFileScanner(((CatalinaSipContext)context).getBasePath(), (CatalinaSipContext)context);
			try {
				scanner.scan();
			} catch (AnnotationVerificationException ave) {
				logger.error("An annotation didn't follow its annotation contract",
						ave);
				ok = false;
			}					

			String applicationSipXmlPath = SipContext.APPLICATION_SIP_XML;
			InputStream sipXmlInputStream = servletContext
					.getResourceAsStream(applicationSipXmlPath);			
			if(sipXmlInputStream == null) {
				// http://code.google.com/p/sipservlets/issues/detail?id=167 Deal with strict compliance
				if(logger.isInfoEnabled()) {
					logger.info(applicationSipXmlPath + " has not been found, checking with a leading slash as servlet compliance might be enabled");
				}
				applicationSipXmlPath = "/" + SipContext.APPLICATION_SIP_XML;
				sipXmlInputStream = servletContext
						.getResourceAsStream(applicationSipXmlPath);
				
			}
			// processing of the sip.xml file
			if (sipXmlInputStream != null) {
				if(logger.isDebugEnabled()) {
					logger.debug(applicationSipXmlPath + " has been found !");
				}								

				Digester sipDigester =  DigesterFactory.newDigester(context.getXmlValidation(),
						context.getXmlNamespaceAware(),
						new SipRuleSet());
				EntityResolver entityResolver = new SipEntityResolver();
				sipDigester.setValidating(false);		
				sipDigester.setEntityResolver(entityResolver);				
				//push the context to the digester
				sipDigester.push(context);
				sipDigester.setClassLoader(context.getClass().getClassLoader());
				//parse the sip.xml and populate the context with it
				try {
					sipDigester.resolveEntity(null, null);
					sipDigester.parse(sipXmlInputStream);
				} catch (Throwable e) {
					logger.warn("Impossible to parse the sip.xml deployment descriptor");
					ok = false;
				}
			} else {
				if(logger.isInfoEnabled()) {
					logger.info(applicationSipXmlPath + " has not been found !");
				}
				ok = false;
			}	

			// Use description from the annotations no matter if sip.xml parsing failed.
			if(scanner.isApplicationParsed()) { 
				ok = true;
			}

			checkSipDeploymentRequirements(context);

			if(!scanner.isApplicationParsed() && sipXmlInputStream != null) {
				context.setWrapperClass(StandardWrapper.class.getName());
			}

			// Make our application available if no problems were encountered
			if (ok) {
				if(logger.isDebugEnabled()) {
					logger.debug("sipContextConfig started");
				}
				context.setConfigured(true);						
			} else {
				logger.warn("sipContextConfig didn't start properly");
				context.setConfigured(false);
			}			
		} 
//		else {
//			super.configureStart();
//		}				
	}

	private void checkSipDeploymentRequirements(Context context) {
		if(((SipContext) context).getApplicationName() == null) {
			ok = false;
			context.setConfigured(false);				
			throw new SipDeploymentException("No app-name present in the sip.xml deployment descriptor or no SipApplication annotation defined");
		}

		boolean servletSelectionSet = false;
		String mainServlet = ((SipContext) context).getMainServlet();
		if(mainServlet != null && mainServlet.length() > 0) {
			servletSelectionSet = true;
		} else if(((SipContext) context).findSipServletMappings() != null && ((SipContext) context).findSipServletMappings().size() > 0) {
			servletSelectionSet = true;
		} else if(((SipContext) context).getSipRubyController() != null) {
			servletSelectionSet = true;
		}

		if(((SipContext) context).getChildrenMap().keySet().size() > 1 && !servletSelectionSet) {
			ok = false;
			context.setConfigured(false);
			throw new SipDeploymentException("the main servlet is not set and there is more than one servlet defined in the sip.xml or as annotations !");
		}
	}


	@Override
	protected synchronized void configureStop() {
		if(logger.isDebugEnabled()) {
			logger.debug("stopping sipContextConfig");
		}
		super.configureStop();
		if(logger.isDebugEnabled()) {
			logger.debug("sipContextConfig stopped");
		}
	}

	/**
	 * Adjust docBase.
	 */
	protected void fixDocBase() throws IOException {
		if(context instanceof SipContext) {
			Host host = (Host) context.getParent();
			String appBase = host.getAppBase();
			boolean unpackWARs = true;
			if (host instanceof StandardHost) {
				unpackWARs = ((StandardHost) host).isUnpackWARs()
				&& ((StandardContext) context).getUnpackWAR();
			}
			File canonicalAppBase = new File(appBase);
			if (canonicalAppBase.isAbsolute()) {
				canonicalAppBase = canonicalAppBase.getCanonicalFile();
			} else {
				canonicalAppBase = new File(System.getProperty("catalina.base"),
						appBase).getCanonicalFile();
			}
			String docBase = context.getDocBase();
			if (docBase == null) {
				// Trying to guess the docBase according to the path
				String path = context.getPath();
				if (path == null) {
					return;
				}

				ContextName cn = new ContextName(path, context.getWebappVersion());
				docBase = cn.getBaseName();

				//				if (path.equals("")) {
				//					docBase = "ROOT";
				//				} else {
				//					if (path.startsWith("/")) {
				//	                    docBase = path.substring(1).replace('/', '#');
				//	                } else {
				//	                    docBase = path.replace('/', '#');
				//	                }
				//				}
			}
			File file = new File(docBase);
			if (!file.isAbsolute()) {
				docBase = (new File(canonicalAppBase, docBase)).getPath();
			} else {
				docBase = file.getCanonicalPath();
			}
			file = new File(docBase);
			//			String origDocBase = docBase;
			if ((docBase.toLowerCase().endsWith(".sar") || docBase.toLowerCase()
					.endsWith(".war"))
					&& !file.isDirectory() && unpackWARs) {
				URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/");
		        //Issue:175: http://code.google.com/p/sipservlets/issues/detail?id=175
				ContextName cn = new ContextName(context.getPath(),
		                context.getWebappVersion());
		        String contextPath = cn.getBaseName();

				//				if (contextPath.equals("")) {
				//					contextPath = "ROOT";
				//				} else {
				//		            if (contextPath.lastIndexOf('/') > 0) {
				//		                contextPath = "/" + contextPath.substring(1).replace('/','#');
				//		            }
				//		        }

				docBase = ExpandWar.expand(host, war, contextPath);
				file = new File(docBase);
				docBase = file.getCanonicalPath();
				if (context instanceof CatalinaSipContext) {
					FileDirContext fileDirContext =new FileDirContext();
					fileDirContext.setDocBase(docBase);
					((CatalinaSipContext) context).setResources(fileDirContext );
				}
			} else {
				File docDir = new File(docBase);
				if (!docDir.exists()) {
					String[] extensions = new String[] { ".sar", ".war" };
					for (String extension : extensions) {
						File archiveFile = new File(docBase + extension);
						if (archiveFile.exists()) {
							if (unpackWARs) {
								URL war = new URL("jar:" + archiveFile.toURI().toURL()
										+ "!/");
								docBase = ExpandWar.expand(host, war, context
										.getPath());
								file = new File(docBase);
								docBase = file.getCanonicalPath();
							} else {
								docBase = archiveFile.getCanonicalPath();
							}

							break;
						}
					}
					if (context instanceof CatalinaSipContext) {
						FileDirContext fileDirContext =new FileDirContext();
						fileDirContext.setDocBase(docBase);
						((CatalinaSipContext) context).setResources(fileDirContext );
					}
				}
			}
			if (docBase.startsWith(canonicalAppBase.getPath() + File.separatorChar)) {
				docBase = docBase.substring(canonicalAppBase.getPath().length());
				docBase = docBase.replace(File.separatorChar, '/');
				if (docBase.startsWith("/")) {
					docBase = docBase.substring(1);
				}
			} else {
				docBase = docBase.replace(File.separatorChar, '/');
			}
			context.setDocBase(docBase);
		} else {
			super.fixDocBase();
		}
	}

	// commenting out to avoid the parent calls out to those overidden methods and don't parse the annotations 
	
	// Check for web servlet annotation in the /WEB-INF/classes	
//	protected void checkWebAnnotations(){
//		URL webinfClasses = null;
//		try{
//			webinfClasses = context.getServletContext().getResource("/WEB-INF/classes");
//		} catch (MalformedURLException e){
//			logger.error(sm.getString("contextConfig.webinfClassesUrl"),e);
//		}
//
//		if (webinfClasses == null) {
//			// Nothing to do.
//			return;
//		} else if ("jar".equals(webinfClasses.getProtocol())) {
//			processAnnotationsJar(webinfClasses, null, false);
//		} else if ("jndi".equals(webinfClasses.getProtocol())) {
//			processAnnotationsJndi(webinfClasses, null, false);
//		} else if ("file".equals(webinfClasses.getProtocol())) {
//			try {
//				processAnnotationsFile(new File(webinfClasses.toURI()), null, false);
//			} catch (URISyntaxException e) {
//				logger.error(sm.getString("contextConfig.fileUrl", webinfClasses), e);
//			} 
//		} else {
//			logger.error(sm.getString("contextConfig.unknownUrlProtocol",
//					webinfClasses.getProtocol(), webinfClasses));
//		}
//	}
//
//	protected void processAnnotationsFile(File file, WebXml fragment,
//            boolean handlesTypesOnly) {
//
//		if (file.isDirectory()) {
//			String[] dirs = file.list();
//			for (String dir : dirs) {
//				processAnnotationsFile(new File(file,dir), fragment, handlesTypesOnly);
//			}
//		} else if (file.canRead() && file.getName().endsWith(".class")) {
//			FileInputStream fis = null;
//			try {
//				fis = new FileInputStream(file);
//				processAnnotationsStream(fis, fragment, handlesTypesOnly);
//			} catch (IOException e) {
//				logger.error(sm.getString("contextConfig.inputStreamFile",
//						file.getAbsolutePath()),e);
//			} finally {
//				if (fis != null) {
//					try {
//						fis.close();
//					} catch (Throwable t) {
//						ExceptionUtils.handleThrowable(t);
//					}
//				}
//			}
//		}
//	}
//
//	protected void processAnnotationsJar(URL url, WebXml fragment,
//            boolean handlesTypesOnly) {
//
//		JarFile jarFile = null;
//
//		try {
//			URLConnection urlConn = url.openConnection();
//			JarURLConnection jarUrlConn;
//			if (!(urlConn instanceof JarURLConnection)) {
//				// This should never happen
//				sm.getString("contextConfig.jarUrl", url);
//				return;
//			}
//
//			jarUrlConn = (JarURLConnection) urlConn;
//			jarUrlConn.setUseCaches(false);
//			jarFile = jarUrlConn.getJarFile();
//
//			Enumeration jarEntries = jarFile.entries();
//			while (jarEntries.hasMoreElements()) {
//				JarEntry jarEntry = jarEntries.nextElement();
//				String entryName = jarEntry.getName();
//				if (entryName.endsWith(".class")) {
//					InputStream is = null;
//					try {
//						is = jarFile.getInputStream(jarEntry);
//						processAnnotationsStream(is, fragment, handlesTypesOnly);
//					} catch (IOException e) {
//						logger.error(sm.getString("contextConfig.inputStreamJar",
//								entryName, url),e);
//					} finally {
//						if (is != null) {
//							try {
//								is.close();
//							} catch (Throwable t) {
//								ExceptionUtils.handleThrowable(t);
//							}
//						}
//					}
//				}
//			}
//		} catch (IOException e) {
//			logger.error(sm.getString("contextConfig.jarFile", url), e);
//		} finally {
//			if (jarFile != null) {
//				try {
//					jarFile.close();
//				} catch (Throwable t) {
//					ExceptionUtils.handleThrowable(t);
//				}
//			}
//		}
//	}
//
//	protected void processAnnotationsJndi(URL url, WebXml fragment,
//            boolean handlesTypesOnly) {
//		try {
//			URLConnection urlConn = url.openConnection();
//			DirContextURLConnection dcUrlConn;
//			if (!(urlConn instanceof DirContextURLConnection)) {
//				// This should never happen
//				sm.getString("contextConfig.jndiUrlNotDirContextConn", url);
//				return;
//			}
//
//			dcUrlConn = (DirContextURLConnection) urlConn;
//			dcUrlConn.setUseCaches(false);
//
//			String type = dcUrlConn.getHeaderField(ResourceAttributes.TYPE);
//			if (ResourceAttributes.COLLECTION_TYPE.equals(type)) {
//				// Collection
//				Enumeration dirs = dcUrlConn.list();
//				while (dirs.hasMoreElements()) {
//					String dir = dirs.nextElement();
//					URL dirUrl = new URL(url.toString() + '/' + dir);
//					processAnnotationsJndi(dirUrl, fragment, handlesTypesOnly);
//				}
//
//			} else {
//				// Single file
//				if (url.getPath().endsWith(".class")) {
//					InputStream is = null;
//					try {
//						is = dcUrlConn.getInputStream();
//						processAnnotationsStream(is, fragment, handlesTypesOnly);
//					} catch (IOException e) {
//						logger.error(sm.getString("contextConfig.inputStreamJndi",
//								url),e);
//					} finally {
//						if (is != null) {
//							try {
//								is.close();
//							} catch (Throwable t) {
//								ExceptionUtils.handleThrowable(t);
//							}
//						}
//					}
//				}
//			}
//		} catch (IOException e) {
//			logger.error(sm.getString("contextConfig.jndiUrl", url), e);
//		} 
//	}
//
//	protected void processAnnotationsStream(InputStream is, WebXml fragment,
//            boolean handlesTypesOnly)
//	throws ClassFormatException, IOException {
//
//		ClassParser parser = new ClassParser(is);
//		JavaClass clazz = parser.parse();
//		checkHandlesTypes(clazz);
//
//        if (handlesTypesOnly) {
//            return;
//        }
//
//		AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
//		if (annotationsEntries != null) {
//			for (AnnotationEntry ae : annotationsEntries) {
//				String type = ae.getAnnotationType();
//				if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
//					hasWebAnnotations=true;
//				}else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
//					hasWebAnnotations=true;
//				}else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
//					hasWebAnnotations=true;
//				}else if ("Ljavax/servlet/annotation/WebInitParam;".equals(type)) {
//					hasWebAnnotations=true;
//				}else if ("Ljavax/servlet/annotation/MultipartConfig;".equals(type)) {
//					hasWebAnnotations=true;
//				} else {
//					// Unknown annotation - ignore
//				}
//			}
//		}
//	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy