From 570cbb6606bdf9fa176460bea0730c6aefabff1b Mon Sep 17 00:00:00 2001 From: David Caruana Date: Thu, 8 Mar 2007 14:36:34 +0000 Subject: [PATCH] Merge Web Scripts from BRANCHES/DEV/DAVE git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5349 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- ...Description_view_opensearchdescription.ftl | 11 - .../templates/api/KeywordSearch_query_.ftl | 13 - .../templates/api/KeywordSearch_view_atom.ftl | 43 -- .../templates/api/KeywordSearch_view_html.ftl | 57 --- .../templates/api/KeywordSearch_view_rss.ftl | 44 -- .../templates/api/SearchEngines_view_atom.ftl | 20 - .../templates/api/SearchEngines_view_html.ftl | 32 -- .../templates/api/Services_view_html.ftl | 26 - .../alfresco/web-api-application-context.xml | 87 ++-- .../org/alfresco/web/api/APIDescription.java | 123 +++++ .../alfresco/web/api/APIDescriptionImpl.java | 142 ++++++ .../org/alfresco/web/api/APIRegistry.java | 30 ++ .../java/org/alfresco/web/api/APIRequest.java | 55 +-- .../java/org/alfresco/web/api/APIService.java | 43 +- .../org/alfresco/web/api/APIServiceMatch.java | 10 + .../alfresco/web/api/APIServiceRegistry.java | 136 ------ .../java/org/alfresco/web/api/APIServlet.java | 82 ++-- .../java/org/alfresco/web/api/APIStore.java | 22 + .../java/org/alfresco/web/api/APIStores.java | 94 ++++ .../web/api/APITemplateProcessor.java | 16 +- .../alfresco/web/api/AbstractAPIService.java | 153 ++++++ .../web/api/AlfWebClientAuthenticator.java | 5 +- .../alfresco/web/api/BasicAuthenticator.java | 7 +- .../alfresco/web/api/ClassPathAPIStore.java | 116 +++++ .../web/api/DeclarativeAPIRegistry.java | 454 ++++++++++++++++++ .../org/alfresco/web/api/ScriptLoader.java | 10 + ...plateImpl.java => ScriptedAPIService.java} | 279 +++++------ .../org/alfresco/web/api/ServiceLogger.java | 5 +- .../alfresco/web/api/SystemAuthenticator.java | 94 ++++ .../web/api/TemplateClassPathSet.java | 106 ---- .../org/alfresco/web/api/TestAPIServer.java | 214 +++++++++ .../web/api/services/APIServiceImpl.java | 288 ----------- ...ywordSearchDescription.java => Index.java} | 146 ++---- .../web/api/services/KeywordSearch.java | 71 +-- .../web/api/services/SearchEngines.java | 63 +-- .../web/api/services/SearchProxy.java | 41 +- .../alfresco/web/api/services/Services.java | 133 ----- 37 files changed, 1788 insertions(+), 1483 deletions(-) delete mode 100644 config/alfresco/templates/api/KeywordSearchDescription_view_opensearchdescription.ftl delete mode 100644 config/alfresco/templates/api/KeywordSearch_query_.ftl delete mode 100644 config/alfresco/templates/api/KeywordSearch_view_atom.ftl delete mode 100644 config/alfresco/templates/api/KeywordSearch_view_html.ftl delete mode 100644 config/alfresco/templates/api/KeywordSearch_view_rss.ftl delete mode 100644 config/alfresco/templates/api/SearchEngines_view_atom.ftl delete mode 100644 config/alfresco/templates/api/SearchEngines_view_html.ftl delete mode 100644 config/alfresco/templates/api/Services_view_html.ftl create mode 100644 source/java/org/alfresco/web/api/APIDescription.java create mode 100644 source/java/org/alfresco/web/api/APIDescriptionImpl.java create mode 100644 source/java/org/alfresco/web/api/APIRegistry.java create mode 100644 source/java/org/alfresco/web/api/APIServiceMatch.java delete mode 100644 source/java/org/alfresco/web/api/APIServiceRegistry.java create mode 100644 source/java/org/alfresco/web/api/APIStore.java create mode 100644 source/java/org/alfresco/web/api/APIStores.java create mode 100644 source/java/org/alfresco/web/api/AbstractAPIService.java create mode 100644 source/java/org/alfresco/web/api/ClassPathAPIStore.java create mode 100644 source/java/org/alfresco/web/api/DeclarativeAPIRegistry.java create mode 100644 source/java/org/alfresco/web/api/ScriptLoader.java rename source/java/org/alfresco/web/api/{services/APIServiceTemplateImpl.java => ScriptedAPIService.java} (59%) create mode 100644 source/java/org/alfresco/web/api/SystemAuthenticator.java delete mode 100644 source/java/org/alfresco/web/api/TemplateClassPathSet.java create mode 100644 source/java/org/alfresco/web/api/TestAPIServer.java delete mode 100644 source/java/org/alfresco/web/api/services/APIServiceImpl.java rename source/java/org/alfresco/web/api/services/{KeywordSearchDescription.java => Index.java} (52%) delete mode 100644 source/java/org/alfresco/web/api/services/Services.java diff --git a/config/alfresco/templates/api/KeywordSearchDescription_view_opensearchdescription.ftl b/config/alfresco/templates/api/KeywordSearchDescription_view_opensearchdescription.ftl deleted file mode 100644 index f16dac89b8..0000000000 --- a/config/alfresco/templates/api/KeywordSearchDescription_view_opensearchdescription.ftl +++ /dev/null @@ -1,11 +0,0 @@ - - - Alfresco Keyword Search - Alfresco ${agent.edition} Keyword Search ${agent.version} - Search Alfresco "company home" using keywords - <#comment>IE takes first template from list, thus html response is listed first - - - - ${request.path}/images/logo/AlfrescoLogo16.ico - \ No newline at end of file diff --git a/config/alfresco/templates/api/KeywordSearch_query_.ftl b/config/alfresco/templates/api/KeywordSearch_query_.ftl deleted file mode 100644 index ad0a1fad7f..0000000000 --- a/config/alfresco/templates/api/KeywordSearch_query_.ftl +++ /dev/null @@ -1,13 +0,0 @@ -TYPE:"{http://www.alfresco.org/model/content/1.0}content" AND <#t> -( <#t> - ( <#t> -<#list 1..terms?size as i> - @\{http\://www.alfresco.org/model/content/1.0\}name:${terms[i - 1]}<#if (i < terms?size)> OR <#t> - - ) <#t> - ( <#t> -<#list 1..terms?size as i> - TEXT:${terms[i - 1]}<#if (i < terms?size)> OR <#t> - - ) <#t> -) <#t> diff --git a/config/alfresco/templates/api/KeywordSearch_view_atom.ftl b/config/alfresco/templates/api/KeywordSearch_view_atom.ftl deleted file mode 100644 index 5b714d8718..0000000000 --- a/config/alfresco/templates/api/KeywordSearch_view_atom.ftl +++ /dev/null @@ -1,43 +0,0 @@ - - - Alfresco (${agent.edition}) - Alfresco Keyword Search: ${search.searchTerms} - ${xmldate(date)} - ${absurl("/images/logo/AlfrescoLogo16.ico")} - - ${request.authenticatedUsername!"unknown"} - - urn:uuid:${search.id} - ${search.totalResults} - ${search.startIndex} - ${search.itemsPerPage} - - - -<#if search.startPage > 1> - - - -<#if search.startPage < search.totalPages> - - - - -<#list search.results as row> -<#attempt> - - ${row.name} - - ${absurl(row.icon16)} <#comment>TODO: What's the standard for entry icons? - urn:uuid:${row.id} - ${xmldate(row.properties.modified)} - ${row.properties.description!""} - - ${row.properties.creator} - - ${row.score} - -<#recover> - - - \ No newline at end of file diff --git a/config/alfresco/templates/api/KeywordSearch_view_html.ftl b/config/alfresco/templates/api/KeywordSearch_view_html.ftl deleted file mode 100644 index 3fc5ee537f..0000000000 --- a/config/alfresco/templates/api/KeywordSearch_view_html.ftl +++ /dev/null @@ -1,57 +0,0 @@ - - - - - Alfresco Keyword Search: ${search.searchTerms} - - - - - - - - - - - - -
AlfrescoAlfresco Keyword Search
-
- - - - -
Results ${search.startIndex} - ${search.startIndex + search.totalPageItems - 1} of ${search.totalResults} for ${search.searchTerms} visible to user ${request.authenticatedUsername!"unknown"}.
-
- -<#list search.results as row> -<#attempt> - - - - <#if row.properties.description?? == true> - - - - - -<#recover> - - -
${row.name}
${row.properties.description}
-
- - - -<#if search.startPage > 1> - - - -<#if search.startPage < search.totalPages> - - - - -
firstprevious${search.startPage}nextlast
- - \ No newline at end of file diff --git a/config/alfresco/templates/api/KeywordSearch_view_rss.ftl b/config/alfresco/templates/api/KeywordSearch_view_rss.ftl deleted file mode 100644 index ddecbe48ad..0000000000 --- a/config/alfresco/templates/api/KeywordSearch_view_rss.ftl +++ /dev/null @@ -1,44 +0,0 @@ - - - - Alfresco Keyword Search: ${search.searchTerms} - ${request.servicePath}/search/keyword?q=${search.searchTerms?url}&p=${search.startPage}&c=${search.itemsPerPage}&l=${search.localeId}&guest=${request.guest?string("true","")}&format=${request.format} - Alfresco Keyword Search: ${search.searchTerms} - ${search.localeId} - ${xmldate(date)} - ${xmldate(date)} - Alfresco ${agent.edition} v${agent.version} - - Alfresco Search: ${search.searchTerms} - 16 - 16 - ${absurl("/images/logo/AlfrescoLogo16.ico")} - - ${search.totalResults} - ${search.startIndex} - ${search.itemsPerPage} - - -<#if search.startPage > 1> - - - -<#if search.startPage < search.totalPages> - - \ No newline at end of file diff --git a/config/alfresco/templates/api/SearchEngines_view_atom.ftl b/config/alfresco/templates/api/SearchEngines_view_atom.ftl deleted file mode 100644 index ccf86b0781..0000000000 --- a/config/alfresco/templates/api/SearchEngines_view_atom.ftl +++ /dev/null @@ -1,20 +0,0 @@ - - - Alfresco (${agent.edition}) - Registered Search Engines - ${xmldate(date)} - ${request.path}/images/logo/AlfrescoLogo16.ico - - ${request.authenticatedUsername!"unknown"} - - ${request.url?xml} -<#list engines as engine> - - ${engine.label} - - <#if engine.urlType == "description">OpenSearch Description<#else>Template URL - ${engine.type} - ${absurl(engine.url)?xml} - ${xmldate(date)} - - - \ No newline at end of file diff --git a/config/alfresco/templates/api/SearchEngines_view_html.ftl b/config/alfresco/templates/api/SearchEngines_view_html.ftl deleted file mode 100644 index a1999105e3..0000000000 --- a/config/alfresco/templates/api/SearchEngines_view_html.ftl +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - Alfresco Registered Search Engines -<#list engines as engine> -<#if engine.urlType == "description"> - - - - - - - - - - -
AlfrescoAlfresco Registered Search Engines
-
- - -<#list engines as engine> - - - -
EngineURL TypeResponse Format
${engine.label} - <#if engine.urlType == "description">OpenSearch Description<#else>Template URL - ${engine.type} -
- - \ No newline at end of file diff --git a/config/alfresco/templates/api/Services_view_html.ftl b/config/alfresco/templates/api/Services_view_html.ftl deleted file mode 100644 index 010254d03f..0000000000 --- a/config/alfresco/templates/api/Services_view_html.ftl +++ /dev/null @@ -1,26 +0,0 @@ - - - - - Alfresco Web APIs - - - - - - - - -
AlfrescoAlfresco Web APIs
Alfresco ${agent.edition} v${agent.version} -
${services?size} services - Online documentation. -
-
- - -<#list services as service> - - - - -
NameMethodURLAuthenticationDefault Format
${service.name}${service.httpMethod}${service.httpUri}${service.requiredAuthentication}${service.defaultFormat}
  [${service.description}]
