From 565c57d893c70c424adf7f6921342a016a32bc26 Mon Sep 17 00:00:00 2001 From: David Caruana Date: Thu, 11 Jun 2009 18:25:59 +0000 Subject: [PATCH] Web Script Framework enhancements: ReadOnly transactions, Buffered Responses - additional 'allow' attribute on element in web script descriptor - values: readonly, readwrite (default) - readonly means that the whole web script executes in read transaction - readonly lighter weight; no flushing, no cache checks/updates - transaction aware web script response buffers - only commits to response when trx is committed - fixes ALFCOM-2497 - CMIS: createFolder & immediately add document can fail - also means errors half-way thru response result in clean response with error contents only - readonly transactions are not buffered - WebScript RepoStore now uses ReadOnly transaction for gets - CMIS getter Web Scripts set to ReadOnly transaction - Fix up Web Script pattern that checks for WebScriptServletResponse using instanceof - no longer the case, as it may be wrapped in BufferedResponse - use getRuntime() instanceof WebScriptServletRuntime and/or - WebScriptServletRuntime.getHttpServletResponse/Request(WebScriptReponse r) - returns null, if none Tests: - Run CMIS Tests - Run CMIS BulkCreateSystemTest (now working) - Run Share Suggestion: - Update your 'read' web script descriptors to include . This will improve repo performance significantly. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@14670 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../repository/search/query.post.desc.xml | 1 + .../store/allowableactions.get.desc.xml | 1 + .../repository/store/checkedout.get.desc.xml | 1 + .../repository/store/children.get.desc.xml | 1 + .../repository/store/content.get.desc.xml | 1 + .../repository/store/descendants.get.desc.xml | 1 + .../repository/store/item.get.desc.xml | 1 + .../repository/store/parent.get.desc.xml | 1 + .../repository/store/parents.get.desc.xml | 1 + .../repository/store/pwc.get.desc.xml | 1 + .../store/relationship.get.desc.xml | 1 + .../repository/store/type.get.desc.xml | 1 + .../store/typechildren.get.desc.xml | 1 + .../store/typedescendants.get.desc.xml | 1 + .../repository/store/types.get.desc.xml | 1 + .../repository/store/unfiled.get.desc.xml | 1 + .../repository/store/versions.get.desc.xml | 1 + .../cmis/rest/test/BulkCreateSystemTest.java | 3 +- .../alfresco/repo/web/scripts/RepoStore.java | 18 +- .../repo/web/scripts/RepositoryContainer.java | 256 +++++++++++++++++- .../repo/web/scripts/bean/AVMRemoteStore.java | 10 +- .../repo/web/scripts/bean/SearchProxy.java | 7 +- .../web/scripts/content/StreamContent.java | 33 +-- 23 files changed, 291 insertions(+), 53 deletions(-) diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/search/query.post.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/search/query.post.desc.xml index 163c7acbab..512c858832 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/search/query.post.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/search/query.post.desc.xml @@ -31,6 +31,7 @@ It is recommended that “includeAllowableActions” be used with query statemen /api/query user + CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/allowableactions.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/allowableactions.get.desc.xml index 12a2fd6469..d2e2ee5315 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/allowableactions.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/allowableactions.get.desc.xml @@ -4,6 +4,7 @@ /api/node/{store_type}/{store_id}/{id}/permissions /api/path/{store_type}/{store_id}/{id}/permissions guest + argument CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/checkedout.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/checkedout.get.desc.xml index af8cb3f32c..a0aa4362eb 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/checkedout.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/checkedout.get.desc.xml @@ -30,6 +30,7 @@ If no “maxItems” value is provided, then the Repository will determine an ap /api/checkedout?folderId={folderId?}&includeDescendants={includeDescendants?}&filter={filter?}&skipCount={skipCount?}&maxItems={maxItems?}&includeAllowableActions={includeAllowableActions?} user + CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/children.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/children.get.desc.xml index 01d7e4de0e..f94e72a592 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/children.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/children.get.desc.xml @@ -37,6 +37,7 @@ If no “maxItems” value is provided, then the Repository will determine an ap /api/node/{store_type}/{store_id}/{id}/children?types={types}&filter={filter?}&skipCount={skipCount?}&maxItems={maxItems?}&includeAllowableActions={includeAllowableActions?} /api/path/{store_type}/{store_id}/{id}/children?types={types}&filter={filter?}&skipCount={skipCount?}&maxItems={maxItems?}&includeAllowableActions={includeAllowableActions?} guest + argument CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/content.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/content.get.desc.xml index ae2d6837a0..281e93cacc 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/content.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/content.get.desc.xml @@ -26,6 +26,7 @@ Each CMIS protocol binding will provide a way for fetching a sub-range within a /api/node/{store_type}/{store_id}/{id}/content{property}?a={attach?} /api/path/{store_type}/{store_id}/{id}/content{property}?a={attach?} guest + argument CMIS public_api diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/descendants.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/descendants.get.desc.xml index 06cd1c561c..20dad2939f 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/descendants.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/descendants.get.desc.xml @@ -35,6 +35,7 @@ If “includeAllowableActions” is TRUE, the repository will return the allowab /api/node/{store_type}/{store_id}/{id}/descendants?types={types}&filter={filter?}&depth={depth?}&includeAllowableActions={includeAllowableActions?} /api/path/{store_type}/{store_id}/{id}/descendants?types={types}&filter={filter?}&depth={depth?}&includeAllowableActions={includeAllowableActions?} guest + argument CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/item.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/item.get.desc.xml index 598fae884e..6a2f11aaf4 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/item.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/item.get.desc.xml @@ -30,6 +30,7 @@ PropertyCollection includes changeToken (if applicable to repository)
/api/path/{store_type}/{store_id}/{id}?filter={filter?}&returnVersion={returnVersion?}&filter={filter?}&includeAllowableActions={includeAllowableActions?} guest + argument CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/parent.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/parent.get.desc.xml index d3db7639a1..be630d2193 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/parent.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/parent.get.desc.xml @@ -29,6 +29,7 @@ If “includeAllowableActions” is TRUE, the repository will return the allowab /api/node/{store_type}/{store_id}/{id}/parent?returnToRoot={returnToRoot}&filter={filter?}&includeAllowableActions={includeAllowableActions?} /api/path/{store_type}/{store_id}/{id}/parent?returnToRoot={returnToRoot}&filter={filter?}&includeAllowableActions={includeAllowableActions?} guest + argument CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/parents.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/parents.get.desc.xml index c98373e2db..df4a71e423 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/parents.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/parents.get.desc.xml @@ -27,6 +27,7 @@ If “includeAllowableActions” is TRUE, the repository will return the allowab /api/node/{store_type}/{store_id}/{id}/parents?filter={filter?}&includeAllowableActions={includeAllowableActions?} /api/path/{store_type}/{store_id}/{id}/parents?filter={filter?}&includeAllowableActions={includeAllowableActions?} guest + argument CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/pwc.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/pwc.get.desc.xml index 9b98336e51..4f3b020b9d 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/pwc.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/pwc.get.desc.xml @@ -3,6 +3,7 @@ Retrieves the properties of a private working copy /api/pwc/{store_type}/{store_id}/{id}?filter={filter?} user + argument CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.desc.xml index 9ae462ecdb..533e36b6c5 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/relationship.get.desc.xml @@ -4,6 +4,7 @@ /api/rel/{store_type}/{store_id}/{id}/type/{rel_type}/target/{target_store_type}/{target_store_id}/{target_id}?filter={filter?}&includeAllowableActions={includeAllowableActions?} guest + argument CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/type.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/type.get.desc.xml index 7408978008..e75fc801c1 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/type.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/type.get.desc.xml @@ -21,6 +21,7 @@ When includeInheritedProperties is true, the repository SHOULD return all proper /api/type/{typeId}?includeInheritedProperties={includeInheritedProperties?} user + CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/typechildren.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/typechildren.get.desc.xml index 50950eb17f..5f0c5c86dc 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/typechildren.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/typechildren.get.desc.xml @@ -3,6 +3,7 @@ Retrieve list of all child Types /api/type/{typeId}/children?includePropertyDefinitions={includePropertyDefinitions?}&skipCount={skipCount?}&maxItems={maxItems?} user + CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/typedescendants.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/typedescendants.get.desc.xml index ea10576f49..31f858d94f 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/typedescendants.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/typedescendants.get.desc.xml @@ -3,6 +3,7 @@ Retrieve list of all descendant Types /api/type/{typeId}/descendants?includePropertyDefinitions={includePropertyDefinitions?}&skipCount={skipCount?}&maxItems={maxItems?} user + CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/types.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/types.get.desc.xml index 07e0cedc7f..3e880f5a42 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/types.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/types.get.desc.xml @@ -28,6 +28,7 @@ If “returnPropertyDefinitions” is False, then the Repository will return onl /api/types?type={type?}&includePropertyDefinitions={includePropertyDefinitions?}&skipCount={skipCount?}&maxItems={maxItems?} user + CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/unfiled.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/unfiled.get.desc.xml index 327e5b9c33..8f3237a878 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/unfiled.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/unfiled.get.desc.xml @@ -3,6 +3,7 @@ Retrieve list of documents that are not in any folder /api/unfiled guest + CMIS \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/store/versions.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/store/versions.get.desc.xml index b7cd1b1d87..82e489ff47 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/store/versions.get.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/store/versions.get.desc.xml @@ -21,6 +21,7 @@ Returns all versions the user can access including checked-out version and priva /api/node/{store_type}/{store_id}/{id}/versions?filter={filter?} /api/path/{store_type}/{store_id}/{id}/versions?filter={filter?} user + argument CMIS \ No newline at end of file diff --git a/source/java/org/alfresco/repo/cmis/rest/test/BulkCreateSystemTest.java b/source/java/org/alfresco/repo/cmis/rest/test/BulkCreateSystemTest.java index dc1b1cd303..bf3a61aa4e 100644 --- a/source/java/org/alfresco/repo/cmis/rest/test/BulkCreateSystemTest.java +++ b/source/java/org/alfresco/repo/cmis/rest/test/BulkCreateSystemTest.java @@ -57,13 +57,12 @@ public class BulkCreateSystemTest abdera.getFactory().registerExtension(new CMISExtensionFactory()); AbderaClient client = new AbderaClient(abdera); - client.setMaxConnectionsTotal(1); client.usePreemptiveAuthentication(true); client.addCredentials("http://localhost:8080", null, "basic", new UsernamePasswordCredentials("admin", "admin")); String root = createFolder(client, "http://localhost:8080/alfresco/service/api/path/workspace/SpacesStore/Company%20Home/children", - "testfolder7"); + "testfolder14"); for (int i = 0; i < 100; i++) { diff --git a/source/java/org/alfresco/repo/web/scripts/RepoStore.java b/source/java/org/alfresco/repo/web/scripts/RepoStore.java index cd416d6049..64fd39f019 100644 --- a/source/java/org/alfresco/repo/web/scripts/RepoStore.java +++ b/source/java/org/alfresco/repo/web/scripts/RepoStore.java @@ -243,7 +243,7 @@ public class RepoStore implements Store, TenantDeployer "Web Script Store " + repoStore.toString() + repoPath + " must exist; it was not found"); } } - }); + }, true, false); } }, AuthenticationUtil.getSystemUserName()); @@ -364,7 +364,7 @@ public class RepoStore implements Store, TenantDeployer return documentPaths != null ? documentPaths.toArray(new String[documentPaths.size()]) : new String[0]; } - }); + }, true, false); } }, AuthenticationUtil.getSystemUserName()); } @@ -423,7 +423,7 @@ public class RepoStore implements Store, TenantDeployer } return documentPaths.toArray(new String[documentPaths.size()]); } - }); + }, true, false); } }, AuthenticationUtil.getSystemUserName()); } @@ -499,7 +499,7 @@ public class RepoStore implements Store, TenantDeployer return documentPaths.toArray(new String[documentPaths.size()]); } - }); + }, true, false); } }, AuthenticationUtil.getSystemUserName()); } @@ -521,7 +521,7 @@ public class RepoStore implements Store, TenantDeployer findNodeRef(documentPath), ContentModel.PROP_CONTENT); return reader.getLastModified(); } - }); + }, true, false); } }, AuthenticationUtil.getSystemUserName()); } @@ -542,7 +542,7 @@ public class RepoStore implements Store, TenantDeployer NodeRef nodeRef = findNodeRef(documentPath); return (nodeRef != null); } - }); + }, true, false); } }, AuthenticationUtil.getSystemUserName()); } @@ -573,7 +573,7 @@ public class RepoStore implements Store, TenantDeployer } return reader.getContentInputStream(); } - }); + }, true, false); } }, AuthenticationUtil.getSystemUserName()); } @@ -867,9 +867,9 @@ public class RepoStore implements Store, TenantDeployer } return location; } - }); + }, true, false); } - }, AuthenticationUtil.getSystemUserName()); + }, AuthenticationUtil.getSystemUserName()); } } diff --git a/source/java/org/alfresco/repo/web/scripts/RepositoryContainer.java b/source/java/org/alfresco/repo/web/scripts/RepositoryContainer.java index 3ff95da1e6..1a49ec9add 100644 --- a/source/java/org/alfresco/repo/web/scripts/RepositoryContainer.java +++ b/source/java/org/alfresco/repo/web/scripts/RepositoryContainer.java @@ -24,7 +24,12 @@ */ package org.alfresco.repo.web.scripts; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; import java.util.HashMap; import java.util.Map; @@ -32,6 +37,7 @@ import javax.servlet.http.HttpServletResponse; import javax.transaction.Status; import javax.transaction.UserTransaction; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.model.Repository; import org.alfresco.repo.security.authentication.AuthenticationUtil; @@ -40,6 +46,7 @@ import org.alfresco.repo.tenant.TenantAdminService; import org.alfresco.repo.tenant.TenantDeployer; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.TemplateService; @@ -47,15 +54,19 @@ import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.descriptor.DescriptorService; import org.alfresco.web.scripts.AbstractRuntimeContainer; import org.alfresco.web.scripts.Authenticator; +import org.alfresco.web.scripts.Cache; import org.alfresco.web.scripts.Description; import org.alfresco.web.scripts.Registry; +import org.alfresco.web.scripts.Runtime; import org.alfresco.web.scripts.ServerModel; import org.alfresco.web.scripts.WebScript; import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptResponse; +import org.alfresco.web.scripts.WrappingWebScriptResponse; import org.alfresco.web.scripts.Description.RequiredAuthentication; import org.alfresco.web.scripts.Description.RequiredTransaction; +import org.alfresco.web.scripts.Description.TransactionCapability; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.ObjectFactory; @@ -71,6 +82,9 @@ public class RepositoryContainer extends AbstractRuntimeContainer implements Ten // Logger protected static final Log logger = LogFactory.getLog(RepositoryContainer.class); + // Transaction key for buffered response + private static String BUFFERED_RESPONSE_KEY = RepositoryContainer.class.getName() + ".bufferedresponse"; + /** Component Dependencies */ private Repository repository; private RepositoryImageResolver imageResolver; @@ -92,7 +106,8 @@ public class RepositoryContainer extends AbstractRuntimeContainer implements Ten /** * @param registryFactory */ - public void setRegistryFactory(ObjectFactory registryFactory) { + public void setRegistryFactory(ObjectFactory registryFactory) + { this.registryFactory = registryFactory; } @@ -314,9 +329,22 @@ public class RepositoryContainer extends AbstractRuntimeContainer implements Ten try { if (logger.isDebugEnabled()) - logger.debug("Begin retry transaction block: " + description.getRequiredTransaction()); + logger.debug("Begin retry transaction block: " + description.getRequiredTransaction() + "," + description.getTransactionCapability()); + + WebScriptResponse redirectedRes = scriptRes; + if (description.getTransactionCapability() == TransactionCapability.readwrite) + { + if (logger.isDebugEnabled()) + logger.debug("Creating Transactional Response for ReadWrite transaction"); + + // create buffered response that's sensitive transaction boundary + BufferedResponse bufferedRes = new BufferedResponse(scriptRes); + AlfrescoTransactionSupport.bindResource(BUFFERED_RESPONSE_KEY, bufferedRes); + AlfrescoTransactionSupport.bindListener(bufferedRes); + redirectedRes = bufferedRes; + } - script.execute(scriptReq, scriptRes); + script.execute(scriptReq, redirectedRes); } catch(Exception e) { @@ -356,21 +384,16 @@ public class RepositoryContainer extends AbstractRuntimeContainer implements Ten finally { if (logger.isDebugEnabled()) - logger.debug("End retry transaction block: " + description.getRequiredTransaction()); + logger.debug("End retry transaction block: " + description.getRequiredTransaction() + "," + description.getTransactionCapability()); } return null; } }; - if (description.getRequiredTransaction() == RequiredTransaction.required) - { - retryingTransactionHelper.doInTransaction(work); - } - else - { - retryingTransactionHelper.doInTransaction(work, false, true); - } + boolean readonly = description.getTransactionCapability() == TransactionCapability.readonly; + boolean requiresNew = description.getRequiredTransaction() == RequiredTransaction.requiresnew; + retryingTransactionHelper.doInTransaction(work, readonly, requiresNew); } } @@ -465,4 +488,213 @@ public class RepositoryContainer extends AbstractRuntimeContainer implements Ten { webScriptsRegistryCache.remove(tenantAdminService.getCurrentUserDomain()); } + + + /** + * Transactional Buffered Response + */ + private static class BufferedResponse implements TransactionListener, WrappingWebScriptResponse + { + private WebScriptResponse res; + private ByteArrayOutputStream outputStream; + private OutputStreamWriter outputWriter; + + /** + * Construct + * + * @param res + */ + public BufferedResponse(WebScriptResponse res) + { + this.res = res; + this.outputStream = new ByteArrayOutputStream(4096); + try + { + this.outputWriter = new OutputStreamWriter(outputStream, "UTF-8"); + } + catch (UnsupportedEncodingException e) + { + throw new AlfrescoRuntimeException("Failed to create buffered response", e); + }; + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WrappingWebScriptResponse#getNext() + */ + public WebScriptResponse getNext() + { + return res; + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#addHeader(java.lang.String, java.lang.String) + */ + public void addHeader(String name, String value) + { + res.addHeader(name, value); + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#encodeScriptUrl(java.lang.String) + */ + public String encodeScriptUrl(String url) + { + return res.encodeScriptUrl(url); + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#getEncodeScriptUrlFunction(java.lang.String) + */ + public String getEncodeScriptUrlFunction(String name) + { + return res.getEncodeScriptUrlFunction(name); + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#getOutputStream() + */ + public OutputStream getOutputStream() throws IOException + { + return outputStream; + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#getRuntime() + */ + public Runtime getRuntime() + { + return res.getRuntime(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#getWriter() + */ + public Writer getWriter() throws IOException + { + return outputWriter; + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#reset() + */ + public void reset() + { + outputStream.reset(); + res.reset(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#setCache(org.alfresco.web.scripts.Cache) + */ + public void setCache(Cache cache) + { + res.setCache(cache); + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#setContentType(java.lang.String) + */ + public void setContentType(String contentType) + { + res.setContentType(contentType); + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#setContentEncoding(java.lang.String) + */ + public void setContentEncoding(String contentEncoding) + { + res.setContentEncoding(contentEncoding); + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#setHeader(java.lang.String, java.lang.String) + */ + public void setHeader(String name, String value) + { + res.setHeader(name, value); + } + + /* + * (non-Javadoc) + * @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int) + */ + public void setStatus(int status) + { + res.setStatus(status); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.transaction.TransactionListener#afterCommit() + */ + public void afterCommit() + { + writeResponse(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.transaction.TransactionListener#afterRollback() + */ + public void afterRollback() + { + writeResponse(); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.transaction.TransactionListener#beforeCommit(boolean) + */ + public void beforeCommit(boolean readOnly) + { + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.transaction.TransactionListener#beforeCompletion() + */ + public void beforeCompletion() + { + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.transaction.TransactionListener#flush() + */ + public void flush() + { + } + + /** + * Write buffered response to underlying response + */ + private void writeResponse() + { + try + { + if (logger.isDebugEnabled()) + logger.debug("Writing Transactional response: size=" + outputStream.size()); + + outputStream.flush(); + outputStream.writeTo(res.getOutputStream()); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Failed to commit buffered response", e); + } + } + } } diff --git a/source/java/org/alfresco/repo/web/scripts/bean/AVMRemoteStore.java b/source/java/org/alfresco/repo/web/scripts/bean/AVMRemoteStore.java index b75f89340e..4abe864aa5 100644 --- a/source/java/org/alfresco/repo/web/scripts/bean/AVMRemoteStore.java +++ b/source/java/org/alfresco/repo/web/scripts/bean/AVMRemoteStore.java @@ -46,7 +46,6 @@ import org.alfresco.service.cmr.search.SearchService; import org.alfresco.web.scripts.Status; import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptResponse; -import org.alfresco.web.scripts.servlet.WebScriptServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -167,11 +166,10 @@ public class AVMRemoteStore extends BaseRemoteStore } // set mimetype for the content and the character encoding + length for the stream - WebScriptServletResponse httpRes = (WebScriptServletResponse)res; - httpRes.setContentType(mimetype); - httpRes.getHttpServletResponse().setCharacterEncoding(reader.getEncoding()); - httpRes.getHttpServletResponse().setDateHeader("Last-Modified", desc.getModDate()); - httpRes.setHeader("Content-Length", Long.toString(reader.getSize())); + res.setContentType(mimetype); + res.setContentEncoding(reader.getEncoding()); + res.setHeader("Last-Modified", Long.toString(desc.getModDate())); + res.setHeader("Content-Length", Long.toString(reader.getSize())); if (logger.isDebugEnabled()) logger.debug("AVMRemoteStore.getDocument() " + mimetype + " of size: " + reader.getSize()); diff --git a/source/java/org/alfresco/repo/web/scripts/bean/SearchProxy.java b/source/java/org/alfresco/repo/web/scripts/bean/SearchProxy.java index bf56f81657..1b6976c6f5 100644 --- a/source/java/org/alfresco/repo/web/scripts/bean/SearchProxy.java +++ b/source/java/org/alfresco/repo/web/scripts/bean/SearchProxy.java @@ -47,7 +47,7 @@ import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.servlet.HTTPProxy; -import org.alfresco.web.scripts.servlet.WebScriptServletResponse; +import org.alfresco.web.scripts.servlet.WebScriptServletRuntime; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Attribute; @@ -166,11 +166,12 @@ public class SearchProxy extends AbstractWebScript implements InitializingBean // issue request against search engine // NOTE: This web script must be executed in a HTTP servlet environment - if (!(res instanceof WebScriptServletResponse)) + if (!(res.getRuntime() instanceof WebScriptServletRuntime)) { throw new WebScriptException("Search Proxy must be executed in HTTP Servlet environment"); } - HttpServletResponse servletRes = ((WebScriptServletResponse)res).getHttpServletResponse(); + + HttpServletResponse servletRes = WebScriptServletRuntime.getHttpServletResponse(res); SearchEngineHttpProxy proxy = new SearchEngineHttpProxy(req.getServicePath() + "/" + req.getContextPath(), engine, engineUrl, servletRes); proxy.service(); } diff --git a/source/java/org/alfresco/repo/web/scripts/content/StreamContent.java b/source/java/org/alfresco/repo/web/scripts/content/StreamContent.java index 03a1643108..eeb136424b 100644 --- a/source/java/org/alfresco/repo/web/scripts/content/StreamContent.java +++ b/source/java/org/alfresco/repo/web/scripts/content/StreamContent.java @@ -35,7 +35,6 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.alfresco.model.ContentModel; @@ -58,8 +57,6 @@ import org.alfresco.web.scripts.WebScriptException; import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptStatus; -import org.alfresco.web.scripts.servlet.WebScriptServletRequest; -import org.alfresco.web.scripts.servlet.WebScriptServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.util.FileCopyUtils; @@ -298,14 +295,6 @@ public class StreamContent extends AbstractWebScript protected void streamContent(WebScriptRequest req, WebScriptResponse res, NodeRef nodeRef, QName propertyQName, boolean attach) throws IOException { - // NOTE: This web script must be executed in a HTTP Servlet environment - if (!(req instanceof WebScriptServletRequest)) - { - throw new WebScriptException("Content retrieval must be executed in HTTP Servlet environment"); - } - HttpServletRequest httpReq = ((WebScriptServletRequest)req).getHttpServletRequest(); - HttpServletResponse httpRes = ((WebScriptServletResponse)res).getHttpServletResponse(); - if (logger.isDebugEnabled()) logger.debug("Retrieving content from node ref " + nodeRef.toString() + " (property: " + propertyQName.toString() + ") (attach: " + attach + ")"); @@ -317,14 +306,19 @@ public class StreamContent extends AbstractWebScript // check If-Modified-Since header and set Last-Modified header as appropriate Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED); - long modifiedSince = httpReq.getDateHeader("If-Modified-Since"); + String modifiedSinceStr = req.getHeader("If-Modified-Since"); + if (modifiedSinceStr == null) + { + modifiedSinceStr = "-1"; + } + long modifiedSince = new Long(modifiedSinceStr); if (modifiedSince > 0L) { // round the date to the ignore millisecond value which is not supplied by header long modDate = (modified.getTime() / 1000L) * 1000L; if (modDate <= modifiedSince) { - httpRes.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + res.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return; } } @@ -337,7 +331,7 @@ public class StreamContent extends AbstractWebScript } // Stream the cotent - streamContentImpl(req, res, reader, attach, modified, String.valueOf(modifiedSince)); + streamContentImpl(req, res, reader, attach, modified, String.valueOf(modifiedSince)); } /** @@ -398,15 +392,12 @@ public class StreamContent extends AbstractWebScript protected void streamContentImpl(WebScriptRequest req, WebScriptResponse res, ContentReader reader, boolean attach, Date modified, String eTag) throws IOException { - HttpServletRequest httpReq = ((WebScriptServletRequest)req).getHttpServletRequest(); - HttpServletResponse httpRes = ((WebScriptServletResponse)res).getHttpServletResponse(); - // handle attachment if (attach == true) { // set header based on filename - will force a Save As from the browse if it doesn't recognize it // this is better than the default response of the browser trying to display the contents - httpRes.setHeader("Content-Disposition", "attachment"); + res.setHeader("Content-Disposition", "attachment"); } // establish mimetype @@ -428,9 +419,9 @@ public class StreamContent extends AbstractWebScript } // set mimetype for the content and the character encoding + length for the stream - httpRes.setContentType(mimetype); - httpRes.setCharacterEncoding(reader.getEncoding()); - httpRes.setHeader("Content-Length", Long.toString(reader.getSize())); + res.setContentType(mimetype); + res.setContentEncoding(reader.getEncoding()); + res.setHeader("Content-Length", Long.toString(reader.getSize())); // set caching Cache cache = new Cache();