com.buglabs.bug.ws.program.ProgramServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.buglabs.bug.ws.program.ProgramServlet.java

Source

/* Copyright (c) 2007, 2008 Bug Labs, Inc.
 * All rights reserved.
 *   
 * This program is free software; you can redistribute it and/or  
 * modify it under the terms of the GNU General Public License version  
 * 2 only, as published by the Free Software Foundation.   
 *   
 * This program 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  
 * General Public License version 2 for more details (a copy is  
 * included at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html).   
 *   
 * You should have received a copy of the GNU General Public License  
 * version 2 along with this work; if not, write to the Free Software  
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  
 * 02110-1301 USA   
 *
 */
package com.buglabs.bug.ws.program;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.knapsack.init.pub.KnapsackInitService;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;

import com.buglabs.bug.ws.Activator;
import com.buglabs.util.osgi.BUGBundleConstants;
import com.buglabs.util.xml.XmlNode;

public class ProgramServlet extends HttpServlet {

    private static final String HTTP_OK_RESPONSE = "OK";

    private static final String MANIFEST_FILENAME = "MANIFEST.MF";

    private static final String CVS_DIRECTORY_NAME = "CVS";

    private static final String TEXT_XML_MIME_TYPE = "text/xml";

    private static final String TEXT_PLAIN_MIME_TYPE = "text/html";

    private static final String BUNDLE_TEMP_FILENAME = "bundle.jar";

    private static final String APPLICATION_JAVA_ARCHIVE_MIME_TYPE = "application/java-archive";

    private static final long serialVersionUID = -6977609397049223637L;

    public static int BUFFER_SIZE = 10240;

    private final BundleContext context;

    private String incomingBundleDir;

    private LogService log;

    private final KnapsackInitService initService;

    public ProgramServlet(BundleContext context, KnapsackInitService initService) {
        this.context = context;
        this.initService = initService;
        log = Activator.getLog();

        //Make sure we know where to install bundles to.
        this.incomingBundleDir = context.getProperty(Activator.APP_BUNDLE_PATH);
        if (incomingBundleDir == null) {
            throw new RuntimeException("Bundle property " + Activator.APP_BUNDLE_PATH + " is not defined.  "
                    + this.getClass().getSimpleName() + " cannot start.");
        }

        //Make sure the configured directory is being scanned by knapsack
        boolean found = false;
        for (File f : initService.getBundleDirectories())
            if (f.getAbsolutePath().equals(incomingBundleDir))
                found = true;

        if (!found)
            throw new RuntimeException(Activator.APP_BUNDLE_PATH
                    + " is set to a directory that knapsack is not configured to read from.  Check knapsack property 'org.knapsack.bundleDirs'.");

        log.log(LogService.LOG_DEBUG, this.getClass().getSimpleName()
                + " initialization complete.  BUGapp storage location set to: " + incomingBundleDir);
    }

    /*
     * (non-Javadoc)
     * 
     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
     *      javax.servlet.http.HttpServletResponse)
     */
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String path = req.getPathInfo();

        if (path == null) {
            log.log(LogService.LOG_WARNING, "Error: null program path.");
            resp.sendError(665, "Error: invalid program path.");
            return;
        }

        String[] toks = path.split("/");

        String jarName = toks[toks.length - 1];

        File bundleDir = new File(incomingBundleDir);

        if (!bundleDir.exists()) {
            log.log(LogService.LOG_ERROR, "Unable to save bundle; can't create file.");
            resp.sendError(0, "Unable to save bundle; can't create file.");
        }

        Bundle existingBundle = findBundleByName(jarName);
        if (existingBundle != null) {
            try {
                uninstallBundle(existingBundle);
            } catch (BundleException e) {
                log.log(LogService.LOG_ERROR, "Unable to uninstall existing bundle.  Aborting install.", e);
            }
        }

        File jarFile = new File(bundleDir.getAbsolutePath() + File.separator + jarName + ".jar");
        FileOutputStream fos = new FileOutputStream(jarFile);
        IOUtils.copy(req.getInputStream(), fos);
        fos.flush();
        fos.close();
        //Tell knapsack to start the bundle.
        jarFile.setExecutable(true);

        initService.updateBundles();

