Web Scripts:

- Addition of reponse status code and template support
- Appropriate status codes added to login & ticket web scripts
- Web Script Index page also mapped to / url
- Various fixes applied to url to web script mapping

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5846 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2007-06-05 10:41:51 +00:00
parent 546eccf561
commit ac9960758f
28 changed files with 1081 additions and 218 deletions

View File

@@ -1 +1,86 @@
testserver.help=alfresco/messages/webscripts-test-help.txt ##
## Web Script Response Codes
##
webscript.code.100.name=Continue
webscript.code.100.description=Client can continue.
webscript.code.101.name=Switching Protocols
webscript.code.101.description=The server is switching protocols according to Upgrade header.
webscript.code.200.name=OK
webscript.code.200.description=The request succeeded normally.
webscript.code.201.name=Created
webscript.code.201.description=Request succeeded and created a new resource on the server.
webscript.code.202.name=Accepted
webscript.code.202.description=Request was accepted for processing, but was not completed.
webscript.code.203.name=Non authoritative information
webscript.code.203.description=The meta information presented by the client did not originate from the server.
webscript.code.204.name=No ContentSC_NO_CONTENT
webscript.code.204.description=The request succeeded but that there was no new information to return.
webscript.code.205.name=Reset Content
webscript.code.205.description=The agent SHOULD reset the document view which caused the request to be sent.
webscript.code.206.name=Partial Content
webscript.code.206.description=The server has fulfilled the partial GET request for the resource.
webscript.code.300.name=Multiple Choices
webscript.code.300.description=The requested resource corresponds to any one of a set of representations, each with its own specific location.
webscript.code.301.name=Moved Permanently
webscript.code.301.description=The resource has permanently moved to a new location, and that future references should use a new URI with their requests.
webscript.code.302.name=Moved Temporarily
webscript.code.302.description=The resource has temporarily moved to another location, but that future references should still use the original URI to access the resource.
webscript.code.303.name=See Other
webscript.code.303.description=The response to the request can be found under a different URI.
webscript.code.304.name=Not Modified
webscript.code.304.description=A conditional GET operation found that the resource was available and not modified.
webscript.code.305.name=Use Proxy
webscript.code.305.description=The requested resource MUST be accessed through the proxy given by the Location field.
webscript.code.400.name=Bad Request
webscript.code.400.description=Request sent by the client was syntactically incorrect.
webscript.code.401.name=Unauthorized
webscript.code.401.description=The request requires HTTP authentication.
webscript.code.402.name=Payment Required
webscript.code.402.description=Reserved for future use.
webscript.code.403.name=Forbidden
webscript.code.403.description=Server understood the request but refused to fulfill it.
webscript.code.404.name=Not Found
webscript.code.404.description=Requested resource is not available.
webscript.code.405.name=Method Not Allowed
webscript.code.405.description=The method specified in the Request-Line is not allowed for the resource identified by the Request-URI.
webscript.code.406.name=Not Acceptable
webscript.code.406.description=The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.
webscript.code.407.name=Proxy Authentication Required
webscript.code.407.description=The client MUST first authenticate itself with the proxy.
webscript.code.408.name=Request Timeout
webscript.code.408.description=The client did not produce a request within the time that the server was prepared to wait.
webscript.code.409.name=Conflict
webscript.code.409.description=Request could not be completed due to a conflict with the current state of the resource.
webscript.code.410.name=Gone
webscript.code.410.description=Resource is no longer available at the server and no forwarding address is known.
webscript.code.411.name=Length required
webscript.code.411.description=Request cannot be handled without a defined Content-Length.
webscript.code.412.name=Precondition Failed
webscript.code.412.description=The precondition given in one or more of the request-header fields evaluated to false when it was tested on the server.
webscript.code.413.name=Request Entity Too Large
webscript.code.413.description=The server is refusing to process the request because the request entity is larger than the server is willing or able to process.
webscript.code.414.name=Request URI Too Long
webscript.code.414.description=The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret.
webscript.code.415.name=Unsupported Media Type
webscript.code.415.description=The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method.
webscript.code.416.name=Requested Range Not Satisfiable
webscript.code.416.description=The server cannot serve the requested byte range.
webscript.code.417.name=Expectation Failed
webscript.code.417.description=Server could not meet the expectation given in the Expect request header.
webscript.code.500.name=Internal Error
webscript.code.500.description=An error inside the HTTP server which prevented it from fulfilling the request.
webscript.code.501.name=Not Implemented
webscript.code.501.description=The HTTP server does not support the functionality needed to fulfill the request.
webscript.code.502.name=Bad Gateway
webscript.code.502.description=HTTP server received an invalid response from a server it consulted when acting as a proxy or gateway.
webscript.code.503.name=Service Unavailable
webscript.code.503.description=The HTTP server is temporarily overloaded, and unable to handle the request.
webscript.code.504.name=Gateway Timeout
webscript.code.504.description=Server did not receive a timely response from the upstream server while acting as a gateway or proxy.
webscript.code.505.name=HTTP version not supported
webscript.code.505.description=Server does not support or refuses to support the HTTP protocol version that was used in the request message.
##
## Test Web Script Server Help
##
testserver.help=alfresco/messages/webscripts-test-help.txt

View File

@@ -1,5 +1,6 @@
<webscript> <webscript>
<shortname>Web Scripts Documentation</shortname> <shortname>Web Scripts Documentation</shortname>
<description>Web Scripts Documentation</description> <description>Web Scripts Documentation</description>
<url format="html" template="/" />
<url format="html" template="/index" /> <url format="html" template="/index" />
</webscript> </webscript>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Web Script Status ${status.code} - ${status.codeName}</title>
<link rel="stylesheet" href="${url.context}/css/main.css" TYPE="text/css">
</head>
<body>
<table>
<tr>
<td><img src="${url.context}/images/logo/AlfrescoLogo32.png" alt="Alfresco" /></td>
<td><nobr><span class="mainTitle">Web Script Status ${status.code} - ${status.codeName}</span></nobr></td>
</tr>
</table>
<br>
<table>
<tr><td>The Web Script <a href="${url.full}">${url.service}</a> has responded with a status of ${status.code} - ${status.codeName}.
</table>
<br>
<table>
<tr><td><b>${status.code} Description:</b><td> ${status.codeDescription}
<tr><td>&nbsp;
<tr><td><b>Message:</b><td>${status.message!"<i>&lt;Not specified&gt;</i>"}
<tr><td><b>Exception:</b><td>${status.exception!"None"}
<#if status.exception?exists>
<tr><td><td>&nbsp;
<#list status.exception.stackTrace as element>
<tr><td><td>${element}
</#list>
</#if>
</table>
</body>
</html>

View File

