com.sonicle.webtop.core.sdk.ServiceManifest.java Source code

Java tutorial

Introduction

Here is the source code for com.sonicle.webtop.core.sdk.ServiceManifest.java

Source

/*
 * WebTop Services is a Web Application framework developed by Sonicle S.r.l.
 * Copyright (C) 2014 Sonicle S.r.l.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY SONICLE, SONICLE DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * 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 for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Sonicle S.r.l. at email address sonicle@sonicle.com
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * Sonicle logo and Sonicle copyright notice. If the display of the logo is not
 * reasonably feasible for technical reasons, the Appropriate Legal Notices must
 * display the words "Copyright (C) 2014 Sonicle S.r.l.".
 */
package com.sonicle.webtop.core.sdk;

import com.sonicle.commons.LangUtils;
import com.sonicle.webtop.core.model.ServicePermission;
import com.sonicle.webtop.core.model.ServiceSharePermission;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

/**
 *
 * @author malbinola
 */
public class ServiceManifest {
    public static final String BUILD_TYPE_DEV = "dev";
    public static final String BUILD_TYPE_PROD = "prod";
    private static final String JAVAPKG_REST = "rest";

    protected String id;
    protected String xid;
    protected String javaPackage;
    protected String jsPackage;
    protected ServiceVersion version;
    protected ServiceVersion oldVersion;
    protected String buildDate;
    protected String buildType;
    protected String company;
    protected String companyEmail;
    protected String companyWebSite;
    protected String supportEmail;
    protected String controllerClassName;
    protected String managerClassName;
    protected String privateServiceClassName;
    protected String userOptionsServiceClassName;
    protected String publicServiceClassName;
    protected String jobServiceClassName;
    protected String privateServiceJsClassName;
    protected String privateServiceVarsModelJsClassName;
    protected String publicServiceJsClassName;
    protected String publicServiceVarsModelJsClassName;
    protected String userOptionsViewJsClassName;
    protected String userOptionsModelJsClassName;
    protected Boolean hidden;
    protected Map<String, RestApiEndpoint> restApiEndpoints = new LinkedHashMap<>();
    protected Map<String, RestApi> restApis = new LinkedHashMap<>();
    protected ArrayList<ServicePermission> permissions = new ArrayList<>();
    protected ArrayList<Portlet> portlets = new ArrayList<>();

    public ServiceManifest() {
        version = new ServiceVersion();
        oldVersion = new ServiceVersion();
        buildDate = StringUtils.EMPTY;
        company = "Unknown Company";
        companyEmail = "sonicle@sonicle.com";
        companyWebSite = "http://www.sonicle.com";
        supportEmail = "sonicle@sonicle.com";
    }

