Google Doc Integration: Fix system unit test failures

* Fixed spreadsheet download
  * Refactored use of DocService to improve reliability
  * Reuse security tokens 
  * Consolidate code as a result of above changes



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@30832 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2011-09-29 00:15:38 +00:00
parent acb0bbf7ee
commit 79508da9fc
6 changed files with 152 additions and 158 deletions

View File

@@ -9,24 +9,7 @@
</property> </property>
</bean> </bean>
<bean id="googleDocsServiceDelegate" class="com.google.gdata.client.docs.DocsService">
<constructor-arg>
<value>${googledocs.application.name}</value>
</constructor-arg>
</bean>
<bean id="spreadsheetsServiceDelegate" class="com.google.gdata.client.GoogleService">
<constructor-arg index="0">
<value>${googledocs.spreadsheet.service.name}</value>
</constructor-arg>
<constructor-arg index="1">
<value>${googledocs.application.name}</value>
</constructor-arg>
</bean>
<bean id="googleDocsService" class="org.alfresco.repo.googledocs.GoogleDocsServiceImpl"> <bean id="googleDocsService" class="org.alfresco.repo.googledocs.GoogleDocsServiceImpl">
<property name="googleDocumentService" ref="googleDocsServiceDelegate"/>
<property name="spreadsheetsService" ref="spreadsheetsServiceDelegate"/>
<property name="nodeService" ref="NodeService"/> <property name="nodeService" ref="NodeService"/>
<property name="contentService" ref="ContentService"/> <property name="contentService" ref="ContentService"/>
<property name="personService" ref="PersonService"/> <property name="personService" ref="PersonService"/>
@@ -37,7 +20,10 @@
<property name="dictionaryService" ref="DictionaryService"/> <property name="dictionaryService" ref="DictionaryService"/>
<property name="enabled" value="${googledocs.googleeditable.enabled}"/> <property name="enabled" value="${googledocs.googleeditable.enabled}"/>
<property name="url" value="${googledocs.url}"/> <property name="url" value="${googledocs.url}"/>
<property name="downloadUrl" value="${googledocs.downloadurl}"/> <property name="downloadUrl" value="${googledocs.downloadurl}"/>
<property name="spreadsheetDownloadUrl" value="${googledocs.spreadsheet.downloadurl}"/>
<property name="applicationName" value="${googledocs.application.name}"/>
<property name="spreadSheetServiceName" value="${googledocs.spreadsheet.service.name}"/>
<property name="username" value="${googledocs.username}"/> <property name="username" value="${googledocs.username}"/>
<property name="password" value="${googledocs.password}"/> <property name="password" value="${googledocs.password}"/>
<property name="permissionMap"> <property name="permissionMap">

View File

@@ -14,4 +14,5 @@ googledocs.username=
googledocs.password= googledocs.password=
# Google docs spreadsheet service name # Google docs spreadsheet service name
googledocs.spreadsheet.service.name=wise googledocs.spreadsheet.service.name=wise
googledocs.spreadsheet.downloadurl=https://spreadsheet.google.com/feeds/download

View File

