org.red5.server.service.Installer.java Source code

Java tutorial

Introduction

Here is the source code for org.red5.server.service.Installer.java

Source

package org.red5.server.service;

/*
 * 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.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.UUID;

import javax.management.MBeanServer;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.red5.compatibility.flex.messaging.messages.AcknowledgeMessage;
import org.red5.compatibility.flex.messaging.messages.AsyncMessage;
import org.red5.logging.Red5LoggerFactory;
import org.red5.server.LoaderMBean;
import org.red5.server.api.IConnection;
import org.red5.server.api.Red5;
import org.red5.server.api.service.ServiceUtils;
import org.red5.server.jmx.JMXFactory;
import org.red5.server.util.FileUtil;
import org.slf4j.Logger;

/**
 * This service provides the means to list, download, install, and un-install 
 * applications from a given url. 
 * 
 * @author Paul Gregoire (mondain@gmail.com)
 * @author Dominick Accattato (daccattato@gmail.com)
 */
public final class Installer {

    private static Logger log = Red5LoggerFactory.getLogger(Installer.class);

    private String applicationRepositoryUrl;

    private static final String userAgent = "Mozilla/4.0 (compatible; Red5 Server)";

    {
        log.info("Installer service created");
    }

    public String getApplicationRepositoryUrl() {
        return applicationRepositoryUrl;
    }

    public void setApplicationRepositoryUrl(String applicationRepositoryUrl) {
        this.applicationRepositoryUrl = applicationRepositoryUrl;
    }

    /**
     * Returns the LoaderMBean.
     * @return LoaderMBean
     */
    @SuppressWarnings("cast")
    public LoaderMBean getLoader() {
        MBeanServer mbs = JMXFactory.getMBeanServer();

        ObjectName oName = JMXFactory.createObjectName("type", "TomcatLoader");

        LoaderMBean proxy = null;
        if (mbs.isRegistered(oName)) {
            proxy = (LoaderMBean) MBeanServerInvocationHandler.newProxyInstance(mbs, oName, LoaderMBean.class,
                    true);
            log.debug("Loader was found");
        } else {
            log.warn("Loader not found");
        }
        return proxy;
    }

    /**
     * Returns a Map containing all of the application wars in the snapshot repository.
     * 
     * @return async message
     */
    public AsyncMessage getApplicationList() {
        AcknowledgeMessage result = new AcknowledgeMessage();

        // create a singular HttpClient object
        HttpClient client = new HttpClient();

        // set the proxy (WT)
        // test if we received variables on the commandline
        if ((System.getProperty("http.proxyHost") != null) && (System.getProperty("http.proxyPort") != null)) {
            HostConfiguration config = client.getHostConfiguration();
            config.setProxy(System.getProperty("http.proxyHost").toString(),
                    Integer.parseInt(System.getProperty("http.proxyPort")));
        }

        // establish a connection within 5 seconds
        client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
        //get the params for the client
        HttpClientParams params = client.getParams();
        params.setParameter(HttpMethodParams.USER_AGENT, userAgent);
        //get registry file
        HttpMethod method = new GetMethod(applicationRepositoryUrl + "registry-0.9.xml");
        //follow any 302's although there shouldnt be any
        method.setFollowRedirects(true);
        // execute the method
        try {
            IConnection conn = Red5.getConnectionLocal();
            int code = client.executeMethod(method);
            log.debug("HTTP response code: {}", code);
            String xml = method.getResponseBodyAsString();
            log.trace("Response: {}", xml);
            //prepare response for flex         
            result.body = xml;
            result.clientId = conn.getClient().getId();
            result.messageId = UUID.randomUUID().toString();
            result.timestamp = System.currentTimeMillis();
            //send the servers java version so the correct apps are installed
            String javaVersion = System.getProperty("java.version");
            if (!ServiceUtils.invokeOnConnection(conn, "onJavaVersion", new Object[] { javaVersion })) {
                log.warn("Client call to onJavaVersion failed");
            }
        } catch (HttpException he) {
            log.error("Http error connecting to {}", applicationRepositoryUrl, he);
        } catch (IOException ioe) {
            log.error("Unable to connect to {}", applicationRepositoryUrl, ioe);
        } finally {
            if (method != null) {
                method.releaseConnection();
            }
        }

        return result;
    }

