diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.desc.xml
index 027bf9d26f..0c032e187f 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/discussions/forum/forum-posts.get.desc.xml
@@ -6,5 +6,5 @@
/api/forum/node/{store_type}/{store_id}/{id}/posts
argument
user
- required
+ required
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/links/link/link.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/links/link/link.get.desc.xml
index 0f42bc72ad..dec0aea1dd 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/links/link/link.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/links/link/link.get.desc.xml
@@ -4,5 +4,5 @@
/api/links/link/site/{site}/{container}/{path}
argument
user
- required
+ required
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/links/links.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/links/links.get.desc.xml
index 15642727b6..b1decc658f 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/links/links.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/links/links.get.desc.xml
@@ -4,5 +4,5 @@
/api/links/site/{site}/{container}
argument
user
- required
+ required
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/tagging/tagscope-tags.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/tagging/tagscope-tags.get.desc.xml
index ca7c533790..6ecf6ca8b6 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/repository/tagging/tagscope-tags.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/tagging/tagscope-tags.get.desc.xml
@@ -8,7 +8,7 @@
/api/tagscopes/site/{site}/{container}/tags
argument
user
- required
+ required
draft_public_api
Tagging
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.desc.xml
index 3d8399223f..0d47c3abb8 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary-v2/node.get.desc.xml
@@ -4,6 +4,6 @@
/slingshot/doclib2/node/{store_type}/{store_id}/{id}
argument
user
- required
+ required
internal
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.desc.xml
index 5f2f33d213..2bd6b47e79 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/categorynode.get.desc.xml
@@ -5,6 +5,6 @@
/slingshot/doclib/categorynode/node/{store_type}/{store_id}/{id}
argument
user
- required
+ required
internal
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.desc.xml
index 9277ab04a4..5cc5522d42 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/location.get.desc.xml
@@ -4,6 +4,6 @@
/slingshot/doclib/node/{store_type}/{store_id}/{id}/location
argument
user
- required
+ required
internal
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.desc.xml
index e9ed5635d7..9675060892 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/node.get.desc.xml
@@ -4,6 +4,6 @@
/slingshot/doclib/node/{store_type}/{store_id}/{id}
argument
user
- required
+ required
internal
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.desc.xml
index dfc2b28ab2..e76c291155 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/page.get.desc.xml
@@ -4,6 +4,6 @@
/slingshot/wiki/page/{siteId}/{pageTitle}
argument
user
- required
+ required
internal
diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.desc.xml
index a50c5a040a..61b5f6e48d 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/pagelist.get.desc.xml
@@ -4,6 +4,6 @@
/slingshot/wiki/pages/{siteId}
argument
user
- required
+ required
internal
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.desc.xml
index 4b606f0226..5ffbf6f12a 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/slingshot/wiki/version.get.desc.xml
@@ -4,6 +4,6 @@
/slingshot/wiki/version/{siteId}/{pageTitle}/{versionId}
argument
user
- required
+ required
internal
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetRestApiTest.java b/source/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetRestApiTest.java
new file mode 100644
index 0000000000..643f1e25e1
--- /dev/null
+++ b/source/java/org/alfresco/repo/web/scripts/ReadOnlyTransactionInGetRestApiTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2005-2011 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.web.scripts;
+
+import java.text.MessageFormat;
+import java.util.List;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.site.SiteInfo;
+import org.alfresco.service.cmr.site.SiteService;
+import org.alfresco.service.cmr.site.SiteVisibility;
+import org.alfresco.service.transaction.TransactionService;
+import org.springframework.context.ApplicationContext;
+import org.springframework.extensions.webscripts.Status;
+import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest;
+import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
+
+/**
+ * Set of tests that ensure GET REST APIs are run successfully in a read-only
+ * transaction (ALF-10179).
+ *
+ * Some webscripts have a side effect of creating a "container" these tests
+ * are to ensure this is handled gracefully in a way that allows the main
+ * transaction to be declared as readonly for performance reasons.
+ *
+ * @author Gavin Cornwell
+ * @since 4.0
+ */
+public class ReadOnlyTransactionInGetRestApiTest extends BaseWebScriptTest
+{
+ private static final String TEST_SITE_NAME = "readOnlyTestSite";
+
+ private static final String URL_GET_SITE_BLOG = "/api/blog/site/" + TEST_SITE_NAME + "/blog";
+ private static final String URL_GET_SITE_FORUM_POSTS = "/api/forum/site/" + TEST_SITE_NAME + "/discussions/posts";
+ private static final String URL_GET_SITE_LINKS = "/api/links/site/" + TEST_SITE_NAME + "/links?page=1&pageSize=10";
+ private static final String URL_GET_SITE_LINK = "/api/links/link/site/" + TEST_SITE_NAME + "/links/123456789";
+ private static final String URL_GET_SITE_TAGS = "/api/tagscopes/site/" + TEST_SITE_NAME + "/tags";
+ private static final String URL_GET_SITE_DATALISTS = "/slingshot/datalists/lists/site/" + TEST_SITE_NAME + "/dataLists";
+ private static final String URL_GET_SITE_WIKI = "/slingshot/wiki/pages/" + TEST_SITE_NAME;
+ private static final String URL_GET_SITE_WIKI_PAGE = "/slingshot/wiki/page/" + TEST_SITE_NAME + "/AWikiPage";
+ private static final String URL_GET_SITE_WIKI_PAGE_VERSION = "/slingshot/wiki/version/" + TEST_SITE_NAME + "/AWikiPage/123456789";
+ private static final String URL_GET_DOCLIB_CATEGORYNODE = "/slingshot/doclib/categorynode/node/{0}";
+ private static final String URL_GET_DOCLIB_TREENODE = "/slingshot/doclib/treenode/site/" + TEST_SITE_NAME + "/documentLibrary";
+ private static final String URL_GET_DOCLIB_NODE = "/slingshot/doclib/node/{0}";
+ private static final String URL_GET_DOCLIB2_NODE = "/slingshot/doclib2/node/{0}";
+ private static final String URL_GET_DOCLIB_LOCATION = "/slingshot/doclib/node/{0}/location";
+ private static final String URL_GET_DOCLIB_DOCLIST = "/slingshot/doclib/doclist/documents/site/" + TEST_SITE_NAME + "/documentLibrary";
+ private static final String URL_GET_DOCLIB2_DOCLIST = "/slingshot/doclib2/doclist/documents/site/" + TEST_SITE_NAME + "/documentLibrary";
+ private static final String URL_GET_DOCLIB_IMAGES = "/slingshot/doclib/images/site/" + TEST_SITE_NAME + "/documentLibrary";
+
+ private SiteService siteService;
+ private NodeService nodeService;
+ private TransactionService transactionService;
+
+ private NodeRef testSiteNodeRef;
+ private String testSiteNodeRefString;
+
+ private boolean logEnabled = false;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ ApplicationContext appContext = getServer().getApplicationContext();
+
+ this.siteService = (SiteService)appContext.getBean("SiteService");
+ this.nodeService = (NodeService)appContext.getBean("NodeService");
+ this.transactionService = (TransactionService)appContext.getBean("TransactionService");
+
+ // set admin as current user
+ AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
+
+ // delete the test site if it's still hanging around from previous runs
+ if (siteService.getSite(TEST_SITE_NAME) != null)
+ {
+ siteService.deleteSite(TEST_SITE_NAME);
+ }
+
+ // create the test site, this should create a site but it won't have any containers created
+ SiteInfo siteInfo = this.siteService.createSite("collaboration", TEST_SITE_NAME, "Read Only Test Site",
+ "Test site for ReadOnlyTransactionRestApiTest", SiteVisibility.PUBLIC);
+ this.testSiteNodeRef = siteInfo.getNodeRef();
+ this.testSiteNodeRefString = this.testSiteNodeRef.toString().replace("://", "/");
+
+ // ensure there are no containers present at the start of the test
+ List children = nodeService.getChildAssocs(this.testSiteNodeRef);
+ assertTrue("The test site should not have any containers", children.isEmpty());
+ }
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ // use retrying transaction to delete the site
+ this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback()
+ {
+ @Override
+ public Void execute() throws Throwable
+ {
+ // delete the test site
+ siteService.deleteSite(TEST_SITE_NAME);
+
+ return null;
+ }
+ });
+
+ AuthenticationUtil.clearCurrentSecurityContext();
+ }
+
+ public void testGetSiteBlog() throws Exception
+ {
+ // TODO: Fixme - This REST API still requires a readwrite transaction to be successful
+ // Also add tests for all other blog GET REST APIs
+
+ Response response = sendRequest(new GetRequest(URL_GET_SITE_BLOG), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetSiteForumPosts() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(URL_GET_SITE_FORUM_POSTS), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetSiteLinks() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(URL_GET_SITE_LINKS), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetSiteLink() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(URL_GET_SITE_LINK), 404);
+ logResponse(response);
+ assertEquals(Status.STATUS_NOT_FOUND, response.getStatus());
+ }
+
+ public void testGetSiteTags() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(URL_GET_SITE_TAGS), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetSiteDataLists() throws Exception
+ {
+ // TODO: Fixme - This REST API still requires a readwrite transaction to be successful
+
+ Response response = sendRequest(new GetRequest(URL_GET_SITE_DATALISTS), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetSiteWiki() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(URL_GET_SITE_WIKI), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetSiteWikiPage() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(URL_GET_SITE_WIKI_PAGE), 404);
+ logResponse(response);
+ assertEquals(Status.STATUS_NOT_FOUND, response.getStatus());
+ }
+
+ public void testGetSiteWikiPageVersion() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(URL_GET_SITE_WIKI_PAGE_VERSION), 404);
+ logResponse(response);
+ assertEquals(Status.STATUS_NOT_FOUND, response.getStatus());
+ }
+
+ public void testGetDoclibTreeNode() throws Exception
+ {
+ // TODO: Fixme - This REST API still requires a readwrite transaction to be successful
+
+ Response response = sendRequest(new GetRequest(URL_GET_DOCLIB_TREENODE), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetDoclibDoclist() throws Exception
+ {
+ // TODO: Fixme - This REST API still requires a readwrite transaction to be successful
+
+ Response response = sendRequest(new GetRequest(URL_GET_DOCLIB_DOCLIST), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetDoclib2Doclist() throws Exception
+ {
+ // TODO: Fixme - This REST API still requires a readwrite transaction to be successful
+
+ Response response = sendRequest(new GetRequest(URL_GET_DOCLIB2_DOCLIST), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetDoclibImages() throws Exception
+ {
+ // TODO: Fixme - This REST API still requires a readwrite transaction to be successful
+
+ Response response = sendRequest(new GetRequest(URL_GET_DOCLIB_IMAGES), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetDoclibCategoryNode() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(MessageFormat.format(URL_GET_DOCLIB_CATEGORYNODE,
+ testSiteNodeRefString)), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetDoclibNode() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(MessageFormat.format(URL_GET_DOCLIB_NODE,
+ testSiteNodeRefString)), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetDoclib2Node() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(MessageFormat.format(URL_GET_DOCLIB2_NODE,
+ testSiteNodeRefString)), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ public void testGetDoclibNodeLocation() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(MessageFormat.format(URL_GET_DOCLIB_LOCATION,
+ testSiteNodeRefString)), 200);
+ logResponse(response);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ }
+
+ private void logResponse(Response response)
+ {
+ if (this.logEnabled)
+ {
+ try
+ {
+ System.out.println(response.getContentAsString());
+ }
+ catch (Exception e)
+ {
+ System.err.println("Unable to log response: " + e.toString());
+ }
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/web/scripts/RepositoryContainer.java b/source/java/org/alfresco/repo/web/scripts/RepositoryContainer.java
index ee82558733..2d09b3e147 100644
--- a/source/java/org/alfresco/repo/web/scripts/RepositoryContainer.java
+++ b/source/java/org/alfresco/repo/web/scripts/RepositoryContainer.java
@@ -448,6 +448,15 @@ public class RepositoryContainer extends AbstractRuntimeContainer implements Ten
boolean readonly = description.getRequiredTransactionParameters().getCapability() == TransactionCapability.readonly;
boolean requiresNew = description.getRequiredTransaction() == RequiredTransaction.requiresnew;
+
+ // log a warning if we detect a GET webscript being run in a readwrite transaction, GET calls should
+ // NOT have any side effects so this scenario as a warning sign something maybe amiss, see ALF-10179.
+ if (logger.isWarnEnabled() && !readonly && "GET".equalsIgnoreCase(description.getMethod()))
+ {
+ logger.warn("Webscript with URL '" + scriptReq.getURL() +
+ "' is a GET request but it's descriptor has declared a readwrite transaction is required");
+ }
+
try
{
retryingTransactionHelper.doInTransaction(work, readonly, requiresNew);