package org.red5.server.jetty; /* * RED5 Open Source Flash Server - http://www.osflash.org/red5 * * Copyright (c) 2006-2009 by respective authors (see below). All rights reserved. * * This library 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 library 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 library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.lang.management.ManagementFactory; import java.util.Map; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import javax.management.MBeanServer; import javax.management.ObjectName; import org.eclipse.jetty.deploy.WebAppDeployer; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.util.thread.ThreadPool; import org.red5.server.LoaderBase; import org.red5.server.api.IApplicationContext; import org.red5.server.jmx.mxbeans.LoaderMXBean; import org.red5.server.util.FileUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.support.AbstractApplicationContext; /** * Class that loads Red5 applications using Jetty. */ @SuppressWarnings("deprecation") public class JettyLoader extends LoaderBase implements LoaderMXBean { /** * Logger */ protected static Logger log = LoggerFactory.getLogger(JettyLoader.class); /** * IServer implementation */ protected Server jetty; protected Connector[] connectors; protected ThreadPool threadPool; protected HandlerCollection handlers; /** * Remove context from the current host. * * @param path Path */ @Override public void removeContext(String path) { Handler[] handlers = jetty.getHandlers(); for (Handler handler : handlers) { if (handler instanceof ContextHandler && ((ContextHandler) handler).getContextPath().equals(path)) { try { ((ContextHandler) handler).stop(); jetty.removeBean(handler); break; } catch (Exception e) { log.error("Could not remove context: {}", path, e); } } } IApplicationContext ctx = LoaderBase.removeRed5ApplicationContext(path); if (ctx != null) { ctx.stop(); } else { log.warn("Red5 application context could not be stopped, it was null for path: {}", path); } } /** * */ @SuppressWarnings("all") public void init() { log.info("Loading Jetty context"); // So this class is left just starting jetty try { // locate default web config File webConfigFile = new File(System.getProperty("red5.plugins_root"), "jetty-web.xml"); if (!webConfigFile.exists()) { // extract from the jar extractFromJAR("jetty-web.xml", System.getProperty("red5.plugins_root")); if (!webConfigFile.exists()) { log.warn("Default web config was not found"); } } String defaultWebConfig = webConfigFile.getAbsolutePath(); log.info("Default web config file: {}", defaultWebConfig); if (webappFolder == null) { // Use default webapps directory webappFolder = FileUtil.formatPath(System.getProperty("red5.root"), "/webapps"); } System.setProperty("red5.webapp.root", webappFolder); log.info("Application root: {}", webappFolder); // root location for servlet container String serverRoot = System.getProperty("red5.root"); log.debug("Server root: {}", serverRoot); // set in the system for tomcat classes System.setProperty("jetty.home", serverRoot); System.setProperty("jetty.class.path", String.format("%s/lib%s%s/plugins", serverRoot, File.separator, serverRoot)); String[] handlersArr = new String[] { "org.eclipse.jetty.webapp.WebInfConfiguration", "org.eclipse.jetty.webapp.WebXmlConfiguration", "org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.webapp.TagLibConfiguration", "org.red5.server.jetty.Red5WebPropertiesConfiguration" }; // instance a new org.mortbay.jetty.Server log.info("Starting jetty servlet engine"); jetty = new Server(); jetty.setConnectors(connectors); jetty.setHandler(handlers); // commented out by dominick // jetty.setThreadPool(threadPool); jetty.setStopAtShutdown(true); LoaderBase.setApplicationLoader(new JettyApplicationLoader(jetty, applicationContext)); try { // Add web applications from web app root with web config HandlerCollection contexts = (HandlerCollection) jetty.getChildHandlerByClass(ContextHandlerCollection.class); if (contexts == null) { contexts = (HandlerCollection) jetty.getChildHandlerByClass(HandlerCollection.class); } WebAppDeployer deployer = new WebAppDeployer(); deployer.setContexts(contexts); deployer.setWebAppDir(webappFolder); deployer.setDefaultsDescriptor(defaultWebConfig); deployer.setConfigurationClasses(handlersArr); deployer.setExtract(true); deployer.setParentLoaderPriority(true); deployer.start(); } catch (Exception e) { log.error("Error deploying web applications", e); } // Start Jetty jetty.start(); } catch (Exception e) { log.error("Error loading jetty", e); } finally { registerJMX(); } } //TODO: Implement this for those who want to use Jetty public boolean startWebApplication(String applicationName) { return false; } protected void registerJMX() { // register with jmx MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); try { ObjectName oName = new ObjectName("org.red5.server:type=JettyLoader"); // check for existing registration before registering if (!mbs.isRegistered(oName)) { mbs.registerMBean(this, oName); } else { log.debug("ContextLoader is already registered in JMX"); } } catch (Exception e) { log.warn("Error on jmx registration", e); } } protected void unregisterJMX() { MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); try { ObjectName oName = new ObjectName("org.red5.server:type=JettyLoader"); mbs.unregisterMBean(oName); } catch (Exception e) { log.warn("Exception unregistering", e); } } public Connector[] getConnectors() { return connectors; } public void setConnectors(Connector[] connectors) { this.connectors = connectors; } public void setThreadPool(ThreadPool threadPool) { this.threadPool = threadPool; } public void setHandlers(HandlerCollection handlers) { this.handlers = handlers; } /** * Shut server down */ public void shutdown() { log.info("Shutting down Jetty context"); //run through the applications and ensure that spring is told //to commence shutdown / disposal AbstractApplicationContext absCtx = (AbstractApplicationContext) LoaderBase.getApplicationContext(); if (absCtx != null) { log.debug("Using loader base application context for shutdown"); //get all the app (web) contexts and shut them down first Map<String, IApplicationContext> contexts = LoaderBase.getRed5ApplicationContexts(); if (contexts.isEmpty()) { log.info("No contexts were found to shutdown"); } for (Map.Entry<String, IApplicationContext> entry : contexts.entrySet()) { //stop the context log.debug("Calling stop on context: {}", entry.getKey()); entry.getValue().stop(); } if (absCtx.isActive()) { log.debug("Closing application context"); absCtx.close(); } } else { log.error("Error getting Spring bean factory for shutdown"); } try { jetty.stop(); System.exit(0); } catch (Exception e) { log.warn("Jetty could not be stopped", e); System.exit(1); } } private void extractFromJAR(String filePath, String dest) { try { String home = getClass().getProtectionDomain().getCodeSource().getLocation().getPath().replaceAll("%20", " "); JarFile jar = new JarFile(home); ZipEntry entry = jar.getEntry(filePath); File efile = new File(dest, entry.getName()); InputStream in = new BufferedInputStream(jar.getInputStream(entry)); OutputStream out = new BufferedOutputStream(new FileOutputStream(efile)); byte[] buffer = new byte[2048]; for (;;) { int nBytes = in.read(buffer); if (nBytes <= 0) { break; } out.write(buffer, 0, nBytes); } out.flush(); out.close(); in.close(); } catch (Exception e) { log.warn("Exception extracting file from jar", e); } } }