();
-
+ protected TransactionService m_transactionService;
+
/**
* Return the service registry
*
@@ -75,7 +71,7 @@ public abstract class AlfrescoDiskDriver implements IOCtlInterface, Transactiona
*/
public final ServiceRegistry getServiceRegistry()
{
- return m_serviceRegistry;
+ return m_serviceRegistry;
}
/**
@@ -95,7 +91,7 @@ public abstract class AlfrescoDiskDriver implements IOCtlInterface, Transactiona
*/
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
- m_serviceRegistry = serviceRegistry;
+ m_serviceRegistry = serviceRegistry;
}
/**
@@ -139,261 +135,6 @@ public abstract class AlfrescoDiskDriver implements IOCtlInterface, Transactiona
throw new IOControlNotImplementedException();
}
- /**
- * Begin a read-only transaction
- *
- * @param sess SrvSession
- */
- public void beginReadTransaction(SrvSession sess) {
- beginTransaction( sess, true);
- }
-
- /**
- * Begin a writeable transaction
- *
- * @param sess SrvSession
- */
- public void beginWriteTransaction(SrvSession sess) {
- beginTransaction( sess, false);
- }
-
- /**
- * Perform a retryable operation in a write transaction
- *
- * WARNING : side effect - that the current transaction, if any, is ended.
- *
- *
- * @param sess
- * the server session
- * @param callback
- * callback for the retryable operation
- * @return the result of the operation
- * @throws Exception
- */
- public T doInWriteTransaction(SrvSession sess, final CallableIO callback)
- throws IOException
- {
- Boolean wasInRetryingTransaction = m_inRetryingTransaction.get();
- try
- {
- boolean hadTransaction = sess.hasTransaction();
- if (hadTransaction)
- {
- sess.endTransaction();
- }
- m_inRetryingTransaction.set(Boolean.TRUE);
- T result = m_transactionService.getRetryingTransactionHelper().doInTransaction(
- new RetryingTransactionHelper.RetryingTransactionCallback()
- {
- public T execute() throws Throwable
- {
- try
- {
- return callback.call();
- }
- catch (IOException e)
- {
- // Ensure original checked IOExceptions get propagated
- throw new PropagatingException(e);
- }
- }
- });
- if (hadTransaction)
- {
- beginReadTransaction(sess);
- }
- return result;
- }
- catch (PropagatingException e)
- {
- // Unwrap checked exceptions
- throw (IOException) e.getCause();
- }
- finally
- {
- m_inRetryingTransaction.set(wasInRetryingTransaction);
- }
- }
-
- /**
- * End an active transaction
- *
- * @param sess SrvSession
- * @param tx Object
- */
- public void endTransaction(SrvSession sess, Object tx) {
-
- // Check that the transaction object is valid
-
- if ( tx == null)
- return;
-
- // Get the filesystem transaction
-
- FilesysTransaction filesysTx = (FilesysTransaction) tx;
-
- // Check if there is an active transaction
-
- if ( filesysTx != null && filesysTx.hasTransaction())
- {
- // Get the active transaction
-
- UserTransaction ftx = filesysTx.getTransaction();
-
- try
- {
- // Commit or rollback the transaction
-
- if ( ftx.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
- ftx.getStatus() == Status.STATUS_ROLLEDBACK ||
- ftx.getStatus() == Status.STATUS_ROLLING_BACK)
- {
- // Transaction is marked for rollback
-
- ftx.rollback();
-
- // DEBUG
-
- if ( logger.isDebugEnabled())
- logger.debug("End transaction (rollback)");
- }
- else
- {
- // Commit the transaction
-
- ftx.commit();
-
- // DEBUG
-
- if ( logger.isDebugEnabled())
- logger.debug("End transaction (commit)");
- }
- }
- catch ( Exception ex)
- {
- if ( logger.isDebugEnabled())
- logger.debug("Failed to end transaction, " + ex.getMessage());
-// throw new AlfrescoRuntimeException("Failed to end transaction", ex);
- }
- finally
- {
- // Clear the current transaction
-
- sess.clearTransaction();
- }
- }
- }
-
- /**
- * Create and start a transaction, if not already active
- *
- * @param sess SrvSession
- * @param readOnly boolean
- * @exception AlfrescoRuntimeException
- */
- private final void beginTransaction( SrvSession sess, boolean readOnly)
- throws AlfrescoRuntimeException
- {
- // Do nothing if we are already in a retrying transaction
- Boolean inRetryingTransaction = m_inRetryingTransaction.get();
-
- if (inRetryingTransaction != null && inRetryingTransaction)
- {
- return;
- }
-
- // Initialize the per session thread local that holds the transaction
-
- sess.initializeTransactionObject();
-
- // Get the filesystem transaction
-
- FilesysTransaction filesysTx = (FilesysTransaction) sess.getTransactionObject().get();
- if ( filesysTx == null)
- {
- filesysTx = new FilesysTransaction();
- sess.getTransactionObject().set( filesysTx);
- }
-
- // If there is an active transaction check that it is the required type
-
- if ( filesysTx.hasTransaction())
- {
- // Get the active transaction
-
- UserTransaction tx = filesysTx.getTransaction();
-
- // Check if the current transaction is marked for rollback
-
- try
- {
- if ( tx.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
- tx.getStatus() == Status.STATUS_ROLLEDBACK ||
- tx.getStatus() == Status.STATUS_ROLLING_BACK)
- {
- // Rollback the current transaction
-
- tx.rollback();
- }
- }
- catch ( Exception ex)
- {
- }
-
- // Check if the transaction is a write transaction, if write has been requested
-
- if ( readOnly == false && filesysTx.isReadOnly() == true)
- {
- // Commit the read-only transaction
-
- try
- {
- tx.commit();
- }
- catch ( Exception ex)
- {
- throw new AlfrescoRuntimeException("Failed to commit read-only transaction, " + ex.getMessage());
- }
- finally
- {
- // Clear the active transaction
-
- filesysTx.clearTransaction();
- }
- }
- }
-
- // Create the transaction
-
- if ( filesysTx.hasTransaction() == false)
- {
- try
- {
- // Create a new transaction
-
- UserTransaction userTrans = m_transactionService.getUserTransaction(readOnly);
- userTrans.begin();
-
- // Store the transaction
-
- filesysTx.setTransaction( userTrans, readOnly);
-
- // DEBUG
-
- if ( logger.isDebugEnabled())
- logger.debug("Created transaction readOnly=" + readOnly);
- }
- catch (Exception ex)
- {
- throw new AlfrescoRuntimeException("Failed to create transaction, " + ex.getMessage());
- }
- }
-
- // Store the transaction callback
-
- sess.setTransaction( this);
- }
-
/**
* Registers a device context object for this instance
* of the shared device. The same DeviceInterface implementation may be used for multiple
@@ -412,8 +153,8 @@ public abstract class AlfrescoDiskDriver implements IOCtlInterface, Transactiona
AlfrescoContext alfCtx = (AlfrescoContext) ctx;
if ( serverConfig != null) {
- alfCtx.setServerConfigurationBean( serverConfig);
- alfCtx.enableStateCache( true);
+ alfCtx.setServerConfigurationBean( serverConfig);
+ alfCtx.enableStateCache( true);
}
// Initialize the filesystem
@@ -421,30 +162,4 @@ public abstract class AlfrescoDiskDriver implements IOCtlInterface, Transactiona
alfCtx.initialize(this);
}
}
-
- /**
- * An extended {@link Callable} that throws {@link IOException}s.
- *
- * @param
- */
- public interface CallableIO extends Callable
- {
- public V call() throws IOException;
- }
-
- /**
- * A wrapper for checked exceptions to be passed through the retrying transaction handler.
- */
- protected static class PropagatingException extends RuntimeException
- {
- private static final long serialVersionUID = 1L;
-
- /**
- * @param cause
- */
- public PropagatingException(Throwable cause)
- {
- super(cause);
- }
- }
}
diff --git a/source/java/org/alfresco/filesys/alfresco/AlfrescoTxDiskDriver.java b/source/java/org/alfresco/filesys/alfresco/AlfrescoTxDiskDriver.java
new file mode 100644
index 0000000000..da89036c6e
--- /dev/null
+++ b/source/java/org/alfresco/filesys/alfresco/AlfrescoTxDiskDriver.java
@@ -0,0 +1,340 @@
+/*
+ * 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.alfresco;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+
+import javax.transaction.Status;
+import javax.transaction.UserTransaction;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.core.DeviceContext;
+import org.alfresco.jlan.server.core.DeviceContextException;
+import org.alfresco.jlan.server.filesys.IOControlNotImplementedException;
+import org.alfresco.jlan.server.filesys.IOCtlInterface;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.jlan.server.filesys.TransactionalFilesystemInterface;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.jlan.smb.SMBException;
+import org.alfresco.jlan.smb.SMBStatus;
+import org.alfresco.jlan.util.DataBuffer;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.transaction.TransactionService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Alfresco Tx Disk Driver Base Class
+ *
+ * Provides common code to the Alfresco filesystem implementations.
+ *
+ * @author gkspencer
+ */
+public abstract class AlfrescoTxDiskDriver extends AlfrescoDiskDriver implements TransactionalFilesystemInterface
+{
+ private static final Log logger = LogFactory.getLog(AlfrescoTxDiskDriver.class);
+
+ // Remember whether the current thread is already in a retrying transaction
+
+ private ThreadLocal m_inRetryingTransaction = new ThreadLocal();
+
+ /**
+ * Begin a read-only transaction
+ *
+ * @param sess SrvSession
+ */
+ public void beginReadTransaction(SrvSession sess) {
+ beginTransaction( sess, true);
+ }
+
+ /**
+ * Begin a writeable transaction
+ *
+ * @param sess SrvSession
+ */
+ public void beginWriteTransaction(SrvSession sess) {
+ beginTransaction( sess, false);
+ }
+
+ /**
+ * Perform a retryable operation in a write transaction
+ *
+ * WARNING : side effect - that the current transaction, if any, is ended.
+ *
+ *
+ * @param sess
+ * the server session
+ * @param callback
+ * callback for the retryable operation
+ * @return the result of the operation
+ * @throws Exception
+ */
+ public T doInWriteTransaction(SrvSession sess, final CallableIO callback)
+ throws IOException
+ {
+ Boolean wasInRetryingTransaction = m_inRetryingTransaction.get();
+ try
+ {
+ boolean hadTransaction = sess.hasTransaction();
+ if (hadTransaction)
+ {
+ sess.endTransaction();
+ }
+ m_inRetryingTransaction.set(Boolean.TRUE);
+ T result = m_transactionService.getRetryingTransactionHelper().doInTransaction(
+ new RetryingTransactionHelper.RetryingTransactionCallback()
+ {
+ public T execute() throws Throwable
+ {
+ try
+ {
+ return callback.call();
+ }
+ catch (IOException e)
+ {
+ // Ensure original checked IOExceptions get propagated
+ throw new PropagatingException(e);
+ }
+ }
+ });
+ if (hadTransaction)
+ {
+ beginReadTransaction(sess);
+ }
+ return result;
+ }
+ catch (PropagatingException e)
+ {
+ // Unwrap checked exceptions
+ throw (IOException) e.getCause();
+ }
+ finally
+ {
+ m_inRetryingTransaction.set(wasInRetryingTransaction);
+ }
+ }
+
+ /**
+ * End an active transaction
+ *
+ * @param sess SrvSession
+ * @param tx Object
+ */
+ public void endTransaction(SrvSession sess, Object tx) {
+
+ // Check that the transaction object is valid
+
+ if ( tx == null)
+ return;
+
+ // Get the filesystem transaction
+
+ FilesysTransaction filesysTx = (FilesysTransaction) tx;
+
+ // Check if there is an active transaction
+
+ if ( filesysTx != null && filesysTx.hasTransaction())
+ {
+ // Get the active transaction
+
+ UserTransaction ftx = filesysTx.getTransaction();
+
+ try
+ {
+ // Commit or rollback the transaction
+
+ if ( ftx.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
+ ftx.getStatus() == Status.STATUS_ROLLEDBACK ||
+ ftx.getStatus() == Status.STATUS_ROLLING_BACK)
+ {
+ // Transaction is marked for rollback
+
+ ftx.rollback();
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("End transaction (rollback)");
+ }
+ else
+ {
+ // Commit the transaction
+
+ ftx.commit();
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("End transaction (commit)");
+ }
+ }
+ catch ( Exception ex)
+ {
+ if ( logger.isDebugEnabled())
+ logger.debug("Failed to end transaction, " + ex.getMessage());
+// throw new AlfrescoRuntimeException("Failed to end transaction", ex);
+ }
+ finally
+ {
+ // Clear the current transaction
+
+ sess.clearTransaction();
+ }
+ }
+ }
+
+ /**
+ * Create and start a transaction, if not already active
+ *
+ * @param sess SrvSession
+ * @param readOnly boolean
+ * @exception AlfrescoRuntimeException
+ */
+ private final void beginTransaction( SrvSession sess, boolean readOnly)
+ throws AlfrescoRuntimeException
+ {
+ // Do nothing if we are already in a retrying transaction
+ Boolean inRetryingTransaction = m_inRetryingTransaction.get();
+
+ if (inRetryingTransaction != null && inRetryingTransaction)
+ {
+ return;
+ }
+
+ // Initialize the per session thread local that holds the transaction
+
+ sess.initializeTransactionObject();
+
+ // Get the filesystem transaction
+
+ FilesysTransaction filesysTx = (FilesysTransaction) sess.getTransactionObject().get();
+ if ( filesysTx == null)
+ {
+ filesysTx = new FilesysTransaction();
+ sess.getTransactionObject().set( filesysTx);
+ }
+
+ // If there is an active transaction check that it is the required type
+
+ if ( filesysTx.hasTransaction())
+ {
+ // Get the active transaction
+
+ UserTransaction tx = filesysTx.getTransaction();
+
+ // Check if the current transaction is marked for rollback
+
+ try
+ {
+ if ( tx.getStatus() == Status.STATUS_MARKED_ROLLBACK ||
+ tx.getStatus() == Status.STATUS_ROLLEDBACK ||
+ tx.getStatus() == Status.STATUS_ROLLING_BACK)
+ {
+ // Rollback the current transaction
+
+ tx.rollback();
+ }
+ }
+ catch ( Exception ex)
+ {
+ }
+
+ // Check if the transaction is a write transaction, if write has been requested
+
+ if ( readOnly == false && filesysTx.isReadOnly() == true)
+ {
+ // Commit the read-only transaction
+
+ try
+ {
+ tx.commit();
+ }
+ catch ( Exception ex)
+ {
+ throw new AlfrescoRuntimeException("Failed to commit read-only transaction, " + ex.getMessage());
+ }
+ finally
+ {
+ // Clear the active transaction
+
+ filesysTx.clearTransaction();
+ }
+ }
+ }
+
+ // Create the transaction
+
+ if ( filesysTx.hasTransaction() == false)
+ {
+ try
+ {
+ // Create a new transaction
+
+ UserTransaction userTrans = m_transactionService.getUserTransaction(readOnly);
+ userTrans.begin();
+
+ // Store the transaction
+
+ filesysTx.setTransaction( userTrans, readOnly);
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Created transaction readOnly=" + readOnly);
+ }
+ catch (Exception ex)
+ {
+ throw new AlfrescoRuntimeException("Failed to create transaction, " + ex.getMessage());
+ }
+ }
+
+ // Store the transaction callback
+
+ sess.setTransaction( this);
+ }
+
+ /**
+ * An extended {@link Callable} that throws {@link IOException}s.
+ *
+ * @param
+ */
+ public interface CallableIO extends Callable
+ {
+ public V call() throws IOException;
+ }
+
+ /**
+ * A wrapper for checked exceptions to be passed through the retrying transaction handler.
+ */
+ protected static class PropagatingException extends RuntimeException
+ {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @param cause
+ */
+ public PropagatingException(Throwable cause)
+ {
+ super(cause);
+ }
+ }
+}
diff --git a/source/java/org/alfresco/filesys/alfresco/DesktopAction.java b/source/java/org/alfresco/filesys/alfresco/DesktopAction.java
index 78bea11196..388d892846 100644
--- a/source/java/org/alfresco/filesys/alfresco/DesktopAction.java
+++ b/source/java/org/alfresco/filesys/alfresco/DesktopAction.java
@@ -1,19 +1,19 @@
/*
- * 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
+ * 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 .
*/
@@ -108,7 +108,7 @@ public abstract class DesktopAction {
// Filesystem driver and context
- protected AlfrescoDiskDriver m_filesysDriver;
+ //private AlfrescoDiskDriver m_filesysDriver;
private AlfrescoContext m_filesysContext;
// Webapp URL
@@ -220,15 +220,15 @@ public abstract class DesktopAction {
return m_pseudoFile;
}
- /**
- * Return the filesystem driver
- *
- * @return AlfrescoDiskDriver
- */
- public final AlfrescoDiskDriver getDriver()
- {
- return m_filesysDriver;
- }
+// /**
+// * Return the filesystem driver
+// *
+// * @return AlfrescoDiskDriver
+// */
+// public final AlfrescoDiskDriver getDriver()
+// {
+// return m_filesysDriver;
+// }
/**
* Return the filesystem context
@@ -250,6 +250,8 @@ public abstract class DesktopAction {
return null;
}
+ ServiceRegistry serviceRegistry;
+
/**
* Return the service registry
*
@@ -257,7 +259,12 @@ public abstract class DesktopAction {
*/
public final ServiceRegistry getServiceRegistry()
{
- return m_filesysDriver.getServiceRegistry();
+ return serviceRegistry;
+ }
+
+ public final void setServiceRegistry (ServiceRegistry serviceRegistry)
+ {
+ this.serviceRegistry = serviceRegistry;
}
/**
@@ -307,8 +314,10 @@ public abstract class DesktopAction {
standardInitialize(global, config, fileSys);
+ AlfrescoDiskDriver driver = (AlfrescoDiskDriver)fileSys.getDiskInterface();
+
// Complete initialization
- initializeAction((AlfrescoDiskDriver) fileSys.getDiskInterface(), (AlfrescoContext) fileSys.getDiskContext());
+ initializeAction(driver.getServiceRegistry(), (AlfrescoContext) fileSys.getDiskContext());
}
/**
@@ -373,11 +382,11 @@ public abstract class DesktopAction {
*
* @exception DesktopActionException
*/
- public void initializeAction(AlfrescoDiskDriver filesysDriver, AlfrescoContext filesysContext) throws DesktopActionException
+ public void initializeAction(ServiceRegistry serviceRegistry, AlfrescoContext filesysContext) throws DesktopActionException
{
- // Save the filesystem device and I/O handler
+ this.serviceRegistry = serviceRegistry;
- m_filesysDriver = filesysDriver;
+ // Save the filesystem device and I/O handler
m_filesysContext = filesysContext;
// Check for standard config values
diff --git a/source/java/org/alfresco/filesys/alfresco/DesktopParams.java b/source/java/org/alfresco/filesys/alfresco/DesktopParams.java
index e24b7f96fa..53fbad2f95 100644
--- a/source/java/org/alfresco/filesys/alfresco/DesktopParams.java
+++ b/source/java/org/alfresco/filesys/alfresco/DesktopParams.java
@@ -38,10 +38,6 @@ public class DesktopParams {
private SrvSession m_session;
- // Filesystem driver
-
- private AlfrescoDiskDriver m_driver;
-
// Folder node that the actions are working in
private NodeRef m_folderNode;
@@ -69,10 +65,9 @@ public class DesktopParams {
* @param folderNode NodeRef
* @param folderFile NetworkFile
*/
- public DesktopParams(SrvSession sess, AlfrescoDiskDriver driver, NodeRef folderNode, NetworkFile folderFile)
+ public DesktopParams(SrvSession sess, NodeRef folderNode, NetworkFile folderFile)
{
m_session = sess;
- m_driver = driver;
m_folderNode = folderNode;
m_folderFile = folderFile;
}
@@ -131,16 +126,6 @@ public class DesktopParams {
{
return m_folderFile;
}
-
- /**
- * Return the filesystem driver
- *
- * @return AlfrescoDiskDriver
- */
- public final AlfrescoDiskDriver getDriver()
- {
- return m_driver;
- }
/**
* Set the folder network file
diff --git a/source/java/org/alfresco/filesys/alfresco/PseudoFileImpl.java b/source/java/org/alfresco/filesys/alfresco/PseudoFileImpl.java
index 0d0685b0b0..14e58bfc0c 100644
--- a/source/java/org/alfresco/filesys/alfresco/PseudoFileImpl.java
+++ b/source/java/org/alfresco/filesys/alfresco/PseudoFileImpl.java
@@ -141,6 +141,8 @@ public class PseudoFileImpl implements PseudoFileInterface
/**
* Add pseudo files to a folder so that they appear in a folder search
+ *
+ * This implementation populates the file state cache.
*
* @param sess SrvSession
* @param tree TreeConnection
diff --git a/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlay.java b/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlay.java
new file mode 100644
index 0000000000..60d961a373
--- /dev/null
+++ b/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlay.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007-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.alfresco;
+
+import org.alfresco.jlan.server.filesys.pseudo.PseudoFile;
+import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+public interface PseudoFileOverlay
+{
+ /**
+ * Is this a pseudo file?
+ * @param path the path of the file
+ * @return true the file is a pseudo file
+ */
+ public boolean isPseudoFile(NodeRef parentDir, String name);
+
+ /**
+ * Get the pseudo file
+ * @param path the path of the file
+ * @return the pseudoFile or null if there is no pseudo file
+ */
+ public PseudoFile getPseudoFile(NodeRef parentDir, String name);
+
+ /**
+ * Search for the pseudo files on the specified path
+ * @param path the path
+ * @return list of pseudo files.
+ */
+ public PseudoFileList searchPseudoFiles(NodeRef parentDir, String name);
+
+}
diff --git a/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlayImpl.java b/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlayImpl.java
new file mode 100644
index 0000000000..1c6676b185
--- /dev/null
+++ b/source/java/org/alfresco/filesys/alfresco/PseudoFileOverlayImpl.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2007-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.alfresco;
+
+import java.util.Enumeration;
+
+import org.alfresco.filesys.repo.ContentDiskDriver2;
+import org.alfresco.jlan.server.filesys.FileName;
+import org.alfresco.jlan.server.filesys.pseudo.MemoryPseudoFile;
+import org.alfresco.jlan.server.filesys.pseudo.PseudoFile;
+import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList;
+import org.alfresco.jlan.util.WildCard;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.util.PropertyCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Re-implementation of PseudoFiles for ContentDiskDriver2
+ *
+ * Overlays "desktop actions"
+ *
+ * @author mrogers
+ * */
+public class PseudoFileOverlayImpl implements PseudoFileOverlay
+{
+ private AlfrescoContext context;
+ private NodeService nodeService;
+
+ private static final Log logger = LogFactory.getLog(PseudoFileOverlayImpl.class);
+
+ PseudoFileList pl = new PseudoFileList();
+
+ public void init()
+ {
+ PropertyCheck.mandatory(this, "nodeService", getNodeService());
+ PropertyCheck.mandatory(this, "context", context);
+
+ DesktopActionTable actions = context.getDesktopActions();
+
+ Enumeration actionNames = actions.enumerateActionNames();
+
+ while(actionNames.hasMoreElements())
+ {
+ // Get the current desktop action
+ String name = actionNames.nextElement();
+ DesktopAction action = actions.getAction(name);
+
+ // Add the pseudo file for the desktop action
+
+ if ( action.hasPseudoFile())
+ {
+ PseudoFile file = action.getPseudoFile();
+ pl.addFile(file);
+ }
+ }
+ }
+
+ private PseudoFile generateURLShortcut(NodeRef nodeRef)
+ {
+ if ( context.hasURLFile())
+ {
+ // Make sure the state has the associated node details
+
+ // Build the URL file data
+
+ StringBuilder urlStr = new StringBuilder();
+
+ urlStr.append("[InternetShortcut]\r\n");
+ urlStr.append("URL=");
+ urlStr.append(context.getURLPrefix());
+ urlStr.append("navigate/browse/workspace/SpacesStore/");
+ urlStr.append( nodeRef.getId());
+ urlStr.append("\r\n");
+
+ // Create the in memory pseudo file for the URL link
+
+ byte[] urlData = urlStr.toString().getBytes();
+
+ MemoryPseudoFile urlFile = new MemoryPseudoFile( context.getURLFileName(), urlData);
+ return urlFile;
+ }
+ return null;
+ }
+
+ /**
+ *
+ */
+ public boolean isPseudoFile(NodeRef parentDir, String name)
+ {
+ if ( parentDir == null)
+ {
+ return false;
+ }
+
+ if(getPseudoFile(parentDir, name) != null)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Get the pseudo file
+ * @param path
+ * @param nodeRef
+ * @return the pseudoFile or null if there is no pseudo file
+ */
+ public PseudoFile getPseudoFile(NodeRef parentDir, String fname)
+ {
+ if ( parentDir == null)
+ {
+ return null;
+ }
+
+ if(context.hasURLFile())
+ {
+ if(context.getURLFileName().equals(fname))
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("returning URL pseudo file");
+ }
+ return generateURLShortcut(parentDir);
+ }
+ }
+
+ PseudoFile file = pl.findFile(fname, false);
+ return file;
+ }
+
+ /**
+ *
+ */
+ public PseudoFileList searchPseudoFiles(NodeRef parentDir, String name)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("searchPseudoFile parentDir:" + parentDir +", name:" + name);
+ }
+ //return pseudo files matching the path/pattern
+
+ if ( parentDir == null || name == null || name.length() == 0 || name.equals("\\"))
+ {
+ return null;
+ }
+
+ String fname = name;
+
+ if ( fname.equals( "*.*"))
+ {
+ fname = "*";
+ }
+
+ if ( WildCard.containsWildcards(fname))
+ {
+ // does contain wildcards
+
+ // Check if the wildcard is for all files or a subset
+
+ if ( fname.equals( "*"))
+ {
+ // Match all pseudo files
+ PseudoFileList filterList = new PseudoFileList();
+
+ // copy desktop actions which do not depend on parentDir
+ for ( int i = 0; i < pl.numberOfFiles(); i++)
+ {
+ PseudoFile pseudoFile = pl.getFileAt(i);
+ filterList.addFile(pseudoFile);
+ }
+
+ // The URL file is dependent upon the parent dir
+ if(context.hasURLFile())
+ {
+ filterList.addFile(generateURLShortcut(parentDir));
+ }
+
+ return filterList;
+ }
+ else
+ {
+ // Generate a subset of pseudo files that match the wildcard search pattern
+
+ WildCard wildCard = new WildCard( fname, false);
+ PseudoFileList filterList = new PseudoFileList();
+
+ for ( int i = 0; i < pl.numberOfFiles(); i++)
+ {
+ PseudoFile pseudoFile = pl.getFileAt( i);
+ if ( wildCard.matchesPattern( pseudoFile.getFileName()))
+ {
+ // Add the pseudo file to the filtered list
+ filterList.addFile( pseudoFile);
+ }
+ }
+
+ // The URL file is dependent upon the parent dir
+ if(context.hasURLFile())
+ {
+ if(wildCard.matchesPattern(context.getURLFileName()))
+ {
+ filterList.addFile(generateURLShortcut(parentDir));
+ }
+ }
+
+ return filterList;
+ // Use the filtered pseudo file list, or null if there were no matches
+ }
+ }
+ else
+ {
+ // does not contain wild cards
+ PseudoFileList filterList = new PseudoFileList();
+ PseudoFile file = getPseudoFile(parentDir, fname);
+
+ if(file != null)
+ {
+ filterList.addFile(file);
+ }
+
+ return filterList;
+ }
+ }
+
+ //
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ public NodeService getNodeService()
+ {
+ return nodeService;
+ }
+
+ public void setContext(AlfrescoContext context)
+ {
+ this.context = context;
+ }
+
+ public AlfrescoContext getContext()
+ {
+ return context;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java b/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java
new file mode 100644
index 0000000000..878a43f612
--- /dev/null
+++ b/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java
@@ -0,0 +1,80 @@
+/*
+ * 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.alfresco;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Extra methods for DiskInterface, primarily implemented to support CIFS shuffles.
+ */
+public interface RepositoryDiskInterface
+{
+ /**
+ * Copy the content from one node to another.
+ *
+ * @param rootNode
+ * @param fromPath - the source node
+ * @param toPath - the target node
+ * @throws FileNotFoundException
+ */
+ public void copyContent(NodeRef rootNode, String fromPath, String toPath) throws FileNotFoundException;
+
+
+ /**
+ * CreateFile.
+ *
+ * @param rootNode
+ * @param fromPath - the source node
+ * @param toPath - the target node
+ * @throws FileNotFoundException
+ */
+ public NetworkFile createFile(NodeRef rootNode, String Path) throws IOException;
+
+ /**
+ * CloseFile.
+ *
+ * @param rootNode
+ * @param fromPath - the source node
+ * @param toPath - the target node
+ * @throws FileNotFoundException
+ */
+ public void closeFile(NodeRef rootNode, String Path, NetworkFile file) throws IOException;
+
+ /**
+ *
+ * @param session
+ * @param tree
+ * @param file
+ */
+ public void reduceQuota(SrvSession session, TreeConnection tree, NetworkFile file);
+
+ /**
+ *
+ * @param rootNode
+ * @param path
+ */
+ public void deleteEmptyFile(NodeRef rootNode, String path);
+
+}
diff --git a/source/java/org/alfresco/filesys/alfresco/ShuffleCache.java b/source/java/org/alfresco/filesys/alfresco/ShuffleCache.java
new file mode 100644
index 0000000000..7d7a7fd46d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/alfresco/ShuffleCache.java
@@ -0,0 +1,61 @@
+package org.alfresco.filesys.alfresco;
+
+/**
+ * Cache for alfresco "save shuffles" which are used by some applications
+ * to compensate for a most computer filesystem being non atomic.
+ *
+ *
+ * Overlays an Alfresco repository with temporary files being created and
+ * soft deleted from folders that are likely to have save shuffles going on.
+ *
+ * Implementations must be thread safe
+ */
+public interface ShuffleCache
+{
+
+ /**
+ * Add a new temporary file to the "shuffle cache". Content is not persisted
+ * in the alfresco repo until either a rename occurs or after a time delay.
+ *
+ * @return content writer?
+ */
+ public void createTemporaryFile(String path);
+
+ /**
+ * Soft delete a file. The file may be re-instated later or the delete made
+ * permenant after a time delay.
+ */
+ public void softDelete(String path);
+
+ /**
+ * Takes the contents of a temporary file and applies it to the new path.
+ *
+ * If the new path has been soft deleted then the soft delete is removed.
+ *
+ * After the contents of the temporary file have been written the it may may be made
+ * available for garbage collection.
+ *
+ * @param oldPath the location of the temporaryFile
+ * @param newPath the location of the new file.
+ */
+ public void renameTemporaryFile(String oldPath, String newPath);
+
+ /**
+ * Does the specified directory contain a shuffled temporary file
+ * @param dir
+ * @return
+ */
+ boolean isShuffleDirectory(String dir);
+
+ /**
+ * Has the path been "soft deleted"
+ */
+ boolean isDeleted(String path);
+
+ /**
+ * Has the path been "soft created"
+ * @param path
+ * @return
+ */
+ boolean isCreated(String path);
+}
diff --git a/source/java/org/alfresco/filesys/alfresco/ShuffleCacheImpl.java b/source/java/org/alfresco/filesys/alfresco/ShuffleCacheImpl.java
new file mode 100644
index 0000000000..6b1a20c320
--- /dev/null
+++ b/source/java/org/alfresco/filesys/alfresco/ShuffleCacheImpl.java
@@ -0,0 +1,101 @@
+package org.alfresco.filesys.alfresco;
+
+import java.util.Map;
+
+/**
+ * Cache for alfresco "CIFS shuffles"
+ *
+ *
+ */
+public class ShuffleCacheImpl implements ShuffleCache
+{
+ /**
+ * time in ms that temporary files should live in the cache before
+ * being persisted.
+ */
+ private long timeBeforePersist = 5 * 60000L; // 5 minutes default
+
+ /**
+ * Is the cache caseSensitive?
+ */
+ private boolean caseSensitive;
+
+ /**
+ * The shuffle folder cache keyed by path.
+ *
+ */
+ private Map folderCache;
+
+
+ /**
+ * The information held for each folder that has a "shuffle"
+ * in progress.
+ * @author mrogers
+ */
+ private class ShuffleFolderInfo
+ {
+
+ }
+
+ @Override
+ public void createTemporaryFile(String path)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void softDelete(String path)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void renameTemporaryFile(String oldPath, String newPath)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean isShuffleDirectory(String dir)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isDeleted(String path)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean isCreated(String path)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ void setTimeBeforePersist(long timeBeforePersist)
+ {
+ this.timeBeforePersist = timeBeforePersist;
+ }
+
+ long getTimeBeforePersist()
+ {
+ return timeBeforePersist;
+ }
+
+ public void setCaseSensitive(boolean caseSensitive)
+ {
+ this.caseSensitive = caseSensitive;
+ }
+
+ public boolean isCaseSensitive()
+ {
+ return caseSensitive;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java
index fe1a835a62..98f653c559 100644
--- a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java
+++ b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java
@@ -1,19 +1,19 @@
/*
- * 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
+ * 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.avm;
@@ -29,6 +29,7 @@ import javax.transaction.UserTransaction;
import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
import org.alfresco.filesys.config.ServerConfigurationBean;
+import org.alfresco.filesys.alfresco.AlfrescoTxDiskDriver;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.auth.ClientInfo;
import org.alfresco.jlan.server.core.DeviceContext;
@@ -88,7 +89,7 @@ import org.springframework.extensions.config.ConfigElement;
*
* @author GKSpencer
*/
-public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface
+public class AVMDiskDriver extends AlfrescoTxDiskDriver implements DiskInterface
{
// Logging
diff --git a/source/java/org/alfresco/filesys/repo/AlfrescoFolder.java b/source/java/org/alfresco/filesys/repo/AlfrescoFolder.java
new file mode 100644
index 0000000000..8fac5b0fa8
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/AlfrescoFolder.java
@@ -0,0 +1,93 @@
+package org.alfresco.filesys.repo;
+
+import java.io.IOException;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.jlan.server.filesys.FileInfo;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.jlan.server.filesys.cache.FileState;
+import org.alfresco.jlan.server.filesys.cache.NetworkFileStateInterface;
+
+/**
+ * Object returned to JLAN if the repository object is a folder.
+ */
+public class AlfrescoFolder extends NetworkFile implements NetworkFileStateInterface
+{
+ public AlfrescoFolder(String path, FileInfo fileInfo, boolean readOnly)
+ {
+ super(path);
+ setFullName(path);
+
+ // Set the file timestamps
+
+ if ( fileInfo.hasCreationDateTime())
+ setCreationDate( fileInfo.getCreationDateTime());
+
+ if ( fileInfo.hasModifyDateTime())
+ setModifyDate(fileInfo.getModifyDateTime());
+
+ if ( fileInfo.hasAccessDateTime())
+ setAccessDate(fileInfo.getAccessDateTime());
+
+ // Set the file attributes
+ setAttributes(fileInfo.getFileAttributes());
+ }
+
+ @Override
+ public void openFile(boolean createFlag) throws IOException
+ {
+ throw new AlfrescoRuntimeException("Unable to open channel for a directory network file: " + this);
+ }
+
+ @Override
+ public int readFile(byte[] buf, int len, int pos, long fileOff)
+ throws IOException
+ {
+ throw new AlfrescoRuntimeException("Unable to open channel for a directory network file: " + this);
+ }
+
+ @Override
+ public void writeFile(byte[] buf, int len, int pos, long fileOff)
+ throws IOException
+ {
+ throw new AlfrescoRuntimeException("Unable to open channel for a directory network file: " + this);
+ }
+
+ @Override
+ public long seekFile(long pos, int typ) throws IOException
+ {
+ return 0;
+ }
+
+ @Override
+ public void flushFile() throws IOException
+ {
+ // Do nothing.
+ }
+
+ @Override
+ public void truncateFile(long siz) throws IOException
+ {
+ throw new AlfrescoRuntimeException("Unable to open channel for a directory network file: " + this);
+ }
+
+ @Override
+ public void closeFile() throws IOException
+ {
+ setClosed(true);
+ }
+
+ // For JLAN file state lock manager
+ public void setFileState(FileState fileState)
+ {
+ this.fileState = fileState;
+ }
+
+ @Override
+ public FileState getFileState()
+ {
+ return fileState;
+
+ }
+ private FileState fileState;
+}
diff --git a/source/java/org/alfresco/filesys/repo/BufferedContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/BufferedContentDiskDriver.java
new file mode 100644
index 0000000000..8827140416
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/BufferedContentDiskDriver.java
@@ -0,0 +1,500 @@
+/*
+ * 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.FileNotFoundException;
+import java.io.IOException;
+import java.io.Serializable;
+
+import org.alfresco.filesys.config.ServerConfigurationBean;
+import org.alfresco.filesys.alfresco.ExtendedDiskInterface;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.core.DeviceContext;
+import org.alfresco.jlan.server.core.DeviceContextException;
+import org.alfresco.jlan.server.filesys.DiskDeviceContext;
+import org.alfresco.jlan.server.filesys.DiskInterface;
+import org.alfresco.jlan.server.filesys.DiskSizeInterface;
+import org.alfresco.jlan.server.filesys.FileInfo;
+import org.alfresco.jlan.server.filesys.FileOpenParams;
+import org.alfresco.jlan.server.filesys.FileStatus;
+import org.alfresco.jlan.server.filesys.IOControlNotImplementedException;
+import org.alfresco.jlan.server.filesys.IOCtlInterface;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.jlan.server.filesys.SearchContext;
+import org.alfresco.jlan.server.filesys.SrvDiskInfo;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.jlan.server.locking.OpLockInterface;
+import org.alfresco.jlan.server.locking.OpLockManager;
+import org.alfresco.jlan.smb.SMBException;
+import org.alfresco.jlan.util.DataBuffer;
+import org.alfresco.repo.cache.SimpleCache;
+import org.alfresco.repo.node.NodeServicePolicies;
+import org.alfresco.repo.policy.JavaBehaviour;
+import org.alfresco.repo.policy.PolicyComponent;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.PropertyCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.extensions.config.ConfigElement;
+
+/**
+ * Alfresco Content Disk Driver Cache
+ *
+ * Decorates ContentDiskDriver with a performance cache of some frequently used
+ * results. In particular for getFileInformation and fileExists
+ */
+public class BufferedContentDiskDriver implements ExtendedDiskInterface,
+ DiskInterface, DiskSizeInterface, IOCtlInterface, OpLockInterface, NodeServicePolicies.OnDeleteNodePolicy,
+ NodeServicePolicies.OnMoveNodePolicy
+{
+ // Logging
+ private static final Log logger = LogFactory.getLog(BufferedContentDiskDriver.class);
+
+ private ExtendedDiskInterface diskInterface;
+
+ private DiskSizeInterface diskSizeInterface;
+
+ private IOCtlInterface ioctlInterface;
+
+ private OpLockInterface opLockInterface;
+
+ private PolicyComponent policyComponent;
+
+ public void init()
+ {
+ PropertyCheck.mandatory(this, "diskInterface", diskInterface);
+ PropertyCheck.mandatory(this, "diskSizeInterface", diskSizeInterface);
+ PropertyCheck.mandatory(this, "ioctltInterface", ioctlInterface);
+ PropertyCheck.mandatory(this, "fileInfoCache", fileInfoCache);
+ PropertyCheck.mandatory(this, "opLockInterface", getOpLockInterface());
+ PropertyCheck.mandatory(this, "policyComponent", getPolicyComponent());
+
+ getPolicyComponent().bindClassBehaviour( NodeServicePolicies.OnDeleteNodePolicy.QNAME,
+ this, new JavaBehaviour(this, "onDeleteNode"));
+ getPolicyComponent().bindClassBehaviour( NodeServicePolicies.OnMoveNodePolicy.QNAME,
+ this, new JavaBehaviour(this, "onMoveNode"));
+ }
+
+
+
+ /**
+ * FileInfo Cache for path to FileInfo
+ */
+ private SimpleCache fileInfoCache;
+
+ /**
+ * Set the cache that maintains node ID-NodeRef cross referencing data
+ *
+ * @param cache the cache
+ */
+ public void setFileInfoCache(SimpleCache cache)
+ {
+ this.fileInfoCache = cache;
+ }
+
+ private class FileInfoKey implements Serializable
+ {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ String path;
+ String user;
+ int hashCode;
+
+ public FileInfoKey(String user, String path)
+ {
+ this.path = path;
+ this.user = user;
+ }
+
+ @Override
+ public boolean equals(Object other)
+ {
+ if (this == other)
+ {
+ return true;
+ }
+ if (other == null || !(other instanceof FileInfoKey))
+ {
+ return false;
+ }
+
+ FileInfoKey o = (FileInfoKey)other;
+
+ return path.equals(o.path) && user.equals(o.user);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ if(hashCode == 0)
+ {
+ hashCode = (user+path).hashCode();
+ }
+ return hashCode;
+ }
+ }
+
+ @Override
+ public FileInfo getFileInformation(SrvSession sess, TreeConnection tree,
+ String path) throws IOException
+ {
+ String userName = sess.getClientInformation().getUserName();
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("getFileInformation userName:" + userName + ", path:" + path);
+ }
+
+ if(path == null)
+ {
+ throw new IllegalArgumentException("Path is null");
+ }
+
+ FileInfoKey key = new FileInfoKey(userName, path);
+
+ FileInfo fromCache = fileInfoCache.get(key);
+
+ if(fromCache != null)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("returning FileInfo from cache");
+ }
+ return fromCache;
+ }
+
+ FileInfo info = diskInterface.getFileInformation(sess, tree, path);
+
+ if(info != null)
+ {
+ fileInfoCache.put(key, info);
+ }
+
+ /*
+ * Dual Key the cache so it can be looked up by NodeRef or Path
+ */
+ if(info instanceof ContentFileInfo)
+ {
+ ContentFileInfo cinfo = (ContentFileInfo)info;
+ fileInfoCache.put(cinfo.getNodeRef(), info);
+
+ }
+
+ return info;
+ }
+
+ @Override
+ public int fileExists(SrvSession sess, TreeConnection tree, String path)
+ {
+ String userName = sess.getClientInformation().getUserName();
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("fileExists userName:" + userName + ", path:" + path);
+ }
+
+ FileInfoKey key = new FileInfoKey(userName, path);
+
+ FileInfo fromCache = fileInfoCache.get(key);
+
+ if(fromCache != null)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("fileExists found FileInfo in cache");
+ }
+ if (fromCache.isDirectory())
+ {
+ return FileStatus.DirectoryExists;
+ }
+ else
+ {
+ return FileStatus.FileExists;
+ }
+ }
+ else
+ {
+ try
+ {
+ FileInfo lookup = getFileInformation(sess, tree, path);
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("fileExists obtained file information");
+ }
+ if (lookup.isDirectory())
+ {
+ return FileStatus.DirectoryExists;
+ }
+ else
+ {
+ return FileStatus.FileExists;
+ }
+ }
+ catch (IOException ie)
+ {
+ return FileStatus.NotExist;
+ }
+ }
+
+ // Not in cache - use the repo directly
+ //return diskInterface.fileExists(sess, tree, path);
+ }
+
+ @Override
+ public DeviceContext createContext(String shareName, ConfigElement args)
+ throws DeviceContextException
+ {
+ return diskInterface.createContext(shareName, args);
+ }
+
+ @Override
+ public void treeOpened(SrvSession sess, TreeConnection tree)
+ {
+ diskInterface.treeOpened(sess, tree);
+ }
+
+ @Override
+ public void treeClosed(SrvSession sess, TreeConnection tree)
+ {
+ diskInterface.treeClosed(sess, tree);
+ }
+
+ @Override
+ public DataBuffer processIOControl(SrvSession sess, TreeConnection tree,
+ int ctrlCode, int fid, DataBuffer dataBuf, boolean isFSCtrl,
+ int filter) throws IOControlNotImplementedException, SMBException
+ {
+ return ioctlInterface.processIOControl(sess, tree, ctrlCode, fid, dataBuf, isFSCtrl, filter);
+ }
+
+ @Override
+ public void getDiskInformation(DiskDeviceContext ctx, SrvDiskInfo diskDev)
+ throws IOException
+ {
+ diskSizeInterface.getDiskInformation(ctx, diskDev);
+ }
+
+ @Override
+ public void closeFile(SrvSession sess, TreeConnection tree,
+ NetworkFile param) throws IOException
+ {
+ diskInterface.closeFile(sess, tree, param);
+ }
+
+ @Override
+ public void createDirectory(SrvSession sess, TreeConnection tree,
+ FileOpenParams params) throws IOException
+ {
+ diskInterface.createDirectory(sess, tree, params);
+ }
+
+ @Override
+ public NetworkFile createFile(SrvSession sess, TreeConnection tree,
+ FileOpenParams params) throws IOException
+ {
+ return diskInterface.createFile(sess, tree, params);
+ }
+
+ @Override
+ public void deleteDirectory(SrvSession sess, TreeConnection tree, String dir)
+ throws IOException
+ {
+ fileInfoCache.remove(dir);
+
+ diskInterface.deleteDirectory(sess, tree, dir);
+ }
+
+ @Override
+ public void deleteFile(SrvSession sess, TreeConnection tree, String name)
+ throws IOException
+ {
+ fileInfoCache.remove(name);
+
+ diskInterface.deleteFile(sess, tree, name);
+ }
+
+ @Override
+ public void flushFile(SrvSession sess, TreeConnection tree, NetworkFile file)
+ throws IOException
+ {
+ diskInterface.flushFile(sess, tree, file);
+ }
+
+ @Override
+ public boolean isReadOnly(SrvSession sess, DeviceContext ctx)
+ throws IOException
+ {
+ return diskInterface.isReadOnly(sess, ctx);
+ }
+
+ @Override
+ public NetworkFile openFile(SrvSession sess, TreeConnection tree,
+ FileOpenParams params) throws IOException
+ {
+ return diskInterface.openFile(sess, tree, params);
+ }
+
+ @Override
+ public int readFile(SrvSession sess, TreeConnection tree, NetworkFile file,
+ byte[] buf, int bufPos, int siz, long filePos) throws IOException
+ {
+ return diskInterface.readFile(sess, tree, file, buf, bufPos, siz, filePos);
+ }
+
+ @Override
+ public void renameFile(SrvSession sess, TreeConnection tree,
+ String oldName, String newName) throws IOException
+ {
+ diskInterface.renameFile(sess, tree, oldName, newName);
+ }
+
+ @Override
+ public long seekFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, long pos, int typ) throws IOException
+ {
+ return diskInterface.seekFile(sess, tree, file, pos, typ);
+ }
+
+ @Override
+ public void setFileInformation(SrvSession sess, TreeConnection tree,
+ String name, FileInfo info) throws IOException
+ {
+ diskInterface.setFileInformation(sess, tree, name, info);
+ }
+
+ @Override
+ public SearchContext startSearch(SrvSession sess, TreeConnection tree,
+ String searchPath, int attrib) throws FileNotFoundException
+ {
+ return diskInterface.startSearch(sess, tree, searchPath, attrib);
+ }
+
+ @Override
+ public void truncateFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, long siz) throws IOException
+ {
+ diskInterface.truncateFile(sess, tree, file, siz);
+ }
+
+ @Override
+ public int writeFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, byte[] buf, int bufoff, int siz, long fileoff)
+ throws IOException
+ {
+ return diskInterface.writeFile(sess, tree, file, buf, bufoff, siz, fileoff);
+ }
+
+ @Override
+ public void registerContext(DeviceContext ctx, ServerConfigurationBean scb)
+ throws DeviceContextException
+ {
+ diskInterface.registerContext(ctx, scb);
+ }
+
+ public void setDiskInterface(ExtendedDiskInterface diskInterface)
+ {
+ this.diskInterface = diskInterface;
+ }
+
+ public ExtendedDiskInterface getDiskInterface()
+ {
+ return diskInterface;
+ }
+
+ public void setDiskSizeInterface(DiskSizeInterface diskSizeInterface)
+ {
+ this.diskSizeInterface = diskSizeInterface;
+ }
+
+ public DiskSizeInterface getDiskSizeInterface()
+ {
+ return diskSizeInterface;
+ }
+
+ public void setIoctlInterface(IOCtlInterface iocltlInterface)
+ {
+ this.ioctlInterface = iocltlInterface;
+ }
+
+ public IOCtlInterface getIoctlInterface()
+ {
+ return ioctlInterface;
+ }
+
+ @Override
+ public void onMoveNode(ChildAssociationRef oldChildAssocRef,
+ ChildAssociationRef newChildAssocRef)
+ {
+ if(fileInfoCache.contains(oldChildAssocRef.getChildRef()))
+ {
+ logger.debug("cached node moved - clear the cache");
+ fileInfoCache.clear();
+ }
+ }
+
+ @Override
+ public void onDeleteNode(ChildAssociationRef oldChildAssocRef, boolean isArchived)
+ {
+ if(fileInfoCache.contains(oldChildAssocRef.getChildRef()))
+ {
+ logger.debug("cached node deleted - clear the cache");
+ fileInfoCache.clear();
+ }
+ }
+
+ public void setPolicyComponent(PolicyComponent policyComponent)
+ {
+ this.policyComponent = policyComponent;
+ }
+
+ public PolicyComponent getPolicyComponent()
+ {
+ return policyComponent;
+ }
+
+ public void setOpLockInterface(OpLockInterface opLockInterface)
+ {
+ this.opLockInterface = opLockInterface;
+ }
+
+ public OpLockInterface getOpLockInterface()
+ {
+ return opLockInterface;
+ }
+
+ @Override
+ public OpLockManager getOpLockManager(SrvSession sess, TreeConnection tree)
+ {
+ return opLockInterface.getOpLockManager(sess, tree);
+ }
+
+ @Override
+ public boolean isOpLocksEnabled(SrvSession sess, TreeConnection tree)
+ {
+ return opLockInterface.isOpLocksEnabled(sess, tree);
+ }
+}
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/filesys/repo/CifsHelper.java b/source/java/org/alfresco/filesys/repo/CifsHelper.java
index 2076535b4f..041d35f1d4 100644
--- a/source/java/org/alfresco/filesys/repo/CifsHelper.java
+++ b/source/java/org/alfresco/filesys/repo/CifsHelper.java
@@ -38,6 +38,7 @@ import org.alfresco.model.ContentModel;
import org.alfresco.repo.model.filefolder.FileFolderServiceImpl;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.model.FileFolderUtil;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
@@ -69,21 +70,16 @@ public class CifsHelper
private FileFolderService fileFolderService;
private MimetypeService mimetypeService;
private PermissionService permissionService;
- private boolean isReadOnly;
- private boolean setReadOnlyFlagOnFolders;
-
- // Mark locked files as offline
-
- private boolean lockedFilesAsOffline;
-
+
private Set excludedTypes = new HashSet();
+ private boolean isReadOnlyFlagOnFolders = false;
+
/**
* Class constructor
*/
public CifsHelper()
{
- isReadOnly = false;
}
public void setDictionaryService(DictionaryService dictionaryService)
@@ -127,45 +123,7 @@ public class CifsHelper
this.excludedTypes.add(QName.createQName(exType));
}
}
-
- /**
- * @return Returns true if all files/folders should be treated as read-only
- */
- public boolean isReadOnly()
- {
- return isReadOnly;
- }
-
- /**
- * Set whether the system allows files to be edited or not. The default is
- * to allow writes.
- * @param allowWrites true to allow writes, otherwise false for read-only mode
- */
- public void setAllowWrites(boolean allowWrites)
- {
- this.isReadOnly = !allowWrites;
- }
-
- /**
- * Enable marking of locked files as offline
- *
- * @param ena boolean
- */
- public final void setMarkLockedFilesAsOffline(boolean ena)
- {
- lockedFilesAsOffline = ena;
- }
-
- /**
- * Check if locked files should be marked as offline
- *
- * @return boolean
- */
- public final boolean hasLockedFilesAsOffline()
- {
- return lockedFilesAsOffline;
- }
-
+
/**
* Controls whether the read only flag is set on folders. This flag, when set, may cause problematic # behaviour in
* Windows clients and doesn't necessarily mean a folder can't be written to. See ALF-6727. Should we ever set the
@@ -174,9 +132,9 @@ public class CifsHelper
* @param setReadOnlyFlagOnFolders
* the setReadOnlyFlagOnFolders to set
*/
- public void setSetReadOnlyFlagOnFolders(boolean setReadOnlyFlagOnFolders)
+ public void setReadOnlyFlagOnFolders(boolean setReadOnlyFlagOnFolders)
{
- this.setReadOnlyFlagOnFolders = setReadOnlyFlagOnFolders;
+ this.isReadOnlyFlagOnFolders = setReadOnlyFlagOnFolders;
}
/**
@@ -208,16 +166,16 @@ public class CifsHelper
* a path relative to an ancestor node.
*
* @param pathRootNodeRef
- * @param path
+ * @param path the path
* @return Returns the existing node reference
* @throws FileNotFoundException
*/
- public ContentFileInfo getFileInformation(NodeRef pathRootNodeRef, String path) throws FileNotFoundException
+ public ContentFileInfo getFileInformation(NodeRef pathRootNodeRef, String path, boolean readOnly, boolean lockedFilesAsOffline) throws FileNotFoundException
{
// get the node being referenced
NodeRef nodeRef = getNodeRef(pathRootNodeRef, path);
- return getFileInformation(nodeRef);
+ return getFileInformation(nodeRef, readOnly, lockedFilesAsOffline);
}
/**
@@ -226,19 +184,20 @@ public class CifsHelper
* This method goes direct to the repo for all information and no data is
* cached here.
*
- * @param nodeRef the node that the path is relative to
- * @param path the path to get info for
+ * @param nodeRef the node
+ * @param readOnly
+ * @param lockedFilesAsOffline
+ *
* @return Returns the file information pertinent to the node
* @throws FileNotFoundException if the path refers to a non-existent file
*/
- public ContentFileInfo getFileInformation(NodeRef nodeRef) throws FileNotFoundException
+ public ContentFileInfo getFileInformation(NodeRef nodeRef, boolean readOnly, boolean lockedFilesAsOffline) throws FileNotFoundException
{
// get the file info
org.alfresco.service.cmr.model.FileInfo fileFolderInfo = fileFolderService.getFileInfo(nodeRef);
- // retrieve required properties and create file info
- ContentFileInfo fileInfo = new ContentFileInfo();
- fileInfo.setNodeRef(nodeRef);
+ // retrieve required properties and create new JLAN file info
+ ContentFileInfo fileInfo = new ContentFileInfo(nodeRef);
// unset all attribute flags
int fileAttributes = 0;
@@ -282,8 +241,10 @@ public class CifsHelper
if (( attr & FileAttribute.ReadOnly) == 0)
attr += FileAttribute.ReadOnly;
- if ( hasLockedFilesAsOffline())
+ if ( lockedFilesAsOffline)
+ {
attr += FileAttribute.NTOffline;
+ }
fileInfo.setFileAttributes( attr);
}
@@ -318,10 +279,10 @@ public class CifsHelper
// Read/write access
- if (!fileFolderInfo.isFolder() || setReadOnlyFlagOnFolders)
+ if (!fileFolderInfo.isFolder() || isReadOnlyFlagOnFolders)
{
boolean deniedPermission = permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED;
- if (isReadOnly || deniedPermission)
+ if (readOnly || deniedPermission)
{
int attr = fileInfo.getFileAttributes();
if (( attr & FileAttribute.ReadOnly) == 0)
@@ -387,7 +348,7 @@ public class CifsHelper
NodeRef parentFolderNodeRef = rootNodeRef;
if (folderPathElements.size() > 0)
{
- parentFolderNodeRef = FileFolderServiceImpl.makeFolders(
+ parentFolderNodeRef = FileFolderUtil.makeFolders(
fileFolderService,
rootNodeRef,
folderPathElements,
@@ -724,7 +685,9 @@ public class CifsHelper
List files = fileFolderService.listFiles( folderNode);
if ( files == null || files.size() == 0)
+ {
return true;
+ }
return false;
}
}
diff --git a/source/java/org/alfresco/filesys/repo/CommandExecutor.java b/source/java/org/alfresco/filesys/repo/CommandExecutor.java
new file mode 100644
index 0000000000..dc59f2b192
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/CommandExecutor.java
@@ -0,0 +1,40 @@
+/*
+ * 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.filesys.repo.rules.Command;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+
+/**
+ * The Command Executor - executes commands!
+ */
+public interface CommandExecutor
+{
+ /**
+ * Execute the command.
+ * @param command
+ *
+ * @return an object for return or null if there is no return value.
+ * @throws IOException
+ */
+ public Object execute(SrvSession sess, TreeConnection tree, Command command) throws IOException;
+}
diff --git a/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java b/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java
new file mode 100644
index 0000000000..0cf5d58aec
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java
@@ -0,0 +1,341 @@
+package org.alfresco.filesys.repo;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.alfresco.filesys.alfresco.ExtendedDiskInterface;
+import org.alfresco.filesys.alfresco.RepositoryDiskInterface;
+import org.alfresco.filesys.repo.FilesystemTransactionAdvice.PropagatingException;
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.filesys.repo.rules.Operation;
+import org.alfresco.filesys.repo.rules.OperationExecutor;
+import org.alfresco.filesys.repo.rules.commands.CloseFileCommand;
+import org.alfresco.filesys.repo.rules.commands.CompoundCommand;
+import org.alfresco.filesys.repo.rules.commands.CopyContentCommand;
+import org.alfresco.filesys.repo.rules.commands.CreateFileCommand;
+import org.alfresco.filesys.repo.rules.commands.DeleteFileCommand;
+import org.alfresco.filesys.repo.rules.commands.DoNothingCommand;
+import org.alfresco.filesys.repo.rules.commands.OpenFileCommand;
+import org.alfresco.filesys.repo.rules.commands.ReduceQuotaCommand;
+import org.alfresco.filesys.repo.rules.commands.RemoveNoContentFileOnError;
+import org.alfresco.filesys.repo.rules.commands.RemoveTempFileCommand;
+import org.alfresco.filesys.repo.rules.commands.RenameFileCommand;
+import org.alfresco.filesys.repo.rules.commands.ReturnValueCommand;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.alfresco.filesys.repo.rules.operations.DeleteFileOperation;
+import org.alfresco.filesys.repo.rules.operations.RenameFileOperation;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.filesys.AccessMode;
+import org.alfresco.jlan.server.filesys.FileAction;
+import org.alfresco.jlan.server.filesys.FileAttribute;
+import org.alfresco.jlan.server.filesys.FileOpenParams;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.PropertyCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+
+/**
+ * Content Disk Driver Command Executor
+ *
+ * Executes commands against the repository.
+ */
+public class CommandExecutorImpl implements CommandExecutor
+{
+ private static Log logger = LogFactory.getLog(CommandExecutorImpl.class);
+
+ // Services go here.
+ private TransactionService transactionService;
+ private RepositoryDiskInterface repositoryDiskInterface;
+ private ExtendedDiskInterface diskInterface;
+
+
+ public void init()
+ {
+ PropertyCheck.mandatory(this, "transactionService", transactionService);
+ PropertyCheck.mandatory(this, "diskInterface", diskInterface);
+ PropertyCheck.mandatory(this, "repositoryDiskInterface", getRepositoryDiskInterface());
+ }
+
+ @Override
+ public Object execute(final SrvSession sess, final TreeConnection tree, final Command command) throws IOException
+ {
+ TxnReadState readState = command.getTransactionRequired();
+
+ Object ret = null;
+
+ // No transaction required.
+ if(readState == TxnReadState.TXN_NONE)
+ {
+ ret = executeInternal(sess, tree, command, null);
+ }
+ else
+ {
+ // Yes a transaction is required.
+ RetryingTransactionHelper helper = transactionService.getRetryingTransactionHelper();
+
+ boolean readOnly = readState == TxnReadState.TXN_READ_ONLY;
+
+ RetryingTransactionCallback cb = new RetryingTransactionCallback()
+ {
+ /**
+ * Perform a set of commands as a unit of transactional work.
+ *
+ * @return Return the result of the unit of work
+ * @throws Throwable This can be anything and will guarantee either a retry or a rollback
+ */
+ public Object execute() throws IOException
+ {
+ try
+ {
+ return executeInternal(sess, tree, command, null);
+ }
+ catch (IOException e)
+ {
+ // Ensure original checked IOExceptions get propagated
+ throw new PropagatingException(e);
+ }
+ }
+ };
+
+ try
+ {
+ ret = helper.doInTransaction(cb, readOnly);
+ }
+ catch(PropagatingException pe)
+ {
+ if(command instanceof CompoundCommand)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("post error", pe);
+ }
+
+ CompoundCommand c = (CompoundCommand)command;
+ // Error Callback Here ?
+ List commands = c.getPostErrorCommands();
+
+ if(commands != null)
+ {
+ for(Command c2 : commands)
+ {
+ try
+ {
+ executeInternal(sess, tree, c2, ret);
+ }
+ catch(Throwable t)
+ {
+ logger.warn("caught and ignored exception from error handler", t);
+ // Swallow exception from error handler.
+ }
+ }
+ }
+ }
+
+ // Unwrap checked exceptions
+ throw (IOException) pe.getCause();
+ }
+ }
+
+ /**
+ * execute post commit commands.
+ */
+ if(command instanceof CompoundCommand)
+ {
+ logger.debug("post Commit");
+ CompoundCommand c = (CompoundCommand)command;
+ List commands = c.getPostCommitCommands();
+
+ if(commands != null)
+ {
+ for(Command c2 : commands)
+ {
+ // TODO - what about exceptions from post commit?
+ executeInternal(sess, tree, c2, ret);
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * @param sess
+ * @param tree
+ * @param command
+ * @param result
+ * @return
+ * @throws IOException
+ */
+ private Object executeInternal(SrvSession sess, TreeConnection tree, Command command, Object result) throws IOException
+ {
+ if(command instanceof CompoundCommand)
+ {
+ Object ret = null;
+ logger.debug("compound command received");
+ CompoundCommand x = (CompoundCommand)command;
+
+ for(Command compoundPart : x.getCommands())
+ {
+ logger.debug("running part of compound command");
+ Object val = executeInternal(sess, tree, compoundPart, result);
+ if(val != null)
+ {
+ // Return the value from the last command.
+ ret = val;
+ }
+ }
+ return ret;
+ }
+ else if(command instanceof CreateFileCommand)
+ {
+ logger.debug("create file command");
+ CreateFileCommand create = (CreateFileCommand)command;
+ return repositoryDiskInterface.createFile(create.getRootNode(), create.getPath());
+ }
+ else if(command instanceof DeleteFileCommand)
+ {
+ logger.debug("delete file command");
+ DeleteFileCommand delete = (DeleteFileCommand)command;
+ diskInterface.deleteFile(sess, tree, delete.getPath());
+ }
+ else if(command instanceof OpenFileCommand)
+ {
+ logger.debug("open file command");
+ OpenFileCommand o = (OpenFileCommand)command;
+ int openAction = FileAction.OpenIfExists;
+
+ // TODO Open Action FileAction.NTOverwrite o.truncate
+ FileOpenParams params = new FileOpenParams(o.getPath(), openAction, o.isWriteAccess() ? AccessMode.ReadWrite : AccessMode.ReadOnly, FileAttribute.NTNormal, 0);
+
+ return diskInterface.openFile(sess, tree, params);
+ }
+ else if(command instanceof CloseFileCommand)
+ {
+ logger.debug("close file command");
+ CloseFileCommand c = (CloseFileCommand)command;
+ repositoryDiskInterface.closeFile(c.getRootNodeRef(), c.getPath(), c.getNetworkFile());
+ }
+ else if(command instanceof ReduceQuotaCommand)
+ {
+ logger.debug("reduceQuota file command");
+ ReduceQuotaCommand r = (ReduceQuotaCommand)command;
+ repositoryDiskInterface.reduceQuota(sess, tree, r.getNetworkFile());
+ }
+ else if(command instanceof RenameFileCommand)
+ {
+ logger.debug("rename command");
+ RenameFileCommand rename = (RenameFileCommand)command;
+ diskInterface.renameFile(sess, tree, rename.getFromPath(), rename.getToPath());
+ }
+ else if(command instanceof CopyContentCommand)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Copy content command - copy content");
+ }
+ CopyContentCommand copy = (CopyContentCommand)command;
+ repositoryDiskInterface.copyContent(copy.getRootNode(), copy.getFromPath(), copy.getToPath());
+ }
+ else if(command instanceof DoNothingCommand)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Do Nothing Command - doing nothing");
+ }
+ }
+ else if(command instanceof ResultCallback)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Result Callback");
+ }
+ ResultCallback callback = (ResultCallback)command;
+ callback.execute(result);
+ }
+ else if(command instanceof RemoveTempFileCommand)
+ {
+ RemoveTempFileCommand r = (RemoveTempFileCommand)command;
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Remove Temp File:" + r.getNetworkFile());
+ }
+ File file = r.getNetworkFile().getFile();
+ file.delete();
+ }
+ else if(command instanceof ReturnValueCommand)
+ {
+ ReturnValueCommand r = (ReturnValueCommand)command;
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Return value");
+ }
+ return r.getReturnValue();
+ }
+ else if(command instanceof RemoveNoContentFileOnError)
+ {
+ RemoveNoContentFileOnError r = (RemoveNoContentFileOnError)command;
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Remove no content file on error");
+ }
+ repositoryDiskInterface.deleteEmptyFile(r.getRootNodeRef(), r.getPath());
+ }
+
+
+ return null;
+ }
+
+
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
+
+ public TransactionService getTransactionService()
+ {
+ return transactionService;
+ }
+
+ public void setRepositoryDiskInterface(RepositoryDiskInterface repositoryDiskInterface)
+ {
+ this.repositoryDiskInterface = repositoryDiskInterface;
+ }
+
+ public RepositoryDiskInterface getRepositoryDiskInterface()
+ {
+ return repositoryDiskInterface;
+ }
+
+ public void setDiskInterface(ExtendedDiskInterface diskInterface)
+ {
+ this.diskInterface = diskInterface;
+ }
+
+ public ExtendedDiskInterface getDiskInterface()
+ {
+ return diskInterface;
+ }
+
+ /**
+ * A wrapper for checked exceptions to be passed through the retrying transaction handler.
+ */
+ protected static class PropagatingException extends RuntimeException
+ {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @param cause
+ */
+ public PropagatingException(Throwable cause)
+ {
+ super(cause);
+ }
+ }
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/ContentContext.java b/source/java/org/alfresco/filesys/repo/ContentContext.java
index 3ece9b04cb..c7e1e4fb00 100644
--- a/source/java/org/alfresco/filesys/repo/ContentContext.java
+++ b/source/java/org/alfresco/filesys/repo/ContentContext.java
@@ -24,6 +24,7 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.alfresco.AlfrescoContext;
import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
import org.alfresco.filesys.alfresco.IOControlHandler;
+import org.alfresco.filesys.alfresco.PseudoFileOverlay;
import org.alfresco.filesys.config.acl.AccessControlListBean;
import org.alfresco.jlan.server.config.CoreServerConfigSection;
import org.alfresco.jlan.server.core.DeviceContextException;
@@ -72,6 +73,8 @@ public class ContentContext extends AlfrescoContext
// Node monitor
private NodeMonitor m_nodeMonitor;
+
+ private PseudoFileOverlay m_PseudoFileOverlay;
// Thread pool
@@ -102,11 +105,8 @@ public class ContentContext extends AlfrescoContext
* NodeRef
*/
public ContentContext(String filesysName, String storeName, String rootPath, NodeRef rootNodeRef)
- {
- this();
-
+ {
setDeviceName(filesysName);
-
setStoreName(storeName);
setRootPath(rootPath);
setRootNodeRef(rootNodeRef);
@@ -389,4 +389,14 @@ public class ContentContext extends AlfrescoContext
if ( m_nodeMonitor != null)
m_nodeMonitor.startMonitor();
}
+
+ public void setPseudoFileOverlay(PseudoFileOverlay pseudoFileOverlay)
+ {
+ this.m_PseudoFileOverlay = pseudoFileOverlay;
+ }
+
+ public PseudoFileOverlay getPseudoFileOverlay()
+ {
+ return m_PseudoFileOverlay;
+ }
}
diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskCallback.java b/source/java/org/alfresco/filesys/repo/ContentDiskCallback.java
new file mode 100644
index 0000000000..7cf2369434
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/ContentDiskCallback.java
@@ -0,0 +1,76 @@
+package org.alfresco.filesys.repo;
+
+import java.io.IOException;
+
+import org.alfresco.filesys.config.ServerConfigurationBean;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.core.DeviceContext;
+import org.alfresco.jlan.server.core.DeviceContextException;
+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.server.filesys.SearchContext;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+
+/**
+ * Called by the NonTransactionalContentDiskDriver to advise of operations completed
+ * by the ContentDiskInterface.
+ */
+public interface ContentDiskCallback
+{
+ public void getFileInformation(SrvSession sess, TreeConnection tree,
+ String path, FileInfo info);
+
+ public void fileExists(SrvSession sess, TreeConnection tree, String path, int fileExists);
+
+ public void treeOpened(SrvSession sess, TreeConnection tree);
+
+ public void treeClosed(SrvSession sess, TreeConnection tree);
+
+ public void closeFile(SrvSession sess, TreeConnection tree,
+ NetworkFile param);
+
+ public void createDirectory(SrvSession sess, TreeConnection tree,
+ FileOpenParams params);
+
+ public void createFile(SrvSession sess, TreeConnection tree,
+ FileOpenParams params, NetworkFile newFile);
+
+
+ public void deleteDirectory(SrvSession sess, TreeConnection tree, String dir);
+
+ public void deleteFile(SrvSession sess, TreeConnection tree, String name);
+
+ public void flushFile(SrvSession sess, TreeConnection tree, NetworkFile file);
+
+ public void isReadOnly(SrvSession sess, DeviceContext ctx, boolean isReadOnly);
+
+ public void openFile(SrvSession sess, TreeConnection tree,
+ FileOpenParams param, NetworkFile openFile);
+
+ public void readFile(SrvSession sess, TreeConnection tree, NetworkFile file,
+ byte[] buf, int bufPos, int siz, long filePos, int readSize);
+
+ public void renameFile(SrvSession sess, TreeConnection tree,
+ String oldPath, String newPath);
+
+ public void seekFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, long pos, int typ) throws IOException;
+
+ public void setFileInformation(SrvSession sess, TreeConnection tree,
+ String name, FileInfo info) throws IOException;
+
+ public void startSearch(SrvSession sess, TreeConnection tree,
+ String searchPath, int attrib, SearchContext context);
+
+ public void truncateFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, long siz);
+
+
+ public void writeFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, byte[] buf, int bufoff, int siz, long fileoff, int writeSize);
+
+ public void registerContext(DeviceContext ctx, ServerConfigurationBean serverConfig)
+ throws DeviceContextException;
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
index ead78e9327..358ecb1a16 100644
--- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
+++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
@@ -40,6 +40,8 @@ import org.alfresco.filesys.alfresco.AlfrescoContext;
import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
import org.alfresco.filesys.alfresco.AlfrescoNetworkFile;
import org.alfresco.filesys.config.ServerConfigurationBean;
+import org.alfresco.filesys.alfresco.AlfrescoTxDiskDriver;
+import org.alfresco.filesys.alfresco.ShuffleCache;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.core.DeviceContext;
import org.alfresco.jlan.server.core.DeviceContextException;
@@ -113,6 +115,7 @@ import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
+import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.config.ConfigElement;
@@ -124,7 +127,7 @@ import org.springframework.extensions.config.ConfigElement;
*
* @author gkspencer
*/
-public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterface, FileLockingInterface, OpLockInterface, DiskSizeInterface
+public class ContentDiskDriver extends AlfrescoTxDiskDriver implements DiskInterface, FileLockingInterface, OpLockInterface, DiskSizeInterface
{
// Logging
private static final Log logger = LogFactory.getLog(ContentDiskDriver.class);
@@ -173,6 +176,9 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
protected static final long DiskSizeDefault = 1 * MemorySize.TERABYTE;
protected static final long DiskFreeDefault = DiskSizeDefault / 2;
+ private boolean isReadOnly;
+ private boolean isLockedFilesAsOffline;
+
// Services and helpers
private CifsHelper cifsHelper;
@@ -207,6 +213,11 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
{
this.cifsHelper = cifsHelper;
}
+
+ public void init()
+ {
+ PropertyCheck.mandatory(this, "nodeService", nodeService);
+ }
/**
* Return the CIFS helper
@@ -759,8 +770,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
if ( context.getOfflineFiles() )
{
// Enable marking locked files as offline
-
- cifsHelper.setMarkLockedFilesAsOffline( true);
+ isLockedFilesAsOffline = true;
// Logging
@@ -782,7 +792,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
// Create the node monitor
- NodeMonitor nodeMonitor = m_nodeMonitorFactory.createNodeMonitor( this, context);
+ NodeMonitor nodeMonitor = m_nodeMonitorFactory.createNodeMonitor(context);
context.setNodeMonitor( nodeMonitor);
}
@@ -844,14 +854,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
*/
public boolean isReadOnly(SrvSession sess, DeviceContext ctx) throws IOException
{
- if (cifsHelper.isReadOnly())
- {
- return true;
- }
- else
- {
- return false;
- }
+ return isReadOnly;
}
/**
@@ -865,6 +868,10 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
*/
public FileInfo getFileInformation(SrvSession session, TreeConnection tree, String path) throws IOException
{
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("getFileInformation:" + path);
+ }
// Start a transaction
beginReadTransaction( session);
@@ -900,7 +907,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
{
// Get the file information for the node
- finfo = cifsHelper.getFileInformation(nodeRef);
+ finfo = cifsHelper.getFileInformation(nodeRef, isReadOnly, isLockedFilesAsOffline);
}
// Create the file state
@@ -951,7 +958,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
logger.debug("getInfo using pseudo file info for " + path);
FileInfo pseudoFileInfo = pfile.getFileInfo();
- if (cifsHelper.isReadOnly())
+ if (isReadOnly)
{
int attr = pseudoFileInfo.getFileAttributes();
if (( attr & FileAttribute.ReadOnly) == 0)
@@ -972,7 +979,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
{
// Get the file information for the node
- finfo = cifsHelper.getFileInformation(nodeRef);
+ finfo = cifsHelper.getFileInformation(nodeRef, isReadOnly, isLockedFilesAsOffline);
// DEBUG
@@ -1007,7 +1014,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
// Access the repository to get the file information
- finfo = cifsHelper.getFileInformation(infoParentNodeRef, infoPath);
+ finfo = cifsHelper.getFileInformation(infoParentNodeRef, infoPath, isReadOnly, isLockedFilesAsOffline);
// DEBUG
@@ -1106,6 +1113,10 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
public SearchContext startSearch(SrvSession sess, TreeConnection tree, String searchPath, int attributes) throws FileNotFoundException
{
// Access the device context
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("startSearch: "+ searchPath);
+ }
ContentContext ctx = (ContentContext) tree.getContext();
@@ -1277,7 +1288,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
{
// Get the '.' pseudo entry file details
- FileInfo finfo = cifsHelper.getFileInformation((NodeRef) searchFolderState.getFilesystemObject());
+ FileInfo finfo = cifsHelper.getFileInformation((NodeRef) searchFolderState.getFilesystemObject(), isReadOnly, isLockedFilesAsOffline);
// Blend in any cached timestamps
@@ -1332,7 +1343,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
// Get the file information for the parent folder
- finfo = cifsHelper.getFileInformation( parentNode);
+ finfo = cifsHelper.getFileInformation( parentNode, isReadOnly, isLockedFilesAsOffline);
// Blend in any cached timestamps
@@ -1573,7 +1584,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
* @return NetworkFile
* @exception IOException
*/
- public NetworkFile openFile(SrvSession sess, TreeConnection tree, FileOpenParams params) throws IOException
+ public NetworkFile openFile(SrvSession sess, TreeConnection tree, FileOpenParams params ) throws IOException
{
// Create the transaction
@@ -1904,7 +1915,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
// Get the file information for the link node
- FileInfo fInfo = cifsHelper.getFileInformation( nodeRef);
+ FileInfo fInfo = cifsHelper.getFileInformation( nodeRef, isReadOnly, isLockedFilesAsOffline);
// Set the file size to the actual data length
@@ -2301,7 +2312,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
// Convert to a general I/O exception
- throw new IOException("Create directory " + params.getFullPath());
+ throw new IOException("Create directory " + params.getFullPath(), ex);
}
}
@@ -3507,7 +3518,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
if ( fstate != null)
isFolder = fstate.isDirectory();
else {
- ContentFileInfo cInfo = cifsHelper.getFileInformation( nodeRef);
+ ContentFileInfo cInfo = cifsHelper.getFileInformation( nodeRef, isReadOnly, isLockedFilesAsOffline);
if ( cInfo != null && cInfo.isDirectory() == false)
isFolder = false;
}
diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java
new file mode 100644
index 0000000000..2d39e6a499
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java
@@ -0,0 +1,3452 @@
+/*
+ * 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.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.nio.channels.Channels;
+import java.nio.charset.Charset;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.filesys.alfresco.AlfrescoClientInfo;
+import org.alfresco.filesys.alfresco.AlfrescoContext;
+import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
+import org.alfresco.filesys.alfresco.AlfrescoNetworkFile;
+import org.alfresco.filesys.alfresco.DesktopAction;
+import org.alfresco.filesys.alfresco.DesktopActionTable;
+import org.alfresco.filesys.alfresco.DesktopParams;
+import org.alfresco.filesys.alfresco.DesktopResponse;
+import org.alfresco.filesys.alfresco.DesktopTarget;
+import org.alfresco.filesys.alfresco.ExtendedDiskInterface;
+import org.alfresco.filesys.alfresco.IOControl;
+import org.alfresco.filesys.alfresco.PseudoFileOverlayImpl;
+import org.alfresco.filesys.alfresco.RepositoryDiskInterface;
+import org.alfresco.filesys.alfresco.ShuffleCache;
+import org.alfresco.filesys.config.ServerConfigurationBean;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.core.DeviceContext;
+import org.alfresco.jlan.server.core.DeviceContextException;
+import org.alfresco.jlan.server.filesys.AccessDeniedException;
+import org.alfresco.jlan.server.filesys.AccessMode;
+import org.alfresco.jlan.server.filesys.DirectoryNotEmptyException;
+import org.alfresco.jlan.server.filesys.DiskDeviceContext;
+import org.alfresco.jlan.server.filesys.DiskFullException;
+import org.alfresco.jlan.server.filesys.DiskInterface;
+import org.alfresco.jlan.server.filesys.DiskSizeInterface;
+import org.alfresco.jlan.server.filesys.FileAttribute;
+import org.alfresco.jlan.server.filesys.FileInfo;
+import org.alfresco.jlan.server.filesys.FileName;
+import org.alfresco.jlan.server.filesys.FileOpenParams;
+import org.alfresco.jlan.server.filesys.FileStatus;
+import org.alfresco.jlan.server.filesys.IOControlNotImplementedException;
+import org.alfresco.jlan.server.filesys.IOCtlInterface;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.jlan.server.filesys.SearchContext;
+import org.alfresco.jlan.server.filesys.SrvDiskInfo;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.jlan.server.filesys.cache.FileState;
+import org.alfresco.jlan.server.filesys.cache.FileStateLockManager;
+import org.alfresco.jlan.server.filesys.pseudo.MemoryPseudoFile;
+import org.alfresco.jlan.server.filesys.pseudo.PseudoFile;
+import org.alfresco.jlan.server.filesys.pseudo.PseudoFileInterface;
+import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList;
+import org.alfresco.jlan.server.filesys.pseudo.PseudoNetworkFile;
+import org.alfresco.jlan.server.filesys.quota.QuotaManager;
+import org.alfresco.jlan.server.filesys.quota.QuotaManagerException;
+import org.alfresco.jlan.server.locking.LockManager;
+import org.alfresco.jlan.server.locking.OpLockInterface;
+import org.alfresco.jlan.server.locking.OpLockManager;
+import org.alfresco.jlan.smb.SMBException;
+import org.alfresco.jlan.smb.SMBStatus;
+import org.alfresco.jlan.smb.nt.NTIOCtl;
+import org.alfresco.jlan.smb.server.SMBServer;
+import org.alfresco.jlan.smb.server.SMBSrvSession;
+import org.alfresco.jlan.smb.server.disk.JavaNetworkFile;
+import org.alfresco.jlan.server.filesys.FileAttribute;
+import org.alfresco.jlan.util.DataBuffer;
+import org.alfresco.jlan.util.MemorySize;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.admin.SysAdminParams;
+import org.alfresco.repo.content.MimetypeMap;
+import org.alfresco.repo.content.encoding.ContentCharsetFinder;
+import org.alfresco.repo.content.filestore.FileContentReader;
+import org.alfresco.repo.node.archive.NodeArchiveService;
+import org.alfresco.repo.policy.BehaviourFilter;
+import org.alfresco.repo.security.authentication.AuthenticationContext;
+import org.alfresco.repo.security.authentication.AuthenticationException;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.service.cmr.action.ActionService;
+import org.alfresco.service.cmr.coci.CheckOutCheckInService;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.lock.LockService;
+import org.alfresco.service.cmr.lock.LockType;
+import org.alfresco.service.cmr.lock.NodeLockedException;
+import org.alfresco.service.cmr.model.FileFolderService;
+import org.alfresco.service.cmr.repository.ContentData;
+import org.alfresco.service.cmr.repository.ContentIOException;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.ContentWriter;
+import org.alfresco.service.cmr.repository.MimetypeService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
+import org.alfresco.service.cmr.search.SearchService;
+import org.alfresco.service.cmr.security.AccessStatus;
+import org.alfresco.service.cmr.security.AuthenticationService;
+import org.alfresco.service.cmr.security.OwnableService;
+import org.alfresco.service.cmr.security.PermissionService;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.PropertyCheck;
+import org.alfresco.util.TempFileProvider;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.james.mime4j.storage.TempFileStorageProvider;
+import org.springframework.extensions.config.ConfigElement;
+
+/**
+ * Alfresco Content repository filesystem driver class
+ *
+ * Provides a JLAN ContentDiskDriver for various JLAN protocols
+ * such as SMB/CIFS, NFS and FTP.
+ *
+ */
+public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedDiskInterface,
+ DiskInterface, DiskSizeInterface, IOCtlInterface, RepositoryDiskInterface, OpLockInterface
+{
+ // Logging
+ private static final Log logger = LogFactory.getLog(ContentDiskDriver2.class);
+
+ private static final Log readLogger = LogFactory.getLog("org.alfresco.filesys.repo.ContentDiskDriver2.Read");
+
+ // Services and helpers
+ private CifsHelper cifsHelper;
+ private NamespaceService namespaceService;
+ private NodeService nodeService;
+ private SearchService searchService;
+ private ContentService contentService;
+ private MimetypeService mimetypeService;
+ private PermissionService permissionService;
+ private FileFolderService fileFolderService;
+ private LockService lockService;
+ private CheckOutCheckInService checkOutCheckInService;
+ private AuthenticationContext authContext;
+ private AuthenticationService authService;
+ private BehaviourFilter policyBehaviourFilter;
+ private NodeMonitorFactory m_nodeMonitorFactory;
+
+ private FileStateLockManager lockManager;
+
+ private boolean isLockedFilesAsOffline;
+
+ /**
+ *
+ */
+ public void init()
+ {
+ PropertyCheck.mandatory(this, "checkOutCheckInService", checkOutCheckInService);
+// PropertyCheck.mandatory(this, "shuffleCache", shuffleCache);
+ PropertyCheck.mandatory(this, "cifsHelper", cifsHelper);
+ PropertyCheck.mandatory(this, "namespaceService", namespaceService);
+ PropertyCheck.mandatory(this, "nodeService", nodeService);
+ PropertyCheck.mandatory(this, "searchService", searchService);
+ PropertyCheck.mandatory(this, "contentService", contentService);
+ PropertyCheck.mandatory(this, "mimetypeService", mimetypeService);
+ PropertyCheck.mandatory(this, "permissionService", permissionService);
+ PropertyCheck.mandatory(this, "fileFolderService", fileFolderService);
+ PropertyCheck.mandatory(this, "lockService",lockService);
+ PropertyCheck.mandatory(this, "authContext", authContext);
+ PropertyCheck.mandatory(this, "authService", authService);
+ PropertyCheck.mandatory(this, "policyBehaviourFilter", policyBehaviourFilter);
+ PropertyCheck.mandatory(this, "m_nodeMonitorFactory", m_nodeMonitorFactory);
+ }
+
+ /**
+ * Return the CIFS helper
+ *
+ * @return CifsHelper
+ */
+ public final CifsHelper getCifsHelper()
+ {
+ return this.cifsHelper;
+ }
+
+ /**
+ * Return the authentication service
+ *
+ * @return AuthenticationService
+ */
+ public final AuthenticationService getAuthenticationService()
+ {
+ return authService;
+ }
+
+ /**
+ * Return the authentication context
+ *
+ * @return AuthenticationContext
+ */
+ public final AuthenticationContext getAuthenticationContext() {
+ return authContext;
+ }
+
+ /**
+ * Return the node service
+ *
+ * @return NodeService
+ */
+ public final NodeService getNodeService()
+ {
+ return this.nodeService;
+ }
+
+ /**
+ * Return the content service
+ *
+ * @return ContentService
+ */
+ public final ContentService getContentService()
+ {
+ return this.contentService;
+ }
+
+ /**
+ * Return the namespace service
+ *
+ * @return NamespaceService
+ */
+ public final NamespaceService getNamespaceService()
+ {
+ return this.namespaceService;
+ }
+
+ /**
+ * Return the search service
+ *
+ * @return SearchService
+ */
+ public final SearchService getSearchService(){
+ return this.searchService;
+ }
+
+ /**
+ * Return the file folder service
+ *
+ * @return FileFolderService
+ */
+ public final FileFolderService getFileFolderService() {
+ return this.fileFolderService;
+ }
+
+ /**
+ * Return the permission service
+ *
+ * @return PermissionService
+ */
+ public final PermissionService getPermissionService() {
+ return this.permissionService;
+ }
+
+ /**
+ * Return the lock service
+ *
+ * @return LockService
+ */
+ public final LockService getLockService() {
+ return lockService;
+ }
+
+ /**
+ * Get the policy behaviour filter, used to inhibit versioning on a per transaction basis
+ */
+ public BehaviourFilter getPolicyFilter()
+ {
+ return policyBehaviourFilter;
+ }
+
+
+ /**
+ * @param contentService the content service
+ */
+ public void setContentService(ContentService contentService)
+ {
+ this.contentService = contentService;
+ }
+
+ /**
+ * @param namespaceService the namespace service
+ */
+ public void setNamespaceService(NamespaceService namespaceService)
+ {
+ this.namespaceService = namespaceService;
+ }
+
+ /**
+ * @param nodeService the node service
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * @param searchService the search service
+ */
+ public void setSearchService(SearchService searchService)
+ {
+ this.searchService = searchService;
+ }
+
+ /**
+ * Set the permission service
+ *
+ * @param permissionService PermissionService
+ */
+ public void setPermissionService(PermissionService permissionService)
+ {
+ this.permissionService = permissionService;
+ }
+
+ /**
+ * Set the authentication context
+ *
+ * @param authContext AuthenticationContext
+ */
+ public void setAuthenticationContext(AuthenticationContext authContext)
+ {
+ this.authContext = authContext;
+ }
+
+ /**
+ * Set the authentication service
+ *
+ * @param authService AuthenticationService
+ */
+ public void setAuthenticationService(AuthenticationService authService)
+ {
+ this.authService = authService;
+ }
+
+ /**
+ * Set the file folder service
+ *
+ * @param fileService FileFolderService
+ */
+ public void setFileFolderService(FileFolderService fileService)
+ {
+ fileFolderService = fileService;
+ }
+
+ /**
+ * @param mimetypeService service for helping with mimetypes and encoding
+ */
+ public void setMimetypeService(MimetypeService mimetypeService)
+ {
+ this.mimetypeService = mimetypeService;
+ }
+
+ /**
+ * Set the node monitor factory
+ *
+ * @param nodeMonitorFactory NodeMonitorFactory
+ */
+ public void setNodeMonitorFactory(NodeMonitorFactory nodeMonitorFactory) {
+ m_nodeMonitorFactory = nodeMonitorFactory;
+ }
+
+
+ /**
+ * Set the lock service
+ *
+ * @param lockService LockService
+ */
+ public void setLockService(LockService lockService) {
+ this.lockService = lockService;
+ }
+
+ /**
+ * Set the policy behaviour filter, used to inhibit versioning on a per transaction basis
+ *
+ * @param policyFilter PolicyBehaviourFilter
+ */
+ public void setPolicyFilter(BehaviourFilter policyFilter)
+ {
+ this.policyBehaviourFilter = policyFilter;
+ }
+
+ // Configuration key names
+
+ private static final String KEY_STORE = "store";
+ private static final String KEY_ROOT_PATH = "rootPath";
+ private static final String KEY_RELATIVE_PATH = "relativePath";
+
+ /**
+ * Parse and validate the parameter string and create a device context object for this instance
+ * of the shared device. The same DeviceInterface implementation may be used for multiple
+ * shares.
+ *
+ * @deprecated - no longer used. Construction of context is via spring now.
+ * @param deviceName The name of the device
+ * @param cfg ConfigElement the configuration of the device context.
+ * @return DeviceContext
+ * @exception DeviceContextException
+ */
+ public DeviceContext createContext(String deviceName, ConfigElement cfg) throws DeviceContextException
+ {
+ throw new DeviceContextException("Obsolete Method called");
+ }
+
+ /*
+ * Register context implementation
+ *
+ * Results in various obscure bits and pieces being initialised, most importantly the
+ * calculation of the root node ref.
+ *
+ * There's a load of initialisation that needs to be moved out of this method, like the
+ * instantiation of the lock manager, quota manager and node monitor.
+ */
+ public void registerContext(DeviceContext ctx, ServerConfigurationBean serverConfig) throws DeviceContextException
+ {
+ logger.debug("registerContext");
+ super.registerContext(ctx, serverConfig);
+
+ final ContentContext context = (ContentContext)ctx;
+
+ final String rootPath = context.getRootPath();
+ final String storeValue = context.getStoreName();
+
+ /**
+ * Work using the repo needs to run as system.
+ */
+ RunAsWork runAsSystem = new RunAsWork()
+ {
+ @Override
+ public Void doWork() throws Exception
+ {
+ StoreRef storeRef = new StoreRef(storeValue);
+
+ // Connect to the repo and ensure that the store exists
+
+ if (! nodeService.exists(storeRef))
+ {
+ throw new DeviceContextException("Store not created prior to application startup: " + storeRef);
+ }
+
+ NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef);
+
+ // Find the root node for this device
+ List nodeRefs = searchService.selectNodes(storeRootNodeRef, rootPath, null, namespaceService, false);
+
+ NodeRef rootNodeRef = null;
+
+ if (nodeRefs.size() > 1)
+ {
+ throw new DeviceContextException("Multiple possible roots for device: \n" +
+ " root path: " + rootPath + "\n" +
+ " results: " + nodeRefs);
+ }
+ else if (nodeRefs.size() == 0)
+ {
+ // Nothing found
+ throw new DeviceContextException("No root found for device: \n" +
+ " root path: " + rootPath);
+ }
+ else
+ {
+ // We found the root node ref
+ rootNodeRef = nodeRefs.get(0);
+ }
+
+ // Check if a relative path has been specified
+ String relPath = context.getRelativePath();
+
+ try
+ {
+ if ( relPath != null && relPath.length() > 0)
+ {
+ // Find the node and validate that the relative path is to a folder
+ NodeRef relPathNode = cifsHelper.getNodeRef( rootNodeRef, relPath);
+
+ if ( cifsHelper.isDirectory( relPathNode) == false)
+ {
+ throw new DeviceContextException("Relative path is not a folder, " + relPath);
+ }
+
+ // Use the relative path node as the root of the filesystem
+ rootNodeRef = relPathNode;
+ }
+ else
+ {
+ // Make sure the default root node is a folder
+ if ( cifsHelper.isDirectory( rootNodeRef) == false)
+ {
+ throw new DeviceContextException("Root node is not a folder type node");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Error during create context", ex);
+ }
+ throw new DeviceContextException("Unable to find root node.", ex);
+ }
+
+ // Record the root node ref
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("set root node ref:" + rootNodeRef);
+ }
+ context.setRootNodeRef(rootNodeRef);
+
+ return null;
+ }
+ };
+
+ /**
+ * Run the above code as system - in particular resolves root node ref.
+ */
+ AuthenticationUtil.runAs(runAsSystem, AuthenticationUtil.getSystemUserName());
+
+
+ /*
+ * Now we have some odds and ends below that should really be configured elsewhere
+ */
+
+ // Check if locked files should be marked as offline
+ if ( context.getOfflineFiles() )
+ {
+ // Enable marking locked files as offline
+ isLockedFilesAsOffline = true;
+ logger.info("Locked files will be marked as offline");
+ }
+
+ // Enable file state caching
+
+ context.enableStateCache( true);
+ context.getStateCache().setCaseSensitive( false);
+
+ logger.debug("initialise the node monitor");
+ // Install the node service monitor
+ if ( !context.getDisableNodeMonitor() && m_nodeMonitorFactory != null)
+ {
+ NodeMonitor nodeMonitor = m_nodeMonitorFactory.createNodeMonitor(context);
+ context.setNodeMonitor( nodeMonitor);
+ }
+
+ logger.debug("initialise the file state lock manager");
+
+ // Create the lock manager
+
+ lockManager = new FileStateLockManager(context.getStateCache());
+
+ // Check if oplocks are enabled
+
+ if ( context.getDisableOplocks() == true)
+ {
+ logger.warn("Oplock support disabled for filesystem " + context.getDeviceName());
+ }
+
+ // Start the quota manager, if enabled
+ if ( context.hasQuotaManager())
+ {
+ try
+ {
+ // Start the quota manager
+ context.getQuotaManager().startManager( this, context);
+ logger.info("Quota manager enabled for filesystem");
+ }
+ catch ( QuotaManagerException ex)
+ {
+ logger.error("Failed to start quota manager", ex);
+ }
+ }
+
+ // TODO mode to spring
+ PseudoFileOverlayImpl ps = new PseudoFileOverlayImpl();
+ ps.setContext(context);
+ ps.setNodeService(nodeService);
+ context.setPseudoFileOverlay(ps);
+ ps.init();
+ }
+
+ /**
+ * Determine if the disk device is read-only.
+ *
+ * @param sess Server session
+ * @param ctx Device context
+ * @return boolean
+ * @exception java.io.IOException If an error occurs.
+ */
+ public boolean isReadOnly(SrvSession sess, DeviceContext ctx) throws IOException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("isReadOnly");
+ }
+ return !m_transactionService.getAllowWrite();
+ }
+
+ /**
+ * Get the file information for the specified file.
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param name File name/path that information is required for.
+ * @return File information if valid, else null
+ * @exception java.io.IOException The exception description.
+ */
+ public FileInfo getFileInformation(SrvSession session, TreeConnection tree, String path) throws IOException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("getFileInformation:" + path + ", session:" + session.getUniqueId());
+ }
+ ContentContext ctx = (ContentContext) tree.getContext();
+
+ if ( path == null || path.length() == 0)
+ {
+ path = FileName.DOS_SEPERATOR_STR;
+ }
+
+ String infoPath = path;
+
+ try
+ {
+ FileInfo finfo = null;
+
+ // Is the node a pseudo file ?
+ if(session.isPseudoFilesEnabled())
+ {
+ String[] paths = FileName.splitPath(path);
+ // lookup parent directory
+ NodeRef dirNodeRef = getNodeForPath(tree, paths[0]);
+
+ // Check whether we are opening a pseudo file
+ if(ctx.getPseudoFileOverlay().isPseudoFile(dirNodeRef, paths[1]))
+ {
+ PseudoFile pfile = ctx.getPseudoFileOverlay().getPseudoFile(dirNodeRef, paths[1]);
+ FileInfo pseudoFileInfo = pfile.getFileInfo();
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("returning psuedo file details:" + pseudoFileInfo);
+ }
+ return pseudoFileInfo;
+ }
+ }
+
+ // no - this is not a specially named pseudo file.
+ NodeRef nodeRef = getNodeForPath(tree, infoPath);
+
+ if ( nodeRef != null)
+ {
+ // Get the file information for the node
+
+ finfo = getCifsHelper().getFileInformation(nodeRef, false, isLockedFilesAsOffline);
+
+ // DEBUG
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("getFileInformation found nodeRef for nodeRef :"+ nodeRef + ", path: " + path);
+ }
+
+ // Set the file id from the node's DBID
+ long id = DefaultTypeConverter.INSTANCE.convert(Long.class, nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID));
+ finfo.setFileId((int) (id & 0xFFFFFFFFL));
+ }
+
+ // Return the file information or null if the node ref does not exist
+ return finfo;
+ }
+ catch (FileNotFoundException e)
+ {
+ // Debug
+
+ if (logger.isDebugEnabled())
+ {
+ // exception not logged - cifs does lots of these
+ logger.debug("Get file info - file not found, " + path);
+ }
+ throw e;
+ }
+ catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Get file info - access denied, " + path, ex);
+ }
+
+ // Convert to a filesystem access denied status
+ throw new AccessDeniedException("Get file information " + path);
+ }
+ catch (AlfrescoRuntimeException ex)
+ {
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Get file info error" + path, ex);
+ }
+
+ // Convert to a general I/O exception
+ throw new IOException("Get file information " + path, ex);
+ }
+ }
+
+ /**
+ * Start a new search on the filesystem using the specified searchPath that may contain
+ * wildcards.
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param searchPath File(s) to search for, may include wildcards.
+ * @param attrib Attributes of the file(s) to search for, see class SMBFileAttribute.
+ * @return SearchContext
+ * @exception java.io.FileNotFoundException If the search could not be started.
+ */
+ public SearchContext startSearch(SrvSession session, TreeConnection tree, String searchPath, int attributes) throws FileNotFoundException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("startSearch: "+ searchPath + ", session:" + session.getUniqueId());
+ }
+ // Access the device context
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+
+ try
+ {
+ String searchFileSpec = searchPath;
+
+ NodeRef searchRootNodeRef = ctx.getRootNode();
+
+ String[] paths = FileName.splitPath(searchPath);
+
+ // lookup parent directory
+ NodeRef dirNodeRef = getNodeForPath(tree, paths[0]);
+ if(dirNodeRef != null)
+ {
+ searchRootNodeRef = dirNodeRef;
+ searchFileSpec = paths[1];
+ }
+
+ // Convert the all files wildcard
+ if ( searchFileSpec.equals( "*.*"))
+ {
+ searchFileSpec = "*";
+ }
+
+ // Debug
+ long startTime = 0L;
+ if ( logger.isDebugEnabled())
+ {
+ startTime = System.currentTimeMillis();
+ }
+
+ // Perform the search
+
+ logger.debug("Call repo to do search");
+
+ List results = getCifsHelper().getNodeRefs(searchRootNodeRef, searchFileSpec);
+ // Debug
+ if ( logger.isDebugEnabled())
+ {
+ long endTime = System.currentTimeMillis();
+ if (( endTime - startTime) > 500)
+ {
+ logger.debug("Search for searchPath=" + searchPath + ", searchSpec=" + searchFileSpec + ", searchRootNode=" + searchRootNodeRef + " took "
+ + ( endTime - startTime) + "ms results=" + results.size());
+ }
+ }
+
+ /**
+ * Search pseudo files if they are enabled
+ */
+ PseudoFileList pseudoList = null;
+ if(session.isPseudoFilesEnabled())
+ {
+ logger.debug("search pseudo files");
+ pseudoList = ctx.getPseudoFileOverlay().searchPseudoFiles(dirNodeRef, searchFileSpec);
+ }
+
+ SearchContext searchCtx = new ContentSearchContext(getCifsHelper(), results, searchFileSpec, pseudoList, paths[0]);
+
+ // Debug
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Started search: search path=" + searchPath + " attributes=" + attributes + ", ctx=" + searchCtx);
+ }
+
+ return searchCtx;
+ }
+ catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Start search - access denied, " + searchPath);
+ }
+
+ // Convert to a file not found status
+
+ throw new FileNotFoundException("Start search " + searchPath);
+ }
+ catch (AlfrescoRuntimeException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Exception in Start search", ex);
+ }
+
+ // Convert to a file not found status
+
+ throw new FileNotFoundException("Start search " + searchPath);
+ }
+ }
+
+ /**
+ * Check if the specified file exists, and whether it is a file or directory.
+ *
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param name the path of the file
+ * @return FileStatus (0: NotExist, 1 : FileExist, 2: DirectoryExists)
+ * @see FileStatus
+ */
+ public int fileExists(SrvSession session, TreeConnection tree, String name)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("fileExists:" + name + ", session:" + session.getUniqueId());
+ }
+ ContentContext ctx = (ContentContext) tree.getContext();
+ int status = FileStatus.Unknown;
+ try
+ {
+ if(session.isPseudoFilesEnabled())
+ {
+ String[] paths = FileName.splitPath(name);
+ // lookup parent directory
+ NodeRef dirNodeRef = getNodeForPath(tree, paths[0]);
+ // Check whether we are opening a pseudo file
+ if(ctx.getPseudoFileOverlay().isPseudoFile(dirNodeRef, paths[1]))
+ {
+ return FileStatus.FileExists;
+ }
+ }
+
+ // Get the file information to check if the file/folder exists
+ FileInfo info = getFileInformation(session, tree, name);
+ if (info.isDirectory())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("is directory");
+ }
+ status = FileStatus.DirectoryExists;
+ }
+ else
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("is file");
+ }
+ status = FileStatus.FileExists;
+ }
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("File status determined: name=" + name + " status=" + status);
+ }
+
+ return status;
+ }
+ catch (FileNotFoundException e)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("file does not exist");
+ }
+ status = FileStatus.NotExist;
+ return status;
+ }
+ catch (IOException e)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("File exists error, " + name, e);
+ }
+
+ status = FileStatus.NotExist;
+ return status;
+ }
+
+ }
+
+ /**
+ * Open a file or folder
+ *
+ * @param sess SrvSession
+ * @param tree TreeConnection
+ * @param params FileOpenParams
+ * @return NetworkFile
+ * @exception IOException
+ */
+ public NetworkFile openFile(SrvSession session, TreeConnection tree, FileOpenParams params) throws IOException
+ {
+ ContentContext ctx = (ContentContext) tree.getContext();
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("openFile :" + params + ", session:" + session.getUniqueId());
+ }
+ try
+ {
+ String name = params.getPath();
+
+ if(session.isPseudoFilesEnabled())
+ {
+ String[] paths = FileName.splitPath(name);
+ // lookup parent directory
+ NodeRef dirNodeRef = getNodeForPath(tree, paths[0]);
+
+ // Check whether we are opening a pseudo file
+ if(ctx.getPseudoFileOverlay().isPseudoFile(dirNodeRef, paths[1]))
+ {
+ PseudoFile pfile = ctx.getPseudoFileOverlay().getPseudoFile(dirNodeRef, paths[1]);
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Opened pseudo file :" + pfile);
+ }
+ return pfile.getFile( params.getPath());
+ }
+ }
+
+ // not a psudo file
+
+ NodeRef nodeRef = getNodeForPath(tree, params.getPath());
+
+ // Check permissions on the file/folder node
+ //
+ // Check for read access
+
+ if ( params.hasAccessMode(AccessMode.NTRead) &&
+ permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.DENIED)
+ {
+ throw new AccessDeniedException("No read access to " + params.getFullPath());
+ }
+
+ // Check for write access
+ if ( params.hasAccessMode(AccessMode.NTWrite) &&
+ permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED)
+ {
+ throw new AccessDeniedException("No write access to " + params.getFullPath());
+ }
+
+ // Check for delete access
+
+ // if ( params.hasAccessMode(AccessMode.NTDelete) &&
+ // permissionService.hasPermission(nodeRef, PermissionService.DELETE) == AccessStatus.DENIED)
+ // throw new AccessDeniedException("No delete access to " + params.getFullPath());
+
+ // will throw a NodeLockedException is locked by somebody else
+ lockService.checkForLock(nodeRef);
+
+ // Check if the node is a link node
+ NodeRef linkRef = (NodeRef) nodeService.getProperty(nodeRef, ContentModel.PROP_LINK_DESTINATION);
+ NetworkFile netFile = null;
+
+ if ( linkRef == null)
+ {
+ // TODO MER REWRITE HERE
+ FileInfo fileInfo = cifsHelper.getFileInformation(nodeRef, "", false, false);
+
+ // TODO this is wasteful - the isDirectory is in the params. We should split off an openDirectory method.
+ if(fileInfo.isDirectory())
+ {
+ logger.debug("open file - is a directory!");
+ netFile = new AlfrescoFolder(params.getFullPath(), fileInfo, params.isReadOnlyAccess());
+ }
+ else
+ {
+ if(params.isReadOnlyAccess())
+ {
+ logger.debug("open file for read only");
+ netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, getCifsHelper(), nodeRef, params, session);
+
+ if(!netFile.isReadOnly())
+ {
+ logger.debug("work around");
+ // Bug work around fix for read only stuff
+ int attrib = netFile.getFileAttributes();
+ attrib = attrib + FileAttribute.ReadOnly;
+ netFile.setAttributes(attrib);
+ }
+ }
+ else if(params.isReadWriteAccess())
+ {
+ logger.debug("open file for read write");
+ File file = TempFileProvider.createTempFile("cifs", ".bin");
+
+ if(! params.isOverwrite())
+ {
+ // Need to open a temp file with a copy of the content.
+ ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
+ if(reader != null)
+ {
+ reader.getContent(file);
+ }
+ }
+
+ netFile = new TempNetworkFile(file, name);
+
+ // Always allow write access to a newly created file
+ netFile.setGrantedAccess(NetworkFile.READWRITE);
+
+ // Generate a file id for the file
+
+ if ( netFile != null)
+ {
+ long id = DefaultTypeConverter.INSTANCE.convert(Long.class, nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID));
+ netFile.setFileId((int) (id & 0xFFFFFFFFL));
+ }
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Created file: path=" + name + " node=" + nodeRef + " network file=" + netFile);
+ }
+ }
+ }
+ }
+ else
+ {
+ // This is a link node
+
+ // Get the CIFS server name
+
+ String srvName = null;
+ SMBServer cifsServer = (SMBServer) session.getServer().getConfiguration().findServer( "CIFS");
+
+ if ( cifsServer != null)
+ {
+ // Use the CIFS server name in the URL
+
+ srvName = cifsServer.getServerName();
+ }
+ else
+ {
+ // Use the local server name in the URL
+ srvName = InetAddress.getLocalHost().getHostName();
+ }
+
+ // Convert the target node to a path, convert to URL format
+
+ String path = getPathForNode( tree, linkRef);
+ path = path.replace( FileName.DOS_SEPERATOR, '/');
+
+ // Build the URL file data
+
+ StringBuilder urlStr = new StringBuilder();
+
+ urlStr.append("[InternetShortcut]\r\n");
+ urlStr.append("URL=file://");
+ urlStr.append( srvName);
+ urlStr.append("/");
+ urlStr.append( tree.getSharedDevice().getName());
+ urlStr.append( path);
+ urlStr.append("\r\n");
+
+ // Create the in memory pseudo file for the URL link
+
+ byte[] urlData = urlStr.toString().getBytes();
+
+ // Get the file information for the link node
+
+ FileInfo fInfo = getCifsHelper().getFileInformation( nodeRef, false, isLockedFilesAsOffline);
+
+ // Set the file size to the actual data length
+
+ fInfo.setFileSize( urlData.length);
+
+ // Create the network file using the in-memory file data
+
+ netFile = new LinkMemoryNetworkFile( fInfo.getFileName(), urlData, fInfo, nodeRef);
+ netFile.setFullName( params.getPath());
+ }
+
+ // Generate a file id for the file
+
+ if ( netFile != null)
+ {
+ long id = DefaultTypeConverter.INSTANCE.convert(Long.class, nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID));
+ netFile.setFileId(( int) ( id & 0xFFFFFFFFL));
+ }
+
+ // If the file has been opened for overwrite then truncate the file to zero length, this will
+ // also prevent the existing content data from being copied to the new version of the file
+
+ if ( params.isOverwrite() && netFile != null)
+ {
+ // Truncate the file to zero length
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("truncate file");
+ }
+
+ netFile.truncateFile( 0L);
+ }
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Opened network file: path=" + params.getPath() + " file open parameters=" + params + " network file=" + netFile);
+ }
+
+ // Return the network file
+
+ return netFile;
+ }
+ catch (NodeLockedException nle)
+ {
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Open file - node is locked, " + params.getFullPath());
+ }
+ throw new AccessDeniedException("File is locked, no write access to " + params.getFullPath());
+ }
+ catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Open file - access denied, " + params.getFullPath());
+ }
+ // Convert to a filesystem access denied status
+
+ throw new AccessDeniedException("Open file " + params.getFullPath());
+ }
+ catch (AlfrescoRuntimeException ex)
+ {
+ // Debug
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Open file error", ex);
+ }
+ // Convert to a general I/O exception
+
+ throw new IOException("Open file " + params.getFullPath(), ex);
+ }
+ }
+
+ /**
+ * Create a new file on the file system.
+ *
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param params File create parameters
+ * @return NetworkFile
+ * @exception java.io.IOException If an error occurs.
+ */
+ public NetworkFile createFile(SrvSession sess, final TreeConnection tree, final FileOpenParams params) throws IOException
+ {
+ return null;
+// final ContentContext ctx = (ContentContext) tree.getContext();
+//
+// if (logger.isDebugEnabled())
+// {
+// logger.debug("createFile :" + params);
+// }
+//
+// try
+// {
+// // Get the device root
+//
+// NodeRef deviceRootNodeRef = ctx.getRootNode();
+//
+// String path = params.getPath();
+// //String parentPath = path;
+//
+// // MER NEW CODE BELOW
+// //NodeRef parentNodeRef = cifsHelper.getNodeRef(deviceRootNodeRef, parentPath);
+//
+// // TODO Check whether node already exists
+//
+// // TODO Check permissions to create new node
+// //ContentWriter writer = contentService.getTempWriter();
+//
+// //File file = TempFileProvider.createTempFile("cifs", ".bin");
+//
+// //TempNetworkFile netFile = new TempNetworkFile(file, path);
+//
+// //netFile.setFileId(12345);
+//
+// //tempFiles.put(path, netFile);
+//
+// //if(logger.isDebugEnabled())
+// //{
+// // logger.debug("temp network file created:" + file);
+// //}
+// // MER NEW CODE ABOVE
+//
+// NodeRef nodeRef = cifsHelper.createNode(deviceRootNodeRef, path, ContentModel.TYPE_CONTENT);
+//
+// nodeService.addAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT, null);
+//
+// // Create the network file
+//
+// ContentNetworkFile netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, getCifsHelper(), nodeRef, params, sess);
+//
+// // Always allow write access to a newly created file
+//
+// netFile.setGrantedAccess(NetworkFile.READWRITE);
+//
+// // Set the owner process id for this open file
+//
+// netFile.setProcessId( params.getProcessId());
+//
+// // Truncate the file so that the content stream is created
+// netFile.truncateFile( 0L);
+//
+// // Generate a file id for the file
+//
+// if ( netFile != null)
+// {
+// long id = DefaultTypeConverter.INSTANCE.convert(Long.class, nodeService.getProperty(netFile.getNodeRef(), ContentModel.PROP_NODE_DBID));
+// netFile.setFileId((int) (id & 0xFFFFFFFFL));
+// }
+//
+// if (logger.isDebugEnabled())
+// {
+// logger.debug("Created file: path=" + params.getPath() + " file open parameters=" + params + " node=" + nodeRef + " network file=" + netFile);
+// }
+//
+// // Return the new network file
+//
+// return netFile;
+// }
+// catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+// {
+// // Debug
+//
+// if ( logger.isDebugEnabled())
+// {
+// logger.debug("Create file - access denied, " + params.getFullPath());
+// }
+//
+// // Convert to a filesystem access denied status
+//
+// throw new AccessDeniedException("Unable to create file " + params.getFullPath());
+// }
+// catch (IOException ex)
+// {
+// // Debug
+//
+// if ( logger.isDebugEnabled())
+// {
+// logger.debug("Create file - content I/O error, " + params.getFullPath());
+// }
+//
+// throw ex;
+// }
+// catch (ContentIOException ex)
+// {
+// // Debug
+//
+// if ( logger.isDebugEnabled())
+// {
+// logger.debug("Create file - content I/O error, " + params.getFullPath());
+// }
+// // Convert to a filesystem disk full status
+//
+// throw new DiskFullException("Unable to create file " + params.getFullPath());
+// }
+// catch (AlfrescoRuntimeException ex)
+// {
+// // Debug
+//
+// if ( logger.isDebugEnabled())
+// {
+// logger.debug("Create file error", ex);
+// }
+//
+// // Convert to a general I/O exception
+//
+// throw new IOException("Unable to create file " + params.getFullPath(), ex);
+// }
+ }
+
+ /**
+ * Create a new directory on this file system.
+ *
+ *
+ * @param sess Server session
+ * @param tree Tree connection.
+ * @param params Directory create parameters
+ * @exception java.io.IOException If an error occurs.
+ */
+ public void createDirectory(SrvSession sess, final TreeConnection tree, final FileOpenParams params) throws IOException
+ {
+ final ContentContext ctx = (ContentContext) tree.getContext();
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("createDirectory :" + params);
+ }
+
+ try
+ {
+ // get the device root
+
+ NodeRef deviceRootNodeRef = ctx.getRootNode();
+
+ String path = params.getPath();
+ String parentPath = null;
+
+ // If the state table is available then try to find the parent folder node for the new folder
+ // to save having to walk the path
+
+ NodeRef nodeRef = getCifsHelper().createNode(deviceRootNodeRef, path, ContentModel.TYPE_FOLDER);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Created directory: path=" + params.getPath() + " file open params=" + params + " node=" + nodeRef);
+ }
+
+ // void return
+ }
+ catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Create directory - access denied, " + params.getFullPath());
+ }
+ // Convert to a filesystem access denied status
+
+ throw new AccessDeniedException("Create directory " + params.getFullPath());
+ }
+ catch (AlfrescoRuntimeException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Create directory error", ex);
+ }
+
+ // Convert to a general I/O exception
+
+ throw new IOException("Create directory " + params.getFullPath(), ex);
+ }
+ }
+
+ /**
+ * Delete the directory from the filesystem.
+ *
+ * The directory must be empty in order to be able to delete ity
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param dir Directory name.
+ * @exception java.io.IOException The exception description.
+ */
+ public void deleteDirectory(SrvSession session, TreeConnection tree, final String dir) throws IOException
+ {
+ // get the device root
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("deleteDirectory: " + dir + ", session:" + session.getUniqueId());
+ }
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+ final NodeRef deviceRootNodeRef = ctx.getRootNode();
+
+ try
+ {
+ // Get the node for the folder
+
+ NodeRef nodeRef = getCifsHelper().getNodeRef(deviceRootNodeRef, dir);
+ if (fileFolderService.exists(nodeRef))
+ {
+ // Check if the folder is empty
+
+ if ( getCifsHelper().isFolderEmpty( nodeRef))
+ {
+ // Delete the folder node
+ fileFolderService.delete(nodeRef);
+ }
+ else
+ {
+ throw new DirectoryNotEmptyException( dir);
+ }
+ }
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Deleted directory: directory=" + dir + " node=" + nodeRef);
+ }
+ // void return
+ }
+ catch (FileNotFoundException e)
+ {
+ // Debug
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Delete directory - file not found, " + dir);
+ }
+ }
+ catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Delete directory - access denied, " + dir);
+ }
+ // Convert to a filesystem access denied status
+
+ throw new AccessDeniedException("Delete directory, access denied :" + dir);
+ }
+ catch (AlfrescoRuntimeException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE))
+ logger.debug("Delete directory", ex);
+
+ // Convert to a general I/O exception
+
+ throw new IOException("Delete directory " + dir, ex);
+ }
+ }
+
+ /**
+ * Flush any buffered output for the specified file.
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param file Network file context.
+ * @exception java.io.IOException The exception description.
+ */
+ public void flushFile(SrvSession session, TreeConnection tree, NetworkFile file) throws IOException
+ {
+ // Debug
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Flush file=" + file.getFullName()+ ", session:" + session.getUniqueId());
+ }
+
+ // Flush the file data
+ file.flushFile();
+ }
+
+ /**
+ * Close the file.
+ *
+ * @param sess Server session
+ * @param tree Tree connection.
+ * @param param Network file context.
+ *
+ * @exception java.io.IOException If an error occurs.
+ */
+ public void closeFile(SrvSession session, TreeConnection tree, final NetworkFile file) throws IOException
+ {
+// if ( logger.isDebugEnabled())
+// {
+// logger.debug("Close file=" + file.getFullName() + ", session:" + session.getUniqueId());
+// }
+// // Get the associated file state
+//
+// final ContentContext ctx = (ContentContext) tree.getContext();
+//
+// if( file instanceof PseudoNetworkFile)
+// {
+// file.close();
+// return;
+// }
+//
+// /**
+// * Delete on close attribute - node needs to be deleted.
+// */
+// if(file.hasDeleteOnClose())
+// {
+// if(logger.isDebugEnabled())
+// {
+// logger.debug("closeFile has delete on close set");
+// }
+//
+// NodeRef target = getCifsHelper().getNodeRef(ctx.getRootNode(), file.getFullName());
+// if(target!=null)
+// {
+// nodeService.deleteNode(target);
+// }
+//
+// //TODO Needs to be post-commit
+// if(file instanceof TempNetworkFile)
+// {
+// TempNetworkFile tnf = (TempNetworkFile)file;
+// final QuotaManager quotaMgr = ctx.getQuotaManager();
+// if (quotaMgr != null)
+// {
+// quotaMgr.releaseSpace(session, tree, file.getFileId(), file.getName(), tnf.getFileSizeInt());
+// }
+// }
+//
+// // Still need to close the open file handle.
+// file.close();
+//
+// if (logger.isDebugEnabled())
+// {
+// logger.debug("Closed file: network file=" + file + " delete on close=" + file.hasDeleteOnClose());
+// }
+//
+//
+// return;
+// }
+//
+// // Check for a temp file - which will be a new file or a read/write file
+// if ( file instanceof TempNetworkFile)
+// {
+// if(logger.isDebugEnabled())
+// {
+// logger.debug("Got a temp network file ");
+// }
+//
+// TempNetworkFile tempFile =(TempNetworkFile)file;
+//
+// tempFile.flushFile();
+// tempFile.close();
+//
+// // Take an initial guess at the mimetype (if it has not been set by something already)
+// String mimetype = mimetypeService.guessMimetype(tempFile.getFullName(), new FileContentReader(tempFile.getFile()));
+// logger.debug("guesssed mimetype:" + mimetype);
+//
+// String encoding;
+// // Take a guess at the locale
+// InputStream is = new BufferedInputStream(new FileInputStream(tempFile.getFile()));
+// try
+// {
+// ContentCharsetFinder charsetFinder = mimetypeService.getContentCharsetFinder();
+// Charset charset = charsetFinder.getCharset(is, mimetype);
+// encoding = charset.name();
+// }
+// finally
+// {
+// if(is != null)
+// {
+// is.close();
+// }
+// }
+//
+// NodeRef target = getCifsHelper().getNodeRef(ctx.getRootNode(), tempFile.getFullName());
+// ContentWriter writer = contentService.getWriter(target, ContentModel.PROP_CONTENT, true);
+// writer.setMimetype(mimetype);
+// writer.setEncoding(encoding);
+// writer.putContent(tempFile.getFile());
+//
+// long size = writer.getSize();
+// if(nodeService.hasAspect(target, ContentModel.ASPECT_NO_CONTENT) && size > 0)
+// {
+// if(logger.isDebugEnabled())
+// {
+// logger.debug("removed no content aspect");
+// }
+// nodeService.removeAspect(target, ContentModel.ASPECT_NO_CONTENT);
+// }
+// }
+//
+// try
+// {
+// // Defer to the network file to close the stream and remove the content
+//
+// file.close();
+//
+// // DEBUG
+//
+// if (logger.isDebugEnabled())
+// {
+// logger.debug("Closed file: network file=" + file + " delete on close=" + file.hasDeleteOnClose());
+//
+// if ( file.hasDeleteOnClose() == false && file instanceof ContentNetworkFile)
+// {
+// ContentNetworkFile cFile = (ContentNetworkFile) file;
+// logger.debug(" File " + file.getFullName() + ", version=" + nodeService.getProperty( cFile.getNodeRef(), ContentModel.PROP_VERSION_LABEL));
+// }
+// }
+// }
+// // Make sure we clean up before propagating exceptions
+// catch (IOException e)
+// {
+// if ( logger.isDebugEnabled())
+// {
+// logger.debug("Exception in closeFile - ", e);
+// }
+// throw e;
+// }
+// catch (Error e)
+// {
+// if ( logger.isDebugEnabled())
+// {
+// logger.debug("Exception in closeFile - ", e);
+// }
+//
+// throw e;
+// }
+ }
+
+ /**
+ * Delete the specified file.
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param file NetworkFile
+ * @exception java.io.IOException The exception description.
+ */
+ public void deleteFile(final SrvSession session, final TreeConnection tree, final String name) throws IOException
+ {
+ // Get the device context
+
+ final ContentContext ctx = (ContentContext) tree.getContext();
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("deleteFile:" + name + ", session:" + session.getUniqueId());
+ }
+
+ try
+ {
+ if(session.isPseudoFilesEnabled())
+ {
+ String[] paths = FileName.splitPath(name);
+ // lookup parent directory
+ NodeRef dirNodeRef = getNodeForPath(tree, paths[0]);
+
+ // Check whether we are opening a pseudo file
+ if(ctx.getPseudoFileOverlay().isPseudoFile(dirNodeRef, paths[1]))
+ {
+ return;
+ }
+ }
+
+ // Check if there is a quota manager enabled, if so then we need to save the current file size
+
+ final QuotaManager quotaMgr = ctx.getQuotaManager();
+
+ // Get the node and delete it
+ final NodeRef nodeRef = getNodeForPath(tree, name);
+
+ if (fileFolderService.exists(nodeRef))
+ {
+ // Get the size of the file being deleted
+ final FileInfo fInfo = quotaMgr == null ? null : getFileInformation(session, tree, name);
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("deleted file" + name);
+ }
+ fileFolderService.delete(nodeRef);
+
+ //TODO Needs to be post-commit
+ if (quotaMgr != null)
+ {
+ quotaMgr.releaseSpace(session, tree, fInfo.getFileId(), name, fInfo.getSize());
+ }
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Deleted file: " + name + ", nodeRef=" + nodeRef);
+ }
+
+ // void return
+ return;
+ }
+ }
+ catch (NodeLockedException ex)
+ {
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Delete file - access denied (locked)", ex);
+ }
+ // Convert to a filesystem access denied status
+
+ throw new AccessDeniedException("Unable to delete " + name);
+ }
+ catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Delete file - access denied", ex);
+ }
+
+ // Convert to a filesystem access denied status
+ throw new AccessDeniedException("Unable to delete " + name);
+ }
+ catch (IOException ex)
+ {
+ // Allow I/O Exceptions to pass through
+ throw ex;
+ }
+ catch (Exception ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Delete file error", ex);
+ }
+
+ // Convert to a general I/O exception
+ IOException ioe = new IOException("Delete file " + name);
+ ioe.initCause(ex);
+ throw ioe;
+ }
+ }
+
+ /**
+ * Rename the specified file.
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param oldName java.lang.String
+ * @param newName java.lang.String
+ * @exception java.io.IOException The exception description.
+ */
+ public void renameFile(final SrvSession session, final TreeConnection tree, final String oldName, final String newName)
+ throws IOException
+ {
+ // Get the device context
+ final ContentContext ctx = (ContentContext) tree.getContext();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("RenameFile oldName=" + oldName + ", newName=" + newName + ", session:" + session.getUniqueId());
+ }
+
+ try
+ {
+ // Get the file/folder to move
+
+ final NodeRef nodeToMoveRef = getNodeForPath(tree, oldName);
+
+ // Check if the node is a link node
+
+ if ( nodeToMoveRef != null && nodeService.getProperty(nodeToMoveRef, ContentModel.PROP_LINK_DESTINATION) != null)
+ {
+ throw new AccessDeniedException("Cannot rename link nodes");
+ }
+
+ // Get the new target folder - it must be a folder
+ String[] splitPaths = FileName.splitPath(newName);
+ String[] oldPaths = FileName.splitPath(oldName);
+
+ final NodeRef targetFolderRef = getNodeForPath(tree, splitPaths[0]);
+ final NodeRef sourceFolderRef = getNodeForPath(tree, oldPaths[0]);
+ final String name = splitPaths[1];
+
+ // Check if this is a rename within the same folder
+
+ final boolean sameFolder = splitPaths[0].equalsIgnoreCase(oldPaths[0]);
+
+ // Check if we are renaming a folder, or the rename is to a different folder
+ boolean isFolder = getCifsHelper().isDirectory(nodeToMoveRef);
+
+ if ( isFolder == true || sameFolder == false) {
+
+ // Rename or move the file/folder to another folder
+ if (sameFolder == true)
+ {
+ getCifsHelper().rename(nodeToMoveRef, name);
+ }
+ else
+ {
+ getCifsHelper().move(nodeToMoveRef, sourceFolderRef, targetFolderRef, name);
+ }
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(" Renamed " + (isFolder ? "folder" : "file") + " using "
+ + (sameFolder ? "rename" : "move"));
+ }
+ }
+ else
+ {
+ // Rename a file within the same folder
+ //
+ // Check if the target file already exists
+
+ final int newExists = fileExists(session, tree, newName);
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ "Rename file within same folder: \n" +
+ " Old name: " + oldName + "\n" +
+ " New name: " + newName + "\n" +
+ " Source folder: " + sourceFolderRef + "\n" +
+ " Target folder: " + targetFolderRef + "\n" +
+ " Node: " + nodeToMoveRef + "\n" +
+ " Aspects: " + nodeService.getAspects(nodeToMoveRef));
+ }
+
+ getCifsHelper().rename(nodeToMoveRef, name);
+
+ }
+ }
+ catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Rename file - access denied, " + oldName);
+ }
+ // Convert to a filesystem access denied status
+ throw new AccessDeniedException("Rename file " + oldName);
+ }
+ catch (NodeLockedException ex)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Rename file", ex);
+ }
+
+ // Convert to an filesystem access denied exception
+ throw new AccessDeniedException("Node locked " + oldName);
+ }
+ catch (AlfrescoRuntimeException ex)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Rename file", ex);
+ }
+
+ // Convert to a general I/O exception
+ throw new AccessDeniedException("Rename file " + oldName);
+ }
+ }
+
+ /**
+ * Set file information
+ *
+ * @param sess SrvSession
+ * @param tree TreeConnection
+ * @param name String
+ * @param info FileInfo
+ * @exception IOException
+ */
+ public void setFileInformation(SrvSession sess, final TreeConnection tree, final String name, final FileInfo info) throws IOException
+ {
+ // Get the device context
+
+ final ContentContext ctx = (ContentContext) tree.getContext();
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("setFileInformation name=" + name + ", info=" + info);
+ }
+
+ try
+ {
+
+ if(sess.isPseudoFilesEnabled())
+ {
+ String[] paths = FileName.splitPath(name);
+ // lookup parent directory
+ NodeRef dirNodeRef = getNodeForPath(tree, paths[0]);
+
+ // Check whether we are opening a pseudo file
+ if(ctx.getPseudoFileOverlay().isPseudoFile(dirNodeRef, paths[1]))
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("pseudo file so do nothing");
+ }
+ return;
+ }
+ }
+
+ // Get the file/folder node
+ NodeRef nodeRef = getNodeForPath(tree, name);
+
+ // Check permissions on the file/folder node
+
+ if ( permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED)
+ {
+ throw new AccessDeniedException("No write access to " + name);
+ }
+
+ // Inhibit versioning for this transaction
+ getPolicyFilter().disableBehaviour( ContentModel.ASPECT_VERSIONABLE);
+
+ // Check if the file is being marked for deletion, if so then check if the file is locked
+ if ( info.hasSetFlag(FileInfo.SetDeleteOnClose) && info.hasDeleteOnClose())
+ {
+ // Check if the node is locked
+ lockService.checkForLock(nodeRef);
+
+ // Get the node for the folder
+
+ if (fileFolderService.exists(nodeRef))
+ {
+ // Check if it is a folder that is being deleted, make sure it is empty
+
+ boolean isFolder = true;
+
+ ContentFileInfo cInfo = getCifsHelper().getFileInformation(nodeRef, false, isLockedFilesAsOffline);
+ if ( cInfo != null && cInfo.isDirectory() == false)
+ {
+ isFolder = false;
+ }
+
+ // Check if the folder is empty
+ if ( isFolder == true && getCifsHelper().isFolderEmpty( nodeRef) == false)
+ {
+ throw new DirectoryNotEmptyException( name);
+ }
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Set deleteOnClose=true file=" + name);
+ }
+ }
+ }
+
+ // Set the creation and modified date/time
+ Map auditableProps = new HashMap(5);
+
+ // Which DeleteOnClose flag has priority?
+ // SetDeleteOnClose is not set or used in this method.
+ // The NTProtocolHandler sets the deleteOnClose in both
+ // info and the NetworkFile - it's the one in NetworkFile that works.
+
+ if ( info.hasSetFlag(FileInfo.SetCreationDate))
+ {
+ // Set the creation date on the file/folder node
+ Date createDate = new Date( info.getCreationDateTime());
+ auditableProps.put(ContentModel.PROP_CREATED, createDate);
+ }
+ if ( info.hasSetFlag(FileInfo.SetModifyDate))
+ {
+ // Set the modification date on the file/folder node
+ Date modifyDate = new Date( info.getModifyDateTime());
+ auditableProps.put(ContentModel.PROP_MODIFIED, modifyDate);
+ }
+
+ // Did we have any cm:auditable properties?
+ if (auditableProps.size() > 0)
+ {
+ getPolicyFilter().disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE);
+ nodeService.addProperties(nodeRef, auditableProps);
+
+ // DEBUG
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Set auditable props: " + auditableProps + " file=" + name);
+ }
+ }
+
+ return;
+ }
+ catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+ {
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Set file information - access denied, " + name);
+ }
+ // Convert to a filesystem access denied status
+ throw new AccessDeniedException("Set file information " + name);
+ }
+ catch (AlfrescoRuntimeException ex)
+ {
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Open file error", ex);
+ }
+ // Convert to a general I/O exception
+
+ throw new IOException("Set file information " + name, ex);
+ }
+ }
+
+ /**
+ * Truncate a file to the specified size
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param file Network file details
+ * @param size New file length
+ * @exception java.io.IOException The exception description.
+ */
+ public void truncateFile(SrvSession sess, TreeConnection tree, NetworkFile file, long size) throws IOException
+ {
+ // Keep track of the allocation/release size in case the file resize fails
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("truncateFile file=" + file);
+ }
+
+ long allocSize = 0L;
+ long releaseSize = 0L;
+
+ // Check if there is a quota manager
+
+ QuotaManager quotaMgr = ctx.getQuotaManager();
+
+ if ( ctx.hasQuotaManager()) {
+
+ // Check if the file content has been opened, we need the content to be opened to get the
+ // current file size
+
+ if ( file instanceof ContentNetworkFile) {
+ ContentNetworkFile contentFile = (ContentNetworkFile) file;
+ if ( contentFile.hasContent() == false)
+ {
+ contentFile.openContent( false, false);
+ }
+ }
+ else
+ {
+ throw new IOException("Invalid file class type, " + file.getClass().getName());
+ }
+ // Determine if the new file size will release space or require space allocating
+
+ if ( size > file.getFileSize())
+ {
+
+ // Calculate the space to be allocated
+
+ allocSize = size - file.getFileSize();
+
+ // Allocate space to extend the file
+
+ quotaMgr.allocateSpace(sess, tree, file, allocSize);
+ }
+ else
+ {
+
+ // Calculate the space to be released as the file is to be truncated, release the space if
+ // the file truncation is successful
+
+ releaseSize = file.getFileSize() - size;
+ }
+ }
+
+ // Check if this is a file extend, update the cached allocation size if necessary
+
+ if ( file instanceof ContentNetworkFile) {
+
+ // Get the cached state for the file
+
+ ContentNetworkFile contentFile = (ContentNetworkFile) file;
+ FileState fstate = contentFile.getFileState();
+ if ( fstate != null && size > fstate.getAllocationSize())
+ fstate.setAllocationSize( size);
+ }
+
+ // Set the file length
+
+ try
+ {
+ file.truncateFile(size);
+ }
+ catch (IOException ex)
+ {
+
+ // Check if we allocated space to the file
+
+ if ( allocSize > 0 && quotaMgr != null)
+ quotaMgr.releaseSpace(sess, tree, file.getFileId(), null, allocSize);
+
+ // Rethrow the exception
+
+ throw ex;
+ }
+
+ // Check if space has been released by the file resizing
+
+ if ( releaseSize > 0 && quotaMgr != null)
+ {
+ quotaMgr.releaseSpace(sess, tree, file.getFileId(), null, releaseSize);
+ }
+ // Debug
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Truncated file: network file=" + file + " size=" + size);
+
+ }
+ }
+
+ /**
+ * Read a block of data from the specified file.
+ *
+ * @param sess Session details
+ * @param tree Tree connection
+ * @param file Network file
+ * @param buf Buffer to return data to
+ * @param bufPos Starting position in the return buffer
+ * @param siz Maximum size of data to return
+ * @param filePos File offset to read data
+ * @return Number of bytes read
+ * @exception java.io.IOException The exception description.
+ */
+ public int readFile(
+ SrvSession sess, TreeConnection tree, NetworkFile file,
+ byte[] buffer, int bufferPosition, int size, long fileOffset) throws IOException
+ {
+ // Check if the file is a directory
+
+ if(readLogger.isDebugEnabled())
+ {
+ readLogger.debug("read File" + file);
+ }
+
+ if(file.isDirectory())
+ {
+ logger.debug("read file called for a directory - throw AccessDeniedException");
+ throw new AccessDeniedException();
+ }
+
+ // Read a block of data from the file
+
+ int count = file.readFile(buffer, size, bufferPosition, fileOffset);
+
+ if ( count == -1)
+ {
+ // Read count of -1 indicates a read past the end of file
+
+ count = 0;
+ }
+
+ // Debug
+
+ //ContentContext ctx = (ContentContext) tree.getContext();
+
+ if (readLogger.isDebugEnabled())
+ {
+ readLogger.debug("Read bytes from file: network file=" + file + " buffer size=" + buffer.length + " buffer pos=" + bufferPosition +
+ " size=" + size + " file offset=" + fileOffset + " bytes read=" + count);
+ }
+
+ return count;
+ }
+
+ /**
+ * Seek to the specified file position.
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param file Network file.
+ * @param pos Position to seek to.
+ * @param typ Seek type.
+ * @return New file position, relative to the start of file.
+ */
+ public long seekFile(SrvSession sess, TreeConnection tree, NetworkFile file, long pos, int typ) throws IOException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("seek File");
+ }
+
+ // Check if the file is a directory
+
+ if ( file.isDirectory())
+ {
+ throw new AccessDeniedException();
+ }
+
+ // Set the file position
+
+ return file.seekFile(pos, typ);
+ }
+
+ /**
+ * Write a block of data to the file.
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param file Network file details
+ * @param buf byte[] Data to be written
+ * @param bufoff Offset within the buffer that the data starts
+ * @param siz int Data length
+ * @param fileoff Position within the file that the data is to be written.
+ * @return Number of bytes actually written
+ * @exception java.io.IOException The exception description.
+ */
+ public int writeFile(SrvSession sess, TreeConnection tree, NetworkFile file,
+ byte[] buffer, int bufferOffset, int size, long fileOffset) throws IOException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("write File:" + file);
+ }
+
+ // Check if there is a quota manager
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+ QuotaManager quotaMgr = ctx.getQuotaManager();
+
+ long curSize = file.getFileSize();
+
+ if ( quotaMgr != null)
+ {
+
+ // Check if the file requires extending
+
+ long extendSize = 0L;
+ long endOfWrite = fileOffset + size;
+
+ if ( endOfWrite > curSize)
+ {
+ // Calculate the amount the file must be extended
+
+ extendSize = endOfWrite - file.getFileSize();
+
+ // Allocate space for the file extend
+
+ quotaMgr.allocateSpace(sess, tree, file, extendSize);
+ }
+ }
+
+ // Write to the file
+
+ file.writeFile(buffer, size, bufferOffset, fileOffset);
+
+ // Check if the file size was reduced by the write, may have been extended previously
+
+ if ( quotaMgr != null)
+ {
+
+ // Check if the file size reduced
+
+ if ( file.getFileSize() < curSize)
+ {
+
+ // Release space that was freed by the write
+
+ quotaMgr.releaseSpace( sess, tree, file.getFileId(), file.getFullName(), curSize - file.getFileSize());
+ }
+ }
+
+ // Debug
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Wrote bytes to file: network file=" + file + " buffer size=" + buffer.length + " size=" + size + " file offset=" + fileOffset);
+ }
+
+ return size;
+ }
+
+ /**
+ * Get the node for the specified path
+ *
+ * @param tree TreeConnection
+ * @param path String
+ * @return NodeRef
+ * @exception FileNotFoundException
+ */
+ private NodeRef getNodeForPath(TreeConnection tree, String path)
+ throws FileNotFoundException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("getNodeRefForPath:" + path);
+ }
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+
+ return getCifsHelper().getNodeRef(ctx.getRootNode(), path);
+ }
+
+ /**
+ * Get the node for the specified path
+ *
+ * @param tree TreeConnection
+ * @param path String
+ * @return NodeRef
+ * @exception FileNotFoundException
+ */
+ public NodeRef getNodeForPath(NodeRef rootNode, String path)
+ throws FileNotFoundException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("getNodeRefForPath:" + path);
+ }
+
+ return getCifsHelper().getNodeRef(rootNode, path);
+ }
+
+ /**
+ * Convert a node into a share relative path
+ *
+ * @param tree TreeConnection
+ * @param nodeRef NodeRef
+ * @return String
+ * @exception FileNotFoundException
+ */
+ private String getPathForNode( TreeConnection tree, NodeRef nodeRef)
+ throws FileNotFoundException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("getPathForNode:" + nodeRef);
+ }
+
+ // Convert the target node to a path
+ ContentContext ctx = (ContentContext) tree.getContext();
+ List linkPaths = null;
+
+ try
+ {
+ linkPaths = fileFolderService.getNamePath( ctx.getRootNode(), nodeRef);
+ }
+ catch ( org.alfresco.service.cmr.model.FileNotFoundException ex)
+ {
+ throw new FileNotFoundException();
+ }
+
+ // Build the share relative path to the node
+
+ StringBuilder pathStr = new StringBuilder();
+
+ for ( org.alfresco.service.cmr.model.FileInfo fInfo : linkPaths)
+ {
+ pathStr.append( FileName.DOS_SEPERATOR);
+ pathStr.append( fInfo.getName());
+ }
+
+ // Return the share relative path
+
+ return pathStr.toString();
+ }
+
+ /**
+ * Return the lock manager used by this filesystem
+ *
+ * @param sess SrvSession
+ * @param tree TreeConnection
+ * @return LockManager
+ */
+ public LockManager getLockManager(SrvSession sess, TreeConnection tree)
+ {
+ return lockManager;
+ }
+
+ /**
+ * Disk Size Interface implementation
+ */
+ private interface DiskSizeInterfaceConsts
+ {
+ static final int DiskBlockSize = 512; // bytes per block
+ static final long DiskAllocationUnit = 32 * MemorySize.KILOBYTE;
+ static final long DiskBlocksPerUnit = DiskAllocationUnit / DiskBlockSize;
+
+ // Disk size returned in the content store does not support free/total size
+
+ static final long DiskSizeDefault = 1 * MemorySize.TERABYTE;
+ static final long DiskFreeDefault = DiskSizeDefault / 2;
+ }
+
+ /**
+ * Get the disk information for this shared disk device.
+ *
+ * @param ctx DiskDeviceContext
+ * @param diskDev SrvDiskInfo
+ * @exception IOException
+ */
+ public void getDiskInformation(DiskDeviceContext ctx, SrvDiskInfo diskDev) throws IOException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("getDiskInformation");
+ }
+
+ // Set the block size and blocks per allocation unit
+ diskDev.setBlockSize( DiskSizeInterfaceConsts.DiskBlockSize);
+ diskDev.setBlocksPerAllocationUnit( DiskSizeInterfaceConsts.DiskBlocksPerUnit);
+
+ // Get the free and total disk size in bytes from the content store
+
+ long freeSpace = contentService.getStoreFreeSpace();
+ long totalSpace= contentService.getStoreTotalSpace();
+
+ if ( totalSpace == -1L) {
+
+ // Use a fixed value for the total space, content store does not support size information
+
+ totalSpace = DiskSizeInterfaceConsts.DiskSizeDefault;
+ freeSpace = DiskSizeInterfaceConsts.DiskFreeDefault;
+ }
+
+ // Convert the total/free space values to allocation units
+
+ diskDev.setTotalUnits( totalSpace / DiskSizeInterfaceConsts.DiskAllocationUnit);
+ diskDev.setFreeUnits( freeSpace / DiskSizeInterfaceConsts.DiskAllocationUnit);
+ }
+
+ public void setCifsHelper(CifsHelper cifsHelper)
+ {
+ this.cifsHelper = cifsHelper;
+ }
+
+ @Override
+ public void treeOpened(SrvSession sess, TreeConnection tree)
+ {
+ // Nothing to do
+ }
+
+ @Override
+ public void treeClosed(SrvSession sess, TreeConnection tree)
+ {
+ // Nothing to do
+ }
+
+// Implementation of IOCtlInterface
+
+ /**
+ * Process a filesystem I/O control request
+ *
+ * @param sess Server session
+ * @param tree Tree connection.
+ * @param ctrlCode I/O control code
+ * @param fid File id
+ * @param dataBuf I/O control specific input data
+ * @param isFSCtrl true if this is a filesystem control, or false for a device control
+ * @param filter if bit0 is set indicates that the control applies to the share root handle
+ * @return DataBuffer
+ * @exception IOControlNotImplementedException
+ * @exception SMBException
+ */
+ public org.alfresco.jlan.util.DataBuffer processIOControl(SrvSession sess, TreeConnection tree, int ctrlCode, int fid, DataBuffer dataBuf,
+ boolean isFSCtrl, int filter)
+ throws IOControlNotImplementedException, SMBException
+ {
+ // Validate the file id
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("processIOControl ctrlCode:" + ctrlCode + ", fid:" + fid);
+ }
+
+ NetworkFile netFile = tree.findFile(fid);
+ if ( netFile == null || netFile.isDirectory() == false)
+ throw new SMBException(SMBStatus.NTErr, SMBStatus.NTInvalidParameter);
+
+ // Split the control code
+
+ int devType = NTIOCtl.getDeviceType(ctrlCode);
+ int ioFunc = NTIOCtl.getFunctionCode(ctrlCode);
+
+ // Check for I/O controls that require a success status
+
+ if ( devType == NTIOCtl.DeviceFileSystem)
+ {
+ // I/O control requests that require a success status
+ //
+ // Create or get object id
+
+ if ( ioFunc == NTIOCtl.FsCtlCreateOrGetObjectId)
+ return null;
+ }
+
+ // Check if the I/O control looks like a custom I/O control request
+
+ if ( devType != NTIOCtl.DeviceFileSystem || dataBuf == null)
+ throw new IOControlNotImplementedException();
+
+ // Check if the request has a valid signature for an Alfresco CIFS server I/O control
+
+ if ( dataBuf.getLength() < IOControl.Signature.length())
+ throw new IOControlNotImplementedException("Bad request length");
+
+ String sig = dataBuf.getFixedString(IOControl.Signature.length(), false);
+
+ if ( sig == null || sig.compareTo(IOControl.Signature) != 0)
+ throw new IOControlNotImplementedException("Bad request signature");
+
+ // Get the node for the parent folder, make sure it is a folder
+
+ NodeRef folderNode = null;
+
+ try
+ {
+ folderNode = getNodeForPath(tree, netFile.getFullName());
+
+ if ( getCifsHelper().isDirectory( folderNode) == false)
+ folderNode = null;
+ }
+ catch ( FileNotFoundException ex)
+ {
+ folderNode = null;
+ }
+
+ // If the folder node is not valid return an error
+
+ if ( folderNode == null)
+ throw new SMBException(SMBStatus.NTErr, SMBStatus.NTAccessDenied);
+
+ // Debug
+
+ if ( logger.isDebugEnabled()) {
+ logger.debug("IO control func=0x" + Integer.toHexString(ioFunc) + ", fid=" + fid + ", buffer=" + dataBuf);
+ logger.debug(" Folder nodeRef=" + folderNode);
+ }
+
+ // Check if the I/O control code is one of our custom codes
+
+ DataBuffer retBuffer = null;
+
+ switch ( ioFunc)
+ {
+ // Probe to check if this is an Alfresco CIFS server
+
+ case IOControl.CmdProbe:
+
+ // Return a buffer with the signature and protocol version
+
+ retBuffer = new DataBuffer(IOControl.Signature.length());
+ retBuffer.putFixedString(IOControl.Signature, IOControl.Signature.length());
+ retBuffer.putInt(DesktopAction.StsSuccess);
+ retBuffer.putInt(IOControl.Version);
+ break;
+
+ // Get file information for a file within the current folder
+
+ case IOControl.CmdFileStatus:
+
+ // Process the file status request
+
+ retBuffer = procIOFileStatus( sess, tree, dataBuf, folderNode);
+ break;
+
+ // Get action information for the specified executable path
+
+ case IOControl.CmdGetActionInfo:
+
+ // Process the get action information request
+
+ retBuffer = procGetActionInfo(sess, tree, dataBuf, folderNode, netFile);
+ break;
+
+ // Run the named action
+
+ case IOControl.CmdRunAction:
+
+ // Process the run action request
+
+ retBuffer = procRunAction(sess, tree, dataBuf, folderNode, netFile);
+ break;
+
+ // Return the authentication ticket
+
+ case IOControl.CmdGetAuthTicket:
+
+ // Process the get auth ticket request
+
+ retBuffer = procGetAuthTicket(sess, tree, dataBuf, folderNode, netFile);
+ break;
+
+ // Unknown I/O control code
+
+ default:
+ throw new IOControlNotImplementedException();
+ }
+
+ // Return the reply buffer, may be null
+
+ return retBuffer;
+ }
+
+ /**
+ * Process the file status I/O request
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param reqBuf Request buffer
+ * @param folderNode NodeRef of parent folder
+ * @return DataBuffer
+ */
+ private final DataBuffer procIOFileStatus( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode)
+ {
+
+ // Get the file name from the request
+
+ String fName = reqBuf.getString( true);
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug(" File status, fname=" + fName);
+ }
+ // Create a response buffer
+
+ DataBuffer respBuf = new DataBuffer(256);
+ respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
+
+ // Get the node for the file/folder
+
+ NodeRef childNode = null;
+
+ try
+ {
+ childNode = getCifsHelper().getNodeRef( folderNode, fName);
+ }
+ catch (FileNotFoundException ex)
+ {
+ }
+
+ // Check if the file/folder was found
+
+ if ( childNode == null)
+ {
+ // Return an error response
+
+ respBuf.putInt(DesktopAction.StsFileNotFound);
+ return respBuf;
+ }
+
+ // Check if this is a file or folder node
+
+ if ( getCifsHelper().isDirectory( childNode))
+ {
+ // Only return the status and node type for folders
+
+ respBuf.putInt(DesktopAction.StsSuccess);
+ respBuf.putInt(IOControl.TypeFolder);
+ }
+ else
+ {
+ // Indicate that this is a file node
+
+ respBuf.putInt(DesktopAction.StsSuccess);
+ respBuf.putInt(IOControl.TypeFile);
+
+ // Check if this file is a working copy
+
+ if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_WORKING_COPY))
+ {
+ // Indicate that this is a working copy
+
+ respBuf.putInt(IOControl.True);
+
+ // Get the owner username and file it was copied from
+
+ String owner = (String) getNodeService().getProperty( childNode, ContentModel.PROP_WORKING_COPY_OWNER);
+ String copiedFrom = null;
+
+ // Get the path of the file the working copy was generated from
+ NodeRef fromNode = checkOutCheckInService.getCheckedOut(childNode);
+ if ( fromNode != null)
+ copiedFrom = (String) getNodeService().getProperty( fromNode, ContentModel.PROP_NAME);
+
+ // Pack the owner and copied from values
+
+ respBuf.putString(owner != null ? owner : "", true, true);
+ respBuf.putString(copiedFrom != null ? copiedFrom : "", true, true);
+ }
+ else
+ {
+ // Not a working copy
+
+ respBuf.putInt(IOControl.False);
+ }
+
+ // Check the lock status of the file
+
+ if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_LOCKABLE))
+ {
+ // Get the lock type and owner
+
+ String lockTypeStr = (String) getNodeService().getProperty( childNode, ContentModel.PROP_LOCK_TYPE);
+ String lockOwner = null;
+
+ if ( lockTypeStr != null)
+ lockOwner = (String) getNodeService().getProperty( childNode, ContentModel.PROP_LOCK_OWNER);
+
+ // Pack the lock type, and owner if there is a lock on the file
+
+ if ( lockTypeStr == null)
+ respBuf.putInt(IOControl.LockNone);
+ else
+ {
+ LockType lockType = LockType.valueOf( lockTypeStr);
+
+ respBuf.putInt(lockType == LockType.READ_ONLY_LOCK ? IOControl.LockRead : IOControl.LockWrite);
+ respBuf.putString(lockOwner != null ? lockOwner : "", true, true);
+ }
+ }
+ else
+ {
+ // File is not lockable
+
+ respBuf.putInt(IOControl.LockNone);
+ }
+
+ // Get the content data details for the file
+
+ ContentData contentData = (ContentData) getNodeService().getProperty( childNode, ContentModel.PROP_CONTENT);
+
+ if ( contentData != null)
+ {
+ // Get the content mime-type
+
+ String mimeType = contentData.getMimetype();
+
+ // Pack the content length and mime-type
+
+ respBuf.putInt( IOControl.True);
+ respBuf.putLong( contentData.getSize());
+ respBuf.putString( mimeType != null ? mimeType : "", true, true);
+ }
+ else
+ {
+ // File does not have any content
+
+ respBuf.putInt( IOControl.False);
+ }
+ }
+
+ // Return the response
+
+ return respBuf;
+ }
+
+ /**
+ * Process the get action information request
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param reqBuf Request buffer
+ * @param folderNode NodeRef of parent folder
+ * @param netFile NetworkFile for the folder
+ * @return DataBuffer
+ */
+ private final DataBuffer procGetActionInfo( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
+ NetworkFile netFile)
+ {
+ // Get the executable file name from the request
+
+ String exeName = reqBuf.getString( true);
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug(" Get action info, exe=" + exeName);
+ }
+
+ // Create a response buffer
+
+ DataBuffer respBuf = new DataBuffer(256);
+ respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
+
+ // Get the desktop actions list
+
+
+
+ DesktopActionTable deskActions = ctx.getDesktopActions();
+ if ( deskActions == null)
+ {
+ respBuf.putInt(DesktopAction.StsNoSuchAction);
+ return respBuf;
+ }
+
+ // Convert the executable name to an action name
+
+ DesktopAction deskAction = deskActions.getActionViaPseudoName(exeName);
+ if ( deskAction == null)
+ {
+ respBuf.putInt(DesktopAction.StsNoSuchAction);
+ return respBuf;
+ }
+
+ // Return the desktop action details
+
+ respBuf.putInt(DesktopAction.StsSuccess);
+ respBuf.putString(deskAction.getName(), true);
+ respBuf.putInt(deskAction.getAttributes());
+ respBuf.putInt(deskAction.getPreProcessActions());
+
+ String confirmStr = deskAction.getConfirmationString();
+ respBuf.putString(confirmStr != null ? confirmStr : "", true);
+
+ // Return the response
+
+ return respBuf;
+ }
+
+ /**
+ * Process the run action request
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param reqBuf Request buffer
+ * @param folderNode NodeRef of parent folder
+ * @param netFile NetworkFile for the folder
+ * @return DataBuffer
+ */
+ private final DataBuffer procRunAction( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
+ NetworkFile netFile)
+ {
+ // Get the name of the action to run
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+
+ String actionName = reqBuf.getString(true);
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug(" Run action, name=" + actionName);
+ }
+
+ // Create a response buffer
+
+ DataBuffer respBuf = new DataBuffer(256);
+ respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
+
+ // Find the action handler
+
+ DesktopActionTable deskActions = ctx.getDesktopActions();
+ DesktopAction action = null;
+
+ if ( deskActions != null)
+ action = deskActions.getAction(actionName);
+
+ if ( action == null)
+ {
+ respBuf.putInt(DesktopAction.StsNoSuchAction);
+ respBuf.putString("", true);
+ return respBuf;
+ }
+
+
+ // Get an authentication ticket for the client, or validate the existing ticket. The ticket can be used when
+ // generating URLs for the client-side application so that the user does not have to re-authenticate
+
+ getTicketForClient( sess);
+
+ // Get the list of targets for the action
+
+ int targetCnt = reqBuf.getInt();
+ DesktopParams deskParams = new DesktopParams(sess, folderNode, netFile);
+
+ while ( reqBuf.getAvailableLength() > 4 && targetCnt > 0)
+ {
+ // Get the desktop target details
+
+ int typ = reqBuf.getInt();
+ String path = reqBuf.getString(true);
+
+ DesktopTarget target = new DesktopTarget(typ, path);
+ deskParams.addTarget(target);
+
+ // Find the node for the target path
+
+ NodeRef childNode = null;
+
+ try
+ {
+ // Check if the target path is relative to the folder we are working in or the root of the filesystem
+
+ if ( path.startsWith("\\"))
+ {
+ // Path is relative to the root of the filesystem
+
+ childNode = getCifsHelper().getNodeRef(ctx.getRootNode(), path);
+ }
+ else
+ {
+ // Path is relative to the folder we are working in
+
+ childNode = getCifsHelper().getNodeRef( folderNode, path);
+ }
+ }
+ catch (FileNotFoundException ex)
+ {
+ }
+
+ // If the node is not valid then return an error status
+
+ if (childNode != null)
+ {
+ // Set the node ref for the target
+
+ target.setNode(childNode);
+ }
+ else
+ {
+ // Build an error response
+
+ respBuf.putInt(DesktopAction.StsFileNotFound);
+ respBuf.putString("Cannot find noderef for path " + path, true);
+
+ return respBuf;
+ }
+
+ // Update the target count
+
+ targetCnt--;
+ }
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(" Desktop params: " + deskParams.numberOfTargetNodes());
+ for ( int i = 0; i < deskParams.numberOfTargetNodes(); i++) {
+ DesktopTarget target = deskParams.getTarget(i);
+ logger.debug(" " + target);
+ }
+ }
+
+ // Run the desktop action
+
+ DesktopResponse deskResponse = null;
+
+ try
+ {
+ // Run the desktop action
+
+ deskResponse = action.runAction(deskParams);
+ }
+ catch (Exception ex)
+ {
+ // Create an error response
+
+ deskResponse = new DesktopResponse(DesktopAction.StsError, ex.getMessage());
+ }
+
+ // Pack the action response
+
+ if ( deskResponse != null)
+ {
+ // Pack the status
+
+ respBuf.putInt(deskResponse.getStatus());
+ respBuf.putString(deskResponse.hasStatusMessage() ? deskResponse.getStatusMessage() : "", true);
+ }
+ else
+ {
+ // Pack an error response
+
+ respBuf.putInt(DesktopAction.StsError);
+ respBuf.putString("Action did not return response", true);
+ }
+
+ // Return the response
+
+ return respBuf;
+ }
+
+ /**
+ * Process the get authentication ticket request
+ *
+ * @param sess Server session
+ * @param tree Tree connection
+ * @param reqBuf Request buffer
+ * @param folderNode NodeRef of parent folder
+ * @param netFile NetworkFile for the folder
+ * @return DataBuffer
+ */
+ private final DataBuffer procGetAuthTicket( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
+ NetworkFile netFile)
+ {
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug(" Get Auth Ticket");
+ }
+
+ // Create a response buffer
+
+ DataBuffer respBuf = new DataBuffer(256);
+ respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
+
+ // Get an authentication ticket for the client, or validate the existing ticket. The ticket can be used when
+ // generating URLs for the client-side application so that the user does not have to re-authenticate
+
+ getTicketForClient( sess);
+
+ // Pack the response
+
+ AlfrescoClientInfo cInfo = (AlfrescoClientInfo) sess.getClientInformation();
+
+ if ( cInfo != null && cInfo.getAuthenticationTicket() != null)
+ {
+ respBuf.putInt(DesktopAction.StsAuthTicket);
+ respBuf.putString( cInfo.getAuthenticationTicket(), true);
+ }
+ else
+ {
+ respBuf.putInt(DesktopAction.StsError);
+ respBuf.putString( "Client information invalid", true);
+ }
+
+ // Return the response
+
+ return respBuf;
+ }
+
+ /**
+ * Get, or validate, an authentication ticket for the client
+ *
+ * @param sess SrvSession
+ */
+ private final void getTicketForClient(SrvSession sess)
+ {
+
+ // Get the client information and check if there is a ticket allocated
+
+ AlfrescoClientInfo cInfo = (AlfrescoClientInfo) sess.getClientInformation();
+ if ( cInfo == null)
+ return;
+
+ boolean needTicket = true;
+
+ if ( cInfo.hasAuthenticationTicket())
+ {
+ // Validate the existing ticket, it may have expired
+
+ try
+ {
+ // Validate the existing ticket
+
+ getAuthenticationService().validate( cInfo.getAuthenticationTicket());
+ needTicket = false;
+ }
+ catch ( AuthenticationException ex)
+ {
+ // Invalidate the current ticket
+
+ try
+ {
+ getAuthenticationService().invalidateTicket( cInfo.getAuthenticationTicket());
+ cInfo.setAuthenticationTicket( null);
+ }
+ catch (Exception ex2)
+ {
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Error during invalidate ticket", ex2);
+ }
+
+ // DEBUG
+
+ if ( logger.isDebugEnabled())
+ logger.debug("Auth ticket expired or invalid");
+ }
+ }
+
+ // Check if a ticket needs to be allocated
+
+ if ( needTicket == true)
+ {
+ // Allocate a new ticket and store in the client information for this session
+
+ String ticket = getAuthenticationService().getCurrentTicket();
+ cInfo.setAuthenticationTicket( ticket);
+ }
+ }
+
+ public void setCheckOutCheckInService(CheckOutCheckInService service)
+ {
+ this.checkOutCheckInService = service;
+ }
+
+
+ /**
+ * @return the service to provide check-in and check-out data
+ */
+ public final CheckOutCheckInService getCheckOutCheckInService()
+ {
+ return checkOutCheckInService;
+ }
+
+ // Implementation of RepositoryDiskInterface
+ @Override
+ public void copyContent(NodeRef rootNode, String fromPath, String toPath) throws FileNotFoundException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("copyContent from:" + fromPath + " to:" + toPath);
+ }
+
+ NodeRef sourceNodeRef = getNodeForPath(rootNode, fromPath);
+ NodeRef targetNodeRef = getNodeForPath(rootNode, toPath);
+
+ Serializable prop = nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CONTENT);
+ if(prop != null)
+ {
+ nodeService.setProperty(targetNodeRef, ContentModel.PROP_CONTENT, prop);
+ }
+ else
+ {
+ // No content to set
+ logger.debug("no content to save");
+ }
+
+ }
+
+ @Override
+ public NetworkFile createFile(NodeRef rootNode, String path)
+ throws IOException
+ {
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("createFile :" + path);
+ }
+
+ try
+ {
+ // Get the device root
+
+ NodeRef deviceRootNodeRef = rootNode;
+
+ NodeRef nodeRef = cifsHelper.createNode(deviceRootNodeRef, path, ContentModel.TYPE_CONTENT);
+
+ nodeService.addAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT, null);
+
+ // Create the network file
+
+// ContentNetworkFile netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, getCifsHelper(), nodeRef, params, sess);
+
+ File file = TempFileProvider.createTempFile("cifs", ".bin");
+
+ TempNetworkFile netFile = new TempNetworkFile(file, path);
+
+ // Always allow write access to a newly created file
+ netFile.setGrantedAccess(NetworkFile.READWRITE);
+
+ // Truncate the file so that the content stream is created
+ //netFile.truncateFile( 0L);
+
+ // Generate a file id for the file
+
+ if ( netFile != null)
+ {
+ long id = DefaultTypeConverter.INSTANCE.convert(Long.class, nodeService.getProperty(nodeRef, ContentModel.PROP_NODE_DBID));
+ netFile.setFileId((int) (id & 0xFFFFFFFFL));
+ }
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Created file: path=" + path + " node=" + nodeRef + " network file=" + netFile);
+ }
+
+ // Return the new network file
+
+ return netFile;
+ }
+ catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Create file - access denied, " + path);
+ }
+
+ // Convert to a filesystem access denied status
+
+ throw new AccessDeniedException("Unable to create file " + path);
+ }
+ catch (IOException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Create file - content I/O error, " + path);
+ }
+
+ throw ex;
+ }
+ catch (ContentIOException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Create file - content I/O error, " + path);
+ }
+ // Convert to a filesystem disk full status
+
+ throw new DiskFullException("Unable to create file " + path);
+ }
+ catch (AlfrescoRuntimeException ex)
+ {
+ // Debug
+
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Create file error", ex);
+ }
+
+ // Convert to a general I/O exception
+
+ throw new IOException("Unable to create file " + path, ex);
+ }
+ }
+
+
+ /**
+ * Close the file.
+ *
+ * @exception java.io.IOException If an error occurs.
+ */
+ public void closeFile(NodeRef rootNode, String path, NetworkFile file) throws IOException
+ {
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Close file:" + path);
+ }
+
+ if( file instanceof PseudoNetworkFile)
+ {
+ file.close();
+ return;
+ }
+
+ /**
+ * Delete on close attribute - node needs to be deleted.
+ */
+ if(file.hasDeleteOnClose())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("closeFile has delete on close set");
+ }
+
+ NodeRef target = getCifsHelper().getNodeRef(rootNode, path);
+ if(target!=null)
+ {
+ nodeService.deleteNode(target);
+ }
+
+ // Still need to close the open file handle.
+ file.close();
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Closed file: network file=" + file + " delete on close=" + file.hasDeleteOnClose());
+ }
+
+ return;
+ }
+
+ // Check for a temp file - which will be a new file or a read/write file
+ if ( file instanceof TempNetworkFile)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Got a temp network file ");
+ }
+
+ TempNetworkFile tempFile =(TempNetworkFile)file;
+
+ tempFile.flushFile();
+ tempFile.close();
+
+ // Take an initial guess at the mimetype (if it has not been set by something already)
+ String mimetype = mimetypeService.guessMimetype(tempFile.getFullName(), new FileContentReader(tempFile.getFile()));
+ logger.debug("guesssed mimetype:" + mimetype);
+
+ String encoding;
+ // Take a guess at the locale
+ InputStream is = new BufferedInputStream(new FileInputStream(tempFile.getFile()));
+ try
+ {
+ ContentCharsetFinder charsetFinder = mimetypeService.getContentCharsetFinder();
+ Charset charset = charsetFinder.getCharset(is, mimetype);
+ encoding = charset.name();
+ }
+ finally
+ {
+ if(is != null)
+ {
+ is.close();
+ }
+ }
+
+ NodeRef target = getCifsHelper().getNodeRef(rootNode, tempFile.getFullName());
+ ContentWriter writer = contentService.getWriter(target, ContentModel.PROP_CONTENT, true);
+ writer.setMimetype(mimetype);
+ writer.setEncoding(encoding);
+ writer.putContent(tempFile.getFile());
+
+ long size = writer.getSize();
+ if(nodeService.hasAspect(target, ContentModel.ASPECT_NO_CONTENT) && size > 0)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("removed no content aspect");
+ }
+ nodeService.removeAspect(target, ContentModel.ASPECT_NO_CONTENT);
+ }
+ }
+
+ try
+ {
+ // Defer to the network file to close the stream and remove the content
+
+ file.close();
+
+ // DEBUG
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Closed file: network file=" + file + " delete on close=" + file.hasDeleteOnClose());
+
+ if ( file.hasDeleteOnClose() == false && file instanceof ContentNetworkFile)
+ {
+ ContentNetworkFile cFile = (ContentNetworkFile) file;
+ logger.debug(" File " + file.getFullName() + ", version=" + nodeService.getProperty( cFile.getNodeRef(), ContentModel.PROP_VERSION_LABEL));
+ }
+ }
+ }
+ // Make sure we clean up before propagating exceptions
+ catch (IOException e)
+ {
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Exception in closeFile - ", e);
+ }
+ throw e;
+ }
+ catch (Error e)
+ {
+ if ( logger.isDebugEnabled())
+ {
+ logger.debug("Exception in closeFile - ", e);
+ }
+
+ throw e;
+ }
+ }
+
+ /**
+ *
+ * @param session
+ * @param tree
+ * @param file
+ */
+ public void reduceQuota(SrvSession session, TreeConnection tree, NetworkFile file)
+ {
+ if(file.hasDeleteOnClose())
+ {
+ final ContentContext ctx = (ContentContext) tree.getContext();
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("closeFile has delete on close set");
+ }
+
+ if(file instanceof TempNetworkFile)
+ {
+ TempNetworkFile tnf = (TempNetworkFile)file;
+ final QuotaManager quotaMgr = ctx.getQuotaManager();
+ if (quotaMgr != null)
+ {
+ try
+ {
+ quotaMgr.releaseSpace(session, tree, file.getFileId(), file.getName(), tnf.getFileSizeInt());
+ }
+ catch (IOException e)
+ {
+ logger.error(e);
+ }
+ }
+ }
+ }
+ }
+
+ public void deleteEmptyFile(NodeRef rootNode, String path)
+ {
+ try
+ {
+ NodeRef target = getCifsHelper().getNodeRef(rootNode, path);
+ if(target!=null)
+ {
+ if (nodeService.hasAspect(target, ContentModel.ASPECT_NO_CONTENT))
+ {
+ nodeService.deleteNode(target);
+ }
+ }
+ }
+ catch(IOException ne)
+ {
+ // Do nothing
+ }
+ }
+
+
+ @Override
+ public OpLockManager getOpLockManager(SrvSession sess, TreeConnection tree)
+ {
+ return lockManager;
+ }
+
+ @Override
+ public boolean isOpLocksEnabled(SrvSession sess, TreeConnection tree)
+ {
+ return true;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java
index a72d181e8d..82cfd44bb8 100644
--- a/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java
+++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java
@@ -18,6 +18,7 @@
*/
package org.alfresco.filesys.repo;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
@@ -27,20 +28,24 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Date;
import javax.transaction.UserTransaction;
import javax.xml.ws.Holder;
import junit.framework.TestCase;
+import org.alfresco.filesys.alfresco.ExtendedDiskInterface;
import org.alfresco.jlan.server.NetworkServer;
import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.auth.ClientInfo;
import org.alfresco.jlan.server.config.ServerConfiguration;
import org.alfresco.jlan.server.core.DeviceContext;
import org.alfresco.jlan.server.core.DeviceContextException;
import org.alfresco.jlan.server.core.SharedDevice;
import org.alfresco.jlan.server.filesys.AccessDeniedException;
import org.alfresco.jlan.server.filesys.AccessMode;
+import org.alfresco.jlan.server.filesys.DiskInterface;
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
import org.alfresco.jlan.server.filesys.FileAction;
import org.alfresco.jlan.server.filesys.FileAttribute;
@@ -72,6 +77,7 @@ import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
+import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.MLText;
@@ -94,7 +100,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ClassPathResource;
-import org.springframework.extensions.config.element.GenericConfigElement;
/**
* Unit tests for Alfresco Repository ContentDiskDriver
@@ -109,7 +114,7 @@ public class ContentDiskDriverTest extends TestCase
private Repository repositoryHelper;
private CifsHelper cifsHelper;
- private ContentDiskDriver driver;
+ private ExtendedDiskInterface driver;
private NodeService mlAwareNodeService;
private NodeService nodeService;
private TransactionService transactionService;
@@ -140,7 +145,7 @@ public class ContentDiskDriverTest extends TestCase
repositoryHelper = (Repository)this.applicationContext.getBean("repositoryHelper");
ApplicationContextFactory fileServers = (ApplicationContextFactory) this.applicationContext.getBean("fileServers");
cifsHelper = (CifsHelper) fileServers.getApplicationContext().getBean("cifsHelper");
- driver = (ContentDiskDriver) fileServers.getApplicationContext().getBean("contentDiskDriver");
+ driver = (ExtendedDiskInterface)this.applicationContext.getBean("contentDiskDriver");
mlAwareNodeService = (NodeService) this.applicationContext.getBean("mlAwareNodeService");
nodeService = (NodeService)applicationContext.getBean("nodeService");
transactionService = (TransactionService)applicationContext.getBean("transactionService");
@@ -152,7 +157,7 @@ public class ContentDiskDriverTest extends TestCase
permissionService = (PermissionService) this.applicationContext.getBean("permissionService");
ownableService = (OwnableService) this.applicationContext.getBean("ownableService");
fileFolderService = (FileFolderService) this.applicationContext.getBean("fileFolderService");
-
+
assertNotNull("content disk driver is null", driver);
assertNotNull("repositoryHelper is null", repositoryHelper);
assertNotNull("mlAwareNodeService is null", mlAwareNodeService);
@@ -161,6 +166,7 @@ public class ContentDiskDriverTest extends TestCase
assertNotNull("contentService is null", contentService);
assertNotNull("ruleService is null", ruleService);
assertNotNull("actionService is null", actionService);
+ assertNotNull("cifsHelper", cifsHelper);
AuthenticationUtil.setRunAsUserSystem();
@@ -189,47 +195,14 @@ public class ContentDiskDriverTest extends TestCase
@Override
protected void tearDown() throws Exception
{
-// UserTransaction txn = transactionService.getUserTransaction();
-// assertNotNull("transaction leaked", txn);
-// txn.getStatus();
-// txn.rollback();
}
-
private DiskSharedDevice getDiskSharedDevice() throws DeviceContextException
{
- ServerConfiguration scfg = new ServerConfiguration("testServer");
-
- FilesystemsConfigSection fcfg = new FilesystemsConfigSection(scfg);
-
-// TestServer testServer = new TestServer("testServer", scfg);
-// SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
-//
-// GenericConfigElement cfg1 = new GenericConfigElement("filesystem");
-//
-// GenericConfigElement store = new GenericConfigElement("store");
-// store.setValue(STORE_NAME);
-// cfg1.addChild(store);
-//
-// GenericConfigElement rootPath = new GenericConfigElement("rootPath");
-// rootPath.setValue(ROOT_PATH);
-// cfg1.addChild(rootPath);
-//
-// ContentContext filesysContext = (ContentContext) driver.createContext(STORE_NAME, cfg1);
-//
-// DiskSharedDevice share = new DiskSharedDevice("test", driver, filesysContext);
ContentContext ctx = new ContentContext( "testContext", STORE_NAME, ROOT_PATH, repositoryHelper.getCompanyHome());
-
- ServerConfigurationBean scb = new ServerConfigurationBean("testServer");
-
- scb.addConfigSection(fcfg);
- ctx.setServerConfigurationBean(scb);
- ctx.enableStateCache(true);
-
- DiskSharedDevice share = new DiskSharedDevice("test", driver, ctx);
-
-
+
+ DiskSharedDevice share = new DiskSharedDevice("test", driver, ctx);
return share;
}
@@ -241,11 +214,10 @@ public class ContentDiskDriverTest extends TestCase
logger.debug("testCreatedFile");
ServerConfiguration scfg = new ServerConfiguration("testServer");
TestServer testServer = new TestServer("testServer", scfg);
- SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
+ final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
DiskSharedDevice share = getDiskSharedDevice();
- TreeConnection testConnection = testServer.getTreeConnection(share);
-
+ final TreeConnection testConnection = testServer.getTreeConnection(share);
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
class TestContext
@@ -260,7 +232,8 @@ public class ContentDiskDriverTest extends TestCase
*/
int openAction = FileAction.CreateNotExist;
- final String FILE_NAME="testCreateFile2.new";
+
+ final String FILE_NAME="testCreateFile.new";
final String FILE_PATH="\\"+FILE_NAME;
FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
@@ -275,8 +248,8 @@ public class ContentDiskDriverTest extends TestCase
public Void execute() throws Throwable
{
byte[] stuff = "Hello World".getBytes();
- file.writeFile(stuff, stuff.length, 0, 0);
- file.close(); // needed to actually flush content to node
+ driver.writeFile(testSession, testConnection, file, stuff, stuff.length, 0, 0);
+ driver.closeFile(testSession, testConnection, file);
return null;
}
};
@@ -302,7 +275,7 @@ public class ContentDiskDriverTest extends TestCase
FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
assertNotNull("info is null", info);
- NodeRef n2 = driver.getNodeForPath(testConnection, FILE_PATH);
+ NodeRef n2 = getNodeForPath(testConnection, FILE_PATH);
assertEquals("get Node For Path returned different node", testContext.testNodeRef, n2);
/**
@@ -348,24 +321,6 @@ public class ContentDiskDriverTest extends TestCase
SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
DiskSharedDevice share = getDiskSharedDevice();
-
-// ServerConfiguration scfg = new ServerConfiguration("testServer");
-// TestServer testServer = new TestServer("testServer", scfg);
-// SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
-//
-// GenericConfigElement cfg1 = new GenericConfigElement("filesystem");
-//
-// GenericConfigElement store = new GenericConfigElement("store");
-// store.setValue(STORE_NAME);
-// cfg1.addChild(store);
-//
-// GenericConfigElement rootPath = new GenericConfigElement("rootPath");
-// rootPath.setValue(ROOT_PATH);
-// cfg1.addChild(rootPath);
-//
-// ContentContext filesysContext = (ContentContext) driver.createContext(STORE_NAME, cfg1);
-//
-// DiskSharedDevice share = new DiskSharedDevice("test", driver, filesysContext);
TreeConnection testConnection = testServer.getTreeConnection(share);
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
@@ -374,7 +329,7 @@ public class ContentDiskDriverTest extends TestCase
* Step 1 : Create a new file in read/write mode and add some content.
*/
int openAction = FileAction.CreateNotExist;
- String FILE_PATH="\\testDeleteFile.new";
+ String FILE_PATH="\\testDeleteFileX.new";
FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
@@ -424,169 +379,178 @@ public class ContentDiskDriverTest extends TestCase
* SetModifyDate
*
*/
- /*
- * MER : I can't see what DeleteOnClose does. Test commented out
- */
-// public void testSetFileInfo() throws Exception
-// {
-// logger.debug("testSetFileInfo");
-// ServerConfiguration scfg = new ServerConfiguration("testServer");
-// TestServer testServer = new TestServer("testServer", scfg);
-// final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
-// DiskSharedDevice share = getDiskSharedDevice();
-// final TreeConnection testConnection = testServer.getTreeConnection(share);
-// final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
-//
-// Date now = new Date();
-//
-// // CREATE 6 hours ago
-// final Date CREATED = new Date(now.getTime() - 1000 * 60 * 60 * 6);
-// // Modify one hour ago
-// final Date MODIFIED = new Date(now.getTime() - 1000 * 60 * 60 * 1);
-//
-// class TestContext
-// {
-// NodeRef testNodeRef;
-// };
-//
-// final TestContext testContext = new TestContext();
-//
-// /**
-// * Step 1 : Create a new file in read/write mode and add some content.
-// * Call SetInfo to set the creation date
-// */
-// int openAction = FileAction.CreateNotExist;
-//
-// final String FILE_NAME="testSetFileInfo.txt";
-// final String FILE_PATH="\\"+FILE_NAME;
-//
-// final FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
-//
-// final NetworkFile file = driver.createFile(testSession, testConnection, params);
-// assertNotNull("file is null", file);
-// assertFalse("file is read only, should be read-write", file.isReadOnly());
-//
-// RetryingTransactionCallback writeStuffCB = new RetryingTransactionCallback() {
-//
-// @Override
-// public Void execute() throws Throwable
-// {
-// byte[] stuff = "Hello World".getBytes();
-// file.writeFile(stuff, stuff.length, 0, 0);
-// file.close(); // needed to actually flush content to node
-//
-// FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
-// info.setFileInformationFlags(FileInfo.SetModifyDate);
-// info.setModifyDateTime(MODIFIED.getTime());
-// driver.setFileInformation(testSession, testConnection, FILE_PATH, info);
-// return null;
-// }
-// };
-// tran.doInTransaction(writeStuffCB);
-//
-// RetryingTransactionCallback validateCB = new RetryingTransactionCallback() {
-//
-// @Override
-// public Void execute() throws Throwable
-// {
-// NodeRef companyHome = repositoryHelper.getCompanyHome();
-// NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
-// testContext.testNodeRef = newNode;
-// assertNotNull("can't find new node", newNode);
-// Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT);
-// assertNotNull("content is null", content);
-// Date modified = (Date)nodeService.getProperty(newNode, ContentModel.PROP_MODIFIED);
-// assertEquals("modified time not set correctly", MODIFIED, modified);
-// return null;
-// }
-// };
-// tran.doInTransaction(validateCB);
-//
-// /**
-// * Step 2: Change the created date
-// */
-// logger.debug("Step 2: Change the created date");
-// RetryingTransactionCallback changeCreatedCB = new RetryingTransactionCallback() {
-//
-// @Override
-// public Void execute() throws Throwable
-// {
-// FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
-// info.setFileInformationFlags(FileInfo.SetCreationDate);
-// info.setCreationDateTime(CREATED.getTime());
-// driver.setFileInformation(testSession, testConnection, FILE_PATH, info);
-// return null;
-// }
-// };
-// tran.doInTransaction(changeCreatedCB);
-//
-// RetryingTransactionCallback validateCreatedCB = new RetryingTransactionCallback() {
-//
-// @Override
-// public Void execute() throws Throwable
-// {
-// NodeRef companyHome = repositoryHelper.getCompanyHome();
-// NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
-// testContext.testNodeRef = newNode;
-// assertNotNull("can't find new node", newNode);
-// Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT);
-// assertNotNull("content is null", content);
-// Date created = (Date)nodeService.getProperty(newNode, ContentModel.PROP_CREATED);
-// assertEquals("created time not set correctly", CREATED, created);
-// return null;
-// }
-// };
-// tran.doInTransaction(validateCreatedCB);
-//
-//// /**
-//// * Step 3: Test
-//// */
-//// logger.debug("Step 3: test deleteOnClose");
-//// RetryingTransactionCallback deleteOnCloseCB = new RetryingTransactionCallback() {
-////
-//// @Override
-//// public Void execute() throws Throwable
-//// {
-//// NetworkFile f2 = driver.openFile(testSession, testConnection, params);
-////
-//// FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
-//// info.setFileInformationFlags(FileInfo.SetDeleteOnClose);
-//// driver.setFileInformation(testSession, testConnection, FILE_PATH, info);
-////
-//// byte[] stuff = "Update".getBytes();
-//// f2.writeFile(stuff, stuff.length, 0, 0);
-//// f2.close(); // needed to actually flush content to node
-////
-//// return null;
-//// }
-//// };
-//// tran.doInTransaction(deleteOnCloseCB);
-////
-//// RetryingTransactionCallback validateDeleteOnCloseCB = new RetryingTransactionCallback() {
-////
-//// @Override
-//// public Void execute() throws Throwable
-//// {
-//// NodeRef companyHome = repositoryHelper.getCompanyHome();
-//// NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
-//// assertNull("can still find new node", newNode);
-//// return null;
-//// }
-//// };
-//// tran.doInTransaction(validateDeleteOnCloseCB);
-//
-// // clean up so we could run the test again
-// driver.deleteFile(testSession, testConnection, FILE_PATH);
-//
-// } // test set file info
+ public void testSetFileInfo() throws Exception
+ {
+ logger.debug("testSetFileInfo");
+ ServerConfiguration scfg = new ServerConfiguration("testServer");
+ TestServer testServer = new TestServer("testServer", scfg);
+ final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
+ DiskSharedDevice share = getDiskSharedDevice();
+ final TreeConnection testConnection = testServer.getTreeConnection(share);
+ final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
+
+ Date now = new Date();
+
+ // CREATE 6 hours ago
+ final Date CREATED = new Date(now.getTime() - 1000 * 60 * 60 * 6);
+ // Modify one hour ago
+ final Date MODIFIED = new Date(now.getTime() - 1000 * 60 * 60 * 1);
+
+ class TestContext
+ {
+ NodeRef testNodeRef;
+ };
+
+ final TestContext testContext = new TestContext();
+
+
+
+ /**
+ * Step 1 : Create a new file in read/write mode and add some content.
+ * Call SetInfo to set the creation date
+ */
+ int openAction = FileAction.CreateNotExist;
+
+ final String FILE_NAME="testSetFileInfo.txt";
+ final String FILE_PATH="\\"+FILE_NAME;
+
+ // Clean up junk if it exists
+ try
+ {
+ driver.deleteFile(testSession, testConnection, FILE_PATH);
+ }
+ catch (IOException ie)
+ {
+ // expect to go here
+ }
+
+ final FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
+
+ final NetworkFile file = driver.createFile(testSession, testConnection, params);
+ assertNotNull("file is null", file);
+ assertFalse("file is read only, should be read-write", file.isReadOnly());
+
+ RetryingTransactionCallback writeStuffCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ byte[] stuff = "Hello World".getBytes();
+
+ driver.writeFile(testSession, testConnection, file, stuff, 0, stuff.length, 0);
+ driver.closeFile(testSession, testConnection, file);
+
+ FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
+ info.setFileInformationFlags(FileInfo.SetModifyDate);
+ info.setModifyDateTime(MODIFIED.getTime());
+ driver.setFileInformation(testSession, testConnection, FILE_PATH, info);
+ return null;
+ }
+ };
+ tran.doInTransaction(writeStuffCB);
+
+ RetryingTransactionCallback validateCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ NodeRef companyHome = repositoryHelper.getCompanyHome();
+ NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
+ testContext.testNodeRef = newNode;
+ assertNotNull("can't find new node", newNode);
+ Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT);
+ assertNotNull("content is null", content);
+ Date modified = (Date)nodeService.getProperty(newNode, ContentModel.PROP_MODIFIED);
+ assertEquals("modified time not set correctly", MODIFIED, modified);
+ return null;
+ }
+ };
+ tran.doInTransaction(validateCB);
+
+ /**
+ * Step 2: Change the created date
+ */
+ logger.debug("Step 2: Change the created date");
+ RetryingTransactionCallback changeCreatedCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
+ info.setFileInformationFlags(FileInfo.SetCreationDate);
+ info.setCreationDateTime(CREATED.getTime());
+ driver.setFileInformation(testSession, testConnection, FILE_PATH, info);
+ return null;
+ }
+ };
+ tran.doInTransaction(changeCreatedCB);
+
+ RetryingTransactionCallback validateCreatedCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ NodeRef companyHome = repositoryHelper.getCompanyHome();
+ NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
+ testContext.testNodeRef = newNode;
+ assertNotNull("can't find new node", newNode);
+ Serializable content = nodeService.getProperty(newNode, ContentModel.PROP_CONTENT);
+ assertNotNull("content is null", content);
+ Date created = (Date)nodeService.getProperty(newNode, ContentModel.PROP_CREATED);
+ assertEquals("created time not set correctly", CREATED, created);
+ return null;
+ }
+ };
+ tran.doInTransaction(validateCreatedCB);
+
+ /**
+ * Step 3: Test
+ */
+ logger.debug("Step 3: test deleteOnClose");
+ RetryingTransactionCallback deleteOnCloseCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ NetworkFile f2 = driver.openFile(testSession, testConnection, params);
+
+ FileInfo info = driver.getFileInformation(testSession, testConnection, FILE_PATH);
+ info.setFileInformationFlags(FileInfo.SetDeleteOnClose);
+ driver.setFileInformation(testSession, testConnection, FILE_PATH, info);
+ file.setDeleteOnClose(true);
+
+ byte[] stuff = "Update".getBytes();
+ driver.writeFile(testSession, testConnection, file, stuff, 0, stuff.length, 0);
+ driver.closeFile(testSession, testConnection, file);
+
+ return null;
+ }
+ };
+ tran.doInTransaction(deleteOnCloseCB);
+
+ RetryingTransactionCallback validateDeleteOnCloseCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ NodeRef companyHome = repositoryHelper.getCompanyHome();
+ NodeRef newNode = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME);
+ assertNull("can still find new node", newNode);
+ return null;
+ }
+ };
+ tran.doInTransaction(validateDeleteOnCloseCB);
+
+ // clean up so we could run the test again
+ //driver.deleteFile(testSession, testConnection, FILE_PATH);
+
+ } // test set file info
/**
* Test Open File
- *
- * MER DISABLED TEST 22/03/2011 won't run.
*/
- public void DISABLED_testOpenFile() throws Exception
+ public void testOpenFile() throws Exception
{
logger.debug("testOpenFile");
ServerConfiguration scfg = new ServerConfiguration("testServer");
@@ -607,7 +571,7 @@ public class ContentDiskDriverTest extends TestCase
FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);
driver.createDirectory(testSession, testConnection, dirParams);
- testContext.testDirNodeRef = driver.getNodeForPath(testConnection, TEST_ROOT_DOS_PATH);
+ testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_ROOT_DOS_PATH);
/**
* Step 1 : Negative test - try to open a file that does not exist
@@ -644,28 +608,30 @@ public class ContentDiskDriverTest extends TestCase
NetworkFile file = driver.openFile(testSession, testConnection, params);
assertNotNull(file);
- //driver.deleteFile(testSession, testConnection, FILE_PATH);
- // BODGE - there's a dangling transaction that needs getting rid of
- // Work around for ALF-7674
- UserTransaction txn = transactionService.getUserTransaction();
- assertNotNull("transaction leaked", txn);
- txn.getStatus();
- txn.rollback();
+ /**
+ * Step 3: Open the root directory.
+ */
+ logger.debug("Step 3) Open the root directory");
+ FileOpenParams rootParams = new FileOpenParams("\\", FileAction.CreateNotExist, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
+ NetworkFile file3 = driver.openFile(testSession, testConnection, rootParams);
+ assertNotNull(file3);
+
+
} // testOpenFile
/**
* Unit test of file exists
*/
- public void DISABLED_testFileExists() throws Exception
+ public void testFileExists() throws Exception
{
logger.debug("testFileExists");
ServerConfiguration scfg = new ServerConfiguration("testServer");
TestServer testServer = new TestServer("testServer", scfg);
- SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
+ final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
DiskSharedDevice share = getDiskSharedDevice();
- TreeConnection testConnection = testServer.getTreeConnection(share);
+ final TreeConnection testConnection = testServer.getTreeConnection(share);
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
final String FILE_PATH= TEST_ROOT_DOS_PATH + "\\testFileExists.new";
@@ -710,7 +676,7 @@ public class ContentDiskDriverTest extends TestCase
{
byte[] stuff = "Hello World".getBytes();
file.writeFile(stuff, stuff.length, 0, 0);
- file.close();
+ driver.closeFile(testSession, testConnection, file);
return null;
}
@@ -727,15 +693,7 @@ public class ContentDiskDriverTest extends TestCase
driver.deleteFile(testSession, testConnection, FILE_PATH);
status = driver.fileExists(testSession, testConnection, FILE_PATH);
- assertEquals(status, 0);
-
- // BODGE - there's a dangling transaction that needs getting rid of
- // Work around for ALF-7674
- UserTransaction txn = transactionService.getUserTransaction();
- assertNotNull("transaction leaked", txn);
- txn.getStatus();
- txn.rollback();
-
+ assertEquals(status, 0);
} // testFileExists
@@ -752,7 +710,7 @@ public class ContentDiskDriverTest extends TestCase
final TreeConnection testConnection = testServer.getTreeConnection(share);
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
-
+
final String FILE_PATH1=TEST_ROOT_DOS_PATH + "\\SourceFile1.new";
final String FILE_NAME2 = "SourceFile2.new";
final String FILE_PATH2=TEST_ROOT_DOS_PATH +"\\" + FILE_NAME2;
@@ -769,7 +727,7 @@ public class ContentDiskDriverTest extends TestCase
/**
* Step 1 : Negative test, Call Rename for a file which does not exist
- */
+ */
try
{
driver.renameFile(testSession, testConnection, "\\Wibble\\wobble", FILE_PATH1);
@@ -816,7 +774,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- final NodeRef file1NodeRef = driver.getNodeForPath(testConnection, FILE_PATH1);
+ final NodeRef file1NodeRef = getNodeForPath(testConnection, FILE_PATH1);
assertNotNull("node ref not found", file1NodeRef);
nodeService.setProperty(file1NodeRef, ContentModel.PROP_LASTNAME, LAST_NAME);
@@ -832,7 +790,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef file2NodeRef = driver.getNodeForPath(testConnection, FILE_PATH2);
+ NodeRef file2NodeRef = getNodeForPath(testConnection, FILE_PATH2);
//assertEquals("node ref has changed on a rename", file1NodeRef, file2NodeRef);
assertEquals(nodeService.getProperty(file2NodeRef, ContentModel.PROP_LASTNAME), LAST_NAME);
ChildAssociationRef parentRef = nodeService.getPrimaryParent(file2NodeRef);
@@ -852,11 +810,11 @@ public class ContentDiskDriverTest extends TestCase
FileOpenParams params5 = new FileOpenParams(DIR_NEW_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
driver.createDirectory(testSession, testConnection, params5);
- NodeRef newDirNodeRef = driver.getNodeForPath(testConnection, DIR_NEW_PATH);
+ NodeRef newDirNodeRef = getNodeForPath(testConnection, DIR_NEW_PATH);
driver.renameFile(testSession, testConnection, FILE_PATH2, NEW_PATH);
- NodeRef file5NodeRef = driver.getNodeForPath(testConnection, NEW_PATH);
+ NodeRef file5NodeRef = getNodeForPath(testConnection, NEW_PATH);
ChildAssociationRef parentRef5 = nodeService.getPrimaryParent(file5NodeRef);
assertTrue(parentRef5.getParentRef().equals(newDirNodeRef));
@@ -917,7 +875,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef file1NodeRef = driver.getNodeForPath(testConnection, FILE_PATH1);
+ NodeRef file1NodeRef = getNodeForPath(testConnection, FILE_PATH1);
nodeService.addAspect(file1NodeRef, ContentModel.ASPECT_VERSIONABLE, null);
ContentWriter contentWriter2 = contentService.getWriter(file1NodeRef, ContentModel.PROP_CONTENT, true);
@@ -942,7 +900,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef file2NodeRef = driver.getNodeForPath(testConnection, FILE_PATH2);
+ NodeRef file2NodeRef = getNodeForPath(testConnection, FILE_PATH2);
assertNotNull("file2 node ref is null", file2NodeRef);
//assertEquals(nodeService.getProperty(file2NodeRef, ContentModel.PROP_LASTNAME), LAST_NAME);
assertTrue("does not have versionable aspect", nodeService.hasAspect(file2NodeRef, ContentModel.ASPECT_VERSIONABLE));
@@ -1053,7 +1011,7 @@ public class ContentDiskDriverTest extends TestCase
// now load up the node with lots of other stuff that we will test to see if it gets preserved during the
// shuffle.
- testContext.testNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
// test non CM namespace property
nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
@@ -1160,6 +1118,7 @@ public class ContentDiskDriverTest extends TestCase
};
tran.doInTransaction(validateOldFileGoneCB, false, true);
+ logger.debug("Shuffle step next");
/**
* Move the new file into place, stuff should get shuffled
*/
@@ -1174,13 +1133,14 @@ public class ContentDiskDriverTest extends TestCase
};
tran.doInTransaction(moveNewFileCB, false, true);
+ logger.debug("end of shuffle step");
RetryingTransactionCallback validateCB = new RetryingTransactionCallback() {
@Override
public Void execute() throws Throwable
{
- NodeRef shuffledNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
Map props = nodeService.getProperties(shuffledNodeRef);
@@ -1194,23 +1154,21 @@ public class ContentDiskDriverTest extends TestCase
assertEquals("title wrong", FILE_TITLE, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_TITLE) );
assertEquals("description wrong", FILE_DESCRIPTION, nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_DESCRIPTION) );
- // commented out due to ALF-7641
- // CIFS shuffle, does not preseve MLText values.
- // Map mlProps = mlAwareNodeService.getProperties(shuffledNodeRef);
+ // ALF-7641 - CIFS shuffle, does not preseve MLText values.
+ Map mlProps = mlAwareNodeService.getProperties(shuffledNodeRef);
- // MLText multi = (MLText)mlAwareNodeService.getProperty(shuffledNodeRef, RESIDUAL_MTTEXT) ;
- // multi.getValues();
+ MLText multi = (MLText)mlAwareNodeService.getProperty(shuffledNodeRef, RESIDUAL_MTTEXT) ;
+ assertTrue("MLText has lost values", multi.getValues().size() > 2);
- // check auditable properties
- // commented out due to ALF-7635
- // assertEquals("creation date not preserved", ((Date)testContext.testCreatedDate).getTime(), ((Date)nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_CREATED)).getTime());
+// // ALF-7635 check auditable properties
+ assertEquals("creation date not preserved", ((java.util.Date)testContext.testCreatedDate).getTime(), ((java.util.Date)nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_CREATED)).getTime());
- // commented out due to ALF-7628
- // assertEquals("ADDRESSEE PROPERTY Not copied", "Fred", nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_ADDRESSEE));
- // assertTrue("CLASSIFIABLE aspect not present", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_CLASSIFIABLE));
+ // ALF-7628 - preserve addressee and classifiable
+ assertEquals("ADDRESSEE PROPERTY Not copied", "Fred", nodeService.getProperty(shuffledNodeRef, ContentModel.PROP_ADDRESSEE));
+ assertTrue("CLASSIFIABLE aspect not present", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_CLASSIFIABLE));
- // commented out due to ALF-7584.
- // assertEquals("noderef changed", testContext.testNodeRef, shuffledNodeRef);
+ // ALF-7584 - preserve node ref.
+ assertEquals("noderef changed", testContext.testNodeRef, shuffledNodeRef);
return null;
}
};
@@ -1300,7 +1258,7 @@ public class ContentDiskDriverTest extends TestCase
// now load up the node with lots of other stuff that we will test to see if it gets preserved during the
// shuffle.
- testContext.testNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
// test non CM namespace property
nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
// test CM property not related to an aspect
@@ -1407,7 +1365,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef shuffledNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
Map props = nodeService.getProperties(shuffledNodeRef);
assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
@@ -1443,8 +1401,8 @@ public class ContentDiskDriverTest extends TestCase
{
logger.debug("testScenarioMSWord2007SaveShuffle");
final String FILE_NAME = "TEST.DOCX";
- final String FILE_OLD_TEMP = "00000001.TMP";
- final String FILE_NEW_TEMP = "00000002.TMP";
+ final String FILE_OLD_TEMP = "788A1D3D.tmp";
+ final String FILE_NEW_TEMP = "19ECA1A.tmp";
class TestContext
{
@@ -1488,7 +1446,7 @@ public class ContentDiskDriverTest extends TestCase
assertNotNull(testContext.firstFileHandle);
// no need to test lots of different properties, that's already been tested above
- testContext.testNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
return null;
@@ -1580,7 +1538,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef shuffledNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
Map props = nodeService.getProperties(shuffledNodeRef);
assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
@@ -1650,7 +1608,7 @@ public class ContentDiskDriverTest extends TestCase
assertNotNull(testContext.firstFileHandle);
// no need to test lots of different properties, that's already been tested above
- testContext.testNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
return null;
@@ -1715,7 +1673,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef shuffledNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
Map props = nodeService.getProperties(shuffledNodeRef);
assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
@@ -1734,8 +1692,9 @@ public class ContentDiskDriverTest extends TestCase
* a) viTest.txt
* b) Rename original file to viTest.txt~
* c) Create viTest.txt
+ * d) Delete viTest.txt~
*/
- public void DISABLED_testScenarioViSave() throws Exception
+ public void testScenarioViSave() throws Exception
{
logger.debug("testScenarioViSave");
final String FILE_NAME = "viTest.txt";
@@ -1784,7 +1743,7 @@ public class ContentDiskDriverTest extends TestCase
assertNotNull(testContext.firstFileHandle);
// no need to test lots of different properties, that's already been tested above
- testContext.testNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
return null;
@@ -1803,7 +1762,7 @@ public class ContentDiskDriverTest extends TestCase
String testContent = "Emacs shuffle test";
byte[] testContentBytes = testContent.getBytes();
testContext.firstFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
- testContext.firstFileHandle.close();
+ driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
return null;
}
};
@@ -1834,11 +1793,12 @@ public class ContentDiskDriverTest extends TestCase
FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
testContext.newFileHandle = driver.createFile(testSession, testConnection, createFileParams);
assertNotNull(testContext.newFileHandle);
- String testContent = "EMACS shuffle test This is new content";
+ String testContent = "Vi shuffle test This is new content";
byte[] testContentBytes = testContent.getBytes();
testContext.newFileHandle.writeFile(testContentBytes, testContentBytes.length, 0, 0);
- testContext.newFileHandle.close();
-
+ driver.closeFile(testSession, testConnection, testContext.newFileHandle);
+ logger.debug("delete temporary file - which will trigger shuffle");
+ driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + FILE_OLD_TEMP);
return null;
}
};
@@ -1849,7 +1809,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef shuffledNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
assertNotNull("shuffledNodeRef is null", shuffledNodeRef);
Map props = nodeService.getProperties(shuffledNodeRef);
@@ -1922,7 +1882,7 @@ public class ContentDiskDriverTest extends TestCase
assertNotNull(testContext.firstFileHandle);
// no need to test lots of different properties, that's already been tested above
- testContext.testNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
nodeService.setProperty(testContext.testNodeRef, TransferModel.PROP_ENABLED, true);
return null;
@@ -2002,7 +1962,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef shuffledNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
Map props = nodeService.getProperties(shuffledNodeRef);
assertTrue("node does not contain shuffled ENABLED property", props.containsKey(TransferModel.PROP_ENABLED));
@@ -2034,6 +1994,7 @@ public class ContentDiskDriverTest extends TestCase
int openAction = FileAction.CreateNotExist;
final String FILE_NAME="testDeleteFileViaNodeService.new";
final String FILE_PATH="\\" + FILE_NAME;
+
FileOpenParams params = new FileOpenParams(FILE_PATH, openAction, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
@@ -2083,7 +2044,7 @@ public class ContentDiskDriverTest extends TestCase
try
{
- driver.getNodeForPath(testConnection, FILE_PATH);
+ getNodeForPath(testConnection, FILE_PATH);
fail("getNode for path unexpectedly succeeded");
}
catch (IOException ie)
@@ -2181,7 +2142,7 @@ public class ContentDiskDriverTest extends TestCase
driver.createDirectory(testSession, testConnection, createRootDirParams);
driver.createDirectory(testSession, testConnection, createDirParams);
- testContext.testDirNodeRef = driver.getNodeForPath(testConnection, TEST_DIR);
+ testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_DIR);
assertNotNull("testDirNodeRef is null", testContext.testDirNodeRef);
UserTransaction txn = transactionService.getUserTransaction();
@@ -2248,7 +2209,7 @@ public class ContentDiskDriverTest extends TestCase
// now load up the node with lots of other stuff that we will test to see if it gets preserved during the
// shuffle.
- testContext.testNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
assertNotNull("testContext.testNodeRef is null", testContext.testNodeRef);
// test non CM namespace property
@@ -2280,7 +2241,8 @@ public class ContentDiskDriverTest extends TestCase
int i = is.read(buffer, 0, buffer.length);
while(i > 0)
{
- testContext.firstFileHandle.writeFile(buffer, i, 0, offset);
+ // testContext.firstFileHandle.writeFile(buffer, i, 0, offset);
+ driver.writeFile(testSession, testConnection, testContext.firstFileHandle, buffer, 0, i, offset);
offset += i;
i = is.read(buffer, 0, buffer.length);
}
@@ -2290,7 +2252,8 @@ public class ContentDiskDriverTest extends TestCase
is.close();
}
- testContext.firstFileHandle.close();
+ logger.debug("close the file, firstFileHandle");
+ driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
return null;
}
@@ -2310,15 +2273,17 @@ public class ContentDiskDriverTest extends TestCase
Map props = nodeService.getProperties(testContext.testNodeRef);
assertTrue("Enabled property has been lost", props.containsKey(TransferModel.PROP_ENABLED));
-
+
+ ContentData data = (ContentData)props.get(ContentModel.PROP_CONTENT);
+ assertEquals("size is wrong", 11302, data.getSize());
+ assertEquals("mimeType is wrong", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", data.getMimetype());
+
// These metadata values should be extracted.
assertEquals("description is not correct", "This is a test file", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_DESCRIPTION));
assertEquals("title is not correct", "ContentDiskDriverTest", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_TITLE));
assertEquals("author is not correct", "mrogers", nodeService.getProperty(testContext.testNodeRef, ContentModel.PROP_AUTHOR));
- ContentData data = (ContentData)props.get(ContentModel.PROP_CONTENT);
- assertEquals("mimeType is wrong", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", data.getMimetype());
- assertEquals("size is wrong", 11302, data.getSize());
+
return null;
}
@@ -2373,7 +2338,7 @@ public class ContentDiskDriverTest extends TestCase
is.close();
}
- testContext.secondFileHandle.close();
+ driver.closeFile(testSession, testConnection, testContext.secondFileHandle);
return null;
}
@@ -2462,7 +2427,7 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef shuffledNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
Map props = nodeService.getProperties(shuffledNodeRef);
@@ -2551,7 +2516,7 @@ public class ContentDiskDriverTest extends TestCase
final TreeConnection treeConnection = server.getTreeConnection(device);
// Getting target entity for testing - ContentDiskDriver
- final ContentDiskDriver deviceInterface = (ContentDiskDriver) treeConnection.getInterface();
+ final ExtendedDiskInterface deviceInterface = (ExtendedDiskInterface) treeConnection.getInterface();
// Creating mock-session
final SrvSession session = new TestSrvSession(13, server, ContentDiskDriverTest.TEST_PROTOTYPE_NAME, ContentDiskDriverTest.TEST_REMOTE_NAME);
@@ -2825,7 +2790,7 @@ public class ContentDiskDriverTest extends TestCase
is.close();
}
- testContext.firstFileHandle.close();
+ driver.closeFile(testSession, testConnection, testContext.firstFileHandle);
return null;
}
@@ -2872,11 +2837,12 @@ public class ContentDiskDriverTest extends TestCase
@Override
public Void execute() throws Throwable
{
- NodeRef shuffledNodeRef = driver.getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
+ NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME);
Map props = nodeService.getProperties(shuffledNodeRef);
ContentData data = (ContentData)props.get(ContentModel.PROP_CONTENT);
+ assertNotNull("data is null", data);
assertEquals("size is wrong", 26112, data.getSize());
assertEquals("mimeType is wrong", "application/msword",data.getMimetype());
@@ -2888,6 +2854,165 @@ public class ContentDiskDriverTest extends TestCase
}
+ /**
+ * Test Open Close File Scenario
+ *
+ * 1) open(readOnly)
+ * 2) open(readWrite)
+ * 3) open(readWrite) - does nothing.
+ * 4) close - does nothing
+ * 5) close - does nothing
+ * 6) close - updates the repo
+ */
+ public void testScenarioOpenCloseFile() throws Exception
+ {
+ logger.debug("start of testScenarioOpenCloseFile");
+ ServerConfiguration scfg = new ServerConfiguration("testServer");
+ TestServer testServer = new TestServer("testServer", scfg);
+ final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName");
+ DiskSharedDevice share = getDiskSharedDevice();
+ final TreeConnection testConnection = testServer.getTreeConnection(share);
+ final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
+
+ class TestContext
+ {
+ NodeRef testDirNodeRef;
+ NodeRef targetNodeRef;
+ };
+
+ final TestContext testContext = new TestContext();
+
+ final String FILE_NAME="testScenarioOpenFile.txt";
+ final String FILE_PATH= TEST_ROOT_DOS_PATH + "\\" + FILE_NAME;
+
+ FileOpenParams dirParams = new FileOpenParams(TEST_ROOT_DOS_PATH, 0, AccessMode.ReadOnly, FileAttribute.NTDirectory, 0);
+ driver.createDirectory(testSession, testConnection, dirParams);
+
+ testContext.testDirNodeRef = getNodeForPath(testConnection, TEST_ROOT_DOS_PATH);
+
+ /**
+ * Clean up just in case garbage is left from a previous run
+ */
+ RetryingTransactionCallback deleteGarbageFileCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ driver.deleteFile(testSession, testConnection, FILE_PATH);
+ return null;
+ }
+ };
+ try
+ {
+ tran.doInTransaction(deleteGarbageFileCB);
+ }
+ catch (Exception e)
+ {
+ // expect to go here
+ }
+
+ /**
+ * Step 1: Now create the file through the node service and open it.
+ */
+ logger.debug("Step 1) Create File and Open file created by node service");
+ RetryingTransactionCallback createFileCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ logger.debug("create file and close it immediatly");
+ FileOpenParams createFileParams = new FileOpenParams(FILE_PATH, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
+ NetworkFile dummy = driver.createFile(testSession, testConnection, createFileParams);
+ driver.closeFile(testSession, testConnection, dummy);
+ logger.debug("after create and close");
+ return null;
+ }
+ };
+ tran.doInTransaction(createFileCB, false, true);
+
+ testContext.targetNodeRef = getNodeForPath(testConnection, FILE_PATH);
+
+ FileOpenParams openRO = new FileOpenParams(FILE_PATH, FileAction.CreateNotExist, AccessMode.ReadOnly, FileAttribute.NTNormal, 0);
+ FileOpenParams openRW = new FileOpenParams(FILE_PATH, FileAction.CreateNotExist, AccessMode.ReadWrite, FileAttribute.NTNormal, 0);
+
+ /**
+ * First open - read only
+ */
+ logger.debug("open file1 read only");
+ NetworkFile file1 = driver.openFile(testSession, testConnection, openRO);
+ assertNotNull(file1);
+
+ final String testString = "Yankee doodle went to town";
+ byte[] stuff = testString.getBytes("UTF-8");
+
+ /**
+ * Negative test - file is open readOnly
+ */
+ try
+ {
+ driver.writeFile(testSession, testConnection, file1, stuff, 0, stuff.length, 0);
+ fail("can write to a read only file!");
+ }
+ catch(Exception e)
+ {
+ // Expect to go here
+ }
+
+ logger.debug("open file 2 for read write");
+ NetworkFile file2 = driver.openFile(testSession, testConnection, openRW);
+ assertNotNull(file2);
+
+ /**
+ * Write Some Content
+ */
+ driver.writeFile(testSession, testConnection, file2, stuff, 0, stuff.length, 0);
+
+ NetworkFile file3 = driver.openFile(testSession, testConnection, openRW);
+ assertNotNull(file3);
+
+ logger.debug("first close");
+ driver.closeFile(testSession, testConnection, file2);
+ // assertTrue("node does not have no content aspect", nodeService.hasAspect(testContext.targetNodeRef, ContentModel.ASPECT_NO_CONTENT));
+
+ logger.debug("second close");
+ driver.closeFile(testSession, testConnection, file3);
+// //assertTrue("node does not have no content aspect", nodeService.hasAspect(testContext.targetNodeRef, ContentModel.ASPECT_NO_CONTENT));
+
+// logger.debug("this should be the last close");
+// driver.closeFile(testSession, testConnection, file1);
+// assertFalse("node still has no content aspect", nodeService.hasAspect(testContext.targetNodeRef, ContentModel.ASPECT_NO_CONTENT));
+
+ /**
+ * Step 2: Negative test - Close the file again - should do nothing quietly!
+ */
+// logger.debug("this is a negative test - should do nothing");
+// driver.closeFile(testSession, testConnection, file1);
+
+ logger.debug("now validate");
+
+ RetryingTransactionCallback validateCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ Map props = nodeService.getProperties(testContext.targetNodeRef);
+ ContentData data = (ContentData)props.get(ContentModel.PROP_CONTENT);
+ assertNotNull("data is null", data);
+ assertEquals("data wrong length", testString.length(), data.getSize());
+
+ ContentReader reader = contentService.getReader(testContext.targetNodeRef, ContentModel.PROP_CONTENT);
+ String s = reader.getContentString();
+ assertEquals("content not written", testString, s);
+
+ return null;
+ }
+ };
+
+ tran.doInTransaction(validateCB, false, true);
+
+ } // testOpenCloseFileScenario
+
+
/**
* Test server
*/
@@ -2928,6 +3053,11 @@ public class ContentDiskDriverTest extends TestCase
String remName)
{
super(sessId, srv, proto, remName);
+
+ // Set the client info to user "fred"
+ ClientInfo cinfo = ClientInfo.createInfo("fred", null);
+ setClientInformation(cinfo);
+
}
@Override
@@ -2942,4 +3072,17 @@ public class ContentDiskDriverTest extends TestCase
return false;
}
}
+
+ private NodeRef getNodeForPath(TreeConnection tree, String path)
+ throws FileNotFoundException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("getNodeRefForPath:" + path);
+ }
+
+ ContentContext ctx = (ContentContext) tree.getContext();
+
+ return cifsHelper.getNodeRef(ctx.getRootNode(), path);
+ }
}
diff --git a/source/java/org/alfresco/filesys/repo/ContentFileInfo.java b/source/java/org/alfresco/filesys/repo/ContentFileInfo.java
index 96ab278a77..078330e046 100644
--- a/source/java/org/alfresco/filesys/repo/ContentFileInfo.java
+++ b/source/java/org/alfresco/filesys/repo/ContentFileInfo.java
@@ -29,7 +29,6 @@ import org.alfresco.service.cmr.repository.NodeRef;
*/
public class ContentFileInfo extends FileInfo {
- // Version id
private static final long serialVersionUID = 2518699645372408663L;
@@ -40,6 +39,11 @@ public class ContentFileInfo extends FileInfo {
// Linked node
private NodeRef m_linkRef;
+
+ public ContentFileInfo(NodeRef nodeRef)
+ {
+ this.m_nodeRef = nodeRef;
+ }
/**
* Return the file/folder node
@@ -90,4 +94,27 @@ public class ContentFileInfo extends FileInfo {
{
m_linkRef = link;
}
+
+ @Override
+ public boolean equals(Object other)
+ {
+ if (this == other)
+ {
+ return true;
+ }
+ if (other == null || !(other instanceof FileInfo))
+ {
+ return false;
+ }
+
+ ContentFileInfo o = (ContentFileInfo)other;
+
+ return m_nodeRef.equals(o.getNodeRef());
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return m_nodeRef.hashCode();
+ }
}
diff --git a/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java b/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java
index 59d6d5c2e8..02cf1b1caf 100644
--- a/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java
+++ b/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java
@@ -569,7 +569,7 @@ public class ContentIOControlHandler implements IOControlHandler
// Get the list of targets for the action
int targetCnt = reqBuf.getInt();
- DesktopParams deskParams = new DesktopParams(sess, contentDriver, folderNode, netFile);
+ DesktopParams deskParams = new DesktopParams(sess, folderNode, netFile);
while ( reqBuf.getAvailableLength() > 4 && targetCnt > 0)
{
diff --git a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
index f23157aa75..89df4bd0bf 100644
--- a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
+++ b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
@@ -104,7 +104,7 @@ public class ContentNetworkFile extends NodeRefNetworkFile
if ( isMSOfficeSpecialFile(path, sess, nodeService, nodeRef)) {
- // Create a file for special processing
+ // Create a file for special processing for Excel
netFile = new MSOfficeContentNetworkFile( nodeService, contentService, mimetypeService, nodeRef, path);
}
@@ -137,7 +137,7 @@ public class ContentNetworkFile extends NodeRefNetworkFile
FileInfo fileInfo;
try
{
- fileInfo = cifsHelper.getFileInformation(nodeRef, "");
+ fileInfo = cifsHelper.getFileInformation(nodeRef, "", false, false);
}
catch (FileNotFoundException e)
{
@@ -327,6 +327,10 @@ public class ContentNetworkFile extends NodeRefNetworkFile
if (write)
{
// Get a writeable channel to the content, along with the original content
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("get writer for content property");
+ }
content = contentService.getWriter( getNodeRef(), ContentModel.PROP_CONTENT, false);
@@ -349,6 +353,10 @@ public class ContentNetworkFile extends NodeRefNetworkFile
else
{
// Get a read-only channel to the content
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("get reader for content property");
+ }
content = contentService.getReader( getNodeRef(), ContentModel.PROP_CONTENT);
@@ -370,11 +378,14 @@ public class ContentNetworkFile extends NodeRefNetworkFile
// Update the current file size
- if ( channel != null) {
- try {
+ if ( channel != null)
+ {
+ try
+ {
setFileSize(channel.size());
}
- catch (IOException ex) {
+ catch (IOException ex)
+ {
logger.error( ex);
}
@@ -393,10 +404,18 @@ public class ContentNetworkFile extends NodeRefNetworkFile
throws IOException
{
// Check if this is a directory
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("closeFile");
+ }
if (isDirectory())
{
// Nothing to do
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("file is a directory - nothing to do");
+ }
setClosed( true);
return;
@@ -404,6 +423,10 @@ public class ContentNetworkFile extends NodeRefNetworkFile
else if (!hasContent())
{
// File was not read/written so channel was not opened
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("no content to write - nothing to do");
+ }
setClosed( true);
return;
@@ -413,6 +436,10 @@ public class ContentNetworkFile extends NodeRefNetworkFile
if (modified)
{
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("content has been modified");
+ }
NodeRef contentNodeRef = getNodeRef();
ContentWriter writer = (ContentWriter)content;
@@ -458,6 +485,10 @@ public class ContentNetworkFile extends NodeRefNetworkFile
if (contentChanged)
{
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("content has changed - remove ASPECT_NO_CONTENT");
+ }
nodeService.removeAspect(contentNodeRef, ContentModel.ASPECT_NO_CONTENT);
try
{
@@ -485,6 +516,10 @@ public class ContentNetworkFile extends NodeRefNetworkFile
else if (channel != null)
{
// Close it - it was not modified
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("content not modified - simply close the channel");
+ }
channel.close();
channel = null;
@@ -500,7 +535,9 @@ public class ContentNetworkFile extends NodeRefNetworkFile
public void truncateFile(long size)
throws IOException
{
- try {
+ logger.debug("truncate file");
+ try
+ {
// If the content data channel has not been opened yet and the requested size is zero
// then this is an open for overwrite so the existing content data is not copied
diff --git a/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java b/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java
index d726d48937..d4da501267 100644
--- a/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java
+++ b/source/java/org/alfresco/filesys/repo/ContentQuotaManager.java
@@ -33,7 +33,9 @@ import org.alfresco.jlan.server.filesys.quota.QuotaManagerException;
import org.alfresco.jlan.util.MemorySize;
import org.alfresco.jlan.util.StringList;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.usage.ContentUsageService;
+import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -60,12 +62,14 @@ public class ContentQuotaManager implements QuotaManager, Runnable {
// Associated filesystem driver
- private ContentDiskDriver m_filesys;
+ private DiskInterface m_filesys;
// Content usage service
private ContentUsageService m_usageService;
+ private ContentService contentService;
+
// Track live usage of users that are writing files
private HashMap m_liveUsage;
@@ -75,6 +79,12 @@ public class ContentQuotaManager implements QuotaManager, Runnable {
private Thread m_thread;
private boolean m_shutdown;
+
+ public void init()
+ {
+ PropertyCheck.mandatory(this, "contentService", getContentService());
+ PropertyCheck.mandatory(this, "contentUsageService", m_usageService);
+ }
/**
* Get the usage service
@@ -103,7 +113,7 @@ public class ContentQuotaManager implements QuotaManager, Runnable {
// Get the live free space value from the content store, if supported
- long freeSpace = m_filesys.getContentService().getStoreFreeSpace();
+ long freeSpace = contentService.getStoreFreeSpace();
if ( freeSpace == -1L) {
// Content store does not support sizing, return a large dummy value
@@ -261,11 +271,12 @@ public class ContentQuotaManager implements QuotaManager, Runnable {
throws QuotaManagerException {
// Save the filesystem driver details
+ m_filesys = disk;
- if ( disk instanceof ContentDiskDriver)
- m_filesys = (ContentDiskDriver) disk;
- else
- throw new QuotaManagerException("Invalid filesystem type, " + disk.getClass().getName());
+// if ( disk instanceof ContentDiskDriver)
+// m_filesys = (ContentDiskDriver) disk;
+// else
+// throw new QuotaManagerException("Invalid filesystem type, " + disk.getClass().getName());
// Allocate the live usage table
@@ -361,7 +372,7 @@ public class ContentQuotaManager implements QuotaManager, Runnable {
// Start a transaction
- m_filesys.beginReadTransaction(sess);
+// m_filesys.beginReadTransaction(sess);
// Get the usage quota and current usage values for the user
@@ -505,4 +516,14 @@ public class ContentQuotaManager implements QuotaManager, Runnable {
}
}
}
+
+ public void setContentService(ContentService contentService)
+ {
+ this.contentService = contentService;
+ }
+
+ public ContentService getContentService()
+ {
+ return contentService;
+ }
}
diff --git a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java
index ec1862a666..8d12657ad1 100644
--- a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java
+++ b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java
@@ -233,7 +233,7 @@ public class ContentSearchContext extends SearchContext
// Get the file information and copy across to the callers file info
- nextInfo = cifsHelper.getFileInformation(nextNodeRef, "");
+ nextInfo = cifsHelper.getFileInformation(nextNodeRef, "", false, false);
info.copyFrom(nextInfo);
}
catch ( InvalidNodeRefException ex) {
@@ -358,7 +358,7 @@ public class ContentSearchContext extends SearchContext
{
// Get the file information and copy across to the callers file info
- FileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, "");
+ FileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, "", false, false);
// Keep track of the last file name returned
diff --git a/source/java/org/alfresco/filesys/repo/FilesystemTransactionAdvice.java b/source/java/org/alfresco/filesys/repo/FilesystemTransactionAdvice.java
new file mode 100644
index 0000000000..ae31252fbf
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/FilesystemTransactionAdvice.java
@@ -0,0 +1,143 @@
+/*
+ * 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.error.AlfrescoRuntimeException;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.service.transaction.TransactionService;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+/**
+ * An advice wrapper for an AlfrescoDiskDriver.
+ */
+public class FilesystemTransactionAdvice implements MethodInterceptor
+{
+ private boolean readOnly;
+
+// private AlfrescoDiskDriver driver;
+
+ private TransactionService transactionService;
+
+ public FilesystemTransactionAdvice()
+ {
+ readOnly = false;
+ }
+
+ public void setReadOnly(boolean readOnly)
+ {
+ this.readOnly = readOnly;
+ }
+
+ public Object invoke(final MethodInvocation methodInvocation) throws IOException, Throwable
+ {
+// Object[] args = methodInvocation.getArguments();
+//
+// if(args.length == 0 || !(args[0] instanceof SrvSession))
+// {
+// throw new AlfrescoRuntimeException("First argument is not of correct type");
+// }
+
+ RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
+
+ RetryingTransactionCallback callback = new RetryingTransactionHelper.RetryingTransactionCallback()
+ {
+ public Object execute() throws Throwable
+ {
+ try
+ {
+ return methodInvocation.proceed();
+ }
+ catch (IOException e)
+ {
+ // Ensure original checked IOExceptions get propagated
+ throw new PropagatingException(e);
+ }
+ }
+ };
+
+ if(readOnly)
+ {
+ // read only transaction
+ try
+ {
+ return tran.doInTransaction(callback, true);
+ }
+ catch(PropagatingException pe)
+ {
+ // Unwrap checked exceptions
+ throw (IOException) pe.getCause();
+ }
+ }
+ else
+ {
+ // read/write only transaction
+ try
+ {
+ return tran.doInTransaction(callback);
+ }
+ catch(PropagatingException pe)
+ {
+ // Unwrap checked exceptions
+ throw (IOException) pe.getCause();
+ }
+
+ }
+ }
+
+// public void setDriver(AlfrescoDiskDriver driver)
+// {
+// this.driver = driver;
+// }
+//
+// public AlfrescoDiskDriver getDriver()
+// {
+// return driver;
+// }
+
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
+ public TransactionService getTransactionService()
+ {
+ return transactionService;
+ }
+
+ /**
+ * A wrapper for checked exceptions to be passed through the retrying transaction handler.
+ */
+ protected static class PropagatingException extends RuntimeException
+ {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * @param cause
+ */
+ public PropagatingException(Throwable cause)
+ {
+ super(cause);
+ }
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java b/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java
new file mode 100644
index 0000000000..12e9d50acf
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/LegacyFileStateDriver.java
@@ -0,0 +1,279 @@
+/*
+ * 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.filesys.repo;
+
+import java.io.IOException;
+
+import org.alfresco.filesys.config.ServerConfigurationBean;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.core.DeviceContext;
+import org.alfresco.jlan.server.core.DeviceContextException;
+import org.alfresco.jlan.server.filesys.FileInfo;
+import org.alfresco.jlan.server.filesys.FileOpenParams;
+import org.alfresco.jlan.server.filesys.FileStatus;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.jlan.server.filesys.SearchContext;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.jlan.server.filesys.cache.FileState;
+import org.alfresco.jlan.smb.SharingMode;
+
+/**
+ * The Legacy file state driver is used to update JLAN's file state cache.
+ *
+ * This class contains odds and ends to keep JLan happy. In particular it
+ * cannot contain any code that requires access to the alfresco repository.
+ *
+ */
+public class LegacyFileStateDriver implements ContentDiskCallback
+{
+
+ public void init()
+ {
+ }
+
+ @Override
+ public void getFileInformation(SrvSession sess, TreeConnection tree,
+ String path, FileInfo info)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void fileExists(SrvSession sess, TreeConnection tree, String path,
+ int fileExists)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void treeOpened(SrvSession sess, TreeConnection tree)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void treeClosed(SrvSession sess, TreeConnection tree)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void closeFile(SrvSession sess, TreeConnection tree,
+ NetworkFile param)
+ {
+ ContentContext tctx = (ContentContext) tree.getContext();
+
+ if(tctx.hasStateCache())
+ {
+ FileState fstate = tctx.getStateCache().findFileState( param.getFullName(), true);
+ if ( fstate.decrementOpenCount() == 0)
+ {
+ fstate.setSharedAccess( SharingMode.READWRITE + SharingMode.DELETE);
+ }
+ }
+
+ }
+
+ @Override
+ public void createDirectory(SrvSession sess, TreeConnection tree,
+ FileOpenParams params)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void createFile(SrvSession sess, TreeConnection tree,
+ FileOpenParams params, NetworkFile newFile)
+ {
+ ContentContext tctx = (ContentContext) tree.getContext();
+
+ // TODO temp code - not of interest to the repo
+ if (newFile instanceof NodeRefNetworkFile)
+ {
+ NodeRefNetworkFile x = (NodeRefNetworkFile)newFile;
+ x.setProcessId( params.getProcessId());
+ }
+
+ if(tctx.hasStateCache())
+ {
+ FileState fstate = tctx.getStateCache().findFileState( params.getPath(), true);
+ fstate.incrementOpenCount();
+ fstate.setProcessId(params.getProcessId());
+ fstate.setSharedAccess( params.getSharedAccess());
+ fstate.setProcessId( params.getProcessId());
+
+ // Indicate that the file is open
+ fstate.setFileStatus(FileStatus.FileExists);
+ fstate.incrementOpenCount();
+ //fstate.setFilesystemObject(result.getSecond());
+ fstate.setAllocationSize( params.getAllocationSize());
+ }
+ }
+
+ @Override
+ public void deleteDirectory(SrvSession sess, TreeConnection tree, String dir)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void deleteFile(SrvSession sess, TreeConnection tree, String name)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void flushFile(SrvSession sess, TreeConnection tree, NetworkFile file)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void isReadOnly(SrvSession sess, DeviceContext ctx,
+ boolean isReadOnly)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void openFile(SrvSession sess, TreeConnection tree,
+ FileOpenParams param, NetworkFile openFile)
+ {
+ ContentContext tctx = (ContentContext) tree.getContext();
+ String path = param.getPath();
+
+ // Stuff to keep JLAN working - of no interest to the repo.
+ if (openFile instanceof ContentNetworkFile)
+ {
+ ContentNetworkFile x = (ContentNetworkFile)openFile;
+ x.setProcessId( param.getProcessId());
+ if(tctx.hasStateCache())
+ {
+ FileState fstate = tctx.getStateCache().findFileState( path, true);
+ x.setFileState(fstate);
+ fstate.incrementOpenCount();
+ fstate.setProcessId(param.getProcessId());
+ }
+ }
+
+ if (openFile instanceof TempNetworkFile)
+ {
+ TempNetworkFile x = (TempNetworkFile)openFile;
+ //x.setProcessId( param.getProcessId());
+ if(tctx.hasStateCache())
+ {
+ FileState fstate = tctx.getStateCache().findFileState( path, true);
+ x.setFileState(fstate);
+ }
+ }
+
+ if (openFile instanceof AlfrescoFolder)
+ {
+ AlfrescoFolder x = (AlfrescoFolder)openFile;
+ //x.setProcessId( param.getProcessId());
+ if(tctx.hasStateCache())
+ {
+ FileState fstate = tctx.getStateCache().findFileState( path, true);
+ x.setFileState(fstate);
+ fstate.setFileStatus(FileStatus.DirectoryExists);
+ }
+ }
+ }
+
+ @Override
+ public void readFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, byte[] buf, int bufPos, int siz, long filePos,
+ int readSize)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void renameFile(SrvSession sess, TreeConnection tree,
+ String oldPath, String newPath)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void seekFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, long pos, int typ) throws IOException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void setFileInformation(SrvSession sess, TreeConnection tree,
+ String name, FileInfo info) throws IOException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void startSearch(SrvSession sess, TreeConnection tree,
+ String searchPath, int attrib, SearchContext context)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void truncateFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, long siz)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void writeFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, byte[] buf, int bufoff, int siz, long fileoff,
+ int writeSize)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void registerContext(DeviceContext ctx,
+ ServerConfigurationBean serverConfig) throws DeviceContextException
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+
+
+
+
+}
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/filesys/repo/NodeMonitor.java b/source/java/org/alfresco/filesys/repo/NodeMonitor.java
index 352c3da500..431b251ee8 100644
--- a/source/java/org/alfresco/filesys/repo/NodeMonitor.java
+++ b/source/java/org/alfresco/filesys/repo/NodeMonitor.java
@@ -1,19 +1,19 @@
/*
- * Copyright (C) 2006-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
+ * Copyright (C) 2006-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 .
*/
@@ -85,7 +85,7 @@ public class NodeMonitor extends TransactionListenerAdapter
// Filesystem driver and context
- private ContentDiskDriver m_filesysDriver;
+// private ContentDiskDriver m_filesysDriver;
private ContentContext m_filesysCtx;
// File state table and change notification handler
@@ -113,9 +113,9 @@ public class NodeMonitor extends TransactionListenerAdapter
* @param filesysDriver ContentDiskDriver
* @param filesysCtx ContentContext
*/
- protected NodeMonitor( ContentDiskDriver filesysDriver, ContentContext filesysCtx, NodeService nodeService, PolicyComponent policyComponent,
+ protected NodeMonitor(ContentContext filesysCtx, NodeService nodeService, PolicyComponent policyComponent,
FileFolderService fileFolderService, PermissionService permissionService, TransactionService transService) {
- m_filesysDriver = filesysDriver;
+// m_filesysDriver = filesysDriver;
m_filesysCtx = filesysCtx;
// Set various services
@@ -544,8 +544,7 @@ public class NodeMonitor extends TransactionListenerAdapter
// Use the system user as the authenticated context for the node monitor
- AuthenticationContext authenticationContext = m_filesysDriver.getAuthenticationContext();
- authenticationContext.setSystemUserAsCurrentUser();
+ AuthenticationUtil.setRunAsUserSystem();
// Loop until shutdown
diff --git a/source/java/org/alfresco/filesys/repo/NodeMonitorFactory.java b/source/java/org/alfresco/filesys/repo/NodeMonitorFactory.java
index b2b91443fd..0d36e8b0bf 100644
--- a/source/java/org/alfresco/filesys/repo/NodeMonitorFactory.java
+++ b/source/java/org/alfresco/filesys/repo/NodeMonitorFactory.java
@@ -19,9 +19,9 @@
package org.alfresco.filesys.repo;
-import javax.transaction.UserTransaction;
-
import org.alfresco.repo.policy.PolicyComponent;
+import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PermissionService;
@@ -60,52 +60,28 @@ public class NodeMonitorFactory {
* @param filesysDriver ContentDiskDriver
* @param filesysCtx ContentContext
*/
- public NodeMonitor createNodeMonitor( ContentDiskDriver filesysDriver, ContentContext filesysCtx) {
+ public NodeMonitor createNodeMonitor(final ContentContext filesysCtx)
+ {
// Initialization needs a transaction
- UserTransaction tx = m_transService.getUserTransaction(true);
- NodeMonitor nodeMonitor = null;
-
- try {
-
- // Start the transaction
-
- tx.begin();
+ RetryingTransactionHelper tran = m_transService.getRetryingTransactionHelper();
+
+ RetryingTransactionCallback initialiseCB = new RetryingTransactionCallback() {
- // Create the node monitor
-
- nodeMonitor = new NodeMonitor( filesysDriver, filesysCtx, m_nodeService, m_policyComponent, m_fileFolderService,
- m_permissionService, m_transService);
-
- // Commit the transaction
-
- tx.commit();
- tx = null;
- }
- catch ( Exception ex) {
- logger.error(ex);
- }
- finally {
-
- // If there is an active transaction then roll it back
-
- if ( tx != null)
- {
- try
- {
- tx.rollback();
- }
- catch (Exception ex)
- {
- logger.warn("Failed to rollback transaction", ex);
- }
+ @Override
+ public NodeMonitor execute() throws Throwable
+ {
+ NodeMonitor nodeMonitor = new NodeMonitor(filesysCtx, m_nodeService, m_policyComponent, m_fileFolderService,
+ m_permissionService, m_transService);
+
+ return nodeMonitor;
}
- }
-
- // Return the node monitor
-
- return nodeMonitor;
+ };
+
+
+ return tran.doInTransaction(initialiseCB);
+
}
/**
diff --git a/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java
new file mode 100644
index 0000000000..7583997e2b
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java
@@ -0,0 +1,644 @@
+/*
+ * 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.FileNotFoundException;
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.alfresco.filesys.alfresco.ExtendedDiskInterface;
+import org.alfresco.filesys.alfresco.RepositoryDiskInterface;
+import org.alfresco.filesys.config.ServerConfigurationBean;
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.filesys.repo.rules.Operation;
+import org.alfresco.filesys.repo.rules.RuleEvaluator;
+import org.alfresco.jlan.server.SrvSession;
+import org.alfresco.jlan.server.core.DeviceContext;
+import org.alfresco.jlan.server.core.DeviceContextException;
+import org.alfresco.jlan.server.filesys.AccessMode;
+import org.alfresco.jlan.server.filesys.FileInfo;
+import org.alfresco.jlan.server.filesys.FileName;
+import org.alfresco.jlan.server.filesys.FileOpenParams;
+import org.alfresco.jlan.server.filesys.FileStatus;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.jlan.server.filesys.SearchContext;
+import org.alfresco.jlan.server.filesys.TreeConnection;
+import org.alfresco.jlan.server.filesys.cache.FileState;
+import org.alfresco.jlan.server.filesys.cache.NetworkFileStateInterface;
+import org.alfresco.jlan.smb.SharingMode;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.util.PropertyCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.ibatis.annotations.Case;
+import org.springframework.extensions.config.ConfigElement;
+import org.alfresco.filesys.repo.rules.EvaluatorContext;
+import org.alfresco.filesys.repo.rules.commands.CompoundCommand;
+import org.alfresco.filesys.repo.rules.commands.CopyContentCommand;
+import org.alfresco.filesys.repo.rules.commands.RenameFileCommand;
+import org.alfresco.filesys.repo.rules.operations.CloseFileOperation;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.alfresco.filesys.repo.rules.operations.DeleteFileOperation;
+import org.alfresco.filesys.repo.rules.operations.MoveFileOperation;
+import org.alfresco.filesys.repo.rules.operations.OpenFileOperation;
+import org.alfresco.filesys.repo.rules.operations.RenameFileOperation;
+
+/**
+ * Non Transactional DiskDriver with rules engine.
+ *
+ * Provides a DiskInterface that deals with "shuffles". Shuffles are implemented by the Rules Engine.
+ *
+ * Sits on top of the repository and is non-retryable and non-transactional.
+ * It is, however thread safe and multiple callers may call in parallel.
+ */
+public class NonTransactionalRuleContentDiskDriver implements ExtendedDiskInterface
+{
+ /**
+ * The Driver State. Contained within the JLAN SrvSession.
+ */
+ private class DriverState
+ {
+ /**
+ * Map of folderName to Evaluator Context.
+ */
+ Map contextMap = new ConcurrentHashMap();
+ }
+
+ // Logging
+ private static final Log logger = LogFactory.getLog(NonTransactionalRuleContentDiskDriver.class);
+
+ private ExtendedDiskInterface diskInterface;
+ private RuleEvaluator ruleEvaluator;
+ private RepositoryDiskInterface repositoryDiskInterface;
+ private CommandExecutor commandExecutor;
+ private ContentDiskCallback callbackInterface;
+
+ public void init()
+ {
+ PropertyCheck.mandatory(this, "diskInterface", diskInterface);
+ PropertyCheck.mandatory(this, "ruleEvaluator", getRuleEvaluator());
+ PropertyCheck.mandatory(this, "repositoryDiskInterface", getRepositoryDiskInterface());
+ PropertyCheck.mandatory(this, "commandExecutor", getCommandExecutor());
+ }
+
+ @Override
+ public FileInfo getFileInformation(SrvSession sess, TreeConnection tree,
+ String path) throws IOException
+ {
+ FileInfo info = diskInterface.getFileInformation(sess, tree, path);
+
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().getFileInformation(sess, tree, path, info);
+ }
+ return info;
+ }
+
+ @Override
+ public int fileExists(SrvSession sess, TreeConnection tree, String path)
+ {
+ int fileExists = diskInterface.fileExists(sess, tree, path);
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().fileExists(sess, tree, path, fileExists);
+ }
+ return fileExists;
+ }
+
+ @Override
+ public DeviceContext createContext(String shareName, ConfigElement args)
+ throws DeviceContextException
+ {
+ return diskInterface.createContext(shareName, args);
+ }
+
+ @Override
+ public void treeOpened(SrvSession sess, TreeConnection tree)
+ {
+ diskInterface.treeOpened(sess, tree);
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().treeOpened(sess, tree);
+ }
+ }
+
+ @Override
+ public void treeClosed(SrvSession sess, TreeConnection tree)
+ {
+ diskInterface.treeClosed(sess, tree);
+
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().treeClosed(sess, tree);
+ }
+ }
+
+ @Override
+ public void closeFile(SrvSession sess, TreeConnection tree,
+ NetworkFile param) throws IOException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("closeFile");
+ }
+
+ ContentContext tctx = (ContentContext) tree.getContext();
+ NodeRef rootNode = tctx.getRootNode();
+
+ DriverState driverState = getDriverState(sess);
+
+ String[] paths = FileName.splitPath(param.getFullName());
+ String folder = paths[0];
+ String file = paths[1];
+
+ EvaluatorContext ctx = driverState.contextMap.get(folder);
+ if(ctx == null)
+ {
+ ctx = ruleEvaluator.createContext();
+ driverState.contextMap.put(folder, ctx);
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("new driver context: " + folder);
+ }
+ }
+
+ Operation o = new CloseFileOperation(file, param, rootNode, param.getFullName(), param.hasDeleteOnClose());
+ Command c = ruleEvaluator.evaluate(ctx, o);
+
+ commandExecutor.execute(sess, tree, c);
+
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().closeFile(sess, tree, param);
+ }
+ }
+
+ @Override
+ public void createDirectory(SrvSession sess, TreeConnection tree,
+ FileOpenParams params) throws IOException
+ {
+ diskInterface.createDirectory(sess, tree, params);
+
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().createDirectory(sess, tree, params);
+ }
+ }
+
+ @Override
+ public NetworkFile createFile(SrvSession sess, TreeConnection tree,
+ FileOpenParams params) throws IOException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("createFile");
+ }
+
+ ContentContext tctx = (ContentContext) tree.getContext();
+ NodeRef rootNode = tctx.getRootNode();
+
+ String[] paths = FileName.splitPath(params.getPath());
+ String folder = paths[0];
+ String file = paths[1];
+
+ DriverState driverState = getDriverState(sess);
+ EvaluatorContext ctx = getEvaluatorContext(driverState, folder);
+
+ Operation o = new CreateFileOperation(file, rootNode, params.getPath());
+ Command c = ruleEvaluator.evaluate(ctx, o);
+
+ Object ret = commandExecutor.execute(sess, tree, c);
+
+ if(ret != null && ret instanceof NetworkFile)
+ {
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().createFile(sess, tree, params, (NetworkFile)ret);
+ }
+
+ return (NetworkFile)ret;
+ }
+ else
+ {
+ // Error - contact broken
+ logger.error("contract broken - NetworkFile not returned");
+ return null;
+ }
+ }
+
+ @Override
+ public void deleteDirectory(SrvSession sess, TreeConnection tree, String dir)
+ throws IOException
+ {
+
+ diskInterface.deleteDirectory(sess, tree, dir);
+
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().deleteDirectory(sess, tree, dir);
+ }
+
+
+ }
+
+ @Override
+ public void deleteFile(SrvSession sess, TreeConnection tree, String name)
+ throws IOException
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("deleteFile name:" + name);
+ }
+
+ ContentContext tctx = (ContentContext) tree.getContext();
+ NodeRef rootNode = tctx.getRootNode();
+
+ DriverState driverState = getDriverState(sess);
+
+ String[] paths = FileName.splitPath(name);
+ String folder = paths[0];
+ String file = paths[1];
+
+ EvaluatorContext ctx = getEvaluatorContext(driverState, folder);
+
+ Operation o = new DeleteFileOperation(file, rootNode, name);
+ Command c = ruleEvaluator.evaluate(ctx, o);
+ commandExecutor.execute(sess, tree, c);
+
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().deleteFile(sess, tree, name);
+ }
+
+ } // End of deleteFile
+
+ @Override
+ public void flushFile(SrvSession sess, TreeConnection tree, NetworkFile file)
+ throws IOException
+ {
+ diskInterface.flushFile(sess, tree, file);
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().flushFile(sess, tree, file);
+ }
+ }
+
+ @Override
+ public boolean isReadOnly(SrvSession sess, DeviceContext ctx)
+ throws IOException
+ {
+ boolean isReadOnly = diskInterface.isReadOnly(sess, ctx);
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().isReadOnly(sess, ctx, isReadOnly);
+ }
+
+ return isReadOnly;
+ }
+
+ @Override
+ public NetworkFile openFile(SrvSession sess, TreeConnection tree,
+ FileOpenParams param) throws IOException
+ {
+ String path = param.getPath();
+
+ if(logger.isDebugEnabled())
+ {
+ int sharedAccess = param.getSharedAccess();
+ String strSharedAccess = "none";
+
+ switch(sharedAccess)
+ {
+ case SharingMode.NOSHARING:
+ strSharedAccess = "nosharing";
+ break;
+ case SharingMode.READ:
+ strSharedAccess = "read";
+ break;
+ case SharingMode.WRITE:
+ strSharedAccess = "write";
+ break;
+ case SharingMode.READWRITE:
+ strSharedAccess = "read-write";
+ break;
+ case SharingMode.DELETE:
+ strSharedAccess = "delete";
+ break;
+ }
+
+ logger.debug("openFile:" + path
+ + ", isDirectory: " + param.isDirectory()
+ + ", isStream: " + param.isStream()
+ + ", readOnlyAccess: " + param.isReadOnlyAccess()
+ + ", readWriteAccess: " + param.isReadWriteAccess()
+ + ", writeOnlyAccess:" +param.isWriteOnlyAccess()
+ + ", attributesOnlyAccess:" +param.isAttributesOnlyAccess()
+ + ", sequentialAccessOnly:" + param.isSequentialAccessOnly()
+ + ", requestBatchOpLock:" +param.requestBatchOpLock()
+ + ", requestExclusiveOpLock:" +param.requestExclusiveOpLock()
+ + ", isDeleteOnClose:" +param.isDeleteOnClose()
+ + ", sharedAccess: " + strSharedAccess
+
+ );
+ }
+
+ ContentContext tctx = (ContentContext) tree.getContext();
+ NodeRef rootNode = tctx.getRootNode();
+
+ DriverState driverState = getDriverState(sess);
+
+ String[] paths = FileName.splitPath(path);
+ String folder = paths[0];
+ String file = paths[1];
+
+ EvaluatorContext ctx = getEvaluatorContext(driverState, folder);
+
+ boolean writeAccess = param.isReadWriteAccess();
+ boolean truncate = param.isOverwrite();
+
+ Operation o = new OpenFileOperation(file, writeAccess, truncate, rootNode, path);
+ Command c = ruleEvaluator.evaluate(ctx, o);
+ Object ret = commandExecutor.execute(sess, tree, c);
+
+ if(ret != null && ret instanceof NetworkFile)
+ {
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().openFile(sess, tree, param, (NetworkFile)ret);
+ }
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("returning open file: for path:" + path +", ret:" + ret);
+ }
+ return (NetworkFile)ret;
+ }
+ else
+ {
+ // Error - contact broken
+ logger.error("contract broken - NetworkFile not returned");
+ return null;
+ }
+
+ //return diskInterface.openFile(sess, tree, params);
+ } // End of OpenFile
+
+ @Override
+ public int readFile(SrvSession sess, TreeConnection tree, NetworkFile file,
+ byte[] buf, int bufPos, int siz, long filePos) throws IOException
+ {
+ int readSize = diskInterface.readFile(sess, tree, file, buf, bufPos, siz, filePos);
+
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().readFile(sess, tree, file, buf, bufPos, siz, filePos, readSize);
+ }
+
+ return readSize;
+ }
+
+ @Override
+ public void renameFile(SrvSession sess, TreeConnection tree,
+ String oldPath, String newPath) throws IOException
+ {
+ ContentContext tctx = (ContentContext) tree.getContext();
+ NodeRef rootNode = tctx.getRootNode();
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("renameFile oldPath:" + oldPath + ", newPath:" + newPath);
+ }
+
+ DriverState driverState = getDriverState(sess);
+
+ // Is this a rename within the same folder or a move between folders?
+
+ String[] paths = FileName.splitPath(oldPath);
+ String oldFolder = paths[0];
+ String oldFile = paths[1];
+
+ paths = FileName.splitPath(newPath);
+ String newFolder = paths[0];
+ String newFile = paths[1];
+
+ if(oldFolder.equalsIgnoreCase(newFolder))
+ {
+ logger.debug("renameFileCommand - is a rename within the same folder");
+
+ EvaluatorContext ctx = getEvaluatorContext(driverState, oldFolder);
+
+ Operation o = new RenameFileOperation(oldFile, newFile, oldPath, newPath, rootNode);
+ Command c = ruleEvaluator.evaluate(ctx, o);
+ commandExecutor.execute(sess, tree, c);
+ }
+ else
+ {
+ logger.debug("move - call renameFile directly");
+// // TODO Use old interface for rename/move until think
+// // through move operation and how it applies to the evaluator contexts
+// // plural since there will be two contexts.
+// logger.debug("move");
+// Operation o = new MoveFileOperation(oldFile, newFile);
+// Command c = ruleEvaluator.evaluate(ctx, o);
+//
+// commandExecutor.execute(sess, tree, c);
+
+ diskInterface.renameFile(sess, tree, oldPath, newPath);
+
+ }
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().renameFile(sess, tree, oldPath, newPath);
+ }
+ }
+
+ @Override
+ public long seekFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, long pos, int typ) throws IOException
+ {
+ long ret = diskInterface.seekFile(sess, tree, file, pos, typ);
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().seekFile(sess, tree, file, pos, typ);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public void setFileInformation(SrvSession sess, TreeConnection tree,
+ String name, FileInfo info) throws IOException
+ {
+ diskInterface.setFileInformation(sess, tree, name, info);
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().setFileInformation(sess, tree, name, info);
+ }
+ }
+
+ @Override
+ public SearchContext startSearch(SrvSession sess, TreeConnection tree,
+ String searchPath, int attrib) throws FileNotFoundException
+ {
+ SearchContext context = diskInterface.startSearch(sess, tree, searchPath, attrib);
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().startSearch(sess, tree, searchPath, attrib, context);
+ }
+ return context;
+ }
+
+ @Override
+ public void truncateFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, long siz) throws IOException
+ {
+ diskInterface.truncateFile(sess, tree, file, siz);
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().truncateFile(sess, tree, file, siz);
+ }
+ }
+
+ @Override
+ public int writeFile(SrvSession sess, TreeConnection tree,
+ NetworkFile file, byte[] buf, int bufoff, int siz, long fileoff)
+ throws IOException
+ {
+ int writeSize = diskInterface.writeFile(sess, tree, file, buf, bufoff, siz, fileoff);
+ if(getCallbackInterface() != null)
+ {
+ getCallbackInterface().writeFile(sess, tree, file, buf, bufoff, siz, fileoff, writeSize);
+ }
+
+ return writeSize;
+ }
+
+ public void setDiskInterface(ExtendedDiskInterface diskInterface)
+ {
+ this.diskInterface = diskInterface;
+ }
+
+ public ExtendedDiskInterface getDiskInterface()
+ {
+ return diskInterface;
+ }
+
+ public void setRuleEvaluator(RuleEvaluator ruleEvaluator)
+ {
+ this.ruleEvaluator = ruleEvaluator;
+ }
+
+ public RuleEvaluator getRuleEvaluator()
+ {
+ return ruleEvaluator;
+ }
+
+ @Override
+ public void registerContext(DeviceContext ctx, ServerConfigurationBean serverConfig)
+ throws DeviceContextException
+ {
+ diskInterface.registerContext(ctx, serverConfig);
+ }
+
+ public void setRepositoryDiskInterface(RepositoryDiskInterface repositoryDiskInterface)
+ {
+ this.repositoryDiskInterface = repositoryDiskInterface;
+ }
+
+ public RepositoryDiskInterface getRepositoryDiskInterface()
+ {
+ return repositoryDiskInterface;
+ }
+
+ public void setCommandExecutor(CommandExecutor commandExecutor)
+ {
+ this.commandExecutor = commandExecutor;
+ }
+
+ public CommandExecutor getCommandExecutor()
+ {
+ return commandExecutor;
+ }
+
+
+ /**
+ * Get the driver state from the session.
+ * @param sess
+ * @return the driver state.
+ */
+ private DriverState getDriverState(SrvSession sess)
+ {
+
+ synchronized (sess)
+ {
+ // Get the driver state
+ Object state = sess.getDriverState();
+ if(state == null)
+ {
+ state = new DriverState();
+ sess.setDriverState(state);
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("new driver state created");
+ }
+
+ }
+ DriverState driverState = (DriverState)state;
+ return driverState;
+ }
+ }
+
+ /**
+ * Get the evaluator context from the state and the folder.
+ * @param driverState
+ * @param folder
+ * @return
+ */
+ private EvaluatorContext getEvaluatorContext(DriverState driverState, String folder)
+ {
+ synchronized(driverState.contextMap)
+ {
+ EvaluatorContext ctx = driverState.contextMap.get(folder);
+ if(ctx == null)
+ {
+ ctx = ruleEvaluator.createContext();
+ driverState.contextMap.put(folder, ctx);
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("new driver context: " + folder);
+ }
+ }
+ return ctx;
+ }
+ }
+
+ public void setCallbackInterface(ContentDiskCallback callbackInterface)
+ {
+ this.callbackInterface = callbackInterface;
+ }
+
+ public ContentDiskCallback getCallbackInterface()
+ {
+ return callbackInterface;
+ }
+
+
+}
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/filesys/repo/ResultCallback.java b/source/java/org/alfresco/filesys/repo/ResultCallback.java
new file mode 100644
index 0000000000..6f6787f39d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/ResultCallback.java
@@ -0,0 +1,13 @@
+package org.alfresco.filesys.repo;
+
+import org.alfresco.filesys.repo.rules.Command;
+
+public interface ResultCallback extends Command
+{
+ /**
+ * Call the callback with the result of the operation.
+ * @param result the result.
+ */
+ void execute(Object result);
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/TempNetworkFile.java b/source/java/org/alfresco/filesys/repo/TempNetworkFile.java
new file mode 100644
index 0000000000..45a4bd07b3
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/TempNetworkFile.java
@@ -0,0 +1,68 @@
+package org.alfresco.filesys.repo;
+
+import java.io.File;
+import java.io.Reader;
+
+import org.alfresco.jlan.server.filesys.cache.FileState;
+import org.alfresco.jlan.server.filesys.cache.NetworkFileStateInterface;
+import org.alfresco.jlan.smb.server.disk.JavaNetworkFile;
+
+/**
+ * Temporary Java backed network file.
+ *
+ * @author mrogers
+ */
+public class TempNetworkFile extends JavaNetworkFile implements NetworkFileStateInterface
+{
+ /**
+ * Create a new temporary file with no existing content.
+ *
+ * @param file the underlying File
+ * @param netPath where in the repo this file is going.
+ */
+ public TempNetworkFile(File file, String netPath)
+ {
+ super(file, netPath);
+ setFullName(netPath);
+ }
+
+ /**
+ * A new temporary network file with some existing content.
+ * @param file
+ * @param netPath
+ * @param existingContent
+ */
+ public TempNetworkFile(File file, String netPath, Reader existingContent)
+ {
+ super(file, netPath);
+ setFullName(netPath);
+ }
+
+ /**
+ * Access to the underlying file.
+ * @return the file.
+ */
+ public File getFile()
+ {
+ return m_file;
+ }
+
+ public String toString()
+ {
+ return "TempNetworkFile:" + getFullName() + " path: " + m_file.getAbsolutePath();
+ }
+
+ // For JLAN file state lock manager
+ public void setFileState(FileState fileState)
+ {
+ this.fileState = fileState;
+ }
+
+ @Override
+ public FileState getFileState()
+ {
+ return fileState;
+
+ }
+ private FileState fileState;
+}
diff --git a/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java b/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java
index 30a18599f5..0aed5fdc8e 100644
--- a/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java
+++ b/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java
@@ -30,7 +30,6 @@ 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;
@@ -39,10 +38,12 @@ import org.alfresco.jlan.server.filesys.cache.FileStateCache;
import org.alfresco.jlan.smb.server.notify.NotifyChangeHandler;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.Pair;
/**
@@ -84,215 +85,214 @@ public class CheckInOutDesktopAction extends DesktopAction {
// Check if there are any files/folders to process
if ( params.numberOfTargetNodes() == 0)
- return new DesktopResponse(StsSuccess);
-
- class WriteTxn implements CallableIO
{
- private List> fileChanges;
-
- /* (non-Javadoc)
- * @see java.util.concurrent.Callable#call()
- */
- public DesktopResponse call() throws IOException
- {
- // Initialize / reset the list of file changes
- fileChanges = new LinkedList>();
-
- // Get required services
-
- ServiceRegistry serviceRegistry = getServiceRegistry();
- NodeService nodeService = serviceRegistry.getNodeService();
- CheckOutCheckInService checkOutCheckInService = serviceRegistry.getCheckOutCheckInService();
-
- // Process the list of target nodes
-
- DesktopResponse response = new DesktopResponse(StsSuccess);
-
- for ( int idx = 0; idx < params.numberOfTargetNodes(); idx++)
- {
- // Get the current target node
-
- DesktopTarget target = params.getTarget(idx);
-
- // Check if the node is a working copy
-
- if ( nodeService.hasAspect( target.getNode(), ContentModel.ASPECT_WORKING_COPY))
- {
- try
- {
- // Check in the file, pass an empty version properties so that veriosnable nodes create a new version
-
- Map versionProperties = new HashMap();
- checkOutCheckInService.checkin( target.getNode(), versionProperties, null, false);
-
- // Check if there are any file/directory change notify requests active
-
- if ( getContext().hasFileServerNotifications()) {
-
- // Build the relative path to the checked in file
-
- String fileName = null;
-
- if ( target.getTarget().startsWith(FileName.DOS_SEPERATOR_STR))
- {
- // Path is already relative to filesystem root
-
- fileName = target.getTarget();
- }
- else
- {
- // Build a root relative path for the file
-
- fileName = FileName.buildPath( params.getFolder().getFullName(), null, target.getTarget(), FileName.DOS_SEPERATOR);
- }
-
- // Queue a file deleted change notification
- fileChanges.add(new Pair(NotifyChange.ActionRemoved, fileName));
- }
- }
- catch (Exception ex)
- {
- // Propagate retryable errors. Log the rest.
- if (RetryingTransactionHelper.extractRetryCause(ex) != null)
- {
- if (ex instanceof RuntimeException)
- {
- throw (RuntimeException)ex;
- }
- else
- {
- throw new AlfrescoRuntimeException("Desktop action error", ex);
- }
- }
-
- // Dump the error
-
- if ( logger.isErrorEnabled())
- logger.error("Desktop action error", ex);
-
- // Return an error status and message
-
- response.setStatus(StsError, "Checkin failed for " + target.getTarget() + ", " + ex.getMessage());
- }
- }
- else
- {
- try
- {
- // Check if the file is locked
-
- if ( nodeService.hasAspect( target.getNode(), ContentModel.ASPECT_LOCKABLE)) {
-
- // Get the lock type
-
- String lockTypeStr = (String) nodeService.getProperty( target.getNode(), ContentModel.PROP_LOCK_TYPE);
- if ( lockTypeStr != null) {
- response.setStatus(StsError, "Checkout failed, file is locked");
- return response;
- }
- }
-
- // Check out the file
-
- NodeRef workingCopyNode = checkOutCheckInService.checkout( target.getNode());
-
- // Get the working copy file name
-
- String workingCopyName = (String) nodeService.getProperty( workingCopyNode, ContentModel.PROP_NAME);
-
- // Check out was successful, pack the working copy name
-
- response.setStatus(StsSuccess, "Checked out working copy " + workingCopyName);
-
- // Build the relative path to the checked out file
-
- String fileName = FileName.buildPath( params.getFolder().getFullName(), null, workingCopyName, FileName.DOS_SEPERATOR);
-
- // Update cached state for the working copy to indicate the file exists
-
- FileStateCache stateCache = getContext().getStateCache();
- if ( stateCache != null) {
-
- // Update any cached state for the working copy file
-
- FileState fstate = stateCache.findFileState( fileName);
- if ( fstate != null)
- fstate.setFileStatus( FileStatus.FileExists);
- }
-
- // Check if there are any file/directory change notify requests active
-
- if ( getContext().hasChangeHandler()) {
-
- // Build the relative path to the checked in file
-
- // Queue a file added change notification
- fileChanges.add(new Pair(NotifyChange.ActionAdded, fileName));
- }
- }
- catch (Exception ex)
- {
- // Propagate retryable errors. Log the rest.
- if (RetryingTransactionHelper.extractRetryCause(ex) != null)
- {
- if (ex instanceof RuntimeException)
- {
- throw (RuntimeException)ex;
- }
- else
- {
- throw new AlfrescoRuntimeException("Desktop action error", ex);
- }
- }
-
- // Dump the error
-
- if ( logger.isErrorEnabled())
- logger.error("Desktop action error", ex);
-
- // Return an error status and message
-
- response.setStatus(StsError, "Failed to checkout " + target.getTarget() + ", " + ex.getMessage());
- }
- }
- }
-
- // Return a success status for now
-
- return response;
- }
-
-
- /**
- * Queue the file change notifications resulting from this successfully processed transaction.
- */
- public void notifyChanges()
- {
- NotifyChangeHandler notifyChangeHandler = getContext().getChangeHandler();
- for (Pair fileChange : fileChanges)
- {
- notifyChangeHandler.notifyFileChanged(fileChange.getFirst(), fileChange.getSecond());
- }
- }
-
+ return new DesktopResponse(StsSuccess);
}
- // Process the transaction
- WriteTxn callback = new WriteTxn();
- 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();
+ TransactionService transactionService = this.getServiceRegistry().getTransactionService();
+
+ RetryingTransactionHelper tx = transactionService.getRetryingTransactionHelper();
+ final List> fileChanges = new LinkedList>();
+
+ RetryingTransactionCallback runCOCICB = new RetryingTransactionCallback() {
+
+ @Override
+ public DesktopResponse execute() throws Throwable
+ {
+
+
+ // Get required services
+
+ ServiceRegistry serviceRegistry = getServiceRegistry();
+ NodeService nodeService = serviceRegistry.getNodeService();
+ CheckOutCheckInService checkOutCheckInService = serviceRegistry.getCheckOutCheckInService();
+
+ // Process the list of target nodes
+
+ DesktopResponse response = new DesktopResponse(StsSuccess);
+
+ for ( int idx = 0; idx < params.numberOfTargetNodes(); idx++)
+ {
+ // Get the current target node
+
+ DesktopTarget target = params.getTarget(idx);
+
+ // Check if the node is a working copy
+
+ if ( nodeService.hasAspect( target.getNode(), ContentModel.ASPECT_WORKING_COPY))
+ {
+ try
+ {
+ // Check in the file, pass an empty version properties so that veriosnable nodes create a new version
+
+ Map versionProperties = new HashMap();
+ checkOutCheckInService.checkin( target.getNode(), versionProperties, null, false);
+
+ // Check if there are any file/directory change notify requests active
+
+ if ( getContext().hasFileServerNotifications()) {
+
+ // Build the relative path to the checked in file
+
+ String fileName = null;
+
+ if ( target.getTarget().startsWith(FileName.DOS_SEPERATOR_STR))
+ {
+ // Path is already relative to filesystem root
+
+ fileName = target.getTarget();
+ }
+ else
+ {
+ // Build a root relative path for the file
+
+ fileName = FileName.buildPath( params.getFolder().getFullName(), null, target.getTarget(), FileName.DOS_SEPERATOR);
+ }
+
+ // Queue a file deleted change notification
+ fileChanges.add(new Pair(NotifyChange.ActionRemoved, fileName));
+ }
+ }
+ catch (Exception ex)
+ {
+ // Propagate retryable errors. Log the rest.
+ if (RetryingTransactionHelper.extractRetryCause(ex) != null)
+ {
+ if (ex instanceof RuntimeException)
+ {
+ throw (RuntimeException)ex;
+ }
+ else
+ {
+ throw new AlfrescoRuntimeException("Desktop action error", ex);
+ }
+ }
+
+ // Dump the error
+
+ if ( logger.isErrorEnabled())
+ logger.error("Desktop action error", ex);
+
+ // Return an error status and message
+
+ response.setStatus(StsError, "Checkin failed for " + target.getTarget() + ", " + ex.getMessage());
+ }
+ }
+ else
+ {
+ try
+ {
+ // Check if the file is locked
+
+ if ( nodeService.hasAspect( target.getNode(), ContentModel.ASPECT_LOCKABLE)) {
+
+ // Get the lock type
+
+ String lockTypeStr = (String) nodeService.getProperty( target.getNode(), ContentModel.PROP_LOCK_TYPE);
+ if ( lockTypeStr != null) {
+ response.setStatus(StsError, "Checkout failed, file is locked");
+ return response;
+ }
+ }
+
+ // Check out the file
+
+ NodeRef workingCopyNode = checkOutCheckInService.checkout( target.getNode());
+
+ // Get the working copy file name
+
+ String workingCopyName = (String) nodeService.getProperty( workingCopyNode, ContentModel.PROP_NAME);
+
+ // Check out was successful, pack the working copy name
+
+ response.setStatus(StsSuccess, "Checked out working copy " + workingCopyName);
+
+ // Build the relative path to the checked out file
+
+ String fileName = FileName.buildPath( params.getFolder().getFullName(), null, workingCopyName, FileName.DOS_SEPERATOR);
+
+ // Update cached state for the working copy to indicate the file exists
+
+ FileStateCache stateCache = getContext().getStateCache();
+ if ( stateCache != null) {
+
+ // Update any cached state for the working copy file
+
+ FileState fstate = stateCache.findFileState( fileName);
+ if ( fstate != null)
+ fstate.setFileStatus( FileStatus.FileExists);
+ }
+
+ // Check if there are any file/directory change notify requests active
+
+ if ( getContext().hasChangeHandler()) {
+
+ // Build the relative path to the checked in file
+
+ // Queue a file added change notification
+ fileChanges.add(new Pair(NotifyChange.ActionAdded, fileName));
+ }
+ }
+ catch (Exception ex)
+ {
+ // Propagate retryable errors. Log the rest.
+ if (RetryingTransactionHelper.extractRetryCause(ex) != null)
+ {
+ if (ex instanceof RuntimeException)
+ {
+ throw (RuntimeException)ex;
+ }
+ else
+ {
+ throw new AlfrescoRuntimeException("Desktop action error", ex);
+ }
+ }
+
+ // Dump the error
+
+ if ( logger.isErrorEnabled())
+ {
+ logger.error("Desktop action error", ex);
+ }
+
+ // Return an error status and message
+
+ response.setStatus(StsError, "Failed to checkout " + target.getTarget() + ", " + ex.getMessage());
+ }
+ }
+ } // end of for loop
+
+ return response;
+ }
+ };
+
+ RetryingTransactionCallback notifyCB = new RetryingTransactionCallback() {
+
+ @Override
+ public Void execute() throws Throwable
+ {
+ NotifyChangeHandler notifyChangeHandler = getContext().getChangeHandler();
+ for (Pair fileChange : fileChanges)
+ {
+ notifyChangeHandler.notifyFileChanged(fileChange.getFirst(), fileChange.getSecond());
+ }
+ return null;
+ }
+ };
+
+ DesktopResponse response = tx.doInTransaction(runCOCICB, false, false);
+
+ /**
+ * Now do the notification in a separate transaction
+ */
+ if(fileChanges.size() > 0)
+ {
+ tx.doInTransaction(notifyCB, false, false);
+ }
+
return response;
}
+
+
}
diff --git a/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java b/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java
index 93499363de..d104806a37 100644
--- a/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java
+++ b/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java
@@ -26,15 +26,17 @@ import java.util.Map;
import java.util.StringTokenizer;
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.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.scripts.ScriptException;
+import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ScriptService;
+import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ResourceFinder;
import org.springframework.core.io.Resource;
import org.springframework.extensions.config.ConfigElement;
@@ -130,12 +132,12 @@ public class JavaScriptDesktopAction extends DesktopAction {
}
@Override
- public void initializeAction(AlfrescoDiskDriver filesysDriver, AlfrescoContext filesysContext)
+ public void initializeAction(ServiceRegistry serviceRegistry, AlfrescoContext filesysContext)
throws DesktopActionException
{
// Perform standard initialization
- super.initializeAction(filesysDriver, filesysContext);
+ super.initializeAction(serviceRegistry, filesysContext);
// Get the script file name and check that it exists
@@ -217,17 +219,23 @@ public class JavaScriptDesktopAction extends DesktopAction {
// Add the webapp URL, if valid
if ( hasWebappURL())
- model.put("webURL", getWebappURL());
-
- try
{
+ model.put("webURL", getWebappURL());
+ }
+
+ TransactionService transactionService = this.getServiceRegistry().getTransactionService();
+
+ RetryingTransactionHelper tx = transactionService.getRetryingTransactionHelper();
- // Compute the response in a retryable write transaction
- return params.getDriver().doInWriteTransaction(params.getSession(), new CallableIO()
- {
- public DesktopResponse call() throws IOException
+ RetryingTransactionCallback runScriptCB = new RetryingTransactionCallback() {
+
+ @Override
+ public DesktopResponse execute() throws Throwable
+ {
+ DesktopResponse response = new DesktopResponse(StsSuccess);
+
+ try
{
- DesktopResponse response = new DesktopResponse(StsSuccess);
// Run the script
@@ -279,21 +287,21 @@ public class JavaScriptDesktopAction extends DesktopAction {
response.setStatus(sts, msgToken != null ? msgToken : "");
}
}
-
// Return the response
return response;
}
- });
- }
- catch (ScriptException ex)
- {
- return new DesktopResponse(StsError, ex.getMessage());
- }
- catch (IOException ex)
- {
- return new DesktopResponse(StsError, ex.getMessage());
- }
+ catch (ScriptException ex)
+ {
+ return new DesktopResponse(StsError, ex.getMessage());
+ }
+ }
+ };
+
+
+ return tx.doInTransaction(runScriptCB, false, false);
+
+ // Compute the response in a retryable write transaction
}
else
{
diff --git a/source/java/org/alfresco/filesys/repo/rules/Command.java b/source/java/org/alfresco/filesys/repo/rules/Command.java
new file mode 100644
index 0000000000..9d131d5ee5
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/Command.java
@@ -0,0 +1,18 @@
+package org.alfresco.filesys.repo.rules;
+
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+
+/**
+ * A Command is something that must be done. Commands are higher level
+ * than Operations. So a rule returns a command or set of commands to
+ * implement an operation.
+ */
+public interface Command
+{
+ /**
+ * Is a transaction required to run this command?
+ */
+ TxnReadState getTransactionRequired();
+
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/EvaluatorContext.java b/source/java/org/alfresco/filesys/repo/rules/EvaluatorContext.java
new file mode 100644
index 0000000000..1aa1994b8f
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/EvaluatorContext.java
@@ -0,0 +1,16 @@
+/**
+ * package org.alfresco.filesys.repo.rules;
+ * @author mrogers
+ *
+ */
+package org.alfresco.filesys.repo.rules;
+
+import java.util.List;
+
+/**
+ * EvaluatorContext
+ */
+public interface EvaluatorContext
+{
+ public List getScenarioInstances();
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/Operation.java b/source/java/org/alfresco/filesys/repo/rules/Operation.java
new file mode 100644
index 0000000000..5cee27bb10
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/Operation.java
@@ -0,0 +1,28 @@
+/*
+ * 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.rules;
+
+/**
+ * An operation is a primitive thing that needs to be executed.
+ * Such as a create, rename or delete.
+ */
+public interface Operation
+{
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/OperationExecutor.java b/source/java/org/alfresco/filesys/repo/rules/OperationExecutor.java
new file mode 100644
index 0000000000..b725f290c5
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/OperationExecutor.java
@@ -0,0 +1,29 @@
+/*
+ * 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.rules;
+
+/**
+ * An operation executor is an implementation of how to execute an
+ * operation. i.e. It is the thing that does stuff to the repo.
+ */
+public interface OperationExecutor
+{
+ public void execute(Operation operation);
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/RuleEvaluator.java b/source/java/org/alfresco/filesys/repo/rules/RuleEvaluator.java
new file mode 100644
index 0000000000..d420ee37a5
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/RuleEvaluator.java
@@ -0,0 +1,41 @@
+/*
+ * 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.rules;
+
+/**
+ * The Rule Evaluator evaluates the operation and returns
+ * details of the commands to implement those operations.
+ *
+ * It is configured with a list of scenarios.
+ */
+public interface RuleEvaluator
+{
+ /**
+ * Create a new evaluator context. Typically for a particular folder.
+ * An evaluator context groups operations together.
+ * @return the new context.
+ */
+ public EvaluatorContext createContext();
+
+ /**
+ * Evaluate the scenarios against the current operation
+ * @param Command the command to fulfill the operation
+ */
+ public Command evaluate(EvaluatorContext context, Operation operation);
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/RuleEvaluatorImpl.java b/source/java/org/alfresco/filesys/repo/rules/RuleEvaluatorImpl.java
new file mode 100644
index 0000000000..6da6688b23
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/RuleEvaluatorImpl.java
@@ -0,0 +1,169 @@
+/*
+ * 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.rules;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking;
+import org.alfresco.util.PropertyCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The Rule Evaluator evaluates the operation and returns
+ * details of the commands to implement those operations.
+ *
+ * It is configured with a list of scenarios.
+ */
+public class RuleEvaluatorImpl implements RuleEvaluator
+{
+ private static Log logger = LogFactory.getLog(RuleEvaluatorImpl.class);
+
+ /**
+ * The evaluator context
+ */
+ private class EvaluatorContextImpl implements EvaluatorContext
+ {
+ /**
+ * Current instances of scenarios
+ */
+ private List currentScenarioInstances = new ArrayList();
+
+ @Override
+ public List getScenarioInstances()
+ {
+ return currentScenarioInstances;
+ }
+ }
+
+ public void init()
+ {
+ PropertyCheck.mandatory(this, "scenarios", scenarios);
+ }
+
+ /**
+ * The scenarios contained within this RuleEvaluator
+ */
+ private List scenarios;
+
+ /**
+ * Evaluate the scenarios against the current operation
+ * @param operation the operation to be evaluated
+ */
+ public Command evaluate(EvaluatorContext context, Operation operation)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("evaluate:" + operation);
+ }
+
+
+ /**
+ * For each scenario, do we need to create a new scenario
+ * instance for the specified operation ?
+ */
+ // currentScenarioInstances needs to be protected for concurrency.
+ synchronized (context.getScenarioInstances())
+ {
+ for(Scenario scenario : scenarios)
+ {
+ ScenarioInstance instance = scenario.createInstance(context.getScenarioInstances(), operation);
+ if(instance != null)
+ {
+ context.getScenarioInstances().add(instance);
+ }
+ }
+
+ /**
+ * For each active scenario.
+ */
+ Iterator i = context.getScenarioInstances().iterator();
+
+ Map executors = new HashMap();
+
+ while(i.hasNext())
+ {
+
+ ScenarioInstance scenario = i.next();
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("evaluating:" + scenario + " operation: " +operation );
+ }
+ Command executor = scenario.evaluate(operation);
+ if(executor != null)
+ {
+ executors.put(scenario.getRanking(), executor);
+ }
+ if(scenario.isComplete())
+ {
+ // That scenario is no longer active.
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Scenario is complete:" + scenario);
+ }
+ i.remove();
+ }
+ }
+
+ // HOW to arbitrate between many scenario executors
+ // Idea : Scenarios have rankings.
+ Command ex = executors.get(Ranking.HIGH);
+ if (ex != null)
+ {
+ logger.debug("returning high priority executor");
+ return ex;
+ }
+ ex = executors.get(Ranking.MEDIUM);
+ if (ex != null)
+ {
+ logger.debug("returning medium priority executor");
+ return ex;
+ }
+ ex = executors.get(Ranking.LOW);
+ if (ex != null)
+ {
+ logger.debug("returning low priority executor");
+ return ex;
+ }
+ }
+
+ return null;
+ }
+
+ public void setScenarios(List scenarios)
+ {
+ this.scenarios = scenarios;
+ }
+
+ public List getScenarios()
+ {
+ return scenarios;
+ }
+
+ @Override
+ public EvaluatorContext createContext()
+ {
+ return new EvaluatorContextImpl();
+ }
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/Scenario.java b/source/java/org/alfresco/filesys/repo/rules/Scenario.java
new file mode 100644
index 0000000000..beb4f83f6c
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/Scenario.java
@@ -0,0 +1,41 @@
+/*
+ * 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.rules;
+
+import java.util.List;
+
+/**
+ * A scenario is a factory for scenario instances.
+ *
+ */
+public interface Scenario
+{
+ /**
+ * Create a new ScenarioInstance
+ *
+ * If the scenario is interested in the specified operation then
+ * return a new scenario instance.
+ * @param currentInstances the current instances of all scenarios.
+ * @param operation the operation to be performed
+ * @return the scenario instance or null if a new instance is not required.
+ */
+ ScenarioInstance createInstance(final List currentInstances, Operation operation);
+
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffle.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffle.java
new file mode 100644
index 0000000000..73e843de6d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffle.java
@@ -0,0 +1,115 @@
+/*
+ * 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.rules;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A shuffle
+ *
+ * a) New file created.
+ * b) Existing file moved out of the way
+ * c) New file moved into place.
+ * d) Old file deleted.
+ */
+public class ScenarioCreateShuffle implements Scenario
+{
+ private static Log logger = LogFactory.getLog(ScenarioCreateShuffle.class);
+
+ /**
+ * The regex pattern of a create that will trigger a new instance of
+ * the scenario.
+ */
+ private Pattern pattern;
+ private String strPattern;
+
+
+ private long timeout = 30000;
+
+ private Ranking ranking = Ranking.HIGH;
+
+ @Override
+ public ScenarioInstance createInstance(final List currentInstances, Operation operation)
+ {
+ /**
+ * This scenario is triggered by a create of a file matching
+ * the pattern
+ */
+ if(operation instanceof CreateFileOperation)
+ {
+ CreateFileOperation c = (CreateFileOperation)operation;
+
+ Matcher m = pattern.matcher(c.getName());
+ if(m.matches())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("New Scenario Create Shuffle Instance pattern:" + strPattern);
+ }
+
+ ScenarioCreateShuffleInstance instance = new ScenarioCreateShuffleInstance() ;
+ instance.setTimeout(timeout);
+ instance.setRanking(ranking);
+ return instance;
+ }
+ }
+
+ // No not interested.
+ return null;
+
+ }
+
+ public void setPattern(String pattern)
+ {
+ this.pattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
+ this.strPattern = pattern;
+ }
+
+ public String getPattern()
+ {
+ return this.strPattern;
+ }
+
+ public void setTimeout(long timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ }
+
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffleInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffleInstance.java
new file mode 100644
index 0000000000..b782a74c4a
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateShuffleInstance.java
@@ -0,0 +1,277 @@
+/*
+ * 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.rules;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.alfresco.filesys.repo.rules.commands.CompoundCommand;
+import org.alfresco.filesys.repo.rules.commands.CopyContentCommand;
+import org.alfresco.filesys.repo.rules.commands.RenameFileCommand;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.alfresco.filesys.repo.rules.operations.DeleteFileOperation;
+import org.alfresco.filesys.repo.rules.operations.RenameFileOperation;
+import org.alfresco.jlan.server.filesys.FileName;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This is an instance of a "classic shuffle" triggered by a create of a
+ * file matching a specified pattern.
+ *
+ * a) New file created. Typically with an obscure name.
+ * b) Existing file moved out of the way
+ * c) New file moved into place.
+ * d) Old file deleted.
+ *
+ *
+ * If this filter is active then this is what happens.
+ * a) New file created. New file created (X).
+ * b) Existing file moved out of the way (Y to Z). Raname tracked.
+ * c) New file moved into place (X to Y). Scenario kicks in to change commands.
+ * d) Old file deleted.
+ */
+public class ScenarioCreateShuffleInstance implements ScenarioInstance
+{
+ private static Log logger = LogFactory.getLog(ScenarioCreateShuffleInstance.class);
+
+ enum InternalState
+ {
+ NONE,
+ RENAME,
+ DELETE
+ }
+
+ InternalState internalState = InternalState.NONE;
+
+ private Date startTime = new Date();
+
+ private String createName;
+ private String move1;
+ private String move2;
+ private Ranking ranking;
+
+ /**
+ * Timeout in ms. Default 30 seconds.
+ */
+ private long timeout = 30000;
+
+ private boolean isComplete;
+
+ /**
+ * Keep track of re-names
+ */
+ private Maprenames = new HashMap();
+
+ /**
+ * Evaluate the next operation
+ * @param operation
+ */
+ public Command evaluate(Operation operation)
+ {
+
+// /**
+// * Anti-pattern for all states - delete the file we are
+// * shuffling
+// */
+// if(createName != null)
+// {
+// if(operation instanceof DeleteFileOperation)
+// {
+// DeleteFileOperation d = (DeleteFileOperation)operation;
+// if(d.getName().equals(createName))
+// {
+// logger.debug("Anti-pattern : Shuffle file deleted");
+// isComplete = true;
+// return null;
+// }
+// }
+// }
+
+ /**
+ * Anti-pattern : timeout
+ */
+ Date now = new Date();
+ if(now.getTime() > startTime.getTime() + getTimeout())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Instance timed out");
+ }
+ }
+
+ switch (internalState)
+ {
+ case NONE:
+ // Looking for a create transition
+ if(operation instanceof CreateFileOperation)
+ {
+ CreateFileOperation c = (CreateFileOperation)operation;
+ this.createName = c.getName();
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("entering RENAME state: " + createName);
+ }
+ internalState = InternalState.RENAME;
+ return null;
+ }
+ else
+ {
+ // anything else bomb out
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("State error, expected a CREATE");
+ }
+ isComplete = true;
+ }
+ break;
+
+ case RENAME:
+
+ /**
+ * Looking for two renames X(createName) to Y(middle) to Z(end)
+ */
+ if(operation instanceof RenameFileOperation)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Tracking rename: " + operation);
+ }
+ RenameFileOperation r = (RenameFileOperation)operation;
+ renames.put(r.getFrom(), r.getTo());
+
+ // Now see if this rename makes a pair.
+ String middle = renames.get(createName);
+ if(middle != null)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Got second rename" );
+ }
+
+ String end = renames.get(middle);
+
+ if(end != null)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Got two renames " );
+ }
+ this.move1 = middle;
+ this.move2 = end;
+
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("entering DELETE state");
+ }
+
+ internalState = InternalState.DELETE;
+
+ /**
+ * This shuffle reverses the rename out of the way and then copies the
+ * content only. Finally it moves the temp file into place for the subsequent
+ * delete.
+ * a) Rename Z to Y (Reverse previous move)
+ * b) Copy Content from X to Y
+ * c) Rename X to Z (move temp file out to old location)
+ */
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Go and shuffle! createName:" + createName + " move1 " + move1 + " move2 " + move2);
+ }
+
+ String[] paths = FileName.splitPath(r.getFromPath());
+ String oldFolder = paths[0];
+ // String oldFile = paths[1];
+
+ ArrayList commands = new ArrayList();
+ RenameFileCommand r1 = new RenameFileCommand(end, middle, r.getRootNodeRef(), oldFolder + "\\" + end, oldFolder + "\\" + middle);
+ CopyContentCommand copyContent = new CopyContentCommand(createName, move1, r.getRootNodeRef(), oldFolder + "\\" + createName, oldFolder + "\\" + middle);
+ RenameFileCommand r2 = new RenameFileCommand(createName, end, r.getRootNodeRef(), oldFolder + "\\" + createName, oldFolder + "\\" + end);
+
+ commands.add(r1);
+ commands.add(copyContent);
+ commands.add(r2);
+
+ return new CompoundCommand(commands);
+ }
+ }
+ }
+
+ break;
+
+ case DELETE:
+
+ /**
+ * Looking for a delete of the destination
+ */
+ if(operation instanceof DeleteFileOperation)
+ {
+ DeleteFileOperation d = (DeleteFileOperation)operation;
+ if(d.getName().equals(move2))
+ {
+ logger.debug("Scenario complete");
+ isComplete = true;
+// ArrayList commands = new ArrayList();
+// // missing stuff here
+// return new CompoundCommand(commands);
+ }
+ }
+
+ break;
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isComplete()
+ {
+ return isComplete;
+ }
+
+ @Override
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ }
+
+ public String toString()
+ {
+ return "ScenarioShuffleInstance:" + createName;
+ }
+
+ public void setTimeout(long timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioInstance.java
new file mode 100644
index 0000000000..2a422d11fb
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioInstance.java
@@ -0,0 +1,58 @@
+/*
+ * 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.rules;
+
+/**
+ * A scenario instance is an active scenario. It has a ranking, an
+ * evaluate method and knows whether it is complete.
+ *
+ * The evaluate method is called repeatedly as operations are processed.
+ */
+public interface ScenarioInstance
+{
+ enum Ranking
+ {
+ LOW, // Bottom priority
+ MEDIUM,
+ HIGH,
+
+ };
+
+ /**
+ * Get the Ranking
+ * @return
+ */
+ public Ranking getRanking();
+
+ /**
+ * evaluate the scenario against the current operation
+ *
+ * @param operation
+ */
+ public Command evaluate(Operation operation);
+
+ /**
+ * Is the scenario complete?
+ *
+ * @return
+ */
+ public boolean isComplete();
+
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFile.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFile.java
new file mode 100644
index 0000000000..ab871edf97
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFile.java
@@ -0,0 +1,179 @@
+/*
+ * 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.rules;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.alfresco.filesys.repo.rules.operations.OpenFileOperation;
+import org.alfresco.filesys.repo.rules.operations.RenameFileOperation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The Open File Scenario is a sequence of multiple openFile operations
+ *
+ * Only on the last close does the repo get closed. Open Files in the middle
+ * share the same file handle.
+ *
+ * For example:
+ *
+ * 1) open(readOnly)
+ * 2) open(readWrite)
+ * 3) open(readOnly) - does nothing.
+ * 4) close - does nothing
+ * 5) close - does nothing
+ * 6) close - updates the repo
+ */
+public class ScenarioOpenFile implements Scenario
+{
+ private static Log logger = LogFactory.getLog(ScenarioOpenFile.class);
+
+ private String pattern;
+
+ private long timeout = 300000;
+
+ @Override
+ public ScenarioInstance createInstance(final List currentInstances, Operation operation)
+ {
+ /**
+ * This scenario is triggered by an open or create of a new file
+ */
+ if(operation instanceof CreateFileOperation)
+ {
+ CreateFileOperation c = (CreateFileOperation)operation;
+ if(c.getName() == null)
+ {
+ logger.debug("c.getName is null! - scenario not active");
+ return null;
+ }
+
+ if(c.getName().matches(pattern))
+ {
+
+ if(checkScenarioActive(c.getName(),currentInstances))
+ {
+ logger.debug("scenario already active for name" + c.getName());
+ return null;
+ }
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("New Open File Instance for CreateFileOperation:" + c);
+ }
+
+ ScenarioOpenFileInstance instance = new ScenarioOpenFileInstance();
+ instance.setTimeout(timeout);
+ instance.setRanking(ranking);
+ return instance;
+ }
+ }
+
+ if(operation instanceof OpenFileOperation)
+ {
+
+ OpenFileOperation o = (OpenFileOperation)operation;
+
+ if(o.getName() == null)
+ {
+ logger.debug("o.getName is null! - scenario not active");
+ return null;
+ }
+
+ if(o.getName().matches(pattern))
+ {
+ if(checkScenarioActive(o.getName(),currentInstances))
+ {
+ logger.debug("scenario already active for name" + o.getName());
+ return null;
+ }
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("New Open File Instance for OpenFileOperation:" + o);
+ }
+
+ ScenarioOpenFileInstance instance = new ScenarioOpenFileInstance();
+ instance.setTimeout(timeout);
+ instance.setRanking(ranking);
+ return instance;
+ }
+ }
+
+ // No not interested.
+ return null;
+
+ }
+
+ public void setTimeout(long timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+
+ public void setPattern(String pattern)
+ {
+ this.pattern = pattern;
+ }
+
+ public String getPattern()
+ {
+ return pattern;
+ }
+
+ private Ranking ranking = Ranking.HIGH;
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ }
+
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+
+ /**
+ * Check whether there is already an instance of the ScenarioOpenFile for the file
+ */
+ private boolean checkScenarioActive(String name, final List currentInstances)
+ {
+ for(ScenarioInstance instance: currentInstances)
+ {
+ if(instance instanceof ScenarioOpenFileInstance)
+ {
+ ScenarioOpenFileInstance i = (ScenarioOpenFileInstance)instance;
+ if(i.getName().equalsIgnoreCase(name));
+ {
+ return true;
+ }
+
+ }
+ }
+ return false;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFileInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFileInstance.java
new file mode 100644
index 0000000000..e8f54ec231
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFileInstance.java
@@ -0,0 +1,417 @@
+/*
+ * 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.rules;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import org.alfresco.filesys.repo.ResultCallback;
+import org.alfresco.filesys.repo.TempNetworkFile;
+import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking;
+import org.alfresco.filesys.repo.rules.ScenarioRenameShuffleInstance.InternalState;
+import org.alfresco.filesys.repo.rules.commands.CallbackCommand;
+import org.alfresco.filesys.repo.rules.commands.CloseFileCommand;
+import org.alfresco.filesys.repo.rules.commands.CompoundCommand;
+import org.alfresco.filesys.repo.rules.commands.CopyContentCommand;
+import org.alfresco.filesys.repo.rules.commands.CreateFileCommand;
+import org.alfresco.filesys.repo.rules.commands.DeleteFileCommand;
+import org.alfresco.filesys.repo.rules.commands.DoNothingCommand;
+import org.alfresco.filesys.repo.rules.commands.OpenFileCommand;
+import org.alfresco.filesys.repo.rules.commands.ReduceQuotaCommand;
+import org.alfresco.filesys.repo.rules.commands.RemoveNoContentFileOnError;
+import org.alfresco.filesys.repo.rules.commands.RemoveTempFileCommand;
+import org.alfresco.filesys.repo.rules.commands.RenameFileCommand;
+import org.alfresco.filesys.repo.rules.commands.ReturnValueCommand;
+import org.alfresco.filesys.repo.rules.operations.CloseFileOperation;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.alfresco.filesys.repo.rules.operations.DeleteFileOperation;
+import org.alfresco.filesys.repo.rules.operations.OpenFileOperation;
+import org.alfresco.filesys.repo.rules.operations.RenameFileOperation;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * An open file scenario is ...
+ *
+ * 1) open(readOnly)
+ * 2) close(readOnly)
+ *
+ * 1) open(readOnly)
+ * 2) open(readWrite)
+ * 3) close(readOnly)
+ * 4 close(readWrite) updates the repo
+ *
+ * 1) open(readOnly)
+ * 2) open(readWrite)
+ * 3) open(readWrite) - does nothing. Increments Open Count.
+ * 4) close(readWrite) - does nothing. Decrements Open Count.
+ * 5) close(readWrite) - updates the repo.
+ * 6) close(readOnly) - closes read only
+ *
+ */
+class ScenarioOpenFileInstance implements ScenarioInstance
+{
+ private static Log logger = LogFactory.getLog(ScenarioOpenFileInstance.class);
+
+ private Date startTime = new Date();
+
+ private String name;
+
+ enum InternalState
+ {
+ NONE,
+ OPEN
+ } ;
+
+ InternalState state = InternalState.NONE;
+
+ /**
+ * For each read only open file
+ */
+ private NetworkFile fileHandleReadOnly;
+ private int openReadOnlyCount = 0;
+
+ /**
+ * For each read/write open file
+ */
+ private NetworkFile fileHandleReadWrite;
+ private int openReadWriteCount = 0;
+
+ /**
+ * Timeout in ms. Default 30 seconds.
+ */
+ private long timeout = 30000;
+
+ private boolean isComplete = false;
+
+ private Ranking ranking = Ranking.HIGH;
+
+ /**
+ * Evaluate the next operation
+ * @param operation
+ */
+ public Command evaluate(Operation operation)
+ {
+ /**
+ * Anti-pattern : timeout - this scenario does not timeout
+ */
+ // Date now = new Date();
+ // if(now.getTime() > startTime.getTime() + getTimeout())
+ // {
+ // if(logger.isDebugEnabled())
+ // {
+ // logger.debug("Instance timed out");
+ // }
+ // }
+
+ /**
+ * Anti Pattern - Delete of the open file.
+ */
+ if(operation instanceof DeleteFileOperation)
+ {
+ DeleteFileOperation d = (DeleteFileOperation)operation;
+ if(name.equalsIgnoreCase(d.getName()))
+ {
+ logger.debug("Anti-Pattern - delete of the open file, scenario:" + this);
+ isComplete = true;
+ return null;
+ }
+ }
+
+ switch (state)
+ {
+ case NONE:
+ if(operation instanceof CreateFileOperation)
+ {
+ CreateFileOperation c = (CreateFileOperation)operation;
+ name = c.getName();
+ ArrayList commands = new ArrayList();
+ ArrayList postCommitCommands = new ArrayList();
+ commands.add(new CreateFileCommand(c.getName(), c.getRootNodeRef(), c.getPath()));
+ postCommitCommands.add(newOpenFileCallbackCommand());
+ return new CompoundCommand(commands, postCommitCommands);
+ }
+ else if(operation instanceof OpenFileOperation)
+ {
+ OpenFileOperation o = (OpenFileOperation)operation;
+ name = o.getName();
+ ArrayList commands = new ArrayList();
+ commands.add(new OpenFileCommand(o.getName(), o.isWriteAccess(), o.isTruncate(), o.getRootNodeRef(), o.getPath()));
+ ArrayList postCommitCommands = new ArrayList();
+ postCommitCommands.add(newOpenFileCallbackCommand());
+ return new CompoundCommand(commands, postCommitCommands);
+ }
+
+ // Scenario Not Started
+ isComplete = true;
+ return null;
+
+ case OPEN:
+
+ if(operation instanceof CloseFileOperation)
+ {
+ CloseFileOperation c = (CloseFileOperation)operation;
+ if(name.equalsIgnoreCase(c.getName()))
+ {
+ NetworkFile file = c.getNetworkFile();
+ if(file.isReadOnly())
+ {
+ // Read Only File
+ if(openReadOnlyCount == 1)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Close of last read only file handle:" + this);
+ }
+
+ openReadOnlyCount = 0;
+
+ if(openReadWriteCount <= 0)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Scenario is complete:" + this);
+ }
+ isComplete=true;
+ }
+
+ if (file instanceof TempNetworkFile)
+ {
+ logger.debug("this is the last close of a temp read only file");
+ ArrayList commands = new ArrayList();
+ ArrayList postCommitCommands = new ArrayList();
+
+ commands.add(new CloseFileCommand(c.getName(), file, c.getRootNodeRef(), c.getPath()));
+ postCommitCommands.add(new RemoveTempFileCommand((TempNetworkFile)file));
+ return new CompoundCommand(commands, postCommitCommands);
+ }
+ else
+ {
+ return new CloseFileCommand(c.getName(), file, c.getRootNodeRef(), c.getPath());
+ }
+ }
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Only decrement count of read only file handle:" + this);
+ }
+
+ openReadOnlyCount--;
+
+ return new DoNothingCommand();
+ }
+ else
+ {
+ // This is a close of a Read Write File
+ // Read Only File
+ if(openReadWriteCount == 1)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Close of last read write file handle:" + this);
+ }
+
+ openReadWriteCount = 0;
+
+ if(openReadOnlyCount <= 0)
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Scenario is complete:" + this);
+ }
+ isComplete=true;
+ }
+
+
+ //
+ ArrayList commands = new ArrayList();
+ ArrayList postCommitCommands = new ArrayList();
+ ArrayList postErrorCommands = new ArrayList();
+
+ commands.add(new CloseFileCommand(c.getName(), file, c.getRootNodeRef(), c.getPath()));
+
+ postErrorCommands.add(new RemoveNoContentFileOnError(c.getName(), c.getRootNodeRef(), c.getPath()));
+
+ if(c.isDeleteOnClose())
+ {
+ postCommitCommands.add(new ReduceQuotaCommand(c.getName(), file, c.getRootNodeRef(), c.getPath()));
+ }
+
+ if (file instanceof TempNetworkFile)
+ {
+ postCommitCommands.add(new RemoveTempFileCommand((TempNetworkFile)file));
+ }
+
+ return new CompoundCommand(commands, postCommitCommands, postErrorCommands);
+
+ }
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Only decrement count of read write file handle:" + this);
+ }
+
+ openReadWriteCount--;
+
+ return new DoNothingCommand();
+ }
+ }
+ }
+ else if(operation instanceof OpenFileOperation)
+ {
+ OpenFileOperation o = (OpenFileOperation)operation;
+
+ if(name.equalsIgnoreCase(o.getName()))
+ {
+ if(o.isWriteAccess())
+ {
+ // This is an open of a read write access
+ if(openReadWriteCount == 0)
+ {
+ logger.debug("Open first read/write from scenario:" + this);
+ ArrayList commands = new ArrayList();
+ commands.add(new OpenFileCommand(o.getName(), o.isWriteAccess(), o.isTruncate(), o.getRootNodeRef(), o.getPath()));
+ ArrayList postCommitCommands = new ArrayList();
+ postCommitCommands.add(newOpenFileCallbackCommand());
+ return new CompoundCommand(commands, postCommitCommands);
+ }
+ else
+ {
+ openReadWriteCount++;
+ logger.debug("Return already open read/write file handle from scenario:" + this);
+ return new ReturnValueCommand(fileHandleReadWrite);
+ }
+ }
+ else
+ {
+ // This is an open for read only access
+ if(openReadOnlyCount == 0)
+ {
+ logger.debug("Open first read only from scenario:" + this);
+ ArrayList commands = new ArrayList();
+ commands.add(new OpenFileCommand(o.getName(), o.isWriteAccess(), o.isTruncate(), o.getRootNodeRef(), o.getPath()));
+ ArrayList postCommitCommands = new ArrayList();
+ postCommitCommands.add(newOpenFileCallbackCommand());
+ return new CompoundCommand(commands, postCommitCommands);
+ }
+ else
+ {
+ openReadOnlyCount++;
+ logger.debug("Return already open only file handle from scenario:" + this);
+ return new ReturnValueCommand(fileHandleReadOnly);
+ }
+ }
+ }
+ }
+
+ break;
+
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isComplete()
+ {
+ return isComplete;
+ }
+
+ public String toString()
+ {
+ return "ScenarioOpenFileInstance name:" + name;
+ }
+
+ public void setTimeout(long timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+
+ @Override
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ /**
+ * Called for open file.
+ */
+ private ResultCallback newOpenFileCallbackCommand()
+ {
+ return new ResultCallback()
+ {
+ @Override
+ public void execute(Object result)
+ {
+ if(result instanceof NetworkFile)
+ {
+
+ // Now update the state of this scenario - we have an open fileHandle
+ NetworkFile fileHandle = (NetworkFile)result;
+
+ state = InternalState.OPEN;
+
+ if(fileHandle.isReadOnly())
+ {
+ openReadOnlyCount++;
+ fileHandleReadOnly=fileHandle;
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("file opened read only:" + result + ", name:" + name);
+ }
+ }
+ else
+ {
+ openReadWriteCount++;
+ fileHandleReadWrite=fileHandle;
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("file opened read write :" + result + ", name:" + name);
+ }
+ }
+ }
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_NONE;
+ }
+ };
+ }
+}
+
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioRenameShuffle.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioRenameShuffle.java
new file mode 100644
index 0000000000..57e907b8d7
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioRenameShuffle.java
@@ -0,0 +1,115 @@
+/*
+ * 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.rules;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking;
+import org.alfresco.filesys.repo.rules.operations.RenameFileOperation;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * The "Vi" shuffle is a sequence where a file is moved out of the way
+ * and then a new copy of the file put into place.
+ *
+ * a) Rename File to File~
+ * b) Create File
+ *
+ */
+public class ScenarioRenameShuffle implements Scenario
+{
+ private static Log logger = LogFactory.getLog(ScenarioRenameShuffle.class);
+
+ /**
+ * The regex pattern of a create that will trigger a new instance of
+ * the scenario.
+ */
+ private Pattern pattern;
+ private String strPattern;
+
+ private long timeout = 30000;
+
+ @Override
+ public ScenarioInstance createInstance(final List currentInstances, Operation operation)
+ {
+ /**
+ * This scenario is triggered by a rename of a file matching
+ * the pattern
+ */
+ if(operation instanceof RenameFileOperation)
+ {
+ RenameFileOperation r = (RenameFileOperation)operation;
+
+ Matcher m = pattern.matcher(r.getTo());
+ if(m.matches())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("New Scenario Rename Shuffle Instance strPattern:" + pattern);
+ }
+ ScenarioRenameShuffleInstance instance = new ScenarioRenameShuffleInstance();
+ instance.setTimeout(timeout);
+ instance.setRanking(ranking);
+ return instance;
+ }
+ }
+
+ // No not interested.
+ return null;
+ }
+
+ public void setTimeout(long timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+
+ public void setPattern(String pattern)
+ {
+ this.pattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
+ this.strPattern = pattern;
+ }
+
+ public String getPattern()
+ {
+ return strPattern;
+ }
+
+ private Ranking ranking = Ranking.HIGH;
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ }
+
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioRenameShuffleInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioRenameShuffleInstance.java
new file mode 100644
index 0000000000..701e3e8e7c
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioRenameShuffleInstance.java
@@ -0,0 +1,188 @@
+/*
+ * 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.rules;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking;
+import org.alfresco.filesys.repo.rules.commands.CompoundCommand;
+import org.alfresco.filesys.repo.rules.commands.CopyContentCommand;
+import org.alfresco.filesys.repo.rules.commands.DeleteFileCommand;
+import org.alfresco.filesys.repo.rules.commands.RenameFileCommand;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.alfresco.filesys.repo.rules.operations.DeleteFileOperation;
+import org.alfresco.filesys.repo.rules.operations.RenameFileOperation;
+import org.alfresco.jlan.server.filesys.FileName;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A rename scenario is ...
+ *
+ * a) Rename File to File~
+ * b) Create File
+ * c) Delete File~
+ *
+ * This rule will kick in and copy the content and then switch the two file over.
+ *
+ */
+class ScenarioRenameShuffleInstance implements ScenarioInstance
+{
+ private static Log logger = LogFactory.getLog(ScenarioRenameShuffleInstance.class);
+
+ private Date startTime = new Date();
+
+ /**
+ * Timeout in ms. Default 30 seconds.
+ */
+ private long timeout = 30000;
+
+ private boolean isComplete = false;
+
+ private Ranking ranking = Ranking.HIGH;
+
+ enum InternalState
+ {
+ NONE,
+ INITIALISED,
+ LOOK_FOR_DELETE
+ } ;
+
+ InternalState state = InternalState.NONE;
+
+ String from;
+ String to;
+
+ /**
+ * Evaluate the next operation
+ * @param operation
+ */
+ public Command evaluate(Operation operation)
+ {
+ /**
+ * Anti-pattern : timeout
+ */
+ Date now = new Date();
+ if(now.getTime() > startTime.getTime() + getTimeout())
+ {
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("Instance timed out");
+ }
+ }
+
+ switch (state)
+ {
+ case NONE:
+ if(operation instanceof RenameFileOperation)
+ {
+ logger.debug("New scenario initialised");
+ RenameFileOperation r = (RenameFileOperation)operation;
+ this.from = r.getFrom();
+ this.to = r.getTo();
+ state = InternalState.INITIALISED;
+ }
+ break;
+
+ case INITIALISED:
+
+ if(operation instanceof CreateFileOperation)
+ {
+ CreateFileOperation c = (CreateFileOperation)operation;
+ if(from.equals(c.getName()))
+ {
+ logger.debug("transition to LOOK_FOR_DELETE");
+
+ state = InternalState.LOOK_FOR_DELETE;
+ }
+ }
+ break;
+
+ case LOOK_FOR_DELETE:
+ if(operation instanceof DeleteFileOperation)
+ {
+ DeleteFileOperation d = (DeleteFileOperation)operation;
+ if(to.equals(d.getName()))
+ {
+ logger.debug("Rename shuffle complete - fire!");
+
+ String[] paths = FileName.splitPath(d.getPath());
+ String oldFolder = paths[0];
+
+ /**
+ * Shuffle is as follows
+ * a) Copy content from File to File~
+ * b) Delete File
+ * c) Rename File~ to File
+ */
+ ArrayList commands = new ArrayList();
+ CopyContentCommand copyContent = new CopyContentCommand(from, to, d.getRootNodeRef(), oldFolder + "\\" + from, oldFolder + "\\" + to);
+ RenameFileCommand r1 = new RenameFileCommand(to, from, d.getRootNodeRef(), oldFolder + "\\" + to, oldFolder + "\\" + from);
+ DeleteFileCommand d1 = new DeleteFileCommand(from, d.getRootNodeRef(), oldFolder + "\\" + from);
+
+ commands.add(copyContent);
+ commands.add(d1);
+ commands.add(r1);
+
+ logger.debug("Scenario complete");
+ isComplete = true;
+ return new CompoundCommand(commands);
+ }
+ }
+ }
+
+
+ return null;
+ }
+
+ @Override
+ public boolean isComplete()
+ {
+ return isComplete;
+ }
+
+ public String toString()
+ {
+ return "ScenarioRenameShuffleInstance from:" + from + " to:" + to;
+ }
+
+ public void setTimeout(long timeout)
+ {
+ this.timeout = timeout;
+ }
+
+ public long getTimeout()
+ {
+ return timeout;
+ }
+
+ @Override
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ }
+
+}
+
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBuffered.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBuffered.java
new file mode 100644
index 0000000000..697b930fad
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBuffered.java
@@ -0,0 +1,61 @@
+/*
+ * 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.rules;
+
+import java.util.List;
+
+import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking;
+
+/**
+ * The Bog Standard Instance. This always executes.
+ *
+ * @author mrogers
+ */
+public class ScenarioSimpleNonBuffered implements Scenario
+{
+ private ScenarioSimpleNonBufferedInstance instance = new ScenarioSimpleNonBufferedInstance();
+
+ private Ranking ranking = Ranking.LOW;
+
+ @Override
+ public ScenarioInstance createInstance(final List currentInstances, Operation operation)
+ {
+ /**
+ * The bog standard scenario is always interested.
+ */
+ return instance;
+ }
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ instance.setRanking(ranking);
+ }
+
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+
+ public String toString()
+ {
+ return "ScenarioSimpleNonBuffered - default instance";
+ }
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBufferedInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBufferedInstance.java
new file mode 100644
index 0000000000..559f40fa87
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBufferedInstance.java
@@ -0,0 +1,136 @@
+/*
+ * 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.rules;
+
+import java.util.ArrayList;
+
+import org.alfresco.filesys.repo.TempNetworkFile;
+import org.alfresco.filesys.repo.rules.ScenarioInstance.Ranking;
+import org.alfresco.filesys.repo.rules.commands.CloseFileCommand;
+import org.alfresco.filesys.repo.rules.commands.CompoundCommand;
+import org.alfresco.filesys.repo.rules.commands.CopyContentCommand;
+import org.alfresco.filesys.repo.rules.commands.CreateFileCommand;
+import org.alfresco.filesys.repo.rules.commands.DoNothingCommand;
+import org.alfresco.filesys.repo.rules.commands.MoveFileCommand;
+import org.alfresco.filesys.repo.rules.commands.DeleteFileCommand;
+import org.alfresco.filesys.repo.rules.commands.OpenFileCommand;
+import org.alfresco.filesys.repo.rules.commands.ReduceQuotaCommand;
+import org.alfresco.filesys.repo.rules.commands.RemoveNoContentFileOnError;
+import org.alfresco.filesys.repo.rules.commands.RemoveTempFileCommand;
+import org.alfresco.filesys.repo.rules.commands.RenameFileCommand;
+import org.alfresco.filesys.repo.rules.operations.CloseFileOperation;
+import org.alfresco.filesys.repo.rules.operations.CreateFileOperation;
+import org.alfresco.filesys.repo.rules.operations.DeleteFileOperation;
+import org.alfresco.filesys.repo.rules.operations.MoveFileOperation;
+import org.alfresco.filesys.repo.rules.operations.OpenFileOperation;
+import org.alfresco.filesys.repo.rules.operations.RenameFileOperation;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+
+/**
+ * The Simple Standard Scenario is what will be done if no other
+ * scenario intervenes.
+ */
+public class ScenarioSimpleNonBufferedInstance implements ScenarioInstance
+{
+ private Ranking ranking = Ranking.LOW;
+
+ @Override
+ public Command evaluate(Operation operation)
+ {
+ if(operation instanceof CreateFileOperation)
+ {
+ CreateFileOperation c = (CreateFileOperation)operation;
+ return new CreateFileCommand(c.getName(), c.getRootNodeRef(), c.getPath());
+ }
+ else if(operation instanceof DeleteFileOperation)
+ {
+ DeleteFileOperation d = (DeleteFileOperation)operation;
+ return new DeleteFileCommand(d.getName(), d.getRootNodeRef(), d.getPath());
+ }
+ else if(operation instanceof RenameFileOperation)
+ {
+ RenameFileOperation r = (RenameFileOperation)operation;
+ return new RenameFileCommand(r.getFrom(), r.getTo(), r.getRootNodeRef(), r.getFromPath(), r.getToPath());
+ }
+ else if(operation instanceof MoveFileOperation)
+ {
+ MoveFileOperation m = (MoveFileOperation)operation;
+ return new MoveFileCommand(m.getFrom(), m.getTo());
+ }
+ else if(operation instanceof OpenFileOperation)
+ {
+ OpenFileOperation o = (OpenFileOperation)operation;
+ return new OpenFileCommand(o.getName(), o.isWriteAccess(), o.isTruncate(), o.getRootNodeRef(), o.getPath());
+ }
+ else if(operation instanceof CloseFileOperation)
+ {
+ CloseFileOperation c = (CloseFileOperation)operation;
+
+ NetworkFile file = c.getNetworkFile();
+
+ ArrayList commands = new ArrayList();
+ ArrayList postCommitCommands = new ArrayList();
+ ArrayList postErrorCommands = new ArrayList();
+
+ commands.add(new CloseFileCommand(c.getName(), file, c.getRootNodeRef(), c.getPath()));
+
+ postErrorCommands.add(new RemoveNoContentFileOnError(c.getName(), c.getRootNodeRef(), c.getPath()));
+
+ if(c.isDeleteOnClose())
+ {
+ postCommitCommands.add(new ReduceQuotaCommand(c.getName(), file, c.getRootNodeRef(), c.getPath()));
+ }
+
+ if (file instanceof TempNetworkFile)
+ {
+ postCommitCommands.add(new RemoveTempFileCommand((TempNetworkFile)file));
+ }
+
+ return new CompoundCommand(commands, postCommitCommands, postErrorCommands);
+
+ }
+ else return new DoNothingCommand();
+ }
+
+ @Override
+ public boolean isComplete()
+ {
+ /**
+ * This instance is always complete
+ */
+ return true;
+ }
+
+
+ @Override
+ public Ranking getRanking()
+ {
+ return ranking;
+ }
+
+ public void setRanking(Ranking ranking)
+ {
+ this.ranking = ranking;
+ }
+
+ public String toString()
+ {
+ return "ScenarioSimpleNonBuffered default instance";
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/ShuffleTest.java b/source/java/org/alfresco/filesys/repo/rules/ShuffleTest.java
new file mode 100644
index 0000000000..b566be419b
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/ShuffleTest.java
@@ -0,0 +1,346 @@
+/*
+ * 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.rules;
+
+
+import java.util.ArrayList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import junit.framework.TestCase;
+
+/**
+ * Shuffle Test
+ */
+//public class ShuffleTest extends TestCase
+//{
+// private static Log logger = LogFactory.getLog(ShuffleTest.class);
+//
+// /**
+// * A single shuffle
+// */
+// public void testSingleShuffle()
+// {
+// logger.debug("testA start");
+// RuleEvaluatorImpl evaluator = new RuleEvaluatorImpl();
+//
+// CommandExecutorImpl executor = new CommandExecutorImpl();
+//
+// ScenarioCreateShuffle shuffle1 = new ScenarioCreateShuffle();
+// shuffle1.setPattern("~WRD.*.TMP");
+// ScenarioSimpleNonBuffered bogStandard = new ScenarioSimpleNonBuffered();
+// ArrayList scenarios = new ArrayList();
+//
+// scenarios.add(bogStandard);
+// scenarios.add(shuffle1);
+//
+// evaluator.setScenarios(scenarios);
+//
+// /**
+// * Bog standard create
+// */
+// Operation c = new CreateFileOperation("MER.TXT");
+// Command x = evaluator.evaluate(c);
+// executor.execute(x);
+//
+// assertTrue(x instanceof SimpleNonBufferedCommand);
+//
+// /**
+// * A simple shuffle
+// */
+// Operation c2 = new CreateFileOperation("~WRD0001.TMP");
+// x = evaluator.evaluate(c2);
+// executor.execute(x);
+// assertTrue(x instanceof SimpleNonBufferedCommand);
+//
+// Operation r1 = new RenameFileOperation("X", "Y");
+// x = evaluator.evaluate(r1);
+// executor.execute(x);
+//
+// Operation r2 = new RenameFileOperation("~WRD0001.TMP", "X");
+// x = evaluator.evaluate(r2);
+// executor.execute(x);
+//
+// Operation d1 = new DeleteFileOperation("Y");
+// x = evaluator.evaluate(d1);
+// executor.execute(x);
+//
+// assertTrue(x instanceof CompoundCommand);
+//
+//
+//
+// }
+//
+// /**
+// * Two shuffles inter-twined
+// */
+// public void testDualShuffle()
+// {
+// logger.debug("test dual shuffle ");
+// RuleEvaluatorImpl evaluator = new RuleEvaluatorImpl();
+// CommandExecutorImpl executor = new CommandExecutorImpl();
+//
+// ScenarioCreateShuffle shuffle1 = new ScenarioCreateShuffle();
+// shuffle1.setPattern("~WRD.*.TMP");
+// ScenarioSimpleNonBuffered bogStandard = new ScenarioSimpleNonBuffered();
+// ArrayList scenarios = new ArrayList();
+//
+// scenarios.add(bogStandard);
+// scenarios.add(shuffle1);
+//
+// evaluator.setScenarios(scenarios);
+//
+// /**
+// * Bog standard create
+// */
+// Operation c = new CreateFileOperation("MER.TXT");
+// Command x = evaluator.evaluate(c);
+// executor.execute(x);
+//
+// /**
+// * Two shuffles interleaved
+// */
+// Operation c2 = new CreateFileOperation("~WRD0001.TMP");
+// x = evaluator.evaluate(c2);
+// executor.execute(x);
+//
+// Operation c3 = new CreateFileOperation("~WRD0002.TMP");
+// x = evaluator.evaluate(c3);
+// executor.execute(x);
+//
+// Operation r1 = new RenameFileOperation("X", "Y");
+// x = evaluator.evaluate(r1);
+// executor.execute(x);
+//
+// Operation r2 = new RenameFileOperation("A", "B");
+// x = evaluator.evaluate(r2);
+// executor.execute(x);
+//
+// Operation r3 = new RenameFileOperation("~WRD0001.TMP", "X");
+// x = evaluator.evaluate(r3);
+// executor.execute(x);
+//
+// Operation r4 = new RenameFileOperation("~WRD0002.TMP", "A");
+// x = evaluator.evaluate(r4);
+// executor.execute(x);
+//
+// Operation d1 = new DeleteFileOperation("Y");
+// x = evaluator.evaluate(d1);
+// executor.execute(x);
+//
+// Operation d2 = new DeleteFileOperation("B");
+// x = evaluator.evaluate(d2);
+// executor.execute(x);
+//
+// }
+//
+// /**
+// * Two shuffles inter-twined
+// */
+// public void testViShuffle()
+// {
+// logger.debug("test vi shuffle ");
+// RuleEvaluatorImpl evaluator = new RuleEvaluatorImpl();
+//
+// CommandExecutorImpl executor = new CommandExecutorImpl();
+//
+// ScenarioCreateShuffle shuffle1 = new ScenarioCreateShuffle();
+// shuffle1.setPattern("~WRD.*.TMP");
+//
+// // Search for files ending with tilda.
+// ScenarioRenameShuffle shuffle2 = new ScenarioRenameShuffle();
+// shuffle2.setPattern(".*~$");
+//
+// ScenarioSimpleNonBuffered bogStandard = new ScenarioSimpleNonBuffered();
+// ArrayList scenarios = new ArrayList();
+//
+// scenarios.add(bogStandard);
+// scenarios.add(shuffle1);
+// scenarios.add(shuffle2);
+//
+// evaluator.setScenarios(scenarios);
+//
+// Operation r1 = new RenameFileOperation("X", "X~");
+// Command ex = evaluator.evaluate(r1);
+// //ex.execute(r1);
+//
+// Operation c = new CreateFileOperation("X");
+// ex = evaluator.evaluate(c);
+// //ex.execute(c);
+//
+// }
+//
+//
+// /**
+// * A negative tests
+// */
+// public void testAntiPattern()
+// {
+// logger.debug("test Anti-Pattern start");
+// RuleEvaluatorImpl evaluator = new RuleEvaluatorImpl();
+//
+// CommandExecutorImpl executor = new CommandExecutorImpl();
+//
+// ScenarioCreateShuffle shuffle1 = new ScenarioCreateShuffle();
+// shuffle1.setPattern("~WRD.*.TMP");
+// ScenarioSimpleNonBuffered bogStandard = new ScenarioSimpleNonBuffered();
+// ArrayList scenarios = new ArrayList();
+//
+// scenarios.add(bogStandard);
+// scenarios.add(shuffle1);
+//
+// evaluator.setScenarios(scenarios);
+//
+// /**
+// * Bog standard create
+// */
+// Operation c = new CreateFileOperation("MER.TXT");
+// Command ex = evaluator.evaluate(c);
+// //ex.execute(c);
+//
+// /**
+// * A simple shuffle
+// */
+// Operation c2 = new CreateFileOperation("~WRD0001.TMP");
+// ex = evaluator.evaluate(c2);
+// //ex.execute(c2);
+//
+// Operation d0 = new DeleteFileOperation("~WRD0001.TMP");
+// ex = evaluator.evaluate(d0);
+// //ex.execute(d0);
+//
+// Operation r1 = new RenameFileOperation("X", "Y");
+// ex = evaluator.evaluate(r1);
+// //ex.execute(r1);
+//
+// Operation r2 = new RenameFileOperation("~WRD0001.TMP", "X");
+// ex = evaluator.evaluate(r2);
+// //ex.execute(r2);
+//
+// Operation d1 = new DeleteFileOperation("Y");
+// ex = evaluator.evaluate(d1);
+// //ex.execute(d1);
+// }
+//
+//
+// /**
+// * A multi threaded shuffle
+// */
+// public void testMultiThreadShuffle() throws Exception
+// {
+//
+// logger.debug("test multi thread shuffle start");
+// int MAX_THREADS=10;
+//
+// CommandExecutorImpl executor = new CommandExecutorImpl();
+//
+// RuleEvaluatorImpl evaluator = new RuleEvaluatorImpl();
+//
+// ScenarioCreateShuffle shuffle1 = new ScenarioCreateShuffle();
+// shuffle1.setPattern("~WRD.*.TMP");
+// ScenarioSimpleNonBuffered bogStandard = new ScenarioSimpleNonBuffered();
+// ArrayList scenarios = new ArrayList();
+//
+// scenarios.add(bogStandard);
+// scenarios.add(shuffle1);
+//
+// evaluator.setScenarios(scenarios);
+//
+// class TestThread extends Thread
+// {
+// String name;
+// RuleEvaluator evaluator;
+// Exception exception;
+//
+// public TestThread( RuleEvaluator evaluator, String name)
+// {
+// this.name = name;
+// this.evaluator = evaluator;
+// }
+//
+// public void run()
+// {
+// String targetName = name;
+// String createName = "~WRD" + name + ".TMP";
+// String deleteName = name;
+// try
+// {
+// /**
+// * Bog standard create
+// */
+// Operation c = new CreateFileOperation("MER.TXT");
+// Command ex = evaluator.evaluate(c);
+// //ex.execute(c);
+// System.out.println("DONE: " + name);
+//
+// /**
+// * A simple shuffle
+// */
+// Operation c2 = new CreateFileOperation(createName);
+// ex = evaluator.evaluate(c2);
+// //ex.execute(c2);
+//
+// Operation r1 = new RenameFileOperation(targetName, deleteName);
+// ex = evaluator.evaluate(r1);
+// //ex.execute(r1);
+//
+// Operation r2 = new RenameFileOperation(createName, targetName);
+// ex = evaluator.evaluate(r2);
+// //ex.execute(r2);
+//
+// Operation d1 = new DeleteFileOperation(deleteName);
+// ex = evaluator.evaluate(d1);
+// //ex.execute(d1);
+// }
+// catch (Exception e)
+// {
+// exception = e;
+// }
+//
+// }
+// }
+//
+// ArrayListthreads = new ArrayList();
+//
+// for(int i = 0; i < MAX_THREADS; i++)
+// {
+// TestThread t = new TestThread(evaluator, "~WRD" + i + ".TMP");
+// threads.add(t);
+// t.start();
+// }
+//
+// for(TestThread thread : threads)
+// {
+// try
+// {
+// thread.join();
+// if(thread.exception != null)
+// {
+// throw thread.exception;
+// }
+// }
+// catch (InterruptedException e)
+// {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// }
+// }
+//}
diff --git a/source/java/org/alfresco/filesys/repo/rules/VirtualFilesystem.java b/source/java/org/alfresco/filesys/repo/rules/VirtualFilesystem.java
new file mode 100644
index 0000000000..b7f19f7f01
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/VirtualFilesystem.java
@@ -0,0 +1,24 @@
+/*
+ * 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.rules;
+
+public class VirtualFilesystem
+{
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/CallbackCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/CallbackCommand.java
new file mode 100644
index 0000000000..98fdbe1649
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/CallbackCommand.java
@@ -0,0 +1,40 @@
+/*
+ * 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.rules.commands;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+
+/**
+ * Callback command.
+ *
+ * Makes a callback when executed.
+ */
+public class CallbackCommand implements Command
+{
+ public CallbackCommand()
+ {
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_NONE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/CloseFileCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/CloseFileCommand.java
new file mode 100644
index 0000000000..81e9922a00
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/CloseFileCommand.java
@@ -0,0 +1,70 @@
+/*
+ * 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.rules.commands;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Open File command
+ */
+public class CloseFileCommand implements Command
+{
+ private String name;
+ private String path;
+ private NodeRef rootNode;
+
+ private NetworkFile networkFile;
+
+ public CloseFileCommand(String name, NetworkFile networkFile, NodeRef rootNode, String path)
+ {
+ this.name = name;
+ this.networkFile = networkFile;
+ this.rootNode = rootNode;
+ this.path = path;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public NetworkFile getNetworkFile()
+ {
+ return networkFile;
+ }
+
+ public NodeRef getRootNodeRef()
+ {
+ return rootNode;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_READ_WRITE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/CompoundCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/CompoundCommand.java
new file mode 100644
index 0000000000..83e69a68a3
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/CompoundCommand.java
@@ -0,0 +1,98 @@
+/*
+ * 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.rules.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+
+/**
+ * A compound operation contains one or more other commands.
+ */
+public class CompoundCommand implements Command
+{
+ List commands;
+
+ List postCommitCommands;
+
+ List postErrorCommands;
+
+ /**
+ * New Compound Command containing the specified commands.
+ * @param commands
+ */
+ public CompoundCommand(List commands)
+ {
+ this.commands = new ArrayList(commands);
+ }
+
+ /**
+ * New Compound Command containing the specified commands.
+ * @param commands
+ */
+ public CompoundCommand(List commands, List postCommitCommands)
+ {
+ this.commands = new ArrayList(commands);
+
+ this.postCommitCommands = new ArrayList(postCommitCommands);
+ }
+
+ public CompoundCommand(List commands, List postCommitCommands, ListpostErrorCommands)
+ {
+ this.commands = new ArrayList(commands);
+
+ this.postCommitCommands = new ArrayList(postCommitCommands);
+
+ this.postErrorCommands = new ArrayList(postErrorCommands);
+ }
+
+ public List getCommands()
+ {
+ return commands;
+ }
+
+ public List getPostCommitCommands()
+ {
+ return postCommitCommands;
+ }
+
+ public List getPostErrorCommands()
+ {
+ return postErrorCommands;
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ TxnReadState readState = TxnReadState.TXN_NONE;
+ for(Command command : commands)
+ {
+ TxnReadState x = command.getTransactionRequired();
+
+ if(x != null && x.compareTo(readState) > 0)
+ {
+ readState = x;
+ }
+ }
+
+ return readState;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/CopyContentCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/CopyContentCommand.java
new file mode 100644
index 0000000000..ec521fcc7d
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/CopyContentCommand.java
@@ -0,0 +1,81 @@
+/*
+ * 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.rules.commands;
+
+import java.util.List;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * CopyContent command
+ *
+ * Copy just the content from one node to another
+ *
+ */
+public class CopyContentCommand implements Command
+{
+
+ private String from;
+ private String to;
+ private NodeRef rootNode;
+ private String fromPath;
+ private String toPath;
+
+ public CopyContentCommand(String from, String to, NodeRef rootNode, String fromPath, String toPath)
+ {
+ this.from = from;
+ this.to = to;
+ this.rootNode = rootNode;
+ this.fromPath = fromPath;
+ this.toPath = toPath;
+ }
+
+ public String getTo()
+ {
+ return from;
+ }
+
+ public String getFrom()
+ {
+ return from;
+ }
+
+ public NodeRef getRootNode()
+ {
+ return rootNode;
+ }
+
+ public String getFromPath()
+ {
+ return fromPath;
+ }
+
+ public String getToPath()
+ {
+ return toPath;
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_READ_WRITE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/CreateFileCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/CreateFileCommand.java
new file mode 100644
index 0000000000..214446e6c9
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/CreateFileCommand.java
@@ -0,0 +1,65 @@
+/*
+ * 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.rules.commands;
+
+import java.util.List;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * CreateFile command
+ */
+public class CreateFileCommand implements Command
+{
+ private String name;
+ private NodeRef rootNode;
+ private String path;
+
+ public CreateFileCommand(String name, NodeRef rootNode, String path)
+ {
+ this.name = name;
+ this.path = path;
+ this.rootNode = rootNode;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public NodeRef getRootNode()
+ {
+ return rootNode;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_READ_WRITE;
+ }
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/DeleteFileCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/DeleteFileCommand.java
new file mode 100644
index 0000000000..8d2bbadda7
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/DeleteFileCommand.java
@@ -0,0 +1,61 @@
+/*
+ * 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.rules.commands;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Delete File command
+ */
+public class DeleteFileCommand implements Command
+{
+ private String name;
+ private NodeRef rootNode;
+ private String path;
+
+ public DeleteFileCommand(String name, NodeRef rootNode, String path)
+ {
+ this.name = name;
+ this.rootNode = rootNode;
+ this.path = path;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public NodeRef getRootNode()
+ {
+ return rootNode;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_READ_WRITE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/DoNothingCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/DoNothingCommand.java
new file mode 100644
index 0000000000..9364c48873
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/DoNothingCommand.java
@@ -0,0 +1,40 @@
+/*
+ * 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.rules.commands;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Do Nothing Command, Does what it says on the tin!
+ */
+public class DoNothingCommand implements Command
+{
+
+ public DoNothingCommand()
+ {
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_NONE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/MoveFileCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/MoveFileCommand.java
new file mode 100644
index 0000000000..64ab21ff53
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/MoveFileCommand.java
@@ -0,0 +1,58 @@
+/*
+ * 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.rules.commands;
+
+import java.util.List;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+
+/**
+ * Rename command
+ */
+public class MoveFileCommand implements Command
+{
+
+ private String from;
+ private String to;
+
+ public MoveFileCommand(String from, String to)
+ {
+ this.from = from;
+ this.to = to;
+ }
+
+
+ public String getFrom()
+ {
+ return from;
+ }
+
+ public String getTo()
+ {
+ return to;
+ }
+
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_READ_WRITE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/OpenFileCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/OpenFileCommand.java
new file mode 100644
index 0000000000..bca7cac376
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/OpenFileCommand.java
@@ -0,0 +1,85 @@
+/*
+ * 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.rules.commands;
+
+import java.util.List;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Open File command
+ */
+public class OpenFileCommand implements Command
+{
+ private String name;
+ private boolean writeAccess = false;
+ private boolean truncate = false;
+ private String path;
+ private NodeRef rootNode;
+
+ /**
+ *
+ * @param name
+ * @param writeAccess
+ * @param rootNode
+ * @param truncate
+ * @param path
+ */
+ public OpenFileCommand(String name, boolean writeAccess, boolean truncate, NodeRef rootNode, String path)
+ {
+ this.name = name;
+ this.writeAccess = writeAccess;
+ this.truncate = truncate;
+ this.rootNode = rootNode;
+ this.path = path;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ public NodeRef getRootNodeRef()
+ {
+ return rootNode;
+ }
+
+ public boolean isWriteAccess()
+ {
+ return writeAccess;
+ }
+
+ public boolean isTruncate()
+ {
+ return truncate;
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_READ_ONLY;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/PostCommitCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/PostCommitCommand.java
new file mode 100644
index 0000000000..5abe4f5b9a
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/PostCommitCommand.java
@@ -0,0 +1,15 @@
+package org.alfresco.filesys.repo.rules.commands;
+
+import java.util.List;
+
+import org.alfresco.filesys.repo.rules.Command;
+
+/**
+ * The post commit command is executed after a successful completion of a command.
+ * @author mrogers
+ *
+ */
+public class PostCommitCommand
+{
+ List commands;
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/ReduceQuotaCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/ReduceQuotaCommand.java
new file mode 100644
index 0000000000..f7a7432497
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/ReduceQuotaCommand.java
@@ -0,0 +1,68 @@
+/*
+ * 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.rules.commands;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Open File command
+ */
+public class ReduceQuotaCommand implements Command
+{
+ private String name;
+ private String path;
+ private NodeRef rootNode;
+
+ private NetworkFile networkFile;
+
+ public ReduceQuotaCommand(String name, NetworkFile networkFile, NodeRef rootNode, String path)
+ {
+ this.name = name;
+ this.networkFile = networkFile;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public NetworkFile getNetworkFile()
+ {
+ return networkFile;
+ }
+
+ public NodeRef getRootNodeRef()
+ {
+ return rootNode;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_NONE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/RemoveNoContentFileOnError.java b/source/java/org/alfresco/filesys/repo/rules/commands/RemoveNoContentFileOnError.java
new file mode 100644
index 0000000000..e9d4755fb5
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/RemoveNoContentFileOnError.java
@@ -0,0 +1,67 @@
+/*
+ * 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.rules.commands;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Remove file with CONTENT_NO_ASPECT on error
+ */
+public class RemoveNoContentFileOnError implements Command
+{
+ private String name;
+ private String path;
+ private NodeRef rootNode;
+
+ private NetworkFile networkFile;
+
+ public RemoveNoContentFileOnError(String name, NodeRef rootNode, String path)
+ {
+ this.name = name;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public NetworkFile getNetworkFile()
+ {
+ return networkFile;
+ }
+
+ public NodeRef getRootNodeRef()
+ {
+ return rootNode;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_NONE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/RemoveTempFileCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/RemoveTempFileCommand.java
new file mode 100644
index 0000000000..d15e7975a7
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/RemoveTempFileCommand.java
@@ -0,0 +1,50 @@
+/*
+ * 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.rules.commands;
+
+import org.alfresco.filesys.repo.TempNetworkFile;
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Remove a temporary file
+ */
+public class RemoveTempFileCommand implements Command
+{
+
+ private TempNetworkFile networkFile;
+
+ public RemoveTempFileCommand(TempNetworkFile file)
+ {
+ this.networkFile = file;
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_NONE;
+ }
+
+ public TempNetworkFile getNetworkFile()
+ {
+ return networkFile;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/RenameFileCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/RenameFileCommand.java
new file mode 100644
index 0000000000..3b41680418
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/RenameFileCommand.java
@@ -0,0 +1,99 @@
+/*
+ * 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.rules.commands;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Rename command
+ */
+public class RenameFileCommand implements Command
+{
+
+ private String from;
+ private String to;
+ private NodeRef rootNode;
+ private String fromPath;
+ private String toPath;
+
+ public RenameFileCommand(String from, String to, NodeRef rootNode, String fromPath, String toPath)
+ {
+ this.from = from;
+ this.to = to;
+ this.rootNode = rootNode;
+ this.fromPath = fromPath;
+ this.toPath = toPath;
+ }
+
+
+ public String getFrom()
+ {
+ return from;
+ }
+
+ public String getTo()
+ {
+ return to;
+ }
+
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_READ_WRITE;
+ }
+
+
+ public void setRootNode(NodeRef rootNode)
+ {
+ this.rootNode = rootNode;
+ }
+
+
+ public NodeRef getRootNode()
+ {
+ return rootNode;
+ }
+
+
+ public void setFromPath(String fromPath)
+ {
+ this.fromPath = fromPath;
+ }
+
+
+ public String getFromPath()
+ {
+ return fromPath;
+ }
+
+
+ public void setToPath(String toPath)
+ {
+ this.toPath = toPath;
+ }
+
+
+ public String getToPath()
+ {
+ return toPath;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/ReturnValueCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/ReturnValueCommand.java
new file mode 100644
index 0000000000..480eb8014a
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/ReturnValueCommand.java
@@ -0,0 +1,29 @@
+package org.alfresco.filesys.repo.rules.commands;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+
+/**
+ * Return a specified value to the executor
+ */
+public class ReturnValueCommand implements Command
+{
+
+ Object returnValue;
+ public ReturnValueCommand(Object returnValue)
+ {
+ this.returnValue = returnValue;
+ }
+
+ public Object getReturnValue()
+ {
+ return returnValue;
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+
+ return TxnReadState.TXN_NONE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/UpdateQuotaCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/UpdateQuotaCommand.java
new file mode 100644
index 0000000000..368ad72ea5
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/commands/UpdateQuotaCommand.java
@@ -0,0 +1,40 @@
+/*
+ * 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.rules.commands;
+
+import org.alfresco.filesys.repo.rules.Command;
+import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Update the user's quota.
+ */
+public class UpdateQuotaCommand implements Command
+{
+
+ public UpdateQuotaCommand()
+ {
+ }
+
+ @Override
+ public TxnReadState getTransactionRequired()
+ {
+ return TxnReadState.TXN_NONE;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/operations/CloseFileOperation.java b/source/java/org/alfresco/filesys/repo/rules/operations/CloseFileOperation.java
new file mode 100644
index 0000000000..8af24bd937
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/operations/CloseFileOperation.java
@@ -0,0 +1,95 @@
+/*
+ * 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.rules.operations;
+
+import org.alfresco.filesys.repo.rules.Operation;
+import org.alfresco.jlan.server.filesys.NetworkFile;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Close File Operation.
+ *
+ * Close a file with the given name.
+ */
+public class CloseFileOperation implements Operation
+{
+ private String name;
+ private NodeRef rootNodeRef;
+ private String path;
+
+ private NetworkFile networkFile;
+ boolean deleteOnClose;
+
+ public CloseFileOperation(String name, NetworkFile networkFile, NodeRef rootNodeRef, String path, boolean deleteOnClose)
+ {
+ this.name = name;
+ this.networkFile = networkFile;
+ this.rootNodeRef = rootNodeRef;
+ this.path = path;
+ this.deleteOnClose = deleteOnClose;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public NodeRef getRootNodeRef()
+ {
+ return rootNodeRef;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ public NetworkFile getNetworkFile()
+ {
+ return networkFile;
+ }
+
+ public String toString()
+ {
+ return "CloseFileOperation: " + name;
+ }
+
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+
+ public boolean isDeleteOnClose()
+ {
+ return deleteOnClose;
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof CloseFileOperation)
+ {
+ CloseFileOperation c = (CloseFileOperation)o;
+ if(name.equals(c.getName()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/operations/CreateFileOperation.java b/source/java/org/alfresco/filesys/repo/rules/operations/CreateFileOperation.java
new file mode 100644
index 0000000000..a76804c2e7
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/operations/CreateFileOperation.java
@@ -0,0 +1,79 @@
+/*
+ * 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.rules.operations;
+
+import org.alfresco.filesys.repo.rules.Operation;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Create File Operation.
+ *
+ * Create a file with the given name.
+ */
+public class CreateFileOperation implements Operation
+{
+ private String name;
+ private NodeRef rootNodeRef;
+ private String path;
+
+ public CreateFileOperation(String name, NodeRef rootNodeRef, String path)
+ {
+ this.name = name;
+ this.rootNodeRef = rootNodeRef;
+ this.path = path;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String toString()
+ {
+ return "CreateFileOperation: " + name;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ public NodeRef getRootNodeRef()
+ {
+ return rootNodeRef;
+ }
+
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof CreateFileOperation)
+ {
+ CreateFileOperation c = (CreateFileOperation)o;
+ if(name.equals(c.getName()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/operations/DeleteFileOperation.java b/source/java/org/alfresco/filesys/repo/rules/operations/DeleteFileOperation.java
new file mode 100644
index 0000000000..5aa187fa98
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/operations/DeleteFileOperation.java
@@ -0,0 +1,76 @@
+/*
+ * 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.rules.operations;
+
+import org.alfresco.filesys.repo.rules.Operation;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+public class DeleteFileOperation implements Operation
+{
+
+ private String name;
+ private NodeRef rootNodeRef;
+ private String path;
+
+ public DeleteFileOperation(String name, NodeRef rootNodeRef, String path)
+ {
+ this.name = name;
+ this.rootNodeRef = rootNodeRef;
+ this.path = path;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ public NodeRef getRootNodeRef()
+ {
+ return rootNodeRef;
+ }
+
+ public String toString()
+ {
+ return "DeleteFileOperation: " + name;
+ }
+
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof DeleteFileOperation)
+ {
+ DeleteFileOperation c = (DeleteFileOperation)o;
+ if(name.equals(c.getName()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/operations/MoveFileOperation.java b/source/java/org/alfresco/filesys/repo/rules/operations/MoveFileOperation.java
new file mode 100644
index 0000000000..55da175e80
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/operations/MoveFileOperation.java
@@ -0,0 +1,70 @@
+/*
+ * 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.rules.operations;
+
+import org.alfresco.filesys.repo.rules.Operation;
+
+public class MoveFileOperation implements Operation
+{
+ private String from;
+ private String to;
+
+ public MoveFileOperation(String from, String to)
+ {
+ this.from = from;
+ this.to = to;
+ }
+
+
+ public String getFrom()
+ {
+ return from;
+ }
+
+ public String getTo()
+ {
+ return to;
+ }
+
+ public String toString()
+ {
+ return "MoveFileOperation: from " + from + " to "+ to;
+ }
+
+ public int hashCode()
+ {
+ return from.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof MoveFileOperation)
+ {
+ MoveFileOperation r = (MoveFileOperation)o;
+ if(from.equals(r.getFrom()) && to.equals(r.getTo()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/operations/OpenFileOperation.java b/source/java/org/alfresco/filesys/repo/rules/operations/OpenFileOperation.java
new file mode 100644
index 0000000000..e7897e1930
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/operations/OpenFileOperation.java
@@ -0,0 +1,100 @@
+/*
+ * 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.rules.operations;
+
+import org.alfresco.filesys.repo.rules.Operation;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Create File Operation.
+ *
+ * Create a file with the given name.
+ */
+public class OpenFileOperation implements Operation
+{
+ private String name;
+ private boolean writeAccess = false;
+ private boolean truncate = false;
+ private String path;
+ private NodeRef rootNode;
+
+ /**
+ *
+ * @param name the name of the file to open
+ * @param writeAccess if true open the file in read/write
+ * @param rootNode
+ * @param path the full path/name to open
+ */
+ public OpenFileOperation(String name, boolean writeAccess, boolean truncate, NodeRef rootNode, String path)
+ {
+ this.name = name;
+ this.rootNode = rootNode;
+ this.truncate = truncate;
+ this.path = path;
+ this.writeAccess = writeAccess;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getPath()
+ {
+ return path;
+ }
+
+ public NodeRef getRootNodeRef()
+ {
+ return rootNode;
+ }
+
+ public boolean isWriteAccess()
+ {
+ return writeAccess;
+ }
+
+ public boolean isTruncate()
+ {
+ return writeAccess;
+ }
+
+ public String toString()
+ {
+ return "OpenFileOperation: " + name;
+ }
+
+ public int hashCode()
+ {
+ return name.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof OpenFileOperation)
+ {
+ OpenFileOperation c = (OpenFileOperation)o;
+ if(name.equals(c.getName()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/operations/RenameFileOperation.java b/source/java/org/alfresco/filesys/repo/rules/operations/RenameFileOperation.java
new file mode 100644
index 0000000000..b7fbf39976
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/operations/RenameFileOperation.java
@@ -0,0 +1,103 @@
+/*
+ * 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.rules.operations;
+
+import org.alfresco.filesys.repo.rules.Operation;
+import org.alfresco.service.cmr.repository.NodeRef;
+
+/**
+ * Rename a file within the same directory
+ */
+public class RenameFileOperation implements Operation
+{
+ private String from;
+ private String to;
+ private String fromPath;
+ private String toPath;
+ NodeRef rootNodeRef;
+
+ /**
+ *
+ * @param from name of file from
+ * @param to name of file to
+ * @param fromPath full path of from
+ * @param toPath full path of to
+ * @param rootNodeRef
+ */
+ public RenameFileOperation(String from, String to, String fromPath, String toPath, NodeRef rootNodeRef)
+ {
+ this.from = from;
+ this.to = to;
+ this.fromPath = fromPath;
+ this.toPath = toPath;
+ this.rootNodeRef = rootNodeRef;
+ }
+
+
+ public String getFrom()
+ {
+ return from;
+ }
+
+ public String getTo()
+ {
+ return to;
+ }
+
+ public String getToPath()
+ {
+ return toPath;
+ }
+
+ public String getFromPath()
+ {
+ return fromPath;
+ }
+
+ public NodeRef getRootNodeRef()
+ {
+ return rootNodeRef;
+ }
+
+ public String toString()
+ {
+ return "RenameFileOperation: from " + from + " to "+ to;
+ }
+
+ public int hashCode()
+ {
+ return from.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof RenameFileOperation)
+ {
+ RenameFileOperation r = (RenameFileOperation)o;
+ if(from.equals(r.getFrom()) && to.equals(r.getTo()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
+}
diff --git a/source/java/org/alfresco/filesys/repo/rules/package-info.java b/source/java/org/alfresco/filesys/repo/rules/package-info.java
new file mode 100644
index 0000000000..b478e12ce0
--- /dev/null
+++ b/source/java/org/alfresco/filesys/repo/rules/package-info.java
@@ -0,0 +1,34 @@
+/*
+ * 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 .
+ */
+/**
+ * Filesystem Rule Evaluator to support Scenarios.
+ *
+ * Low level operations (create, update, delete etc) are passed into the RuleEvaluator.
+ *
+ * The RuleEvaluator is configured with a list of Scenarios which process the operations as and
+ * when their patterns match.
+ * The RuleEvaluator evaluates the stream of operations and returns commands to execute.
+ *
+ * The Command Executor executes the commands returned from the RuleEvaluator.
+ *
+ * Each Scenario is a Factory for A ScenarioInstance. The RuleEvaluator contains a set of active scenario instances.
+ *
+ * @since 4.0
+ */
+package org.alfresco.filesys.repo.rules;
diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
index 80bfa7789a..c68b9cee7a 100644
--- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
+++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java
@@ -778,6 +778,11 @@ public class FileFolderServiceImpl implements FileFolderService
*/
private List listSimpleDeep(NodeRef contextNodeRef, boolean files, boolean folders, SubFolderFilter folderFilter)
{
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("searchSimpleDeep contextNodeRef:" + contextNodeRef);
+ }
+
// To hold the results.
List result = new ArrayList();
@@ -843,6 +848,12 @@ public class FileFolderServiceImpl implements FileFolderService
}
}
+
+ if(logger.isDebugEnabled())
+ {
+ logger.debug("searchSimpleDeep finished size:" + result.size());
+ }
+
// Done
return result;
}
diff --git a/source/java/org/alfresco/service/cmr/model/FileFolderUtil.java b/source/java/org/alfresco/service/cmr/model/FileFolderUtil.java
index ddff3ad7b0..3b65cebe25 100644
--- a/source/java/org/alfresco/service/cmr/model/FileFolderUtil.java
+++ b/source/java/org/alfresco/service/cmr/model/FileFolderUtil.java
@@ -32,22 +32,7 @@ import org.alfresco.service.namespace.QName;
*/
public class FileFolderUtil
{
-
-// /**
-// * Utility method for deep listings of files/folders
-// *
-// * This method walks down a tree of file/folders and for each file or folder it encounters
-// *
-// * @param contextNodeRef the starting point for our deep listing.
-// * @param filter
-// * @return
-// */
-// public List listDeep(NodeRef contextNodeRef, FileFolderFilter filter)
-// {
-// }
-
-
-
+
/**
* Checks for the presence of, and creates as necessary, the folder
* structure in the provided path.