Java tutorial
/** * ========================================================================================== * = JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION = * ========================================================================================== * * http://www.jahia.com * * Copyright (C) 2002-2017 Jahia Solutions Group SA. All rights reserved. * * THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES: * 1/GPL OR 2/JSEL * * 1/ GPL * ================================================================================== * * IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * 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 3 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, see <http://www.gnu.org/licenses/>. * * * 2/ JSEL - Commercial and Supported Versions of the program * =================================================================================== * * IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * Alternatively, commercial and supported versions of the program - also known as * Enterprise Distributions - must be used in accordance with the terms and conditions * contained in a separate written agreement between you and Jahia Solutions Group SA. * * If you are unsure which license is appropriate for your use, * please contact the sales department at sales@jahia.com. */ package org.jahia.services.render; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import javax.jcr.AccessDeniedException; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.servlet.http.HttpServletRequest; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; import org.apache.commons.lang.StringUtils; import org.jahia.bin.Render; import org.jahia.exceptions.JahiaBadRequestException; import org.jahia.exceptions.JahiaException; import org.jahia.registries.ServicesRegistry; import org.jahia.services.SpringContextSingleton; import org.jahia.services.content.JCRCallback; import org.jahia.services.content.JCRContentUtils; import org.jahia.services.content.JCRNodeWrapper; import org.jahia.services.content.JCRSessionFactory; import org.jahia.services.content.JCRSessionWrapper; import org.jahia.services.content.JCRTemplate; import org.jahia.services.content.decorator.JCRSiteNode; import org.jahia.services.seo.VanityUrl; import org.jahia.services.seo.jcr.VanityUrlManager; import org.jahia.services.seo.jcr.VanityUrlService; import org.jahia.services.usermanager.JahiaUserManagerService; import org.jahia.settings.SettingsBean; import org.jahia.utils.LanguageCodeConverters; import org.jahia.utils.Url; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.jahia.api.Constants.LIVE_WORKSPACE; /** * Class to resolve URLs and URL paths, so that the workspace / locale / node-path information is * returned. * * There are also convenience methods to directly return the Node or Resource where the URL points to. * * The method also considers vanity URL mappings and resolves them to the mapped nodes, so that * the URL resolver will return the info as if no mapping have been used. * * @author Benjamin Papez * */ public class URLResolver { private static final Locale DEFAULT_LOCALE = Locale.ENGLISH; private static final String DEFAULT_WORKSPACE = LIVE_WORKSPACE; private static final Pattern MACRO_URL_PATTERN = Pattern.compile("(.?)*.html##[a-zA-Z]*##$"); private static final String VANITY_URL_NODE_PATH_SEGMENT = "/" + VanityUrlManager.VANITYURLMAPPINGS_NODE + "/"; private static final Pattern crossSitesURLPattern = Pattern.compile("[a-z]{0,2}_?[A-Z]{0,2}/sites/.*"); private static Logger logger = LoggerFactory.getLogger(URLResolver.class); private static String[] servletsAllowingUrlMapping = new String[] { StringUtils.substringAfterLast(Render.getRenderServletPath(), "/") }; private String urlPathInfo = null; private String servletPart = ""; private String workspace; private Locale locale; private String path = ""; private String siteKey; private String siteKeyByServerName; private boolean mappable = false; private String redirectUrl; private String vanityUrl; private Date versionDate; private String versionLabel; private static final String CACHE_KEY_SEPARATOR = "___"; private SiteInfo siteInfo; public void setRenderContext(RenderContext renderContext) { this.renderContext = renderContext; } private RenderContext renderContext; private Map<String, JCRNodeWrapper> resolvedNodes = new ConcurrentHashMap<String, JCRNodeWrapper>(); private Ehcache nodePathCache; private Ehcache siteInfoCache; protected URLResolver(String urlPathInfo, String serverName, HttpServletRequest request, Ehcache nodePathCache, Ehcache siteInfoCache) { this(urlPathInfo, serverName, null, request, nodePathCache, siteInfoCache); } /** * Initializes an instance of this class. This constructor is mainly used when * resolving URLs of incoming requests. * * @param pathInfo the path info (usually obtained with @link javax.servlet.http.HttpServletRequest.getPathInfo()) * @param serverName the server name (usually obtained with @link javax.servlet.http.HttpServletRequest.getServerName()) * @param request the current HTTP servlet request object */ protected URLResolver(String pathInfo, String serverName, String workspace, HttpServletRequest request, Ehcache nodePathCache, Ehcache siteInfoCache) { super(); this.nodePathCache = nodePathCache; this.siteInfoCache = siteInfoCache; this.workspace = workspace; this.urlPathInfo = normalizeUrlPathInfo(pathInfo); if (!JahiaUserManagerService.isGuest(JCRSessionFactory.getInstance().getCurrentUser())) { Date date = getVersionDate(request); String versionLabel = getVersionLabel(request); setVersionDate(date); setVersionLabel(versionLabel); } if (urlPathInfo != null) { servletPart = StringUtils.substring(getUrlPathInfo(), 1, StringUtils.indexOf(getUrlPathInfo(), "/", 1)); path = StringUtils.substring(getUrlPathInfo(), servletPart.length() + 2, getUrlPathInfo().length()); } if (!resolveUrlMapping(serverName, request)) { init(); if (!Url.isLocalhost(serverName) && isMappable() && SettingsBean.getInstance().isPermanentMoveForVanityURL()) { try { if (siteKeyByServerName != null && siteKeyByServerName.equals(getNode().getResolveSite().getSiteKey())) { VanityUrl defaultVanityUrl = getVanityUrlService() .getVanityUrlForWorkspaceAndLocale(getNode(), this.workspace, locale, siteKey); if (defaultVanityUrl != null && defaultVanityUrl.isActive()) { redirect(request, defaultVanityUrl); } } } catch (PathNotFoundException e) { logger.debug("Path not found : " + urlPathInfo); } catch (AccessDeniedException e) { logger.debug("User has no access to the resource, so there will not be a redirection"); } catch (RepositoryException e) { logger.warn("Error when trying to check whether there is a vanity URL mapping", e); } } } } private void redirect(HttpServletRequest request, VanityUrl defaultVanityUrl) { if (request == null || StringUtils.isEmpty(request.getQueryString())) { setRedirectUrl(defaultVanityUrl.getUrl()); } else { setRedirectUrl(defaultVanityUrl.getUrl() + "?" + request.getQueryString()); } } private static String normalizeUrlPathInfo(String urlPathInfo) { if (urlPathInfo != null && urlPathInfo.length() > 1 && urlPathInfo.charAt(urlPathInfo.length() - 1) == '/') { urlPathInfo = urlPathInfo.substring(0, urlPathInfo.length() - 1); return urlPathInfo; } return urlPathInfo; } /** * Initializes an instance of this class. This constructor is mainly used when * trying to find mapping for URLs in outgoing requests. * * @param url URL in HTML links of outgoing requests * @param context The current request in order to obtain the context path */ protected URLResolver(String url, RenderContext context, Ehcache nodePathCache, Ehcache siteInfoCache) { this.nodePathCache = nodePathCache; this.siteInfoCache = siteInfoCache; renderContext = context; String contextPath = context.getRequest().getContextPath(); this.urlPathInfo = normalizeUrlPathInfo(StringUtils.substringAfter(url, !StringUtils.isEmpty(contextPath) ? contextPath + context.getServletPath() : context.getServletPath())); this.servletPart = StringUtils.substringAfterLast(context.getServletPath(), "/"); if (!StringUtils.isEmpty(urlPathInfo)) { path = getUrlPathInfo().substring(1); init(); } } private void init() { workspace = verifyWorkspace(StringUtils.substringBefore(path, "/")); path = StringUtils.substringAfter(path, "/"); locale = verifyLanguage(StringUtils.substringBefore(path, "/")); path = "/" + (locale != null ? StringUtils.substringAfter(path, "/") : path); // TODO: this is perhaps a temporary limitation as URL points to special templates, when // there are more than one dots - and the path needs to end with .html // and in some cases macro extension are added like the ##requestParameters## for languageswitcher String lastPart = StringUtils.substringAfterLast(path, "/"); int indexOfHTMLSuffix = lastPart.indexOf(".html"); if (isServletAllowingUrlMapping() && indexOfHTMLSuffix > 0 && (lastPart.endsWith(".html") || MACRO_URL_PATTERN.matcher(lastPart).matches())) { mappable = true; } } private Date getVersionDate(HttpServletRequest req) { // we assume here that the date has been passed as milliseconds. String msString = req.getParameter("v"); if (msString == null) { return null; } try { long msLong = Long.parseLong(msString); if (logger.isDebugEnabled()) { logger.debug("Display version of date : " + SimpleDateFormat .getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(new Date(msLong))); } return new Date(msLong); } catch (NumberFormatException nfe) { logger.warn("Invalid version date found in URL " + msString); return null; } } private String getVersionLabel(HttpServletRequest req) { return req.getParameter("l"); } private boolean isServletAllowingUrlMapping() { boolean isServletAllowingUrlMapping = false; for (String servletAllowingUrlMapping : servletsAllowingUrlMapping) { if (servletAllowingUrlMapping.equals(servletPart)) { isServletAllowingUrlMapping = true; break; } } return isServletAllowingUrlMapping; } protected boolean resolveUrlMapping(String serverName, HttpServletRequest request) { boolean mappingResolved = false; try { siteKeyByServerName = ServicesRegistry.getInstance().getJahiaSitesService() .getSitenameByServerName(serverName); } catch (JahiaException e) { logger.warn("Error finding site via servername: " + serverName, e); } if (getSiteKey() == null) { String siteKeyInPath = StringUtils.substringBetween(getPath(), "/sites/", "/"); if (!StringUtils.isEmpty(siteKeyInPath)) { setSiteKey(siteKeyInPath); } else if (!Url.isLocalhost(serverName)) { if (siteKeyByServerName != null) { setSiteKey(siteKeyByServerName); } } } if (isServletAllowingUrlMapping() && !Url.isLocalhost(serverName)) { String tempPath = null; try { String tempWorkspace = verifyWorkspace(StringUtils.substringBefore(getPath(), "/")); tempPath = StringUtils.substringAfter(getPath(), "/"); VanityUrl resolvedVanityUrl = null; if (logger.isDebugEnabled()) { logger.debug("Trying to resolve vanity url for tempPath = " + tempPath); } boolean doNotMatchesCrossSitesPattern = !crossSitesURLPattern.matcher(tempPath).matches(); if (!StringUtils.isEmpty(getSiteKey()) && doNotMatchesCrossSitesPattern) { List<VanityUrl> vanityUrls = getVanityUrlService().findExistingVanityUrls("/" + tempPath, getSiteKey(), tempWorkspace); for (VanityUrl vanityUrl : vanityUrls) { if (vanityUrl.isActive()) { resolvedVanityUrl = vanityUrl; break; } } } else if (doNotMatchesCrossSitesPattern) { List<VanityUrl> vanityUrls = getVanityUrlService().findExistingVanityUrls("/" + tempPath, StringUtils.EMPTY, tempWorkspace); for (VanityUrl vanityUrl : vanityUrls) { if (vanityUrl.isActive() && (StringUtils.isEmpty(getSiteKey()) || getSiteKey().equals(vanityUrl.getSite()))) { resolvedVanityUrl = vanityUrl; break; } } } if (resolvedVanityUrl != null) { workspace = tempWorkspace; locale = StringUtils.isEmpty(resolvedVanityUrl.getLanguage()) ? DEFAULT_LOCALE : LanguageCodeConverters.languageCodeToLocale(resolvedVanityUrl.getLanguage()); path = StringUtils.substringBefore(resolvedVanityUrl.getPath(), VANITY_URL_NODE_PATH_SEGMENT) + ".html"; setVanityUrl(resolvedVanityUrl.getUrl()); if (SettingsBean.getInstance().isPermanentMoveForVanityURL() && !resolvedVanityUrl.isDefaultMapping()) { VanityUrl defaultVanityUrl = getVanityUrlService() .getVanityUrlForWorkspaceAndLocale(getNode(), workspace, locale, siteKey); if (defaultVanityUrl != null && defaultVanityUrl.isActive() && !resolvedVanityUrl.equals(defaultVanityUrl)) { redirect(request, defaultVanityUrl); } } mappingResolved = true; } } catch (RepositoryException e) { logger.warn("Error when trying to resolve URL mapping: " + tempPath, e); } } return mappingResolved; } /** * Gets the pathInfo of the given URL (@link javax.servlet.http.HttpServletRequest.getPathInfo()) * @return the pathInfo of the given URL */ public String getUrlPathInfo() { return urlPathInfo; } public String getServletPart() { return servletPart; } /** * Gets the workspace of the request resolved by the URL * @return the workspace of the given URL */ public String getWorkspace() { return workspace; } /** * Gets the locale of the request resolved by the URL * @return the locale of the given URL */ public Locale getLocale() { Locale uiLocale = null; if (renderContext != null && renderContext.isForceUILocaleForJCRSession()) { uiLocale = renderContext.getUILocale(); } return uiLocale != null ? uiLocale : locale; } /** * Gets the content node path of the request resolved by the URL * @return the content node path of the given URL */ public String getPath() { return path; } /** * Creates a node from the path in the URL. * * @return The node, if found * @throws PathNotFoundException * if the resource cannot be resolved * @throws RepositoryException */ public JCRNodeWrapper getNode() throws RepositoryException { return resolveNode(getWorkspace(), getLocale(), getPath()); } /** * Creates a resource from the path in the URL. * <p/> * The path should looks like : [nodepath][.templatename].[templatetype] or [nodepath].[templatetype] * * Workspace, locale and path are taken from the given resolved URL. * * @return The resource, if found * @throws PathNotFoundException * if the resource cannot be resolved * @throws RepositoryException */ public Resource getResource() throws RepositoryException { return resolveResource(getWorkspace(), getLocale(), getPath()); } public Resource getResource(String path) throws RepositoryException { return resolveResource(getWorkspace(), getLocale(), path); } /** * Creates a node from the specified path. * <p/> * The path should looks like : [nodepath][.templatename].[templatetype] or [nodepath].[templatetype] * * @param workspace * The workspace where to get the node * @param locale * current locale * @param path * The path of the node, in the specified workspace * @return The node, if found * @throws PathNotFoundException * if the resource cannot be resolved * @throws RepositoryException */ protected JCRNodeWrapper resolveNode(final String workspace, final Locale locale, final String path) throws RepositoryException { if (logger.isDebugEnabled()) { logger.debug("Resolving node for workspace '" + workspace + "' locale '" + locale + "' and path '" + path + "'"); } final String cacheKey = getCacheKey(workspace, locale, path); if (resolvedNodes.containsKey(cacheKey)) { return resolvedNodes.get(cacheKey); } JCRNodeWrapper node = null; Element element = nodePathCache.get(cacheKey); String nodePath = null; if (element != null) { nodePath = (String) element.getObjectValue(); } element = siteInfoCache.get(cacheKey); if (element != null) { siteInfo = (SiteInfo) element.getObjectValue(); } if (nodePath == null || siteInfo == null) { nodePath = JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(null, workspace, locale, new JCRCallback<String>() { public String doInJCR(JCRSessionWrapper session) throws RepositoryException { String nodePath = JCRContentUtils.escapeNodePath( path.endsWith("/*") ? path.substring(0, path.lastIndexOf("/*")) : path); String siteName = StringUtils.substringBetween(nodePath, "/sites/", "/"); if (siteName != null && session.itemExists("/sites/" + siteName)) { siteInfo = new SiteInfo((JCRSiteNode) session.getNode("/sites/" + siteName)); if (siteInfo.isMixLanguagesActive() && siteInfo.getDefaultLanguage() != null) { session.setFallbackLocale(LanguageCodeConverters .getLocaleFromCode(siteInfo.getDefaultLanguage())); } } if (logger.isDebugEnabled()) { logger.debug(cacheKey + " has not been found in the cache, still looking for node " + nodePath); } JCRNodeWrapper node = null; while (true) { try { node = session.getNode(nodePath); break; } catch (PathNotFoundException ex) { if (nodePath.lastIndexOf("/") < nodePath.lastIndexOf(".")) { nodePath = nodePath.substring(0, nodePath.lastIndexOf(".")); } else { throw new PathNotFoundException("'" + nodePath + "'not found"); } } } nodePathCache.put(new Element(cacheKey, nodePath)); // the next condition is false e.g. when nodePath is "/" and session's locale is not in systemsite's locales JCRSiteNode resolveSite = node.getResolveSite(); if (resolveSite != null) { siteInfo = new SiteInfo(resolveSite); siteInfoCache.put(new Element(cacheKey, siteInfo)); } return nodePath; } }); } if (siteInfo == null) { siteInfoCache.remove(cacheKey); throw new RepositoryException( "could not resolve site for " + path + " in workspace " + workspace + " in language " + locale); } if (siteInfo.isMixLanguagesActive() && siteInfo.getDefaultLanguage() != null) { JCRSessionFactory.getInstance() .setFallbackLocale(LanguageCodeConverters.getLocaleFromCode(siteInfo.getDefaultLanguage())); } JCRSessionWrapper userSession = JCRSessionFactory.getInstance().getCurrentUserSession(workspace, locale); if (userSession.getVersionDate() == null && versionDate != null) userSession.setVersionDate(versionDate); if (userSession.getVersionLabel() == null && versionLabel != null) userSession.setVersionLabel(versionLabel); try { node = userSession.getNode(nodePath); } catch (PathNotFoundException e) { throw new AccessDeniedException(path); } resolvedNodes.put(cacheKey, node); return node; } private String getCacheKey(final String workspace, final Locale locale, final String path) { StringBuilder builder = new StringBuilder(workspace != null ? workspace : "null"); builder.append(CACHE_KEY_SEPARATOR); builder.append(locale != null ? locale.toString() : "null"); builder.append(CACHE_KEY_SEPARATOR); builder.append(path); return builder.toString(); } /** * Creates a resource from the specified path. * <p/> * The path should looks like : [nodepath][.templatename].[templatetype] or [nodepath].[templatetype] * * @param workspace * The workspace where to get the node * @param locale * current locale * @param path * The path of the node, in the specified workspace * @return The resource, if found * @throws PathNotFoundException * if the resource cannot be resolved * @throws RepositoryException */ protected Resource resolveResource(final String workspace, final Locale locale, final String path) throws RepositoryException { if (logger.isDebugEnabled()) { logger.debug("Resolving resource for workspace '" + workspace + "' locale '" + locale + "' and path '" + path + "'"); } if (locale == null) { throw new JahiaBadRequestException("Unknown locale"); } final URLResolver urlResolver = this; return JCRTemplate.getInstance().doExecuteWithSystemSessionAsUser(null, workspace, locale, new JCRCallback<Resource>() { public Resource doInJCR(JCRSessionWrapper session) throws RepositoryException { String ext = null; String tpl = null; String nodePath = JCRContentUtils.escapeNodePath(path); JCRNodeWrapper node; while (true) { int i = nodePath.lastIndexOf('.'); if (i > nodePath.lastIndexOf('/')) { if (ext == null) { ext = nodePath.substring(i + 1); if ("ajax".equals(ext)) { ext = null; if (renderContext != null) { renderContext.setAjaxRequest(true); HttpServletRequest req = renderContext.getRequest(); if (req.getParameter("mainResource") != null && !req.getParameter("mainResource").equals(path)) { try { Resource resource = urlResolver .getResource(req.getParameter("mainResource")); renderContext.setAjaxResource(resource); } catch (PathNotFoundException e) { } } } } } else if (tpl == null) { tpl = nodePath.substring(i + 1); } else { tpl = nodePath.substring(i + 1) + "." + tpl; } nodePath = nodePath.substring(0, i); } else { throw new PathNotFoundException("not found"); } try { node = session.getNode(nodePath); break; } catch (PathNotFoundException ex) { // ignore it } } final Element element = siteInfoCache.get(getCacheKey(workspace, locale, path)); SiteInfo siteInfo = null; if (element != null) { siteInfo = (SiteInfo) element.getObjectValue(); } boolean mixLanguagesActive = false; String defaultLanguage = null; if (siteInfo == null) { JCRSiteNode site = node.getResolveSite(); if (site != null) { defaultLanguage = site.getDefaultLanguage(); mixLanguagesActive = site.isMixLanguagesActive(); siteInfoCache .put(new Element(getCacheKey(workspace, locale, path), new SiteInfo(site))); } } else { defaultLanguage = siteInfo.getDefaultLanguage(); mixLanguagesActive = siteInfo.isMixLanguagesActive(); } JCRSessionWrapper userSession; if (defaultLanguage != null && mixLanguagesActive) { userSession = JCRSessionFactory.getInstance().getCurrentUserSession(workspace, locale, LanguageCodeConverters.languageCodeToLocale(defaultLanguage)); } else { userSession = JCRSessionFactory.getInstance().getCurrentUserSession(workspace, locale); } if (userSession.getVersionDate() == null) userSession.setVersionDate(versionDate); if (userSession.getVersionLabel() == null) userSession.setVersionLabel(versionLabel); try { node = userSession.getNode(nodePath); } catch (PathNotFoundException e) { throw new AccessDeniedException(path); } Resource r = new Resource(node, ext, tpl, Resource.CONFIGURATION_PAGE); if (logger.isDebugEnabled()) { logger.debug("Resolved resource: " + r); } return r; } }); } /** * Checks whether the URL points to a Jahia content object, which can be mapped to vanity URLs. * @return true if current node can be mapped to vanity URLs, otherwise false */ public boolean isMappable() { return mappable; } /** * Checks whether the URL points to a Jahia content object, which can be mapped to vanity * URLs. If this is the case then also check if there already is a mapping for the current node. * @return true if mappings exist(ed) for the current node, otherwise false */ public boolean isMapped() { boolean mapped = mappable; if (mapped) { try { Resource resource = getResource(); if (!resource.getTemplate().equals("default")) { return false; } JCRNodeWrapper node = resource.getNode(); if (node != null && !node.isNodeType(VanityUrlManager.JAHIAMIX_VANITYURLMAPPED)) { mapped = false; } } catch (RepositoryException e) { logger.debug("Cannot check if node has the jmix:vanityUrlMapped mixin", e); } } return mapped; } private VanityUrlService getVanityUrlService() { return (VanityUrlService) SpringContextSingleton.getBean(VanityUrlService.class.getName()); } /** * Gets the site-key of the request resolved by the URL * @return the site-key of the given URL */ public String getSiteKey() { return siteKey; } /** * Sets the site-key resolved for the current URL * @param siteKey the site-key resolved for the current URL */ public void setSiteKey(String siteKey) { this.siteKey = siteKey; } public String getSiteKeyByServerName() { return siteKeyByServerName; } /** * If value is not null, then URL resolving encountered that the URL has * permanently been changed and thus a server-side redirect to the new * location should be triggered. * @return URL for the new location */ public String getRedirectUrl() { return redirectUrl; } /** * Set the URL location for a redirection suggestion. * @param redirectUrl suggested vanity URL to redirect to */ public void setRedirectUrl(String redirectUrl) { this.redirectUrl = redirectUrl; } public String getVanityUrl() { return vanityUrl; } public void setVanityUrl(String vanityUrl) { this.vanityUrl = vanityUrl; } protected Locale verifyLanguage(String lang) { if (StringUtils.isEmpty(lang)) { return DEFAULT_LOCALE; } if (!LanguageCodeConverters.LANGUAGE_PATTERN.matcher(lang).matches()) { return null; } return LanguageCodeConverters.languageCodeToLocale(lang); } protected String verifyWorkspace(String workspace) { if (StringUtils.isEmpty(workspace)) { if (workspace == null) { workspace = DEFAULT_WORKSPACE; } } else { if (!JCRContentUtils.isValidWorkspace(workspace) && this.workspace == null) { throw new JahiaBadRequestException("Unknown workspace '" + workspace + "'"); } if (JCRContentUtils.isValidWorkspace(workspace) && this.workspace != null && !workspace.equals(this.workspace)) { throw new JahiaBadRequestException("Invalid workspace '" + workspace + "'"); } if (!JCRContentUtils.isValidWorkspace(workspace) && this.workspace != null) { workspace = this.workspace; path = this.workspace + "/" + path; } } return workspace; } public void setVersionDate(Date versionDate) { this.versionDate = versionDate; } public void setVersionLabel(String versionLabel) { this.versionLabel = versionLabel; } public Date getVersionDate() { return versionDate; } public String getVersionLabel() { return versionLabel; } public SiteInfo getSiteInfo() { return siteInfo; } }