    public ServiceManifest(HierarchicalConfiguration svcEl) throws Exception {

        String pkg = svcEl.getString("package");
        if (StringUtils.isEmpty(pkg))
            throw new Exception("Invalid value for property [package]");
        javaPackage = StringUtils.lowerCase(pkg);
        id = javaPackage;

        String jspkg = svcEl.getString("jsPackage");
        if (StringUtils.isEmpty(jspkg))
            throw new Exception("Invalid value for property [jsPackage]");
        jsPackage = jspkg; // Lowercase allowed!

        String sname = svcEl.getString("shortName");
        if (StringUtils.isEmpty(sname))
            throw new Exception("Invalid value for property [shortName]");
        xid = sname;

        ServiceVersion ver = new ServiceVersion(svcEl.getString("version"));
        if (ver.isUndefined())
            throw new Exception("Invalid value for property [version]");
        version = ver;

        buildDate = StringUtils.defaultIfBlank(svcEl.getString("buildDate"), null);
        buildType = StringUtils.defaultIfBlank(svcEl.getString("buildType"), BUILD_TYPE_DEV);
        company = StringUtils.defaultIfBlank(svcEl.getString("company"), null);
        companyEmail = StringUtils.defaultIfBlank(svcEl.getString("companyEmail"), null);
        companyWebSite = StringUtils.defaultIfBlank(svcEl.getString("companyWebSite"), null);
        supportEmail = StringUtils.defaultIfBlank(svcEl.getString("supportEmail"), null);

        List<HierarchicalConfiguration> hconf = null;

        hconf = svcEl.configurationsAt("controller");
        if (!hconf.isEmpty()) {
            //if (hconf.size() != 1) throw new Exception(invalidCardinalityEx("controller", "1"));
            if (hconf.size() > 1)
                throw new Exception(invalidCardinalityEx("controller", "*1"));

            final String cn = hconf.get(0).getString("[@className]");
            if (StringUtils.isBlank(cn))
                throw new Exception(invalidAttributeValueEx("controller", "className"));
            controllerClassName = buildJavaClassName(javaPackage, cn);

        } else { // Old-style configuration
            if (svcEl.containsKey("controllerClassName")) {
                controllerClassName = LangUtils.buildClassName(javaPackage,
                        StringUtils.defaultIfEmpty(svcEl.getString("controllerClassName"), "Controller"));
            }
        }

        hconf = svcEl.configurationsAt("manager");
        if (!hconf.isEmpty()) {
            if (!hconf.isEmpty()) {
                if (hconf.size() > 1)
                    throw new Exception(invalidCardinalityEx("manager", "*1"));

                final String cn = hconf.get(0).getString("[@className]");
                if (StringUtils.isBlank(cn))
                    throw new Exception(invalidAttributeValueEx("manager", "className"));
                managerClassName = buildJavaClassName(javaPackage, cn);
            }

        } else { // Old-style configuration
            if (svcEl.containsKey("managerClassName")) {
                managerClassName = LangUtils.buildClassName(javaPackage,
                        StringUtils.defaultIfEmpty(svcEl.getString("managerClassName"), "Manager"));
            }
        }

        /*
        hconf = svcEl.configurationsAt("privateService");
        if (!hconf.isEmpty()) {
           if (!hconf.isEmpty()) {
        if (hconf.size() > 1) throw new Exception(invalidCardinalityEx("manager", "*1"));
            
        final String cn = hconf.get(0).getString("[@className]");
        if (StringUtils.isBlank(cn)) throw new Exception(invalidAttributeValueEx("privateService", "className"));
        final String jcn = hconf.get(0).getString("[@jsClassName]");
        if (StringUtils.isBlank(jcn)) throw new Exception(invalidAttributeValueEx("privateService", "jsClassName"));
            
        privateServiceClassName = LangUtils.buildClassName(javaPackage, cn);
        privateServiceJsClassName = jcn;
        privateServiceVarsModelJsClassName = hconf.get(0).getString("[@jsClassName]");
           }
               
        } else { // Old-style configuration
           if (svcEl.containsKey("serviceClassName")) {
        String cn = StringUtils.defaultIfEmpty(svcEl.getString("serviceClassName"), "Service");
        privateServiceClassName = LangUtils.buildClassName(javaPackage, cn);
        privateServiceJsClassName = StringUtils.defaultIfEmpty(svcEl.getString("serviceJsClassName"), cn);
        privateServiceVarsModelJsClassName = StringUtils.defaultIfEmpty(svcEl.getString("serviceVarsModelJsClassName"), "model.ServiceVars");
           }
        }
        */

        if (svcEl.containsKey("serviceClassName")) {
            String cn = StringUtils.defaultIfEmpty(svcEl.getString("serviceClassName"), "Service");
            privateServiceClassName = LangUtils.buildClassName(javaPackage, cn);
            privateServiceJsClassName = StringUtils.defaultIfEmpty(svcEl.getString("serviceJsClassName"), cn);
            privateServiceVarsModelJsClassName = StringUtils
                    .defaultIfEmpty(svcEl.getString("serviceVarsModelJsClassName"), "model.ServiceVars");
        }

        if (svcEl.containsKey("publicServiceClassName")) {
            String cn = StringUtils.defaultIfEmpty(svcEl.getString("publicServiceClassName"), "PublicService");
            publicServiceClassName = LangUtils.buildClassName(javaPackage,
                    StringUtils.defaultIfEmpty(svcEl.getString("publicServiceClassName"), "PublicService"));
            publicServiceJsClassName = StringUtils.defaultIfEmpty(svcEl.getString("publicServiceJsClassName"), cn);
            publicServiceVarsModelJsClassName = StringUtils.defaultIfEmpty(
                    svcEl.getString("publicServiceVarsModelJsClassName"), "model.PublicServiceVars");
        }

        hconf = svcEl.configurationsAt("jobService");
        if (!hconf.isEmpty()) {
            if (hconf.size() > 1)
                throw new Exception(invalidCardinalityEx("jobService", "*1"));

            final String cn = hconf.get(0).getString("[@className]");
            if (StringUtils.isBlank(cn))
                throw new Exception(invalidAttributeValueEx("jobService", "className"));
            jobServiceClassName = LangUtils.buildClassName(javaPackage, cn);

        } else { // Old-style configuration
            if (svcEl.containsKey("jobServiceClassName")) {
                jobServiceClassName = LangUtils.buildClassName(javaPackage,
                        StringUtils.defaultIfEmpty(svcEl.getString("jobServiceClassName"), "JobService"));
            }
        }

        if (!svcEl.configurationsAt("userOptions").isEmpty()) {
            userOptionsServiceClassName = LangUtils.buildClassName(javaPackage, StringUtils
                    .defaultIfEmpty(svcEl.getString("userOptions.serviceClassName"), "UserOptionsService"));
            userOptionsViewJsClassName = StringUtils.defaultIfEmpty(svcEl.getString("userOptions.viewJsClassName"),
                    "view.UserOptions");
            userOptionsModelJsClassName = StringUtils
                    .defaultIfEmpty(svcEl.getString("userOptions.modelJsClassName"), "model.UserOptions");
        }

        hidden = svcEl.getBoolean("hidden", false);

        hconf = svcEl.configurationsAt("restApiEndpoint");
        if (!hconf.isEmpty()) {
            for (HierarchicalConfiguration el : hconf) {
                final String name = el.getString("[@name]");
                if (StringUtils.isBlank(name))
                    throw new Exception(invalidAttributeValueEx("restApiEndpoint", "name"));
                final String path = el.getString("[@path]", "");

                if (restApiEndpoints.containsKey(path))
                    throw new Exception(invalidAttributeValueEx("restApiEndpoint", "path"));
                restApiEndpoints.put(path, new RestApiEndpoint(buildJavaClassName(javaPackage, name), path));
            }
        }

        if (!svcEl.configurationsAt("restApis").isEmpty()) {
            List<HierarchicalConfiguration> restApiEls = svcEl.configurationsAt("restApis.restApi");
            for (HierarchicalConfiguration el : restApiEls) {
                final String oasFile = el.getString("[@oasFile]");
                if (StringUtils.isBlank(oasFile))
                    throw new Exception(invalidAttributeValueEx("restApis.restApi", "oasFile"));
                final String context = oasFileToContext(oasFile);
                final String implPackage = el.getString("[@package]", "." + JAVAPKG_REST + "." + context);

                if (restApis.containsKey(oasFile))
                    throw new Exception(invalidAttributeValueEx("restApis.restApi", "oasFile"));
                //String oasFilePath = LangUtils.packageToPath(buildJavaPackage(javaPackage, "." + JAVAPKG_REST)) + "/" + oasFile;
                String oasFilePath = LangUtils.packageToPath(javaPackage) + "/" + oasFile;
                restApis.put(oasFile,
                        new RestApi(oasFilePath, context, buildJavaPackage(javaPackage, implPackage)));
            }
        }

        if (!svcEl.configurationsAt("permissions").isEmpty()) {
            List<HierarchicalConfiguration> elPerms = svcEl.configurationsAt("permissions.permission");
            for (HierarchicalConfiguration elPerm : elPerms) {
                if (elPerm.containsKey("[@group]")) {
                    String groupName = elPerm.getString("[@group]");
                    if (StringUtils.isEmpty(groupName))
                        throw new Exception("Permission must have a valid uppercase group name");

                    if (elPerm.containsKey("[@actions]")) {
                        String[] actions = StringUtils.split(elPerm.getString("[@actions]"), ",");
                        if (actions.length == 0)
                            throw new Exception("Resource must declare at least 1 action");
                        permissions.add(new ServicePermission(groupName, actions));
                    } else {
                        throw new Exception("Permission must declare actions supported on group");
                    }
                }
            }

            List<HierarchicalConfiguration> elShPerms = svcEl.configurationsAt("permissions.sharePermission");
            for (HierarchicalConfiguration elShPerm : elShPerms) {
                if (elShPerm.containsKey("[@group]")) {
                    String groupName = elShPerm.getString("[@group]");
                    if (StringUtils.isEmpty(groupName))
                        throw new Exception("Permission must have a valid uppercase group name");
                    permissions.add(new ServiceSharePermission(groupName));
                }
            }
        }

        if (!svcEl.configurationsAt("portlets").isEmpty()) {
            List<HierarchicalConfiguration> elPortlets = svcEl.configurationsAt("portlets.portlet");
            for (HierarchicalConfiguration el : elPortlets) {
                if (el.containsKey("[@jsClassName]")) {
                    final String jsClassName = el.getString("[@jsClassName]");
                    if (StringUtils.isBlank(jsClassName))
                        throw new Exception("Invalid value for attribute [portlet->jsClassName]");
                    portlets.add(new Portlet(LangUtils.buildClassName(jsPackage, jsClassName)));
                }
            }
        }
    }

