org.red5.server.war.RootContextLoaderServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.red5.server.war.RootContextLoaderServlet.java

Source

package org.red5.server.war;

/*
 * RED5 Open Source Flash Server - http://www.osflash.org/red5
 *
 * Copyright (c) 2006-2007 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.beans.Introspector;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Timer;
import java.util.TimerTask;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;

import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Logger;
import org.red5.server.ClientRegistry;
import org.red5.server.Context;
import org.red5.server.GlobalScope;
import org.red5.server.MappingStrategy;
import org.red5.server.ScopeResolver;
import org.red5.server.Server;
import org.red5.server.WebScope;
import org.red5.server.jmx.JMXAgent;
import org.red5.server.service.ServiceInvoker;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.access.ContextBeanFactoryReference;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;

/**
 * Entry point from which the server config file is loaded while running within
 * a J2EE application container.
 * 
 * This listener should be registered after Log4jConfigListener in web.xml, if
 * the latter is used.
 * 
 * @author The Red5 Project (red5@osflash.org)
 * @author Paul Gregoire (mondain@gmail.com)
 */
public class RootContextLoaderServlet extends ContextLoaderListener {

    private final static long serialVersionUID = 41919712007L;

    private static ClassLoader myClassloader;

    // Initialize Logging
    public static Logger logger = Logger.getLogger(RootContextLoaderServlet.class.getName());

    private static Timer timer;

    private CheckScopeListTask checkScopeList;

    private static ArrayList<ServletContext> registeredContexts = new ArrayList<ServletContext>(3);

    private ConfigurableWebApplicationContext applicationContext;

    private DefaultListableBeanFactory parentFactory;

    private static ServletContext servletContext;

    private static RootContextLoaderServlet instance;

    private ClientRegistry clientRegistry;

    private ServiceInvoker globalInvoker;

    private MappingStrategy globalStrategy;

    private ScopeResolver globalResolver;

    private GlobalScope global;

    private Server server;

    protected Integer rmiPort = 1099;

    /**
     * Main entry point for the Red5 Server as a war
     */
    // Notification that the web application is ready to process requests
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        if (null != servletContext) {
            return;
        }
        instance = this;
        System.setProperty("red5.deployment.type", "war");

        myClassloader = getClass().getClassLoader();

        servletContext = sce.getServletContext();
        String prefix = servletContext.getRealPath("/");

        servletContext.setAttribute("root.classloader", myClassloader);

        initRegistry(servletContext);

        long time = System.currentTimeMillis();

        logger.info("RED5 Server (http://www.osflash.org/red5)");
        logger.info("Root context loader");
        logger.debug("Path: " + prefix);