    /**
     * Installs a given application.
     * 
     * @param applicationWarName app war name
     * @return true if installed; false otherwise
     */
    public boolean install(String applicationWarName) {
        IConnection conn = Red5.getConnectionLocal();

        boolean result = false;

        //strip everything except the applications name
        String application = applicationWarName.substring(0, applicationWarName.indexOf('-'));
        log.debug("Application name: {}", application);

        //get webapp location
        String webappsDir = System.getProperty("red5.webapp.root");
        log.debug("Webapp folder: {}", webappsDir);

        //setup context
        String contextPath = '/' + application;
        String contextDir = webappsDir + contextPath;

        //verify this is a unique app
        File appDir = new File(webappsDir, application);
        if (appDir.exists()) {
            if (appDir.isDirectory()) {
                log.debug("Application directory exists");
            } else {
                log.warn("Application destination is not a directory");
            }

            ServiceUtils.invokeOnConnection(conn, "onAlert",
                    new Object[] { String.format(
                            "Application %s already installed, please un-install before attempting another install",
                            application) });
        } else {
            //use the system temp directory for moving files around
            String srcDir = System.getProperty("java.io.tmpdir");
            log.debug("Source directory: {}", srcDir);
            //look for archive containing application (war, zip, etc..)
            File dir = new File(srcDir);
            if (!dir.exists()) {
                log.warn("Source directory not found");
                //use another directory
                dir = new File(System.getProperty("red5.root"), "/webapps/installer/WEB-INF/cache");
                if (!dir.exists()) {
                    if (dir.mkdirs()) {
                        log.info("Installer cache directory created");
                    }
                }
            } else {
                if (!dir.isDirectory()) {
                    log.warn("Source directory is not a directory");
                }
            }
            //get a list of temp files
            File[] files = dir.listFiles();
            for (File f : files) {
                String fileName = f.getName();
                if (fileName.equals(applicationWarName)) {
                    log.debug("File found matching application name");
                    result = true;
                    break;
                }
            }
            dir = null;

            //if the file was not found then download it
            if (!result) {
                // create a singular HttpClient object
                HttpClient client = new HttpClient();

                // set the proxy (WT)
                if ((System.getProperty("http.proxyHost") != null)
                        && (System.getProperty("http.proxyPort") != null)) {
                    HostConfiguration config = client.getHostConfiguration();
                    config.setProxy(System.getProperty("http.proxyHost").toString(),
                            Integer.parseInt(System.getProperty("http.proxyPort")));
                }

                // establish a connection within 5 seconds
                client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);
                //get the params for the client
                HttpClientParams params = client.getParams();
                params.setParameter(HttpMethodParams.USER_AGENT, userAgent);
                params.setParameter(HttpMethodParams.STRICT_TRANSFER_ENCODING, Boolean.TRUE);

                //try the wav version first
                HttpMethod method = new GetMethod(applicationRepositoryUrl + applicationWarName);
                //we dont want any transformation - RFC2616
                method.addRequestHeader("Accept-Encoding", "identity");
                //follow any 302's although there shouldnt be any
                method.setFollowRedirects(true);
                FileOutputStream fos = null;
                // execute the method
                try {
                    int code = client.executeMethod(method);
                    log.debug("HTTP response code: {}", code);
                    //create output file
                    fos = new FileOutputStream(srcDir + '/' + applicationWarName);
                    log.debug("Writing response to {}/{}", srcDir, applicationWarName);

                    // have to receive the response as a byte array.  This has the advantage of writing to the filesystem
                    // faster and it also works on macs ;)
                    byte[] buf = method.getResponseBody();
                    fos.write(buf);
                    fos.flush();

                    result = true;
                } catch (HttpException he) {
                    log.error("Http error connecting to {}", applicationRepositoryUrl, he);
                } catch (IOException ioe) {
                    log.error("Unable to connect to {}", applicationRepositoryUrl, ioe);
                } finally {
                    if (fos != null) {
                        try {
                            fos.close();
                        } catch (IOException e) {
                        }
                    }
                    if (method != null) {
                        method.releaseConnection();
                    }
                }
            }

            //if we've found or downloaded the war
            if (result) {
                //get the webapp loader
                LoaderMBean loader = getLoader();
                if (loader != null) {
                    //un-archive it to app dir
                    FileUtil.unzip(srcDir + '/' + applicationWarName, contextDir);
                    //load and start the context
                    loader.startWebApplication(application);
                } else {
                    //just copy the war to the webapps dir
                    try {
                        FileUtil.moveFile(srcDir + '/' + applicationWarName,
                                webappsDir + '/' + application + ".war");
                        ServiceUtils.invokeOnConnection(conn, "onAlert",
                                new Object[] { String.format(
                                        "Application %s will not be available until container is restarted",
                                        application) });
                    } catch (IOException e) {
                    }
                }
            }

            ServiceUtils.invokeOnConnection(conn, "onAlert", new Object[] { String.format("Application %s was %s",
                    application, (result ? "installed" : "not installed")) });

        }
        appDir = null;

        return result;
    }

    /**
     * Un-installs a given application.
     * 
     * @param applicationName name to uninstall
     * @return true if uninstalled; else false
     */
    public boolean uninstall(String applicationName) {
        ServiceUtils.invokeOnConnection(Red5.getConnectionLocal(), "onAlert",
                new Object[] { "Uninstall function not available" });

        return false;
    }

}