call() throws IOException
{
// Get the file/folder node
diff --git a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
index 131c15d201..735182829a 100644
--- a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
+++ b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
@@ -29,6 +29,7 @@ import java.nio.charset.Charset;
import org.alfresco.error.AlfrescoRuntimeException;
import org.springframework.extensions.surf.util.I18NUtil;
+import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.filesys.AccessDeniedException;
import org.alfresco.jlan.server.filesys.DiskFullException;
import org.alfresco.jlan.server.filesys.FileAttribute;
@@ -36,6 +37,7 @@ import org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.jlan.server.filesys.FileOpenParams;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.smb.SeekType;
+import org.alfresco.jlan.smb.server.SMBSrvSession;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.AbstractContentReader;
import org.alfresco.repo.content.encoding.ContentCharsetFinder;
@@ -93,29 +95,27 @@ public class ContentNetworkFile extends NodeRefNetworkFile
/**
* Helper method to create a {@link NetworkFile network file} given a node reference.
*/
- public static ContentNetworkFile createFile(
- NodeService nodeService,
- ContentService contentService,
- MimetypeService mimetypeService,
- CifsHelper cifsHelper,
- NodeRef nodeRef,
- FileOpenParams params)
+ public static ContentNetworkFile createFile( NodeService nodeService, ContentService contentService, MimetypeService mimetypeService,
+ CifsHelper cifsHelper, NodeRef nodeRef, FileOpenParams params, SrvSession sess)
{
String path = params.getPath();
- // Check write access
- // TODO: Check access writes and compare to write requirements
-
// Create the file
ContentNetworkFile netFile = null;
- if ( isMSOfficeSpecialFile(path)) {
+ if ( isMSOfficeSpecialFile(path, sess, nodeService, nodeRef)) {
// Create a file for special processing
netFile = new MSOfficeContentNetworkFile( nodeService, contentService, mimetypeService, nodeRef, path);
}
+ else if ( isOpenOfficeSpecialFile( path, sess, nodeService, nodeRef)) {
+
+ // Create a file for special processing
+
+ netFile = new OpenOfficeContentNetworkFile( nodeService, contentService, mimetypeService, nodeRef, path);
+ }
else {
// Create a normal content file
@@ -172,6 +172,10 @@ public class ContentNetworkFile extends NodeRefNetworkFile
netFile.setAttributes(fileInfo.getFileAttributes());
+ // Set the owner process id
+
+ netFile.setProcessId( params.getProcessId());
+
// If the file is read-only then only allow read access
if ( netFile.isReadOnly())
@@ -725,20 +729,62 @@ public class ContentNetworkFile extends NodeRefNetworkFile
logger.debug("Flush file=" + this);
}
+ /**
+ * Return the modified status
+ *
+ * @return boolean
+ */
+ public final boolean isModified() {
+ return modified;
+ }
+
/**
* Check if the file is an MS Office document type that needs special processing
*
* @param path String
+ * @param sess SrvSession
+ * @param nodeService NodeService
+ * @param nodeRef NodeRef
* @return boolean
*/
- private static final boolean isMSOfficeSpecialFile(String path) {
+ private static final boolean isMSOfficeSpecialFile( String path, SrvSession sess, NodeService nodeService, NodeRef nodeRef) {
// Check if the file extension indicates a problem MS Office format
path = path.toLowerCase();
- if ( path.endsWith( ".xls"))
- return true;
+ if ( path.endsWith( ".xls") && sess instanceof SMBSrvSession) {
+
+ // Check if the file is versionable
+
+ if ( nodeService.hasAspect( nodeRef, ContentModel.ASPECT_VERSIONABLE))
+ return true;
+ }
return false;
}
+
+ /**
+ * Check if the file is an OpenOffice document type that needs special processing
+ *
+ * @param path String
+ * @param sess SrvSession
+ * @param nodeService NodeService
+ * @param nodeRef NodeRef
+ * @return boolean
+ */
+ private static final boolean isOpenOfficeSpecialFile( String path, SrvSession sess, NodeService nodeService, NodeRef nodeRef) {
+
+ // Check if the file extension indicates a problem OpenOffice format
+
+ path = path.toLowerCase();
+
+ if ( path.endsWith( ".odt") && sess instanceof SMBSrvSession) {
+
+ // Check if the file is versionable
+
+ if ( nodeService.hasAspect( nodeRef, ContentModel.ASPECT_VERSIONABLE))
+ return true;
+ }
+ return false;
+ }
}
diff --git a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java
index 5d2300f552..4d52acd3a9 100644
--- a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java
+++ b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java
@@ -404,11 +404,17 @@ public class ContentSearchContext extends SearchContext
}
}
- // Check if the resume file name is the last file returned, no need to reposition the file index
+ // Check if the resume file name is the last file returned
if ( m_lastFileName != null && info.getFileName().equalsIgnoreCase( m_lastFileName)) {
+
+ // Reset the index/resume id
- // DEBUG
+ index = index - 1;
+ resumeId = resId - 1;
+ donePseudoFiles = true;
+
+ // DEBUG
if ( logger.isDebugEnabled())
logger.debug("Fast search restart - " + m_lastFileName);
diff --git a/source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java b/source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java
index 25b7332955..11cc49b28c 100644
--- a/source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java
+++ b/source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java
@@ -22,6 +22,7 @@ package org.alfresco.filesys.repo;
import java.io.IOException;
import org.alfresco.jlan.server.filesys.FileInfo;
+import org.alfresco.jlan.server.filesys.cache.FileState;
import org.alfresco.jlan.smb.SeekType;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -83,7 +84,9 @@ public class LinkMemoryNetworkFile extends NodeRefNetworkFile
*/
public void closeFile() throws java.io.IOException
{
- // Nothing to do
+ // Clear the file state
+
+ setFileState( null);
}
/**
@@ -247,4 +250,18 @@ public class LinkMemoryNetworkFile extends NodeRefNetworkFile
{
// Allow the write, just do not do anything
}
+
+ /**
+ * Return a dummy file state for this file
+ *
+ * @return FileState
+ */
+ public FileState getFileState() {
+
+ // Create a dummy file state
+
+ if ( super.getFileState() == null)
+ setFileState(new FileState(getFullName()));
+ return super.getFileState();
+ }
}
diff --git a/source/java/org/alfresco/filesys/repo/NodeRefNetworkFile.java b/source/java/org/alfresco/filesys/repo/NodeRefNetworkFile.java
index 21a7d4051d..f3561557bb 100644
--- a/source/java/org/alfresco/filesys/repo/NodeRefNetworkFile.java
+++ b/source/java/org/alfresco/filesys/repo/NodeRefNetworkFile.java
@@ -18,7 +18,6 @@
package org.alfresco.filesys.repo;
import org.alfresco.filesys.alfresco.AlfrescoNetworkFile;
-import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -122,12 +121,12 @@ public abstract class NodeRefNetworkFile extends AlfrescoNetworkFile {
return --m_openCount;
}
- /**
- * Return the open file count
- *
- * @return int
- */
- public final int getOpenCount() {
- return m_openCount;
- }
+ /**
+ * Return the open file count
+ *
+ * @return int
+ */
+ public final int getOpenCount() {
+ return m_openCount;
+ }
}
diff --git a/source/java/org/alfresco/filesys/repo/OpenOfficeContentNetworkFile.java b/source/java/org/alfresco/filesys/repo/OpenOfficeContentNetworkFile.java
new file mode 100644
index 0000000000..d1c84dfd4c
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/OpenOfficeContentNetworkFile.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2005-2010 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.filesys.repo;
+
+import java.io.IOException;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.MimetypeService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * OpenOffice Content Network File Class
+ *
+ * Provides special handling for OpenOffice file saves that open the file, truncate, close, then open the file
+ * again to write the data, as this causes multiple versions to be generated when the file is versionable.
+ *
+ * @author gkspencer
+ */
+public class OpenOfficeContentNetworkFile extends ContentNetworkFile {
+
+ // Debug logging
+
+ private static final Log logger = LogFactory.getLog(OpenOfficeContentNetworkFile.class);
+
+ // Flag to indicate the last I/O operation was a truncate file to zero size
+
+ private boolean m_truncateToZero;
+
+ // Delayed file close count
+
+ private int m_delayedClose;
+
+ /**
+ * Class constructor
+ *
+ * @param transactionService TransactionService
+ * @param nodeService NodeService
+ * @param contentService ContentService
+ * @param nodeRef NodeRef
+ * @param name String
+ */
+ protected OpenOfficeContentNetworkFile(
+ NodeService nodeService,
+ ContentService contentService,
+ MimetypeService mimetypeService,
+ NodeRef nodeRef,
+ String name)
+ {
+ super(nodeService, contentService, mimetypeService, nodeRef, name);
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ logger.debug("Using OpenOffice network file for " + name + ", versionLabel=" + nodeService.getProperty( nodeRef, ContentModel.PROP_VERSION_LABEL));
+ }
+
+ /**
+ * Return the delayed close count
+ *
+ * @return int
+ */
+ public final int getDelayedCloseCount() {
+ return m_delayedClose;
+ }
+
+ /**
+ * Increment the delayed close count
+ */
+ public final void incrementDelayedCloseCount() {
+ m_delayedClose++;
+
+ // Clear the truncate to zero status
+
+ m_truncateToZero = false;
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Increment delayed close count=" + getDelayedCloseCount() + ", path=" + getName());
+ }
+
+ /**
+ * Check if the last file operation was a truncate to zero length
+ *
+ * @return boolean
+ */
+ public final boolean truncatedToZeroLength() {
+ return m_truncateToZero;
+ }
+
+ /**
+ * Read from the file.
+ *
+ * @param buf byte[]
+ * @param len int
+ * @param pos int
+ * @param fileOff long
+ * @return Length of data read.
+ * @exception IOException
+ */
+ public int readFile(byte[] buffer, int length, int position, long fileOffset)
+ throws IOException
+ {
+ // Clear the truncate flag
+
+ m_truncateToZero = false;
+
+ // Chain to the standard read
+
+ return super.readFile( buffer, length, position, fileOffset);
+ }
+
+ /**
+ * Write a block of data to the file.
+ *
+ * @param buf byte[]
+ * @param len int
+ * @param pos int
+ * @param fileOff long
+ * @exception IOException
+ */
+ public void writeFile(byte[] buffer, int length, int position, long fileOffset)
+ throws IOException
+ {
+ // Clear the truncate flag
+
+ m_truncateToZero = false;
+
+ // Chain to the standard write
+
+ super.writeFile( buffer, length, position, fileOffset);
+ }
+
+ /**
+ * Truncate or extend the file to the specified length
+ *
+ * @param size long
+ * @exception IOException
+ */
+ public void truncateFile(long size)
+ throws IOException
+ {
+ // Chain to the standard truncate
+
+ super.truncateFile( size);
+
+ // Check for a truncate to zero length
+
+ if ( size == 0L) {
+ m_truncateToZero = true;
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("OpenOffice document truncated to zero length, path=" + getName());
+ }
+ }
+
+ /**
+ * Close the file
+ *
+ * @exception IOException
+ */
+ public void closeFile()
+ throws IOException
+ {
+ // DEBUG
+
+ if ( logger.isDebugEnabled()) {
+ logger.debug("Close OpenOffice file, " + getName() + ", delayed close count=" + getDelayedCloseCount() + ", writes=" + getWriteCount() +
+ ", modified=" + isModified());
+ logger.debug(" Open count=" + getOpenCount() + ", fstate open=" + getFileState().getOpenCount());
+ }
+
+ // Chain to the standard close
+
+ super.closeFile();
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java b/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java
index ab00a0dde5..c823130779 100644
--- a/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java
+++ b/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java
@@ -24,17 +24,19 @@
*/
package org.alfresco.filesys.repo.desk;
+import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.Callable;
+import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.alfresco.DesktopAction;
import org.alfresco.filesys.alfresco.DesktopParams;
import org.alfresco.filesys.alfresco.DesktopResponse;
import org.alfresco.filesys.alfresco.DesktopTarget;
+import org.alfresco.filesys.alfresco.AlfrescoDiskDriver.CallableIO;
import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.server.filesys.FileStatus;
import org.alfresco.jlan.server.filesys.NotifyChange;
@@ -90,14 +92,14 @@ public class CheckInOutDesktopAction extends DesktopAction {
if ( params.numberOfTargetNodes() == 0)
return new DesktopResponse(StsSuccess);
- class WriteTxn implements Callable
+ class WriteTxn implements CallableIO
{
private List> fileChanges;
/* (non-Javadoc)
* @see java.util.concurrent.Callable#call()
*/
- public DesktopResponse call() throws Exception
+ public DesktopResponse call() throws IOException
{
// Initialize / reset the list of file changes
fileChanges = new LinkedList>();
@@ -156,10 +158,17 @@ public class CheckInOutDesktopAction extends DesktopAction {
}
catch (Exception ex)
{
- // If this is a 'retryable' exception, pass it on
+ // Propagate retryable errors. Log the rest.
if (RetryingTransactionHelper.extractRetryCause(ex) != null)
{
- throw ex;
+ if (ex instanceof RuntimeException)
+ {
+ throw (RuntimeException)ex;
+ }
+ else
+ {
+ throw new AlfrescoRuntimeException("Desktop action error", ex);
+ }
}
// Dump the error
@@ -229,10 +238,17 @@ public class CheckInOutDesktopAction extends DesktopAction {
}
catch (Exception ex)
{
- // If this is a 'retryable' exception, pass it on
+ // Propagate retryable errors. Log the rest.
if (RetryingTransactionHelper.extractRetryCause(ex) != null)
{
- throw ex;
+ if (ex instanceof RuntimeException)
+ {
+ throw (RuntimeException)ex;
+ }
+ else
+ {
+ throw new AlfrescoRuntimeException("Desktop action error", ex);
+ }
}
// Dump the error
@@ -269,7 +285,16 @@ public class CheckInOutDesktopAction extends DesktopAction {
// Process the transaction
WriteTxn callback = new WriteTxn();
- DesktopResponse response = params.getDriver().doInWriteTransaction(params.getSession(), callback);
+ DesktopResponse response;
+ try
+ {
+ response = params.getDriver().doInWriteTransaction(params.getSession(), callback);
+ }
+ catch (IOException e)
+ {
+ // Should not happen
+ throw new AlfrescoRuntimeException("Desktop action error", e);
+ }
// Queue file change notifications
callback.notifyChanges();
diff --git a/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java b/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java
index f091aa1fa6..dd7a1b6d44 100644
--- a/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java
+++ b/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java
@@ -25,21 +25,21 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
-import java.util.concurrent.Callable;
-import org.springframework.extensions.config.ConfigElement;
import org.alfresco.filesys.alfresco.AlfrescoContext;
import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
import org.alfresco.filesys.alfresco.DesktopAction;
import org.alfresco.filesys.alfresco.DesktopActionException;
import org.alfresco.filesys.alfresco.DesktopParams;
import org.alfresco.filesys.alfresco.DesktopResponse;
+import org.alfresco.filesys.alfresco.AlfrescoDiskDriver.CallableIO;
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.scripts.ScriptException;
import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.util.ResourceFinder;
import org.springframework.core.io.Resource;
+import org.springframework.extensions.config.ConfigElement;
/**
* Javascript Desktop Action Class
@@ -230,23 +230,19 @@ public class JavaScriptDesktopAction extends DesktopAction {
if ( hasWebappURL())
model.put("webURL", getWebappURL());
- // Compute the response in a retryable write transaction
- return params.getDriver().doInWriteTransaction(params.getSession(), new Callable()
+ try
{
- public DesktopResponse call() throws Exception
+ // Compute the response in a retryable write transaction
+ return params.getDriver().doInWriteTransaction(params.getSession(), new CallableIO()
{
- DesktopResponse response = new DesktopResponse(StsSuccess);
-
- // Run the script
-
- Object result = null;
-
- try
+ public DesktopResponse call() throws IOException
{
+ DesktopResponse response = new DesktopResponse(StsSuccess);
+
// Run the script
- result = scriptService.executeScriptString(getScript(), model);
+ Object result = scriptService.executeScriptString(getScript(), model);
// Check the result
@@ -294,23 +290,21 @@ public class JavaScriptDesktopAction extends DesktopAction {
response.setStatus(sts, msgToken != null ? msgToken : "");
}
}
+
+ // Return the response
+
+ return response;
}
- catch (ScriptException ex)
- {
- if (RetryingTransactionHelper.extractRetryCause(ex) != null)
- {
- throw ex;
- }
-
- // Set the error response for the client
- response.setStatus(StsError, ex.getMessage());
- }
-
- // Return the response
-
- return response;
- }
- });
+ });
+ }
+ catch (ScriptException ex)
+ {
+ return new DesktopResponse(StsError, ex.getMessage());
+ }
+ catch (IOException ex)
+ {
+ return new DesktopResponse(StsError, ex.getMessage());
+ }
}
else
{
diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java
index 3fdacbe9d7..b0fdf93a41 100644
--- a/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java
+++ b/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java
@@ -32,6 +32,7 @@ import org.alfresco.repo.importer.ACPImportPackageHandler;
import org.alfresco.repo.importer.ImporterBootstrap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.admin.PatchException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
@@ -217,7 +218,7 @@ public class ImapFoldersPatch extends AbstractPatch
if (imapConfigFolderNodeRef == null)
{
// import the content
- RunAsWork