        try {
            // instance the context loader
            ContextLoader loader = createContextLoader();
            applicationContext = (ConfigurableWebApplicationContext) loader
                    .initWebApplicationContext(servletContext);
            logger.debug("Root context path: " + applicationContext.getServletContext().getContextPath());

            ConfigurableBeanFactory factory = applicationContext.getBeanFactory();

            // register default
            factory.registerSingleton("default.context", applicationContext);

            // get the main factory
            parentFactory = (DefaultListableBeanFactory) factory.getParentBeanFactory();

            // create a wrapper around our primary context
            BeanFactoryReference beanfactoryRef = new ContextBeanFactoryReference(applicationContext);

            // set it in the root servlet context
            servletContext.setAttribute("bean.factory.ref", beanfactoryRef);

            // set a remoting codec factory for AMF use
            servletContext.setAttribute("remoting.codec.factory", parentFactory.getBean("remotingCodecFactory"));

            server = (Server) parentFactory.getBean("red5.server");

            clientRegistry = (ClientRegistry) factory.getBean("global.clientRegistry");

            globalInvoker = (ServiceInvoker) factory.getBean("global.serviceInvoker");

            globalStrategy = (MappingStrategy) factory.getBean("global.mappingStrategy");

            global = (GlobalScope) factory.getBean("global.scope");
            logger.debug("GlobalScope: " + global);
            global.setServer(server);
            global.register();
            global.start();

            globalResolver = new ScopeResolver();
            globalResolver.setGlobalScope(global);

            logger.debug("About to grab Webcontext bean for Global");
            Context globalContext = (Context) factory.getBean("global.context");
            globalContext.setCoreBeanFactory(parentFactory);
            globalContext.setClientRegistry(clientRegistry);
            globalContext.setServiceInvoker(globalInvoker);
            globalContext.setScopeResolver(globalResolver);
            globalContext.setMappingStrategy(globalStrategy);

            logger.debug("About to grab Webcontext bean for ROOT");
            Context webContext = (Context) factory.getBean("web.context");
            webContext.setCoreBeanFactory(parentFactory);
            webContext.setClientRegistry(clientRegistry);
            webContext.setServiceInvoker(globalInvoker);
            webContext.setScopeResolver(globalResolver);
            webContext.setMappingStrategy(globalStrategy);

            WebScope scope = (WebScope) factory.getBean("web.scope");
            scope.setServer(server);
            scope.setParent(global);
            scope.register();
            scope.start();

            // grab the scope list (other war/webapps)
            IRemotableList remote = (IRemotableList) Naming
                    .lookup("rmi://localhost:" + rmiPort + "/subContextList");
            logger.debug("Children: " + remote.numChildren());
            if (remote.hasChildren()) {
                logger.debug("Children were detected");
                for (int i = 0; i < remote.numChildren(); i++) {
                    logger.debug("Enumerating children");
                    WebSettings settings = remote.getAt(i);
                    registerSubContext(settings.getWebAppKey());
                }
                logger.debug("End of children...");
            }

        } catch (Throwable t) {
            logger.error(t);
        } finally {
            timer = new Timer();
            checkScopeList = new CheckScopeListTask();
            timer.scheduleAtFixedRate(checkScopeList, 1000, 30000);
        }

