diff --git a/config/alfresco/web-pagerenderer-application-context.xml b/config/alfresco/web-pagerenderer-application-context.xml new file mode 100644 index 0000000000..e23734930e --- /dev/null +++ b/config/alfresco/web-pagerenderer-application-context.xml @@ -0,0 +1,28 @@ + + + + + + + + + + classpath:alfresco/web-pagerenderer-config.xml + classpath:alfresco/extension/web-pagerenderer-config-custom.xml + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/web-pagerenderer-config.xml b/config/alfresco/web-pagerenderer-config.xml new file mode 100644 index 0000000000..92d9cf3a93 --- /dev/null +++ b/config/alfresco/web-pagerenderer-config.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/web/app/servlet/PageRendererServlet.java b/source/java/org/alfresco/web/app/servlet/PageRendererServlet.java new file mode 100644 index 0000000000..659c2d41ac --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/PageRendererServlet.java @@ -0,0 +1,703 @@ +/* + * 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.app.servlet; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.config.Config; +import org.alfresco.config.ConfigService; +import org.alfresco.config.JNDIConstants; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.repo.template.ClassPathRepoTemplateLoader; +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.security.AuthenticationService; +import org.alfresco.web.scripts.WebScriptCache; +import org.alfresco.web.scripts.WebScriptMatch; +import org.alfresco.web.scripts.WebScriptRequest; +import org.alfresco.web.scripts.WebScriptResponse; +import org.alfresco.web.scripts.WebScriptRuntime; +import org.alfresco.web.scripts.WebScriptServlet; +import org.alfresco.web.scripts.WebScriptURLRequest; +import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.springframework.context.ApplicationContext; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +import freemarker.cache.MultiTemplateLoader; +import freemarker.cache.TemplateLoader; + +/** + * Servlet for rendering templated pages based on Webscript components. + * + * NOTE: No web-client helper classes should be used here! This servlet should be movable + * to the headerless repo remote client with the minimum of work. + * + * @author Kevin Roast + */ +public class PageRendererServlet extends WebScriptServlet +{ + private static Log logger = LogFactory.getLog(PageRendererServlet.class); + + private static final String MIMETYPE_HTML = "text/html;charset=utf-8"; + + private PageTemplateProcessor templateProcessor; + private WebScriptTemplateLoader webscriptTemplateLoader; + private Map defaultPageDefCache = null; + + @Override + public void init() throws ServletException + { + super.init(); + + // init beans + ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); + templateProcessor = (PageTemplateProcessor)context.getBean("pagerenderer.templateprocessor"); + webscriptTemplateLoader = new WebScriptTemplateLoader(); + ClassPathRepoTemplateLoader repoLoader = new ClassPathRepoTemplateLoader( + serviceRegistry.getNodeService(), + serviceRegistry.getContentService(), + templateProcessor.getDefaultEncoding()); + templateProcessor.setTemplateLoader(new MultiTemplateLoader(new TemplateLoader[]{ + webscriptTemplateLoader, repoLoader})); + templateProcessor.initConfig(); + + // we use a specific config service instance + configService = (ConfigService)context.getBean("pagerenderer.config"); + + // create cache for default config + defaultPageDefCache = Collections.synchronizedMap(new HashMap()); + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse res) + throws ServletException, IOException + { + String uri = req.getRequestURI(); + + if (logger.isDebugEnabled()) + { + String qs = req.getQueryString(); + logger.debug("Processing Page Renderer URL: (" + req.getMethod() + ") " + uri + + ((qs != null && qs.length() != 0) ? ("?" + qs) : "")); + } + + uri = uri.substring(req.getContextPath().length()); // skip server context path + StringTokenizer t = new StringTokenizer(uri, "/"); + int tokenCount = t.countTokens(); + t.nextToken(); // skip servlet name + if (t.hasMoreTokens() == false) + { + throw new IllegalArgumentException("Invalid URL to PageRendererServlet: " + uri); + } + + // retrieve the page name from the url + String page = t.nextToken(); + + // the AVM store to retrieve pages and config from + // TODO: find a better way to set the store for a website servlet + String store = req.getParameter("store"); + if (store == null) + { + throw new AlfrescoRuntimeException("'store' argument mandatory for PageRendererServlet."); + } + + try + { + // lookup template path from page config in website AVM store + PageDefinition pageDefinition = lookupPageDefinition(store, page); + + // set response content type and charset + res.setContentType(MIMETYPE_HTML); + + // TODO: What authentication to use here? Guest? Or all templates and webscripts + // must reside in a known AVM repo - i.e. skip permissions. Template API still + // uses NodeService for some calls - even for AVMTemplateNode - so will need + // some kind of security context... + authenticate(getServletContext(), req, res); + + // set the web app context path for the template loader to use when rebuilding urls + this.webscriptTemplateLoader.setContextPath(req.getContextPath()); + this.webscriptTemplateLoader.setPageDefinition(pageDefinition); + + // Process the template page using our custom loader - the loader will find and buffer + // individual included webscript output into the main writer for the servlet page. + // TODO: where does the theme name come from!? same problem as where does store name come from... + String templatePath = getStoreSitePath(store) + '/' + "themes" + '/' + "default" + '/' + + "templates" + '/' + pageDefinition.TemplateName; + + if (logger.isDebugEnabled()) + logger.debug("Page template resolved as: " + templatePath); + + processTemplatePage(templatePath, req, res); + } + catch (Throwable err) + { + throw new AlfrescoRuntimeException("Error occurred during page rendering. Page id: " + + page + " with error: " + err.getMessage(), err); + } + } + + private void processTemplatePage(String templatePath, HttpServletRequest req, HttpServletResponse res) + throws IOException + { + NodeRef ref = AVMNodeConverter.ToNodeRef(-1, templatePath); + templateProcessor.process(ref.toString(), getModel(req), res.getWriter()); + } + + private Object getModel(HttpServletRequest req) + { + // TODO: add the full template model here? + // just a basic minimum model for now + Map model = new HashMap(); + model.put("url", new URLHelper(req.getContextPath())); + return model; + } + + /** + * Get the page definition config object based on the page ID and AVM store. + * + * @param store AVM store to retrieve data from + * @param page Page ID to lookup in web-pagerenderer-config.xml + * + * @return Path to the template content + */ + private PageDefinition lookupPageDefinition(String store, String page) + { + // Lookup page via standard Alfresco XML config + /*Config config = getConfig(); + if (config != null) + { + ConfigElement pagesConfig = config.getConfigElement("pages"); + for (ConfigElement pageConfig : pagesConfig.getChildren("page")) + { + String pageId = pageConfig.getAttribute("id"); + if (page.equals(pageId)) + { + // found page reference + if (logger.isDebugEnabled()) + logger.debug("Looked up page id: " + page + " as " + pageConfig.getValue()); + pageValue = pageConfig.getValue(); + break; + } + } + }*/ + + // Lookup (and cache) config for default page-definition file in root + PageDefinition defaultPageDef = defaultPageDefCache.get(store); + if (defaultPageDef == null) + { + // read default config for the site and cache the result + String defaultConfigPath = getStoreSitePath(store) + '/' + "pages" + '/' + "page-definition.xml"; + defaultPageDef = readPageDefinitionConfig(defaultConfigPath, page, null); + defaultPageDefCache.put(store, defaultPageDef); + } + + // Lookup page xml config in AVM store using page name as location + String configPath = getStoreSitePath(store) + '/' + "pages" + '/' + page + '/' + "page-definition.xml"; + + // read the page definition and return it + return readPageDefinitionConfig(configPath, page, defaultPageDef); + } + + /** + * Read the page definition config at the specified location. A default config object can be + * supplied from which values will be taken if none are supplied in the config that is read. + * + * @return PageDefinition representing the config + */ + private PageDefinition readPageDefinitionConfig(String configPath, String page, PageDefinition defaultPageDef) + { + PageDefinition pageDef = null; + + AVMService avm = this.serviceRegistry.getAVMService(); + try + { + // parse page definition xml config file + // TODO: convert to pull parser to optimize (see importer ViewParser) + SAXReader reader = new SAXReader(); + try + { + Document document = reader.read(avm.getFileInputStream(-1, configPath)); + + Element rootElement = document.getRootElement(); + if (!rootElement.getName().equals("page")) + { + throw new AlfrescoRuntimeException( + "Expected 'page' root element in page-definition.xml config: " + configPath); + } + + String templateName = null; + if (defaultPageDef != null && defaultPageDef.TemplateName != null) + { + // take template name from default config in case none is specified locally + templateName = defaultPageDef.TemplateName; + } + Element templateElement = rootElement.element("template"); + if (defaultPageDef != null && (templateElement == null && templateName == null)) + { + throw new AlfrescoRuntimeException( + "No 'template' element (and no default set) found in page-definition.xml config: " + configPath); + } + templateName = templateElement.attributeValue("name"); + if (templateName == null || templateName.length() == 0) + { + throw new AlfrescoRuntimeException( + "The 'template' element is missing mandatory 'name' attribute in page-definition.xml config: " + + configPath); + } + + // create config object for this page and store template name for this page + pageDef = new PageDefinition(templateName); + + // copy in component mappings from default config definitions first + if (defaultPageDef != null) + { + pageDef.Components.putAll(defaultPageDef.Components); + } + + // read the component defs for this page + // removing 'disabled' components as configured locally + Element componentsElements = rootElement.element("components"); + if (componentsElements != null) + { + for (Element ce : (List)componentsElements.elements("component")) + { + // read the mandatory component 'id' attribute + String id = ce.attributeValue("id"); + if (id == null || id.length() == 0) + { + throw new AlfrescoRuntimeException( + "A 'component' element is missing mandatory 'id' attribute in page-definition.xml config: " + + configPath); + } + + // next check for the 'disabled' boolean attribute - used to disable components + // that are specified in the default config - we don't need to read further + String disabled = ce.attributeValue("disabled"); + if (disabled != null) + { + if (Boolean.parseBoolean(disabled) == true) + { + pageDef.Components.remove(id); + continue; + } + } + + String url = ce.attributeValue("url"); + if (url == null || url.length() == 0) + { + throw new AlfrescoRuntimeException( + "A 'component' element is missing mandatory 'url' attribute in page-definition.xml config: " + + configPath); + } + + // create minimum component config definition + PageComponent component = new PageComponent(id, url); + + // store any other component properties + for (Element pe : (List)ce.elements()) + { + component.Properties.put(pe.getName(), pe.getTextTrim()); + } + + // store component definition in the page definition + pageDef.Components.put(component.Id, component); + } + } + } + catch (DocumentException docErr) + { + throw new AlfrescoRuntimeException("Failed to parse 'page-definition.xml' for page '" + page + + "' in config: " + configPath, docErr); + } + } + catch (AVMNotFoundException avmErr) + { + throw new AlfrescoRuntimeException("Unable to find 'page-definition.xml' for page '" + page + + "' in expected location path: " + configPath, avmErr); + } + + return pageDef; + } + + private Config getConfig() + { + return this.configService.getConfig("PageRenderer"); + } + + private static void authenticate(ServletContext sc, HttpServletRequest req, HttpServletResponse res) + throws IOException + { + WebApplicationContext wc = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); + AuthenticationService auth = (AuthenticationService)wc.getBean("AuthenticationService"); + auth.authenticate("admin", "admin".toCharArray()); + //auth.authenticateAsGuest(); + } + + private static String getStoreSitePath(String store) + { + return store + ":/" + JNDIConstants.DIR_DEFAULT_WWW + '/' + "avm_site"; + } + + + /** + * WebScript runtime for the PageRenderer servlet. + */ + private class PageRendererWebScriptRuntime extends WebScriptRuntime + { + private String webScript; + private String scriptUrl; + private String encoding; + private ByteArrayOutputStream baOut = null; + + PageRendererWebScriptRuntime(String webScript, String scriptUrl, String encoding) + { + super(registry, serviceRegistry); + this.encoding = encoding; + this.webScript = webScript; + this.scriptUrl = scriptUrl; + if (logger.isDebugEnabled()) + logger.debug("Constructing runtime for url: " + scriptUrl); + } + + @Override + protected String getScriptUrl() + { + return webScript; + } + + @Override + protected WebScriptRequest createRequest(WebScriptMatch match) + { + return new WebScriptPageRendererRequest(scriptUrl, match); + } + + @Override + protected WebScriptResponse createResponse() + { + // create a response object that we control to write to a temporary output + // we later use that as the source for the webscript "template" + try + { + this.baOut = new ByteArrayOutputStream(4096); + BufferedWriter wrOut = new BufferedWriter( + encoding == null ? new OutputStreamWriter(baOut) : new OutputStreamWriter(baOut, encoding)); + return new WebScriptPageRendererResponse(wrOut, baOut); + } + catch (UnsupportedEncodingException err) + { + throw new AlfrescoRuntimeException("Unsupported encoding.", err); + } + } + + @Override + protected boolean authenticate(RequiredAuthentication required, boolean isGuest) + { + // TODO: what authentication here? + return true; + } + + @Override + protected String getScriptMethod() + { + return "GET"; + } + + public Reader getResponseReader() + { + try + { + if (baOut == null) + { + return null; + } + else + { + return new BufferedReader(new InputStreamReader( + encoding == null ? new ByteArrayInputStream(baOut.toByteArray()) : + new ByteArrayInputStream(baOut.toByteArray()), encoding)); + } + } + catch (UnsupportedEncodingException err) + { + throw new AlfrescoRuntimeException("Unsupported encoding.", err); + } + } + } + + /** + * Simple implementation of a WebScript URL Request for a webscript on the page + */ + private class WebScriptPageRendererRequest extends WebScriptURLRequest + { + WebScriptPageRendererRequest(String scriptUrl, WebScriptMatch match) + { + super(scriptUrl, match); + } + + public String getAgent() + { + return null; + } + + public String getServerPath() + { + return null; + } + } + + /** + * Implementation of a WebScript Response object for PageRenderer servlet + */ + private class WebScriptPageRendererResponse implements WebScriptResponse + { + private Writer outWriter; + private OutputStream outStream; + + public WebScriptPageRendererResponse(Writer outWriter, OutputStream outStream) + { + this.outWriter = outWriter; + this.outStream = outStream; + } + + public String encodeScriptUrl(String url) + { + // TODO: some kind of encoding required here - need to allow webscripts to call themselves + // on this page - so need the servlet PageRenderer URL plus args to identify the webscript + // and it's new url - similar to the JSF or Portlet runtimes + return url; + } + + public String getEncodeScriptUrlFunction(String name) + { + // TODO: may be required? + return null; + } + + public OutputStream getOutputStream() throws IOException + { + return this.outStream; + } + + public Writer getWriter() throws IOException + { + return this.outWriter; + } + + public void reset() + { + // not supported + } + + public void setCache(WebScriptCache cache) + { + // not supported + } + + public void setContentType(String contentType) + { + // not supported + } + + public void setStatus(int status) + { + // not supported + } + } + + /** + * Template loader that resolves and executes webscript components by looking up layout keys + * in the template against the component definition service URLs for the page. + */ + private class WebScriptTemplateLoader implements TemplateLoader + { + private ThreadLocal pageDefinition = new ThreadLocal(); + private String contextPath; + private long last = 0L; + + public void closeTemplateSource(Object templateSource) throws IOException + { + // nothing to do + } + + public Object findTemplateSource(String name) throws IOException + { + // The webscript is looked up based on the key in the #include directive - it must + // be of the form [somekey] so that it can be recognised by the loader + + // most templates included by this loader will be children of other templates + // unfortunately FreeMarker attempts to build paths for you to child templates - they are not + // really children - so this information must be discarded + if (name.startsWith("avm://")) + { + name = name.substring(name.indexOf("/[") + 1); + } + + if (name.startsWith("[") && name.endsWith("]")) + { + String key = name.substring(1, name.length() - 1); + + if (logger.isDebugEnabled()) + logger.debug("Found webscript component key: " + key); + + // lookup against component def config + PageComponent component = this.pageDefinition.get().Components.get(key); + if (component == null) + { + // TODO: if the lookup fails, exception or just ignore render and log...? + throw new AlfrescoRuntimeException("Failed to find component identified by key '" + key + + "' found in template: " + pageDefinition.get().TemplateName); + } + + // TODO: remove the /service prefix from all config files? + String url = component.Url; + if (url.lastIndexOf('?') != -1) + { + url = url.substring("/service".length(), url.lastIndexOf('?')); + } + else + { + url = url.substring("/service".length()); + } + return url; + } + else + { + return null; + } + } + + public long getLastModified(Object templateSource) + { + return last--; + } + + public Reader getReader(Object templateSource, String encoding) throws IOException + { + // Execute the webscript and return a Reader to the textual content + String webscriptUrl = this.contextPath + "/service" + templateSource.toString(); + PageRendererWebScriptRuntime runtime = new PageRendererWebScriptRuntime( + templateSource.toString(), webscriptUrl, encoding); + runtime.executeScript(); + + // Return a reader from the runtime that executed the webscript - this effectively + // returns the result as a "template" source to freemarker. Generally this will not itself + // be a template but it can contain additional freemarker syntax if required. The downside + // to this approach is that the result of the webscript is parsed again by freemarker - this + // should be checked for performance issues. + return runtime.getResponseReader(); + } + + /** + * Setter to apply the current page definition for this template execution. A ThreadLocal is used + * to allow multiple servlet threads to execute using the same TemplateLoader (there can only be one) + * but with different page definitions for each thread. + */ + public void setPageDefinition(PageDefinition pageDef) + { + this.pageDefinition.set(pageDef); + } + + /** + * Setter called by the servlet to ensure the loader has the full context path available + * Does not matter if called multiple times by multiple threads as value is always the same. + */ + public void setContextPath(String path) + { + this.contextPath = path; + } + } + + /** + * Helper to return context path for generating urls + */ + public static class URLHelper + { + String context; + + public URLHelper(String context) + { + this.context = context; + } + + public String getContext() + { + return context; + } + } + + private static class PageDefinition + { + public String TemplateName; + public Map Components = new HashMap(); + + PageDefinition(String templateId) + { + this.TemplateName = templateId; + } + } + + private static class PageComponent + { + public String Id; + public String Url; + public Map Properties = new HashMap(4, 1.0f); + + PageComponent(String id, String url) + { + this.Id = id; + this.Url = url; + } + } +} diff --git a/source/java/org/alfresco/web/app/servlet/PageTemplateProcessor.java b/source/java/org/alfresco/web/app/servlet/PageTemplateProcessor.java new file mode 100644 index 0000000000..33f133e5c8 --- /dev/null +++ b/source/java/org/alfresco/web/app/servlet/PageTemplateProcessor.java @@ -0,0 +1,49 @@ +/* + * 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.app.servlet; + +import org.alfresco.web.scripts.TemplateProcessor; + +/** + * Extension to the webscripts TemplateProcessor class to allow the default encoding + * to be returned and also enable config to be initialised from a public accessor. + * + * @author Kevin Roast + */ +public class PageTemplateProcessor extends TemplateProcessor +{ + public String getDefaultEncoding() + { + return this.defaultEncoding; + } + + /** + * Initialise FreeMarker Configuration + */ + public void initConfig() + { + super.initConfig(); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/web/scripts/TemplateProcessor.java b/source/java/org/alfresco/web/scripts/TemplateProcessor.java index 96d9d771ef..3eca3d8685 100644 --- a/source/java/org/alfresco/web/scripts/TemplateProcessor.java +++ b/source/java/org/alfresco/web/scripts/TemplateProcessor.java @@ -56,10 +56,10 @@ import freemarker.template.TemplateExceptionHandler; public class TemplateProcessor extends FreeMarkerProcessor implements ApplicationContextAware, ApplicationListener { private ProcessorLifecycle lifecycle = new ProcessorLifecycle(); - private TemplateLoader templateLoader = null; - private String defaultEncoding; - private Configuration templateConfig; - private FreeMarkerProcessor freeMarkerProcessor; + protected TemplateLoader templateLoader = null; + protected String defaultEncoding; + protected Configuration templateConfig; + protected FreeMarkerProcessor freeMarkerProcessor; /* (non-Javadoc) @@ -154,6 +154,9 @@ public class TemplateProcessor extends FreeMarkerProcessor implements Applicatio // rethrow any exception so we can deal with them config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + // turn off locale sensitive lookup - to save numerous wasted calls to nodeservice.exists() + config.setLocalizedLookup(false); + // set template encoding if (defaultEncoding != null) { diff --git a/source/java/org/alfresco/web/scripts/WebScriptServlet.java b/source/java/org/alfresco/web/scripts/WebScriptServlet.java index 56694e9b07..45413436de 100644 --- a/source/java/org/alfresco/web/scripts/WebScriptServlet.java +++ b/source/java/org/alfresco/web/scripts/WebScriptServlet.java @@ -54,13 +54,13 @@ public class WebScriptServlet extends HttpServlet private static final Log logger = LogFactory.getLog(WebScriptServlet.class); // Component Dependencies - private DeclarativeWebScriptRegistry registry; - private ServiceRegistry serviceRegistry; - private WebScriptServletAuthenticator authenticator; + protected DeclarativeWebScriptRegistry registry; + protected ServiceRegistry serviceRegistry; + protected WebScriptServletAuthenticator authenticator; protected ConfigService configService; /** Host Server Configuration */ - private static ServerConfigElement serverConfig; + protected static ServerConfigElement serverConfig; @Override diff --git a/source/web/WEB-INF/web.xml b/source/web/WEB-INF/web.xml index d82ab30e56..1321bb398c 100644 --- a/source/web/WEB-INF/web.xml +++ b/source/web/WEB-INF/web.xml @@ -71,6 +71,7 @@ classpath:alfresco/application-context.xml classpath:alfresco/web-client-application-context.xml classpath:alfresco/web-scripts-application-context.xml + classpath:alfresco/web-pagerenderer-application-context.xml classpath:web-services-application-context.xml Spring config file locations @@ -290,6 +291,11 @@ JBPMDeployProcessServlet org.alfresco.web.app.servlet.JBPMDeployProcessServlet + + + pageRendererServlet + org.alfresco.web.app.servlet.PageRendererServlet + Faces Servlet @@ -430,6 +436,11 @@ workflowDefinitionImageServlet /workflowdefinitionimage/* + + + pageRendererServlet + /page/* + 60