diff --git a/config/alfresco/messages/webscripts.properties b/config/alfresco/messages/webscripts.properties
index f79e5bd3a8..fe82a3a1cb 100644
--- a/config/alfresco/messages/webscripts.properties
+++ b/config/alfresco/messages/webscripts.properties
@@ -1 +1,86 @@
-testserver.help=alfresco/messages/webscripts-test-help.txt
\ No newline at end of file
+##
+## 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
diff --git a/config/alfresco/templates/webscripts/org/alfresco/index.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/index.get.desc.xml
index 70638bdc72..3564ad84f6 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/index.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/index.get.desc.xml
@@ -1,5 +1,6 @@
Web Scripts Documentation
Web Scripts Documentation
+
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/status.ftl b/config/alfresco/templates/webscripts/status.ftl
new file mode 100644
index 0000000000..4ddce6d97f
--- /dev/null
+++ b/config/alfresco/templates/webscripts/status.ftl
@@ -0,0 +1,33 @@
+
+
+
+
+ Web Script Status ${status.code} - ${status.codeName}
+
+
+
+
+
+
+ Web Script Status ${status.code} - ${status.codeName}
+
+
+
+
+ The Web Script ${url.service} has responded with a status of ${status.code} - ${status.codeName}.
+
+
+
+ ${status.code} Description: ${status.codeDescription}
+
+ Message: ${status.message!"<Not specified> "}
+ Exception: ${status.exception!"None"}
+ <#if status.exception?exists>
+
+ <#list status.exception.stackTrace as element>
+ ${element}
+ #list>
+ #if>
+
+
+
diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml
index c86956ce41..c63f637954 100644
--- a/config/alfresco/web-scripts-application-context.xml
+++ b/config/alfresco/web-scripts-application-context.xml
@@ -3,6 +3,19 @@
+
+
+
+
+
+
+
+ alfresco.messages.webscripts
+
+
+
+
+
diff --git a/source/java/org/alfresco/web/scripts/AbstractWebScript.java b/source/java/org/alfresco/web/scripts/AbstractWebScript.java
index a2e1c507e6..377bea1a55 100644
--- a/source/java/org/alfresco/web/scripts/AbstractWebScript.java
+++ b/source/java/org/alfresco/web/scripts/AbstractWebScript.java
@@ -24,10 +24,12 @@
*/
package org.alfresco.web.scripts;
+import java.io.IOException;
import java.io.Writer;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.alfresco.repo.jscript.Node;
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.web.scripts.WebScriptDescription.RequiredAuthentication;
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
{
+ // Logger
+ private static final Log logger = LogFactory.getLog(AbstractWebScript.class);
+
// dependencies
private WebScriptContext scriptContext;
private WebScriptRegistry scriptRegistry;
private WebScriptDescription description;
private ServiceRegistry serviceRegistry;
private DescriptorService descriptorService;
+
+ // Status Template cache
+ private Map statusTemplates = new HashMap();
+ private ReentrantReadWriteLock statusTemplateLock = new ReentrantReadWriteLock();
//
@@ -102,6 +113,15 @@ public abstract class AbstractWebScript implements WebScript
public void init(WebScriptRegistry 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
*
* @param req web script request
- * @param res web script response
* @param customModel custom model entries
*
* @return script model
*/
- final protected Map createScriptModel(WebScriptRequest req, WebScriptResponse res, Map customModel)
+ final protected Map createScriptModel(WebScriptRequest req, Map customModel)
{
// create script model
Map model = new HashMap(7, 1.0f);
@@ -301,6 +320,188 @@ public abstract class AbstractWebScript implements WebScript
{
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 model)
+ throws IOException
+ {
+ // locate status template
+ // NOTE: search order...
+ // NOTE: package path is recursed to root package
+ // 1) script located ...ftl
+ // 2) script located ..status.ftl
+ // 3) package located /..ftl
+ // 4) package located /.status.ftl
+ // 5) default .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
@@ -312,4 +513,26 @@ public abstract class AbstractWebScript implements WebScript
{
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;
+ }
+
}
diff --git a/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java b/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java
index f7104491a5..789946b184 100644
--- a/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java
+++ b/source/java/org/alfresco/web/scripts/DeclarativeWebScript.java
@@ -31,11 +31,12 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
+import javax.servlet.http.HttpServletResponse;
+
import org.alfresco.repo.jscript.Node;
import org.alfresco.repo.jscript.ScriptableHashMap;
import org.alfresco.repo.template.TemplateNode;
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.LogFactory;
import org.mozilla.javascript.Context;
@@ -76,57 +77,90 @@ public class DeclarativeWebScript extends AbstractWebScript
*/
final public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException
{
- // construct data model for template
- Map model = executeImpl(req, res);
- if (model == null)
- {
- model = new HashMap(7, 1.0f);
- }
-
- // execute script if it exists
- if (executeScript != null)
- {
- if (logger.isDebugEnabled())
- logger.debug("Executing script " + executeScript);
-
- Map scriptModel = createScriptModel(req, res, model);
- // add return model allowing script to add items to template model
- Map returnModel = new ScriptableHashMap();
- scriptModel.put("model", returnModel);
- executeScript(executeScript, scriptModel);
- mergeScriptModelIntoTemplateModel(returnModel, model);
- }
-
- // process requested format
+ // retrieve 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
{
+ // 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 model = executeImpl(req, status);
+ if (model == null)
+ {
+ model = new HashMap(7, 1.0f);
+ }
+ model.put("status", status);
+
+ // execute script if it exists
+ if (executeScript != null)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Executing script " + executeScript);
+
+ Map scriptModel = createScriptModel(req, model);
+ // add return model allowing script to add items to template model
+ Map returnModel = new ScriptableHashMap();
+ scriptModel.put("model", returnModel);
+ executeScript(executeScript, scriptModel);
+ mergeScriptModelIntoTemplateModel(returnModel, model);
+ }
+
+ // create model for template rendering
Map 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 customModel = new HashMap();
+ customModel.put("status", status);
+ Map templateModel = createTemplateModel(req, res, customModel);
+ sendStatus(req, res, status, format, templateModel);
}
}
-
+
/**
* Merge script generated model into template-ready model
*
@@ -192,10 +226,9 @@ public class DeclarativeWebScript extends AbstractWebScript
* Execute custom Java logic
*
* @param req Web Script request
- * @param res Web Script response
* @return custom service model
*/
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
return null;
}
diff --git a/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java b/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java
index 6a6c0ecafe..afe4ec2cc9 100644
--- a/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java
+++ b/source/java/org/alfresco/web/scripts/DeclarativeWebScriptRegistry.java
@@ -81,7 +81,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
// map of web scripts by url
// NOTE: The map is sorted by url (descending order)
- private Map webscriptsByURL = new TreeMap(Collections.reverseOrder());
+ private Map webscriptsByURL = new TreeMap(Collections.reverseOrder());
// map of web script packages by path
private Map packageByPath = new TreeMap();
@@ -258,6 +258,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
for (URI uri : serviceDesc.getURIs())
{
// establish static part of url template
+ boolean wildcard = false;
String uriTemplate = uri.getURI();
int queryArgIdx = uriTemplate.indexOf('?');
if (queryArgIdx != -1)
@@ -268,6 +269,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
if (tokenIdx != -1)
{
uriTemplate = uriTemplate.substring(0, tokenIdx);
+ wildcard = true;
}
if (serviceDesc.getFormatStyle() != WebScriptDescription.FormatStyle.argument)
{
@@ -282,7 +284,8 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
String uriIdx = serviceDesc.getMethod().toString() + ":" + uriTemplate;
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()))
{
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
{
- webscriptsByURL.put(uriIdx, serviceImpl);
+ URLIndex urlIndex = new URLIndex(uriTemplate, wildcard, serviceImpl);
+ webscriptsByURL.put(uriIdx, urlIndex);
if (logger.isDebugEnabled())
logger.debug("Registered Web Script URL '" + uriIdx + "'");
@@ -357,7 +361,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
subpath = path.createChildPath(part);
uriByPath.put(subpath.getPath(), subpath);
if (logger.isDebugEnabled())
- logger.debug("Registered Web Script URI " + subpath.getPath());
+ logger.debug("Registered Web Script URI Path " + subpath.getPath());
}
path = subpath;
}
@@ -387,7 +391,8 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
}
// 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
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)
{
+ long startTime = System.currentTimeMillis();
+
// TODO: Replace with more efficient approach
+ String matchedPath = null;
DeclarativeWebScriptMatch apiServiceMatch = null;
String match = method.toString().toUpperCase() + ":" + uri;
- for (Map.Entry service : webscriptsByURL.entrySet())
+
+ // locate full match - on URI and METHOD
+ for (Map.Entry entry : webscriptsByURL.entrySet())
{
- String indexedPath = service.getKey();
- if (match.startsWith(indexedPath))
+ URLIndex urlIndex = entry.getValue();
+ 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(matchPath, service.getValue());
+ apiServiceMatch = new DeclarativeWebScriptMatch(urlIndex.path, urlIndex.script);
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;
}
@@ -634,6 +658,7 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
{
private String path;
private WebScript service;
+ private Kind kind;
/**
* Construct
@@ -643,9 +668,30 @@ public class DeclarativeWebScriptRegistry extends AbstractLifecycleBean
*/
public DeclarativeWebScriptMatch(String path, WebScript service)
{
+ this.kind = Kind.FULL;
this.path = path;
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)
* @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;
+ }
+
}
diff --git a/source/java/org/alfresco/web/scripts/TemplateProcessor.java b/source/java/org/alfresco/web/scripts/TemplateProcessor.java
index 0550da5f5e..beb41a7729 100644
--- a/source/java/org/alfresco/web/scripts/TemplateProcessor.java
+++ b/source/java/org/alfresco/web/scripts/TemplateProcessor.java
@@ -24,6 +24,8 @@
*/
package org.alfresco.web.scripts;
+import java.io.IOException;
+
import org.alfresco.repo.template.FreeMarkerProcessor;
import org.alfresco.repo.template.QNameAwareObjectWrapper;
import org.alfresco.service.cmr.repository.ProcessorExtension;
@@ -37,6 +39,7 @@ import org.springframework.context.ApplicationListener;
import freemarker.cache.MruCacheStorage;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
+import freemarker.template.Template;
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
*/
diff --git a/source/java/org/alfresco/web/scripts/TestWebScriptServer.java b/source/java/org/alfresco/web/scripts/TestWebScriptServer.java
index 77a5da20c5..4ec42f4135 100644
--- a/source/java/org/alfresco/web/scripts/TestWebScriptServer.java
+++ b/source/java/org/alfresco/web/scripts/TestWebScriptServer.java
@@ -353,12 +353,7 @@ public class TestWebScriptServer
command[0].equals("post") ||
command[0].equals("delete"))
{
- if (command.length < 2)
- {
- return "Syntax Error.\n";
- }
-
- String uri = command[1];
+ String uri = (command.length > 1) ? command[1] : null;
MockHttpServletResponse res = submitRequest(command[0], uri);
bout.write(res.getContentAsByteArray());
out.println();
@@ -392,23 +387,24 @@ public class TestWebScriptServer
{
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.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;
}
diff --git a/source/java/org/alfresco/web/scripts/WebScriptException.java b/source/java/org/alfresco/web/scripts/WebScriptException.java
index f7c336e226..bc7ec77c6e 100644
--- a/source/java/org/alfresco/web/scripts/WebScriptException.java
+++ b/source/java/org/alfresco/web/scripts/WebScriptException.java
@@ -24,6 +24,8 @@
*/
package org.alfresco.web.scripts;
+import javax.servlet.http.HttpServletResponse;
+
import org.alfresco.error.AlfrescoRuntimeException;
/**
@@ -35,6 +37,15 @@ public class WebScriptException extends AlfrescoRuntimeException
{
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)
{
super(msgId);
@@ -54,4 +65,10 @@ public class WebScriptException extends AlfrescoRuntimeException
{
super(msgId, args, cause);
}
+
+ public int getStatus()
+ {
+ return status;
+ }
+
}
diff --git a/source/java/org/alfresco/web/scripts/WebScriptMatch.java b/source/java/org/alfresco/web/scripts/WebScriptMatch.java
index 325368f568..90cc97e12b 100644
--- a/source/java/org/alfresco/web/scripts/WebScriptMatch.java
+++ b/source/java/org/alfresco/web/scripts/WebScriptMatch.java
@@ -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
*/
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
*
@@ -43,7 +55,7 @@ public interface WebScriptMatch
/**
* Gets the matching web script
*
- * @return service
+ * @return service (or null, if match kind is URI)
*/
public WebScript getWebScript();
diff --git a/source/java/org/alfresco/web/scripts/WebScriptRequestImpl.java b/source/java/org/alfresco/web/scripts/WebScriptRequestImpl.java
index 420e0fd399..c5c724a4ba 100644
--- a/source/java/org/alfresco/web/scripts/WebScriptRequestImpl.java
+++ b/source/java/org/alfresco/web/scripts/WebScriptRequestImpl.java
@@ -40,13 +40,21 @@ public abstract class WebScriptRequestImpl implements WebScriptRequest
*/
public String getExtensionPath()
{
- String servicePath = getServiceMatch().getPath();
- String extensionPath = getPathInfo();
- int extIdx = extensionPath.indexOf(servicePath);
- if (extIdx != -1)
+ String extensionPath = "";
+ WebScriptMatch match = getServiceMatch();
+ if (match != null)
{
- int extLength = (servicePath.endsWith("/") ? servicePath.length() : servicePath.length() + 1);
- extensionPath = extensionPath.substring(extIdx + extLength);
+ String servicePath = getServiceMatch().getPath();
+ 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;
}
@@ -65,30 +73,37 @@ public abstract class WebScriptRequestImpl implements WebScriptRequest
public String getFormat()
{
String format = null;
- FormatStyle style = getServiceMatch().getWebScript().getDescription().getFormatStyle();
-
- // extract format from extension
- if (style == FormatStyle.extension || style == FormatStyle.any)
+ WebScriptMatch match = getServiceMatch();
+ if (match != null)
{
- String pathInfo = getPathInfo();
- int extIdx = pathInfo.lastIndexOf('.');
- if (extIdx != -1)
+ FormatStyle style = getServiceMatch().getWebScript().getDescription().getFormatStyle();
+
+ // extract format from extension
+ if (style == FormatStyle.extension || style == FormatStyle.any)
{
- 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)
+ String pathInfo = getPathInfo();
+ if (pathInfo != null)
{
- 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()
{
- 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)
{
return style;
diff --git a/source/java/org/alfresco/web/scripts/WebScriptResponse.java b/source/java/org/alfresco/web/scripts/WebScriptResponse.java
index 3dde185821..7b6aef5017 100644
--- a/source/java/org/alfresco/web/scripts/WebScriptResponse.java
+++ b/source/java/org/alfresco/web/scripts/WebScriptResponse.java
@@ -44,6 +44,12 @@ public interface WebScriptResponse
public static final String JSON_FORMAT = "json";
public static final String OPENSEARCH_DESCRIPTION_FORMAT = "opensearchdescription";
+ /**
+ * Sets the Response Status
+ *
+ * @param status
+ */
+ public void setStatus(int status);
/**
* Sets the Content Type
@@ -68,6 +74,11 @@ public interface WebScriptResponse
*/
public OutputStream getOutputStream() throws IOException;
+ /**
+ * Clears response buffer
+ */
+ public void reset();
+
/**
* Encode a script URL
*
diff --git a/source/java/org/alfresco/web/scripts/WebScriptRuntime.java b/source/java/org/alfresco/web/scripts/WebScriptRuntime.java
index 4ac9ef8666..1c969ff752 100644
--- a/source/java/org/alfresco/web/scripts/WebScriptRuntime.java
+++ b/source/java/org/alfresco/web/scripts/WebScriptRuntime.java
@@ -25,8 +25,14 @@
package org.alfresco.web.scripts;
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.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.service.transaction.TransactionService;
@@ -77,94 +83,159 @@ public abstract class WebScriptRuntime
long startRuntime = System.currentTimeMillis();
String method = getScriptMethod();
- String scriptUrl = getScriptUrl();
-
+ String scriptUrl = null;
+
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())
logger.debug("Processing script url (" + method + ") " + scriptUrl);
WebScriptMatch match = registry.findWebScript(method, scriptUrl);
- if (match != null)
+ if (match == null || match.getKind() == WebScriptMatch.Kind.URI)
{
- // setup web script context
- 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 (match == null)
{
+ String msg = "Script url " + scriptUrl + " does not map to a Web Script.";
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 work = new TransactionUtil.TransactionWork()
{
- 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)
+ 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)
{
- authenticatedExecute(scriptReq, scriptRes);
+ TransactionUtil.executeInUserTransaction(transactionService, work);
}
else
{
- // encapsulate script within transaction
- TransactionUtil.TransactionWork work = new TransactionUtil.TransactionWork()
- {
- 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");
+ TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, work);
}
}
}
- else
+ finally
{
- String msg = "Script url (" + method + ") " + scriptUrl + " does not map to a Web Script.";
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 model = new HashMap();
+ model.put("status", status);
+ model.put("url", new URLModel(req));
+
+ // locate status template
+ // NOTE: search order...
+ // 1) root located .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
@@ -196,7 +267,7 @@ public abstract class WebScriptRuntime
}
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
{
@@ -220,7 +291,6 @@ public abstract class WebScriptRuntime
//
if (authenticate(required, isGuest))
{
- //
// Execute Web Script
wrappedExecute(scriptReq, scriptRes);
}
diff --git a/source/java/org/alfresco/web/scripts/WebScriptServletRequest.java b/source/java/org/alfresco/web/scripts/WebScriptServletRequest.java
index fd08ad3b2c..b9155e50c5 100644
--- a/source/java/org/alfresco/web/scripts/WebScriptServletRequest.java
+++ b/source/java/org/alfresco/web/scripts/WebScriptServletRequest.java
@@ -120,7 +120,8 @@ public class WebScriptServletRequest extends WebScriptRequestImpl
*/
public String getServicePath()
{
- return getServiceContextPath() + req.getPathInfo();
+ String pathInfo = req.getPathInfo();
+ return getServiceContextPath() + ((pathInfo == null) ? "" : req.getPathInfo());
}
/* (non-Javadoc)
diff --git a/source/java/org/alfresco/web/scripts/WebScriptServletResponse.java b/source/java/org/alfresco/web/scripts/WebScriptServletResponse.java
index c0469dfdeb..dc622abfac 100644
--- a/source/java/org/alfresco/web/scripts/WebScriptServletResponse.java
+++ b/source/java/org/alfresco/web/scripts/WebScriptServletResponse.java
@@ -28,7 +28,6 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
-import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
@@ -60,6 +59,14 @@ public class WebScriptServletResponse implements WebScriptResponse
return res;
}
+ /* (non-Javadoc)
+ * @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int)
+ */
+ public void setStatus(int status)
+ {
+ res.setStatus(status);
+ }
+
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String)
*/
@@ -68,6 +75,20 @@ public class WebScriptServletResponse implements WebScriptResponse
res.setContentType(contentType);
}
+ /* (non-Javadoc)
+ * @see org.alfresco.web.scripts.WebScriptResponse#reset()
+ */
+ public void reset()
+ {
+ try
+ {
+ res.reset();
+ }
+ catch(IllegalStateException e)
+ {
+ }
+ }
+
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#getWriter()
*/
@@ -91,5 +112,5 @@ public class WebScriptServletResponse implements WebScriptResponse
{
return url;
}
-
+
}
diff --git a/source/java/org/alfresco/web/scripts/WebScriptStatus.java b/source/java/org/alfresco/web/scripts/WebScriptStatus.java
new file mode 100644
index 0000000000..87f91b5847
--- /dev/null
+++ b/source/java/org/alfresco/web/scripts/WebScriptStatus.java
@@ -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();
+ }
+
+}
diff --git a/source/java/org/alfresco/web/scripts/bean/Index.java b/source/java/org/alfresco/web/scripts/bean/Index.java
index 1b163aa6d2..5a12684b68 100644
--- a/source/java/org/alfresco/web/scripts/bean/Index.java
+++ b/source/java/org/alfresco/web/scripts/bean/Index.java
@@ -29,7 +29,7 @@ import java.util.Map;
import org.alfresco.web.scripts.DeclarativeWebScript;
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)
*/
@Override
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
Map model = new HashMap(7, 1.0f);
model.put("webscripts", getWebScriptRegistry().getWebScripts());
diff --git a/source/java/org/alfresco/web/scripts/bean/IndexPackage.java b/source/java/org/alfresco/web/scripts/bean/IndexPackage.java
index 1f39c380a5..09a0c54f42 100644
--- a/source/java/org/alfresco/web/scripts/bean/IndexPackage.java
+++ b/source/java/org/alfresco/web/scripts/bean/IndexPackage.java
@@ -31,7 +31,7 @@ import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptPath;
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)
*/
@Override
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
// extract web script package
String packagePath = req.getExtensionPath();
diff --git a/source/java/org/alfresco/web/scripts/bean/IndexURI.java b/source/java/org/alfresco/web/scripts/bean/IndexURI.java
index f5e360d848..4a57f9593b 100644
--- a/source/java/org/alfresco/web/scripts/bean/IndexURI.java
+++ b/source/java/org/alfresco/web/scripts/bean/IndexURI.java
@@ -31,7 +31,7 @@ import org.alfresco.web.scripts.DeclarativeWebScript;
import org.alfresco.web.scripts.WebScriptException;
import org.alfresco.web.scripts.WebScriptPath;
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)
*/
@Override
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
// extract web script package
String uriPath = req.getExtensionPath();
diff --git a/source/java/org/alfresco/web/scripts/bean/IndexUpdate.java b/source/java/org/alfresco/web/scripts/bean/IndexUpdate.java
index f60c70545f..407edbf0c0 100644
--- a/source/java/org/alfresco/web/scripts/bean/IndexUpdate.java
+++ b/source/java/org/alfresco/web/scripts/bean/IndexUpdate.java
@@ -31,7 +31,7 @@ import java.util.Map;
import org.alfresco.web.scripts.DeclarativeWebScript;
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)
*/
@Override
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
List tasks = new ArrayList();
diff --git a/source/java/org/alfresco/web/scripts/bean/KeywordSearch.java b/source/java/org/alfresco/web/scripts/bean/KeywordSearch.java
index 16935ffa0b..bc9bf09169 100644
--- a/source/java/org/alfresco/web/scripts/bean/KeywordSearch.java
+++ b/source/java/org/alfresco/web/scripts/bean/KeywordSearch.java
@@ -42,7 +42,7 @@ 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.scripts.WebScriptStatus;
import org.apache.commons.logging.Log;
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)
*/
@Override
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
//
// process arguments
diff --git a/source/java/org/alfresco/web/scripts/bean/Login.java b/source/java/org/alfresco/web/scripts/bean/Login.java
index ddcb9d54ce..83ba1f60db 100644
--- a/source/java/org/alfresco/web/scripts/bean/Login.java
+++ b/source/java/org/alfresco/web/scripts/bean/Login.java
@@ -27,11 +27,14 @@ package org.alfresco.web.scripts.bean;
import java.util.HashMap;
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.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.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)
*/
@Override
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
// extract username and password
String username = req.getParameter("u");
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");
-
- // get ticket
- authenticationService.authenticate(username, password == null ? null : password.toCharArray());
-
- // add ticket to model for javascript and template access
+ if (password == null)
+ {
+ throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Password not specified");
+ }
+
try
{
+ // get ticket
+ authenticationService.authenticate(username, password.toCharArray());
+
+ // add ticket to model for javascript and template access
Map model = new HashMap(7, 1.0f);
model.put("ticket", authenticationService.getCurrentTicket());
return model;
}
+ catch(AuthenticationException e)
+ {
+ throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Login failed");
+ }
finally
{
authenticationService.clearCurrentSecurityContext();
diff --git a/source/java/org/alfresco/web/scripts/bean/LoginTicket.java b/source/java/org/alfresco/web/scripts/bean/LoginTicket.java
index 1515853d8b..ba42d372d3 100644
--- a/source/java/org/alfresco/web/scripts/bean/LoginTicket.java
+++ b/source/java/org/alfresco/web/scripts/bean/LoginTicket.java
@@ -27,13 +27,15 @@ package org.alfresco.web.scripts.bean;
import java.util.HashMap;
import java.util.Map;
+import javax.servlet.http.HttpServletResponse;
+
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.TicketComponent;
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.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)
*/
@Override
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
// retrieve ticket from request and current ticket
String ticket = req.getExtensionPath();
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 model = new HashMap(7, 1.0f);
+ model.put("ticket", ticket);
+
try
{
String ticketUser = ticketComponent.validateTicket(ticket);
@@ -75,17 +81,18 @@ public class LoginTicket extends DeclarativeWebScript
// do not go any further if tickets are different
if (!AuthenticationUtil.getCurrentUserName().equals(ticketUser))
{
- // TODO: 404 error
- throw new WebScriptException("Ticket not found");
+ status.setRedirect(true);
+ status.setCode(HttpServletResponse.SC_NOT_FOUND);
+ status.setMessage("Ticket not found");
}
}
catch(AuthenticationException e)
{
- throw new WebScriptException("Ticket not found");
+ status.setRedirect(true);
+ status.setCode(HttpServletResponse.SC_NOT_FOUND);
+ status.setMessage("Ticket not found");
}
- Map model = new HashMap(7, 1.0f);
- model.put("ticket", ticket);
return model;
}
diff --git a/source/java/org/alfresco/web/scripts/bean/LoginTicketDelete.java b/source/java/org/alfresco/web/scripts/bean/LoginTicketDelete.java
index 898e5f335e..f65b77093c 100644
--- a/source/java/org/alfresco/web/scripts/bean/LoginTicketDelete.java
+++ b/source/java/org/alfresco/web/scripts/bean/LoginTicketDelete.java
@@ -27,6 +27,8 @@ package org.alfresco.web.scripts.bean;
import java.util.HashMap;
import java.util.Map;
+import javax.servlet.http.HttpServletResponse;
+
import org.alfresco.repo.security.authentication.AuthenticationException;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
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.WebScriptException;
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)
*/
@Override
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
// retrieve ticket from request and current ticket
String ticket = req.getExtensionPath();
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 model = new HashMap(7, 1.0f);
+ model.put("ticket", ticket);
+
try
{
String ticketUser = ticketComponent.validateTicket(ticket);
@@ -85,20 +91,23 @@ public class LoginTicketDelete extends DeclarativeWebScript
// do not go any further if tickets are different
if (!AuthenticationUtil.getCurrentUserName().equals(ticketUser))
{
- // TODO: 404 error
- throw new WebScriptException("Ticket not found");
+ status.setRedirect(true);
+ status.setCode(HttpServletResponse.SC_NOT_FOUND);
+ status.setMessage("Ticket not found");
+ }
+ else
+ {
+ // delete the ticket
+ authenticationService.invalidateTicket(ticket);
}
}
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 model = new HashMap(7, 1.0f);
- model.put("ticket", ticket);
return model;
}
diff --git a/source/java/org/alfresco/web/scripts/bean/SearchEngines.java b/source/java/org/alfresco/web/scripts/bean/SearchEngines.java
index cb013c3e29..7ef0ddc8c7 100644
--- a/source/java/org/alfresco/web/scripts/bean/SearchEngines.java
+++ b/source/java/org/alfresco/web/scripts/bean/SearchEngines.java
@@ -36,7 +36,7 @@ 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.alfresco.web.scripts.WebScriptStatus;
import org.apache.commons.logging.Log;
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)
*/
@Override
- protected Map executeImpl(WebScriptRequest req, WebScriptResponse res)
+ protected Map executeImpl(WebScriptRequest req, WebScriptStatus status)
{
String urlType = req.getParameter("type");
if (urlType == null || urlType.length() == 0)
diff --git a/source/java/org/alfresco/web/scripts/jsf/WebScriptJSFResponse.java b/source/java/org/alfresco/web/scripts/jsf/WebScriptJSFResponse.java
index 10180fb793..9a7a0b0352 100644
--- a/source/java/org/alfresco/web/scripts/jsf/WebScriptJSFResponse.java
+++ b/source/java/org/alfresco/web/scripts/jsf/WebScriptJSFResponse.java
@@ -105,6 +105,13 @@ public class WebScriptJSFResponse implements WebScriptResponse
return buf.toString();
}
+ /* (non-Javadoc)
+ * @see org.alfresco.web.scripts.WebScriptResponse#reset()
+ */
+ public void reset()
+ {
+ }
+
/**
* @see org.alfresco.web.scripts.WebScriptResponse#getOutputStream()
*/
@@ -121,6 +128,13 @@ public class WebScriptJSFResponse implements WebScriptResponse
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)
*/
@@ -128,4 +142,5 @@ public class WebScriptJSFResponse implements WebScriptResponse
{
// Alfresco JSF framework only supports the default of text-html
}
+
}
diff --git a/source/java/org/alfresco/web/scripts/portlet/WebScriptPortletResponse.java b/source/java/org/alfresco/web/scripts/portlet/WebScriptPortletResponse.java
index 90e6f66909..b01a88c173 100644
--- a/source/java/org/alfresco/web/scripts/portlet/WebScriptPortletResponse.java
+++ b/source/java/org/alfresco/web/scripts/portlet/WebScriptPortletResponse.java
@@ -66,6 +66,13 @@ public class WebScriptPortletResponse implements WebScriptResponse
return res;
}
+ /* (non-Javadoc)
+ * @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int)
+ */
+ public void setStatus(int status)
+ {
+ }
+
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String)
*/
@@ -74,6 +81,20 @@ public class WebScriptPortletResponse implements WebScriptResponse
res.setContentType(contentType);
}
+ /* (non-Javadoc)
+ * @see org.alfresco.web.scripts.WebScriptResponse#reset()
+ */
+ public void reset()
+ {
+ try
+ {
+ res.reset();
+ }
+ catch(IllegalStateException e)
+ {
+ }
+ }
+
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#getWriter()
*/
@@ -105,5 +126,5 @@ public class WebScriptPortletResponse implements WebScriptResponse
}
return portletUrl.toString();
}
-
+
}