com.liferay.web.extender.internal.webbundle.WebBundleProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.liferay.web.extender.internal.webbundle.WebBundleProcessor.java

Source

/**
 * Copyright (c) 2000-2012 Liferay, Inc. 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.
 */

package com.liferay.web.extender.internal.webbundle;

import com.liferay.portal.deploy.DeployUtil;
import com.liferay.portal.deploy.auto.OSGiAutoDeployListener;
import com.liferay.portal.events.GlobalStartupAction;
import com.liferay.portal.kernel.deploy.auto.AutoDeployException;
import com.liferay.portal.kernel.deploy.auto.AutoDeployListener;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.MapUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.xml.Document;
import com.liferay.portal.kernel.xml.DocumentException;
import com.liferay.portal.kernel.xml.Element;
import com.liferay.portal.kernel.xml.Node;
import com.liferay.portal.kernel.xml.QName;
import com.liferay.portal.kernel.xml.SAXReaderUtil;
import com.liferay.portal.kernel.xml.XPath;
import com.liferay.portal.kernel.zip.ZipWriter;
import com.liferay.portal.kernel.zip.ZipWriterFactoryUtil;
import com.liferay.portal.osgi.OSGiConstants;
import com.liferay.portal.util.Portal;
import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
import com.liferay.util.UniqueList;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import java.net.URI;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.osgi.framework.Constants;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.depend.DependencyVisitor;

/**
 * @author Raymond Aug
 */
public class WebBundleProcessor {

    public WebBundleProcessor(java.io.File file, Map<String, String[]> parameterMap) {

        _file = file;

        _parameterMap = parameterMap;
    }

    public void process() throws IOException {
        String webContextpath = MapUtil.getString(_parameterMap, OSGiConstants.WEB_CONTEXTPATH);

        if (!webContextpath.startsWith(StringPool.SLASH)) {
            webContextpath = StringPool.SLASH.concat(webContextpath);
        }

        List<AutoDeployListener> autoDeployListeners = GlobalStartupAction.getAutoDeployListeners();

        for (AutoDeployListener autoDeployListener : autoDeployListeners) {
            if (autoDeployListener instanceof OSGiAutoDeployListener) {
                continue;
            }

            try {
                autoDeployListener.deploy(_file, webContextpath);
            } catch (AutoDeployException e) {
                e.printStackTrace();
            }
        }

        String deployDir = null;

        try {
            deployDir = DeployUtil.getAutoDeployDestDir();
        } catch (Exception e) {
            throw new IOException(e);
        }

        _deployedAppFolder = new File(deployDir, webContextpath);

        if (!_deployedAppFolder.exists() || !_deployedAppFolder.isDirectory()) {
            return;
        }

        File manifestFile = new File(_deployedAppFolder, "META-INF/MANIFEST.MF");

        if (!manifestFile.exists()) {
            FileUtil.mkdirs(manifestFile.getParent());

            manifestFile.createNewFile();
        }

        Manifest manifest = new Manifest();

        FileInputStream fis = new FileInputStream(manifestFile);

        try {
            manifest.read(fis);
        } finally {
            fis.close();
        }

        _resourcePaths = new ArrayList<String>();

        processPaths(_deployedAppFolder, _deployedAppFolder.toURI());

        Attributes attributes = manifest.getMainAttributes();

        attributes.putValue(OSGiConstants.WEB_CONTEXTPATH, webContextpath);

        // If it's not a bundle, then we need to manipulate it into one. The
        // spec states that this is only true when the Manifest does not contain
        // a Bundle_SymbolicName header.

        if (!attributes.containsKey(Constants.BUNDLE_SYMBOLICNAME)) {
            processBundleSymbolicName(attributes, webContextpath);
            processBundleVersion(attributes);
            processBundleManifestVersion(attributes);
            processPortletXML(webContextpath);
            processLiferayPortletXML(webContextpath);

            // The order of these operations is important

            processBundleClassPath(attributes);
            processDeclarativeReferences(attributes);

            processExportImportPackage(attributes);
        }

        if (!attributes.containsKey(Attributes.Name.MANIFEST_VERSION.toString())) {

            attributes.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
        }

        FileOutputStream fos = new FileOutputStream(manifestFile);

        try {
            manifest.write(fos);
        } finally {
            fos.close();
        }
    }

