/* * Copyright (C) 2005-2007 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.web.bean.wcm; import java.text.MessageFormat; import java.util.Map; import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.faces.context.FacesContext; import org.alfresco.config.ConfigElement; import org.alfresco.config.ConfigService; import org.alfresco.config.JNDIConstants; import org.alfresco.mbeans.VirtServerRegistry; import org.alfresco.repo.avm.AVMNodeConverter; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.sandbox.SandboxConstants; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.avm.AVMNotFoundException; import org.alfresco.service.cmr.avm.AVMService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.namespace.QName; import org.alfresco.util.VirtServerUtils; import org.alfresco.web.app.Application; import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.config.ClientConfigElement; /** * Helper methods and constants related to AVM directories, paths and store name manipulation. * * @author Ariel Backenroth * @author Kevin Roast */ public final class AVMUtil { ///////////////////////////////////////////////////////////////////////////// public static enum PathRelation { SANDBOX_RELATIVE { @Override protected Pattern pattern() { return AVMUtil.SANDBOX_RELATIVE_PATH_PATTERN; } }, WEBAPP_RELATIVE { @Override protected Pattern pattern() { return AVMUtil.WEBAPP_RELATIVE_PATH_PATTERN; } }; protected abstract Pattern pattern(); } ///////////////////////////////////////////////////////////////////////////// /** * Private constructor */ private AVMUtil() { } /** * Extracts the store name from the avmpath * * @param avmPath an absolute avm path * * @return the store name */ public static String getStoreName(final String avmPath) { final int i = avmPath.indexOf(':'); if (i == -1) { throw new IllegalArgumentException("path " + avmPath + " does not contain a store"); } return avmPath.substring(0, i); } /** * Indicates whether the store name describes a preview store. * * @param storeName the store name * * @return true if the store is a preview store, false otherwise. */ public static boolean isPreviewStore(final String storeName) { return storeName.endsWith(AVMUtil.STORE_SEPARATOR + AVMUtil.STORE_PREVIEW); } /** * Indicates whether the store name describes a workflow store. * * @param storeName the store name * * @return true if the store is a workflow store, false otherwise. */ public static boolean isWorkflowStore(String storeName) { if (AVMUtil.isPreviewStore(storeName)) { storeName = AVMUtil.getCorrespondingMainStoreName(storeName); } return storeName.indexOf(STORE_SEPARATOR + STORE_WORKFLOW) != -1; } /** * Indicates whether the store name describes a user store. * * @param storeName the store name * * @return true if the store is a user store, false otherwise. */ public static boolean isUserStore(String storeName) { if (AVMUtil.isPreviewStore(storeName)) { storeName = AVMUtil.getCorrespondingMainStoreName(storeName); } return storeName.indexOf(AVMUtil.STORE_SEPARATOR) != -1; } /** * Extracts the username from the store name. * * @param storeName the store name * * @return the username associated or null if this is a staging store. */ public static String getUserName(String storeName) { if (AVMUtil.isPreviewStore(storeName)) { storeName = AVMUtil.getCorrespondingMainStoreName(storeName); } final int index = storeName.indexOf(AVMUtil.STORE_SEPARATOR); return (index == -1 ? null : storeName.substring(index + AVMUtil.STORE_SEPARATOR.length())); } /** * Extracts the store id from the store name. * * @param storeName the store name. * * @return the store id. */ public static String getStoreId(final String storeName) { final int index = storeName.indexOf(AVMUtil.STORE_SEPARATOR); return (index == -1 ? storeName : storeName.substring(0, index)); } /** * Returns the corresponding main store name if this is a preview store name. * * @param storeName the preview store name. * * @return the corresponding main store name. * * @exception IllegalArgumentException if this is not a preview store name. */ public static String getCorrespondingMainStoreName(final String storeName) { if (!AVMUtil.isPreviewStore(storeName)) { throw new IllegalArgumentException("store " + storeName + " is not a preview store"); } return storeName.substring(0, (storeName.length() - (AVMUtil.STORE_SEPARATOR + AVMUtil.STORE_PREVIEW).length())); } /** * Returns the corresponding preview store name if this is a main store name. * * @param storeName the main store name. * * @return the corresponding preview store name. * * @exception IllegalArgumentException if this is not a main store name. */ public static String getCorrespondingPreviewStoreName(final String storeName) { if (AVMUtil.isPreviewStore(storeName)) { throw new IllegalArgumentException("store " + storeName + " is already a preview store"); } return storeName + AVMUtil.STORE_SEPARATOR + AVMUtil.STORE_PREVIEW; } /** * Returns the corresponding path in the main store name if this is a path in * a preview store. * * @param avmPath an avm path within the main store. * * @return the corresponding path within the preview store. * * @exception IllegalArgumentException if this is not a path within the preview store. */ public static String getCorrespondingPathInMainStore(final String avmPath) { String storeName = AVMUtil.getStoreName(avmPath); storeName = AVMUtil.getCorrespondingMainStoreName(storeName); return AVMUtil.getCorrespondingPath(avmPath, storeName); } /** * Returns the corresponding path in the preview store name if this is a path in * a main store. * * @param avmPath an avm path within the main store. * * @return the corresponding path within the preview store. * * @exception IllegalArgumentException if this is not a path within the preview store. */ public static String getCorrespondingPathInPreviewStore(final String avmPath) { String storeName = AVMUtil.getStoreName(avmPath); storeName = AVMUtil.getCorrespondingPreviewStoreName(storeName); return AVMUtil.getCorrespondingPath(avmPath, storeName); } /** * Returns the corresponding path in the store provided. * * @param avmPath an avm path * @param otherStore the other store to return the corresponding path for * * @return the corresponding path within the supplied store */ public static String getCorrespondingPath(final String avmPath, final String otherStore) { return (otherStore + ':' + AVMUtil.getStoreRelativePath(avmPath)); } /** * Returns the username to connect as on the remote machine being deployed to *
* This value is read from the <wcm> config section in * web-client-config-wcm.xml *
* * @return Username for remote machine */ public static String getRemoteDeploymentUsername() { String username = "admin"; ConfigElement deploymentConfig = getDeploymentConfig(); if (deploymentConfig != null) { ConfigElement elem = deploymentConfig.getChild("remote-username"); if (elem != null) { username = elem.getValue(); } } return username; } /** * Returns the password to use on the remote machine being deployed to ** This value is read from the <wcm> config section in * web-client-config-wcm.xml *
* * @return Password for remote machine */ public static String getRemoteDeploymentPassword() { String password = "admin"; ConfigElement deploymentConfig = getDeploymentConfig(); if (deploymentConfig != null) { ConfigElement elem = deploymentConfig.getChild("remote-password"); if (elem != null) { password = elem.getValue(); } } return password; } /** * Returns the number of seconds between each call back to the server to * obtain the latest status of an in progress deployment. ** This value is read from the <wcm> config section in * web-client-config-wcm.xml *
* * @return Number of seconds between each call to the server (in seconds). * The default is 2. */ public static int getRemoteDeploymentPollingFrequency() { int pollFreq = 2; ConfigElement deploymentConfig = getDeploymentConfig(); if (deploymentConfig != null) { ConfigElement elem = deploymentConfig.getChild("progress-polling-frequency"); if (elem != null) { try { int value = Integer.parseInt(elem.getValue()); if (value > 0) { pollFreq = value; } } catch (NumberFormatException nfe) { // do nothing, just use the default } } } return pollFreq; } /** * Returns the default RMI registry port to use when one is not supplied * for target Alfresco deployment servers. ** This value is read from the <wcm> config section in * web-client-config-wcm.xml *
* * @return The remote RMI registry port to use for deployments. * The default is 50500. */ public static int getRemoteRMIRegistryPort() { int rmiPort = 50500; ConfigElement deploymentConfig = getDeploymentConfig(); if (deploymentConfig != null) { ConfigElement elem = deploymentConfig.getChild("remote-rmi-port"); if (elem != null) { try { int value = Integer.parseInt(elem.getValue()); if (value > 0) { rmiPort = value; } } catch (NumberFormatException nfe) { // do nothing, just use the default } } } return rmiPort; } /** * Returns the default RMI port to use when one is not supplied * for target deployment receivers. ** This value is read from the <wcm> config section in * web-client-config-wcm.xml *
* * @return The deployment receiver RMI port to use for deployments. * The default is 44100. */ public static int getRemoteReceiverRMIPort() { int rmiPort = 44100; ConfigElement deploymentConfig = getDeploymentConfig(); if (deploymentConfig != null) { ConfigElement elem = deploymentConfig.getChild("receiver-rmi-port"); if (elem != null) { try { int value = Integer.parseInt(elem.getValue()); if (value > 0) { rmiPort = value; } } catch (NumberFormatException nfe) { // do nothing, just use the default } } } return rmiPort; } /** * Returns the delay (in seconds) to apply to the start of a deployment * operation (mainly for demo and testing purposes). ** This value is read from the <wcm> config section in * web-client-config-wcm.xml *
* * @return The delay to use at the start of a deployment. * The default is 30. */ public static int getRemoteDeploymentDelay() { int delay = 30; ConfigElement deploymentConfig = getDeploymentConfig(); if (deploymentConfig != null) { ConfigElement elem = deploymentConfig.getChild("delay"); if (elem != null) { try { int value = Integer.parseInt(elem.getValue()); if (value > 0) { delay = value; } } catch (NumberFormatException nfe) { // do nothing, just use the default } } } return delay; } /** * Returns the number of seconds between each call back to the server to * obtain the latest status of a link validation check. ** This value is read from the <wcm> config section in * web-client-config-wcm.xml *
* * @return Number of seconds between each call to the server (in seconds). * The default is 2. */ public static int getLinkValidationPollingFrequency() { int pollFreq = 2; ConfigElement linkMngmtConfig = getLinksManagementConfig(); if (linkMngmtConfig != null) { ConfigElement elem = linkMngmtConfig.getChild("progress-polling-frequency"); if (elem != null) { try { int value = Integer.parseInt(elem.getValue()); if (value > 0) { pollFreq = value; } } catch (NumberFormatException nfe) { // do nothing, just use the default } } } return pollFreq; } /** * Returns the main staging store name for the specified store id. * * @param storeId store id to build staging store name for * * @return main staging store name for the specified store id */ public static String buildStagingStoreName(final String storeId) { if (storeId == null || storeId.length() == 0) { throw new IllegalArgumentException("Store id is mandatory."); } return storeId; } /** * Returns the preview store name for the specified store id. * * @param storeId store id to build preview store name for * * @return preview store name for the specified store id */ public static String buildStagingPreviewStoreName(final String storeId) { return (AVMUtil.buildStagingStoreName(storeId) + AVMUtil.STORE_SEPARATOR + AVMUtil.STORE_PREVIEW); } /** * Returns the main store name for a specific username. * * @param storeId store id to build user store name for * @param username of the user to build store name for * * @return the main store for the specified user and store id */ public static String buildUserMainStoreName(final String storeId, final String username) { if (username == null || username.length() == 0) { throw new IllegalArgumentException("Username is mandatory."); } return (AVMUtil.buildStagingStoreName(storeId) + AVMUtil.STORE_SEPARATOR + username); } /** * Returns the preview store name for a specific username. * * @param storeId store id to build user preview store name for * @param username of the user to build preview store name for * * @return the preview store for the specified user and store id */ public static String buildUserPreviewStoreName(final String storeId, final String username) { return (AVMUtil.buildUserMainStoreName(storeId, username) + AVMUtil.STORE_SEPARATOR + AVMUtil.STORE_PREVIEW); } /** * Returns the store name for a specific workflow Id. * * @param storeId store id to build workflow store name for * @param workflowId of the user to build workflow store name for * * @return the store for the specified workflow and store ids */ public static String buildWorkflowMainStoreName(final String storeId, final String workflowId) { if (workflowId == null || workflowId.length() == 0) { throw new IllegalArgumentException("workflowId is mandatory."); } return (AVMUtil.buildStagingStoreName(storeId) + AVMUtil.STORE_SEPARATOR + workflowId); } /** * Returns the preview store name for a specific workflow Id. * * @param storeId store id to build preview workflow store name for * @param workflowId of the user to build preview workflow store name for * * @return the store for the specified preview workflow and store ids */ public static String buildWorkflowPreviewStoreName(final String storeId, final String workflowId) { return (AVMUtil.buildWorkflowMainStoreName(storeId, workflowId) + AVMUtil.STORE_SEPARATOR + AVMUtil.STORE_PREVIEW); } /** * Returns the root path for the specified store name * * @param storeName store to build root path for * * @return root path for the specified store name */ public static String buildStoreRootPath(final String storeName) { if (storeName == null || storeName.length() == 0) { throw new IllegalArgumentException("Store name is mandatory."); } return storeName + ":/" + JNDIConstants.DIR_DEFAULT_WWW; } /** * Returns the root path for the specified sandbox name * * @param storeName store to build root sandbox path for * * @return root sandbox path for the specified store name */ public static String buildSandboxRootPath(final String storeName) { return AVMUtil.buildStoreRootPath(storeName) + '/' + JNDIConstants.DIR_DEFAULT_APPBASE; } /** * Returns the root webapp path for the specified store and webapp name * * @param storeName store to build root webapp path for * @param webapp webapp folder name * * @return the root webapp path for the specified store and webapp name */ public static String buildStoreWebappPath(final String storeName, String webapp) { if (webapp == null || webapp.length() == 0) { throw new IllegalArgumentException("Webapp name is mandatory."); } return AVMUtil.buildSandboxRootPath(storeName) + '/' + webapp; } public static String buildStoreUrl(String store) { if (store == null || store.length() == 0) { throw new IllegalArgumentException("Store name is mandatory."); } if (store.indexOf(':') != -1) { store = store.substring(0, store.indexOf(':')); } ClientConfigElement config = Application.getClientConfig(FacesContext.getCurrentInstance()); return MessageFormat.format(JNDIConstants.PREVIEW_SANDBOX_URL, lookupStoreDNS(store), config.getWCMDomain(), config.getWCMPort()); } public static String buildWebappUrl(final String avmPath) { if (avmPath == null || avmPath.length() == 0) { throw new IllegalArgumentException("AVM path is mandatory."); } return AVMUtil.buildWebappUrl(AVMUtil.getStoreName(avmPath), AVMUtil.getWebapp(avmPath)); } public static String buildWebappUrl(final String store, final String webapp) { if (webapp == null || webapp.length() == 0) { throw new IllegalArgumentException("Webapp name is mandatory."); } return (webapp.equals(DIR_ROOT) ? buildStoreUrl(store) : buildStoreUrl(store) + '/' + webapp); } public static String buildAssetUrl(final String avmPath) { if (avmPath == null || avmPath.length() == 0) { throw new IllegalArgumentException("AVM path is mandatory."); } final String[] s = avmPath.split(":"); if (s.length != 2) { throw new IllegalArgumentException("expected exactly one ':' in " + avmPath); } return AVMUtil.buildAssetUrl(s[0], s[1]); } public static String buildAssetUrl(String store, String assetPath) { if (store == null || store.length() == 0) { throw new IllegalArgumentException("Store name is mandatory."); } if (assetPath == null || assetPath.length() == 0) { throw new IllegalArgumentException("Asset path is mandatory."); } ClientConfigElement config = Application.getClientConfig(FacesContext.getCurrentInstance()); return buildAssetUrl(assetPath, config.getWCMDomain(), config.getWCMPort(), lookupStoreDNS(store)); } public static String buildAssetUrl(String assetPath, String domain, String port, String dns) { if (domain == null || port == null || dns == null) { throw new IllegalArgumentException("Domain, port and dns name are mandatory."); } if (assetPath == null || assetPath.length() == 0) { throw new IllegalArgumentException("Asset path is mandatory."); } if (assetPath.startsWith('/' + JNDIConstants.DIR_DEFAULT_WWW + '/' + JNDIConstants.DIR_DEFAULT_APPBASE)) { assetPath = assetPath.substring(('/' + JNDIConstants.DIR_DEFAULT_WWW + '/' + JNDIConstants.DIR_DEFAULT_APPBASE).length()); } if (assetPath.startsWith('/' + DIR_ROOT)) { assetPath = assetPath.substring(('/' + DIR_ROOT).length()); } if (assetPath.length() == 0 || assetPath.charAt(0) != '/') { assetPath = '/' + assetPath; } return MessageFormat.format(JNDIConstants.PREVIEW_ASSET_URL, dns, domain, port, assetPath); } public static String lookupStoreDNS(String store) { if (store == null || store.length() == 0) { throw new IllegalArgumentException("Store name is mandatory."); } final ServiceRegistry serviceRegistry = Repository.getServiceRegistry(FacesContext.getCurrentInstance()); final AVMService avmService = serviceRegistry.getAVMService(); final Map