    private String oasFileToContext(String oasFile) {
        return StringUtils.removeStartIgnoreCase(FilenameUtils.getBaseName(oasFile), "openapi-").toLowerCase();
    }

    /**
     * Gets specified service ID.
     * @return The value.
     */
    public String getId() {
        return id;
    }

    /**
     * Gets specified service XID (short ID).
     * @return The value.
     */
    public String getXId() {
        return xid;
    }

    /**
     * Gets the server-side package name.
     * (eg. com.sonicle.webtop.core)
     * @return The value.
     */
    public String getPackageName() {
        return javaPackage;
    }

    /**
     * Converts the package name into its path representation.
     * (eg. com.sonicle.webtop.mail -> com/sonicle/webtop/mail)
     * @return The value.
     */
    public String getJarPath() {
        return StringUtils.lowerCase(StringUtils.replace(getPackageName(), ".", "/"));
    }

    /**
     * Gets the client-side package name.
     * (eg. Sonicle.webtop.mail)
     * @return The value.
     */
    public String getJsPackageName() {
        return jsPackage;
    }

    /**
     * Converts the js package name into its path representation.
     * (eg. Sonicle.webtop.mail -> sonicle/webtop/mail)
     * @return The value.
     */
    public String getJsPath() {
        return StringUtils.lowerCase(StringUtils.replace(getJsPackageName(), ".", "/"));
    }