    public java.io.InputStream getInputStream() throws IOException {
        if (!_deployedAppFolder.exists() || !_deployedAppFolder.isDirectory()) {
            return null;
        }

        ZipWriter zipWriter = ZipWriterFactoryUtil.getZipWriter();

        try {
            writeJarPaths(_deployedAppFolder, _deployedAppFolder.toURI(), zipWriter);
        } finally {
            //_deployedAppFolder.delete();
        }

        zipWriter.getFile();

        return new FileInputStream(zipWriter.getFile());
    }

    protected void processBundleClassPath(Attributes attributes) {
        StringBundler sb = new StringBundler();

        sb.append("WEB-INF/classes/");

        if (_resourcePaths.contains("WEB-INF/lib/")) {
            for (String path : _resourcePaths) {
                if (!path.startsWith("WEB-INF/lib/") || !path.endsWith(".jar")
                        || ArrayUtil.contains(_EXCLUDED_CLASS_PATHS, path)) {

                    continue;
                }

                sb.append(", ");
                sb.append(path);
            }
        }

        attributes.putValue(Constants.BUNDLE_CLASSPATH, sb.toString());
    }

    protected void processBundleManifestVersion(Attributes attributes) {
        String bundleManifestVersion = MapUtil.getString(_parameterMap, Constants.BUNDLE_MANIFESTVERSION);

        if (Validator.isNull(bundleManifestVersion)) {
            bundleManifestVersion = "2";
        }

        attributes.putValue(Constants.BUNDLE_MANIFESTVERSION, bundleManifestVersion);
    }

    protected void processBundleSymbolicName(Attributes attributes, String webContextpath) {

        String bundleSymbolicName = MapUtil.getString(_parameterMap, Constants.BUNDLE_SYMBOLICNAME);

        if (Validator.isNull(bundleSymbolicName)) {
            bundleSymbolicName = webContextpath.substring(1);
        }

        attributes.putValue(Constants.BUNDLE_SYMBOLICNAME, bundleSymbolicName);
    }

    protected void processBundleVersion(Attributes attributes) {
        _version = MapUtil.getString(_parameterMap, Constants.BUNDLE_VERSION);

        if (Validator.isNull(_version)) {
            _version = "0.0.1";
        }

        attributes.putValue(Constants.BUNDLE_VERSION, _version);
    }

    protected void processClass(DependencyVisitor dependencyVisitor, String className, InputStream inputStream,
            List<String> packageList) throws IOException {

        try {
            ClassReader classReader = new ClassReader(inputStream);

            classReader.accept(dependencyVisitor, 0);

            Set<String> packages = dependencyVisitor.getPackages();

            for (String packageName : packages) {
                packageList.add(packageName.replaceAll(StringPool.SLASH, StringPool.PERIOD));
            }
        } catch (NullPointerException npe) {
            //npe.printStackTrace();

        }
    }

    protected void processClassDependencies(File classFile) throws IOException {
        DependencyVisitor dependencyVisitor = new DependencyVisitor();

        FileInputStream fis = new FileInputStream(classFile);

        processClass(dependencyVisitor, classFile.getName(), fis, _referencedPackages);

        Set<String> jarPackages = dependencyVisitor.getGlobals().keySet();

        for (String jarPackage : jarPackages) {
            _classProvidedPackages.add(jarPackage.replaceAll(StringPool.SLASH, StringPool.PERIOD));
        }
    }