@@ -3,6 +3,19 @@
<beans> <beans>
<!-- -->
<!-- Web Script Resource Bundles -->
<!-- -->
<bean id="webscripts.resources" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
<property name="resourceBundles">
<list>
<value>alfresco.messages.webscripts</value>
</list>
</property>
</bean>
<!-- --> <!-- -->
<!-- API Definition & Implementation Storage --> <!-- API Definition & Implementation Storage -->
<!-- --> <!-- -->

View File

@@ -24,10 +24,12 @@
*/ */
package org.alfresco.web.scripts; package org.alfresco.web.scripts;
import java.io.IOException;
import java.io.Writer; import java.io.Writer;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.alfresco.repo.jscript.Node; import org.alfresco.repo.jscript.Node;
import org.alfresco.repo.jscript.ScriptableHashMap; import org.alfresco.repo.jscript.ScriptableHashMap;
@@ -39,6 +41,8 @@ import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.descriptor.DescriptorService; import org.alfresco.service.descriptor.DescriptorService;
import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication; import org.alfresco.web.scripts.WebScriptDescription.RequiredAuthentication;
import org.alfresco.web.scripts.WebScriptDescription.RequiredTransaction; import org.alfresco.web.scripts.WebScriptDescription.RequiredTransaction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/** /**
@@ -48,12 +52,19 @@ import org.alfresco.web.scripts.WebScriptDescription.RequiredTransaction;
*/ */
public abstract class AbstractWebScript implements WebScript public abstract class AbstractWebScript implements WebScript
{ {
// Logger
private static final Log logger = LogFactory.getLog(AbstractWebScript.class);
// dependencies // dependencies
private WebScriptContext scriptContext; private WebScriptContext scriptContext;
private WebScriptRegistry scriptRegistry; private WebScriptRegistry scriptRegistry;
private WebScriptDescription description; private WebScriptDescription description;
private ServiceRegistry serviceRegistry; private ServiceRegistry serviceRegistry;
private DescriptorService descriptorService; private DescriptorService descriptorService;
// Status Template cache
private Map<String, StatusTemplate> statusTemplates = new HashMap<String, StatusTemplate>();
private ReentrantReadWriteLock statusTemplateLock = new ReentrantReadWriteLock();
// //
@@ -102,6 +113,15 @@ public abstract class AbstractWebScript implements WebScript
public void init(WebScriptRegistry scriptRegistry) public void init(WebScriptRegistry scriptRegistry)
{ {
this.scriptRegistry = scriptRegistry; this.scriptRegistry = scriptRegistry;
this.statusTemplateLock.writeLock().lock();
try
{
this.statusTemplates.clear();
}
finally
{
this.statusTemplateLock.writeLock().unlock();
}
} }
@@ -184,12 +204,11 @@ public abstract class AbstractWebScript implements WebScript
* Create a model for script usage * Create a model for script usage
* *
* @param req web script request * @param req web script request
* @param res web script response
* @param customModel custom model entries * @param customModel custom model entries
* *
* @return script model * @return script model
*/ */
final protected Map<String, Object> createScriptModel(WebScriptRequest req, WebScriptResponse res, Map<String, Object> customModel) final protected Map<String, Object> createScriptModel(WebScriptRequest req, Map<String, Object> customModel)
{ {
// create script model // create script model
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f); Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
@@ -301,6 +320,188 @@ public abstract class AbstractWebScript implements WebScript
{ {
getWebScriptRegistry().getTemplateProcessor().processString(template, model, writer); getWebScriptRegistry().getTemplateProcessor().processString(template, model, writer);
} }
/**
* Render an explicit response status template
*
* @param req web script request
* @param res web script response
* @param status web script status
* @param format format
* @param model model
* @throws IOException
*/
final protected void sendStatus(WebScriptRequest req, WebScriptResponse res, WebScriptStatus status, String format, Map<String, Object> model)
throws IOException
{
// locate status template
// NOTE: search order...
// NOTE: package path is recursed to root package
// 1) script located <scriptid>.<format>.<status>.ftl
// 2) script located <scriptid>.<format>.status.ftl
// 3) package located <scriptpath>/<format>.<status>.ftl
// 4) package located <scriptpath>/<format>.status.ftl
// 5) default <status>.ftl
// 6) default status.ftl
int statusCode = status.getCode();
String statusFormat = (format == null) ? "" : format;
String scriptId = getDescription().getId();
StatusTemplate template = getStatusTemplate(scriptId, statusCode, statusFormat);
// render output
String mimetype = getWebScriptRegistry().getFormatRegistry().getMimeType(req.getAgent(), template.format);
if (mimetype == null)
{
throw new WebScriptException("Web Script format '" + template.format + "' is not registered");
}
if (logger.isDebugEnabled())
{
logger.debug("Sending status " + statusCode + " (Template: " + template.path + ")");
logger.debug("Rendering response: content type=" + mimetype);
}
res.reset();
res.setStatus(statusCode);
res.setContentType(mimetype + ";charset=UTF-8");
renderTemplate(template.path, model, res.getWriter());
}
/**
* Find status template
*
* Note: This method caches template search results
*
* @param scriptId
* @param statusCode
* @param format
* @return status template (or null if not found)
*/
private StatusTemplate getStatusTemplate(String scriptId, int statusCode, String format)
{
StatusTemplate statusTemplate = null;
statusTemplateLock.readLock().lock();
try
{
String key = statusCode + "." + format;
statusTemplate = statusTemplates.get(key);
if (statusTemplate == null)
{
// Upgrade read lock to write lock
statusTemplateLock.readLock().unlock();
statusTemplateLock.writeLock().lock();
try
{
// Check again
statusTemplate = statusTemplates.get(key);
if (statusTemplate == null)
{
// Locate template in web script store
statusTemplate = getScriptStatusTemplate(scriptId, statusCode, format);
if (statusTemplate == null)
{
WebScriptPath path = getWebScriptRegistry().getPackage(Path.concatPath("/", getDescription().getScriptPath()));
statusTemplate = getPackageStatusTemplate(path, statusCode, format);
if (statusTemplate == null)
{
statusTemplate = getDefaultStatusTemplate(statusCode);
}
}
if (logger.isDebugEnabled())
logger.debug("Caching template " + statusTemplate.path + " for web script " + scriptId + " and status " + statusCode + " (format: " + format + ")");
statusTemplates.put(key, statusTemplate);
}
}
finally
{
// Downgrade lock to read
statusTemplateLock.readLock().lock();
statusTemplateLock.writeLock().unlock();
}
}
return statusTemplate;
}
finally
{
statusTemplateLock.readLock().unlock();
}
}
/**
* Find a script specific status template
*
* @param scriptId
* @param statusCode
* @param format
* @return status template (or null, if not found)
*/
private StatusTemplate getScriptStatusTemplate(String scriptId, int statusCode, String format)
{
String path = scriptId + "." + format + "." + statusCode + ".ftl";
if (getWebScriptRegistry().getTemplateProcessor().hasTemplate(path))
{
return new StatusTemplate(path, format);
}
path = scriptId + "." + format + ".status.ftl";
if (getWebScriptRegistry().getTemplateProcessor().hasTemplate(path))
{
return new StatusTemplate(path, format);
}
return null;
}
/**
* Find a package specific status template
*
* @param scriptPath
* @param statusCode
* @param format
* @return status template (or null, if not found)
*/
private StatusTemplate getPackageStatusTemplate(WebScriptPath scriptPath, int statusCode, String format)
{
while(scriptPath != null)
{
String path = Path.concatPath(scriptPath.getPath(), format + "." + statusCode + ".ftl");
if (getWebScriptRegistry().getTemplateProcessor().hasTemplate(path))
{
return new StatusTemplate(path, format);
}
path = Path.concatPath(scriptPath.getPath(), format + ".status.ftl");
if (getWebScriptRegistry().getTemplateProcessor().hasTemplate(path))
{
return new StatusTemplate(path, format);
}
scriptPath = scriptPath.getParent();
}
return null;
}
/**
* Find default status template
*
* @param statusCode
* @return status template
*/
private StatusTemplate getDefaultStatusTemplate(int statusCode)
{
String path = statusCode + ".ftl";
if (getWebScriptRegistry().getTemplateProcessor().hasTemplate(path))
{
return new StatusTemplate(path, WebScriptResponse.HTML_FORMAT);
}
path = "status.ftl";
if (getWebScriptRegistry().getTemplateProcessor().hasTemplate(path))
{
return new StatusTemplate(path, WebScriptResponse.HTML_FORMAT);
}
throw new WebScriptException("Default status template /status.ftl could not be found");
}
/** /**
* Execute a script * Execute a script
@@ -312,4 +513,26 @@ public abstract class AbstractWebScript implements WebScript
{ {
getWebScriptRegistry().getScriptProcessor().executeScript(location, model); getWebScriptRegistry().getScriptProcessor().executeScript(location, model);
} }
/**
* Status Template
*/
private class StatusTemplate
{
/**
* Construct
*
* @param path
* @param format
*/
private StatusTemplate(String path, String format)
{
this.path = path;
this.format = format;
}
private String path;
private String format;
}
} }

View File

@@ -31,11 +31,12 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.jscript.Node; import org.alfresco.repo.jscript.Node;
import org.alfresco.repo.jscript.ScriptableHashMap; import org.alfresco.repo.jscript.ScriptableHashMap;
import org.alfresco.repo.template.TemplateNode; import org.alfresco.repo.template.TemplateNode;
import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.TemplateException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
@@ -76,57 +77,90 @@ public class DeclarativeWebScript extends AbstractWebScript
*/ */
final public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException final public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException
{ {
// construct data model for template // retrieve requested format
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(); String format = req.getFormat();
if (format == null || format.length() == 0) if (format == null || format.length() == 0)
{ {
format = getDescription().getDefaultFormat(); 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 try
{ {
// establish mimetype from format
String mimetype = getWebScriptRegistry().getFormatRegistry().getMimeType(req.getAgent(), format);
if (mimetype == null)
{
throw new WebScriptException("Web Script format '" + format + "' is not registered");
}
// construct data model for template
WebScriptStatus status = new WebScriptStatus();
Map<String, Object> model = executeImpl(req, status);
if (model == null)
{
model = new HashMap<String, Object>(7, 1.0f);
}
model.put("status", status);
// execute script if it exists
if (executeScript != null)
{
if (logger.isDebugEnabled())
logger.debug("Executing script " + executeScript);
Map<String, Object> scriptModel = createScriptModel(req, 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);
}
// create model for template rendering
Map<String, Object> templateModel = createTemplateModel(req, res, model); Map<String, Object> templateModel = createTemplateModel(req, res, model);
renderFormatTemplate(format, templateModel, res.getWriter());
// is a redirect to a status specific template required?
if (status.getRedirect())
{
sendStatus(req, res, status, format, templateModel);
}
else
{
// render output
int statusCode = status.getCode();
if (statusCode != HttpServletResponse.SC_OK)
{
res.setStatus(statusCode);
}
res.setContentType(mimetype + ";charset=UTF-8");
if (logger.isDebugEnabled())
logger.debug("Rendering response: content type=" + mimetype + ", status=" + statusCode);
renderFormatTemplate(format, templateModel, res.getWriter());
}
} }
catch(TemplateException e) catch(Throwable e)
{ {
throw new WebScriptException("Failed to process format '" + format + "'", e); // extract status code, if specified
int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
if (e instanceof WebScriptException)
{
statusCode = ((WebScriptException)e).getStatus();
}
// send status
WebScriptStatus status = new WebScriptStatus();
status.setCode(statusCode);
status.setMessage(e.getMessage());
status.setException(e);
Map<String, Object> customModel = new HashMap<String, Object>();
customModel.put("status", status);
Map<String, Object> templateModel = createTemplateModel(req, res, customModel);
sendStatus(req, res, status, format, templateModel);
} }
} }
/** /**
* Merge script generated model into template-ready model * Merge script generated model into template-ready model
* *
@@ -192,10 +226,9 @@ public class DeclarativeWebScript extends AbstractWebScript
* Execute custom Java logic * Execute custom Java logic
* *
* @param req Web Script request * @param req Web Script request
* @param res Web Script response
* @return custom service model * @return custom service model
*/ */
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
return null; return null;
} }

View File

@@ -81,7 +81,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
// map of web scripts by url // map of web scripts by url
// NOTE: The map is sorted by url (descending order) // NOTE: The map is sorted by url (descending order)
private Map<String, WebScript> webscriptsByURL = new TreeMap<String, WebScript>(Collections.reverseOrder()); private Map<String, URLIndex> webscriptsByURL = new TreeMap<String, URLIndex>(Collections.reverseOrder());
// map of web script packages by path // map of web script packages by path
private Map<String, Path> packageByPath = new TreeMap<String, Path>(); private Map<String, Path> packageByPath = new TreeMap<String, Path>();
@@ -258,6 +258,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
for (URI uri : serviceDesc.getURIs()) for (URI uri : serviceDesc.getURIs())
{ {
// establish static part of url template // establish static part of url template
boolean wildcard = false;
String uriTemplate = uri.getURI(); String uriTemplate = uri.getURI();
int queryArgIdx = uriTemplate.indexOf('?'); int queryArgIdx = uriTemplate.indexOf('?');
if (queryArgIdx != -1) if (queryArgIdx != -1)
@@ -268,6 +269,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
if (tokenIdx != -1) if (tokenIdx != -1)
{ {
uriTemplate = uriTemplate.substring(0, tokenIdx); uriTemplate = uriTemplate.substring(0, tokenIdx);
wildcard = true;
} }
if (serviceDesc.getFormatStyle() != WebScriptDescription.FormatStyle.argument) if (serviceDesc.getFormatStyle() != WebScriptDescription.FormatStyle.argument)
{ {
@@ -282,7 +284,8 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
String uriIdx = serviceDesc.getMethod().toString() + ":" + uriTemplate; String uriIdx = serviceDesc.getMethod().toString() + ":" + uriTemplate;
if (webscriptsByURL.containsKey(uriIdx)) if (webscriptsByURL.containsKey(uriIdx))
{ {
WebScript existingService = webscriptsByURL.get(uriIdx); URLIndex urlIndex = webscriptsByURL.get(uriIdx);
WebScript existingService = urlIndex.script;
if (!existingService.getDescription().getId().equals(serviceDesc.getId())) if (!existingService.getDescription().getId().equals(serviceDesc.getId()))
{ {
String msg = "Web Script document " + serviceDesc.getDescPath() + " is attempting to define the url '" + uriIdx + "' already defined by " + existingService.getDescription().getDescPath(); String msg = "Web Script document " + serviceDesc.getDescPath() + " is attempting to define the url '" + uriIdx + "' already defined by " + existingService.getDescription().getDescPath();
@@ -291,7 +294,8 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
} }
else else
{ {
webscriptsByURL.put(uriIdx, serviceImpl); URLIndex urlIndex = new URLIndex(uriTemplate, wildcard, serviceImpl);
webscriptsByURL.put(uriIdx, urlIndex);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("Registered Web Script URL '" + uriIdx + "'"); logger.debug("Registered Web Script URL '" + uriIdx + "'");
@@ -357,7 +361,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
subpath = path.createChildPath(part); subpath = path.createChildPath(part);
uriByPath.put(subpath.getPath(), subpath); uriByPath.put(subpath.getPath(), subpath);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("Registered Web Script URI " + subpath.getPath()); logger.debug("Registered Web Script URI Path " + subpath.getPath());
} }
path = subpath; path = subpath;
} }
@@ -387,7 +391,8 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
} }
// retrieve script path // retrieve script path
String scriptPath = serviceDescPath.substring(0, serviceDescPath.lastIndexOf('/')); int iPathIdx = serviceDescPath.lastIndexOf('/');
String scriptPath = serviceDescPath.substring(0, iPathIdx == -1 ? 0 : iPathIdx);
// retrieve script id // retrieve script id
String id = serviceDescPath.substring(0, serviceDescPath.lastIndexOf(".desc.xml")); String id = serviceDescPath.substring(0, serviceDescPath.lastIndexOf(".desc.xml"));
@@ -569,19 +574,38 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
*/ */
public WebScriptMatch findWebScript(String method, String uri) public WebScriptMatch findWebScript(String method, String uri)
{ {
long startTime = System.currentTimeMillis();
// TODO: Replace with more efficient approach // TODO: Replace with more efficient approach
String matchedPath = null;
DeclarativeWebScriptMatch apiServiceMatch = null; DeclarativeWebScriptMatch apiServiceMatch = null;
String match = method.toString().toUpperCase() + ":" + uri; String match = method.toString().toUpperCase() + ":" + uri;
for (Map.Entry<String, WebScript> service : webscriptsByURL.entrySet())
// locate full match - on URI and METHOD
for (Map.Entry<String, URLIndex> entry : webscriptsByURL.entrySet())
{ {
String indexedPath = service.getKey(); URLIndex urlIndex = entry.getValue();
if (match.startsWith(indexedPath)) String index = entry.getKey();
if ((urlIndex.wildcardPath && match.startsWith(index)) || (!urlIndex.wildcardPath && match.equals(index)))
{ {
String matchPath = indexedPath.substring(indexedPath.indexOf(':') +1); apiServiceMatch = new DeclarativeWebScriptMatch(urlIndex.path, urlIndex.script);
apiServiceMatch = new DeclarativeWebScriptMatch(matchPath, service.getValue());
break; break;
} }
else if ((urlIndex.wildcardPath && uri.startsWith(urlIndex.path)) || (!urlIndex.wildcardPath && uri.equals(urlIndex.path)))
{
matchedPath = urlIndex.path;
}
} }
// locate URI match
if (apiServiceMatch == null && matchedPath != null)
{
apiServiceMatch = new DeclarativeWebScriptMatch(matchedPath);
}
if (logger.isDebugEnabled())
logger.debug("Web Script index lookup for uri " + uri + " took " + (System.currentTimeMillis() - startTime) + "ms");
return apiServiceMatch; return apiServiceMatch;
} }
@@ -634,6 +658,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
{ {
private String path; private String path;
private WebScript service; private WebScript service;
private Kind kind;
/** /**
* Construct * Construct
@@ -643,9 +668,30 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
*/ */
public DeclarativeWebScriptMatch(String path, WebScript service) public DeclarativeWebScriptMatch(String path, WebScript service)
{ {
this.kind = Kind.FULL;
this.path = path; this.path = path;
this.service = service; this.service = service;
} }
/**
* Construct
*
* @param path
* @param service
*/
public DeclarativeWebScriptMatch(String path)
{
this.kind = Kind.URI;
this.path = path;
}
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptMatch#getKind()
*/
public Kind getKind()
{
return this.kind;
}
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptMatch#getPath() * @see org.alfresco.web.scripts.WebScriptMatch#getPath()
@@ -664,4 +710,21 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
} }
} }
/**
* Web Script URL Index Entry
*/
private static class URLIndex
{
private URLIndex(String path, boolean wildcardPath, WebScript script)
{
this.path = path;
this.wildcardPath = wildcardPath;
this.script = script;
}
private String path;
private boolean wildcardPath;
private WebScript script;
}
} }

