com.bstek.dorado.core.pkgs.PackageManager.java Source code

Java tutorial

Introduction

Here is the source code for com.bstek.dorado.core.pkgs.PackageManager.java

Source

/*
 * This file is part of Dorado 7.x (http://dorado7.bsdn.org).
 * 
 * Copyright (c) 2002-2012 BSTEK Corp. All rights reserved.
 * 
 * This file is dual-licensed under the AGPLv3 (http://www.gnu.org/licenses/agpl-3.0.html) 
 * and BSDN commercial (http://www.bsdn.org/licenses) licenses.
 * 
 * If you are unsure which license is appropriate for your use, please contact the sales department
 * at http://www.bstek.com/contact.
 */

package com.bstek.dorado.core.pkgs;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletContextListener;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.bstek.dorado.util.clazz.ClassUtils;

/**
 * @author Benny Bao (mailto:benny.bao@bstek.com)
 * @since 2011-7-22
 */
public final class PackageManager {
    private static final Log logger = LogFactory.getLog(PackageManager.class);

    private static final String PACKAGE_PROPERTIES_LOCATION = "META-INF/dorado-package.properties";

    private static final String AGPL = "AGPL";
    private static final String BSDN_MEMBER = "BSDN-Member";
    private static final String BSDN_COMMERCIAL = "BSDN-Commercial";
    private static final String INHERITED = "Inherited";
    private static final String[] LICENSE_INHERITED = new String[] { AGPL, BSDN_MEMBER, BSDN_COMMERCIAL };

    private static final Map<String, PackageInfo> packageInfosMap = new LinkedHashMap<String, PackageInfo>();
    private static boolean packageInfoBuilded = false;

    private static class DependsVersion {
        private boolean includeMinVersion = true;
        private String minVersion;
        private boolean includeMaxVersion = true;
        private String maxVersion;

        public boolean isIncludeMinVersion() {
            return includeMinVersion;
        }

        public void setIncludeMinVersion(boolean includeMinVersion) {
            this.includeMinVersion = includeMinVersion;
        }

        public String getMinVersion() {
            return minVersion;
        }

        public void setMinVersion(String minVersion) {
            this.minVersion = minVersion;
        }

        public boolean isIncludeMaxVersion() {
            return includeMaxVersion;
        }

        public void setIncludeMaxVersion(boolean includeMaxVersion) {
            this.includeMaxVersion = includeMaxVersion;
        }

        public String getMaxVersion() {
            return maxVersion;
        }

        public void setMaxVersion(String maxVersion) {
            this.maxVersion = maxVersion;
        }
    }

    private PackageManager() {
    }

    private static String trimDependsVersion(String version) {
        if ("*".equals(version)) {
            return null;
        } else if (version.indexOf('*') >= 0) {
            throw new IllegalArgumentException();
        }
        return version;
    }

    private static DependsVersion parseDependsVersion(String text) {
        DependsVersion dependsVersion = new DependsVersion();

        boolean beforeContent = true, afterContent = false, inVersion = false, commaFound = false;
        StringBuffer version = new StringBuffer(16);
        char c;
        for (int i = 0, len = text.length(); i < len; i++) {
            c = text.charAt(i);
            if (c == ' ') {
                if (inVersion) {
                    throw new IllegalArgumentException();
                }
                continue;
            } else if (afterContent) {
                throw new IllegalArgumentException();
            } else if (c == '[' || c == '(') {
                if (!beforeContent) {
                    throw new IllegalArgumentException();
                }
                beforeContent = false;
                dependsVersion.setIncludeMinVersion(c == '[');
            } else if (c == ']' || c == ')') {
                if (beforeContent) {
                    throw new IllegalArgumentException();
                }
                afterContent = true;
                dependsVersion.setIncludeMaxVersion(c == ']');
            } else if (c == ',') {
                if (beforeContent || commaFound) {
                    throw new IllegalArgumentException();
                }
                if (version.length() > 0) {
                    String v = trimDependsVersion(version.toString());
                    dependsVersion.setMinVersion(v);
                    version.setLength(0);
                }
            } else if (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'A' || c == '.' || c == '-'
                    || c == '*') {
                version.append(c);
            } else {
                throw new IllegalArgumentException();
            }
        }

        if (version.length() > 0) {
            String v = trimDependsVersion(version.toString());
            if (commaFound) {
                dependsVersion.setMaxVersion(v);
            } else {
                dependsVersion.setMinVersion(v);
                dependsVersion.setMaxVersion(v);
            }
        }
        return dependsVersion;
    }