    /**
     * Returns current service version.
     * @return The service version.
     */
    public ServiceVersion getVersion() {
        return version;
    }

    /**
     * Returns service previous version (before the current one).
     * @return The service version.
     */
    public ServiceVersion getOldVersion() {
        return oldVersion;
    }

    /**
     * Sets service previous version.
     * @param value 
     */
    public void setOldVersion(ServiceVersion value) {
        oldVersion = value;
    }

    /**
     * Gets the specified build date.
     * @return The build date (as declared in manifest)
     */
    public String getBuildDate() {
        return buildDate;
    }

    /**
     * Gets the specified build type.
     * @return The build type (as declared in manifest)
     */
    public String getBuildType() {
        return buildType;
    }

    /**
     * Gets the class name of server-side Controller implementation.
     * (eg. com.sonicle.webtop.core.CoreController)
     * @return The class name.
     */
    public String getControllerClassName() {
        return controllerClassName;
    }

    /**
     * Gets the class name of server-side Manager implementation.
     * (eg. com.sonicle.webtop.core.CoreManager)
     * @return The class name.
     */
    public String getManagerClassName() {
        return managerClassName;
    }

    public RestApiEndpoint getApiEndpoint(String path) {
        return restApiEndpoints.get(path);
    }

    public Collection<RestApiEndpoint> getApiEndpoints() {
        return restApiEndpoints.values();
    }

    public Collection<RestApi> getRestApis() {
        return restApis.values();
    }

    /**
     * Gets the class name of server-side private (authenticated) service implementation.
     * (eg. com.sonicle.webtop.core.CoreService)
     * @return The class name.
     */
    public String getPrivateServiceClassName() {
        return privateServiceClassName;
    }

    public String getUserOptionsServiceClassName() {
        return userOptionsServiceClassName;
    }

    /**
     * Gets the class name of server-side public (not authenticated) service implementation.
     * (eg. com.sonicle.webtop.core.CorePublicService)
     * @return The class name.
     */
    public String getPublicServiceClassName() {
        return publicServiceClassName;
    }

    /**
     * Gets the class name of server-side job service implementation.
     * (eg. com.sonicle.webtop.core.CoreJobService)
     * @return The class name.
     */
    public String getJobServiceClassName() {
        return jobServiceClassName;
    }

    /**
     * Gets the class name of client-side private service implementation.
     * (eg. Sonicle.webtop.mail.MailService)
     * @param full True to include js package.
     * @return The class name.
     */
    public String getPrivateServiceJsClassName(boolean full) {
        return (full) ? LangUtils.buildClassName(jsPackage, privateServiceJsClassName) : privateServiceJsClassName;
    }

    public String getPrivateServiceVarsModelJsClassName(boolean full) {
        return (full) ? LangUtils.buildClassName(jsPackage, privateServiceVarsModelJsClassName)
                : privateServiceVarsModelJsClassName;
    }

    /**
     * Gets the class name of client-side public service implementation.
     * (eg. Sonicle.webtop.mail.MailService)
     * @param full True to include js package.
     * @return The class name.
     */
    public String getPublicServiceJsClassName(boolean full) {
        return (full) ? LangUtils.buildClassName(jsPackage, publicServiceJsClassName) : publicServiceJsClassName;
    }