View File

@@ -24,6 +24,8 @@
*/ */
package org.alfresco.web.scripts; package org.alfresco.web.scripts;
import java.io.IOException;
import org.alfresco.repo.template.FreeMarkerProcessor; import org.alfresco.repo.template.FreeMarkerProcessor;
import org.alfresco.repo.template.QNameAwareObjectWrapper; import org.alfresco.repo.template.QNameAwareObjectWrapper;
import org.alfresco.service.cmr.repository.ProcessorExtension; import org.alfresco.service.cmr.repository.ProcessorExtension;
@@ -37,6 +39,7 @@ import org.springframework.context.ApplicationListener;
import freemarker.cache.MruCacheStorage; import freemarker.cache.MruCacheStorage;
import freemarker.cache.TemplateLoader; import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler; import freemarker.template.TemplateExceptionHandler;
@@ -106,6 +109,26 @@ public class TemplateProcessor extends FreeMarkerProcessor implements Applicatio
} }
} }
/**
* Determines if a template exists
*
* @param template
* @return true => exists
*/
public boolean hasTemplate(String templatePath)
{
boolean hasTemplate = false;
try
{
Template template = templateConfig.getTemplate(templatePath);
hasTemplate = (template != null);
}
catch(IOException e)
{
}
return hasTemplate;
}
/** /**
* Initialise FreeMarker Configuration * Initialise FreeMarker Configuration
*/ */