    protected void processDeclarativeReferences(Attributes attributes) throws IOException {

        // References from web.xml

        File xml = new File(_deployedAppFolder, "WEB-INF/web.xml");

        if (xml.exists()) {
            String content = FileUtil.read(xml);

            Document document = null;

            try {
                document = SAXReaderUtil.read(content, false);
            } catch (DocumentException de) {
                throw new IOException(de);
            }

            Element rootElement = document.getRootElement();

            for (String classReference : _WEBXML_CLASSREFERENCE_ELEMENTS) {
                XPath xPath = SAXReaderUtil.createXPath(classReference, "x", "http://java.sun.com/xml/ns/j2ee");

                List<Node> selectNodes = xPath.selectNodes(rootElement);

                for (Node node : selectNodes) {
                    String value = node.getText().trim();

                    int pos = value.lastIndexOf(StringPool.PERIOD);

                    _referencedPackages.add(value.substring(0, pos));
                }
            }
        }

        // References from portlet.xml

        xml = new File(_deployedAppFolder, "WEB-INF/portlet.xml");

        if (xml.exists()) {
            String content = FileUtil.read(xml);

            Document document = null;

            try {
                document = SAXReaderUtil.read(content);
            } catch (DocumentException de) {
                throw new IOException(de);
            }

            Element rootElement = document.getRootElement();

            for (String classReference : _PORTLETXML_CLASSREFERENCE_ELEMENTS) {
                XPath xPath = SAXReaderUtil.createXPath(classReference, "x",
                        "http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd");

                List<Node> selectNodes = xPath.selectNodes(rootElement);

                for (Node node : selectNodes) {
                    String value = node.getText().trim();

                    int pos = value.lastIndexOf(StringPool.PERIOD);

                    _referencedPackages.add(value.substring(0, pos));
                }
            }
        }

        // References from liferay-web.xml

        // TODO do we really need this?

        // References from liferay-portlet.xml

        xml = new File(_deployedAppFolder, "WEB-INF/liferay-portlet.xml");

        if (xml.exists()) {
            String content = FileUtil.read(xml);

            Document document = null;

            try {
                document = SAXReaderUtil.read(content);
            } catch (DocumentException de) {
                throw new IOException(de);
            }

            Element rootElement = document.getRootElement();

            for (String classReference : _LIFERAYPORTLETXML_CLASSREFERENCE_ELEMENTS) {
                XPath xPath = SAXReaderUtil.createXPath(classReference);

                List<Node> selectNodes = xPath.selectNodes(rootElement);

                for (Node node : selectNodes) {
                    String value = node.getText().trim();

                    int pos = value.lastIndexOf(StringPool.PERIOD);

                    _referencedPackages.add(value.substring(0, pos));
                }
            }
        }
    }

    protected void processExportImportPackage(Attributes attributes) throws IOException {

        for (String packageName : _referencedPackages) {
            if (packageName.startsWith("java.") || _jarProvidedPackages.contains(packageName)) {

                continue;
            }

            if (_classProvidedPackages.contains(packageName)) {
                _exportPackages.add(packageName);
            } else {
                _importPackages.add(packageName);
            }
        }

        for (String packageName : _classProvidedPackages) {
            _exportPackages.add(packageName);
        }

        if (!_exportPackages.isEmpty()) {
            Collections.sort(_exportPackages);

            String exportPackages = StringUtil.merge(_exportPackages, ";version=\"".concat(_version).concat("\","));

            attributes.putValue(Constants.EXPORT_PACKAGE,
                    exportPackages.concat(";version=\"").concat(_version).concat("\""));
        }
        String importPackage = MapUtil.getString(_parameterMap, Constants.IMPORT_PACKAGE);

        if (Validator.isNotNull(importPackage)) {
            attributes.putValue(Constants.IMPORT_PACKAGE, importPackage);
        } else if (!_importPackages.isEmpty()) {
            Collections.sort(_importPackages);

            String importPackages = StringUtil.merge(_importPackages, StringPool.COMMA);

            attributes.putValue(Constants.IMPORT_PACKAGE, importPackages);
        }
    }