        long startupIn = System.currentTimeMillis() - time;
        logger.info("Startup done in: " + startupIn + " ms");

    }

    /*
     * Registers a subcontext with red5
     */
    public void registerSubContext(String webAppKey) {
        // get the sub contexts - servlet context
        ServletContext ctx = servletContext.getContext(webAppKey);
        logger.info("Registering subcontext for servlet context: " + ctx.getContextPath());
        if (registeredContexts.contains(ctx)) {
            logger.debug("Context is already registered: " + webAppKey);
            return;
        }

        ContextLoader loader = new ContextLoader();

        ConfigurableWebApplicationContext appCtx = (ConfigurableWebApplicationContext) loader
                .initWebApplicationContext(ctx);
        appCtx.setParent(applicationContext);
        appCtx.refresh();

        ctx.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, appCtx);

        ConfigurableBeanFactory appFactory = appCtx.getBeanFactory();

        logger.debug("About to grab Webcontext bean for " + webAppKey);
        Context webContext = (Context) appCtx.getBean("web.context");
        webContext.setCoreBeanFactory(parentFactory);
        webContext.setClientRegistry(clientRegistry);
        webContext.setServiceInvoker(globalInvoker);
        webContext.setScopeResolver(globalResolver);
        webContext.setMappingStrategy(globalStrategy);

        WebScope scope = (WebScope) appFactory.getBean("web.scope");
        scope.setServer(server);
        scope.setParent(global);
        scope.register();
        scope.start();

        // register the context so we dont try to reinitialize it
        registeredContexts.add(ctx);

    }

    /**
     * Clearing the in-memory configuration parameters, we will receive
     * notification that the servlet context is about to be shut down
     */
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        synchronized (instance) {
            logger.info("Webapp shutdown");
            // XXX Paul: grabbed this from
            // http://opensource.atlassian.com/confluence/spring/display/DISC/Memory+leak+-+classloader+won%27t+let+go
            // in hopes that we can clear all the issues with J2EE containers
            // during shutdown
            try {
                ServletContext ctx = sce.getServletContext();
                // if the ctx being destroyed is root then kill the timer
                if (ctx.getContextPath().equals("/ROOT")) {
                    timer.cancel();
                } else {
                    // remove from registered list
                    registeredContexts.remove(ctx);
                }
                // prepare spring for shutdown
                Introspector.flushCaches();
                // dereg any drivers
                for (Enumeration e = DriverManager.getDrivers(); e.hasMoreElements();) {
                    Driver driver = (Driver) e.nextElement();
                    if (driver.getClass().getClassLoader() == getClass().getClassLoader()) {
                        DriverManager.deregisterDriver(driver);
                    }
                }
                // shutdown jmx
                JMXAgent.shutdown();
                // shutdown spring
                Object attr = ctx.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
                if (attr != null) {
                    // get web application context from the servlet context
                    ConfigurableWebApplicationContext applicationContext = (ConfigurableWebApplicationContext) attr;
                    ConfigurableBeanFactory factory = applicationContext.getBeanFactory();
                    // for (String scope : factory.getRegisteredScopeNames()) {
                    // logger.debug("Registered scope: " + scope);
                    // }
                    try {
                        for (String singleton : factory.getSingletonNames()) {
                            logger.debug("Registered singleton: " + singleton);
                            factory.destroyScopedBean(singleton);
                        }
                    } catch (RuntimeException e) {
                    }
                    factory.destroySingletons();

                    ctx.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
                    applicationContext.close();
                }
                instance.getContextLoader().closeWebApplicationContext(ctx);
            } catch (Throwable e) {
                e.printStackTrace();
            } finally {
                // http://jakarta.apache.org/commons/logging/guide.html#Classloader_and_Memory_Management
                // http://wiki.apache.org/jakarta-commons/Logging/UndeployMemoryLeak?action=print
                LogFactory.release(Thread.currentThread().getContextClassLoader());
            }
        }
    }

    protected void initRegistry(ServletContext ctx) {
        Registry r = null;
        try {
            Object o = ctx.getInitParameter("rmiPort");
            if (o != null) {
                rmiPort = Integer.valueOf((String) o);
            }
            if (System.getSecurityManager() != null) {
                System.setSecurityManager(new RMISecurityManager());
            }
            // lookup the registry
            r = LocateRegistry.getRegistry(rmiPort);
            // ensure we are not already registered with the registry
            for (String regName : r.list()) {
                logger.debug("Registry entry: " + regName);
            }
        } catch (RemoteException re) {
            logger.info("RMI Registry server was not found on port " + rmiPort);
            // if we didnt find the registry and the user wants it created
            try {
                logger.info("Starting an internal RMI registry");
                // create registry for rmi
                r = LocateRegistry.createRegistry(rmiPort);
            } catch (RemoteException e) {
                logger.info("RMI Registry server was not started on port " + rmiPort);
            }

        }
    }

    /**
     * Task to check the scope list periodically for new contexts
     */
    public final class CheckScopeListTask extends TimerTask {
        @Override
        public void run() {
            logger.debug("Checking scope list");
            try {
                // grab the scope list (other war/webapps)
                IRemotableList remote = (IRemotableList) Naming
                        .lookup("rmi://localhost:" + rmiPort + "/subContextList");
                logger.debug("Children: " + remote.numChildren());
                if (remote.hasChildren()) {
                    logger.debug("Children were detected");
                    for (int i = 0; i < remote.numChildren(); i++) {
                        logger.debug("Enumerating children");
                        WebSettings settings = remote.getAt(i);
                        registerSubContext(settings.getWebAppKey());
                    }
                    logger.debug("End of children...");
                }
            } catch (Throwable t) {
                logger.error(t);
            }
        }
    }

}