Merge Web Scripts from BRANCHES/DEV/DAVE

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5358 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2007-03-08 18:51:38 +00:00
parent 4b30804f20
commit 3bb7471045
37 changed files with 6464 additions and 0 deletions

View File

@@ -0,0 +1,320 @@
/*
* 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.scripts;
import java.io.Writer;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.repo.jscript.Node;
import org.alfresco.repo.jscript.ScriptableHashMap;
import org.alfresco.repo.template.AbsoluteUrlMethod;
import org.alfresco.repo.template.ISO8601DateFormatMethod;
import org.alfresco.repo.template.UrlEncodeMethod;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.descriptor.DescriptorService;
import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication;
import org.alfresco.web.scripts.WebScriptDescription.RequiredTransaction;
/**
* Skeleton implementation of a Web Script
*
* @author davidc
*/
public abstract class AbstractWebScript implements WebScript
{
// dependencies
private WebScriptContext scriptContext;
private WebScriptRegistry scriptRegistry;
private WebScriptDescription description;
private ServiceRegistry serviceRegistry;
private DescriptorService descriptorService;
//
// Initialisation
//
/**
* @param scriptContext
*/
final public void setScriptContext(WebScriptContext scriptContext)
{
this.scriptContext = scriptContext;
}
/**
* @param serviceRegistry
*/
final public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
/**
* @param descriptorService
*/
final public void setDescriptorService(DescriptorService descriptorService)
{
this.descriptorService = descriptorService;
}
/**
* Sets the Service Description
*
* @param description
*/
final public void setDescription(WebScriptDescription description)
{
this.description = description;
}
/**
* Initialise Web Script
*
* @param scriptRegistry
*/
public void init(WebScriptRegistry scriptRegistry)
{
this.scriptRegistry = scriptRegistry;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScript#getDescription()
*/
final public WebScriptDescription getDescription()
{
return this.description;
}
//
// Service Implementation Helpers
//
/**
* Gets the Repository Context
*
* @return repository context
*/
final public WebScriptContext getRepositoryContext()
{
return this.scriptContext;
}
/**
* Gets the Web Script Registry
*
* @return Web Script Registry
*/
final public WebScriptRegistry getWebScriptRegistry()
{
return this.scriptRegistry;
}
/**
* Gets the Alfresco Service Registry
*
* @return service registry
*/
final public ServiceRegistry getServiceRegistry()
{
return this.serviceRegistry;
}
/**
* Gets the Alfresco Descriptor
*
* @return descriptor
*/
final public DescriptorService getDescriptorService()
{
return this.descriptorService;
}
//
// Scripting Support
//
/**
* Create a map of arguments from Web Script Request
*
* @param req Web Script Request
* @return argument map
*/
final protected Map<String, String> createArgModel(WebScriptRequest req)
{
Map<String, String> args = new ScriptableHashMap<String, String>();
Enumeration names = req.getParameterNames();
while (names.hasMoreElements())
{
String name = (String)names.nextElement();
args.put(name, req.getParameter(name));
}
return args;
}
/**
* Create a model for script usage
*
* @param req web script request
* @param res web script response
* @param customModel custom model entries
*
* @return script model
*/
final protected Map<String, Object> createScriptModel(WebScriptRequest req, WebScriptResponse res, Map<String, Object> customModel)
{
// create script model
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
// add repository context (only if authenticated and transaction enabled)
if (getDescription().getRequiredAuthentication() != RequiredAuthentication.none &&
getDescription().getRequiredTransaction() != RequiredTransaction.none)
{
NodeRef companyHome = scriptContext.getCompanyHome();
if (companyHome != null)
{
model.put("companyhome", new Node(scriptContext.getCompanyHome(), serviceRegistry));
}
NodeRef person = scriptContext.getPerson();
if (person != null)
{
model.put("person", new Node(person, serviceRegistry));
model.put("userhome", new Node(scriptContext.getUserHome(person), serviceRegistry));
}
}
// add web script context
model.put("args", createArgModel(req));
model.put("guest", req.isGuest());
model.put("url", new URLModel(req));
model.put("server", new ServerModel(descriptorService.getServerDescriptor()));
// add custom model
if (customModel != null)
{
model.putAll(customModel);
}
// return model
return model;
}
/**
* Create a model for template usage
*
* @param req web script request
* @param res web script response
* @param customModel custom model entries
*
* @return template model
*/
final protected Map<String, Object> createTemplateModel(WebScriptRequest req, WebScriptResponse res, Map<String, Object> customModel)
{
// create script model
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
// add repository context
if (getDescription().getRequiredAuthentication() != RequiredAuthentication.none &&
getDescription().getRequiredTransaction() != RequiredTransaction.none)
{
NodeRef companyHome = scriptContext.getCompanyHome();
if (companyHome != null)
{
model.put("companyhome", new TemplateNode(scriptContext.getCompanyHome(), serviceRegistry, null));
}
NodeRef person = scriptContext.getPerson();
if (person != null)
{
model.put("person", new TemplateNode(person, serviceRegistry, null));
model.put("userhome", new TemplateNode(scriptContext.getUserHome(person), serviceRegistry, null));
}
}
// add web script context
model.put("args", createArgModel(req));
model.put("guest", req.isGuest());
model.put("url", new URLModel(req));
model.put("server", new ServerModel(descriptorService.getServerDescriptor()));
// add template support
model.put("xmldate", new ISO8601DateFormatMethod());
model.put("absurl", new AbsoluteUrlMethod(req.getServerPath()));
model.put("urlencode", new UrlEncodeMethod());
model.put("date", new Date());
// add custom model
if (customModel != null)
{
model.putAll(customModel);
}
return model;
}
/**
* Render a template (identified by path)
*
* @param templatePath template path
* @param model model
* @param writer output writer
*/
final protected void renderTemplate(String templatePath, Map<String, Object> model, Writer writer)
{
getWebScriptRegistry().getTemplateProcessor().process(templatePath, model, writer);
}
/**
* Render a template (contents as string)
* @param template the template
* @param model model
* @param writer output writer
*/
final protected void renderString(String template, Map<String, Object> model, Writer writer)
{
getWebScriptRegistry().getTemplateProcessor().processString(template, model, writer);
}
/**
* Execute a script
*
* @param location script location
* @param model model
*/
final protected void executeScript(ScriptLocation location, Map<String, Object> model)
{
getWebScriptRegistry().getScriptProcessor().executeScript(location, model);
}
}

View File

@@ -0,0 +1,194 @@
/*
* 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.scripts;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.util.Base64;
import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* HTTP Basic Authentication Interceptor
*
* @author davidc
*/
public class BasicAuthenticator implements MethodInterceptor
{
// Logger
private static final Log logger = LogFactory.getLog(BasicAuthenticator.class);
// dependencies
private AuthenticationService authenticationService;
/**
* @param authenticationService
*/
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
public Object invoke(MethodInvocation invocation)
throws Throwable
{
boolean authorized = false;
String currentUser = null;
Object retVal = null;
Object[] args = invocation.getArguments();
WebScriptRequest request = (WebScriptRequest)args[0];
WebScript service = (WebScript)invocation.getThis();
WebScriptDescription description = service.getDescription();
try
{
//
// Determine if user already authenticated
//
currentUser = AuthenticationUtil.getCurrentUserName();
if (logger.isDebugEnabled())
logger.debug("Current authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
//
// validate credentials
//
boolean isGuest = request.isGuest();
String authorization = request.getHeader("Authorization");
if (logger.isDebugEnabled())
{
logger.debug("Web Service authentication required: " + description.getRequiredAuthentication());
logger.debug("Guest login: " + isGuest);
logger.debug("Authorization provided (overrides Guest login): " + (authorization != null && authorization.length() > 0));
}
// authenticate as guest, if service allows
if (((authorization == null || authorization.length() == 0) || isGuest)
&& description.getRequiredAuthentication().equals(RequiredAuthentication.guest))
{
if (logger.isDebugEnabled())
logger.debug("Authenticating as Guest");
authenticationService.authenticateAsGuest();
authorized = true;
}
// authenticate as specified by HTTP Basic Authentication
else if (authorization != null && authorization.length() > 0)
{
try
{
String[] authorizationParts = authorization.split(" ");
if (!authorizationParts[0].equalsIgnoreCase("basic"))
{
throw new WebScriptException("Authorization '" + authorizationParts[0] + "' not supported.");
}
String decodedAuthorisation = new String(Base64.decode(authorizationParts[1]));
String[] parts = decodedAuthorisation.split(":");
if (parts.length == 1)
{
if (logger.isDebugEnabled())
logger.debug("Authenticating ticket " + parts[0]);
// assume a ticket has been passed
authenticationService.validate(parts[0]);
authorized = true;
}
else
{
if (logger.isDebugEnabled())
logger.debug("Authenticating user " + parts[0]);
// assume username and password passed
if (parts[0].equals(AuthenticationUtil.getGuestUserName()))
{
if (description.getRequiredAuthentication().equals(RequiredAuthentication.guest))
{
authenticationService.authenticateAsGuest();
authorized = true;
}
}
else
{
authenticationService.authenticate(parts[0], parts[1].toCharArray());
authorized = true;
}
}
}
catch(AuthenticationException e)
{
// failed authentication
}
}
//
// execute web script or request authorization
//
if (authorized)
{
retVal = invocation.proceed();
}
else
{
if (logger.isDebugEnabled())
logger.debug("Requesting authorization credentials");
WebScriptResponse response = (WebScriptResponse)args[1];
response.setStatus(401);
response.setHeader("WWW-Authenticate", "Basic realm=\"Alfresco\"");
}
}
finally
{
// reset authentication
if (authorized)
{
authenticationService.clearCurrentSecurityContext();
if (currentUser != null)
{
AuthenticationUtil.setCurrentUser(currentUser);
}
if (logger.isDebugEnabled())
logger.debug("Authentication reset: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
}
}
return retVal;
}
}

View File

@@ -0,0 +1,231 @@
/*
* 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.scripts;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.TemplateLoader;
/**
* ClassPath based Web Script Store
*
* @author davidc
*/
public class ClassPathStore implements WebScriptStore, InitializingBean
{
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
protected String classPath;
protected File fileDir;
/**
* Sets the class path
*
* @param classPath classpath
*/
public void setClassPath(String classPath)
{
this.classPath = classPath;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet()
throws Exception
{
ClassPathResource resource = new ClassPathResource(classPath);
fileDir = resource.getFile();
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getBasePath()
*/
public String getBasePath()
{
return "classpath:" + classPath;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getDescriptionDocumentPaths()
*/
public String[] getDescriptionDocumentPaths()
{
String[] paths;
try
{
int filePathLength = fileDir.getAbsolutePath().length() +1;
List<String> documentPaths = new ArrayList<String>();
Resource[] resources = resolver.getResources("classpath*:" + classPath + "/**/*_desc.xml");
for (Resource resource : resources)
{
if (resource instanceof FileSystemResource)
{
String documentPath = resource.getFile().getAbsolutePath().substring(filePathLength);
documentPath = documentPath.replace('\\', '/');
documentPaths.add(documentPath);
}
}
paths = documentPaths.toArray(new String[documentPaths.size()]);
}
catch(IOException e)
{
// Note: Ignore: no service description documents found
paths = new String[0];
}
return paths;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getDescriptionDocument(java.lang.String)
*/
public InputStream getDescriptionDocument(String documentPath)
throws IOException
{
File document = new File(fileDir, documentPath);
if (!document.exists())
{
throw new IOException("Description document " + documentPath + " does not exist.");
}
return new FileInputStream(document);
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getTemplateLoader()
*/
public TemplateLoader getTemplateLoader()
{
FileTemplateLoader loader = null;
try
{
loader = new FileTemplateLoader(fileDir);
}
catch (IOException e)
{
// Note: Can't establish loader, so return null
}
return loader;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getScriptLoader()
*/
public ScriptLoader getScriptLoader()
{
return new ClassPathScriptLoader();
}
/**
* Class path based script loader
*
* @author davidc
*/
private class ClassPathScriptLoader implements ScriptLoader
{
/* (non-Javadoc)
* @see org.alfresco.web.scripts.ScriptLoader#getScriptLocation(java.lang.String)
*/
public ScriptLocation getScriptLocation(String path)
{
ScriptLocation location = null;
File scriptPath = new File(fileDir, path);
if (scriptPath.exists())
{
location = new ClassPathScriptLocation(scriptPath);
}
return location;
}
}
/**
* Class path script location
*
* @author davidc
*/
private static class ClassPathScriptLocation implements ScriptLocation
{
private File location;
/**
* Construct
*
* @param location
*/
public ClassPathScriptLocation(File location)
{
this.location = location;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.repository.ScriptLocation#getInputStream()
*/
public InputStream getInputStream()
{
try
{
return new FileInputStream(location);
}
catch (FileNotFoundException e)
{
throw new WebScriptException("Unable to retrieve input stream for script " + location.getAbsolutePath());
}
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.repository.ScriptLocation#getReader()
*/
public Reader getReader()
{
return new InputStreamReader(getInputStream());
}
@Override
public String toString()
{
return location.getAbsolutePath();
}
}
}

View File

@@ -0,0 +1,221 @@
/*
* 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.scripts;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.repo.jscript.Node;
import org.alfresco.repo.jscript.ScriptableHashMap;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.TemplateException;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.Wrapper;
/**
* Script/template driven based implementation of an Web Script
*
* @author davidc
*/
public class DeclarativeWebScript extends AbstractWebScript
{
// Logger
private static final Log logger = LogFactory.getLog(DeclarativeWebScript.class);
private String baseTemplatePath;
private ScriptLocation executeScript;
/* (non-Javadoc)
* @see org.alfresco.web.scripts.AbstractWebScript#init(org.alfresco.web.scripts.WebScriptRegistry)
*/
@Override
public void init(WebScriptRegistry apiRegistry)
{
super.init(apiRegistry);
baseTemplatePath = getDescription().getId().replace('.', '/');
// Test for "execute" script
String scriptPath = baseTemplatePath + ".js";
executeScript = getWebScriptRegistry().getScriptProcessor().findScript(scriptPath);
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScript#execute(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/
final public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException
{
// construct data model for template
Map<String, Object> model = executeImpl(req, res);
if (model == null)
{
model = new HashMap<String, Object>(7, 1.0f);
}
// execute script if it exists
if (executeScript != null)
{
if (logger.isDebugEnabled())
logger.debug("Executing script " + executeScript);
Map<String, Object> scriptModel = createScriptModel(req, res, model);
// add return model allowing script to add items to template model
Map<String, Object> returnModel = new ScriptableHashMap<String, Object>();
scriptModel.put("model", returnModel);
executeScript(executeScript, scriptModel);
mergeScriptModelIntoTemplateModel(returnModel, model);
}
// process requested format
String format = req.getFormat();
if (format == null || format.length() == 0)
{
format = getDescription().getDefaultFormat();
}
String mimetype = getWebScriptRegistry().getFormatRegistry().getMimeType(req.getAgent(), format);
if (mimetype == null)
{
throw new WebScriptException("Web Script format '" + format + "' is not registered");
}
// render output
res.setContentType(mimetype + ";charset=UTF-8");
if (logger.isDebugEnabled())
logger.debug("Response content type: " + mimetype);
try
{
Map<String, Object> templateModel = createTemplateModel(req, res, model);
renderFormatTemplate(format, templateModel, res.getWriter());
}
catch(TemplateException e)
{
throw new WebScriptException("Failed to process format '" + format + "'", e);
}
}
/**
* Merge script generated model into template-ready model
*
* @param scriptModel script model
* @param templateModel template model
*/
@SuppressWarnings("unchecked")
final private void mergeScriptModelIntoTemplateModel(Map<String, Object> scriptModel, Map<String, Object> templateModel)
{
for (Map.Entry<String, Object> entry : scriptModel.entrySet())
{
// retrieve script model value
Object value = entry.getValue();
// convert from js to java, if required
if (value instanceof Wrapper)
{
value = ((Wrapper)value).unwrap();
}
else if (value instanceof NativeArray)
{
value = Context.jsToJava(value, Object[].class);
}
// convert script node to template node, if required
if (value instanceof Node)
{
value = new TemplateNode(((Node)value).getNodeRef(), getServiceRegistry(), null);
}
else if (value instanceof Collection)
{
Collection coll = (Collection)value;
Collection templateColl = new ArrayList(coll.size());
for (Object object : coll)
{
if (value instanceof Node)
{
templateColl.add(new TemplateNode(((Node)object).getNodeRef(), getServiceRegistry(), null));
}
else
{
templateColl.add(object);
}
}
value = templateColl;
}
else if (value instanceof Node[])
{
Node[] nodes = (Node[])value;
TemplateNode[] templateNodes = new TemplateNode[nodes.length];
int i = 0;
for (Node node : nodes)
{
templateNodes[i++] = new TemplateNode(node.getNodeRef(), getServiceRegistry(), null);
}
value = templateNodes;
}
templateModel.put(entry.getKey(), value);
}
}
/**
* Execute custom Java logic
*
* @param req Web Script request
* @param res Web Script response
* @return custom service model
*/
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res)
{
return null;
}
/**
* Render a template (of given format) to the Web Script Response
*
* @param format template format (null, default format)
* @param model data model to render
* @param writer where to output
*/
final protected void renderFormatTemplate(String format, Map<String, Object> model, Writer writer)
{
format = (format == null) ? "" : format;
String templatePath = baseTemplatePath + "_" + format + ".ftl";
if (logger.isDebugEnabled())
logger.debug("Rendering template '" + templatePath + "'");
renderTemplate(templatePath, model, writer);
}
}

View File

@@ -0,0 +1,574 @@
/*
* 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.scripts;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.ServletContext;
import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication;
import org.alfresco.web.scripts.WebScriptDescription.RequiredTransaction;
import org.alfresco.web.scripts.WebScriptDescription.URI;
import org.aopalliance.intercept.MethodInterceptor;
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.aop.framework.ProxyFactory;
import org.springframework.aop.support.RegexpMethodPointcutAdvisor;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.context.ServletContextAware;
/**
* Registry of declarative (scripted/template driven) Web Scripts
*
* @author davidc
*/
public class DeclarativeWebScriptRegistry implements WebScriptRegistry, ApplicationContextAware, ServletContextAware
{
// Logger
private static final Log logger = LogFactory.getLog(DeclarativeWebScriptRegistry.class);
private ApplicationContext applicationContext;
private ServletContext servletContext;
private String defaultWebScript;
private MethodInterceptor authenticator;
private MethodInterceptor serviceLogger;
private MethodInterceptor serviceTransaction;
private FormatRegistry formatRegistry;
private WebScriptStorage storage;
// map of web scripts by id
// NOTE: The map is sorted by id (ascending order)
private Map<String, WebScript> webscriptsById = new TreeMap<String, WebScript>();
// map of web scripts by url
// NOTE: The map is sorted by url (descending order)
private Map<String, WebScript> webscriptsByURL = new TreeMap<String, WebScript>(Collections.reverseOrder());
//
// Initialisation
//
/**
* Sets the available Web Script Stores
*
* @param storage
*/
public void setStorage(WebScriptStorage storage)
{
this.storage = storage;
}
/**
* Sets the web script authenticator
*
* @param authenticator
*/
public void setAuthenticator(MethodInterceptor authenticator)
{
this.authenticator = authenticator;
}
/**
* Sets the web script logger
*
* @param serviceLogger
*/
public void setServiceLogger(MethodInterceptor serviceLogger)
{
this.serviceLogger = serviceLogger;
}
/**
* Sets the service transaction
*
* @param serviceTransaction
*/
public void setServiceTransaction(MethodInterceptor serviceTransaction)
{
this.serviceTransaction = serviceTransaction;
}
/**
* Sets the default service implementation bean
*
* @param defaultWebScript
*/
public void setDefaultWebScript(String defaultWebScript)
{
this.defaultWebScript = defaultWebScript;
}
/**
* Sets the response format registry
*
* @param formatRegistry
*/
public void setFormatRegistry(FormatRegistry formatRegistry)
{
this.formatRegistry = formatRegistry;
}
/* (non-Javadoc)
* @see org.springframework.web.context.ServletContextAware#setServletContext(javax.servlet.ServletContext)
*/
public void setServletContext(ServletContext context)
{
this.servletContext = context;
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
/**
* Initialise Web Scripts
*
* Note: Each invocation of this method resets the list of the services
*/
public void initWebScripts()
{
if (logger.isDebugEnabled())
logger.debug("Initialising Web Scripts");
// clear currently registered services
webscriptsById.clear();
webscriptsByURL.clear();
// register services
for (WebScriptStore apiStore : storage.getStores())
{
if (logger.isDebugEnabled())
logger.debug("Locating Web Scripts within " + apiStore.getBasePath());
String basePath = apiStore.getBasePath();
String[] serviceDescPaths = apiStore.getDescriptionDocumentPaths();
for (String serviceDescPath : serviceDescPaths)
{
// build service description
WebScriptDescription serviceDesc = null;
InputStream serviceDescIS = null;
try
{
serviceDescIS = apiStore.getDescriptionDocument(serviceDescPath);
serviceDesc = createDescription(basePath, serviceDescPath, serviceDescIS);
}
catch(IOException e)
{
throw new WebScriptException("Failed to read Web Script description document " + apiStore.getBasePath() + serviceDescPath, e);
}
finally
{
try
{
if (serviceDescIS != null) serviceDescIS.close();
}
catch(IOException e)
{
// NOTE: ignore close exception
}
}
// determine if service description has been registered
String id = serviceDesc.getId();
if (webscriptsById.containsKey(id))
{
// move to next service
if (logger.isDebugEnabled())
{
WebScript existingService = webscriptsById.get(id);
logger.debug("Web Script description document " + serviceDesc.getSourceLocation() + " overridden by " + existingService.getDescription().getSourceLocation());
}
continue;
}
// construct service implementation
String serviceImplName = (applicationContext.containsBean("webscript." + id)) ? "webscript." + id : defaultWebScript;
AbstractWebScript serviceImpl = (AbstractWebScript)applicationContext.getBean(serviceImplName);
serviceImpl.setDescription(serviceDesc);
serviceImpl.init(this);
// wrap service implementation in appropriate interceptors (e.g. authentication)
WebScript serviceImplIF = (WebScript)serviceImpl;
if (serviceLogger != null && serviceTransaction != null && authenticator != null)
{
ProxyFactory authFactory = new ProxyFactory();
authFactory.addInterface(WebScript.class);
authFactory.setTarget(serviceImplIF);
// logging interceptor
if (serviceLogger != null)
{
RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor(".*execute", serviceLogger);
authFactory.addAdvisor(advisor);
}
// transaction interceptor
if (serviceDesc.getRequiredTransaction() != RequiredTransaction.none)
{
if (serviceTransaction == null)
{
throw new WebScriptException("Web Script Transaction not specified");
}
RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor(".*execute", serviceTransaction);
authFactory.addAdvisor(advisor);
}
// authentication interceptor
if (serviceDesc.getRequiredAuthentication() != RequiredAuthentication.none)
{
if (authenticator == null)
{
throw new WebScriptException("Web Script Authenticator not specified");
}
RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor(".*execute", authenticator);
authFactory.addAdvisor(advisor);
}
serviceImplIF = (WebScript)authFactory.getProxy();
}
if (logger.isDebugEnabled())
logger.debug("Found Web Script " + serviceDescPath + " (id: " + id + ", impl: " + serviceImplName + ", auth: " + serviceDesc.getRequiredAuthentication() + ", trx: " + serviceDesc.getRequiredTransaction() + ")");
// register service and its urls
webscriptsById.put(id, serviceImplIF);
for (URI uri : serviceDesc.getURIs())
{
// establish static part of url template
String uriTemplate = uri.getURI();
int queryArgIdx = uriTemplate.indexOf('?');
if (queryArgIdx != -1)
{
uriTemplate = uriTemplate.substring(0, queryArgIdx);
}
int tokenIdx = uriTemplate.indexOf('{');
if (tokenIdx != -1)
{
uriTemplate = uriTemplate.substring(0, tokenIdx);
}
// index service by static part of url (ensuring no other service has already claimed the url)
String uriIdx = serviceDesc.getMethod().toString() + ":" + uriTemplate;
if (webscriptsByURL.containsKey(uriIdx))
{
WebScript existingService = webscriptsByURL.get(uriIdx);
if (!existingService.getDescription().getId().equals(serviceDesc.getId()))
{
String msg = "Web Script document " + serviceDesc.getSourceLocation() + " is attempting to define the url '" + uriIdx + "' already defined by " + existingService.getDescription().getSourceLocation();
throw new WebScriptException(msg);
}
}
else
{
webscriptsByURL.put(uriIdx, serviceImplIF);
if (logger.isDebugEnabled())
logger.debug("Registered Web Script URL '" + uriIdx + "'");
}
}
}
}
if (logger.isDebugEnabled())
logger.debug("Registered " + webscriptsById.size() + " Web Scripts; " + webscriptsByURL.size() + " URLs");
}
/**
* Create an Web Script Description
*
* @param basePath
* @param serviceDescPath
* @param serviceDoc
*
* @return web script service description
*/
private WebScriptDescription createDescription(String basePath, String serviceDescPath, InputStream serviceDoc)
{
SAXReader reader = new SAXReader();
try
{
Document document = reader.read(serviceDoc);
Element rootElement = document.getRootElement();
if (!rootElement.getName().equals("webscript"))
{
throw new WebScriptException("Expected <webscript> root element - found <" + rootElement.getName() + ">");
}
// retrieve id
String id = serviceDescPath.substring(0, serviceDescPath.lastIndexOf("_desc.xml")).replace('/', '.');
// retrieve http method
int methodIdx = id.lastIndexOf('_');
if (methodIdx == id.length() - 1)
{
throw new WebScriptException("Unable to establish HTTP Method from web script description: naming convention must be <name>_<method>_desc.xml");
}
String method = id.substring(id.lastIndexOf('_') + 1).toUpperCase();
// retrieve short name
Element shortNameElement = rootElement.element("shortname");
if (shortNameElement == null || shortNameElement.getTextTrim() == null || shortNameElement.getTextTrim().length() == 0)
{
throw new WebScriptException("Expected <shortname> value");
}
String shortName = shortNameElement.getTextTrim();
// retrieve description
Element descriptionElement = rootElement.element("description");
String description = descriptionElement.getTextTrim();
// retrieve urls
List urlElements = rootElement.elements("url");
if (urlElements == null || urlElements.size() == 0)
{
throw new WebScriptException("Expected at one <url> element");
}
List<WebScriptDescription.URI> uris = new ArrayList<WebScriptDescription.URI>();
Iterator iterElements = urlElements.iterator();
while(iterElements.hasNext())
{
// retrieve url element
Element urlElement = (Element)iterElements.next();
// retrieve url mimetype
String format = urlElement.attributeValue("format");
if (format == null)
{
// default to unspecified format
format = "";
}
// retrieve url template
String template = urlElement.attributeValue("template");
if (template == null || template.length() == 0)
{
throw new WebScriptException("Expected template attribute on <url> element");
}
WebScriptDescriptionImpl.URIImpl uriImpl = new WebScriptDescriptionImpl.URIImpl();
uriImpl.setFormat(format);
uriImpl.setUri(template);
uris.add(uriImpl);
}
// retrieve authentication
RequiredAuthentication reqAuth = RequiredAuthentication.none;
Element authElement = rootElement.element("authentication");
if (authElement != null)
{
String reqAuthStr = authElement.getTextTrim();
if (reqAuthStr == null || reqAuthStr.length() == 0)
{
throw new WebScriptException("Expected <authentication> value");
}
reqAuth = RequiredAuthentication.valueOf(reqAuthStr);
if (reqAuth == null)
{
throw new WebScriptException("Authentication '" + reqAuthStr + "' is not a valid value");
}
}
// retrieve transaction
RequiredTransaction reqTrx = (reqAuth == RequiredAuthentication.none) ? RequiredTransaction.none : RequiredTransaction.required;
Element trxElement = rootElement.element("transaction");
if (trxElement != null)
{
String reqTrxStr = trxElement.getTextTrim();
if (reqTrxStr == null || reqTrxStr.length() == 0)
{
throw new WebScriptException("Expected <transaction> value");
}
reqTrx = RequiredTransaction.valueOf(reqTrxStr);
if (reqTrx == null)
{
throw new WebScriptException("Transaction '" + reqTrxStr + "' is not a valid value");
}
}
// construct service description
WebScriptDescriptionImpl serviceDesc = new WebScriptDescriptionImpl();
serviceDesc.setSourceLocation(basePath + "/" + serviceDescPath);
serviceDesc.setId(id);
serviceDesc.setShortName(shortName);
serviceDesc.setDescription(description);
serviceDesc.setRequiredAuthentication(reqAuth);
serviceDesc.setRequiredTransaction(reqTrx);
serviceDesc.setMethod(method);
serviceDesc.setUris(uris.toArray(new WebScriptDescription.URI[uris.size()]));
serviceDesc.setDefaultFormat(uris.get(0).getFormat());
return serviceDesc;
}
catch(DocumentException e)
{
throw new WebScriptException("Failed to parse web script description document " + serviceDescPath, e);
}
catch(WebScriptException e)
{
throw new WebScriptException("Failed to parse web script description document " + serviceDescPath, e);
}
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptRegistry#getWebScripts()
*/
public Collection<WebScript> getWebScripts()
{
return webscriptsById.values();
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptRegistry#getWebScript(java.lang.String)
*/
public WebScript getWebScript(String id)
{
return webscriptsById.get(id);
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptRegistry#findWebScript(java.lang.String, java.lang.String)
*/
public WebScriptMatch findWebScript(String method, String uri)
{
// TODO: Replace with more efficient approach
DeclarativeWebScriptMatch apiServiceMatch = null;
String match = method.toString().toUpperCase() + ":" + uri;
for (Map.Entry<String, WebScript> service : webscriptsByURL.entrySet())
{
String indexedPath = service.getKey();
if (match.startsWith(indexedPath))
{
String matchPath = indexedPath.substring(indexedPath.indexOf(':') +1);
apiServiceMatch = new DeclarativeWebScriptMatch(matchPath, service.getValue());
break;
}
}
return apiServiceMatch;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptRegistry#getContext()
*/
public ServletContext getContext()
{
return servletContext;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptRegistry#getFormatRegistry()
*/
public FormatRegistry getFormatRegistry()
{
return this.formatRegistry;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptRegistry#getTemplateProcessor()
*/
public TemplateProcessor getTemplateProcessor()
{
return this.storage.getTemplateProcessor();
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptRegistry#getScriptProcessor()
*/
public ScriptProcessor getScriptProcessor()
{
return this.storage.getScriptProcessor();
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptRegistry#reset()
*/
public void reset()
{
getTemplateProcessor().resetCache();
getScriptProcessor().resetCache();
initWebScripts();
}
/**
* Web Script Match
*
* @author davidc
*/
public static class DeclarativeWebScriptMatch implements WebScriptMatch
{
private String path;
private WebScript service;
/**
* Construct
*
* @param path
* @param service
*/
public DeclarativeWebScriptMatch(String path, WebScript service)
{
this.path = path;
this.service = service;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptMatch#getPath()
*/
public String getPath()
{
return path;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptMatch#getWebScript()
*/
public WebScript getWebScript()
{
return service;
}
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.scripts;
import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
/**
* A map of mimetypes indexed by format.
*
* @author davidc
*/
public class FormatMap implements InitializingBean
{
private FormatRegistry registry;
private String agent;
private Map<String, String> formats;
/**
* Sets the Format Registry
*
* @param registry
*/
public void setRegistry(FormatRegistry registry)
{
this.registry = registry;
}
/**
* Sets the User Agent for which the formats apply
*
* @param agent
*/
public void setAgent(String agent)
{
this.agent = agent;
}
/**
* Sets the formats
*
* @param formats
*/
public void setFormats(Map<String, String> formats)
{
this.formats = formats;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception
{
// Add formats to format registry
registry.addFormats(agent, formats);
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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.scripts;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Maintains a registry of mimetypes (indexed by format and user agent)
*
* @author davidc
*/
public class FormatRegistry
{
// Logger
private static final Log logger = LogFactory.getLog(FormatRegistry.class);
private Map<String, String> formats;
private Map<String, String> mimetypes;
private Map<String, Map<String, String>> agentFormats;
private Map<String, Map<String, String>> agentMimetypes;
/**
* Construct
*/
public FormatRegistry()
{
formats = new HashMap<String, String>();
mimetypes = new HashMap<String, String>();
agentFormats = new HashMap<String, Map<String, String>>();
agentMimetypes = new HashMap<String, Map<String, String>>();
}
/**
* Add formats
*
* @param agent
* @param formatsToAdd
*/
public void addFormats(String agent, Map<String, String> formatsToAdd)
{
// retrieve formats list for agent
Map<String, String> formatsForAgent = formats;
Map<String, String> mimetypesForAgent = mimetypes;
if (agent != null)
{
formatsForAgent = agentFormats.get(agent);
if (formatsForAgent == null)
{
formatsForAgent = new HashMap<String, String>();
mimetypesForAgent = new HashMap<String, String>();
agentFormats.put(agent, formatsForAgent);
agentMimetypes.put(agent, mimetypesForAgent);
}
}
for (Map.Entry<String, String> entry : formatsToAdd.entrySet())
{
if (logger.isWarnEnabled())
{
String mimetype = formatsForAgent.get(entry.getKey());
if (mimetype != null)
{
logger.warn("Replacing mime type '" + mimetype + "' with '" + entry.getValue() + "' for Web Script format '" + entry.getKey() + "' (agent: " + agent + ")");
}
}
formatsForAgent.put(entry.getKey(), entry.getValue());
mimetypesForAgent.put(entry.getValue(), entry.getKey());
if (logger.isDebugEnabled())
logger.debug("Registered Web Script format '" + entry.getKey() + "' with mime type '" + entry.getValue() + "' (agent: " + agent + ")");
}
}
/**
* Gets the mimetype for the specified user agent and format
*
* @param agent
* @param format
* @return mimetype (or null, if one is not registered)
*/
public String getMimeType(String agent, String format)
{
String mimetype = null;
if (agent != null)
{
Map<String, String> formatsForAgent = agentFormats.get(agent);
if (formatsForAgent != null)
{
mimetype = formatsForAgent.get(format);
}
}
if (mimetype == null)
{
mimetype = formats.get(format);
}
return mimetype;
}
/**
* Gets the format for the specified user agent and mimetype
*
* @param agent
* @param mimetype
* @return format (or null, if one is not registered)
*/
public String getFormat(String agent, String mimetype)
{
String format = null;
if (agent != null)
{
Map<String, String> mimetypesForAgent = agentMimetypes.get(agent);
if (mimetypesForAgent != null)
{
format = mimetypesForAgent.get(mimetype);
}
}
if (format == null)
{
format = mimetypes.get(mimetype);
}
return format;
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.scripts;
import org.alfresco.service.cmr.repository.ScriptLocation;
/**
* Composite script loader
*
* @author davidc
*/
public class MultiScriptLoader implements ScriptLoader
{
private ScriptLoader[] loaders;
/**
* Construct
*
* @param loaders
*/
public MultiScriptLoader(ScriptLoader[] loaders)
{
this.loaders = loaders;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.ScriptLoader#getScriptLocation(java.lang.String)
*/
public ScriptLocation getScriptLocation(String path)
{
ScriptLocation location = null;
for (ScriptLoader loader : loaders)
{
location = loader.getScriptLocation(path);
if (location != null)
{
break;
}
}
return location;
}
}

View File

@@ -0,0 +1,578 @@
/*
* 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.scripts;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.AbstractLifecycleBean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import freemarker.cache.TemplateLoader;
/**
* Repository based Web Script Store
*
* @author davidc
*/
public class RepoStore implements WebScriptStore, ApplicationContextAware, ApplicationListener
{
private ProcessorLifecycle lifecycle = new ProcessorLifecycle();
protected StoreRef repoStore;
protected String repoPath;
protected NodeRef baseNodeRef;
protected String baseDir;
// dependencies
protected TransactionService transactionService;
protected SearchService searchService;
protected NodeService nodeService;
protected ContentService contentService;
protected NamespaceService namespaceService;
/**
* Sets transaction service
*
* @param transactionService
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Sets the search service
*
* @param searchService
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* Sets the node service
*
* @param nodeService
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Sets the content service
*
* @param contentService
*/
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
/**
* Sets the namespace service
*
* @param namespaceService
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* Sets the repo store
*
* @param repoStore
*/
public void setStore(String repoStore)
{
this.repoStore = new StoreRef(repoStore);
}
/**
* Sets the repo path
*
* @param repoPath repoPath
*/
public void setPath(String repoPath)
{
this.repoPath = repoPath;
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
lifecycle.setApplicationContext(applicationContext);
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(ApplicationEvent event)
{
lifecycle.onApplicationEvent(event);
}
/**
* Hooks into Spring Application Lifecycle
*/
private class ProcessorLifecycle extends AbstractLifecycleBean
{
@Override
protected void onBootstrap(ApplicationEvent event)
{
init();
}
@Override
protected void onShutdown(ApplicationEvent event)
{
}
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
protected void init()
{
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
public Object doWork() throws Exception
{
return TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<Object>()
{
public Object doWork() throws Exception
{
String query = "PATH:\"" + repoPath + "\"";
ResultSet resultSet = searchService.query(repoStore, SearchService.LANGUAGE_LUCENE, query);
if (resultSet.length() == 0)
{
throw new WebScriptException("Unable to locate repository path " + repoStore.toString() + repoPath);
}
if (resultSet.length() > 1)
{
throw new WebScriptException("Multiple repository paths found for " + repoStore.toString() + repoPath);
}
baseNodeRef = resultSet.getNodeRef(0);
baseDir = getPath(baseNodeRef);
return null;
}
});
}
}, AuthenticationUtil.getSystemUserName());
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getBasePath()
*/
public String getBasePath()
{
return repoStore.toString() + repoPath;
}
/**
* Gets the display path for the specified node
*
* @param nodeRef
* @return display path
*/
protected String getPath(NodeRef nodeRef)
{
return nodeService.getPath(nodeRef).toDisplayPath(nodeService) + "/" + nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
}
/**
* Gets the node ref for the specified path within this repo store
*
* @param documentPath
* @return node ref
*/
protected NodeRef findNodeRef(String documentPath)
{
StringBuilder xpath = new StringBuilder(documentPath.length() << 1);
for (StringTokenizer t = new StringTokenizer(documentPath, "/"); t.hasMoreTokens(); /**/)
{
if (xpath.length() != 0)
{
xpath.append('/');
}
xpath.append("*[@cm:name='").append(t.nextToken()).append("']");
}
List<NodeRef> nodes = searchService.selectNodes(baseNodeRef, xpath.toString(), null, namespaceService, false);
return (nodes.size() == 1) ? nodes.get(0) : null;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getDescriptionDocumentPaths()
*/
public String[] getDescriptionDocumentPaths()
{
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<String[]>()
{
public String[] doWork() throws Exception
{
return TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<String[]>()
{
public String[] doWork() throws Exception
{
int baseDirLength = baseDir.length() +1;
List<String> documentPaths = new ArrayList<String>();
String query = "PATH:\"" + repoPath + "//*\" AND @cm\\:name:\"*_desc.xml\"";
ResultSet resultSet = searchService.query(repoStore, SearchService.LANGUAGE_LUCENE, query);
List<NodeRef> nodes = resultSet.getNodeRefs();
for (NodeRef nodeRef : nodes)
{
String nodeDir = getPath(nodeRef);
String documentPath = nodeDir.substring(baseDirLength);
documentPaths.add(documentPath);
}
return documentPaths.toArray(new String[documentPaths.size()]);
}
});
}
}, AuthenticationUtil.getSystemUserName());
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getDescriptionDocument(java.lang.String)
*/
public InputStream getDescriptionDocument(final String documentPath)
throws IOException
{
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<InputStream>()
{
public InputStream doWork() throws Exception
{
return TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<InputStream>()
{
public InputStream doWork() throws Exception
{
NodeRef nodeRef = findNodeRef(documentPath);
if (nodeRef == null)
{
throw new IOException("Description document " + documentPath + " does not exist.");
}
ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
return reader.getContentInputStream();
}
});
}
}, AuthenticationUtil.getSystemUserName());
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getTemplateLoader()
*/
public TemplateLoader getTemplateLoader()
{
return new RepoTemplateLoader();
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptStore#getScriptLoader()
*/
public ScriptLoader getScriptLoader()
{
return new RepoScriptLoader();
}
/**
* Repository path based template loader
*
* @author davidc
*/
private class RepoTemplateLoader implements TemplateLoader
{
/* (non-Javadoc)
* @see freemarker.cache.TemplateLoader#findTemplateSource(java.lang.String)
*/
public Object findTemplateSource(final String name)
throws IOException
{
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
public Object doWork() throws Exception
{
return TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<Object>()
{
public Object doWork() throws Exception
{
RepoTemplateSource source = null;
NodeRef nodeRef = findNodeRef(name);
if (nodeRef != null)
{
source = new RepoTemplateSource(nodeRef);
}
return source;
}
});
}
}, AuthenticationUtil.getSystemUserName());
}
/* (non-Javadoc)
* @see freemarker.cache.TemplateLoader#getLastModified(java.lang.Object)
*/
public long getLastModified(Object templateSource)
{
return ((RepoTemplateSource)templateSource).lastModified();
}
/* (non-Javadoc)
* @see freemarker.cache.TemplateLoader#getReader(java.lang.Object, java.lang.String)
*/
public Reader getReader(Object templateSource, String encoding) throws IOException
{
return ((RepoTemplateSource)templateSource).getReader();
}
/* (non-Javadoc)
* @see freemarker.cache.TemplateLoader#closeTemplateSource(java.lang.Object)
*/
public void closeTemplateSource(Object arg0) throws IOException
{
}
}
/**
* Repository (content) node template source
*
* @author davidc
*/
private class RepoTemplateSource
{
protected final NodeRef nodeRef;
/**
* Construct
*
* @param ref
*/
private RepoTemplateSource(NodeRef ref)
{
this.nodeRef = ref;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object o)
{
if (o instanceof RepoTemplateSource)
{
return nodeRef.equals(((RepoTemplateSource)o).nodeRef);
}
else
{
return false;
}
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
public int hashCode()
{
return nodeRef.hashCode();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString()
{
return nodeRef.toString();
}
/**
* Gets the last modified time of the content
*
* @return last modified time
*/
public long lastModified()
{
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Long>()
{
public Long doWork() throws Exception
{
return TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<Long>()
{
public Long doWork() throws Exception
{
ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
return reader.getLastModified();
}
});
}
}, AuthenticationUtil.getSystemUserName());
}
/**
* Gets the content reader
*
* @return content reader
* @throws IOException
*/
public Reader getReader() throws IOException
{
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Reader>()
{
public Reader doWork() throws Exception
{
return TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<Reader>()
{
public Reader doWork() throws Exception
{
ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
return new InputStreamReader(reader.getContentInputStream());
}
});
}
}, AuthenticationUtil.getSystemUserName());
}
}
/**
* Repository path based script loader
*
* @author davidc
*/
private class RepoScriptLoader implements ScriptLoader
{
/* (non-Javadoc)
* @see org.alfresco.web.scripts.ScriptLoader#getScriptLocation(java.lang.String)
*/
public ScriptLocation getScriptLocation(final String path)
{
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<ScriptLocation>()
{
public ScriptLocation doWork() throws Exception
{
return TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<ScriptLocation>()
{
public ScriptLocation doWork() throws Exception
{
ScriptLocation location = null;
NodeRef nodeRef = findNodeRef(path);
if (nodeRef != null)
{
location = new RepoScriptLocation(path, nodeRef);
}
return location;
}
});
}
}, AuthenticationUtil.getSystemUserName());
}
}
/**
* Repo path script location
*
* @author davidc
*/
private class RepoScriptLocation implements ScriptLocation
{
protected String path;
protected NodeRef nodeRef;
/**
* Construct
*
* @param location
*/
public RepoScriptLocation(String path, NodeRef nodeRef)
{
this.path = path;
this.nodeRef = nodeRef;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.repository.ScriptLocation#getInputStream()
*/
public InputStream getInputStream()
{
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<InputStream>()
{
public InputStream doWork() throws Exception
{
return TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<InputStream>()
{
public InputStream doWork() throws Exception
{
ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
return reader.getContentInputStream();
}
});
}
}, AuthenticationUtil.getSystemUserName());
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.repository.ScriptLocation#getReader()
*/
public Reader getReader()
{
return new InputStreamReader(getInputStream());
}
@Override
public String toString()
{
return repoStore + "/" + baseDir + "/" + path;
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.scripts;
import org.alfresco.service.cmr.repository.ScriptLocation;
/**
* Locate a script
*
* @author davidc
*/
public interface ScriptLoader
{
/**
* Gets the script location for the script at the specified path
*
* @param path path within api store
* @return script location (or null, if script does not exist at path)
*/
ScriptLocation getScriptLocation(String path);
}

View File

@@ -0,0 +1,119 @@
/*
* 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.scripts;
import java.util.Map;
import org.alfresco.service.cmr.repository.ScriptException;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.ScriptService;
/**
* Script Processor for use in Web Script
*
* @author davidc
*/
public class ScriptProcessor
{
// dependencies
private ScriptService scriptService;
// script loader
private ScriptLoader scriptLoader;
/**
* Sets the script service
*
* @param scriptService
*/
public void setScriptService(ScriptService scriptService)
{
this.scriptService = scriptService;
}
/**
* Sets the script loader
*
* @param scriptLoader
*/
public void setScriptLoader(ScriptLoader scriptLoader)
{
this.scriptLoader = scriptLoader;
}
/**
* Find a script at the specified path (within registered Web Script stores)
*
* @param path script path
* @return script location (or null, if not found)
*/
public ScriptLocation findScript(String path)
{
return scriptLoader.getScriptLocation(path);
}
/**
* Execute script
*
* @param path script path
* @param model model
* @return script result
* @throws ScriptException
*/
public Object executeScript(String path, Map<String, Object> model)
throws ScriptException
{
// locate script within web script stores
ScriptLocation scriptLocation = findScript(path);
if (scriptLocation == null)
{
throw new WebScriptException("Unable to locate script " + path);
}
// execute script
return executeScript(scriptLocation, model);
}
/**
* Execute script
*
* @param location script location
* @param model model
* @return script result
*/
public Object executeScript(ScriptLocation location, Map<String, Object> model)
{
return scriptService.executeScript(location, model);
}
/**
* Reset script cache
*/
public void resetCache()
{
// NOOP
}
}

View File

@@ -0,0 +1,169 @@
/*
* 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.scripts;
import org.alfresco.service.descriptor.Descriptor;
/**
* Script / Template Model representing Repository Server meta-data
*
* @author davidc
*/
public class ServerModel
{
private Descriptor serverDescriptor;
/**
* Construct
*
* @param serverDescriptor
*/
/*package*/ ServerModel(Descriptor serverDescriptor)
{
this.serverDescriptor = serverDescriptor;
}
/**
* Gets the major version number, e.g. <u>1</u>.2.3
*
* @return major version number
*/
public String getVersionMajor()
{
return serverDescriptor.getVersionMajor();
}
public String jsGet_versionMajor()
{
return getVersionMajor();
}
/**
* Gets the minor version number, e.g. 1.<u>2</u>.3
*
* @return minor version number
*/
public String getVersionMinor()
{
return serverDescriptor.getVersionMinor();
}
public String jsGet_versionMinor()
{
return getVersionMinor();
}
/**
* Gets the version revision number, e.g. 1.2.<u>3</u>
*
* @return revision number
*/
public String getVersionRevision()
{
return serverDescriptor.getVersionRevision();
}
public String jsGet_versionRevision()
{
return getVersionRevision();
}
/**
* Gets the version label
*
* @return the version label
*/
public String getVersionLabel()
{
return serverDescriptor.getVersionLabel();
}
public String jsGet_versionLabel()
{
return getVersionLabel();
}
/**
* Gets the build number
*
* @return the build number i.e. build-1
*/
public String getVersionBuild()
{
return serverDescriptor.getVersionBuild();
}
public String jsGet_versionBuild()
{
return getVersionBuild();
}
/**
* Gets the full version number
*
* @return full version number as major.minor.revision (label)
*/
public String getVersion()
{
return serverDescriptor.getVersion();
}
public String jsGet_version()
{
return getVersion();
}
/**
* Gets the edition
*
* @return the edition
*/
public String getEdition()
{
return serverDescriptor.getEdition();
}
public String jsGet_edition()
{
return getEdition();
}
/**
* Gets the schema number
*
* @return a positive integer
*/
public int getSchema()
{
return serverDescriptor.getSchema();
}
public int jsGet_schema()
{
return getSchema();
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.scripts;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* System Authentication Interceptor
*
* @author davidc
*/
public class SystemAuthenticator implements MethodInterceptor
{
// Logger
private static final Log logger = LogFactory.getLog(SystemAuthenticator.class);
/* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
public Object invoke(MethodInvocation invocation)
throws Throwable
{
String currentUser = null;
Object retVal = null;
try
{
//
// Determine if user already authenticated
//
currentUser = AuthenticationUtil.getCurrentUserName();
if (logger.isDebugEnabled())
logger.debug("Current authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
//
// Force system user
//
if (logger.isDebugEnabled())
logger.debug("Authenticating as System");
AuthenticationUtil.setSystemUserAsCurrentUser();
//
// Invoke service
//
retVal = invocation.proceed();
}
finally
{
AuthenticationUtil.clearCurrentSecurityContext();
if (currentUser != null)
{
AuthenticationUtil.setCurrentUser(currentUser);
}
if (logger.isDebugEnabled())
logger.debug("Authentication reset: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
}
return retVal;
}
}

View File

@@ -0,0 +1,161 @@
/*
* 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.scripts;
import org.alfresco.repo.template.FreeMarkerProcessor;
import org.alfresco.repo.template.QNameAwareObjectWrapper;
import org.alfresco.util.AbstractLifecycleBean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import freemarker.cache.MruCacheStorage;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
/**
* FreeMarker Processor for use in Web Scripts
*
* Adds the ability to:
* - specify template loaders
* - caching of templates
*
* @author davidc
*/
public class TemplateProcessor extends FreeMarkerProcessor implements ApplicationContextAware, ApplicationListener
{
private ProcessorLifecycle lifecycle = new ProcessorLifecycle();
private TemplateLoader templateLoader = null;
private String defaultEncoding;
private Configuration templateConfig;
/* (non-Javadoc)
* @see org.alfresco.repo.template.FreeMarkerProcessor#setDefaultEncoding(java.lang.String)
*/
public void setDefaultEncoding(String defaultEncoding)
{
this.defaultEncoding = defaultEncoding;
}
/* (non-Javadoc)
* @see org.alfresco.repo.template.FreeMarkerProcessor#getConfig()
*/
@Override
protected Configuration getConfig()
{
return templateConfig;
}
/**
* Sets the Template Loader
*
* @param templateLoader template loader
*/
public void setTemplateLoader(TemplateLoader templateLoader)
{
this.templateLoader = templateLoader;
}
/**
* Reset template cache
*/
public void resetCache()
{
if (templateConfig != null)
{
templateConfig.clearTemplateCache();
}
}
/**
* Initialise FreeMarker Configuration
*/
protected void initConfig()
{
Configuration config = new Configuration();
// setup template cache
config.setCacheStorage(new MruCacheStorage(20, 100));
// setup template loaders
config.setTemplateLoader(templateLoader);
// use our custom object wrapper that can deal with QNameMap objects directly
config.setObjectWrapper(new QNameAwareObjectWrapper());
// rethrow any exception so we can deal with them
config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
// set template encoding
if (defaultEncoding != null)
{
config.setDefaultEncoding(defaultEncoding);
}
// set output encoding
config.setOutputEncoding("UTF-8");
templateConfig = config;
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
lifecycle.setApplicationContext(applicationContext);
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(ApplicationEvent event)
{
lifecycle.onApplicationEvent(event);
}
/**
* Hooks into Spring Application Lifecycle
*/
private class ProcessorLifecycle extends AbstractLifecycleBean
{
@Override
protected void onBootstrap(ApplicationEvent event)
{
initConfig();
}
@Override
protected void onShutdown(ApplicationEvent event)
{
}
}
}

View File

@@ -0,0 +1,309 @@
/*
* 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.scripts;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.transaction.TransactionService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
/**
* Stand-alone Web Script Test Server
*
* @author davidc
*/
public class TestWebScriptServer
{
// dependencies
protected TransactionService transactionService;
protected DeclarativeWebScriptRegistry registry;
/** The reader for interaction. */
private BufferedReader fIn;
/** Last command issued */
private String lastCommand = null;
/** Current user */
private String username = "admin";
/**
* Sets the transaction service
*
* @param transactionService
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Sets the Web Script Registry
*
* @param registry
*/
public void setRegistry(DeclarativeWebScriptRegistry registry)
{
this.registry = registry;
}
/**
* Initialise the Test Web Script Server
*
* @throws Exception
*/
public void init() throws Exception
{
registry.initWebScripts();
fIn = new BufferedReader(new InputStreamReader(System.in));
}
/**
* Main entry point.
*/
public static void main(String[] args)
{
try
{
String[] CONFIG_LOCATIONS = new String[] { "classpath:alfresco/application-context.xml", "classpath:alfresco/web-scripts-application-context.xml", "classpath:alfresco/web-scripts-application-context-test.xml" };
ApplicationContext context = new ClassPathXmlApplicationContext(CONFIG_LOCATIONS);
TestWebScriptServer testServer = (TestWebScriptServer)context.getBean("webscripts.test");
testServer.init();
testServer.rep();
}
catch(Throwable e)
{
StringWriter strWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(strWriter);
e.printStackTrace(printWriter);
System.out.println(strWriter.toString());
}
finally
{
System.exit(0);
}
}
/**
* A Read-Eval-Print loop.
*/
public void rep()
{
// accept commands
while (true)
{
System.out.print("ok> ");
try
{
// get command
final String line = fIn.readLine();
if (line.equals("exit") || line.equals("quit"))
{
return;
}
// execute command in context of currently selected user
long startms = System.currentTimeMillis();
System.out.print(interpretCommand(line));
System.out.println("" + (System.currentTimeMillis() - startms) + "ms");
}
catch (Exception e)
{
e.printStackTrace(System.err);
System.out.println("");
}
}
}
/**
* Interpret a single command using the BufferedReader passed in for any data needed.
*
* @param line The unparsed command
* @return The textual output of the command.
*/
public String interpretCommand(final String line)
throws IOException
{
// execute command in context of currently selected user
return AuthenticationUtil.runAs(new RunAsWork<String>()
{
public String doWork() throws Exception
{
return executeCommand(line);
}
}, username);
}
/**
* Execute a single command using the BufferedReader passed in for any data needed.
*
* TODO: Use decent parser!
*
* @param line The unparsed command
* @return The textual output of the command.
*/
protected String executeCommand(String line)
throws IOException
{
String[] command = line.split(" ");
if (command.length == 0)
{
command = new String[1];
command[0] = line;
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bout);
// repeat last command?
if (command[0].equals("r"))
{
if (lastCommand == null)
{
return "No command entered yet.";
}
return "repeating command " + lastCommand + "\n\n" + interpretCommand(lastCommand);
}
// remember last command
lastCommand = line;
// execute command
if (command[0].equals("help"))
{
// TODO:
String helpFile = I18NUtil.getMessage("test_service.help");
ClassPathResource helpResource = new ClassPathResource(helpFile);
byte[] helpBytes = new byte[500];
InputStream helpStream = helpResource.getInputStream();
try
{
int read = helpStream.read(helpBytes);
while (read != -1)
{
bout.write(helpBytes, 0, read);
read = helpStream.read(helpBytes);
}
}
finally
{
helpStream.close();
}
}
else if (command[0].equals("user"))
{
if (command.length == 2)
{
username = command[1];
}
out.println("using user " + username);
}
else if (command[0].equals("get"))
{
if (command.length < 2)
{
return "Syntax Error.\n";
}
String uri = command[1];
MockHttpServletRequest req = createRequest("get", uri);
MockHttpServletResponse res = new MockHttpServletResponse();
WebScriptMatch match = registry.findWebScript(req.getMethod(), uri);
if (match == null)
{
throw new WebScriptException("No service bound to uri '" + uri + "'");
}
WebScriptRequest apiReq = new WebScriptRequest(req, match);
WebScriptResponse apiRes = new WebScriptResponse(res);
match.getWebScript().execute(apiReq, apiRes);
bout.write(res.getContentAsByteArray());
out.println();
}
else
{
return "Syntax Error.\n";
}
out.flush();
String retVal = new String(bout.toByteArray());
out.close();
return retVal;
}
/**
* Create a Mock HTTP Servlet Request
*
* @param method
* @param uri
* @return mock http servlet request
*/
private MockHttpServletRequest createRequest(String method, String uri)
{
MockHttpServletRequest req = new MockHttpServletRequest("get", uri);
// set parameters
int iArgIndex = uri.indexOf('?');
if (iArgIndex != -1 && iArgIndex != uri.length() -1)
{
String uriArgs = uri.substring(iArgIndex +1);
String[] args = uriArgs.split("&");
for (String arg : args)
{
String[] parts = arg.split("=");
req.addParameter(parts[0], (parts.length == 2) ? parts[1] : null);
}
}
// set paths
req.setContextPath("/alfresco");
req.setServletPath("/service");
req.setPathInfo(iArgIndex == -1 ? uri : uri.substring(0, iArgIndex));
return req;
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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.scripts;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Trusted Authentication Interceptor
*
* Just use the currently authenticated user
*
* @author davidc
*/
public class TrustedAuthenticator implements MethodInterceptor
{
// Logger
private static final Log logger = LogFactory.getLog(TrustedAuthenticator.class);
/* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
public Object invoke(MethodInvocation invocation)
throws Throwable
{
String currentUser = null;
Object retVal = null;
try
{
//
// Determine if user already authenticated
//
currentUser = AuthenticationUtil.getCurrentUserName();
if (logger.isDebugEnabled())
logger.debug("Current authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
//
// Use current authentication
//
//
// Invoke service
//
retVal = invocation.proceed();
}
finally
{
AuthenticationUtil.clearCurrentSecurityContext();
if (currentUser != null)
{
AuthenticationUtil.setCurrentUser(currentUser);
}
if (logger.isDebugEnabled())
logger.debug("Authentication reset: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
}
return retVal;
}
}

View File

@@ -0,0 +1,157 @@
/*
* 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.scripts;
/**
* Script / Template Model representing Web Script URLs
*
* @author davidc
*/
public class URLModel
{
private WebScriptRequest req;
/**
* Construct
*
* @param req
*/
URLModel(WebScriptRequest req)
{
this.req = req;
}
/**
* Gets the Context Path
*
* e.g. /alfresco
*
* @return context path
*/
public String getContext()
{
return req.getContextPath();
}
public String jsGet_context()
{
return getContext();
}
/**
* Gets the Service Context Path
*
* e.g. /alfresco/service
*
* @return service context path
*/
public String getServiceContext()
{
return req.getServiceContextPath();
}
public String jsGet_serviceContext()
{
return getServiceContext();
}
/**
* Gets the Service Path
*
* e.g. /alfresco/service/search/keyword
*
* @return service path
*/
public String getService()
{
return req.getServicePath();
}
public String jsGet_service()
{
return getService();
}
/**
* Gets the full path
*
* e.g. /alfresco/service/search/keyword?q=term
*
* @return service path
*/
public String getFull()
{
return req.getURL();
}
public String jsGet_full()
{
return getFull();
}
/**
* Gets the matching service path
*
* e.g.
* a) service registered path = /search/engine
* b) request path = /search/engine/external
*
* => /search/engine
*
* @return matching path
*/
public String getMatch()
{
return getServiceContext() + req.getServiceMatch().getPath();
}
public String jsGet_match()
{
return getMatch();
}
/**
* Gets the Service Extension Path
*
* e.g.
* a) service registered path = /search/engine
* b) request path = /search/engine/external
*
* => /external
*
* @return extension path
*/
public String getExtension()
{
return req.getExtensionPath();
}
public String jsGet_extension()
{
return getExtension();
}
}

View File

@@ -0,0 +1,173 @@
/*
* 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.scripts;
import javax.servlet.ServletContext;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.web.app.servlet.AuthenticationHelper;
import org.alfresco.web.app.servlet.AuthenticationStatus;
import org.alfresco.web.app.servlet.BaseServlet;
import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.context.ServletContextAware;
/**
* Alfresco Web Client Authentication Interceptor
*
* @author davidc
*/
public class WebClientAuthenticator implements MethodInterceptor, ServletContextAware
{
// Logger
private static final Log logger = LogFactory.getLog(WebClientAuthenticator.class);
// dependencies
private ServletContext context;
private AuthenticationService authenticationService;
/* (non-Javadoc)
* @see org.springframework.web.context.ServletContextAware#setServletContext(javax.servlet.ServletContext)
*/
public void setServletContext(ServletContext context)
{
this.context = context;
}
/**
* @param authenticationService
*/
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
public Object invoke(MethodInvocation invocation)
throws Throwable
{
String currentUser = null;
Object retVal = null;
Object[] args = invocation.getArguments();
WebScriptRequest request = (WebScriptRequest)args[0];
WebScriptResponse response = (WebScriptResponse)args[1];
WebScript service = (WebScript)invocation.getThis();
WebScriptDescription description = service.getDescription();
AuthenticationStatus status = null;
try
{
//
// Determine if user already authenticated
//
currentUser = AuthenticationUtil.getCurrentUserName();
if (logger.isDebugEnabled())
logger.debug("Current authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
//
// validate credentials
//
String ticket = request.getParameter("ticket");
boolean isGuest = request.isGuest();
if (logger.isDebugEnabled())
{
logger.debug("Web Script authentication required: " + description.getRequiredAuthentication());
logger.debug("Guest login: " + isGuest);
logger.debug("Ticket provided: " + (ticket != null && ticket.length() > 0));
}
if (ticket != null && ticket.length() > 0)
{
if (logger.isDebugEnabled())
logger.debug("Authenticating ticket " + ticket);
status = AuthenticationHelper.authenticate(context, request, response, ticket);
}
else
{
if (isGuest && description.getRequiredAuthentication() == RequiredAuthentication.guest)
{
if (logger.isDebugEnabled())
logger.debug("Authenticating as Guest");
status = AuthenticationHelper.authenticate(context, request, response, true);
}
else
{
if (logger.isDebugEnabled())
logger.debug("Authenticating session");
status = AuthenticationHelper.authenticate(context, request, response, false);
}
}
//
// execute web script or request authorization
//
if (status != null && status != AuthenticationStatus.Failure)
{
retVal = invocation.proceed();
}
else
{
// authentication failed - now need to display the login page to the user, if asked to
if (logger.isDebugEnabled())
logger.debug("Redirecting to Alfresco Login");
BaseServlet.redirectToLoginPage(request, response, context);
}
}
finally
{
if (status != null && status != AuthenticationStatus.Failure)
{
authenticationService.clearCurrentSecurityContext();
if (currentUser != null)
{
AuthenticationUtil.setCurrentUser(currentUser);
}
if (logger.isDebugEnabled())
logger.debug("Authentication reset: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
}
}
return retVal;
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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.scripts;
import java.io.IOException;
/**
* Web Script
*
* @author davidc
*/
public interface WebScript
{
/**
* Gets the Service Description
*
* @return service description
*/
public WebScriptDescription getDescription();
/**
* Execute service
*
* @param req
* @param res
* @throws IOException
*/
public void execute(WebScriptRequest req, WebScriptResponse res)
throws IOException;
}

View File

@@ -0,0 +1,230 @@
/*
* 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.scripts;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.AbstractLifecycleBean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
/**
* Provision of Repository Context
*
* @author davidc
*/
public class WebScriptContext implements ApplicationContextAware, ApplicationListener
{
private ProcessorLifecycle lifecycle = new ProcessorLifecycle();
// dependencies
private TransactionService transactionService;
private NamespaceService namespaceService;
private SearchService searchService;
private NodeService nodeService;
private PersonService personService;
// company home
private StoreRef companyHomeStore;
private String companyHomePath;
private NodeRef companyHome;
/**
* Sets the Company Home Store
*
* @param companyHomeStore
*/
public void setCompanyHomeStore(String companyHomeStore)
{
this.companyHomeStore = new StoreRef(companyHomeStore);
}
/**
* Sets the Company Home Path
*
* @param companyHomePath
*/
public void setCompanyHomePath(String companyHomePath)
{
this.companyHomePath = companyHomePath;
}
/**
* Sets the transaction service
*
* @param transactionService
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/**
* Sets the namespace service
*
* @param namespaceService
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* Sets the search service
*
* @param searchService
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* Sets the node service
*
* @param nodeService
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* Sets the person service
*
* @param personService
*/
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
lifecycle.setApplicationContext(applicationContext);
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(ApplicationEvent event)
{
lifecycle.onApplicationEvent(event);
}
/**
* Hooks into Spring Application Lifecycle
*/
private class ProcessorLifecycle extends AbstractLifecycleBean
{
@Override
protected void onBootstrap(ApplicationEvent event)
{
initContext();
}
@Override
protected void onShutdown(ApplicationEvent event)
{
}
}
/**
* Initialise Repository Context
*/
protected void initContext()
{
TransactionUtil.executeInUserTransaction(transactionService, new TransactionUtil.TransactionWork<Object>()
{
@SuppressWarnings("synthetic-access")
public Object doWork() throws Exception
{
List<NodeRef> refs = searchService.selectNodes(nodeService.getRootNode(companyHomeStore), companyHomePath, null, namespaceService, false);
if (refs.size() != 1)
{
throw new IllegalStateException("Invalid company home path: " + companyHomePath + " - found: " + refs.size());
}
companyHome = refs.get(0);
return null;
}
});
}
/**
* Gets the Company Home
*
* @return company home node ref
*/
public NodeRef getCompanyHome()
{
return companyHome;
}
/**
* Gets the currently authenticated person
*
* @return person node ref
*/
public NodeRef getPerson()
{
NodeRef person = null;
String currentUserName = AuthenticationUtil.getCurrentUserName();
if (personService.personExists(currentUserName))
{
person = personService.getPerson(currentUserName);
}
return person;
}
/**
* Gets the user home of the currently authenticated person
*
* @param person person
* @return user home of person
*/
public NodeRef getUserHome(NodeRef person)
{
return (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER);
}
}

View File

@@ -0,0 +1,151 @@
/*
* 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.scripts;
/**
* Web Script Description
*
* @author davidc
*/
public interface WebScriptDescription
{
/**
* Enumeration of "required" Authentication level
*/
public enum RequiredAuthentication
{
none,
guest,
user
}
/**
* Enumeration of "required" Transaction level
*/
public enum RequiredTransaction
{
none,
required,
requiresnew
}
/**
* Gets the source document location of this service description
*
* @return document location (path)
*/
public String getSourceLocation();
/**
* Gets the id of this service
*
* @return service id
*/
public String getId();
/**
* Gets the short name of this service
*
* @return service short name
*/
public String getShortName();
/**
* Gets the description of this service
*/
public String getDescription();
/**
* Gets the required authentication level for execution of this service
*
* @return the required authentication level
*/
public RequiredAuthentication getRequiredAuthentication();
/**
* Gets the required transaction level
*
* @return the required transaction level
*/
public RequiredTransaction getRequiredTransaction();
/**
* Gets the HTTP method this service is bound to
*
* @return HTTP method
*/
public String getMethod();
/**
* Gets the URIs this service supports
*
* @return array of URIs in order specified in service description document
*/
public URI[] getURIs();
/**
* Gets a URI by format
*
* @param format the format
* @return the URI (or null, if no URI registered for the format)
*/
public URI getURI(String format);
/**
* Gets the default response format
*
* Note: the default response format is the first listed in the service
* description document
*
* @return default response format
*/
public String getDefaultFormat();
/**
* Web Script URL
*
* @author davidc
*/
public interface URI
{
/**
* Gets the URI response format
*
* @return format
*/
public String getFormat();
/**
* Gets the URI
*
* @return uri
*/
public String getURI();
}
}

View File

@@ -0,0 +1,273 @@
/*
* 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.scripts;
import java.util.HashMap;
import java.util.Map;
/**
* Implementation of a Web Script Description
*
* @author davidc
*/
public class WebScriptDescriptionImpl implements WebScriptDescription
{
private String sourceLocation;
private String id;
private String shortName;
private String description;
private RequiredAuthentication requiredAuthentication;
private RequiredTransaction requiredTransaction;
private String httpMethod;
private URI[] uris;
private String defaultFormat;
private Map<String, URI> uriByFormat;
/**
* Sets the source location
*
* @param sourceLocation
*/
public void setSourceLocation(String sourceLocation)
{
this.sourceLocation = sourceLocation;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getSourceLocation()
*/
public String getSourceLocation()
{
return this.sourceLocation;
}
/**
* Sets the service id
*
* @param id
*/
public void setId(String id)
{
this.id = id;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getId()
*/
public String getId()
{
return this.id;
}
/**
* Sets the service short name
*
* @param shortName
*/
public void setShortName(String shortName)
{
this.shortName = shortName;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getShortName()
*/
public String getShortName()
{
return this.shortName;
}
/**
* Sets the service description
*
* @param description
*/
public void setDescription(String description)
{
this.description = description;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getDescription()
*/
public String getDescription()
{
return this.description;
}
/**
* Sets the required level of authentication
*
* @param requiredAuthentication
*/
public void setRequiredAuthentication(RequiredAuthentication requiredAuthentication)
{
this.requiredAuthentication = requiredAuthentication;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getRequiredAuthentication()
*/
public RequiredAuthentication getRequiredAuthentication()
{
return this.requiredAuthentication;
}
/**
* Sets the required level of transaction
*
* @param requiredTransaction
*/
public void setRequiredTransaction(RequiredTransaction requiredTransaction)
{
this.requiredTransaction = requiredTransaction;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getRequiredTransaction()
*/
public RequiredTransaction getRequiredTransaction()
{
return this.requiredTransaction;
}
/**
* Sets the service http method
*
* @param httpMethod
*/
public void setMethod(String httpMethod)
{
this.httpMethod = httpMethod;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getMethod()
*/
public String getMethod()
{
return this.httpMethod;
}
/**
* Sets the service URIs
*
* @param uris
*/
public void setUris(URI[] uris)
{
this.uriByFormat = new HashMap<String, URI>(uris.length);
for (URI uri : uris)
{
this.uriByFormat.put(uri.getFormat(), uri);
}
this.uris = uris;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getURIs()
*/
public URI[] getURIs()
{
return this.uris;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getURI(java.lang.String)
*/
public URI getURI(String format)
{
return this.uriByFormat.get(format);
}
/**
* Sets the default response format
*
* @param defaultFormat
*/
public void setDefaultFormat(String defaultFormat)
{
this.defaultFormat = defaultFormat;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription#getDefaultFormat()
*/
public String getDefaultFormat()
{
return this.defaultFormat;
}
/**
* Web Script URL Implementation
*
* @author davidc
*/
public static class URIImpl implements WebScriptDescription.URI
{
private String format;
private String uri;
/**
* Sets the URI response format
*
* @param format
*/
public void setFormat(String format)
{
this.format = format;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription.URI#getFormat()
*/
public String getFormat()
{
return this.format;
}
/**
* Sets the URI
*
* @param uri
*/
public void setUri(String uri)
{
this.uri = uri;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptDescription.URI#getURI()
*/
public String getURI()
{
return this.uri;
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.scripts;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Web Script Exceptions.
*
* @author David Caruana
*/
public class WebScriptException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = -7338963365877285084L;
public WebScriptException(String msgId)
{
super(msgId);
}
public WebScriptException(String msgId, Throwable cause)
{
super(msgId, cause);
}
public WebScriptException(String msgId, Object ... args)
{
super(msgId, args);
}
public WebScriptException(String msgId, Throwable cause, Object ... args)
{
super(msgId, args, cause);
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.scripts;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Web Script Logger
*
* @author davidc
*/
public class WebScriptLogger implements MethodInterceptor
{
// Logger
private static final Log logger = LogFactory.getLog(WebScriptLogger.class);
/* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
public Object invoke(MethodInvocation invocation)
throws Throwable
{
Object retVal = null;
if (logger.isDebugEnabled())
{
WebScript service = (WebScript)invocation.getThis();
WebScriptDescription description = service.getDescription();
String user = AuthenticationUtil.getCurrentUserName();
String locale = I18NUtil.getLocale().toString();
logger.debug("Invoking Web Script " + description.getId() + (user == null ? " (unauthenticated)" : " (authenticated as " + user + ")" + " (" + locale + ")"));
long start = System.currentTimeMillis();
retVal = invocation.proceed();
long end = System.currentTimeMillis();
logger.debug("Web Script " + description.getId() + " executed in " + (end - start) + "ms");
}
else
{
retVal = invocation.proceed();
}
return retVal;
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.scripts;
/**
* Represents a URL to Web Script match
*
* @author davidc
*/
public interface WebScriptMatch
{
/**
* Gets the part of the request URL that matched the Web Script URL Template
*
* @return matching url path
*/
public String getPath();
/**
* Gets the matching web script
*
* @return service
*/
public WebScript getWebScript();
}

View File

@@ -0,0 +1,95 @@
/*
* 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.scripts;
import java.util.Collection;
import javax.servlet.ServletContext;
/**
* Web Scripts Registry
*
* @author davidc
*/
public interface WebScriptRegistry
{
/**
* Gets all Web Scripts
*
* @return web scripts
*/
public Collection<WebScript> getWebScripts();
/**
* Gets a Web Script by Id
*
* @param id web script id
* @return web script
*/
public WebScript getWebScript(String id);
/**
* Gets a Web Script given an HTTP Method and URI
*
* @param method http method
* @param uri uri
* @return service match (pair of service and uri that matched)
*/
public WebScriptMatch findWebScript(String method, String uri);
/**
* Gets the Servlet Context
*
* @return servlet context
*/
public ServletContext getContext();
/**
* Gets the response format registry
*
* @return response format registry
*/
public FormatRegistry getFormatRegistry();
/**
* Gets the Template Processor
*
* @return template processor
*/
public TemplateProcessor getTemplateProcessor();
/**
* Gets the Script Processor
*
* @return script processor
*/
public ScriptProcessor getScriptProcessor();
/**
* Resets the Registry
*/
public void reset();
}

View File

@@ -0,0 +1,174 @@
/*
* 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.scripts;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* Web Script Request
*
* @author davidc
*/
public class WebScriptRequest extends HttpServletRequestWrapper
{
/** Service bound to this request */
private WebScriptMatch serviceMatch;
/**
* Construct
*
* @param req
* @param serviceMatch
*/
WebScriptRequest(HttpServletRequest req, WebScriptMatch serviceMatch)
{
super(req);
this.serviceMatch = serviceMatch;
}
/**
* Gets the matching API Service for this request
*
* @return the service match
*/
public WebScriptMatch getServiceMatch()
{
return serviceMatch;
}
/**
* Get server portion of the request
*
* e.g. scheme://host:port
*
* @return server path
*/
public String getServerPath()
{
return getScheme() + "://" + getServerName() + ":" + getServerPort();
}
/**
* Gets the Alfresco Web Script Context Path
*
* @return service url e.g. /alfresco/service
*/
public String getServiceContextPath()
{
return getContextPath() + getServletPath();
}
/**
* Gets the Alfresco Service Path
*
* @return service url e.g. /alfresco/service/search/keyword
*/
public String getServicePath()
{
return getServiceContextPath() + getPathInfo();
}
/**
* Gets the full request URL
*
* @return request url e.g. /alfresco/service/search/keyword?q=term
*/
public String getURL()
{
return getServicePath() + (getQueryString() != null ? "?" + getQueryString() : "");
}
/**
* Gets the path extension beyond the path registered for this service
*
* e.g.
* a) service registered path = /search/engine
* b) request path = /search/engine/external
*
* => /external
*
* @return extension path
*/
public String getExtensionPath()
{
String servicePath = serviceMatch.getPath();
String extensionPath = getPathInfo();
int extIdx = extensionPath.indexOf(servicePath);
if (extIdx != -1)
{
int extLength = (servicePath.endsWith("/") ? servicePath.length() : servicePath.length() + 1);
extensionPath = extensionPath.substring(extIdx + extLength);
}
return extensionPath;
}
/**
* Determine if Guest User?
*
* @return true => guest user
*/
public boolean isGuest()
{
return Boolean.valueOf(getParameter("guest"));
}
/**
* Get Requested Format
*
* @return content type requested
*/
public String getFormat()
{
String format = getParameter("format");
return (format == null || format.length() == 0) ? "" : format;
}
/**
* Get User Agent
*
* TODO: Expand on known agents
*
* @return MSIE / Firefox
*/
public String getAgent()
{
String userAgent = getHeader("user-agent");
if (userAgent != null)
{
if (userAgent.indexOf("Firefox/") != -1)
{
return "Firefox";
}
else if (userAgent.indexOf("MSIE") != -1)
{
return "MSIE";
}
}
return null;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.scripts;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
/**
* API Service Response
*
* @author davidc
*/
public class WebScriptResponse extends HttpServletResponseWrapper
{
// API Formats
public static final String HTML_FORMAT = "html";
public static final String ATOM_FORMAT = "atom";
public static final String RSS_FORMAT = "rss";
public static final String XML_FORMAT = "xml";
public static final String JSON_FORMAT = "json";
public static final String OPENSEARCH_DESCRIPTION_FORMAT = "opensearchdescription";
/**
* Construct
*
* @param res
*/
WebScriptResponse(HttpServletResponse res)
{
super(res);
}
}

View File

@@ -0,0 +1,123 @@
/*
* 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.scripts;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* Entry point for Web Scripts
*
* @author davidc
*/
public class WebScriptServlet extends HttpServlet
{
private static final long serialVersionUID = 4209892938069597860L;
// Logger
private static final Log logger = LogFactory.getLog(WebScriptServlet.class);
// Web Scripts
private DeclarativeWebScriptRegistry registry;
@Override
public void init() throws ServletException
{
super.init();
ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
registry = (DeclarativeWebScriptRegistry)context.getBean("webscripts.registry");
registry.initWebScripts();
}
// TODO:
// - authentication (as suggested in http://www.xml.com/pub/a/2003/12/17/dive.html)
/*
* (non-Javadoc)
*
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
{
long start = System.currentTimeMillis();
try
{
//
// Execute appropriate web scripy
//
// TODO: Handle errors (with appropriate HTTP error responses)
String uri = req.getPathInfo();
if (logger.isDebugEnabled())
logger.debug("Processing request (" + req.getMethod() + ") " + req.getRequestURL() + (req.getQueryString() != null ? "?" + req.getQueryString() : ""));
WebScriptMatch match = registry.findWebScript(req.getMethod(), uri);
if (match != null)
{
// setup web script context
WebScriptRequest apiReq = new WebScriptRequest(req, match);
WebScriptResponse apiRes = new WebScriptResponse(res);
if (logger.isDebugEnabled())
logger.debug("Agent: " + apiReq.getAgent());
// execute service
match.getWebScript().execute(apiReq, apiRes);
}
else
{
if (logger.isDebugEnabled())
logger.debug("Request does not map to service.");
res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
// TODO: add appropriate error detail
}
}
// TODO: exception handling
finally
{
long end = System.currentTimeMillis();
if (logger.isDebugEnabled())
logger.debug("Processed request (" + req.getMethod() + ") " + req.getRequestURL() + (req.getQueryString() != null ? "?" + req.getQueryString() : "") + " in " + (end - start) + "ms");
}
}
}

View File

@@ -0,0 +1,179 @@
/*
* 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.scripts;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.alfresco.util.AbstractLifecycleBean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
/**
* Web Script Storage
*
* @author davidc
*/
public class WebScriptStorage implements ApplicationContextAware, ApplicationListener
{
private ApplicationContext applicationContext;
private ProcessorLifecycle lifecycle = new ProcessorLifecycle();
private TemplateProcessor templateProcessor;
private ScriptProcessor scriptProcessor;
/* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
this.lifecycle.setApplicationContext(applicationContext);
}
/* (non-Javadoc)
* @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
public void onApplicationEvent(ApplicationEvent event)
{
lifecycle.onApplicationEvent(event);
}
/**
* Sets the template processor
*
* @param templateProcessor
*/
public void setTemplateProcessor(TemplateProcessor templateProcessor)
{
this.templateProcessor = templateProcessor;
}
/**
* Sets the script processor
*
* @param scriptProcessor
*/
public void setScriptProcessor(ScriptProcessor scriptProcessor)
{
this.scriptProcessor = scriptProcessor;
}
/**
* Gets all Web Script Stores
*
* @return all Web Script Stores
*/
@SuppressWarnings("unchecked")
public Collection<WebScriptStore> getStores()
{
return applicationContext.getBeansOfType(WebScriptStore.class, false, false).values();
}
/**
* Gets the Template Processor
*
* @return template processor
*/
public TemplateProcessor getTemplateProcessor()
{
return templateProcessor;
}
/**
* Gets the Script Processor
*
* @return script processor
*/
public ScriptProcessor getScriptProcessor()
{
return scriptProcessor;
}
/**
* Register template loader from each Web Script Store with Template Processor
*/
protected void initTemplateProcessor()
{
List<TemplateLoader> loaders = new ArrayList<TemplateLoader>();
for (WebScriptStore apiStore : getStores())
{
TemplateLoader loader = apiStore.getTemplateLoader();
if (loader == null)
{
throw new WebScriptException("Unable to retrieve template loader for Web Script store " + apiStore.getBasePath());
}
loaders.add(loader);
}
MultiTemplateLoader loader = new MultiTemplateLoader(loaders.toArray(new TemplateLoader[loaders.size()]));
templateProcessor.setTemplateLoader(loader);
}
/**
* Register script loader from each Web Script Store with Script Processor
*/
protected void initScriptProcessor()
{
List<ScriptLoader> loaders = new ArrayList<ScriptLoader>();
for (WebScriptStore apiStore : getStores())
{
ScriptLoader loader = apiStore.getScriptLoader();
if (loader == null)
{
throw new WebScriptException("Unable to retrieve script loader for Web Script store " + apiStore.getBasePath());
}
loaders.add(loader);
}
MultiScriptLoader loader = new MultiScriptLoader(loaders.toArray(new ScriptLoader[loaders.size()]));
scriptProcessor.setScriptLoader(loader);
}
/**
* Hooks into Spring Application Lifecycle
*/
private class ProcessorLifecycle extends AbstractLifecycleBean
{
@Override
protected void onBootstrap(ApplicationEvent event)
{
initTemplateProcessor();
initScriptProcessor();
}
@Override
protected void onShutdown(ApplicationEvent event)
{
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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.scripts;
import java.io.IOException;
import java.io.InputStream;
import freemarker.cache.TemplateLoader;
/**
* Store for holding Web Script Definitions and Implementations
*
* @author davidc
*/
public interface WebScriptStore
{
/**
* Gets the base path of the store
*
* @return base path
*/
public String getBasePath();
/**
* Gets the paths of all Web Script description documents in this store
*
* @return array of description document paths
*/
public String[] getDescriptionDocumentPaths();
/**
* Gets a description document
*
* @param documentPath description document path
* @return input stream onto description document
*
* @throws IOException
*/
public InputStream getDescriptionDocument(String documentPath)
throws IOException;
/**
* Gets the template loader for this store
*
* @return template loader
*/
public TemplateLoader getTemplateLoader();
/**
* Gets the script loader for this store
*
* @return script loader
*/
public ScriptLoader getScriptLoader();
}

View File

@@ -0,0 +1,100 @@
/*
* 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.scripts;
import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.web.scripts.WebScriptDescription.RequiredTransaction;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Web Script Transaction
*
* @author davidc
*/
public class WebScriptTransaction implements MethodInterceptor
{
// Logger
protected static final Log logger = LogFactory.getLog(WebScriptTransaction.class);
// dependencies
private TransactionService transactionService;
/**
* Sets the transaction service
*
* @param transactionService
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
public Object invoke(final MethodInvocation invocation)
throws Throwable
{
WebScript service = (WebScript)invocation.getThis();
final WebScriptDescription description = service.getDescription();
// encapsulate service call within transaction
TransactionUtil.TransactionWork<Object> work = new TransactionUtil.TransactionWork<Object>()
{
public Object doWork() throws Throwable
{
if (logger.isDebugEnabled())
logger.debug("Begin transaction: " + description.getRequiredTransaction());
Object retVal = invocation.proceed();
if (logger.isDebugEnabled())
logger.debug("End transaction: " + description.getRequiredTransaction());
return retVal;
}
};
// execute call within transaction
Object retVal;
if (description.getRequiredTransaction() == RequiredTransaction.required)
{
retVal = TransactionUtil.executeInUserTransaction(transactionService, work);
}
else
{
retVal = TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, work);
}
return retVal;
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.scripts.bean;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse;
/**
* Retrieves the list of available Web APIs
*
* @author davidc
*/
public class Index extends DeclarativeWebScript
{
/* (non-Javadoc)
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/
@Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res)
{
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("webscripts", getWebScriptRegistry().getWebScripts());
return model;
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.scripts.bean;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse;
/**
* Retrieves the list of available Web APIs
*
* @author davidc
*/
public class IndexUpdate extends DeclarativeWebScript
{
/* (non-Javadoc)
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/
@Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res)
{
List<String> tasks = new ArrayList<String>();
// reset index
String reset = req.getParameter("reset");
if (reset != null && reset.equals("on"))
{
int previousCount = getWebScriptRegistry().getWebScripts().size();
getWebScriptRegistry().reset();
tasks.add("Reset Web Scripts Registry; found " + getWebScriptRegistry().getWebScripts().size() + " Web Scripts. Previously, there were " + previousCount + ".");
}
// create model for rendering
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("tasks", tasks);
model.put("webscripts", getWebScriptRegistry().getWebScripts());
return model;
}
}

View File

@@ -0,0 +1,382 @@
/*
* 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.scripts.bean;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateNode;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.util.GUID;
import org.alfresco.util.ParameterCheck;
import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse;
import org.alfresco.web.ui.common.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Alfresco Keyword (simple) Search Service
*
* @author davidc
*/
public class KeywordSearch extends DeclarativeWebScript
{
// Logger
private static final Log logger = LogFactory.getLog(KeywordSearch.class);
// search parameters
// TODO: allow configuration of search store
protected static final StoreRef SEARCH_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
protected static final int DEFAULT_ITEMS_PER_PAGE = 10;
protected static final String QUERY_FORMAT = "query_";
// dependencies
protected SearchService searchService;
// icon resolver
protected TemplateImageResolver iconResolver = new TemplateImageResolver()
{
public String resolveImagePathForName(String filename, boolean small)
{
return Utils.getFileTypeImage(getWebScriptRegistry().getContext(), filename, small);
}
};
/**
* @param searchService
*/
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/
@Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res)
{
//
// process arguments
//
String searchTerms = req.getParameter("q");
ParameterCheck.mandatoryString("q", searchTerms);
String startPageArg = req.getParameter("p");
int startPage = 1;
try
{
startPage = new Integer(startPageArg);
}
catch(NumberFormatException e)
{
// NOTE: use default startPage
}
String itemsPerPageArg = req.getParameter("c");
int itemsPerPage = DEFAULT_ITEMS_PER_PAGE;
try
{
itemsPerPage = new Integer(itemsPerPageArg);
}
catch(NumberFormatException e)
{
// NOTE: use default itemsPerPage
}
Locale locale = I18NUtil.getLocale();
String language = req.getParameter("l");
if (language != null && language.length() > 0)
{
// NOTE: Simple conversion from XML Language Id to Java Locale Id
locale = new Locale(language.replace("-", "_"));
}
//
// execute the search
//
SearchResult results = search(searchTerms, startPage, itemsPerPage, locale, req);
//
// create model
//
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("search", results);
return model;
}
/**
* Execute the search
*
* @param searchTerms
* @param startPage
* @return
*/
private SearchResult search(String searchTerms, int startPage, int itemsPerPage, Locale locale, WebScriptRequest req)
{
SearchResult searchResult = null;
ResultSet results = null;
try
{
// construct search statement
String[] terms = searchTerms.split(" ");
Map<String, Object> statementModel = new HashMap<String, Object>(7, 1.0f);
statementModel.put("args", createArgModel(req));
statementModel.put("terms", terms);
Writer queryWriter = new StringWriter(1024);
renderFormatTemplate(QUERY_FORMAT, statementModel, queryWriter);
String query = queryWriter.toString();
// execute query
if (logger.isDebugEnabled())
{
logger.debug("Search parameters: searchTerms=" + searchTerms + ", startPage=" + startPage + ", itemsPerPage=" + itemsPerPage + ", search locale=" + locale.toString());
logger.debug("Issuing lucene search: " + query);
}
SearchParameters parameters = new SearchParameters();
parameters.addStore(SEARCH_STORE);
parameters.setLanguage(SearchService.LANGUAGE_LUCENE);
parameters.setQuery(query);
if (locale != null)
{
parameters.addLocale(locale);
}
results = searchService.query(parameters);
int totalResults = results.length();
if (logger.isDebugEnabled())
logger.debug("Results: " + totalResults + " rows (limited: " + results.getResultSetMetaData().getLimitedBy() + ")");
// are we out-of-range
int totalPages = (totalResults / itemsPerPage);
totalPages += (totalResults % itemsPerPage != 0) ? 1 : 0;
if (totalPages != 0 && (startPage < 1 || startPage > totalPages))
{
throw new WebScriptException("Start page " + startPage + " is outside boundary of " + totalPages + " pages");
}
// construct search result
searchResult = new SearchResult();
searchResult.setSearchTerms(searchTerms);
searchResult.setLocale(locale);
searchResult.setItemsPerPage(itemsPerPage);
searchResult.setStartPage(startPage);
searchResult.setTotalPages(totalPages);
searchResult.setTotalResults(totalResults);
searchResult.setStartIndex(((startPage -1) * itemsPerPage) + 1);
searchResult.setTotalPageItems(Math.min(itemsPerPage, totalResults - searchResult.getStartIndex() + 1));
SearchTemplateNode[] nodes = new SearchTemplateNode[searchResult.getTotalPageItems()];
for (int i = 0; i < searchResult.getTotalPageItems(); i++)
{
NodeRef node = results.getNodeRef(i + searchResult.getStartIndex() - 1);
float score = results.getScore(i + searchResult.getStartIndex() - 1);
nodes[i] = new SearchTemplateNode(node, score);
}
searchResult.setResults(nodes);
return searchResult;
}
finally
{
if (results != null)
{
results.close();
}
}
}
/**
* Search Result
*
* @author davidc
*/
public static class SearchResult
{
private String id;
private String searchTerms;
private Locale locale;
private int itemsPerPage;
private int totalPages;
private int totalResults;
private int totalPageItems;
private int startPage;
private int startIndex;
private SearchTemplateNode[] results;
public int getItemsPerPage()
{
return itemsPerPage;
}
/*package*/ void setItemsPerPage(int itemsPerPage)
{
this.itemsPerPage = itemsPerPage;
}
public TemplateNode[] getResults()
{
return results;
}
/*package*/ void setResults(SearchTemplateNode[] results)
{
this.results = results;
}
public int getStartIndex()
{
return startIndex;
}
/*package*/ void setStartIndex(int startIndex)
{
this.startIndex = startIndex;
}
public int getStartPage()
{
return startPage;
}
/*package*/ void setStartPage(int startPage)
{
this.startPage = startPage;
}
public int getTotalPageItems()
{
return totalPageItems;
}
/*package*/ void setTotalPageItems(int totalPageItems)
{
this.totalPageItems = totalPageItems;
}
public int getTotalPages()
{
return totalPages;
}
/*package*/ void setTotalPages(int totalPages)
{
this.totalPages = totalPages;
}
public int getTotalResults()
{
return totalResults;
}
/*package*/ void setTotalResults(int totalResults)
{
this.totalResults = totalResults;
}
public String getSearchTerms()
{
return searchTerms;
}
/*package*/ void setSearchTerms(String searchTerms)
{
this.searchTerms = searchTerms;
}
public Locale getLocale()
{
return locale;
}
/**
* @return XML 1.0 Language Identification
*/
public String getLocaleId()
{
return locale.toString().replace('_', '-');
}
/*package*/ void setLocale(Locale locale)
{
this.locale = locale;
}
public String getId()
{
if (id == null)
{
id = GUID.generate();
}
return id;
}
}
/**
* Search result row template node
*/
public class SearchTemplateNode extends TemplateNode
{
private static final long serialVersionUID = -1791913270786140012L;
private float score;
/**
* Construct
*
* @param nodeRef
* @param score
*/
public SearchTemplateNode(NodeRef nodeRef, float score)
{
super(nodeRef, getServiceRegistry(), iconResolver);
this.score = score;
}
/**
* Gets the result row score
*
* @return score
*/
public float getScore()
{
return score;
}
}
}

View File

@@ -0,0 +1,209 @@
/*
* 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.scripts.bean;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigService;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.web.config.OpenSearchConfigElement;
import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* List of (server-side) registered Search Engines
*
* @author davidc
*/
public class SearchEngines extends DeclarativeWebScript
{
// url argument values
public static final String URL_ARG_DESCRIPTION = "description";
public static final String URL_ARG_TEMPLATE = "template";
public static final String URL_ARG_ALL = "all";
// Logger
private static final Log logger = LogFactory.getLog(SearchEngines.class);
// dependencies
protected ConfigService configService;
protected SearchProxy searchProxy;
/**
* @param configService
*/
public void setConfigService(ConfigService configService)
{
this.configService = configService;
}
/**
* @param searchProxy
*/
public void setSearchProxy(SearchProxy searchProxy)
{
this.searchProxy = searchProxy;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/
@Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res)
{
String urlType = req.getParameter("type");
if (urlType == null || urlType.length() == 0)
{
urlType = URL_ARG_DESCRIPTION;
}
else if (!urlType.equals(URL_ARG_DESCRIPTION) && !urlType.equals(URL_ARG_TEMPLATE) && !urlType.equals(URL_ARG_ALL))
{
urlType = URL_ARG_DESCRIPTION;
}
//
// retrieve open search engines configuration
//
Set<UrlTemplate> urls = getUrls(urlType);
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("urltype", urlType);
model.put("engines", urls);
return model;
}
/**
* Retrieve registered search engines
*
* @return set of search engines
*/
private Set<UrlTemplate> getUrls(String urlType)
{
if (logger.isDebugEnabled())
logger.debug("Search Engine parameters: urltype=" + urlType);
Set<UrlTemplate> urls = new HashSet<UrlTemplate>();
Config config = configService.getConfig("OpenSearch");
OpenSearchConfigElement searchConfig = (OpenSearchConfigElement)config.getConfigElement(OpenSearchConfigElement.CONFIG_ELEMENT_ID);
for (OpenSearchConfigElement.EngineConfig engineConfig : searchConfig.getEngines())
{
Map<String, String> engineUrls = engineConfig.getUrls();
for (Map.Entry<String, String> engineUrl : engineUrls.entrySet())
{
String type = engineUrl.getKey();
String url = searchProxy.createUrl(engineConfig, type);
if ((urlType.equals(URL_ARG_ALL)) ||
(urlType.equals(URL_ARG_DESCRIPTION) && type.equals(MimetypeMap.MIMETYPE_OPENSEARCH_DESCRIPTION)) ||
(urlType.equals(URL_ARG_TEMPLATE) && !type.equals(MimetypeMap.MIMETYPE_OPENSEARCH_DESCRIPTION)))
{
String label = engineConfig.getLabel();
String labelId = engineConfig.getLabelId();
if (labelId != null && labelId.length() > 0)
{
String i18nLabel = I18NUtil.getMessage(labelId);
if (i18nLabel == null && label == null)
{
label = (i18nLabel == null) ? "$$" + labelId + "$$" : i18nLabel;
}
}
urls.add(new UrlTemplate(label, type, url));
}
// TODO: Extract URL templates from OpenSearch description
else if (urlType.equals(URL_ARG_TEMPLATE) &&
type.equals(MimetypeMap.MIMETYPE_OPENSEARCH_DESCRIPTION))
{
}
}
}
if (logger.isDebugEnabled())
logger.debug("Retrieved " + urls.size() + " engine registrations");
return urls;
}
/**
* Model object for representing a registered search engine
*/
public static class UrlTemplate
{
private String type;
private String label;
private String url;
private UrlTemplate engine;
public UrlTemplate(String label, String type, String url)
{
this.label = label;
this.type = type;
this.url = url;
this.engine = null;
}
public UrlTemplate(String label, String type, String url, UrlTemplate engine)
{
this(label, type, url);
this.engine = engine;
}
public String getLabel()
{
return label;
}
public String getType()
{
return type;
}
public String getUrl()
{
return url;
}
public String getUrlType()
{
return (type.equals(MimetypeMap.MIMETYPE_OPENSEARCH_DESCRIPTION) ? "description" : "template");
}
public UrlTemplate getEngine()
{
return engine;
}
}
}

View File

@@ -0,0 +1,311 @@
/*
* 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.scripts.bean;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.config.Config;
import org.alfresco.config.ConfigService;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.web.app.servlet.HTTPProxy;
import org.alfresco.web.config.OpenSearchConfigElement;
import org.alfresco.web.config.OpenSearchConfigElement.EngineConfig;
import org.alfresco.web.config.OpenSearchConfigElement.ProxyConfig;
import org.alfresco.web.scripts.AbstractWebScript;
import org.alfresco.web.scripts.FormatRegistry;
import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.InitializingBean;
/**
* Alfresco OpenSearch Proxy Service
*
* Provides the ability to submit a request to a registered search engine
* via the Alfresco server.
*
* @author davidc
*/
public class SearchProxy extends AbstractWebScript implements InitializingBean
{
// Logger
private static final Log logger = LogFactory.getLog(SearchProxy.class);
// dependencies
protected FormatRegistry formatRegistry;
protected ConfigService configService;
protected OpenSearchConfigElement searchConfig;
protected String proxyPath;
/**
* @param formatRegistry
*/
public void setFormatRegistry(FormatRegistry formatRegistry)
{
this.formatRegistry = formatRegistry;
}
/**
* @param configService
*/
public void setConfigService(ConfigService configService)
{
this.configService = configService;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
public void afterPropertiesSet() throws Exception
{
Config config = configService.getConfig("OpenSearch");
searchConfig = (OpenSearchConfigElement)config.getConfigElement(OpenSearchConfigElement.CONFIG_ELEMENT_ID);
if (searchConfig == null)
{
throw new WebScriptException("OpenSearch configuration not found");
}
ProxyConfig proxyConfig = searchConfig.getProxy();
if (proxyConfig == null)
{
throw new WebScriptException("OpenSearch proxy configuration not found");
}
proxyPath = proxyConfig.getUrl();
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScript#execute(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/
public void execute(WebScriptRequest req, WebScriptResponse res)
throws IOException
{
String extensionPath = req.getExtensionPath();
String[] extensionPaths = extensionPath.split("/");
if (extensionPaths.length != 2)
{
throw new WebScriptException("OpenSearch engine has not been specified as /{engine}/{format}");
}
// retrieve search engine configuration
String engine = extensionPaths[0];
EngineConfig engineConfig = searchConfig.getEngine(engine);
if (engineConfig == null)
{
throw new WebScriptException("OpenSearch engine '" + engine + "' does not exist");
}
// retrieve engine url as specified by format
String format = extensionPaths[1];
String mimetype = formatRegistry.getMimeType(null, format);
if (mimetype == null)
{
throw new WebScriptException("Format '" + format + "' does not map to a registered mimetype");
}
Map<String, String> engineUrls = engineConfig.getUrls();
String engineUrl = engineUrls.get(mimetype);
if (engineUrl == null)
{
throw new WebScriptException("Url mimetype '" + mimetype + "' does not exist for engine '" + engine + "'");
}
// replace template url arguments with actual arguments specified on request
int engineUrlArgIdx = engineUrl.indexOf("?");
if (engineUrlArgIdx != -1)
{
engineUrl = engineUrl.substring(0, engineUrlArgIdx);
}
if (req.getQueryString() != null)
{
engineUrl += "?" + req.getQueryString();
}
if (logger.isDebugEnabled())
logger.debug("Mapping engine '" + engine + "' (mimetype '" + mimetype + "') to url '" + engineUrl + "'");
// issue request against search engine
SearchEngineHttpProxy proxy = new SearchEngineHttpProxy(req.getServicePath() + "/" + req.getContextPath(), engine, engineUrl, res);
proxy.service();
}
/**
* OpenSearch HTTPProxy
*
* This proxy remaps OpenSearch links (e.g. previous, next) found in search results.
*
* @author davidc
*/
private class SearchEngineHttpProxy extends HTTPProxy
{
private final static String ATOM_NS_URI = "http://www.w3.org/2005/Atom";
private final static String ATOM_NS_PREFIX = "atom";
private final static String ATOM_LINK_XPATH = "atom:link[@rel=\"first\" or @rel=\"last\" or @rel=\"next\" or @rel=\"previous\" or @rel=\"self\" or @rel=\"alternate\"]";
private String engine;
private String rootPath;
/**
* Construct
*
* @param requestUrl
* @param response
* @throws MalformedURLException
*/
public SearchEngineHttpProxy(String rootPath, String engine, String engineUrl, HttpServletResponse response)
throws MalformedURLException
{
super(engineUrl.startsWith("/") ? rootPath + engineUrl : engineUrl, response);
this.engine = engine;
this.rootPath = rootPath;
}
/* (non-Javadoc)
* @see org.alfresco.web.app.servlet.HTTPProxy#writeResponse(java.io.InputStream, java.io.OutputStream)
*/
@Override
protected void writeResponse(InputStream input, OutputStream output)
throws IOException
{
if (response.getContentType().startsWith(MimetypeMap.MIMETYPE_ATOM) ||
response.getContentType().startsWith(MimetypeMap.MIMETYPE_RSS))
{
// Only post-process ATOM and RSS feeds
// Replace all navigation links with "proxied" versions
SAXReader reader = new SAXReader();
try
{
Document document = reader.read(input);
Element rootElement = document.getRootElement();
XPath xpath = rootElement.createXPath(ATOM_LINK_XPATH);
Map<String,String> uris = new HashMap<String,String>();
uris.put(ATOM_NS_PREFIX, ATOM_NS_URI);
xpath.setNamespaceURIs(uris);
List nodes = xpath.selectNodes(rootElement);
Iterator iter = nodes.iterator();
while (iter.hasNext())
{
Element element = (Element)iter.next();
Attribute hrefAttr = element.attribute("href");
String mimetype = element.attributeValue("type");
if (mimetype == null || mimetype.length() == 0)
{
mimetype = MimetypeMap.MIMETYPE_HTML;
}
String url = createUrl(engine, hrefAttr.getValue(), mimetype);
if (url.startsWith("/"))
{
url = rootPath + url;
}
hrefAttr.setValue(url);
}
OutputFormat outputFormat = OutputFormat.createPrettyPrint();
XMLWriter writer = new XMLWriter(output, outputFormat);
writer.write(rootElement);
writer.flush();
}
catch(DocumentException e)
{
throw new IOException(e.toString());
}
}
else
{
super.writeResponse(input, output);
}
}
}
/**
* Construct a "proxied" search engine url
*
* @param engine engine name (as identified by <engine proxy="<name>">)
* @param mimetype url to proxy (as identified by mimetype)
* @return "proxied" url
*/
public String createUrl(OpenSearchConfigElement.EngineConfig engine, String mimetype)
{
Map<String, String> urls = engine.getUrls();
String url = urls.get(mimetype);
if (url != null)
{
String proxy = engine.getProxy();
if (proxy != null && !mimetype.equals(MimetypeMap.MIMETYPE_OPENSEARCH_DESCRIPTION))
{
url = createUrl(proxy, url, mimetype);
}
}
return url;
}
/**
* Construct a "proxied" search engine url
*
* @param engine engine name (as identified by <engine proxy="<name>">)
* @param url engine url
* @param mimetype mimetype of url
* @return "proxied" url
*/
public String createUrl(String engine, String url, String mimetype)
{
String format = formatRegistry.getFormat(null, mimetype);
if (format == null)
{
throw new WebScriptException("Mimetype '" + mimetype + "' is not registered.");
}
String proxyUrl = null;
int argIdx = url.indexOf("?");
if (argIdx == -1)
{
proxyUrl = proxyPath + "/" + engine + "/" + format;
}
else
{
proxyUrl = proxyPath + "/" + engine + "/" + format + url.substring(argIdx);
}
return proxyUrl;
}
}