    protected void processJarDependencies(File jarFile) throws IOException {
        DependencyVisitor dependencyVisitor = new DependencyVisitor();

        ZipFile zipFile = new ZipFile(jarFile);

        Enumeration<? extends ZipEntry> en = zipFile.entries();

        while (en.hasMoreElements()) {
            ZipEntry zipEntry = en.nextElement();

            String name = zipEntry.getName();

            if (name.endsWith(".class")) {
                InputStream inputStream = zipFile.getInputStream(zipEntry);

                processClass(dependencyVisitor, name, inputStream, _referencedPackages);
            }
        }

        Set<String> jarPackages = dependencyVisitor.getGlobals().keySet();

        for (String jarPackage : jarPackages) {
            _jarProvidedPackages.add(jarPackage.replaceAll(StringPool.SLASH, StringPool.PERIOD));
        }
    }

    protected void processLiferayPortletXML(String webContextpath) throws IOException {

        File liferayPortletXMLFile = new File(_deployedAppFolder, "WEB-INF/liferay-portlet.xml");

        if (!liferayPortletXMLFile.exists()) {
            return;
        }

        String content = FileUtil.read(liferayPortletXMLFile);

        Document liferayPortletXMLDoc = null;

        try {
            liferayPortletXMLDoc = SAXReaderUtil.read(content);
        } catch (DocumentException de) {
            throw new IOException(de);
        }

        Element rootEl = liferayPortletXMLDoc.getRootElement();

        List<Element> portletElements = rootEl.elements("portlet");

        for (Element portletElement : portletElements) {
            Element previousChild = portletElement.element("virtual-path");

            if (previousChild == null) {
                previousChild = portletElement.element("icon");
            }

            if (previousChild == null) {
                previousChild = portletElement.element("portlet-name");
            }

            Element strutsPathElement = portletElement.element("struts-path");

            if (strutsPathElement == null) {
                List<Node> children = portletElement.content();

                int pos = children.indexOf(previousChild);

                strutsPathElement = SAXReaderUtil.createElement("struts-path");

                strutsPathElement.setText("osgi".concat(webContextpath));

                children.add(pos + 1, strutsPathElement);
            } else {
                String strutsPath = strutsPathElement.getTextTrim();

                if (!strutsPath.startsWith(StringPool.SLASH)) {
                    strutsPath = StringPool.SLASH.concat(strutsPath);
                }

                strutsPath = "osgi".concat(webContextpath).concat(strutsPath);

                strutsPathElement.setText(strutsPath);
            }
        }

        content = DDMXMLUtil.formatXML(liferayPortletXMLDoc);

        FileUtil.write(liferayPortletXMLFile, content);
    }

    protected void processPaths(File directory, URI baseURI) throws IOException {
        File[] files = directory.listFiles();

        for (File file : files) {
            String relativePath = baseURI.relativize(file.toURI()).getPath();

            _resourcePaths.add(relativePath);

            if (relativePath.startsWith("WEB-INF/classes/") && relativePath.endsWith(".class")) {

                processClassDependencies(file);
            } else if (relativePath.startsWith("WEB-INF/lib/") && relativePath.endsWith(".jar")
                    && !ArrayUtil.contains(_EXCLUDED_CLASS_PATHS, relativePath)) {

                processJarDependencies(file);
            }

            if (file.isDirectory()) {
                processPaths(file, baseURI);
            }
        }
    }

    protected void processPortletXML(String webContextpath) throws IOException {
        File portletXMLFile = new File(_deployedAppFolder, "WEB-INF/" + Portal.PORTLET_XML_FILE_NAME_STANDARD);

        if (!portletXMLFile.exists()) {
            return;
        }

        String content = FileUtil.read(portletXMLFile);

        Document document = null;

        try {
            document = SAXReaderUtil.read(content);
        } catch (DocumentException de) {
            throw new IOException(de);
        }

        Element rootElement = document.getRootElement();

        List<Element> portletElements = rootElement.elements("portlet");

        for (Element portletElement : portletElements) {
            String portletName = portletElement.elementText("portlet-name");

            String invokerPortletName = "osgi".concat(webContextpath).concat(StringPool.SLASH).concat(portletName);

            XPath xPath = SAXReaderUtil.createXPath(_INVOKER_PORTLET_NAME_XPATH);

            Element invokerPortletNameEl = (Element) xPath.selectSingleNode(portletElement);

            if (invokerPortletNameEl == null) {
                Element portletClassElement = portletElement.element("portlet-class");

                List<Node> children = portletElement.content();

                int pos = children.indexOf(portletClassElement);

                QName qName = rootElement.getQName();

                Element initParamElement = SAXReaderUtil
                        .createElement(SAXReaderUtil.createQName("init-param", qName.getNamespace()));

                initParamElement.addElement("name").setText("com.liferay.portal.invokerPortletName");
                initParamElement.addElement("value").setText(invokerPortletName);

                children.add(pos + 1, initParamElement);
            } else {
                Element valueElement = invokerPortletNameEl.element("value");

                invokerPortletName = valueElement.getTextTrim();

                if (!invokerPortletName.startsWith(StringPool.SLASH)) {
                    invokerPortletName = StringPool.SLASH.concat(invokerPortletName);
                }

                invokerPortletName = "osgi".concat(webContextpath).concat(invokerPortletName);

                valueElement.setText(invokerPortletName);
            }
        }

        content = DDMXMLUtil.formatXML(document);

        FileUtil.write(portletXMLFile, content);
    }

