Java tutorial
/* * 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; } } }