/** * Copyright (C) 2009 eXo Platform SAS. * * This 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 software 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. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.application.registry.impl; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; import org.chromattic.api.ChromatticSession; import org.exoplatform.application.gadget.Gadget; import org.exoplatform.application.gadget.GadgetRegistryService; import org.exoplatform.application.registry.Application; import org.exoplatform.application.registry.ApplicationCategoriesPlugins; import org.exoplatform.application.registry.ApplicationCategory; import org.exoplatform.application.registry.ApplicationRegistryService; import org.exoplatform.commons.chromattic.ChromatticLifeCycle; import org.exoplatform.commons.chromattic.ChromatticManager; import org.exoplatform.container.ExoContainer; import org.exoplatform.container.ExoContainerContext; import org.exoplatform.container.component.ComponentPlugin; import org.exoplatform.container.component.RequestLifeCycle; import org.exoplatform.services.security.MembershipEntry; import org.exoplatform.portal.config.UserACL; import org.exoplatform.portal.config.model.ApplicationType; import org.exoplatform.portal.pom.config.POMSessionManager; import org.exoplatform.portal.pom.spi.portlet.Portlet; import org.exoplatform.portal.pom.spi.wsrp.WSRP; import org.gatein.common.i18n.LocalizedString; import org.gatein.common.logging.Logger; import org.gatein.common.logging.LoggerFactory; import org.gatein.mop.api.content.ContentType; import org.gatein.mop.api.content.Customization; import org.gatein.pc.api.PortletInvoker; import org.gatein.pc.api.info.MetaInfo; import org.gatein.pc.api.info.PortletInfo; import org.picocontainer.Startable; /** * The fundamental reason that motives to use tasks is because of the JMX access that does not setup a context and therefore the * task either reuse the existing context setup by the portal or create a temporary context when accessed by JMX. * * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> * @version $Revision$ */ public class ApplicationRegistryServiceImpl implements ApplicationRegistryService, Startable { /** . */ private static final String INTERNAL_PORTLET_TAG = "gatein_internal"; /** . */ private static final String REMOTE_CATEGORY_NAME = "remote"; /** . */ private List<ApplicationCategoriesPlugins> plugins; /** . */ private final ChromatticManager manager; /** . */ private final ChromatticLifeCycle lifeCycle; /** . */ private final Logger log = LoggerFactory.getLogger(ApplicationRegistryServiceImpl.class); /** . */ final POMSessionManager mopManager; /** Should match WSRPPortletInfo.PRODUCER_NAME_META_INFO_KEY */ private static final String PRODUCER_NAME_META_INFO_KEY = "producer-name"; /* * MIN and MAX values similar to StringLengthValidator. * * @see org.exoplatform.applicationregistry.webui.component.UIApplicationForm */ private static final int MIN = 3; private static final int MAX = 30; public static final String PRODUCER_CATEGORY_NAME_SUFFIX = " Producer"; private UserACL acl; private String anyOfAdminGroup; public ApplicationRegistryServiceImpl(ChromatticManager manager, POMSessionManager mopManager, UserACL userACL) { ApplicationRegistryChromatticLifeCycle lifeCycle = (ApplicationRegistryChromatticLifeCycle) manager.getLifeCycle("app"); lifeCycle.registry = this; // this.manager = manager; this.lifeCycle = lifeCycle; this.mopManager = mopManager; this.acl = userACL; this.anyOfAdminGroup = new MembershipEntry(acl.getAdminGroups()).toString(); } public ContentRegistry getContentRegistry() { ChromatticSession session = lifeCycle.getChromattic().openSession(); ContentRegistry registry = session.findByPath(ContentRegistry.class, "app:applications"); if (registry == null) { registry = session.insert(ContentRegistry.class, "app:applications"); } return registry; } public void initListener(ComponentPlugin com) { if (com instanceof ApplicationCategoriesPlugins) { if (plugins == null) { plugins = new ArrayList<ApplicationCategoriesPlugins>(); } plugins.add((ApplicationCategoriesPlugins) com); } } public List<ApplicationCategory> getApplicationCategories(final Comparator<ApplicationCategory> sortComparator, String accessUser, final ApplicationType<?>... appTypes) { final List<ApplicationCategory> categories = new ArrayList<ApplicationCategory>(); // ContentRegistry registry = getContentRegistry(); // for (CategoryDefinition categoryDef : registry.getCategoryList()) { ApplicationCategory category = load(categoryDef, appTypes); categories.add(category); } // if (sortComparator != null) { Collections.sort(categories, sortComparator); } // return categories; } public List<ApplicationCategory> getApplicationCategories(String accessUser, ApplicationType<?>... appTypes) throws Exception { return getApplicationCategories(null, accessUser, appTypes); } public List<ApplicationCategory> getApplicationCategories() throws Exception { return getApplicationCategories(null); } public List<ApplicationCategory> getApplicationCategories(Comparator<ApplicationCategory> sortComparator) throws Exception { return getApplicationCategories(sortComparator, null); } public ApplicationCategory getApplicationCategory(final String name) { ContentRegistry registry = getContentRegistry(); // CategoryDefinition categoryDef = registry.getCategory(name); if (categoryDef != null) { ApplicationCategory applicationCategory = load(categoryDef); return applicationCategory; } // return null; } public void save(final ApplicationCategory category) { ContentRegistry registry = getContentRegistry(); // String categoryName = category.getName(); // CategoryDefinition categoryDef = registry.getCategory(categoryName); if (categoryDef == null) { categoryDef = registry.createCategory(categoryName); } // categoryDef.setDisplayName(category.getDisplayName()); categoryDef.setCreationDate(category.getCreatedDate()); categoryDef.setLastModificationDate(category.getModifiedDate()); categoryDef.setDescription(category.getDescription()); categoryDef.setAccessPermissions(category.getAccessPermissions()); } public void remove(final ApplicationCategory category) { ContentRegistry registry = getContentRegistry(); registry.getCategoryMap().remove(category.getName()); } public List<Application> getApplications(ApplicationCategory category, ApplicationType<?>... appTypes) throws Exception { return getApplications(category, null, appTypes); } public List<Application> getApplications(final ApplicationCategory category, final Comparator<Application> sortComparator, final ApplicationType<?>... appTypes) { ContentRegistry registry = getContentRegistry(); // CategoryDefinition categoryDef = registry.getCategory(category.getName()); List<Application> applications = load(categoryDef, appTypes).getApplications(); // if (sortComparator != null) { Collections.sort(applications, sortComparator); } // return applications; } public List<Application> getAllApplications() throws Exception { List<Application> applications = new ArrayList<Application>(); List<ApplicationCategory> categories = getApplicationCategories(); for (ApplicationCategory category : categories) { applications.addAll(getApplications(category)); } return applications; } public Application getApplication(String id) throws Exception { String[] fragments = id.split("/"); if (fragments.length < 2) { throw new Exception("Invalid Application Id: [" + id + "]"); } String category = fragments[0]; String applicationName = fragments[1]; // If the application name contained a beginning slash (which can happen with WSRP), we need to hack around the // hardcoding of portlet id expectations >_< if (fragments.length == 3 && applicationName.length() == 0) { applicationName = "/" + fragments[2]; } return getApplication(category, applicationName); } public Application getApplication(final String category, final String name) { ContentRegistry registry = getContentRegistry(); // CategoryDefinition categoryDef = registry.getCategory(category); if (categoryDef != null) { ContentDefinition contentDef = categoryDef.getContentMap().get(name); if (contentDef != null) { return load(contentDef); } } // return null; } public void save(final ApplicationCategory category, final Application application) { ContentRegistry registry = getContentRegistry(); // String categoryName = category.getName(); CategoryDefinition categoryDef = registry.getCategory(categoryName); if (categoryDef == null) { categoryDef = registry.createCategory(categoryName); save(category, categoryDef); } // ContentDefinition contentDef = null; CategoryDefinition applicationCategoryDef = registry.getCategory(application.getCategoryName()); String applicationName = application.getApplicationName(); if (applicationCategoryDef != null) { contentDef = applicationCategoryDef.getContentMap().get(applicationName); } if (contentDef == null) { String contentId = application.getContentId(); ContentType<?> contentType = application.getType().getContentType(); String definitionName = application.getApplicationName(); contentDef = categoryDef.createContent(definitionName, contentType, contentId); } else { // A JCR move actually categoryDef.getContentList().add(contentDef); } // Update state save(application, contentDef); } public void update(final Application application) { ContentRegistry registry = getContentRegistry(); // String categoryName = application.getCategoryName(); CategoryDefinition categoryDef = registry.getCategory(categoryName); if (categoryDef == null) { throw new IllegalStateException(); } // ContentDefinition contentDef = categoryDef.getContentMap().get(application.getApplicationName()); if (contentDef == null) { throw new IllegalStateException(); } // Update state save(application, contentDef); } public void remove(final Application app) { if (app == null) { throw new NullPointerException(); } // ContentRegistry registry = getContentRegistry(); // String categoryName = app.getCategoryName(); CategoryDefinition categoryDef = registry.getCategory(categoryName); // if (categoryDef != null) { String contentName = app.getApplicationName(); categoryDef.getContentMap().remove(contentName); } } public void importExoGadgets() throws Exception { ContentRegistry registry = getContentRegistry(); // ExoContainer container = ExoContainerContext.getCurrentContainer(); GadgetRegistryService gadgetService = (GadgetRegistryService) container .getComponentInstanceOfType(GadgetRegistryService.class); List<Gadget> eXoGadgets = gadgetService.getAllGadgets(); // if (eXoGadgets != null) { ArrayList<String> permissions = new ArrayList<String>(); permissions.add(anyOfAdminGroup); String categoryName = "Gadgets"; // CategoryDefinition category = registry.getCategory(categoryName); if (category == null) { category = registry.createCategory(categoryName); category.setDisplayName(categoryName); category.setDescription(categoryName); category.setAccessPermissions(permissions); } // for (Gadget ele : eXoGadgets) { ContentDefinition app = category.getContentMap().get(ele.getName()); if (app == null) { app = category.createContent(ele.getName(), org.exoplatform.portal.pom.spi.gadget.Gadget.CONTENT_TYPE, ele.getName()); app.setDisplayName(ele.getTitle()); app.setDescription(ele.getDescription()); app.setAccessPermissions(permissions); } } } } public void importAllPortlets() throws Exception { ContentRegistry registry = getContentRegistry(); // log.info("About to import portlets in application registry"); // ExoContainer manager = ExoContainerContext.getCurrentContainer(); PortletInvoker portletInvoker = (PortletInvoker) manager.getComponentInstance(PortletInvoker.class); Set<org.gatein.pc.api.Portlet> portlets = portletInvoker.getPortlets(); // portlet: for (org.gatein.pc.api.Portlet portlet : portlets) { PortletInfo info = portlet.getInfo(); String portletApplicationName = info.getApplicationName(); String portletName = portlet.getContext().getId(); // Need to sanitize portlet and application names in case they contain characters that would // cause an improper Application name portletApplicationName = sanitizePortletApplicationName(portletApplicationName); portletName = sanitizePortletName(portletName); MetaInfo metaInfo = portlet.getInfo().getMeta(); LocalizedString keywordsLS = metaInfo.getMetaValue(MetaInfo.KEYWORDS); // Set<String> categoryNames = new HashSet<String>(); // Process keywords if (keywordsLS != null) { String keywords = keywordsLS.getDefaultString(); if (keywords != null && keywords.length() != 0) { for (String categoryName : keywords.split(",")) { // Trim name categoryName = categoryName.trim(); if (INTERNAL_PORTLET_TAG.equalsIgnoreCase(categoryName)) { log.debug("Skipping portlet (" + portletApplicationName + "," + portletName + ") + tagged as internal"); continue portlet; } else { categoryNames.add(categoryName); } } } } ArrayList<String> permissions = new ArrayList<String>(); permissions.add(anyOfAdminGroup); // If no keywords, use the portlet application name if (categoryNames.isEmpty()) { categoryNames.add(portletApplicationName.trim()); } // Additionally categorise the portlet as remote boolean remote = portlet.isRemote(); if (remote) { categoryNames.add(REMOTE_CATEGORY_NAME); // add producer name to categories for easier finding of portlets for GTNPORTAL-823 LocalizedString producerNameLS = metaInfo.getMetaValue(PRODUCER_NAME_META_INFO_KEY); if (producerNameLS != null) { categoryNames.add(producerNameLS.getDefaultString() + PRODUCER_CATEGORY_NAME_SUFFIX); } } // log.info("Importing portlet (" + portletApplicationName + "," + portletName + ") in categories " + categoryNames); // Process category names for (String categoryName : categoryNames) { categoryName = sanitizeCategoryName(categoryName); CategoryDefinition category = registry.getCategory(categoryName); // if (category == null) { category = registry.createCategory(categoryName); category.setDisplayName(categoryName); category.setAccessPermissions(permissions); } // ContentDefinition app = category.getContentMap().get(portletName); if (app == null) { LocalizedString descriptionLS = metaInfo.getMetaValue(MetaInfo.DESCRIPTION); LocalizedString displayNameLS = metaInfo.getMetaValue(MetaInfo.DISPLAY_NAME); String displayName = getLocalizedStringValue(displayNameLS, portletName); ContentType<?> contentType; String contentId; if (remote) { contentType = WSRP.CONTENT_TYPE; contentId = portlet.getContext().getId(); displayName += REMOTE_DISPLAY_NAME_SUFFIX; // add remote to display name to make it more obvious that // the portlet is remote } else { contentType = Portlet.CONTENT_TYPE; contentId = info.getApplicationName() + "/" + info.getName(); } // Check if the portlet has already existed in this category List<Application> applications = load(category).getApplications(); boolean isExist = false; for (Application application : applications) { if (application.getContentId().equals(contentId)) { isExist = true; break; } } if (!isExist) { app = category.createContent(uniqueDefinitionName(portletName, contentId), contentType, contentId); app.setDisplayName(displayName); app.setDescription(getLocalizedStringValue(descriptionLS, portletName)); app.setAccessPermissions(permissions); } } } } } private String uniqueDefinitionName(String portletName, String contentId) { String unique = portletName.substring(0,Math.min(portletName.length(), 20)) + String.valueOf(contentId.hashCode()); String sanitized = sanitizePortletName(unique); return sanitized; } private String sanitizePortletApplicationName(String portletApplicationName) { return portletApplicationName.replace('/', '_'); } private String sanitizePortletName(String portletName) { String sanitizedPortletName = portletName.replace('/', '_'); /* * PortletName should validate similar to UIApplicationForm for Application Name * as this is read-only value in Application Registry. * * @see org.exoplatform.applicationregistry.webui.component.UIApplicationForm */ int sizePortletName = sanitizedPortletName.length(); if (sizePortletName >= MAX) { sanitizedPortletName = sanitizedPortletName.substring(0, MAX - 1); } else if (sizePortletName <= MIN) { sanitizedPortletName += "Portlet"; } return sanitizedPortletName; } private String sanitizeCategoryName(String categoryName) { /* * CategoryName should validate similar to UIApplicationForm for Application Name * as this is read-only value in Application Registry. * * @see org.exoplatform.applicationregistry.webui.component.UIApplicationForm */ int sizeCategoryName = categoryName.length(); if (sizeCategoryName >= MAX) { categoryName = categoryName.substring(0, MAX - 1); } else if (sizeCategoryName <= MIN) { categoryName += "Category"; } return categoryName; } private boolean isApplicationType(Application app, ApplicationType<?>... appTypes) { if (appTypes == null || appTypes.length == 0) { return true; } for (ApplicationType<?> appType : appTypes) { if (appType.equals(app.getType())) { return true; } } return false; } private void save(ApplicationCategory category, CategoryDefinition categoryDef) { categoryDef.setDisplayName(category.getDisplayName()); categoryDef.setDescription(category.getDescription()); categoryDef.setAccessPermissions(category.getAccessPermissions()); categoryDef.setCreationDate(category.getCreatedDate()); categoryDef.setLastModificationDate(category.getModifiedDate()); } private ApplicationCategory load(CategoryDefinition categoryDef, ApplicationType<?>... appTypes) { ApplicationCategory category = new ApplicationCategory(); // category.setName(categoryDef.getName()); category.setDisplayName(categoryDef.getDisplayName()); category.setDescription(categoryDef.getDescription()); category.setAccessPermissions(new ArrayList<String>(categoryDef.getAccessPermissions())); category.setCreatedDate(categoryDef.getCreationDate()); category.setModifiedDate(categoryDef.getLastModificationDate()); // for (ContentDefinition contentDef : categoryDef.getContentList()) { Application application = load(contentDef); if (isApplicationType(application, appTypes)) { category.getApplications().add(application); } } // return category; } private void save(Application application, ContentDefinition contentDef) { contentDef.setDisplayName(application.getDisplayName()); contentDef.setDescription(application.getDescription()); contentDef.setAccessPermissions(application.getAccessPermissions()); contentDef.setCreationDate(application.getCreatedDate()); contentDef.setLastModificationDate(application.getModifiedDate()); } private Application load(ContentDefinition contentDef) { Customization customization = contentDef.getCustomization(); // ContentType<?> contentType = customization.getType(); ApplicationType<?> applicationType = ApplicationType.getType(contentType); // Application application = new Application(); /** * Apps ID has a "/" character that is rendered into id markup. * We need to workaround it to be W3C compliant. */ application.setId(contentDef.getCategory().getName() + "_slash_" + contentDef.getName()); application.setCategoryName(contentDef.getCategory().getName()); application.setType(applicationType); application.setApplicationName(contentDef.getName()); application.setIconURL(getApplicationIconURL(contentDef)); application.setDisplayName(contentDef.getDisplayName()); application.setDescription(contentDef.getDescription()); application.setAccessPermissions(new ArrayList<String>(contentDef.getAccessPermissions())); application.setCreatedDate(contentDef.getCreationDate()); application.setModifiedDate(contentDef.getLastModificationDate()); application.setStorageId(customization.getId()); application.setContentId(customization.getContentId()); return application; } private String getLocalizedStringValue(LocalizedString localizedString, String portletName) { if (localizedString == null || localizedString.getDefaultString() == null) { return portletName; } else { return localizedString.getDefaultString(); } } private static String getApplicationIconURL(ContentDefinition contentDef) { Customization customization = contentDef.getCustomization(); if (customization != null) { ContentType type = customization.getType(); String contentId = customization.getContentId(); if (type == Portlet.CONTENT_TYPE) { String[] chunks = contentId.split("/"); if (chunks.length == 2) { return "/" + chunks[0] + "/skin/DefaultSkin/portletIcons/" + chunks[1] + ".png"; } } else if (type == WSRP.CONTENT_TYPE) { return "/eXoResources/skin/sharedImages/Icon80x80/DefaultPortlet.png"; } else if (type == org.exoplatform.portal.pom.spi.gadget.Gadget.CONTENT_TYPE) { return "/" + "eXoGadgets" + "/skin/DefaultSkin/portletIcons/" + contentId + ".png"; } } // return null; } public void start() { if (plugins != null) { RequestLifeCycle.begin(manager); boolean save = false; try { if (this.getApplicationCategories().size() < 1) { for (ApplicationCategoriesPlugins plugin : plugins) { plugin.run(); } } save = true; } catch (Exception e) { log.error(e.getMessage(), e); } finally { // lifeCycle.closeContext(context, true); manager.getSynchronization().setSaveOnClose(save); RequestLifeCycle.end(); } } } public void stop() { } }