From f9b7237eab8c5cc72248e2aa12c092c61cae541c Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Thu, 13 May 2010 09:32:35 +0000 Subject: [PATCH] ALF-2703: Incorrect folder sctructure is created in Google Docs If the transaction within which folders or documents are created within Google Docs fails then the created items will be rolledback as if part of the Alfresco transaction. This prevents the behaviour reported in this issue. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@20216 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../googledocs/default/googledocs-context.xml | 3 +- .../googledocs/GoogleDocsServiceImpl.java | 122 ++++++++++++++++-- .../googledocs/GoogleDocumentServiceTest.java | 35 ++++- 3 files changed, 146 insertions(+), 14 deletions(-) diff --git a/config/alfresco/subsystems/googledocs/default/googledocs-context.xml b/config/alfresco/subsystems/googledocs/default/googledocs-context.xml index 589a57d690..903156e5bc 100755 --- a/config/alfresco/subsystems/googledocs/default/googledocs-context.xml +++ b/config/alfresco/subsystems/googledocs/default/googledocs-context.xml @@ -33,7 +33,8 @@ - + + diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java b/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java index 9bb49f9525..a882885599 100755 --- a/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocsServiceImpl.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,6 +32,9 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.TransactionListenerAdapter; +import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; @@ -60,7 +64,7 @@ import com.google.gdata.data.acl.AclScope; import com.google.gdata.data.docs.DocumentEntry; import com.google.gdata.data.docs.DocumentListEntry; import com.google.gdata.data.docs.FolderEntry; -import com.google.gdata.data.docs.PresentationEntry; +import com.google.gdata.data.docs.SpreadsheetEntry; import com.google.gdata.data.media.MediaSource; import com.google.gdata.data.media.MediaStreamSource; import com.google.gdata.util.AuthenticationException; @@ -70,9 +74,10 @@ import com.google.gdata.util.ServiceException; /** * Google docs integration service implementation */ -public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel +public class GoogleDocsServiceImpl extends TransactionListenerAdapter + implements GoogleDocsService, GoogleDocsModel { - @SuppressWarnings("unused") + /** Log */ private static Log logger = LogFactory.getLog(GoogleDocsServiceImpl.class); /** Google document types */ @@ -91,7 +96,8 @@ public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel private PermissionService permissionService; private OwnableService ownableService; private AuthorityService authorityService; - + private DictionaryService dictionaryService; + /** GoogleDoc base feed url */ private String url = "http://docs.google.com/feeds/default/private/full"; @@ -175,6 +181,14 @@ public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel this.authorityService = authorityService; } + /** + * @param dictionaryService dictionary service + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + /** * @param url root googleDoc URL */ @@ -448,7 +462,7 @@ public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel * @param nodeRef node reference * @return DocumentList Entry folder resource */ - private DocumentListEntry getParentFolder(NodeRef nodeRef) + private DocumentListEntry getParentFolder(final NodeRef nodeRef) { DocumentListEntry folder = null; @@ -460,14 +474,32 @@ public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel String resourceType = (String)nodeService.getProperty(parentNodeRef, PROP_RESOURCE_TYPE); String resourceId = (String)nodeService.getProperty(parentNodeRef, PROP_RESOURCE_ID); folder = getDocumentListEntry(resourceType + ":" + resourceId); + + if (logger.isDebugEnabled() == true) + { + logger.debug("Found existing google folder + " + resourceId); + } } else { + // Get the parent folder DocumentListEntry parentFolder = getParentFolder(parentNodeRef); - String name = (String)nodeService.getProperty(parentNodeRef, ContentModel.PROP_NAME); - folder = createGoogleFolder(name, parentFolder); - - setResourceDetails(parentNodeRef, folder); + + // Determine the name of the new google folder + String name = null; + QName parentNodeType = nodeService.getType(parentNodeRef); + if (dictionaryService.isSubClass(parentNodeType, ContentModel.TYPE_STOREROOT) == true) + { + name = parentNodeRef.getStoreRef().getIdentifier(); + } + else + { + name = (String)nodeService.getProperty(parentNodeRef, ContentModel.PROP_NAME); + } + + // Create the folder and set the meta data in Alfresco + folder = createGoogleFolder(name, parentFolder); + setResourceDetails(parentNodeRef, folder); } } @@ -694,13 +726,14 @@ public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel DocumentListEntry docEntry = null; if (MimetypeMap.MIMETYPE_EXCEL.equals(mimetype) == true) { - docEntry = new PresentationEntry(); + docEntry = new SpreadsheetEntry(); } else { docEntry = new DocumentEntry(); } + // Set the content and the title of the document docEntry.setContent(mediaContent); docEntry.setTitle(new PlainTextConstruct(name)); @@ -708,6 +741,9 @@ public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel document = googleDocumentService.insert( new URL(parentFolderUrl), docEntry); + + // Mark create entry + markCreated(document.getResourceId()); } catch (IOException e) { @@ -816,6 +852,9 @@ public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel folderEntry = googleDocumentService.insert( new URL(parentFolderUrl), folder); + + // Mark create entry + markCreated(folderEntry.getResourceId()); } catch (IOException e) { @@ -938,7 +977,70 @@ public class GoogleDocsServiceImpl implements GoogleDocsService, GoogleDocsModel { throw new AlfrescoRuntimeException("Unable to set premissions on google document", e); } + } + + private final static String KEY_MARKED_RESOURCES = "GoogleDocService.marked_resources"; + + @SuppressWarnings("unchecked") + private void markCreated(String resourceId) + { + List resources = (List)AlfrescoTransactionSupport.getResource(KEY_MARKED_RESOURCES); + if (resources == null) + { + // bind pending rules to the current transaction + resources = new ArrayList(); + AlfrescoTransactionSupport.bindResource(KEY_MARKED_RESOURCES, resources); + // bind the rule transaction listener + AlfrescoTransactionSupport.bindListener(this); + } + + if (resources.contains(resourceId) == false) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Marking created resource " + resourceId); + } + + resources.add(resourceId); + } } + @Override + public void afterCommit() + { + // TODO go ahead and delete any resources that have be queued up for deletion .... + } + @SuppressWarnings("unchecked") + @Override + public void afterRollback() + { + List resources = (List)AlfrescoTransactionSupport.getResource(KEY_MARKED_RESOURCES); + if (resources != null) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Transaction rolled back, manually deleting created Google Resources"); + } + + for (String resourceId : resources) + { + if (logger.isDebugEnabled() == true) + { + logger.debug("Deleting created resource " + resourceId); + } + + // Delete resource + try + { + DocumentListEntry entry = getDocumentListEntry(resourceId); + googleDocumentService.delete(new URL(entry.getEditLink().getHref() + "?delete=true"), entry.getEtag()); + } + catch (Throwable e) + { + // Ignore + } + } + } + } } diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java b/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java index d86a86f970..2e59a7c567 100755 --- a/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java +++ b/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceTest.java @@ -23,6 +23,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; +import java.net.URL; import javax.transaction.UserTransaction; @@ -51,6 +52,10 @@ import org.alfresco.util.PropertyMap; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; +import com.google.gdata.client.DocumentQuery; +import com.google.gdata.client.docs.DocsService; +import com.google.gdata.data.docs.DocumentListEntry; +import com.google.gdata.data.docs.DocumentListFeed; import com.google.gdata.util.ServiceException; public class GoogleDocumentServiceTest extends TestCase implements GoogleDocsModel @@ -147,7 +152,7 @@ public class GoogleDocumentServiceTest extends TestCase implements GoogleDocsMod // Create test documents nodeRefDoc = createTestDocument("mydoc.docx", "alfresco/subsystems/googledocs/default/test.docx", MimetypeMap.MIMETYPE_WORD); //nodeRefSpread = createTestDocument("mydoc.xls", "alfresco/subsystems/googledocs/default/testBook.xls", MimetypeMap.MIMETYPE_EXCEL); - nodeRefSpread = createTestDocument("mydoc2.docx", "alfresco/subsystems/googledocs/default/test.docx", MimetypeMap.MIMETYPE_WORD); + nodeRefSpread = createTestDocument("mydoc2.xlsx", "alfresco/subsystems/googledocs/default/test.xlsx", MimetypeMap.MIMETYPE_EXCEL); // Create an empty content node (simulate creation of a new google doc in UI) nodeRef2 = fileFolderService.create(folder, "mygoogledoc.doc", ContentModel.TYPE_CONTENT).getNodeRef(); @@ -225,12 +230,24 @@ public class GoogleDocumentServiceTest extends TestCase implements GoogleDocsMod assertNotNull(nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_ID)); assertNotNull(nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_TYPE)); + System.out.println("For node ref " + nodeRefDoc.toString()); System.out.println("Google doc URL: " + nodeService.getProperty(nodeRefDoc, PROP_URL)); System.out.println("Google doc type: " + nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_TYPE)); System.out.println("Google doc id: " + nodeService.getProperty(nodeRefDoc, PROP_RESOURCE_ID)); String downloadFile = downloadFile(googleDocsService.getGoogleDocContent(nodeRefDoc), ".doc"); System.out.println("Download file: " + downloadFile); + DocsService client = new DocsService("Alfresco"); + client.setUserCredentials("rwetherall@alfresco.com", "123test123"); + + URL feedUri = new URL("https://docs.google.com/feeds/default/private/full/"); + DocumentQuery query = new DocumentQuery(feedUri); + query.setTitleExact(true); + query.setTitleQuery((String)nodeService.getProperty(nodeRefDoc, ContentModel.PROP_NAME)); + DocumentListFeed feed = client.getFeed(query, DocumentListFeed.class); + printDocuments(feed); + + googleDocsService.createGoogleDoc(nodeRefSpread, GoogleDocsPermissionContext.SHARE_WRITE); assertTrue(nodeService.hasAspect(nodeRefSpread, ASPECT_GOOGLERESOURCE)); @@ -241,12 +258,24 @@ public class GoogleDocumentServiceTest extends TestCase implements GoogleDocsMod System.out.println("Google doc URL: " + nodeService.getProperty(nodeRefSpread, PROP_URL)); System.out.println("Google doc type: " + nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_TYPE)); System.out.println("Google doc id: " + nodeService.getProperty(nodeRefSpread, PROP_RESOURCE_ID)); -// // downloadFile = downloadFile(googleDocsService.download(nodeRefSpread), ".xls"); -// // System.out.println("Download file: " + downloadFile); + downloadFile = downloadFile(googleDocsService.getGoogleDocContent(nodeRefSpread), ".xls"); + System.out.println("Download file: " + downloadFile); + + } } + public void printDocuments(DocumentListFeed feed) + { + for (DocumentListEntry entry : feed.getEntries() ) + { + String resourceId = entry.getResourceId(); + System.out.println(" -- Document(" + resourceId + "/" + entry.getTitle().getPlainText() + ") "); + + } + } + public void testCheckOutCheckIn() throws Exception { if (isGoogleServiceAvailable() == true)