    private static int compareVersionSection(String section1, String section2) {
        if (StringUtils.isNumeric(section1) && StringUtils.isNumeric(section2)) {
            return Integer.valueOf(section1) - Integer.valueOf(section2);
        } else {
            return section1.compareTo(section2);
        }
    }

    private static int compareVersion(String version1, String version2) {
        String[] sections1 = StringUtils.split(version1, ".-");
        String[] sections2 = StringUtils.split(version2, ".-");
        for (int i = 0; i < sections1.length; i++) {
            if (i >= sections2.length) {
                break;
            }
            String section1 = sections1[i], section2 = sections2[i];
            int result = compareVersionSection(section1, section2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }

    private static void calculateDepends(PackageInfo packageInfo, List<PackageInfo> calculatedPackages,
            Map<String, PackageInfo> packageMap) throws Exception {
        Dependence[] dependences = packageInfo.getDepends();
        if (dependences == null || dependences.length == 0) {
            pushPackageInfo(calculatedPackages, packageInfo);
            return;
        }

        for (Dependence dependence : dependences) {
            PackageInfo dependedPackageInfo = packageMap.get(dependence.getPackageName());
            if (dependedPackageInfo == null) {
                throw new IllegalArgumentException("Package  \"" + dependence.getPackageName()
                        + "\" not found, Which is depended by \"" + packageInfo.getName() + "\".");
            }

            if (StringUtils.isNotEmpty(dependence.getVersion())) {
                String dependedPackageVersion = dependedPackageInfo.getVersion();

                DependsVersion dependsVersion;
                try {
                    dependsVersion = parseDependsVersion(dependence.getVersion());
                } catch (IllegalArgumentException e) {
                    throw new IllegalArgumentException("Invalid depends version \"" + dependence.getVersion()
                            + "\" found in Package \"" + packageInfo.getName() + "\".");
                }

                boolean versionMatch = true;
                if (StringUtils.isNotEmpty(dependsVersion.getMinVersion())) {
                    int i = compareVersion(dependsVersion.getMinVersion(), dependedPackageVersion);
                    if (i > 0 || i == 0 && !dependsVersion.isIncludeMinVersion()) {
                        versionMatch = false;
                    }
                }

                if (StringUtils.isNotEmpty(dependsVersion.getMaxVersion())) {
                    int i = compareVersion(dependsVersion.getMaxVersion(), dependedPackageVersion);
                    if (i < 0 || i == 0 && !dependsVersion.isIncludeMaxVersion()) {
                        versionMatch = false;
                    }
                }

                if (!versionMatch) {
                    throw new IllegalArgumentException("Depended version mismatch. Expect \""
                            + dependence.getVersion() + "\" but \"" + dependedPackageVersion + "\" found.");
                }
            }

            calculateDepends(dependedPackageInfo, calculatedPackages, packageMap);
        }
        pushPackageInfo(calculatedPackages, packageInfo);
    }

    private static void pushPackageInfo(List<PackageInfo> calculatedPackages, PackageInfo packageInfo) {
        if (!calculatedPackages.contains(packageInfo)) {
            String packageName = packageInfo.getName();
            if (packageName.equals("dorado-hibernate") || packageName.equals("dorado-jdbc")) {
                calculatedPackages.add(1, packageInfo);
            } else {
                calculatedPackages.add(packageInfo);
            }
        }
    }

    private static Dependence parseDependence(String text) {
        Dependence dependence = new Dependence();

        String packageName = "", version = "";
        boolean versionFound = false;
        char c;
        for (int i = 0, len = text.length(); i < len; i++) {
            c = text.charAt(i);
            if (!versionFound) {
                if (c == '[' || c == '(') {
                    versionFound = true;
                    version += c;
                } else {
                    packageName += c;
                }
            } else {
                version += c;
            }
        }

        if (StringUtils.isEmpty(packageName)) {
            throw new IllegalArgumentException("Depended packageName undefined.");
        }
        dependence.setPackageName(packageName);

        if (version != null) {
            dependence.setVersion(version);
        }
        return dependence;
    }

    private static void doBuildPackageInfos() throws Exception {
        Map<String, PackageInfo> packageMap = new HashMap<String, PackageInfo>();

        Enumeration<URL> defaultContextFileResources = org.springframework.util.ClassUtils.getDefaultClassLoader()
                .getResources(PACKAGE_PROPERTIES_LOCATION);
        while (defaultContextFileResources.hasMoreElements()) {
            URL url = defaultContextFileResources.nextElement();
            InputStream in = null;
            try {
                URLConnection con = url.openConnection();
                con.setUseCaches(false);
                in = con.getInputStream();
                Properties properties = new Properties();
                properties.load(in);

                String packageName = properties.getProperty("name");
                if (StringUtils.isEmpty(packageName)) {
                    throw new IllegalArgumentException("Package name undefined.");
                }

                PackageInfo packageInfo = new PackageInfo(packageName);

                packageInfo.setAddonVersion(properties.getProperty("addonVersion"));
                packageInfo.setVersion(properties.getProperty("version"));

                String dependsText = properties.getProperty("depends");
                if (StringUtils.isNotBlank(dependsText)) {
                    List<Dependence> dependences = new ArrayList<Dependence>();
                    for (String depends : StringUtils.split(dependsText, "; ")) {
                        if (StringUtils.isNotEmpty(depends)) {
                            Dependence dependence = parseDependence(depends);
                            dependences.add(dependence);
                        }
                    }
                    if (!dependences.isEmpty()) {
                        packageInfo.setDepends(dependences.toArray(new Dependence[0]));
                    }
                }

                String license = StringUtils.trim(properties.getProperty("license"));
                if (StringUtils.isNotEmpty(license)) {
                    if (INHERITED.equals(license)) {
                        packageInfo.setLicense(LICENSE_INHERITED);
                    } else {
                        String[] licenses = StringUtils.split(license);
                        licenses = StringUtils.stripAll(licenses);
                        packageInfo.setLicense(licenses);
                    }
                }

                packageInfo.setLoadUnlicensed(BooleanUtils.toBoolean(properties.getProperty("loadUnlicensed")));

                packageInfo.setClassifier(properties.getProperty("classifier"));
                packageInfo.setHomePage(properties.getProperty("homePage"));
                packageInfo.setDescription(properties.getProperty("description"));

                packageInfo.setPropertiesLocations(properties.getProperty("propertiesConfigLocations"));
                packageInfo.setContextLocations(properties.getProperty("contextConfigLocations"));
                packageInfo.setComponentLocations(properties.getProperty("componentConfigLocations"));
                packageInfo.setServletContextLocations(properties.getProperty("servletContextConfigLocations"));

                String configurerClass = properties.getProperty("configurer");
                if (StringUtils.isNotBlank(configurerClass)) {
                    Class<?> type = ClassUtils.forName(configurerClass);
                    packageInfo.setConfigurer((PackageConfigurer) type.newInstance());
                }

                String listenerClass = properties.getProperty("listener");
                if (StringUtils.isNotBlank(listenerClass)) {
                    Class<?> type = ClassUtils.forName(listenerClass);
                    packageInfo.setListener((PackageListener) type.newInstance());
                }

                String servletContextListenerClass = properties.getProperty("servletContextListener");
                if (StringUtils.isNotBlank(servletContextListenerClass)) {
                    Class<?> type = ClassUtils.forName(servletContextListenerClass);
                    packageInfo.setServletContextListener((ServletContextListener) type.newInstance());
                }

                if (packageMap.containsKey(packageName)) {
                    PackageInfo conflictPackageInfo = packageMap.get(packageName);
                    StringBuffer conflictInfo = new StringBuffer(20);
                    conflictInfo.append('[').append(conflictPackageInfo.getName()).append(" - ")
                            .append(conflictPackageInfo.getVersion()).append(']');
                    conflictInfo.append(" and ");
                    conflictInfo.append('[').append(packageInfo.getName()).append(" - ")
                            .append(packageInfo.getVersion()).append(']');

                    Exception e = new IllegalArgumentException("More than one package \"" + packageName
                            + "\" found. They are " + conflictInfo.toString());
                    logger.warn(e, e);
                }

                packageMap.put(packageName, packageInfo);
            } catch (Exception e) {
                throw new IllegalArgumentException("Error occured during parsing \"" + url.getPath() + "\".", e);
            } finally {
                if (in != null) {
                    in.close();
                }
            }
        }

        List<PackageInfo> calculatedPackages = new ArrayList<PackageInfo>();
        for (PackageInfo packageInfo : packageMap.values()) {
            calculateDepends(packageInfo, calculatedPackages, packageMap);
        }

        packageInfosMap.clear();
        for (PackageInfo packageInfo : calculatedPackages) {
            packageInfosMap.put(packageInfo.getName(), packageInfo);
        }
    }

    private static void buildPackageInfos() throws Exception {
        if (!packageInfoBuilded) {
            packageInfoBuilded = true;
            doBuildPackageInfos();
        }
    }

    public static Map<String, PackageInfo> getPackageInfoMap() throws Exception {
        buildPackageInfos();
        return packageInfosMap;
    }
}