- \ No newline at end of file diff --git a/config/alfresco/web-api-application-context.xml b/config/alfresco/web-api-application-context.xml index ee1e6cbeb2..500d39b840 100644 --- a/config/alfresco/web-api-application-context.xml +++ b/config/alfresco/web-api-application-context.xml @@ -60,14 +60,18 @@ + + + + + + + + - - - - @@ -78,14 +82,33 @@ + + + - + - - + + + + + alfresco/templates/api + + + + + + + + web.api.ScriptedAPIService + + + + + @@ -95,33 +118,19 @@ - - - - - - - - alfresco/templates/api - - - - - + - - - - + + @@ -129,33 +138,23 @@ - - - + - - + - - - - - - - - - - - - - + - - + + + + + + + diff --git a/source/java/org/alfresco/web/api/APIDescription.java b/source/java/org/alfresco/web/api/APIDescription.java new file mode 100644 index 0000000000..86852ce26b --- /dev/null +++ b/source/java/org/alfresco/web/api/APIDescription.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.web.api; + + +/** + * API Service + * + * @author davidc + */ +public interface APIDescription +{ + /** + * Gets the source document location of this service description + * + * @return document location (path) + */ + public String getSourceLocation(); + + /** + * Gets the id of this service + * + * @return service id + */ + public String getId(); + + /** + * Gets the short name of this service + * + * @return service short name + */ + public String getShortName(); + + /** + * Gets the description of this service + */ + public String getDescription(); + + /** + * Gets the required authentication level for execution of this service + * + * @return the required authentication level + */ + public APIRequest.RequiredAuthentication getRequiredAuthentication(); + + /** + * Gets the HTTP method this service is bound to + * + * @return HTTP method + */ + public String getMethod(); + + /** + * Gets the URIs this service supports + * + * @return array of URIs in order specified in service description document + */ + public URI[] getURIs(); + + /** + * Gets a URI by format + * + * @param format the format + * @return the URI (or null, if no URI registered for the format) + */ + public URI getURI(String format); + + /** + * Gets the default response format + * + * Note: the default response format is the first listed in the service + * description document + * + * @return default response format + */ + public String getDefaultFormat(); + + + /** + * API Service URL + * + * @author davidc + */ + public interface URI + { + /** + * Gets the URI response format + * + * @return format + */ + public String getFormat(); + + /** + * Gets the URI + * + * @return uri + */ + public String getURI(); + } + +} diff --git a/source/java/org/alfresco/web/api/APIDescriptionImpl.java b/source/java/org/alfresco/web/api/APIDescriptionImpl.java new file mode 100644 index 0000000000..6f61b6ec40 --- /dev/null +++ b/source/java/org/alfresco/web/api/APIDescriptionImpl.java @@ -0,0 +1,142 @@ +package org.alfresco.web.api; + +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.web.api.APIRequest.RequiredAuthentication; + + +public class APIDescriptionImpl implements APIDescription +{ + private String sourceLocation; + private String id; + private String shortName; + private String description; + private RequiredAuthentication requiredAuthentication; + private String httpMethod; + private URI[] uris; + private String defaultFormat; + private Map uriByFormat; + + + public void setId(String id) + { + this.id = id; + } + + public String getId() + { + return this.id; + } + + public void setShortName(String shortName) + { + this.shortName = shortName; + } + + public String getShortName() + { + return this.shortName; + } + + public void setDescription(String description) + { + this.description = description; + } + + public String getDescription() + { + return this.description; + } + + public void setRequiredAuthentication(RequiredAuthentication requiredAuthentication) + { + this.requiredAuthentication = requiredAuthentication; + } + + public RequiredAuthentication getRequiredAuthentication() + { + return this.requiredAuthentication; + } + + public void setMethod(String httpMethod) + { + this.httpMethod = httpMethod; + } + + public String getMethod() + { + return this.httpMethod; + } + + public void setUris(URI[] uris) + { + this.uriByFormat = new HashMap(uris.length); + for (URI uri : uris) + { + this.uriByFormat.put(uri.getFormat(), uri); + } + this.uris = uris; + } + + public URI[] getURIs() + { + return this.uris; + } + + public URI getURI(String format) + { + return this.uriByFormat.get(format); + } + + public void setDefaultFormat(String defaultFormat) + { + this.defaultFormat = defaultFormat; + } + + public String getDefaultFormat() + { + return this.defaultFormat; + } + + + public static class URIImpl implements APIDescription.URI + { + private String format; + private String uri; + + public void setFormat(String format) + { + this.format = format; + } + + public String getFormat() + { + return this.format; + } + + public void setUri(String uri) + { + this.uri = uri; + } + + public String getURI() + { + return this.uri; + } + + + } + + + public void setSourceLocation(String sourceLocation) + { + this.sourceLocation = sourceLocation; + } + + public String getSourceLocation() + { + return this.sourceLocation; + } + +} diff --git a/source/java/org/alfresco/web/api/APIRegistry.java b/source/java/org/alfresco/web/api/APIRegistry.java new file mode 100644 index 0000000000..a57c83706c --- /dev/null +++ b/source/java/org/alfresco/web/api/APIRegistry.java @@ -0,0 +1,30 @@ +package org.alfresco.web.api; + +import java.util.Collection; + +import javax.servlet.ServletContext; + +public interface APIRegistry +{ + + public ServletContext getContext(); + + /** + * Gets an API Service given an HTTP Method and URI + * + * @param method + * @param uri + * @return + */ + public APIServiceMatch findService(String method, String uri); + + public APIService getService(String id); + + public Collection getServices(); + + public FormatRegistry getFormatRegistry(); + + public APITemplateProcessor getTemplateProcessor(); + + //public APIScriptProcessor getScriptProcessor(); +} diff --git a/source/java/org/alfresco/web/api/APIRequest.java b/source/java/org/alfresco/web/api/APIRequest.java index 78a20db89e..eef534bd98 100644 --- a/source/java/org/alfresco/web/api/APIRequest.java +++ b/source/java/org/alfresco/web/api/APIRequest.java @@ -37,24 +37,17 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; */ public class APIRequest extends HttpServletRequestWrapper { - - /** - * Enumerartion of HTTP Methods - */ - public enum HttpMethod - { - GET; - // TODO: Complete list... - } - + private APIServiceMatch serviceMatch; + + /** * Enumeration of "required" Authentication level */ public enum RequiredAuthentication { - None, - Guest, - User + none, + guest, + user } /** @@ -62,22 +55,22 @@ public class APIRequest extends HttpServletRequestWrapper * * @param req */ - /*package*/ APIRequest(HttpServletRequest req) + /*package*/ APIRequest(HttpServletRequest req, APIServiceMatch serviceMatch) { super(req); + this.serviceMatch = serviceMatch; } /** - * Gets the HTTP Method + * Gets the matching API Service for this request * - * @return Http Method + * @return the service match */ - public HttpMethod getHttpMethod() + public APIServiceMatch getServiceMatch() { - String method = getMethod().trim().toUpperCase(); - return HttpMethod.valueOf(method); + return serviceMatch; } - + /** * Gets the Alfresco Context URL * @@ -109,14 +102,15 @@ public class APIRequest extends HttpServletRequestWrapper * * @return extension path */ - public String getExtensionPath(APIService service) + public String getExtensionPath() { - String servicePath = service.getHttpUri(); + String servicePath = serviceMatch.getPath(); String extensionPath = getPathInfo(); int extIdx = extensionPath.indexOf(servicePath); if (extIdx != -1) { - extensionPath = extensionPath.substring(extIdx + servicePath.length() + 1 /* exclude leading / */); + int extLength = (servicePath.endsWith("/") ? servicePath.length() : servicePath.length() + 1); + extensionPath = extensionPath.substring(extIdx + extLength); } return extensionPath; } @@ -172,13 +166,16 @@ public class APIRequest extends HttpServletRequestWrapper public String getAgent() { String userAgent = getHeader("user-agent"); - if (userAgent.indexOf("Firefox/") != -1) + if (userAgent != null) { - return "Firefox"; - } - else if (userAgent.indexOf("MSIE") != -1) - { - return "MSIE"; + if (userAgent.indexOf("Firefox/") != -1) + { + return "Firefox"; + } + else if (userAgent.indexOf("MSIE") != -1) + { + return "MSIE"; + } } return null; } diff --git a/source/java/org/alfresco/web/api/APIService.java b/source/java/org/alfresco/web/api/APIService.java index 4c6ea6da51..6b360d5890 100644 --- a/source/java/org/alfresco/web/api/APIService.java +++ b/source/java/org/alfresco/web/api/APIService.java @@ -33,46 +33,7 @@ import java.io.IOException; */ public interface APIService { - - /** - * Gets the name of this service - * - * @return service name - */ - public String getName(); - - /** - * Gets the description of this service - */ - public String getDescription(); - - /** - * Gets the required authentication level for execution of this service - * - * @return the required authentication level - */ - public APIRequest.RequiredAuthentication getRequiredAuthentication(); - - /** - * Gets the HTTP method this service is bound to - * - * @return HTTP method - */ - public APIRequest.HttpMethod getHttpMethod(); - - /** - * Gets the HTTP uri this service is bound to - * - * @return HTTP uri - */ - public String getHttpUri(); - - /** - * Gets the default response format - * - * @return response format - */ - public String getDefaultFormat(); + public APIDescription getDescription(); /** * Execute service @@ -83,5 +44,5 @@ public interface APIService */ public void execute(APIRequest req, APIResponse res) throws IOException; - + } diff --git a/source/java/org/alfresco/web/api/APIServiceMatch.java b/source/java/org/alfresco/web/api/APIServiceMatch.java new file mode 100644 index 0000000000..b2fbd994de --- /dev/null +++ b/source/java/org/alfresco/web/api/APIServiceMatch.java @@ -0,0 +1,10 @@ +package org.alfresco.web.api; + +public interface APIServiceMatch +{ + + public String getPath(); + + public APIService getService(); + +} diff --git a/source/java/org/alfresco/web/api/APIServiceRegistry.java b/source/java/org/alfresco/web/api/APIServiceRegistry.java deleted file mode 100644 index d11bffcde8..0000000000 --- a/source/java/org/alfresco/web/api/APIServiceRegistry.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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.api; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.aopalliance.intercept.MethodInterceptor; -import org.springframework.aop.framework.ProxyFactory; -import org.springframework.aop.support.RegexpMethodPointcutAdvisor; -import org.springframework.context.ApplicationContext; - - -/** - * Registry of Web API Services methods - * - * @author davidc - */ -public class APIServiceRegistry -{ - // TODO: Support different kinds of uri resolution (e.g. regex:/search/.*) - - private List methods = new ArrayList(); - private List uris = new ArrayList(); - private List services = new ArrayList(); - - - /** - * Construct list of API Services - * - * @param appContext The app context - */ - public APIServiceRegistry(ApplicationContext appContext) - { - // retrieve service authenticator - MethodInterceptor authenticator = (MethodInterceptor)appContext.getBean("web.api.Authenticator"); - MethodInterceptor serviceLogger = (MethodInterceptor)appContext.getBean("web.api.ServiceLogger"); - - // register all API Services - // NOTE: An API Service is one registered in Spring which is of type APIServiceImpl - Map apiServices = appContext.getBeansOfType(APIService.class, false, false); - for (Map.Entry apiService : apiServices.entrySet()) - { - // retrieve service - APIService service = apiService.getValue(); - - // retrieve http method - APIRequest.HttpMethod method = service.getHttpMethod(); - String httpUri = service.getHttpUri(); - if (httpUri == null || httpUri.length() == 0) - { - throw new APIException("Web API Service " + apiService.getKey() + " does not specify a HTTP URI mapping"); - } - - // wrap API Service in appropriate interceptors (e.g. authentication) - if (serviceLogger != null && authenticator != null) - { - ProxyFactory authFactory = new ProxyFactory((APIService)service); - - // authentication - if (service.getRequiredAuthentication() != APIRequest.RequiredAuthentication.None) - { - if (authenticator == null) - { - throw new APIException("Web API Authenticator not specified"); - } - RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor(".*execute", authenticator); - authFactory.addAdvisor(advisor); - } - - // logging - if (serviceLogger != null) - { - RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor(".*execute", serviceLogger); - authFactory.addAdvisor(advisor); - } - - service = (APIService)authFactory.getProxy(); - } - - // register service - methods.add(method); - uris.add(httpUri); - services.add(service); - } - } - - - /** - * Gets an API Service given an HTTP Method and URI - * - * @param method The method - * @param uri The URI - * @return The matching service or null if there isn't a match - */ - public APIService get(APIRequest.HttpMethod method, String uri) - { - APIService apiService = null; - - // TODO: Replace with more efficient approach - for (int i = 0; i < services.size(); i++) - { - if (methods.get(i).equals(method) && uri.startsWith(uris.get(i))) - { - apiService = services.get(i); - break; - } - } - - return apiService; - } - -} diff --git a/source/java/org/alfresco/web/api/APIServlet.java b/source/java/org/alfresco/web/api/APIServlet.java index 48b495c4f8..fcff8d0a9f 100644 --- a/source/java/org/alfresco/web/api/APIServlet.java +++ b/source/java/org/alfresco/web/api/APIServlet.java @@ -52,7 +52,7 @@ public class APIServlet extends HttpServlet private static final Log logger = LogFactory.getLog(APIServlet.class); // API Services - private APIServiceRegistry apiServiceRegistry; + private DeclarativeAPIRegistry apiServiceRegistry; @Override @@ -63,7 +63,7 @@ public class APIServlet extends HttpServlet // Retrieve all web api services and index by http url & http method ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext()); initContext(context); - apiServiceRegistry = new APIServiceRegistry(context); +// apiServiceRegistry = new DeclarativeAPIServiceRegistry(context); } @@ -79,45 +79,45 @@ public class APIServlet extends HttpServlet */ protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { - long start = System.currentTimeMillis(); - APIRequest request = new APIRequest(req); - APIResponse response = new APIResponse(res); - - try - { - // - // Execute appropriate service - // - // TODO: Handle errors (with appropriate HTTP error responses) - - APIRequest.HttpMethod method = request.getHttpMethod(); - String uri = request.getPathInfo(); - - if (logger.isDebugEnabled()) - logger.debug("Processing request (" + request.getHttpMethod() + ") " + request.getRequestURL() + (request.getQueryString() != null ? "?" + request.getQueryString() : "") + " (agent: " + request.getAgent() + ")"); - - APIService service = apiServiceRegistry.get(method, uri); - if (service != null) - { - // TODO: Wrap in single transaction - service.execute(request, response); - } - else - { - if (logger.isDebugEnabled()) - logger.debug("Request does not map to service."); - - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - // TODO: add appropriate error detail - } - } - // TODO: exception handling - finally - { - long end = System.currentTimeMillis(); - if (logger.isDebugEnabled()) - logger.debug("Processed request (" + request.getHttpMethod() + ") " + request.getRequestURL() + (request.getQueryString() != null ? "?" + request.getQueryString() : "") + " in " + (end - start) + "ms"); - } +// long start = System.currentTimeMillis(); +// APIRequest request = new APIRequest(req); +// APIResponse response = new APIResponse(res); +// +// try +// { +// // +// // Execute appropriate service +// // +// // TODO: Handle errors (with appropriate HTTP error responses) +// +// APIRequest.HttpMethod method = request.getHttpMethod(); +// String uri = request.getPathInfo(); +// +// if (logger.isDebugEnabled()) +// logger.debug("Processing request (" + request.getHttpMethod() + ") " + request.getRequestURL() + (request.getQueryString() != null ? "?" + request.getQueryString() : "") + " (agent: " + request.getAgent() + ")"); +// +// APIServiceDescription service = apiServiceRegistry.get(method, uri); +// if (service != null) +// { +// // TODO: Wrap in single transaction +// service.execute(request, response); +// } +// else +// { +// if (logger.isDebugEnabled()) +// logger.debug("Request does not map to service."); +// +// response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); +// // TODO: add appropriate error detail +// } +// } +// // TODO: exception handling +// finally +// { +// long end = System.currentTimeMillis(); +// if (logger.isDebugEnabled()) +// logger.debug("Processed request (" + request.getHttpMethod() + ") " + request.getRequestURL() + (request.getQueryString() != null ? "?" + request.getQueryString() : "") + " in " + (end - start) + "ms"); +// } } /** diff --git a/source/java/org/alfresco/web/api/APIStore.java b/source/java/org/alfresco/web/api/APIStore.java new file mode 100644 index 0000000000..16a4fe3cdb --- /dev/null +++ b/source/java/org/alfresco/web/api/APIStore.java @@ -0,0 +1,22 @@ +package org.alfresco.web.api; + +import java.io.IOException; +import java.io.InputStream; + +import freemarker.cache.TemplateLoader; + + +public interface APIStore +{ + public String getBasePath(); + + public String[] getDescriptionDocumentPaths(); + + public InputStream getDescriptionDocument(String documentPath) + throws IOException; + + public TemplateLoader getTemplateLoader(); + + public ScriptLoader getScriptLoader(); +} + diff --git a/source/java/org/alfresco/web/api/APIStores.java b/source/java/org/alfresco/web/api/APIStores.java new file mode 100644 index 0000000000..048f0d55e4 --- /dev/null +++ b/source/java/org/alfresco/web/api/APIStores.java @@ -0,0 +1,94 @@ +package org.alfresco.web.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.alfresco.util.AbstractLifecycleBean; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; + +import freemarker.cache.MultiTemplateLoader; +import freemarker.cache.TemplateLoader; + +public class APIStores implements ApplicationContextAware, ApplicationListener +{ + private ApplicationContext applicationContext; + private ProcessorLifecycle lifecycle = new ProcessorLifecycle(); + private APITemplateProcessor templateProcessor; + + + /* (non-Javadoc) + * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) + */ + public void onApplicationEvent(ApplicationEvent event) + { + lifecycle.onApplicationEvent(event); + } + + /** + * Hooks into Spring Application Lifecycle + */ + private class ProcessorLifecycle extends AbstractLifecycleBean + { + @Override + protected void onBootstrap(ApplicationEvent event) + { + initTemplateProcessor(); + initScriptProcessor(); + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + } + } + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + this.lifecycle.setApplicationContext(applicationContext); + } + + + protected void initTemplateProcessor() + { + List loaders = new ArrayList(); + for (APIStore apiStore : getAPIStores()) + { + TemplateLoader loader = apiStore.getTemplateLoader(); + if (loader == null) + { + throw new APIException("Unable to retrieve template loader for api store " + apiStore.getBasePath()); + } + loaders.add(loader); + } + MultiTemplateLoader loader = new MultiTemplateLoader(loaders.toArray(new TemplateLoader[loaders.size()])); + templateProcessor.setTemplateLoader(loader); + } + + protected void initScriptProcessor() + { + + } + + @SuppressWarnings("unchecked") + public Collection getAPIStores() + { + return applicationContext.getBeansOfType(APIStore.class, false, false).values(); + } + + public void setTemplateProcessor(APITemplateProcessor templateProcessor) + { + this.templateProcessor = templateProcessor; + } + + public APITemplateProcessor getTemplateProcessor() + { + return templateProcessor; + } + +} diff --git a/source/java/org/alfresco/web/api/APITemplateProcessor.java b/source/java/org/alfresco/web/api/APITemplateProcessor.java index d8847344af..abb21cae81 100644 --- a/source/java/org/alfresco/web/api/APITemplateProcessor.java +++ b/source/java/org/alfresco/web/api/APITemplateProcessor.java @@ -24,9 +24,6 @@ */ package org.alfresco.web.api; -import java.util.HashSet; -import java.util.Set; - import org.alfresco.repo.template.FreeMarkerProcessor; import org.alfresco.repo.template.QNameAwareObjectWrapper; import org.alfresco.util.AbstractLifecycleBean; @@ -54,7 +51,7 @@ import freemarker.template.TemplateExceptionHandler; public class APITemplateProcessor extends FreeMarkerProcessor implements ApplicationContextAware, ApplicationListener { private ProcessorLifecycle lifecycle = new ProcessorLifecycle(); - private Set templateLoaders = new HashSet(); + private TemplateLoader templateLoader = null; private String defaultEncoding; private Configuration templateConfig; @@ -77,13 +74,13 @@ public class APITemplateProcessor extends FreeMarkerProcessor implements Applica } /** - * Add a Template Loader + * Sets the Template Loader * * @param templateLoader template loader */ - public void addTemplateLoader(TemplateLoader templateLoader) + public void setTemplateLoader(TemplateLoader templateLoader) { - templateLoaders.add(templateLoader); + this.templateLoader = templateLoader; } /** @@ -97,10 +94,7 @@ public class APITemplateProcessor extends FreeMarkerProcessor implements Applica config.setCacheStorage(new MruCacheStorage(20, 100)); // setup template loaders - for (TemplateLoader templateLoader : templateLoaders) - { - config.setTemplateLoader(templateLoader); - } + config.setTemplateLoader(templateLoader); // use our custom object wrapper that can deal with QNameMap objects directly config.setObjectWrapper(new QNameAwareObjectWrapper()); diff --git a/source/java/org/alfresco/web/api/AbstractAPIService.java b/source/java/org/alfresco/web/api/AbstractAPIService.java new file mode 100644 index 0000000000..c0fc776f7b --- /dev/null +++ b/source/java/org/alfresco/web/api/AbstractAPIService.java @@ -0,0 +1,153 @@ +/* + * 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.api; + +import java.io.Writer; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.repo.template.AbsoluteUrlMethod; +import org.alfresco.repo.template.ISO8601DateFormatMethod; +import org.alfresco.repo.template.UrlEncodeMethod; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.descriptor.DescriptorService; + + +/** + * Skeleton implementation of an API Service + * + * @author davidc + */ +public abstract class AbstractAPIService implements APIService +{ + // dependencies + private APIRegistry apiRegistry; + private APIDescription apiDescription; + private ServiceRegistry serviceRegistry; + private DescriptorService descriptorService; + + // + // Initialisation + // + + final public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + final public void setDescriptorService(DescriptorService descriptorService) + { + this.descriptorService = descriptorService; + } + + final public void setDescription(APIDescription apiDescription) + { + this.apiDescription = apiDescription; + } + + public void init(APIRegistry apiRegistry) + { + this.apiRegistry = apiRegistry; + } + + + /* (non-Javadoc) + * @see org.alfresco.web.api.APIService#getServiceDescription() + */ + final public APIDescription getDescription() + { + return this.apiDescription; + } + + // + // Service Implementation Helpers + // + + final public APIRegistry getAPIRegistry() + { + return this.apiRegistry; + } + + final public ServiceRegistry getServiceRegistry() + { + return this.serviceRegistry; + } + + final public DescriptorService getDescriptorService() + { + return this.descriptorService; + } + + + // + // Basic Templating Support + // + + + /** + * Create a basic API model + * + * @param req api request + * @param res api response + * @return template model + */ + final protected Map createAPIModel(APIRequest req, APIResponse res) + { + Map model = new HashMap(7, 1.0f); + model.put("xmldate", new ISO8601DateFormatMethod()); + model.put("absurl", new AbsoluteUrlMethod(req.getPath())); + model.put("urlencode", new UrlEncodeMethod()); + model.put("date", new Date()); + model.put("agent", descriptorService.getServerDescriptor()); + model.put("request", req); + model.put("response", res); + return model; + } + + /** + * Render a template (identified by path) + * + * @param templatePath template path + * @param model model + * @param writer output writer + */ + final protected void renderTemplate(String templatePath, Map model, Writer writer) + { + getAPIRegistry().getTemplateProcessor().process(templatePath, model, writer); + } + + /** + * Render a template (contents as string) + * @param template the template + * @param model model + * @param writer output writer + */ + final protected void renderString(String template, Map model, Writer writer) + { + getAPIRegistry().getTemplateProcessor().processString(template, model, writer); + } + +} diff --git a/source/java/org/alfresco/web/api/AlfWebClientAuthenticator.java b/source/java/org/alfresco/web/api/AlfWebClientAuthenticator.java index 2befbebcb0..f58fe36936 100644 --- a/source/java/org/alfresco/web/api/AlfWebClientAuthenticator.java +++ b/source/java/org/alfresco/web/api/AlfWebClientAuthenticator.java @@ -80,6 +80,7 @@ public class AlfWebClientAuthenticator implements MethodInterceptor, APIContextA APIRequest request = (APIRequest)args[0]; APIResponse response = (APIResponse)args[1]; APIService service = (APIService)invocation.getThis(); + APIDescription description = service.getDescription(); AuthenticationStatus status = null; try @@ -102,7 +103,7 @@ public class AlfWebClientAuthenticator implements MethodInterceptor, APIContextA if (logger.isDebugEnabled()) { - logger.debug("Service authentication required: " + service.getRequiredAuthentication()); + logger.debug("Service authentication required: " + description.getRequiredAuthentication()); logger.debug("Guest login: " + isGuest); logger.debug("Ticket provided: " + (ticket != null && ticket.length() > 0)); } @@ -116,7 +117,7 @@ public class AlfWebClientAuthenticator implements MethodInterceptor, APIContextA } else { - if (isGuest && service.getRequiredAuthentication() == APIRequest.RequiredAuthentication.Guest) + if (isGuest && description.getRequiredAuthentication() == APIRequest.RequiredAuthentication.guest) { if (logger.isDebugEnabled()) logger.debug("Authenticating as Guest"); diff --git a/source/java/org/alfresco/web/api/BasicAuthenticator.java b/source/java/org/alfresco/web/api/BasicAuthenticator.java index 1c7c7f2a43..2554a1e648 100644 --- a/source/java/org/alfresco/web/api/BasicAuthenticator.java +++ b/source/java/org/alfresco/web/api/BasicAuthenticator.java @@ -67,6 +67,7 @@ public class BasicAuthenticator implements MethodInterceptor Object[] args = invocation.getArguments(); APIRequest request = (APIRequest)args[0]; APIService service = (APIService)invocation.getThis(); + APIDescription description = service.getDescription(); try { @@ -87,14 +88,14 @@ public class BasicAuthenticator implements MethodInterceptor if (logger.isDebugEnabled()) { - logger.debug("Service authentication required: " + service.getRequiredAuthentication()); + logger.debug("Service authentication required: " + description.getRequiredAuthentication()); logger.debug("Guest login: " + isGuest); logger.debug("Authorization provided (overrides Guest login): " + (authorization != null && authorization.length() > 0)); } // authenticate as guest, if service allows if (((authorization == null || authorization.length() == 0) || isGuest) - && service.getRequiredAuthentication().equals(APIRequest.RequiredAuthentication.Guest)) + && description.getRequiredAuthentication().equals(APIRequest.RequiredAuthentication.guest)) { if (logger.isDebugEnabled()) logger.debug("Authenticating as Guest"); @@ -133,7 +134,7 @@ public class BasicAuthenticator implements MethodInterceptor // assume username and password passed if (parts[0].equals(AuthenticationUtil.getGuestUserName())) { - if (service.getRequiredAuthentication().equals(APIRequest.RequiredAuthentication.Guest)) + if (description.getRequiredAuthentication().equals(APIRequest.RequiredAuthentication.guest)) { authenticationService.authenticateAsGuest(); authorized = true; diff --git a/source/java/org/alfresco/web/api/ClassPathAPIStore.java b/source/java/org/alfresco/web/api/ClassPathAPIStore.java new file mode 100644 index 0000000000..5c6c5a30af --- /dev/null +++ b/source/java/org/alfresco/web/api/ClassPathAPIStore.java @@ -0,0 +1,116 @@ +package org.alfresco.web.api; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +import freemarker.cache.FileTemplateLoader; +import freemarker.cache.TemplateLoader; + + +public class ClassPathAPIStore implements APIStore, InitializingBean +{ + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + private String classPath; + private String classPathDir; + + + public void setClassPath(String classPath) + { + this.classPath = classPath; + } + + public void afterPropertiesSet() + throws Exception + { + ClassPathResource resource = new ClassPathResource(classPath); + classPathDir = resource.getURL().toExternalForm(); + } + + public String getBasePath() + { + return "classpath:" + classPath; + } + + + public String[] getDescriptionDocumentPaths() + { + String[] paths = null; + + try + { + Resource[] resources = resolver.getResources("classpath*:" + classPath + "/**/*_desc.xml"); + paths = new String[resources.length]; + int i = 0; + for (Resource resource : resources) + { + paths[i++] = resource.getURL().toExternalForm().substring(classPathDir.length()); + } + } + catch(IOException e) + { + // Note: Ignore: no service description documents found + paths = new String[0]; + } + + return paths; + } + + + public InputStream getDescriptionDocument(String documentPath) + throws IOException + { + DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); + Resource resource = resourceLoader.getResource(classPathDir + "/" + documentPath); + return resource.getInputStream(); + } + + + public static void main(String[] args) + throws Exception + { + ClassPathAPIStore store = new ClassPathAPIStore(); + store.setClassPath("alfresco/templates/api"); + store.afterPropertiesSet(); + String[] paths = store.getDescriptionDocumentPaths(); + InputStream is = store.getDescriptionDocument(paths[0]); + is.close(); + TemplateLoader loader = store.getTemplateLoader(); + Object obj = loader.findTemplateSource("KeywordSearch_view_atom.ftl"); + } + + public TemplateLoader getTemplateLoader() + { + FileTemplateLoader loader = null; + try + { + File classPathFile = new File(new URI(classPathDir)); + loader = new FileTemplateLoader(classPathFile); + } + catch (URISyntaxException e) + { + // Note: Can't establish loader, so return null + } + catch (IOException e) + { + // Note: Can't establish loader, so return null + } + return loader; + } + + public ScriptLoader getScriptLoader() + { + // TODO Auto-generated method stub + return null; + } + + +} diff --git a/source/java/org/alfresco/web/api/DeclarativeAPIRegistry.java b/source/java/org/alfresco/web/api/DeclarativeAPIRegistry.java new file mode 100644 index 0000000000..cf41dcb882 --- /dev/null +++ b/source/java/org/alfresco/web/api/DeclarativeAPIRegistry.java @@ -0,0 +1,454 @@ +/* + * 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.api; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import javax.servlet.ServletContext; + +import org.alfresco.web.api.APIDescription.URI; +import org.alfresco.web.api.APIRequest.RequiredAuthentication; +import org.aopalliance.intercept.MethodInterceptor; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.aop.support.RegexpMethodPointcutAdvisor; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + + +/** + * Registry of Web API Services methods + * + * @author davidc + */ +public class DeclarativeAPIRegistry implements APIRegistry, ApplicationContextAware, APIContextAware +{ + // TODO: Support different kinds of uri resolution (e.g. regex:/search/.*) ?? + + + // Logger + private static final Log logger = LogFactory.getLog(DeclarativeAPIRegistry.class); + + private ApplicationContext applicationContext; + private ServletContext servletContext; + private String defaultServiceImpl; + private MethodInterceptor authenticator; + private MethodInterceptor serviceLogger; + private FormatRegistry formatRegistry; + private APIStores stores; + + + // map of services by service id + // NOTE: The map is sorted by id (ascending order) + private Map servicesById = new TreeMap(); + + // map of services by url + // NOTE: The map is sorted by url (descending order) + private Map servicesByURL = new TreeMap(Collections.reverseOrder()); + + + public void setAuthenticator(MethodInterceptor authenticator) + { + this.authenticator = authenticator; + } + + public void setServiceLogger(MethodInterceptor serviceLogger) + { + this.serviceLogger = serviceLogger; + } + + public void setDefaultServiceImpl(String defaultServiceImpl) + { + this.defaultServiceImpl = defaultServiceImpl; + } + + public void setStores(APIStores stores) + { + this.stores = stores; + } + + + + + public void initServices() + { + if (logger.isDebugEnabled()) + logger.debug("Initialising Web API services"); + + // clear currently registered services + servicesById.clear(); + servicesByURL.clear(); + + // register services + for (APIStore apiStore : stores.getAPIStores()) + { + if (logger.isDebugEnabled()) + logger.debug("Locating services within " + apiStore.getBasePath()); + + String basePath = apiStore.getBasePath(); + String[] serviceDescPaths = apiStore.getDescriptionDocumentPaths(); + for (String serviceDescPath : serviceDescPaths) + { + // build service description + APIDescription serviceDesc = null; + InputStream serviceDescIS = null; + try + { + serviceDescIS = apiStore.getDescriptionDocument(serviceDescPath); + serviceDesc = createServiceDescription(basePath, serviceDescPath, serviceDescIS); + } + catch(IOException e) + { + throw new APIException("Failed to read service description document " + apiStore.getBasePath() + serviceDescPath, e); + } + finally + { + try + { + serviceDescIS.close(); + } + catch(IOException e) + { + // NOTE: ignore close exception + } + } + + // determine if service description has been registered + String id = serviceDesc.getId(); + if (servicesById.containsKey(id)) + { + // move to next service + if (logger.isDebugEnabled()) + { + APIService existingService = servicesById.get(id); + logger.debug("Service description document " + existingService.getDescription().getSourceLocation() + " overridden by " + serviceDesc.getSourceLocation()); + } + continue; + } + + // construct service implementation + String serviceImplName = (applicationContext.containsBean("web.api.services." + id)) ? "web.api.services." + id : defaultServiceImpl; + AbstractAPIService serviceImpl = (AbstractAPIService)applicationContext.getBean(serviceImplName); + serviceImpl.setDescription(serviceDesc); + serviceImpl.init(this); + + // wrap service implementation in appropriate interceptors (e.g. authentication) + APIService serviceImplIF = (APIService)serviceImpl; + if (serviceLogger != null && authenticator != null) + { + ProxyFactory authFactory = new ProxyFactory(); + authFactory.addInterface(APIService.class); + authFactory.setTarget(serviceImplIF); + + // authentication interceptor + if (serviceDesc.getRequiredAuthentication() != APIRequest.RequiredAuthentication.none) + { + if (authenticator == null) + { + throw new APIException("Web API Authenticator not specified"); + } + RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor(".*execute", authenticator); + authFactory.addAdvisor(advisor); + } + + // logging interceptor + if (serviceLogger != null) + { + RegexpMethodPointcutAdvisor advisor = new RegexpMethodPointcutAdvisor(".*execute", serviceLogger); + authFactory.addAdvisor(advisor); + } + + serviceImplIF = (APIService)authFactory.getProxy(); + } + + if (logger.isDebugEnabled()) + logger.debug("Found service " + serviceDescPath + " (id: " + id + ", impl: " + serviceImplName + ")"); + + // register service and its urls + servicesById.put(id, serviceImplIF); + for (URI uri : serviceDesc.getURIs()) + { + // establish static part of url template + String uriTemplate = uri.getURI(); + int queryArgIdx = uriTemplate.indexOf('?'); + if (queryArgIdx != -1) + { + uriTemplate = uriTemplate.substring(0, queryArgIdx); + } + int tokenIdx = uriTemplate.indexOf('{'); + if (tokenIdx != -1) + { + uriTemplate = uriTemplate.substring(0, tokenIdx); + } + + // index service by static part of url (ensuring no other service has already claimed the url) + String uriIdx = serviceDesc.getMethod().toString() + ":" + uriTemplate; + if (servicesByURL.containsKey(uriIdx)) + { + APIService existingService = servicesByURL.get(uriIdx); + if (!existingService.getDescription().getId().equals(serviceDesc.getId())) + { + throw new APIException("Service document " + existingService.getDescription().getSourceLocation() + " already defines the url '" + uriIdx); + } + } + else + { + servicesByURL.put(uriIdx, serviceImplIF); + + if (logger.isDebugEnabled()) + logger.debug("Registered Web API URL '" + uriIdx + "'"); + } + } + } + } + + if (logger.isDebugEnabled()) + logger.debug("Registered " + servicesById.size() + " services; " + servicesByURL.size() + " URLs"); + } + + private APIDescription createServiceDescription(String basePath, String serviceDescPath, InputStream serviceDoc) + { + SAXReader reader = new SAXReader(); + try + { + Document document = reader.read(serviceDoc); + Element rootElement = document.getRootElement(); + if (!rootElement.getName().equals("servicedescription")) + { + throw new APIException("Expected root element - found <" + rootElement.getName() + ">"); + } + + // retrieve id + String id = serviceDescPath.substring(0, serviceDescPath.lastIndexOf("_desc.xml")).replace('/', '.'); + + // retrieve http method + int methodIdx = id.lastIndexOf('_'); + if (methodIdx == id.length() - 1) + { + throw new APIException("Unable to establish HTTP Method from service description: naming convention must be __desc.xml"); + } + String method = id.substring(id.lastIndexOf('_') + 1).toUpperCase(); + + // retrieve short name + Element shortNameElement = rootElement.element("shortname"); + if (shortNameElement == null || shortNameElement.getTextTrim() == null || shortNameElement.getTextTrim().length() == 0) + { + throw new APIException("Expected value"); + } + String shortName = shortNameElement.getTextTrim(); + + // retrieve description + Element descriptionElement = rootElement.element("description"); + String description = descriptionElement.getTextTrim(); + + // retrieve urls + List urlElements = rootElement.elements("url"); + if (urlElements == null || urlElements.size() == 0) + { + throw new APIException("Expected at one element"); + } + List uris = new ArrayList(); + Iterator iterElements = urlElements.iterator(); + while(iterElements.hasNext()) + { + // retrieve url element + Element urlElement = (Element)iterElements.next(); + + // retrieve url mimetype + String format = urlElement.attributeValue("format"); + if (format == null) + { + // default to unspecified format + format = ""; + } + + // retrieve url template + String template = urlElement.attributeValue("template"); + if (template == null || template.length() == 0) + { + throw new APIException("Expected template attribute on element"); + } + + APIDescriptionImpl.URIImpl uriImpl = new APIDescriptionImpl.URIImpl(); + uriImpl.setFormat(format); + uriImpl.setUri(template); + uris.add(uriImpl); + } + + // retrieve authentication + RequiredAuthentication reqAuth = RequiredAuthentication.none; + Element authElement = rootElement.element("authentication"); + if (authElement != null) + { + String reqAuthStr = authElement.getTextTrim(); + if (reqAuthStr == null || reqAuthStr.length() == 0) + { + throw new APIException("Expected value"); + } + reqAuth = RequiredAuthentication.valueOf(reqAuthStr); + if (reqAuth == null) + { + throw new APIException("Authentication '" + reqAuthStr + "' is not a valid value"); + } + } + + // construct service description + APIDescriptionImpl serviceDesc = new APIDescriptionImpl(); + serviceDesc.setSourceLocation(basePath + "/" + serviceDescPath); + serviceDesc.setId(id); + serviceDesc.setShortName(shortName); + serviceDesc.setDescription(description); + serviceDesc.setRequiredAuthentication(reqAuth); + serviceDesc.setMethod(method); + serviceDesc.setUris(uris.toArray(new APIDescription.URI[uris.size()])); + serviceDesc.setDefaultFormat(uris.get(0).getFormat()); + return serviceDesc; + } + catch(DocumentException e) + { + throw new APIException("Failed to parse service description document " + serviceDescPath, e); + } + catch(APIException e) + { + throw new APIException("Failed to parise service description document " + serviceDescPath, e); + } + } + + + /** + * Gets an API Service given an HTTP Method and URI + * + * @param method + * @param uri + * @return + */ + public APIServiceMatch findService(String method, String uri) + { + // TODO: Replace with more efficient approach + DeclarativeAPIServiceMatch apiServiceMatch = null; + String match = method.toString().toUpperCase() + ":" + uri; + for (Map.Entry service : servicesByURL.entrySet()) + { + String indexedPath = service.getKey(); + if (match.startsWith(indexedPath)) + { + String matchPath = indexedPath.substring(indexedPath.indexOf(':') +1); + apiServiceMatch = new DeclarativeAPIServiceMatch(matchPath, service.getValue()); + break; + } + } + return apiServiceMatch; + } + + + public ServletContext getContext() + { + return servletContext; + } + + + public APIService getService(String id) + { + return servicesById.get(id); + } + + + public void setFormatRegistry(FormatRegistry formatRegistry) + { + this.formatRegistry = formatRegistry; + } + + + public FormatRegistry getFormatRegistry() + { + return this.formatRegistry; + } + + + public APITemplateProcessor getTemplateProcessor() + { + return this.stores.getTemplateProcessor(); + } + + + public void setAPIContext(ServletContext context) + { + this.servletContext = context; + } + + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + this.applicationContext = applicationContext; + } + + + public Collection getServices() + { + return servicesById.values(); + } + + + public static class DeclarativeAPIServiceMatch implements APIServiceMatch + { + private String path; + private APIService service; + + + public DeclarativeAPIServiceMatch(String path, APIService service) + { + this.path = path; + this.service = service; + } + + + public String getPath() + { + return path; + } + + public APIService getService() + { + return service; + } + + } + + +} diff --git a/source/java/org/alfresco/web/api/ScriptLoader.java b/source/java/org/alfresco/web/api/ScriptLoader.java new file mode 100644 index 0000000000..8903d658d7 --- /dev/null +++ b/source/java/org/alfresco/web/api/ScriptLoader.java @@ -0,0 +1,10 @@ +package org.alfresco.web.api; + +import org.alfresco.service.cmr.repository.ScriptLocation; + +public interface ScriptLoader +{ + + ScriptLocation getScriptLocation(String path); + +} diff --git a/source/java/org/alfresco/web/api/services/APIServiceTemplateImpl.java b/source/java/org/alfresco/web/api/ScriptedAPIService.java similarity index 59% rename from source/java/org/alfresco/web/api/services/APIServiceTemplateImpl.java rename to source/java/org/alfresco/web/api/ScriptedAPIService.java index 87656000b7..dae8320d40 100644 --- a/source/java/org/alfresco/web/api/services/APIServiceTemplateImpl.java +++ b/source/java/org/alfresco/web/api/ScriptedAPIService.java @@ -1,147 +1,132 @@ -/* - * 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.api.services; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.io.Writer; -import java.util.Map; - -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.repository.TemplateException; -import org.alfresco.web.api.APIException; -import org.alfresco.web.api.APIRequest; -import org.alfresco.web.api.APIResponse; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - - -/** - * Template based implementation of an API Service - * - * @author davidc - */ -public abstract class APIServiceTemplateImpl extends APIServiceImpl -{ - // Logger - private static final Log logger = LogFactory.getLog(APIServiceTemplateImpl.class); - - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#execute(org.alfresco.web.api.APIRequest, org.alfresco.web.api.APIResponse) - */ - public void execute(APIRequest req, APIResponse res) throws IOException - { - // construct data model for template - Map model = createAPIModel(req, res); - model = createModel(req, res, model); - - // process requested format - String format = req.getFormat(); - if (format == null || format.length() == 0) - { - format = getDefaultFormat(); - } - String mimetype = getFormatRegistry().getMimeType(req.getAgent(), format); - if (mimetype == null) - { - throw new APIException("API format '" + format + "' does not exist"); - } - - // render output - res.setContentType(mimetype + ";charset=UTF-8"); - - if (logger.isDebugEnabled()) - logger.debug("Response content type: " + mimetype); - - try - { - renderTemplate(null, format, model, res.getWriter()); - } - catch(TemplateException e) - { - throw new APIException("Failed to process format '" + format + "'", e); - } - } - - - /** - * Create a custom service model - * - * @param req API request - * @param res API response - * @param model basic API model - * @return custom service model - */ - protected abstract Map createModel(APIRequest req, APIResponse res, Map model); - - - /** - * Render a template to the API Response - * - * @param type type of template (null defaults to type VIEW) - * @param format template format (null, default format) - * @param model data model to render - * @param writer where to output - */ - protected void renderTemplate(String type, String format, Map model, Writer writer) - { - type = (type == null) ? "view" : type; - format = (format == null) ? "" : format; - String templatePath = (this.getClass().getSimpleName() + "_" + type + "_" + format).replace(".", "/") + ".ftl"; - - if (logger.isDebugEnabled()) - logger.debug("Rendering service template '" + templatePath + "'"); - - renderTemplate(templatePath, model, writer); - } - - - /** - * Simple test that can be executed outside of web context - */ - /*package*/ void test(final String format) - throws IOException - { - AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { - public Object doWork() - { - // create test model - Map model = createTestModel(); - - // render service template to string - StringWriter rendition = new StringWriter(); - PrintWriter writer = new PrintWriter(rendition); - renderTemplate(null, format, model, writer); - System.out.println(rendition.toString()); - - return null; - } - }, AuthenticationUtil.getSystemUserName()); - } - -} +/* + * 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.api; + +import java.io.IOException; +import java.io.Writer; +import java.util.Map; + +import org.alfresco.service.cmr.repository.TemplateException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * Script based implementation of an API Service + * + * @author davidc + */ +public class ScriptedAPIService extends AbstractAPIService +{ + // Logger + private static final Log logger = LogFactory.getLog(ScriptedAPIService.class); + + private String baseTemplatePath; + + + @Override + public void init(APIRegistry apiRegistry) + { + super.init(apiRegistry); + baseTemplatePath = getDescription().getId().replace('.', '/'); + + // TODO: Test for .js script + } + + + /* (non-Javadoc) + * @see org.alfresco.web.api.APIService#execute(org.alfresco.web.api.APIRequest, org.alfresco.web.api.APIResponse) + */ + final public void execute(APIRequest req, APIResponse res) throws IOException + { + // construct data model for template + Map model = createAPIModel(req, res); + model = createModel(req, res, model); + + // TODO: execute script if it exists + + // process requested format + String format = req.getFormat(); + if (format == null || format.length() == 0) + { + format = getDescription().getDefaultFormat(); + } + + String mimetype = getAPIRegistry().getFormatRegistry().getMimeType(req.getAgent(), format); + if (mimetype == null) + { + throw new APIException("API format '" + format + "' is not registered"); + } + + // render output + res.setContentType(mimetype + ";charset=UTF-8"); + + if (logger.isDebugEnabled()) + logger.debug("Response content type: " + mimetype); + + try + { + renderFormatTemplate(format, model, res.getWriter()); + } + catch(TemplateException e) + { + throw new APIException("Failed to process format '" + format + "'", e); + } + } + + + /** + * Create a custom service model + * + * @param req API request + * @param res API response + * @param model basic API model + * @return custom service model + */ + protected Map createModel(APIRequest req, APIResponse res, Map model) + { + return model; + } + + + /** + * Render a template (of given format) to the API Response + * + * @param format template format (null, default format) + * @param model data model to render + * @param writer where to output + */ + final protected void renderFormatTemplate(String format, Map model, Writer writer) + { + format = (format == null) ? "" : format; + String templatePath = baseTemplatePath + "_" + format + ".ftl"; + + if (logger.isDebugEnabled()) + logger.debug("Rendering service template '" + templatePath + "'"); + + renderTemplate(templatePath, model, writer); + } + +} diff --git a/source/java/org/alfresco/web/api/ServiceLogger.java b/source/java/org/alfresco/web/api/ServiceLogger.java index ead62cc75e..d3d6d91072 100644 --- a/source/java/org/alfresco/web/api/ServiceLogger.java +++ b/source/java/org/alfresco/web/api/ServiceLogger.java @@ -53,13 +53,14 @@ public class ServiceLogger implements MethodInterceptor if (logger.isDebugEnabled()) { APIService service = (APIService)invocation.getThis(); + APIDescription description = service.getDescription(); String user = AuthenticationUtil.getCurrentUserName(); String locale = I18NUtil.getLocale().toString(); - logger.debug("Invoking service " + service.getName() + (user == null ? " (unauthenticated)" : " (authenticated as " + user + ")" + " (" + locale + ")")); + logger.debug("Invoking service " + description.getId() + (user == null ? " (unauthenticated)" : " (authenticated as " + user + ")" + " (" + locale + ")")); long start = System.currentTimeMillis(); retVal = invocation.proceed(); long end = System.currentTimeMillis(); - logger.debug("Service " + service.getName() + " executed in " + (end - start) + "ms"); + logger.debug("Service " + description.getId() + " executed in " + (end - start) + "ms"); } else { diff --git a/source/java/org/alfresco/web/api/SystemAuthenticator.java b/source/java/org/alfresco/web/api/SystemAuthenticator.java new file mode 100644 index 0000000000..4732be5888 --- /dev/null +++ b/source/java/org/alfresco/web/api/SystemAuthenticator.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.web.api; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * System Authentication Interceptor + * + * @author davidc + */ +public class SystemAuthenticator implements MethodInterceptor +{ + // Logger + private static final Log logger = LogFactory.getLog(SystemAuthenticator.class); + + + /* (non-Javadoc) + * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) + */ + public Object invoke(MethodInvocation invocation) + throws Throwable + { + String currentUser = null; + Object retVal = null; + + try + { + // + // Determine if user already authenticated + // + + currentUser = AuthenticationUtil.getCurrentUserName(); + if (logger.isDebugEnabled()) + logger.debug("Current authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser)); + + // + // Force system user + // + + if (logger.isDebugEnabled()) + logger.debug("Authenticating as System"); + + AuthenticationUtil.setSystemUserAsCurrentUser(); + + // + // Invoke service + // + + retVal = invocation.proceed(); + } + finally + { + AuthenticationUtil.clearCurrentSecurityContext(); + if (currentUser != null) + { + AuthenticationUtil.setCurrentUser(currentUser); + } + + if (logger.isDebugEnabled()) + logger.debug("Authentication reset: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser)); + } + + return retVal; + } + +} diff --git a/source/java/org/alfresco/web/api/TemplateClassPathSet.java b/source/java/org/alfresco/web/api/TemplateClassPathSet.java deleted file mode 100644 index 5ef8a39089..0000000000 --- a/source/java/org/alfresco/web/api/TemplateClassPathSet.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.api; - -import java.io.File; -import java.util.Set; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; - -import freemarker.cache.FileTemplateLoader; - - -/** - * A set of template class paths - * - * @author davidc - */ -public class TemplateClassPathSet implements InitializingBean -{ - // Logger - private static final Log logger = LogFactory.getLog(TemplateClassPathSet.class); - - private Set classPaths; - private APITemplateProcessor templateProcessor; - private ResourceLoader resourceLoader; - - /** - * Construct - */ - public TemplateClassPathSet() - { - resourceLoader = new DefaultResourceLoader(); - } - - /** - * Sets the Template Processor - * - * @param templateProcessor - */ - public void setTemplateProcessor(APITemplateProcessor templateProcessor) - { - this.templateProcessor = templateProcessor; - } - - /** - * Sets the paths - * - * @param classPaths - */ - public void setPaths(Set classPaths) - { - this.classPaths = classPaths; - } - - /* (non-Javadoc) - * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() - */ - public void afterPropertiesSet() throws Exception - { - // Add class paths to template processor - for (String classPath : classPaths) - { - Resource resource = resourceLoader.getResource(classPath); - if (resource.exists()) - { - File file = resource.getFile(); - templateProcessor.addTemplateLoader(new FileTemplateLoader(file)); - - if (logger.isDebugEnabled()) - logger.debug("Registered template classpath '" + classPath); - } - else if (logger.isWarnEnabled()) - { - logger.warn("Template classpath '" + classPath + "' does not exist"); - } - } - } - -} diff --git a/source/java/org/alfresco/web/api/TestAPIServer.java b/source/java/org/alfresco/web/api/TestAPIServer.java new file mode 100644 index 0000000000..3901b95eee --- /dev/null +++ b/source/java/org/alfresco/web/api/TestAPIServer.java @@ -0,0 +1,214 @@ +package org.alfresco.web.api; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.alfresco.i18n.I18NUtil; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.core.io.ClassPathResource; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +public class TestAPIServer +{ + /** + * The reader for interaction. + */ + private BufferedReader fIn; + + private APIRegistry apiRegistry; + + /** + * Last command issued + */ + private String lastCommand = null; + + + + + /** + * Main entry point. + */ + public static void main(String[] args) + { + try + { + TestAPIServer test = new TestAPIServer(); + test.rep(); + } + catch(Throwable e) + { + StringWriter strWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(strWriter); + e.printStackTrace(printWriter); + System.out.println(strWriter.toString()); + } + finally + { + System.exit(0); + } + } + + /** + * Make up a new console. + */ + public TestAPIServer() + { + fIn = new BufferedReader(new InputStreamReader(System.in)); + String[] CONFIG_LOCATIONS = new String[] { "classpath:alfresco/application-context.xml", "classpath:alfresco/web-api-application-context.xml" }; + ApplicationContext context = new ClassPathXmlApplicationContext(CONFIG_LOCATIONS); + DeclarativeAPIRegistry testAPIRegistry = (DeclarativeAPIRegistry)context.getBean("web.api.test.Registry"); + testAPIRegistry.initServices(); + apiRegistry = testAPIRegistry; + } + + /** + * A Read-Eval-Print loop. + */ + public void rep() + { + // accept commands + while (true) + { + System.out.print("ok> "); + try + { + // get command + final String line = fIn.readLine(); + if (line.equals("exit") || line.equals("quit")) + { + return; + } + + // execute command in context of currently selected user + long startms = System.currentTimeMillis(); + System.out.print(interpretCommand(line)); + System.out.println("" + (System.currentTimeMillis() - startms) + "ms"); + } + catch (Exception e) + { + e.printStackTrace(System.err); + System.out.println(""); + } + } + } + + /** + * Execute a single command using the BufferedReader passed in for any data needed. + * + * TODO: Use decent parser! + * + * @param line The unparsed command + * @return The textual output of the command. + */ + private String interpretCommand(String line) + throws IOException + { + String[] command = line.split(" "); + if (command.length == 0) + { + command = new String[1]; + command[0] = line; + } + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(bout); + + // repeat last command? + if (command[0].equals("r")) + { + if (lastCommand == null) + { + return "No command entered yet."; + } + return "repeating command " + lastCommand + "\n\n" + interpretCommand(lastCommand); + } + + // remember last command + lastCommand = line; + + // execute command + if (command[0].equals("help")) + { + // TODO: + String helpFile = I18NUtil.getMessage("test_service.help"); + ClassPathResource helpResource = new ClassPathResource(helpFile); + byte[] helpBytes = new byte[500]; + InputStream helpStream = helpResource.getInputStream(); + try + { + int read = helpStream.read(helpBytes); + while (read != -1) + { + bout.write(helpBytes, 0, read); + read = helpStream.read(helpBytes); + } + } + finally + { + helpStream.close(); + } + } + + else if (command[0].equals("req")) + { + if (command.length < 2) + { + return "Syntax Error.\n"; + } + + String uri = command[1]; + MockHttpServletRequest req = createRequest("get", uri); + MockHttpServletResponse res = new MockHttpServletResponse(); + + APIServiceMatch match = apiRegistry.findService(req.getMethod(), uri); + if (match == null) + { + throw new APIException("No service bound to uri '" + uri + "'"); + } + + APIRequest apiReq = new APIRequest(req, match); + APIResponse apiRes = new APIResponse(res); + match.getService().execute(apiReq, apiRes); + bout.write(res.getContentAsByteArray()); + out.println(); + } + + out.flush(); + String retVal = new String(bout.toByteArray()); + out.close(); + return retVal; + } + + + private MockHttpServletRequest createRequest(String method, String uri) + { + MockHttpServletRequest req = new MockHttpServletRequest("get", uri); + + // set parameters + int iArgIndex = uri.indexOf('?'); + if (iArgIndex != -1 && iArgIndex != uri.length() -1) + { + String uriArgs = uri.substring(iArgIndex +1); + String[] args = uriArgs.split("&"); + for (String arg : args) + { + String[] parts = arg.split("="); + req.addParameter(parts[0], (parts.length == 2) ? parts[1] : null); + } + } + + // set path info + req.setPathInfo(iArgIndex == -1 ? uri : uri.substring(0, iArgIndex)); + + return req; + } + +} diff --git a/source/java/org/alfresco/web/api/services/APIServiceImpl.java b/source/java/org/alfresco/web/api/services/APIServiceImpl.java deleted file mode 100644 index 1b5d1cdfa7..0000000000 --- a/source/java/org/alfresco/web/api/services/APIServiceImpl.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * 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.api.services; - -import java.io.Writer; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.ServletContext; - -import org.alfresco.repo.template.AbsoluteUrlMethod; -import org.alfresco.repo.template.ISO8601DateFormatMethod; -import org.alfresco.repo.template.UrlEncodeMethod; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.TemplateProcessor; -import org.alfresco.service.descriptor.DescriptorService; -import org.alfresco.web.api.APIContextAware; -import org.alfresco.web.api.APIRequest; -import org.alfresco.web.api.APIResponse; -import org.alfresco.web.api.APIService; -import org.alfresco.web.api.FormatRegistry; -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; - - -/** - * Skeleton implementation of an API Service - * - * @author davidc - */ -public abstract class APIServiceImpl implements BeanNameAware, APIService, APIContextAware -{ - private String name; - private String uri; - - // dependencies - private ServletContext context; - private ServiceRegistry serviceRegistry; - private DescriptorService descriptorService; - private TemplateProcessor templateProcessor; - private FormatRegistry formatRegistry; - - // - // Initialisation - // - - /* (non-Javadoc) - * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String) - */ - public void setBeanName(String name) - { - this.name = name; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIContextAware#setAPIContext(javax.servlet.ServletContext) - */ - public void setAPIContext(ServletContext context) - { - this.context = context; - } - - /** - * @param serviceRegistry - */ - public void setServiceRegistry(ServiceRegistry serviceRegistry) - { - this.serviceRegistry = serviceRegistry; - } - - /** - * @param templateProcessor - */ - public void setTemplateProcessor(TemplateProcessor templateProcessor) - { - this.templateProcessor = templateProcessor; - } - - /** - * @param descriptorService - */ - public void setDescriptorService(DescriptorService descriptorService) - { - this.descriptorService = descriptorService; - } - - /** - * @param formatRegistry - */ - public void setFormatRegistry(FormatRegistry formatRegistry) - { - this.formatRegistry = formatRegistry; - } - - /** - * Sets the Http URI - * - * @param uri - */ - public void setHttpUri(String uri) - { - this.uri = uri; - } - - // - // Service Meta-Data - // - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getName() - */ - public String getName() - { - return this.name; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getHttpUri() - */ - public String getHttpUri() - { - return this.uri; - } - - - // - // Service Implementation Helpers - // - - /** - * @return descriptorService - */ - protected ServletContext getAPIContext() - { - return context; - } - - /** - * @return descriptorService - */ - protected DescriptorService getDescriptorService() - { - return descriptorService; - } - - /** - * @return serviceRegistry - */ - protected ServiceRegistry getServiceRegistry() - { - return serviceRegistry; - } - - /** - * @return templateProcessor - */ - protected TemplateProcessor getTemplateProcessor() - { - return templateProcessor; - } - - /** - * @return formatRegistry - */ - protected FormatRegistry getFormatRegistry() - { - return formatRegistry; - } - - - // - // Basic Templating Support - // - - - /** - * Create a basic API model - * - * @param req api request - * @param res api response - * @return template model - */ - protected Map createAPIModel(APIRequest req, APIResponse res) - { - Map model = new HashMap(7, 1.0f); - model.put("xmldate", new ISO8601DateFormatMethod()); - model.put("absurl", new AbsoluteUrlMethod(req.getPath())); - model.put("urlencode", new UrlEncodeMethod()); - model.put("date", new Date()); - model.put("agent", descriptorService.getServerDescriptor()); - model.put("request", req); - model.put("response", res); - return model; - } - - /** - * Render a template (identified by path) - * - * @param templatePath template path - * @param model model - * @param writer output writer - */ - protected void renderTemplate(String templatePath, Map model, Writer writer) - { - templateProcessor.process(templatePath, model, writer); - } - - /** - * Render a template (contents as string) - * @param template the template - * @param model model - * @param writer output writer - */ - protected void renderString(String template, Map model, Writer writer) - { - templateProcessor.processString(template, model, writer); - } - - - /** - * Helper to retrieve API Service - * - * @param name name of service - * @return the service - */ - protected static APIService getMethod(String name) - { - String[] CONFIG_LOCATIONS = new String[] { "classpath:alfresco/application-context.xml", "classpath:alfresco/web-api-application-context.xml" }; - ApplicationContext context = new ClassPathXmlApplicationContext(CONFIG_LOCATIONS); - APIService method = (APIService)context.getBean(name); - return method; - } - - /** - * Create a base test model (for use stand-alone) - * - * @return test model - */ - protected Map createTestModel() - { - Map model = new HashMap(7, 1.0f); - - // create api methods - model.put("xmldate", new ISO8601DateFormatMethod()); - model.put("urlencode", new UrlEncodeMethod()); - model.put("absurl", new AbsoluteUrlMethod("http://test:8080/test")); - model.put("date", new Date()); - - // create dummy request model - Map request = new HashMap(); - request.put("servicePath", "http://localhost:8080/alfresco/service"); - request.put("path", "http://localhost:8080/alfresco"); - request.put("url", "http://localhost:8080/alfresco/service/testurl"); - request.put("guest", false); - request.put("format", "xml"); - model.put("request", request); - - // create dummy agent model - model.put("agent", getDescriptorService().getServerDescriptor()); - - return model; - } - -} diff --git a/source/java/org/alfresco/web/api/services/KeywordSearchDescription.java b/source/java/org/alfresco/web/api/services/Index.java similarity index 52% rename from source/java/org/alfresco/web/api/services/KeywordSearchDescription.java rename to source/java/org/alfresco/web/api/services/Index.java index fad1c95052..1ac4e4f372 100644 --- a/source/java/org/alfresco/web/api/services/KeywordSearchDescription.java +++ b/source/java/org/alfresco/web/api/services/Index.java @@ -1,94 +1,52 @@ -/* - * 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.api.services; - -import java.util.Map; - -import org.alfresco.web.api.APIRequest; -import org.alfresco.web.api.APIResponse; -import org.alfresco.web.api.APIRequest.HttpMethod; -import org.alfresco.web.api.APIRequest.RequiredAuthentication; - - -/** - * Provide OpenSearch Description for an Alfresco Keyword (simple) Search - * - * @author davidc - */ -public class KeywordSearchDescription extends APIServiceTemplateImpl -{ - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getRequiredAuthentication() - */ - public RequiredAuthentication getRequiredAuthentication() - { - return APIRequest.RequiredAuthentication.None; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getHttpMethod() - */ - public HttpMethod getHttpMethod() - { - return APIRequest.HttpMethod.GET; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDefaultFormat() - */ - public String getDefaultFormat() - { - return APIResponse.OPENSEARCH_DESCRIPTION_FORMAT; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.services.APIServiceTemplateImpl#createModel(org.alfresco.web.api.APIRequest, org.alfresco.web.api.APIResponse, java.util.Map) - */ - @Override - protected Map createModel(APIRequest req, APIResponse res, Map model) - { - return model; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDescription() - */ - public String getDescription() - { - return "Retrieve the OpenSearch Description for the Alfresco Web Client keyword search"; - } - - /** - * Simple test that can be executed outside of web context - */ - public static void main(String[] args) - throws Exception - { - KeywordSearchDescription service = (KeywordSearchDescription)APIServiceImpl.getMethod("web.api.KeywordSearchDescription"); - service.test(APIResponse.OPENSEARCH_DESCRIPTION_FORMAT); - } - -} +/* + * 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.api.services; + +import java.util.Map; + +import org.alfresco.web.api.APIRequest; +import org.alfresco.web.api.APIResponse; +import org.alfresco.web.api.ScriptedAPIService; + + +/** + * Retrieves the list of available Web APIs + * + * @author davidc + */ +public class Index extends ScriptedAPIService +{ + + /* (non-Javadoc) + * @see org.alfresco.web.api.services.APIServiceTemplateImpl#createModel(org.alfresco.web.api.APIRequest, org.alfresco.web.api.APIResponse, java.util.Map) + */ + @Override + protected Map createModel(APIRequest req, APIResponse res, Map model) + { + model.put("services", getAPIRegistry().getServices()); + return model; + } + +} diff --git a/source/java/org/alfresco/web/api/services/KeywordSearch.java b/source/java/org/alfresco/web/api/services/KeywordSearch.java index c9aa116b1d..149372c58b 100644 --- a/source/java/org/alfresco/web/api/services/KeywordSearch.java +++ b/source/java/org/alfresco/web/api/services/KeywordSearch.java @@ -43,8 +43,7 @@ import org.alfresco.util.ParameterCheck; import org.alfresco.web.api.APIException; import org.alfresco.web.api.APIRequest; import org.alfresco.web.api.APIResponse; -import org.alfresco.web.api.APIRequest.HttpMethod; -import org.alfresco.web.api.APIRequest.RequiredAuthentication; +import org.alfresco.web.api.ScriptedAPIService; import org.alfresco.web.ui.common.Utils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -55,7 +54,7 @@ import org.apache.commons.logging.LogFactory; * * @author davidc */ -public class KeywordSearch extends APIServiceTemplateImpl +public class KeywordSearch extends ScriptedAPIService { // Logger private static final Log logger = LogFactory.getLog(KeywordSearch.class); @@ -64,7 +63,7 @@ public class KeywordSearch extends APIServiceTemplateImpl // TODO: allow configuration of search store protected static final StoreRef SEARCH_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); protected static final int DEFAULT_ITEMS_PER_PAGE = 10; - protected static final String QUERY_TEMPLATE_TYPE = "query"; + protected static final String QUERY_FORMAT = "query_"; // dependencies protected SearchService searchService; @@ -74,7 +73,7 @@ public class KeywordSearch extends APIServiceTemplateImpl { public String resolveImagePathForName(String filename, boolean small) { - return Utils.getFileTypeImage(getAPIContext(), filename, small); + return Utils.getFileTypeImage(getAPIRegistry().getContext(), filename, small); } }; @@ -86,38 +85,6 @@ public class KeywordSearch extends APIServiceTemplateImpl this.searchService = searchService; } - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getRequiredAuthentication() - */ - public RequiredAuthentication getRequiredAuthentication() - { - return APIRequest.RequiredAuthentication.User; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getHttpMethod() - */ - public HttpMethod getHttpMethod() - { - return APIRequest.HttpMethod.GET; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDefaultFormat() - */ - public String getDefaultFormat() - { - return APIResponse.HTML_FORMAT; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDescription() - */ - public String getDescription() - { - return "Issue an Alfresco Web Client keyword search"; - } - /* (non-Javadoc) * @see org.alfresco.web.api.services.APIServiceTemplateImpl#createModel(org.alfresco.web.api.APIRequest, org.alfresco.web.api.APIResponse, java.util.Map) */ @@ -162,7 +129,7 @@ public class KeywordSearch extends APIServiceTemplateImpl // execute the search // - SearchResult results = search(searchTerms, startPage, itemsPerPage, locale); + SearchResult results = search(searchTerms, startPage, itemsPerPage, locale, req.getParameterMap()); // // append to model @@ -181,7 +148,7 @@ public class KeywordSearch extends APIServiceTemplateImpl * @param locale The current locale * @return The search result */ - private SearchResult search(String searchTerms, int startPage, int itemsPerPage, Locale locale) + private SearchResult search(String searchTerms, int startPage, int itemsPerPage, Locale locale, Map reqParams) { SearchResult searchResult = null; ResultSet results = null; @@ -191,9 +158,10 @@ public class KeywordSearch extends APIServiceTemplateImpl // construct search statement String[] terms = searchTerms.split(" "); Map statementModel = new HashMap(7, 1.0f); + statementModel.put("args", reqParams); statementModel.put("terms", terms); Writer queryWriter = new StringWriter(1024); - renderTemplate(QUERY_TEMPLATE_TYPE, null, statementModel, queryWriter); + renderFormatTemplate(QUERY_FORMAT, statementModel, queryWriter); String query = queryWriter.toString(); // execute query @@ -411,28 +379,5 @@ public class KeywordSearch extends APIServiceTemplateImpl return score; } } - - - /** - * Simple test that can be executed outside of web context - */ - public static void main(String[] args) - throws Exception - { - KeywordSearch service = (KeywordSearch)APIServiceImpl.getMethod("web.api.KeywordSearch"); - service.test(APIResponse.HTML_FORMAT); - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.services.APIServiceImpl#createTestModel() - */ - @Override - protected Map createTestModel() - { - Map model = super.createTestModel(); - SearchResult result = search("alfresco tutorial", 1, 5, I18NUtil.getLocale()); - model.put("search", result); - return model; - } } \ No newline at end of file diff --git a/source/java/org/alfresco/web/api/services/SearchEngines.java b/source/java/org/alfresco/web/api/services/SearchEngines.java index 166a3aad65..9a9d2abfb3 100644 --- a/source/java/org/alfresco/web/api/services/SearchEngines.java +++ b/source/java/org/alfresco/web/api/services/SearchEngines.java @@ -34,8 +34,7 @@ import org.alfresco.i18n.I18NUtil; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.web.api.APIRequest; import org.alfresco.web.api.APIResponse; -import org.alfresco.web.api.APIRequest.HttpMethod; -import org.alfresco.web.api.APIRequest.RequiredAuthentication; +import org.alfresco.web.api.ScriptedAPIService; import org.alfresco.web.config.OpenSearchConfigElement; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -46,7 +45,7 @@ import org.apache.commons.logging.LogFactory; * * @author davidc */ -public class SearchEngines extends APIServiceTemplateImpl +public class SearchEngines extends ScriptedAPIService { // url argument values public static final String URL_ARG_DESCRIPTION = "description"; @@ -76,38 +75,6 @@ public class SearchEngines extends APIServiceTemplateImpl this.searchProxy = searchProxy; } - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getRequiredAuthentication() - */ - public RequiredAuthentication getRequiredAuthentication() - { - return APIRequest.RequiredAuthentication.None; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getHttpMethod() - */ - public HttpMethod getHttpMethod() - { - return APIRequest.HttpMethod.GET; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDefaultFormat() - */ - public String getDefaultFormat() - { - return APIResponse.HTML_FORMAT; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDescription() - */ - public String getDescription() - { - return "Retrieve a list of (server-side) registered search engines"; - } - @Override protected Map createModel(APIRequest req, APIResponse res, Map model) { @@ -233,29 +200,5 @@ public class SearchEngines extends APIServiceTemplateImpl return engine; } } - - - /** - * Simple test that can be executed outside of web context - */ - public static void main(String[] args) - throws Exception - { - SearchEngines service = (SearchEngines)APIServiceImpl.getMethod("web.api.SearchEngines"); - service.test(APIResponse.ATOM_FORMAT); - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.services.APIServiceImpl#createTestModel() - */ - @Override - protected Map createTestModel() - { - Map model = super.createTestModel(); - Set urls = getUrls(URL_ARG_ALL); - model.put("urltype", "template"); - model.put("engines", urls); - return model; - } - + } \ No newline at end of file diff --git a/source/java/org/alfresco/web/api/services/SearchProxy.java b/source/java/org/alfresco/web/api/services/SearchProxy.java index 760dea31df..7a9e79cdc7 100644 --- a/source/java/org/alfresco/web/api/services/SearchProxy.java +++ b/source/java/org/alfresco/web/api/services/SearchProxy.java @@ -41,9 +41,8 @@ import org.alfresco.repo.content.MimetypeMap; import org.alfresco.web.api.APIException; import org.alfresco.web.api.APIRequest; import org.alfresco.web.api.APIResponse; +import org.alfresco.web.api.AbstractAPIService; import org.alfresco.web.api.FormatRegistry; -import org.alfresco.web.api.APIRequest.HttpMethod; -import org.alfresco.web.api.APIRequest.RequiredAuthentication; import org.alfresco.web.app.servlet.HTTPProxy; import org.alfresco.web.config.OpenSearchConfigElement; import org.alfresco.web.config.OpenSearchConfigElement.EngineConfig; @@ -69,7 +68,7 @@ import org.springframework.beans.factory.InitializingBean; * * @author davidc */ -public class SearchProxy extends APIServiceImpl implements InitializingBean +public class SearchProxy extends AbstractAPIService implements InitializingBean { // Logger private static final Log logger = LogFactory.getLog(SearchProxy.class); @@ -96,38 +95,6 @@ public class SearchProxy extends APIServiceImpl implements InitializingBean this.configService = configService; } - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getRequiredAuthentication() - */ - public RequiredAuthentication getRequiredAuthentication() - { - return APIRequest.RequiredAuthentication.None; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getHttpMethod() - */ - public HttpMethod getHttpMethod() - { - return APIRequest.HttpMethod.GET; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDefaultFormat() - */ - public String getDefaultFormat() - { - return null; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDescription() - */ - public String getDescription() - { - return "Issue an OpenSearch query via Alfresco"; - } - /* (non-Javadoc) * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @@ -153,11 +120,11 @@ public class SearchProxy extends APIServiceImpl implements InitializingBean public void execute(APIRequest req, APIResponse res) throws IOException { - String extensionPath = req.getExtensionPath(this); + String extensionPath = req.getExtensionPath(); String[] extensionPaths = extensionPath.split("/"); if (extensionPaths.length != 2) { - throw new APIException("OpenSearch engine has not been specified as //"); + throw new APIException("OpenSearch engine has not been specified as /{engine}/{format}"); } // retrieve search engine configuration diff --git a/source/java/org/alfresco/web/api/services/Services.java b/source/java/org/alfresco/web/api/services/Services.java deleted file mode 100644 index ce1d732eaf..0000000000 --- a/source/java/org/alfresco/web/api/services/Services.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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.api.services; - -import java.util.Collection; -import java.util.Map; - -import org.alfresco.web.api.APIRequest; -import org.alfresco.web.api.APIResponse; -import org.alfresco.web.api.APIService; -import org.alfresco.web.api.APIRequest.HttpMethod; -import org.alfresco.web.api.APIRequest.RequiredAuthentication; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; - - -/** - * Retrieves the list of available Web APIs - * - * @author davidc - */ -public class Services extends APIServiceTemplateImpl implements ApplicationContextAware -{ - private ApplicationContext applicationContext; - - - /* (non-Javadoc) - * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) - */ - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - this.applicationContext = applicationContext; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDescription() - */ - public String getDescription() - { - return "Retrieve the list of available Alfresco Web APIs"; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getRequiredAuthentication() - */ - public RequiredAuthentication getRequiredAuthentication() - { - return RequiredAuthentication.None; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getHttpMethod() - */ - public HttpMethod getHttpMethod() - { - return HttpMethod.GET; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.APIService#getDefaultFormat() - */ - public String getDefaultFormat() - { - return APIResponse.HTML_FORMAT; - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.services.APIServiceTemplateImpl#createModel(org.alfresco.web.api.APIRequest, org.alfresco.web.api.APIResponse, java.util.Map) - */ - @Override - protected Map createModel(APIRequest req, APIResponse res, Map model) - { - model.put("services", getServices()); - return model; - } - - /** - * Gets the collection of API Services - * - * @return api services - */ - private Collection getServices() - { - Map services = applicationContext.getBeansOfType(APIService.class, false, false); - return services.values(); - } - - - /** - * Simple test that can be executed outside of web context - */ - public static void main(String[] args) - throws Exception - { - Services service = (Services)APIServiceImpl.getMethod("web.api.Services"); - service.test(APIResponse.HTML_FORMAT); - } - - /* (non-Javadoc) - * @see org.alfresco.web.api.services.APIServiceImpl#createTestModel() - */ - @Override - protected Map createTestModel() - { - Map model = super.createTestModel(); - model.put("services", getServices()); - return model; - } - -}