        resp.getWriter().println(HTTP_OK_RESPONSE);
        req.getInputStream().close();
    }

    private void uninstallBundle(Bundle bundle) throws BundleException {
        if (bundle != null && bundle.getState() != Bundle.UNINSTALLED) {
            bundle.stop();
            bundle.uninstall();
        }
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String path = req.getPathInfo();
        String response = null;

        if (path == null) {
            // Get a list of description of all programs.
            resp.setContentType(TEXT_XML_MIME_TYPE);
            response = getAllBundles();
            resp.getWriter().println(response);
        } else {
            String id = path.substring(1).trim();

            // User should not be able to access System Bundle
            if (Long.parseLong(id) == 0) {
                resp.setContentType(TEXT_PLAIN_MIME_TYPE);
                resp.sendError(503);
                return;

            }

            if (!isNumber(id)) {
                resp.setContentType(TEXT_PLAIN_MIME_TYPE);
                resp.sendError(0, "Invalid program id: " + id);
                return;
            }

            Bundle b = findBundleById(id);

            if (b == null) {
                resp.setContentType(TEXT_PLAIN_MIME_TYPE);
                resp.sendError(0, "No program with id: " + id);
                return;
            }

            File jarfile = null;

            try {
                URI uri = new URI(b.getLocation());
                jarfile = new File(uri);
            } catch (URISyntaxException e) {
                log.log(LogService.LOG_ERROR, "Unable to get bundle location, invalid URL.", e);
            }

            if (jarfile != null && jarfile.exists() && jarfile.isFile()) {
                resp.setContentType(APPLICATION_JAVA_ARCHIVE_MIME_TYPE);
                FileInputStream fis = new FileInputStream(jarfile);
                IOUtils.copy(fis, resp.getOutputStream());
            } else if (jarfile != null && jarfile.exists() && jarfile.isDirectory()) {
                resp.setContentType(APPLICATION_JAVA_ARCHIVE_MIME_TYPE);
                File bundleFile = new File(incomingBundleDir + File.separator + BUNDLE_TEMP_FILENAME);
                jarfile = createJar(jarfile, bundleFile);

                FileInputStream fis = new FileInputStream(jarfile);
                IOUtils.copy(fis, resp.getOutputStream());
            } else {
                resp.setContentType(TEXT_PLAIN_MIME_TYPE);
                resp.sendError(0, "No program with id: " + id);
            }
        }
    }

    /**
     * Implementation of doDelete that uninstalls supplied bundle
     */
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String path = req.getPathInfo().replace('+', ' ');

        if (path == null) {
            resp.sendError(665, "Error: invalid program path.");
            return;
        }

        String[] toks = path.split("/");

        try {
            Bundle b = findBundleByName(toks[toks.length - 1]);

            //TODO: Implement telling knapsack that bundles have changed
            log.log(LogService.LOG_ERROR, "IMPLEMENT ME.");
            //if (b != null && b.getState() != Bundle.UNINSTALLED) {
            if (b != null) {
                uninstallBundle(b);
            }
        } catch (BundleException e) {
            resp.sendError(0, "Error: Unable to remove bundle." + e.getMessage());
        }
    }

    private boolean isNumber(String id) {

        try {
            Long.parseLong(id);
        } catch (NumberFormatException e) {
            return false;
        }

        return true;
    }

    /**
     * Geenerate a Jar file from a directory.
     * 
     * @param jarDir
     * @param bundleFile
     * @return
     * @throws IOException
     */
    private File createJar(File jarDir, File bundleFile) throws IOException {
        List<File> files = new ArrayList<File>();
        getAllChildFiles(jarDir, files);
        createJarArchive(bundleFile, (File[]) files.toArray(new File[files.size()]), jarDir);

        return bundleFile;
    }

    /**
     * Recursively return set of all child files.
     * 
     * @param root
     * @param files
     */
    private void getAllChildFiles(File root, List<File> files) {

        File[] children = root.listFiles(new FilenameFilter() {

            public boolean accept(File arg0, String arg1) {
                if (arg1.startsWith(".")) {
                    return false;
                }

                if (arg1.equals(CVS_DIRECTORY_NAME)) {
                    return false;
                }

                if (arg1.equals(MANIFEST_FILENAME)) {
                    return false;
                }

                return true;
            }

        });

        if (children != null) {
            for (int i = 0; i < children.length; ++i) {
                if (children[i].isFile()) {
                    files.add(children[i].getAbsoluteFile());
                } else {
                    getAllChildFiles(children[i], files);
                }
            }
        }
    }

    /**
     * Code from forum board for creating a Jar.
     * 
     * @param archiveFile
     * @param tobeJared
     * @param rootDir
     * @throws IOException
     */
    protected void createJarArchive(File archiveFile, File[] tobeJared, File rootDir) throws IOException {

        byte buffer[] = new byte[BUFFER_SIZE];
        // Open archive file
        FileOutputStream stream = new FileOutputStream(archiveFile);
        JarOutputStream out = new JarOutputStream(stream, new Manifest(new FileInputStream(
                rootDir.getAbsolutePath() + File.separator + "META-INF" + File.separator + MANIFEST_FILENAME)));

        for (int i = 0; i < tobeJared.length; i++) {
            if (tobeJared[i] == null || !tobeJared[i].exists() || tobeJared[i].isDirectory())
                continue; // Just in case...

            String relPath = getRelPath(rootDir.getAbsolutePath(), tobeJared[i].getAbsolutePath());

            // Add archive entry
            JarEntry jarAdd = new JarEntry(relPath);
            jarAdd.setTime(tobeJared[i].lastModified());
            out.putNextEntry(jarAdd);
            // Write file to archive
            FileInputStream in = new FileInputStream(tobeJared[i]);
            while (true) {
                int nRead = in.read(buffer, 0, buffer.length);
                if (nRead <= 0)
                    break;
                out.write(buffer, 0, nRead);
            }
            in.close();
        }

        out.close();
        stream.close();

    }

    /**
     * Return the difference of two paths.
     * 
     * @param rootPath
     * @param subPath
     * @return
     */
    private String getRelPath(String rootPath, String subPath) {

        return subPath.substring(rootPath.length() + 1);
    }

    private XmlNode getBundleXmlNode(Bundle b) {
        XmlNode pnode = new XmlNode(IProgramXml.NODE_PROGRAM);

        int state = b.getState();

        String strState = "false";
        if (state == Bundle.ACTIVE) {
            strState = "true";
        }
        pnode.addAttribute(IProgramXml.ATTRIB_ACTIVE, strState);
        pnode.addAttribute(IProgramXml.ATTRIB_VERSION, (String) b.getHeaders().get("Bundle-Version"));
        pnode.addAttribute(IProgramXml.ATTRIB_ID, "" + b.getBundleId());

        pnode.addAttribute(IProgramXml.ATTRIB_TYPE, getBugBundleType(b));
        new XmlNode(pnode, IProgramXml.NODE_TITLE, (String) b.getHeaders().get("Bundle-Name"));
        new XmlNode(pnode, IProgramXml.NODE_AUTHOR, (String) b.getHeaders().get("Bundle-Vendor"));
        // We don't have date created in the Bundle interface so for now use
        // modified date. TODO Fix this.
        new XmlNode(pnode, "date_created", "1-1-2007");
        new XmlNode(pnode, IProgramXml.NODE_DATE_MODIFIED, "1-1-2007");
        new XmlNode(pnode, IProgramXml.NODE_NOTES);
        XmlNode sNode = new XmlNode(pnode, IProgramXml.NODE_SERVICES);
        XmlNode servicePropsNode = new XmlNode(pnode, IProgramXml.NODE_SERVICES2);

        ServiceReference[] sr = b.getRegisteredServices();

        if (sr != null) {
            for (int i = 0; i < sr.length; ++i) {
                String[] s = (String[]) sr[i].getProperty("objectClass");
                new XmlNode(sNode, IProgramXml.NODE_SERVICE, s[0]);
                // the following mass of code adds a services2 node to the xml
                // that includes the service properties
                XmlNode propertiesNode = new XmlNode(servicePropsNode, IProgramXml.NODE_SERVICE);
                propertiesNode.addAttribute(IProgramXml.ATTRIB_NAME, s[0]);
                String[] properties = sr[i].getPropertyKeys();
                for (int j = 0; j < properties.length; j++) {
                    if (properties[j] == "service.id" || properties[j] == "objectClass")
                        continue;
                    XmlNode propNode = new XmlNode(propertiesNode, IProgramXml.NODE_PROPERTY);
                    propNode.addAttribute(IProgramXml.ATTRIB_NAME, properties[j]);
                    propNode.addAttribute(IProgramXml.ATTRIB_VALUE, "" + sr[i].getProperty(properties[j]));
                }
            }
        }

        return pnode;
    }

    private String getBugBundleType(Bundle b) {
        String type = "";

        String temp = (String) b.getHeaders().get(BUGBundleConstants.BUG_BUNDLE_TYPE_HEADER);
        if (temp != null) {
            type = temp;
        }

        return type;
    }

    private Bundle findBundleById(String id) {
        Bundle[] bundles = context.getBundles();

        for (int i = 0; i < bundles.length; ++i) {
            if (bundles[i].getBundleId() == Long.parseLong(id)) {
                return bundles[i];
            }
        }

        return null;
    }

    private Bundle findBundleByName(String name) {
        Bundle[] bundles = context.getBundles();

        for (int i = 0; i < bundles.length; ++i) {
            Dictionary headers = bundles[i].getHeaders();
            if (headers != null) {
                String symbolicName = (String) headers.get("Bundle-Name");
                if (symbolicName != null) {
                    if (symbolicName.equals(name)) {
                        return bundles[i];
                    }
                }
            }
        }

        return null;
    }

    private String getAllBundles() {
        Bundle[] bundles = context.getBundles();

        XmlNode root = new XmlNode(IProgramXml.NODE_PROGRAMS);

        for (int i = 0; i < bundles.length; ++i) {
            Bundle b = bundles[i];
            root.addChild(getBundleXmlNode(b));
        }

        return root.toString();
    }
}