    protected void writeJarPaths(File directory, URI baseURI, ZipWriter zipWriter) {

        File[] files = directory.listFiles();

        FileInputStream fis = null;

        for (File file : files) {
            String path = baseURI.relativize(file.toURI()).getPath();

            if (!file.isDirectory()) {
                if (ArrayUtil.contains(_EXCLUDED_CLASS_PATHS, path)) {
                    continue;
                }

                try {
                    fis = new FileInputStream(file);

                    zipWriter.addEntry(path, fis);
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                } finally {
                    if (fis != null) {
                        try {
                            fis.close();
                        } catch (IOException ioe) {
                            ioe.printStackTrace();
                        }
                    }

                    fis = null;
                }
            } else {
                writeJarPaths(file, baseURI, zipWriter);
            }
        }
    }

    private static final String[] _EXCLUDED_CLASS_PATHS = new String[] { "WEB-INF/lib/commons-codec.jar",
            "WEB-INF/lib/commons-fileupload.jar", "WEB-INF/lib/commons-io.jar", "WEB-INF/lib/commons-lang.jar",
            "WEB-INF/lib/commons-logging.jar", "WEB-INF/lib/log4j.jar", "WEB-INF/lib/util-bridges.jar",
            "WEB-INF/lib/util-java.jar", "WEB-INF/lib/util-taglib.jar" };

    private static final String _INVOKER_PORTLET_NAME_XPATH = "init-param[name/text()='com.liferay.portal.invokerPortletName']";

    private static final String[] _LIFERAYPORTLETXML_CLASSREFERENCE_ELEMENTS = new String[] {
            "//configuration-action-class", "//indexer-class", "//open-search-class", "//portlet-url-class",
            "//friendly-url-mapper-class", "//url-encoder-class", "//portlet-data-handler-class",
            "//portlet-layout-listener-class", "//poller-processor-class", "//pop-message-listener-class",
            "//social-activity-interpreter-class", "//social-request-interpreter-class", "//webdav-storage-class",
            "//xml-rpc-method-class", "//control-panel-entry-class", "//asset-renderer-factory",
            "//atom-collection-adapter", "//custom-attributes-display", "//permission-propagator",
            "//workflow-handler" };

    private static final String[] _PORTLETXML_CLASSREFERENCE_ELEMENTS = new String[] { "//x:filter-class",
            "//x:listener-class", "//x:portlet-class", "//x:resource-bundle" };

    private static final String[] _WEBXML_CLASSREFERENCE_ELEMENTS = new String[] { "//x:filter-class",
            "//x:listener-class", "//x:servlet-class" };

    private List<String> _classProvidedPackages = new UniqueList<String>();
    private File _deployedAppFolder;
    private List<String> _exportPackages = new UniqueList<String>();
    private File _file;
    private List<String> _importPackages = new UniqueList<String>();
    private List<String> _jarProvidedPackages = new UniqueList<String>();
    private List<String> _referencedPackages = new UniqueList<String>();
    private List<String> _resourcePaths;
    private Map<String, String[]> _parameterMap;
    private String _version;

}