View File

@@ -353,12 +353,7 @@ public class TestWebScriptServer
command[0].equals("post") || command[0].equals("post") ||
command[0].equals("delete")) command[0].equals("delete"))
{ {
if (command.length < 2) String uri = (command.length > 1) ? command[1] : null;
{
return "Syntax Error.\n";
}
String uri = command[1];
MockHttpServletResponse res = submitRequest(command[0], uri); MockHttpServletResponse res = submitRequest(command[0], uri);
bout.write(res.getContentAsByteArray()); bout.write(res.getContentAsByteArray());
out.println(); out.println();
@@ -392,23 +387,24 @@ public class TestWebScriptServer
{ {
MockHttpServletRequest req = new MockHttpServletRequest(method, uri); MockHttpServletRequest req = new MockHttpServletRequest(method, 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.setContextPath("/alfresco");
req.setServletPath("/service"); req.setServletPath("/service");
req.setPathInfo(iArgIndex == -1 ? uri : uri.substring(0, iArgIndex));
if (uri != null)
{
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);
}
}
req.setPathInfo(iArgIndex == -1 ? uri : uri.substring(0, iArgIndex));
}
return req; return req;
} }

View File

@@ -24,6 +24,8 @@
*/ */
package org.alfresco.web.scripts; package org.alfresco.web.scripts;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
/** /**
@@ -35,6 +37,15 @@ public class WebScriptException extends AlfrescoRuntimeException
{ {
private static final long serialVersionUID = -7338963365877285084L; private static final long serialVersionUID = -7338963365877285084L;
private int status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
public WebScriptException(int status, String msgId)
{
this(msgId);
this.status = status;
}
public WebScriptException(String msgId) public WebScriptException(String msgId)
{ {
super(msgId); super(msgId);
@@ -54,4 +65,10 @@ public class WebScriptException extends AlfrescoRuntimeException
{ {
super(msgId, args, cause); super(msgId, args, cause);
} }
public int getStatus()
{
return status;
}
} }

View File

@@ -26,13 +26,25 @@ package org.alfresco.web.scripts;
/** /**
* Represents a URL to Web Script match * Represents a URL request to Web Script match
* *
* @author davidc * @author davidc
*/ */
public interface WebScriptMatch public interface WebScriptMatch
{ {
public enum Kind
{
/** URL request matches on URI only */
URI,
/** URL request matches on URI and Method */
FULL
};
/**
* Gets the kind of Match
*/
public Kind getKind();
/** /**
* Gets the part of the request URL that matched the Web Script URL Template * Gets the part of the request URL that matched the Web Script URL Template
* *
@@ -43,7 +55,7 @@ public interface WebScriptMatch
/** /**
* Gets the matching web script * Gets the matching web script
* *
* @return service * @return service (or null, if match kind is URI)
*/ */
public WebScript getWebScript(); public WebScript getWebScript();

View File

@@ -40,13 +40,21 @@ public abstract class WebScriptRequestImpl implements WebScriptRequest
*/ */
public String getExtensionPath() public String getExtensionPath()
{ {
String servicePath = getServiceMatch().getPath(); String extensionPath = "";
String extensionPath = getPathInfo(); WebScriptMatch match = getServiceMatch();
int extIdx = extensionPath.indexOf(servicePath); if (match != null)
if (extIdx != -1)
{ {
int extLength = (servicePath.endsWith("/") ? servicePath.length() : servicePath.length() + 1); String servicePath = getServiceMatch().getPath();
extensionPath = extensionPath.substring(extIdx + extLength); extensionPath = getPathInfo();
if (extensionPath != null)
{
int extIdx = extensionPath.indexOf(servicePath);
if (extIdx != -1)
{
int extLength = (servicePath.endsWith("/") ? servicePath.length() : servicePath.length() + 1);
extensionPath = extensionPath.substring(extIdx + extLength);
}
}
} }
return extensionPath; return extensionPath;
} }
@@ -65,30 +73,37 @@ public abstract class WebScriptRequestImpl implements WebScriptRequest
public String getFormat() public String getFormat()
{ {
String format = null; String format = null;
FormatStyle style = getServiceMatch().getWebScript().getDescription().getFormatStyle(); WebScriptMatch match = getServiceMatch();
if (match != null)
// extract format from extension
if (style == FormatStyle.extension || style == FormatStyle.any)
{ {
String pathInfo = getPathInfo(); FormatStyle style = getServiceMatch().getWebScript().getDescription().getFormatStyle();
int extIdx = pathInfo.lastIndexOf('.');
if (extIdx != -1) // extract format from extension
if (style == FormatStyle.extension || style == FormatStyle.any)
{ {
format = pathInfo.substring(extIdx +1); String pathInfo = getPathInfo();
} if (pathInfo != null)
}
// extract format from argument
if (style == FormatStyle.argument || style == FormatStyle.any)
{
String argFormat = getParameter("format");
if (argFormat != null)
{
if (format != null && format.length() > 0)
{ {
throw new WebScriptException("Format specified both in extension and format argument"); int extIdx = pathInfo.lastIndexOf('.');
if (extIdx != -1)
{
format = pathInfo.substring(extIdx +1);
}
}
}
// extract format from argument
if (style == FormatStyle.argument || style == FormatStyle.any)
{
String argFormat = getParameter("format");
if (argFormat != null)
{
if (format != null && format.length() > 0)
{
throw new WebScriptException("Format specified both in extension and format argument");
}
format = argFormat;
} }
format = argFormat;
} }
} }
@@ -100,7 +115,12 @@ public abstract class WebScriptRequestImpl implements WebScriptRequest
*/ */
public FormatStyle getFormatStyle() public FormatStyle getFormatStyle()
{ {
FormatStyle style = getServiceMatch().getWebScript().getDescription().getFormatStyle(); WebScriptMatch match = getServiceMatch();
if (match == null)
{
return FormatStyle.any;
}
FormatStyle style = match.getWebScript().getDescription().getFormatStyle();
if (style != FormatStyle.any) if (style != FormatStyle.any)
{ {
return style; return style;

View File

@@ -44,6 +44,12 @@ public interface WebScriptResponse
public static final String JSON_FORMAT = "json"; public static final String JSON_FORMAT = "json";
public static final String OPENSEARCH_DESCRIPTION_FORMAT = "opensearchdescription"; public static final String OPENSEARCH_DESCRIPTION_FORMAT = "opensearchdescription";
/**
* Sets the Response Status
*
* @param status
*/
public void setStatus(int status);
/** /**
* Sets the Content Type * Sets the Content Type
@@ -68,6 +74,11 @@ public interface WebScriptResponse
*/ */
public OutputStream getOutputStream() throws IOException; public OutputStream getOutputStream() throws IOException;
/**
* Clears response buffer
*/
public void reset();
/** /**
* Encode a script URL * Encode a script URL
* *

View File

@@ -25,8 +25,14 @@
package org.alfresco.web.scripts; package org.alfresco.web.scripts;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.i18n.I18NUtil; import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
@@ -77,94 +83,159 @@ public abstract class WebScriptRuntime
long startRuntime = System.currentTimeMillis(); long startRuntime = System.currentTimeMillis();
String method = getScriptMethod(); String method = getScriptMethod();
String scriptUrl = getScriptUrl(); String scriptUrl = null;
try try
{ {
// extract script url
scriptUrl = getScriptUrl();
if (scriptUrl == null || scriptUrl.length() == 0)
{
throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Script URL not specified");
}
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("Processing script url (" + method + ") " + scriptUrl); logger.debug("Processing script url (" + method + ") " + scriptUrl);
WebScriptMatch match = registry.findWebScript(method, scriptUrl); WebScriptMatch match = registry.findWebScript(method, scriptUrl);
if (match != null) if (match == null || match.getKind() == WebScriptMatch.Kind.URI)
{ {
// setup web script context if (match == null)
final WebScriptRequest scriptReq = createRequest(match);
final WebScriptResponse scriptRes = createResponse();
if (logger.isDebugEnabled())
logger.debug("Agent: " + scriptReq.getAgent());
long startScript = System.currentTimeMillis();
final WebScript script = match.getWebScript();
final WebScriptDescription description = script.getDescription();
try
{ {
String msg = "Script url " + scriptUrl + " does not map to a Web Script.";
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug(msg);
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, msg);
}
else
{
String msg = "Script url " + scriptUrl + " does not support the method " + method;
if (logger.isDebugEnabled())
logger.debug(msg);
throw new WebScriptException(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
}
}
// create web script request & response
final WebScriptRequest scriptReq = createRequest(match);
final WebScriptResponse scriptRes = createResponse();
if (logger.isDebugEnabled())
logger.debug("Agent: " + scriptReq.getAgent());
long startScript = System.currentTimeMillis();
final WebScript script = match.getWebScript();
final WebScriptDescription description = script.getDescription();
try
{
if (logger.isDebugEnabled())
{
String user = AuthenticationUtil.getCurrentUserName();
String locale = I18NUtil.getLocale().toString();
String reqFormat = scriptReq.getFormat();
String format = (reqFormat == null || reqFormat.length() == 0) ? "default" : scriptReq.getFormat();
WebScriptDescription desc = scriptReq.getServiceMatch().getWebScript().getDescription();
logger.debug("Format style: " + desc.getFormatStyle());
logger.debug("Default format: " + desc.getDefaultFormat());
logger.debug("Invoking Web Script " + description.getId() + (user == null ? " (unauthenticated)" : " (authenticated as " + user + ") (format " + format + ") (" + locale + ")"));
}
if (description.getRequiredTransaction() == RequiredTransaction.none)
{
authenticatedExecute(scriptReq, scriptRes);
}
else
{
// encapsulate script within transaction
TransactionUtil.TransactionWork<Object> work = new TransactionUtil.TransactionWork<Object>()
{ {
String user = AuthenticationUtil.getCurrentUserName(); public Object doWork() throws Throwable
String locale = I18NUtil.getLocale().toString(); {
String reqFormat = scriptReq.getFormat(); if (logger.isDebugEnabled())
String format = (reqFormat == null || reqFormat.length() == 0) ? "default" : scriptReq.getFormat(); logger.debug("Begin transaction: " + description.getRequiredTransaction());
WebScriptDescription desc = scriptReq.getServiceMatch().getWebScript().getDescription();
logger.debug("Format style: " + desc.getFormatStyle()); authenticatedExecute(scriptReq, scriptRes);
logger.debug("Default format: " + desc.getDefaultFormat());
logger.debug("Invoking Web Script " + description.getId() + (user == null ? " (unauthenticated)" : " (authenticated as " + user + ") (format " + format + ") (" + locale + ")")); if (logger.isDebugEnabled())
} logger.debug("End transaction: " + description.getRequiredTransaction());
if (description.getRequiredTransaction() == RequiredTransaction.none) return null;
}
};
if (description.getRequiredTransaction() == RequiredTransaction.required)
{ {
authenticatedExecute(scriptReq, scriptRes); TransactionUtil.executeInUserTransaction(transactionService, work);
} }
else else
{ {
// encapsulate script within transaction TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, work);
TransactionUtil.TransactionWork<Object> work = new TransactionUtil.TransactionWork<Object>()
{
public Object doWork() throws Throwable
{
if (logger.isDebugEnabled())
logger.debug("Begin transaction: " + description.getRequiredTransaction());
authenticatedExecute(scriptReq, scriptRes);
if (logger.isDebugEnabled())
logger.debug("End transaction: " + description.getRequiredTransaction());
return null;
}
};
if (description.getRequiredTransaction() == RequiredTransaction.required)
{
TransactionUtil.executeInUserTransaction(transactionService, work);
}
else
{
TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, work);
}
}
}
catch(IOException e)
{
throw new WebScriptException("Failed to execute script", e);
}
finally
{
if (logger.isDebugEnabled())
{
long endScript = System.currentTimeMillis();
logger.debug("Web Script " + description.getId() + " executed in " + (endScript - startScript) + "ms");
} }
} }
} }
else finally
{ {
String msg = "Script url (" + method + ") " + scriptUrl + " does not map to a Web Script.";
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug(msg); {
long endScript = System.currentTimeMillis();
logger.debug("Web Script " + description.getId() + " executed in " + (endScript - startScript) + "ms");
}
}
}
catch(Throwable e)
{
// extract status code, if specified
int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
if (e instanceof WebScriptException)
{
statusCode = ((WebScriptException)e).getStatus();
}
throw new WebScriptException(msg); // create web script status for status template rendering
WebScriptStatus status = new WebScriptStatus();
status.setCode(statusCode);
status.setMessage(e.getMessage());
status.setException(e);
// create basic model for status template rendering
WebScriptRequest req = createRequest(null);
WebScriptResponse res = createResponse();
Map<String, Object> model = new HashMap<String, Object>();
model.put("status", status);
model.put("url", new URLModel(req));
// locate status template
// NOTE: search order...
// 1) root located <status>.ftl
// 2) root located status.ftl
String templatePath = "/" + statusCode + ".ftl";
if (!registry.getTemplateProcessor().hasTemplate(templatePath))
{
templatePath = "/status.ftl";
if (!registry.getTemplateProcessor().hasTemplate(templatePath))
{
throw new WebScriptException("Failed to find status template " + status + " (format: " + WebScriptResponse.HTML_FORMAT + ")");
}
}
// render output
if (logger.isDebugEnabled())
{
logger.debug("Sending status " + statusCode + " (Template: " + templatePath + ")");
logger.debug("Rendering response: content type=" + MimetypeMap.MIMETYPE_HTML);
}
res.reset();
res.setStatus(statusCode);
res.setContentType(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8");
try
{
registry.getTemplateProcessor().process(templatePath, model, res.getWriter());
}
catch (IOException e1)
{
throw new WebScriptException("Internal error", e1);
} }
} }
finally finally
@@ -196,7 +267,7 @@ public abstract class WebScriptRuntime
} }
else if (required == RequiredAuthentication.user && isGuest) else if (required == RequiredAuthentication.user && isGuest)
{ {
throw new WebScriptException("Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access."); throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access.");
} }
else else
{ {
@@ -220,7 +291,6 @@ public abstract class WebScriptRuntime
// //
if (authenticate(required, isGuest)) if (authenticate(required, isGuest))
{ {
//
// Execute Web Script // Execute Web Script
wrappedExecute(scriptReq, scriptRes); wrappedExecute(scriptReq, scriptRes);
} }

View File

@@ -120,7 +120,8 @@ public class WebScriptServletRequest extends WebScriptRequestImpl
*/ */
public String getServicePath() public String getServicePath()
{ {
return getServiceContextPath() + req.getPathInfo(); String pathInfo = req.getPathInfo();
return getServiceContextPath() + ((pathInfo == null) ? "" : req.getPathInfo());
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -28,7 +28,6 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Writer; import java.io.Writer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
/** /**
@@ -60,6 +59,14 @@ public class WebScriptServletResponse implements WebScriptResponse
return res; return res;
} }
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int)
*/
public void setStatus(int status)
{
res.setStatus(status);
}
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String) * @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String)
*/ */
@@ -68,6 +75,20 @@ public class WebScriptServletResponse implements WebScriptResponse
res.setContentType(contentType); res.setContentType(contentType);
} }
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#reset()
*/
public void reset()
{
try
{
res.reset();
}
catch(IllegalStateException e)
{
}
}
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#getWriter() * @see org.alfresco.web.scripts.WebScriptResponse#getWriter()
*/ */
@@ -91,5 +112,5 @@ public class WebScriptServletResponse implements WebScriptResponse
{ {
return url; return url;
} }
} }

View File

@@ -0,0 +1,178 @@
/*
* 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 org.alfresco.i18n.I18NUtil;
/**
* Web Script Status
*
* Records the outcome of a Web Script.
*
* @author davidc
*/
public class WebScriptStatus
{
private Throwable exception = null;
private int code = HttpServletResponse.SC_OK;
private String message = null;
private boolean redirect = false;
/**
* @param exception
*/
public void setException(Throwable exception)
{
this.exception = exception;
}
/**
* @return exception
*/
public Throwable getException()
{
return exception;
}
public Throwable jsGet_exception()
{
return getException();
}
/**
* @param message
*/
public void setMessage(String message)
{
this.message = message;
}
public void jsSet_message(String message)
{
setMessage(message);
}
/**
* @return message
*/
public String getMessage()
{
return message;
}
public String jsGet_message()
{
return getMessage();
}
/**
* @param redirect redirect to status code response
*/
public void setRedirect(boolean redirect)
{
this.redirect = redirect;
}
public void jsSet_redirect(boolean redirect)
{
setRedirect(redirect);
}
/**
* @return redirect to status code response
*/
public boolean getRedirect()
{
return redirect;
}
public boolean jsGet_redirect()
{
return getRedirect();
}
/**
* @see javax.servlet.http.HTTPServletResponse
*
* @param code status code
*/
public void setCode(int code)
{
this.code = code;
}
public void jsSet_code(int code)
{
this.code = code;
}
/**
* @return status code
*/
public int getCode()
{
return code;
}
public int jsGet_code()
{
return getCode();
}
/**
* Gets the short name of the status code
*
* @return status code name
*/
public String getCodeName()
{
return I18NUtil.getMessage("webscript.code." + code + ".name");
}
public String jsGet_codeName()
{
return getCodeName();
}
/**
* Gets the description of the status code
*
* @return status code description
*/
public String getCodeDescription()
{
return I18NUtil.getMessage("webscript.code." + code + ".description");
}
public String jsGet_codeDescription()
{
return getCodeDescription();
}
}

View File

@@ -29,7 +29,7 @@ import java.util.Map;
import org.alfresco.web.scripts.DeclarativeWebScript; import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus;
/** /**
@@ -44,7 +44,7 @@ public class Index extends DeclarativeWebScript
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/ */
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f); Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("webscripts", getWebScriptRegistry().getWebScripts()); model.put("webscripts", getWebScriptRegistry().getWebScripts());

View File

@@ -31,7 +31,7 @@ import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptPath; import org.alfresco.web.scripts.WebScriptPath;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus;
/** /**
@@ -46,7 +46,7 @@ public class IndexPackage extends DeclarativeWebScript
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/ */
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
// extract web script package // extract web script package
String packagePath = req.getExtensionPath(); String packagePath = req.getExtensionPath();

View File

@@ -31,7 +31,7 @@ import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptPath; import org.alfresco.web.scripts.WebScriptPath;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus;
/** /**
@@ -46,7 +46,7 @@ public class IndexURI extends DeclarativeWebScript
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/ */
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
// extract web script package // extract web script package
String uriPath = req.getExtensionPath(); String uriPath = req.getExtensionPath();

View File

@@ -31,7 +31,7 @@ import java.util.Map;
import org.alfresco.web.scripts.DeclarativeWebScript; import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus;
/** /**
@@ -46,7 +46,7 @@ public class IndexUpdate extends DeclarativeWebScript
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/ */
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
List<String> tasks = new ArrayList<String>(); List<String> tasks = new ArrayList<String>();

View File

@@ -42,7 +42,7 @@ import org.alfresco.util.ParameterCheck;
import org.alfresco.web.scripts.DeclarativeWebScript; import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -78,7 +78,7 @@ public class KeywordSearch extends DeclarativeWebScript
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/ */
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
// //
// process arguments // process arguments

View File

@@ -27,11 +27,14 @@ package org.alfresco.web.scripts.bean;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.web.scripts.DeclarativeWebScript; import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus;
/** /**
@@ -57,26 +60,34 @@ public class Login extends DeclarativeWebScript
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/ */
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
// extract username and password // extract username and password
String username = req.getParameter("u"); String username = req.getParameter("u");
if (username == null || username.length() == 0) if (username == null || username.length() == 0)
{ {
throw new WebScriptException("Username has not been specified"); throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Username not specified");
} }
String password = req.getParameter("pw"); String password = req.getParameter("pw");
if (password == null)
// get ticket {
authenticationService.authenticate(username, password == null ? null : password.toCharArray()); throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Password not specified");
}
// add ticket to model for javascript and template access
try try
{ {
// get ticket
authenticationService.authenticate(username, password.toCharArray());
// add ticket to model for javascript and template access
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f); Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("ticket", authenticationService.getCurrentTicket()); model.put("ticket", authenticationService.getCurrentTicket());
return model; return model;
} }
catch(AuthenticationException e)
{
throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Login failed");
}
finally finally
{ {
authenticationService.clearCurrentSecurityContext(); authenticationService.clearCurrentSecurityContext();

View File

@@ -27,13 +27,15 @@ package org.alfresco.web.scripts.bean;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.TicketComponent; import org.alfresco.repo.security.authentication.TicketComponent;
import org.alfresco.web.scripts.DeclarativeWebScript; import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus;
/** /**
@@ -59,15 +61,19 @@ public class LoginTicket extends DeclarativeWebScript
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/ */
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
// retrieve ticket from request and current ticket // retrieve ticket from request and current ticket
String ticket = req.getExtensionPath(); String ticket = req.getExtensionPath();
if (ticket == null && ticket.length() == 0) if (ticket == null && ticket.length() == 0)
{ {
throw new WebScriptException("Ticket not specified"); throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Ticket not specified");
} }
// construct model for ticket
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("ticket", ticket);
try try
{ {
String ticketUser = ticketComponent.validateTicket(ticket); String ticketUser = ticketComponent.validateTicket(ticket);
@@ -75,17 +81,18 @@ public class LoginTicket extends DeclarativeWebScript
// do not go any further if tickets are different // do not go any further if tickets are different
if (!AuthenticationUtil.getCurrentUserName().equals(ticketUser)) if (!AuthenticationUtil.getCurrentUserName().equals(ticketUser))
{ {
// TODO: 404 error status.setRedirect(true);
throw new WebScriptException("Ticket not found"); status.setCode(HttpServletResponse.SC_NOT_FOUND);
status.setMessage("Ticket not found");
} }
} }
catch(AuthenticationException e) catch(AuthenticationException e)
{ {
throw new WebScriptException("Ticket not found"); status.setRedirect(true);
status.setCode(HttpServletResponse.SC_NOT_FOUND);
status.setMessage("Ticket not found");
} }
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("ticket", ticket);
return model; return model;
} }

View File

@@ -27,6 +27,8 @@ package org.alfresco.web.scripts.bean;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.TicketComponent; import org.alfresco.repo.security.authentication.TicketComponent;
@@ -34,7 +36,7 @@ import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.web.scripts.DeclarativeWebScript; import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus;
/** /**
@@ -69,15 +71,19 @@ public class LoginTicketDelete extends DeclarativeWebScript
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/ */
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
// retrieve ticket from request and current ticket // retrieve ticket from request and current ticket
String ticket = req.getExtensionPath(); String ticket = req.getExtensionPath();
if (ticket == null && ticket.length() == 0) if (ticket == null && ticket.length() == 0)
{ {
throw new WebScriptException("Ticket not specified"); throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Ticket not specified");
} }
// construct model for ticket
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("ticket", ticket);
try try
{ {
String ticketUser = ticketComponent.validateTicket(ticket); String ticketUser = ticketComponent.validateTicket(ticket);
@@ -85,20 +91,23 @@ public class LoginTicketDelete extends DeclarativeWebScript
// do not go any further if tickets are different // do not go any further if tickets are different
if (!AuthenticationUtil.getCurrentUserName().equals(ticketUser)) if (!AuthenticationUtil.getCurrentUserName().equals(ticketUser))
{ {
// TODO: 404 error status.setRedirect(true);
throw new WebScriptException("Ticket not found"); status.setCode(HttpServletResponse.SC_NOT_FOUND);
status.setMessage("Ticket not found");
}
else
{
// delete the ticket
authenticationService.invalidateTicket(ticket);
} }
} }
catch(AuthenticationException e) catch(AuthenticationException e)
{ {
throw new WebScriptException("Ticket not found"); status.setRedirect(true);
status.setCode(HttpServletResponse.SC_NOT_FOUND);
status.setMessage("Ticket not found");
} }
// delete the ticket
authenticationService.invalidateTicket(ticket);
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
model.put("ticket", ticket);
return model; return model;
} }