    public String getPublicServiceVarsModelJsClassName(boolean full) {
        return (full) ? LangUtils.buildClassName(jsPackage, publicServiceVarsModelJsClassName)
                : publicServiceVarsModelJsClassName;
    }

    public String getUserOptionsViewJsClassName(boolean full) {
        return (full) ? LangUtils.buildClassName(jsPackage, userOptionsViewJsClassName)
                : userOptionsViewJsClassName;
    }

    public String getUserOptionsModelJsClassName(boolean full) {
        return (full) ? LangUtils.buildClassName(jsPackage, userOptionsModelJsClassName)
                : userOptionsModelJsClassName;
    }

    /**
     * Gets the class name of client-side locale object that applies localized strings.
     * @param locale The required locale.
     * @param full True to include js package.
     * @return The class name.
     */
    public String getLocaleJsClassName(Locale locale, boolean full) {
        String cn = "Locale_" + locale.getLanguage();
        return (full) ? LangUtils.buildClassName(jsPackage, cn) : cn;
    }

    /**
     * Returns the client-side URL path to reach service package base folder.
     * Eg. "resources/com.sonicle.webtop.mail/1.0.0"
     * @return 
     */
    public String getPackageBaseUrl() {
        return "resources/" + getId() + "/" + getVersion().toString();
    }

    /**
     * Returns the client-side URL path to reach service package sources folder.
     * Eg. "resources/com.sonicle.webtop.mail/1.0.0/src"
     * @return 
     */
    public String getPackageSrcUrl() {
        return getPackageBaseUrl() + "/src";
    }

    /**
     * Returns the client-side URL path to reach service package LAF folder.
     * Eg. "resources/com.sonicle.webtop.mail/1.0.0/laf"
     * @param lookAndFeel The look&feel name.
     * @return 
     */
    public String getPackageLookAndFeelUrl(String lookAndFeel) {
        return getPackageBaseUrl() + "/laf/" + lookAndFeel;
    }

    public String getBundleJsFileName() {
        return getId() + ".js";
    }

    public String getPrivateServiceJsFileName() {
        return getPrivateServiceJsClassName(false) + ".js";
    }

    public String getPublicServiceJsFileName() {
        return getPublicServiceJsClassName(false) + ".js";
    }

    public String getLocaleJsFileName(Locale locale) {
        return getLocaleJsClassName(locale, false) + ".js";
    }

    public String getJsBaseUrl(boolean devMode) {
        String base = getPackageBaseUrl();
        if (devMode)
            base += "/src";
        return base;
    }

    public String getCompany() {
        return company;
    }

    public String getCompanyEmail() {
        return companyEmail;
    }

    public String getCompanyWebSite() {
        return companyWebSite;
    }

    public String getSupportEmail() {
        return supportEmail;
    }

    public ArrayList<ServicePermission> getDeclaredPermissions() {
        return permissions;
    }

    public ArrayList<Portlet> getPortlets() {
        return portlets;
    }

    private String buildJavaClassName(String javaPackage, String className) {
        if (StringUtils.startsWith(className, ".")) {
            return LangUtils.buildClassName(javaPackage, className);
        } else {
            return className;
        }
    }

    private String buildJavaPackage(String javaBasePackage, String javaPackage) {
        if (StringUtils.startsWith(javaPackage, ".")) {
            return LangUtils.joinClassPackages(javaBasePackage, javaPackage);
        } else {
            return javaPackage;
        }
    }

    private String invalidValueEx(String elName) {
        return "Invalid value for element '" + elName + "'";
    }

    private String invalidAttributeValueEx(String elName, String attName) {
        return "Invalid value for element '" + elName + "@" + attName + "'";
    }

    private String invalidCardinalityEx(String elName, String expCardinality) {
        return "Invalid cardinality for element '" + elName + "', expected " + expCardinality;
    }

    public static class RestApiEndpoint {
        public final String className;
        public final String path;

        public RestApiEndpoint(String className, String path) {
            this.className = className;
            this.path = path;
        }
    }

    public static class RestApi {
        public final String oasFilePath;
        public final String context;
        public final String implPackage;

        public RestApi(String oasFilePath, String context, String implPackage) {
            this.oasFilePath = oasFilePath;
            this.context = context;
            this.implPackage = implPackage;
        }
    }

    public static class Portlet {
        public final String jsClassName;

        public Portlet(String jsClassName) {
            this.jsClassName = jsClassName;
        }
    }
}