@@ -32,13 +32,6 @@ public interface GoogleDocsService
* @return boolean true if enabled, false otherwise * @return boolean true if enabled, false otherwise
*/ */
boolean isEnabled(); boolean isEnabled();
/**
* Initialises the googles doc service, checking the provided credentials are correct. This need
* not be called manually since other service calls will initialise the service on demand, but it can
* be helpful to know the "health" of the service up front.
*/
void initialise() throws GoogleDocsServiceInitException;
/** /**
* Indicates whether the mimetype is supported for creation in google docs. * Indicates whether the mimetype is supported for creation in google docs.

View File

@@ -52,9 +52,9 @@ import org.alfresco.util.ParameterCheck;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import com.google.gdata.client.GoogleService;
import com.google.gdata.client.GoogleAuthTokenFactory.UserToken; import com.google.gdata.client.GoogleAuthTokenFactory.UserToken;
import com.google.gdata.client.docs.DocsService; import com.google.gdata.client.docs.DocsService;
import com.google.gdata.client.media.MediaService;
import com.google.gdata.data.IEntry; import com.google.gdata.data.IEntry;
import com.google.gdata.data.MediaContent; import com.google.gdata.data.MediaContent;
import com.google.gdata.data.PlainTextConstruct; import com.google.gdata.data.PlainTextConstruct;
@@ -95,8 +95,6 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
private final static String KEY_MARKED_DELETE = "google_doc_service.marked_delete"; private final static String KEY_MARKED_DELETE = "google_doc_service.marked_delete";
/** Services */ /** Services */
private DocsService googleDocumentService;
private GoogleService spreadsheetsService;
private NodeService nodeService; private NodeService nodeService;
private ContentService contentService; private ContentService contentService;
private PersonService personService; private PersonService personService;
@@ -112,12 +110,17 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
/** GoogleDoc base feed url */ /** GoogleDoc base feed url */
private String url = "http://docs.google.com/feeds/default/private/full"; private String url = "http://docs.google.com/feeds/default/private/full";
private String downloadUrl = "https://docs.google.com/feeds/download"; private String downloadUrl = "https://docs.google.com/feeds/download";
private String spreadsheetDownloadUrl = "https://spreadsheet.google.com/feeds/download";
/** Authentication credentials */ /** Authentication credentials */
private boolean initialised = false; private String applicationName;
private String spreadSheetServiceName;
private String username; private String username;
private String password; private String password;
/** Cached service tokens */
private Map<String, String> serviceTokens = new HashMap<String, String>(2);
/** Permission map */ /** Permission map */
private Map<String, String> permissionMap; private Map<String, String> permissionMap;
@@ -146,22 +149,81 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
"application/vnd.ms-powerpoint", "application/vnd.ms-powerpoint",
"image/x-wmf" "image/x-wmf"
); );
/** /**
* @param googleDocumentService google document service * Get a new instance of the spread sheet service
*
* @return spreadsheet service
*/ */
public void setGoogleDocumentService(DocsService googleDocumentService) public MediaService getSpreadSheetService()
{ {
this.googleDocumentService = googleDocumentService; if (spreadSheetServiceName == null)
{
throw new GoogleDocsServiceInitException("No Google Docs spreadsheet service has been specified.");
}
return getMediaService(spreadSheetServiceName);
}
/**
* Get a new instance of a document service
*
* @return document service
*/
public DocsService getDocumentService()
{
return (DocsService)getMediaService(DocsService.DOCS_SERVICE);
}
/**
*
* @param serviceName
* @return
*/
public MediaService getMediaService(String serviceName)
{
if (applicationName == null)
{
throw new GoogleDocsServiceInitException("Google Docs service " + serviceName + " could not be initialised, because no Google Doc application name has been specified.");
}
MediaService service = null;
if (serviceName.equals(DocsService.DOCS_SERVICE) == true)
{
service = new DocsService(applicationName);
}
else
{
service = new MediaService(serviceName, applicationName);
}
service.setChunkedMediaUpload(-1);
String token = serviceTokens.get(serviceName);
if (token == null)
{
try
{
if (username == null ||username.length() == 0 || password == null)
{
throw new GoogleDocsServiceInitException("No Google Docs credentials found. Please set the Google Docs authentication configuration.");
}
service.setUserCredentials(username, password);
serviceTokens.put(serviceName, ((UserToken)service.getAuthTokenFactory().getAuthToken()).getValue());
}
catch (AuthenticationException e)
{
throw new GoogleDocsServiceInitException("Unable to connect to Google Docs. Please check the Google Docs authentication configuration.", e);
}
}
else
{
service.setUserToken(token);
}
return service;
} }
/**
* @param spreadsheetsService spread sheets service
*/
public void setSpreadsheetsService(GoogleService spreadsheetsService)
{
this.spreadsheetsService = spreadsheetsService;
}
/** /**
* @param nodeService node service * @param nodeService node service
@@ -242,6 +304,30 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
{ {
this.downloadUrl = downloadUrl; this.downloadUrl = downloadUrl;
} }
/**
* @param spreadsheetDownloadUrl root spreadsheet download URL
*/
public void setSpreadsheetDownloadUrl(String spreadsheetDownloadUrl)
{
this.spreadsheetDownloadUrl = spreadsheetDownloadUrl;
}
/**
* @param applicationName GDoc application name
*/
public void setApplicationName(String applicationName)
{
this.applicationName = applicationName;
}
/**
* @param spreadSheetServiceName GDoc spread sheet service name
*/
public void setSpreadSheetServiceName(String spreadSheetServiceName)
{
this.spreadSheetServiceName = spreadSheetServiceName;
}
/** /**
* @param username google service user name * @param username google service user name
@@ -249,7 +335,8 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
public void setUsername(String username) public void setUsername(String username)
{ {
this.username = username; this.username = username;
this.initialised = false; // Reset the token map
serviceTokens = new HashMap<String, String>(2);
} }
/** /**
@@ -258,7 +345,8 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
public void setPassword(String password) public void setPassword(String password)
{ {
this.password = password; this.password = password;
this.initialised = false; // Reset the token map
serviceTokens = new HashMap<String, String>(2);
} }
/** /**
@@ -276,7 +364,8 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
public void setEnabled(boolean enabled) public void setEnabled(boolean enabled)
{ {
this.enabled = enabled; this.enabled = enabled;
this.initialised = false; // Reset the token map
serviceTokens = new HashMap<String, String>(2);
} }
/** /**
@@ -287,42 +376,6 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
return enabled; return enabled;
} }
/**
* Initialise google docs services
*/
public void initialise() throws GoogleDocsServiceInitException
{
if (initialised == false)
{
if (logger.isDebugEnabled() == true)
{
logger.debug("Trying to initialise google docs service for user " + username);
}
if (username == null ||username.length() == 0 || password == null)
{
throw new GoogleDocsServiceInitException("No Google Docs credentials found. Please set the Google Docs authentication configuration.");
}
try
{
googleDocumentService.setUserCredentials(username, password);
spreadsheetsService.setUserCredentials(username, password);
googleDocumentService.setChunkedMediaUpload(-1);
}
catch (AuthenticationException e)
{
throw new GoogleDocsServiceInitException("Unable to connect to Google Docs. Please check the Google Docs authentication configuration.", e);
}
initialised = true;
if (logger.isDebugEnabled() == true)
{
logger.debug("Successfully initialised google docs service for user " + username);
}
}
}
/** /**
* @see org.alfresco.repo.googledocs.GoogleDocsService#isSupportedMimetype(java.lang.String) * @see org.alfresco.repo.googledocs.GoogleDocsService#isSupportedMimetype(java.lang.String)
*/ */
@@ -340,16 +393,6 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
{ {
// Check for mandatory parameters // Check for mandatory parameters
ParameterCheck.mandatory("nodeRef", nodeRef); ParameterCheck.mandatory("nodeRef", nodeRef);
// Initialise google doc services
try
{
initialise();
}
catch (GoogleDocsServiceInitException e)
{
throw new AlfrescoRuntimeException("Unable to create google doc, because service could not be initialised.", e);
}
// Get property values // Get property values
String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); String name = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
@@ -413,16 +456,6 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
{ {
// Check for mandatory parameters // Check for mandatory parameters
ParameterCheck.mandatory("nodeRef", nodeRef); ParameterCheck.mandatory("nodeRef", nodeRef);
// Initialise google doc services
try
{
initialise();
}
catch (GoogleDocsServiceInitException e)
{
throw new AlfrescoRuntimeException("Unable to create google doc, because service could not be initialised.", e);
}
if (nodeService.hasAspect(nodeRef, ASPECT_GOOGLERESOURCE) == true) if (nodeService.hasAspect(nodeRef, ASPECT_GOOGLERESOURCE) == true)
{ {
@@ -627,16 +660,6 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
// Check for mandatory parameters // Check for mandatory parameters
ParameterCheck.mandatory("nodeRef", nodeRef); ParameterCheck.mandatory("nodeRef", nodeRef);
// Initialise google doc services
try
{
initialise();
}
catch (GoogleDocsServiceInitException e)
{
throw new AlfrescoRuntimeException("Unable to create google doc, because service could not be initialised.", e);
}
try try
{ {
if (nodeService.hasAspect(nodeRef, ASPECT_GOOGLERESOURCE) == true) if (nodeService.hasAspect(nodeRef, ASPECT_GOOGLERESOURCE) == true)
@@ -660,18 +683,30 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
if (docType.equals(TYPE_DOCUMENT) || docType.equals(TYPE_PRESENTATION)) if (docType.equals(TYPE_DOCUMENT) || docType.equals(TYPE_PRESENTATION))
{ {
downloadUrl = this.downloadUrl + "/" + docType + "s/Export?docId=" + document.getDocId() + StringBuffer buffer = new StringBuffer(this.downloadUrl);
"&exportFormat=" + fileExtension; buffer.append("/").
append(docType).append("s").
append("/Export?docId=").append(document.getDocId()).
append("&exportFormat=").append(fileExtension);
downloadUrl = buffer.toString();
} }
else if (docType.equals(TYPE_SPREADSHEET)) else if (docType.equals(TYPE_SPREADSHEET))
{ {
downloadUrl = ((MediaContent)document.getContent()).getUri() + "&exportFormat=" + fileExtension; StringBuffer buffer = new StringBuffer(spreadsheetDownloadUrl);
buffer.append("/").
append(docType).append("s").
append("/Export?key=").append(document.getDocId()).
append("&exportFormat=").append(fileExtension);
// If exporting to .csv or .tsv, add the gid parameter to specify which sheet to export // If exporting to .csv or .tsv, add the gid parameter to specify which sheet to export
if (fileExtension.equals("csv") || fileExtension.equals("tsv")) if (fileExtension.equals("csv") || fileExtension.equals("tsv"))
{ {
downloadUrl += "&gid=0"; // gid=0 will download only the first sheet buffer.append("&gid=0"); // gid=0 will download only the first sheet
} }
downloadUrl = buffer.toString();
} }
else if (docType.equals(TYPE_PDF)) else if (docType.equals(TYPE_PDF))
{ {
@@ -689,27 +724,21 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
logger.debug("Download URL for " + docType + " is " + downloadUrl); logger.debug("Download URL for " + docType + " is " + downloadUrl);
} }
// TODO need to verify that download of a spreadsheet works before we delete this historical code ... MediaService service = null;
UserToken docsToken = null;
if (docType.equals(TYPE_SPREADSHEET) == true) if (docType.equals(TYPE_SPREADSHEET) == true)
{ {
docsToken = (UserToken) googleDocumentService.getAuthTokenFactory().getAuthToken(); service = getSpreadSheetService();
UserToken spreadsheetsToken = (UserToken) spreadsheetsService.getAuthTokenFactory().getAuthToken();
googleDocumentService.setUserToken(spreadsheetsToken.getValue());
} }
else
{
service = getDocumentService();
}
MediaContent mc = new MediaContent(); MediaContent mc = new MediaContent();
mc.setUri(downloadUrl); mc.setUri(downloadUrl);
MediaSource ms = googleDocumentService.getMedia(mc); MediaSource ms = service.getMedia(mc);
if (docType.equals(TYPE_SPREADSHEET) == true) result = ms.getInputStream();
{
googleDocumentService.setUserToken(docsToken.getValue());
}
result = ms.getInputStream();
} }
else else
{ {
@@ -778,7 +807,7 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
try try
{ {
URL docEntryURL = new URL(url + "/" + resourceId); URL docEntryURL = new URL(url + "/" + resourceId);
result = googleDocumentService.getEntry(docEntryURL, entryClass); result = getDocumentService().getEntry(docEntryURL, entryClass);
} }
catch (ServiceException e) catch (ServiceException e)
{ {
@@ -892,7 +921,7 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
docEntry.setTitle(new PlainTextConstruct(name)); docEntry.setTitle(new PlainTextConstruct(name));
// Upload the document into the parent folder // Upload the document into the parent folder
document = googleDocumentService.insert( document = getDocumentService().insert(
new URL(parentFolderUrl), new URL(parentFolderUrl),
docEntry); docEntry);
@@ -944,10 +973,10 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
try try
{ {
// Update the existing content // Update the existing content
googleDocumentService.getRequestFactory().setHeader("If-Match", "*"); DocsService service = getDocumentService();
service.getRequestFactory().setHeader("If-Match", "*");
document.setMediaSource(new MediaStreamSource(is, mimeType)); document.setMediaSource(new MediaStreamSource(is, mimeType));
document.updateMedia(false); document.updateMedia(false);
googleDocumentService.getRequestFactory().setHeader("If-Match", null);
} }
catch (ServiceException e) catch (ServiceException e)
{ {
@@ -1004,7 +1033,7 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
folder.setTitle(new PlainTextConstruct(folderName)); folder.setTitle(new PlainTextConstruct(folderName));
// Create the folder // Create the folder
folderEntry = googleDocumentService.insert( folderEntry = getDocumentService().insert(
new URL(parentFolderUrl), new URL(parentFolderUrl),
folder); folder);
@@ -1067,7 +1096,7 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
// See if we have already set this permission or not // See if we have already set this permission or not
AclEntry aclEntry = null; AclEntry aclEntry = null;
AclFeed aclFeed = googleDocumentService.getFeed(aclFeedLinkURL, AclFeed.class); AclFeed aclFeed = getDocumentService().getFeed(aclFeedLinkURL, AclFeed.class);
if (aclFeed != null) if (aclFeed != null)
{ {
List<AclEntry> aclEntries = aclFeed.getEntries(); List<AclEntry> aclEntries = aclFeed.getEntries();
@@ -1089,7 +1118,7 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
aclEntry = new AclEntry(); aclEntry = new AclEntry();
aclEntry.setRole(aclRole); aclEntry.setRole(aclRole);
aclEntry.setScope(scope); aclEntry.setScope(scope);
googleDocumentService.insert(aclFeedLinkURL, aclEntry); getDocumentService().insert(aclFeedLinkURL, aclEntry);
} }
else else
{ {
@@ -1191,7 +1220,7 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
DocumentListEntry entry = getDocumentListEntry(resourceId); DocumentListEntry entry = getDocumentListEntry(resourceId);
if (entry != null) if (entry != null)
{ {
googleDocumentService.delete(new URL(entry.getEditLink().getHref() + "?delete=true"), entry.getEtag()); getDocumentService().delete(new URL(entry.getEditLink().getHref() + "?delete=true"), entry.getEtag());
} }
else else
{ {
@@ -1241,7 +1270,7 @@ public class GoogleDocsServiceImpl extends TransactionListenerAdapter
DocumentListEntry entry = getDocumentListEntry(resourceId); DocumentListEntry entry = getDocumentListEntry(resourceId);
if (entry != null) if (entry != null)
{ {
googleDocumentService.delete(new URL(entry.getEditLink().getHref() + "?delete=true"), entry.getEtag()); getDocumentService().delete(new URL(entry.getEditLink().getHref() + "?delete=true"), entry.getEtag());
} }
else else
{ {

View File

@@ -18,11 +18,13 @@
*/ */
package org.alfresco.repo.googledocs; package org.alfresco.repo.googledocs;
import org.alfresco.error.AlfrescoRuntimeException;
/** /**
* Google docs service initialisation exception class. * Google docs service initialisation exception class.
*/ */
public class GoogleDocsServiceInitException extends Exception public class GoogleDocsServiceInitException extends AlfrescoRuntimeException
{ {
/** Serial version UUID */ /** Serial version UUID */
private static final long serialVersionUID = -2104024155137888545L; private static final long serialVersionUID = -2104024155137888545L;
@@ -43,12 +45,4 @@ public class GoogleDocsServiceInitException extends Exception
{ {
super(message, cause); super(message, cause);
} }
/**
* @param cause causing exception
*/
public GoogleDocsServiceInitException(Throwable cause)
{
super(cause);
}
} }

View File

@@ -216,16 +216,7 @@ public class GoogleDocumentServiceSystemTest extends TestCase implements GoogleD
private boolean isGoogleServiceAvailable() private boolean isGoogleServiceAvailable()
{ {
boolean result = true; return googleDocsService.isEnabled();
try
{
googleDocsService.initialise();
}
catch (GoogleDocsServiceInitException e)
{
result = false;
}
return result;
} }
public void testGoogleDocUploadDownload() throws Exception public void testGoogleDocUploadDownload() throws Exception