/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco 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 3 of the License, or
* (at your option) any later version.
*
* Alfresco 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 Alfresco. If not, see .
* #L%
*/
package org.alfresco.repo.web.scripts;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Element;
import org.springframework.extensions.config.ConfigImpl;
import org.springframework.extensions.config.ConfigSection;
import org.springframework.extensions.config.ConfigService;
import org.springframework.extensions.config.evaluator.Evaluator;
import org.springframework.extensions.config.xml.XMLConfigService;
import org.springframework.extensions.config.xml.elementreader.ConfigElementReader;
import org.springframework.extensions.surf.extensibility.BasicExtensionModule;
import org.springframework.extensions.surf.extensibility.ExtensibilityModel;
import org.springframework.extensions.surf.extensibility.HandlesExtensibility;
import org.springframework.extensions.surf.extensibility.WebScriptExtensibilityModuleHandler;
import org.springframework.extensions.surf.extensibility.impl.ExtensibilityModelImpl;
import org.springframework.extensions.surf.extensibility.impl.MarkupDirective;
import org.springframework.extensions.webscripts.Authenticator;
import org.springframework.extensions.webscripts.ExtendedScriptConfigModel;
import org.springframework.extensions.webscripts.ExtendedTemplateConfigModel;
import org.springframework.extensions.webscripts.ScriptConfigModel;
import org.springframework.extensions.webscripts.TemplateConfigModel;
import org.springframework.extensions.webscripts.WebScriptPropertyResourceBundle;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
/**
*
A simple extensibility {@link org.springframework.extensions.webscripts.Container} for processing WebScripts. This extends the {@link RepositoryContainer} and
* implements the {@link HandlesExtensibility} interface to provide extensibility capabilities.
*
* @author David Draper
*/
public class ExtensibilityContainer extends RepositoryContainer implements HandlesExtensibility
{
private static final Log logger = LogFactory.getLog(ExtensibilityContainer.class);
public boolean isExtensibilitySuppressed()
{
return false;
}
/**
* Opens a new {@link ExtensibilityModel}, defers execution to the extended {@link RepositoryContainer} and
* then closes the {@link ExtensibilityModel}.
*/
@Override
public void executeScript(WebScriptRequest scriptReq,
WebScriptResponse scriptRes,
Authenticator auth) throws IOException
{
ExtensibilityModel extModel = this.openExtensibilityModel();
try
{
super.executeScript(scriptReq, scriptRes, auth);
}
finally
{
// It's only necessary to close the model if it's actually been used. Not all WebScripts will make use of the
// model. An example of this would be the StreamContent WebScript. It is important not to attempt to close
// an unused model since the WebScript executed may have already flushed the response if it has overridden
// the default .execute() method.
if (this.modelUsed.get())
{
try
{
this.closeExtensibilityModel(extModel, scriptRes.getWriter());
}
catch (IOException e)
{
logger.error("An error occurred getting the Writer when closing an ExtensibilityModel", e);
}
}
}
}
/**
* This keeps track of whether or not the {@link ExtensibilityModel} for the current thread has been used. The
* thread local value will only be set to true
if the getCurrentExtensibilityModel
method
* is called.
*/
private ThreadLocal modelUsed = new ThreadLocal();
/**
* A {@link WebScriptExtensibilityModuleHandler} is required for retrieving information on what
* {@link BasicExtensionModule} instances have been configured and the extension files that need
* to be processed. This variable should be set thorugh the Spring application context configuration.
*/
private WebScriptExtensibilityModuleHandler extensibilityModuleHandler = null;
/**
* Sets the {@link WebScriptExtensibilityModuleHandler} for this {@link org.springframework.extensions.webscripts.Container}.
* @param extensibilityModuleHandler WebScriptExtensibilityModuleHandler
*/
public void setExtensibilityModuleHandler(WebScriptExtensibilityModuleHandler extensibilityModuleHandler)
{
this.extensibilityModuleHandler = extensibilityModuleHandler;
}
/**
* Maintains a list of all the {@link ExtensibilityModel} instances being used across all the
* available threads.
*/
private ThreadLocal extensibilityModel = new ThreadLocal();
/**
* Creates a new {@link ExtensibilityModel} and sets it on the current thread
*/
public ExtensibilityModel openExtensibilityModel()
{
if (logger.isDebugEnabled())
{
logger.debug("Opening for thread: " + Thread.currentThread().getName());
}
this.extendedBundleCache.set(new HashMap());
this.evaluatedModules.set(null);
this.fileBeingProcessed.set(null);
this.globalConfig.set(null);
this.sections.set(null);
this.sectionsByArea.set(null);
ExtensibilityModel model = new ExtensibilityModelImpl(null, this);
this.extensibilityModel.set(model);
this.modelUsed.set(Boolean.FALSE);
return model;
}
/**
* Flushes the {@link ExtensibilityModel} provided and sets its parent as the current {@link ExtensibilityModel}
* for the current thread.
*/
public void closeExtensibilityModel(ExtensibilityModel model, Writer out)
{
if (logger.isDebugEnabled())
{
logger.debug("Closing for thread: " + Thread.currentThread().getName());
}
model.flushModel(out);
this.modelUsed.set(Boolean.FALSE);
this.extensibilityModel.set(null);
}
/**
* Returns the {@link ExtensibilityModel} for the current thread.
*/
public ExtensibilityModel getCurrentExtensibilityModel()
{
if (logger.isDebugEnabled())
{
logger.debug("Getting current for thread: " + Thread.currentThread().getName());
}
this.modelUsed.set(Boolean.TRUE);
return this.extensibilityModel.get();
}
/**
* This method is implemented to perform no action as it is not necessary for a standalone WebScript
* container to add dependencies for processing.
*/
public void updateExtendingModuleDependencies(String pathBeingProcessed, Map model)
{
// NOT REQUIRED FOR STANDALONE WEBSCRIPT CONTAINER
}
/**
* A thread-safe cache of extended {@link ResourceBundle} instances for the current request.
*/
private ThreadLocal