View File

@@ -36,7 +36,7 @@ import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.web.config.OpenSearchConfigElement; import org.alfresco.web.config.OpenSearchConfigElement;
import org.alfresco.web.scripts.DeclarativeWebScript; import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@@ -80,7 +80,7 @@ public class SearchEngines extends DeclarativeWebScript
* @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse) * @see org.alfresco.web.scripts.DeclarativeWebScript#executeImpl(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
*/ */
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptResponse res) protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
{ {
String urlType = req.getParameter("type"); String urlType = req.getParameter("type");
if (urlType == null || urlType.length() == 0) if (urlType == null || urlType.length() == 0)

View File

@@ -105,6 +105,13 @@ public class WebScriptJSFResponse implements WebScriptResponse
return buf.toString(); return buf.toString();
} }
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#reset()
*/
public void reset()
{
}
/** /**
* @see org.alfresco.web.scripts.WebScriptResponse#getOutputStream() * @see org.alfresco.web.scripts.WebScriptResponse#getOutputStream()
*/ */
@@ -121,6 +128,13 @@ public class WebScriptJSFResponse implements WebScriptResponse
return fc.getResponseWriter(); return fc.getResponseWriter();
} }
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int)
*/
public void setStatus(int status)
{
}
/** /**
* @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String) * @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String)
*/ */
@@ -128,4 +142,5 @@ public class WebScriptJSFResponse implements WebScriptResponse
{ {
// Alfresco JSF framework only supports the default of text-html // Alfresco JSF framework only supports the default of text-html
} }
} }

View File

@@ -66,6 +66,13 @@ public class WebScriptPortletResponse implements WebScriptResponse
return res; return res;
} }
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int)
*/
public void setStatus(int status)
{
}
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String) * @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String)
*/ */
@@ -74,6 +81,20 @@ public class WebScriptPortletResponse implements WebScriptResponse
res.setContentType(contentType); res.setContentType(contentType);
} }
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#reset()
*/
public void reset()
{
try
{
res.reset();
}
catch(IllegalStateException e)
{
}
}
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#getWriter() * @see org.alfresco.web.scripts.WebScriptResponse#getWriter()
*/ */
@@ -105,5 +126,5 @@ public class WebScriptPortletResponse implements WebScriptResponse
} }
return portletUrl.toString(); return portletUrl.toString();
} }
} }