diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 81f21dbff7..23fdf8fe13 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -219,6 +219,9 @@ classpath*:alfresco/module/*/log4j.properties + + classpath*:alfresco/enterprise/*-log4j.properties + classpath*:alfresco/extension/*-log4j.properties diff --git a/config/alfresco/network-protocol-context.xml b/config/alfresco/network-protocol-context.xml index c00e5e7271..cc5fc764ef 100644 --- a/config/alfresco/network-protocol-context.xml +++ b/config/alfresco/network-protocol-context.xml @@ -65,6 +65,7 @@ + diff --git a/source/java/org/alfresco/filesys/ServerConfigurationBean.java b/source/java/org/alfresco/filesys/ServerConfigurationBean.java index 6db8a61d6c..e48eeb9cf0 100644 --- a/source/java/org/alfresco/filesys/ServerConfigurationBean.java +++ b/source/java/org/alfresco/filesys/ServerConfigurationBean.java @@ -1924,6 +1924,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean { // Create the shared filesystem filesys = new DiskSharedDevice(filesysName, filesysDriver, filesysContext); + filesys.setConfiguration( this); // Check if the filesystem uses the file state cache, if so then add to the file state reaper @@ -1987,6 +1988,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean { // Create the shared filesystem filesys = new DiskSharedDevice(filesysName, filesysDriver, filesysContext); + filesys.setConfiguration( this); // Attach desktop actions to the filesystem @@ -2071,7 +2073,10 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean { // Create the shared filesystem - fsysConfig.addShare( new DiskSharedDevice( storeName, avmDriver, avmContext)); + DiskSharedDevice filesys = new DiskSharedDevice( storeName, avmDriver, avmContext); + filesys.setConfiguration( this); + + fsysConfig.addShare( filesys); // DEBUG diff --git a/source/java/org/alfresco/filesys/alfresco/AlfrescoDiskDriver.java b/source/java/org/alfresco/filesys/alfresco/AlfrescoDiskDriver.java index 1903e38420..2965e45f52 100644 --- a/source/java/org/alfresco/filesys/alfresco/AlfrescoDiskDriver.java +++ b/source/java/org/alfresco/filesys/alfresco/AlfrescoDiskDriver.java @@ -18,6 +18,7 @@ package org.alfresco.filesys.alfresco; +import java.io.IOException; import java.util.concurrent.Callable; import javax.transaction.Status; @@ -163,8 +164,10 @@ public abstract class AlfrescoDiskDriver implements IOCtlInterface, Transactiona * @param callback * callback for the retryable operation * @return the result of the operation + * @throws Exception */ - public T doInWriteTransaction(SrvSession sess, final Callable callback) + public T doInWriteTransaction(SrvSession sess, final CallableIO callback) + throws IOException { Boolean wasInRetryingTransaction = m_inRetryingTransaction.get(); try @@ -178,10 +181,17 @@ public abstract class AlfrescoDiskDriver implements IOCtlInterface, Transactiona T result = m_transactionService.getRetryingTransactionHelper().doInTransaction( new RetryingTransactionHelper.RetryingTransactionCallback() { - public T execute() throws Throwable { - return callback.call(); + try + { + return callback.call(); + } + catch (IOException e) + { + // Ensure original checked IOExceptions get propagated + throw new PropagatingException(e); + } } }); if (hadTransaction) @@ -190,6 +200,11 @@ public abstract class AlfrescoDiskDriver implements IOCtlInterface, Transactiona } return result; } + catch (PropagatingException e) + { + // Unwrap checked exceptions + throw (IOException) e.getCause(); + } finally { m_inRetryingTransaction.set(wasInRetryingTransaction); @@ -390,4 +405,30 @@ public abstract class AlfrescoDiskDriver implements IOCtlInterface, Transactiona ((AlfrescoContext) ctx).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/AlfrescoNetworkFile.java b/source/java/org/alfresco/filesys/alfresco/AlfrescoNetworkFile.java index 65aa86f9fe..46475f8294 100644 --- a/source/java/org/alfresco/filesys/alfresco/AlfrescoNetworkFile.java +++ b/source/java/org/alfresco/filesys/alfresco/AlfrescoNetworkFile.java @@ -49,7 +49,7 @@ public abstract class AlfrescoNetworkFile extends NetworkFile implements Network * * @return FileState */ - public final FileState getFileState() + public FileState getFileState() { return m_state; } diff --git a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java index ed76eff069..1b3738c629 100644 --- a/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java +++ b/source/java/org/alfresco/filesys/avm/AVMDiskDriver.java @@ -24,11 +24,9 @@ import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.StringTokenizer; -import java.util.concurrent.Callable; import javax.transaction.UserTransaction; -import org.springframework.extensions.config.ConfigElement; import org.alfresco.filesys.alfresco.AlfrescoDiskDriver; import org.alfresco.jlan.server.SrvSession; import org.alfresco.jlan.server.auth.ClientInfo; @@ -80,6 +78,7 @@ import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.wcm.sandbox.SandboxConstants; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.config.ConfigElement; /** * AVM Repository Filesystem Driver Class @@ -825,9 +824,9 @@ public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface if ( logger.isDebugEnabled()) logger.debug("Close file " + file.getFullName()); - doInWriteTransaction(sess, new Callable(){ + doInWriteTransaction(sess, new CallableIO(){ - public Void call() throws Exception + public Void call() throws IOException { // Close the file @@ -896,9 +895,9 @@ public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface try { - doInWriteTransaction(sess, new Callable(){ + doInWriteTransaction(sess, new CallableIO(){ - public Void call() throws Exception + public Void call() throws IOException { // Create the new file entry @@ -984,9 +983,9 @@ public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface try { // Create a new file - return doInWriteTransaction(sess, new Callable(){ + return doInWriteTransaction(sess, new CallableIO(){ - public NetworkFile call() throws Exception + public NetworkFile call() throws IOException { // Create the new file entry @@ -1080,9 +1079,9 @@ public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface try { - doInWriteTransaction(sess, new Callable(){ + doInWriteTransaction(sess, new CallableIO(){ - public Void call() throws Exception + public Void call() throws IOException { AVMNodeDescriptor nodeDesc = m_avmService.lookup(storePath.getVersion(), storePath.getAVMPath()); if (nodeDesc != null) @@ -1159,9 +1158,9 @@ public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface try { - doInWriteTransaction(sess, new Callable(){ + doInWriteTransaction(sess, new CallableIO(){ - public Void call() throws Exception + public Void call() throws IOException { AVMNodeDescriptor nodeDesc = m_avmService.lookup(storePath.getVersion(), storePath.getAVMPath()); if (nodeDesc != null) @@ -1695,9 +1694,9 @@ public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface try { - doInWriteTransaction(sess, new Callable(){ + doInWriteTransaction(sess, new CallableIO(){ - public Void call() throws Exception + public Void call() throws IOException { // Rename the file/folder @@ -2004,9 +2003,9 @@ public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface // Truncate or extend the file if (avmFile.hasContentChannel() == false || avmFile.isWritable() == false) { - doInWriteTransaction(sess, new Callable(){ + doInWriteTransaction(sess, new CallableIO(){ - public Void call() throws Exception + public Void call() throws IOException { file.truncateFile(siz); file.flushFile(); @@ -2058,9 +2057,9 @@ public class AVMDiskDriver extends AlfrescoDiskDriver implements DiskInterface // Write the data to the file if (avmFile.hasContentChannel() == false || avmFile.isWritable() == false) { - doInWriteTransaction(sess, new Callable(){ + doInWriteTransaction(sess, new CallableIO(){ - public Void call() throws Exception + public Void call() throws IOException { file.writeFile(buf, siz, bufoff, fileoff); return null; diff --git a/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java b/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java index 067cb75601..50cf50e2c0 100644 --- a/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java +++ b/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java @@ -1559,6 +1559,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean // Create the shared filesystem filesys = new DiskSharedDevice(filesystem.getDeviceName(), filesysDriver, (AVMContext)filesystem); + filesys.setConfiguration( this); // Check if the filesystem uses the file state cache, if so then add to the file state reaper @@ -1603,6 +1604,7 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean // Create the shared filesystem filesys = new DiskSharedDevice(filesystem.getDeviceName(), filesysDriver, filesysContext); + filesys.setConfiguration( this); // Add any access controls to the share @@ -1672,7 +1674,10 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean // Create the shared filesystem - fsysConfig.addShare(new DiskSharedDevice(storeName, avmDriver, avmContext)); + DiskSharedDevice filesys = new DiskSharedDevice(storeName, avmDriver, avmContext); + filesys.setConfiguration( this); + + fsysConfig.addShare( filesys); // DEBUG diff --git a/source/java/org/alfresco/filesys/repo/ContentContext.java b/source/java/org/alfresco/filesys/repo/ContentContext.java index a946802437..4cf2c825a2 100644 --- a/source/java/org/alfresco/filesys/repo/ContentContext.java +++ b/source/java/org/alfresco/filesys/repo/ContentContext.java @@ -23,12 +23,14 @@ import org.alfresco.filesys.alfresco.AlfrescoContext; import org.alfresco.filesys.alfresco.AlfrescoDiskDriver; import org.alfresco.filesys.alfresco.IOControlHandler; import org.alfresco.filesys.config.acl.AccessControlListBean; +import org.alfresco.jlan.server.config.CoreServerConfigSection; import org.alfresco.jlan.server.core.DeviceContextException; import org.alfresco.jlan.server.filesys.DiskInterface; import org.alfresco.jlan.server.filesys.DiskSharedDevice; import org.alfresco.jlan.server.filesys.FileName; import org.alfresco.jlan.server.filesys.FileSystem; import org.alfresco.jlan.server.filesys.quota.QuotaManagerException; +import org.alfresco.jlan.server.thread.ThreadRequestPool; import org.alfresco.service.cmr.repository.NodeRef; /** @@ -65,6 +67,10 @@ public class ContentContext extends AlfrescoContext private NodeMonitor m_nodeMonitor; + // Thread pool + + private ThreadRequestPool m_threadPool; + /** * Default constructor allowing initialization by container. */ @@ -251,6 +257,15 @@ public class ContentContext extends AlfrescoContext return m_rootNodeRef; } + /** + * Return the thread pool + * + * @return ThreadRequestPool + */ + public final ThreadRequestPool getThreadPool() { + return m_threadPool; + } + /** * Close the filesystem context */ @@ -309,6 +324,12 @@ public class ContentContext extends AlfrescoContext super.startFilesystem(share); + // Find the thread pool via the configuration + + CoreServerConfigSection coreConfig = (CoreServerConfigSection) share.getConfiguration().getConfigSection( CoreServerConfigSection.SectionName); + if ( coreConfig != null) + m_threadPool = coreConfig.getThreadPool(); + // Start the node monitor, if enabled if ( m_nodeMonitor != null) diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java index 4978f601f9..7d352fe7cf 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver.java @@ -73,6 +73,7 @@ import org.alfresco.jlan.util.WildCard; import org.alfresco.model.ContentModel; import org.alfresco.repo.admin.SysAdminParams; 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.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -149,6 +150,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa private AuthenticationService authService; private SysAdminParams sysAdminParams; + private BehaviourFilter policyBehaviourFilter; + // Node monitor factory private NodeMonitorFactory m_nodeMonitorFactory; @@ -271,6 +274,14 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa 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 */ @@ -389,6 +400,16 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa 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; + } + /** * 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 @@ -1595,7 +1616,6 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa nosharing = false; // Check if the caller wants read access, check the sharing mode - // Check if the caller wants write access, check if the sharing mode allows write else if ( params.isReadOnlyAccess() && (fstate.getSharedAccess() & SharingMode.READ) != 0) nosharing = false; @@ -1668,7 +1688,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Check if the file is already opened by this client/process - if ( tree.openFileCount() > 1) { + if ( tree.openFileCount() > 0) { // Search the open file table for this session/virtual circuit @@ -1703,8 +1723,14 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) - logger.debug("Re-use existing file open Path " + params.getPath() + ", PID=" + params.getProcessId()); + logger.debug("Re-use existing file open Path " + params.getPath() + ", PID=" + params.getProcessId() + ", params=" + + ( params.isReadOnlyAccess() ? "ReadOnly" : "Write") + ", file=" + + ( contentFile.getGrantedAccess() == NetworkFile.READONLY ? "ReadOnly" : "Write")); } + else if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Not re-using file path=" + params.getPath() + ", readWrite=" + (params.isReadWriteAccess() ? "true" : "false") + + ", readOnly=" + (params.isReadOnlyAccess() ? "true" : "false") + + ", grantedAccess=" + contentFile.getGrantedAccessAsString()); } } @@ -1716,8 +1742,12 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Create the network file, if we could not match an existing file open - if ( netFile == null) - netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, nodeRef, params); + if ( netFile == null) { + + // Create a new network file for the open request + + netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, nodeRef, params, sess); + } } else { @@ -1861,8 +1891,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa { // Access the repository in a retryable write transaction - Pair result = doInWriteTransaction(sess, new Callable>(){ - public Pair call() throws Exception + Pair result = doInWriteTransaction(sess, new CallableIO>(){ + public Pair call() throws IOException { // Get the device root @@ -1920,7 +1950,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Create the network file - ContentNetworkFile netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, result.getSecond(), params); + ContentNetworkFile netFile = ContentNetworkFile.createFile(nodeService, contentService, mimetypeService, cifsHelper, result.getSecond(), params, sess); // Always allow write access to a newly created file @@ -2037,10 +2067,10 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa try { // Access the repository in a retryable write transaction - Pair result = doInWriteTransaction(sess, new Callable>() + Pair result = doInWriteTransaction(sess, new CallableIO>() { - public Pair call() throws Exception + public Pair call() throws IOException { // get the device root @@ -2166,9 +2196,9 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa try { - NodeRef nodeRef = doInWriteTransaction(sess, new Callable(){ + NodeRef nodeRef = doInWriteTransaction(sess, new CallableIO(){ - public NodeRef call() throws Exception + public NodeRef call() throws IOException { // Get the node for the folder @@ -2286,25 +2316,13 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa final ContentContext ctx = (ContentContext) tree.getContext(); FileState toUpdate = null; + + // Check for a content file if ( file instanceof ContentNetworkFile) { - // Decrement the file open count - - ContentNetworkFile contentFile = (ContentNetworkFile) file; - - if ( contentFile.decrementOpenCount() > 0) { - - // DEBUG - - if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) - logger.debug("Deferred file close, path=" + file.getFullName() + ", openCount=" + contentFile.getOpenCount()); - - // Defer the file close to the last reference - - return; - } - + // Update the file state + if ( ctx.hasStateCache()) { FileState fstate = ctx.getStateCache().findFileState(file.getFullName()); @@ -2324,6 +2342,24 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa } } } + + // Decrement the file open count + + ContentNetworkFile contentFile = (ContentNetworkFile) file; + + if ( contentFile.decrementOpenCount() > 0) { + + // DEBUG + + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Deferred file close, path=" + file.getFullName() + ", openCount=" + contentFile.getOpenCount()); + + // Defer the file close to the last reference + + return; + } + else if ( logger.isDebugEnabled()) + logger.debug("Last reference to file, closing, path=" + file.getFullName() + ", access=" + file.getGrantedAccessAsString() + ", fid=" + file.getProtocolId()); } // Check if there is a quota manager enabled @@ -2347,23 +2383,60 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Perform repository updates in a retryable write transaction final FileState finalFileState = toUpdate; - Pair result = doInWriteTransaction(sess, new Callable>() + Pair result = doInWriteTransaction(sess, new CallableIO>() { - public Pair call() throws Exception + public Pair call() throws IOException { - // Update the modification date on the file/folder node - if (finalFileState != null) - { + // Check if the file is an OpenOffice document and hte truncation flag is set + // + // Note: Check before the timestamp update + + if ( file instanceof OpenOfficeContentNetworkFile) { + OpenOfficeContentNetworkFile ooFile = (OpenOfficeContentNetworkFile) file; + if ( ooFile.truncatedToZeroLength()) { + + // Inhibit versioning for this transaction + + getPolicyFilter().disableBehaviour( ContentModel.ASPECT_VERSIONABLE); + // Debug + + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("OpenOffice file truncation update only, inhibit versioning, " + file.getFullName()); + } + } + + // Update the modification date on the file/folder node + if (finalFileState != null && file instanceof ContentNetworkFile) + { + // Check if the file data has been updated, if not then inhibit versioning for this txn + // so the timestamp update does not generate a new file version + + ContentNetworkFile contentFile = (ContentNetworkFile) file; + if ( contentFile.isModified() == false && + nodeService.hasAspect((NodeRef) finalFileState.getFilesystemObject(), ContentModel.ASPECT_VERSIONABLE)) { + + // Stop a new file version being generated + + getPolicyFilter().disableBehaviour( ContentModel.ASPECT_VERSIONABLE); + + // Debug + + if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) + logger.debug("Timestamp update only, inhibit versioning, " + file.getFullName()); + } + + // Update the modification timestamp + Date modifyDate = new Date(finalFileState.getModifyDateTime()); nodeService.setProperty((NodeRef) finalFileState.getFilesystemObject(), ContentModel.PROP_MODIFIED, modifyDate); // Debug if ( logger.isDebugEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) - logger.debug("Updated modifcation timestamp, " + file.getFullName() + ", modTime=" + modifyDate); + logger.debug("Updated modification timestamp, " + file.getFullName() + ", modTime=" + modifyDate); } - + // Defer to the network file to close the stream and remove the content file.closeFile(); @@ -2396,9 +2469,17 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa } catch ( Exception ex) { + // Propagate retryable errors. Log the rest. if (RetryingTransactionHelper.extractRetryCause(ex) != null) { - throw ex; + if (ex instanceof RuntimeException) + { + throw (RuntimeException)ex; + } + else + { + throw new AlfrescoRuntimeException("Error during delete on close, " + file.getFullName(), ex); + } } if ( logger.isWarnEnabled() && ctx.hasDebug(AlfrescoContext.DBG_FILE)) logger.warn("Error during delete on close, " + file.getFullName(), ex); @@ -2472,8 +2553,13 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // DEBUG - if (logger.isDebugEnabled() && (ctx.hasDebug(AlfrescoContext.DBG_FILE) || ctx.hasDebug(AlfrescoContext.DBG_RENAME))) + if (logger.isDebugEnabled() && (ctx.hasDebug(AlfrescoContext.DBG_FILE) || ctx.hasDebug(AlfrescoContext.DBG_RENAME))) { 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)); + } + } } /** @@ -2497,19 +2583,20 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa final QuotaManager quotaMgr = ctx.getQuotaManager(); // Perform repository updates in a retryable write transaction - Callable postTxn = doInWriteTransaction(sess, new Callable>() + Callable postTxn = doInWriteTransaction(sess, new CallableIO>() { - public Callable call() throws Exception + public Callable call() throws IOException { - // Get the size of the file being deleted - final FileInfo fInfo = quotaMgr == null ? null : getFileInformation(sess, tree, name); - // Get the node and delete it final NodeRef nodeRef = getNodeForPath(tree, name); Callable result = null; if (fileFolderService.exists(nodeRef)) { + // Get the size of the file being deleted + + final FileInfo fInfo = quotaMgr == null ? null : getFileInformation(sess, tree, name); + // Check if the node is versionable final boolean isVersionable = nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE); @@ -2517,6 +2604,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa fileFolderService.delete(nodeRef); // Return the operations to perform when the transaction succeeds + result = new Callable() { @@ -2699,10 +2787,10 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa // Rename or move the file/folder - doInWriteTransaction(sess, new Callable() + doInWriteTransaction(sess, new CallableIO() { - public Void call() throws Exception + public Void call() throws IOException { if (sameFolder == true) cifsHelper.rename(nodeToMoveRef, name); @@ -2727,10 +2815,10 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa final int newExists = fileExists(sess, tree, newName); final FileState newState = ctx.getStateCache().findFileState(newName, true); - List postTxn = doInWriteTransaction(sess, new Callable>() + List postTxn = doInWriteTransaction(sess, new CallableIO>() { - public List call() throws Exception + public List call() throws IOException { List postTxn = new LinkedList(); @@ -3005,9 +3093,9 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa final FileState fstate = getStateForPath(tree, name); - doInWriteTransaction(sess, new Callable>(){ + doInWriteTransaction(sess, new CallableIO>(){ - public Pair call() throws Exception + public Pair call() throws IOException { // Get the file/folder node diff --git a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java index 131c15d201..735182829a 100644 --- a/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java +++ b/source/java/org/alfresco/filesys/repo/ContentNetworkFile.java @@ -29,6 +29,7 @@ import java.nio.charset.Charset; import org.alfresco.error.AlfrescoRuntimeException; import org.springframework.extensions.surf.util.I18NUtil; +import org.alfresco.jlan.server.SrvSession; import org.alfresco.jlan.server.filesys.AccessDeniedException; import org.alfresco.jlan.server.filesys.DiskFullException; import org.alfresco.jlan.server.filesys.FileAttribute; @@ -36,6 +37,7 @@ import org.alfresco.jlan.server.filesys.FileInfo; import org.alfresco.jlan.server.filesys.FileOpenParams; import org.alfresco.jlan.server.filesys.NetworkFile; import org.alfresco.jlan.smb.SeekType; +import org.alfresco.jlan.smb.server.SMBSrvSession; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.AbstractContentReader; import org.alfresco.repo.content.encoding.ContentCharsetFinder; @@ -93,29 +95,27 @@ public class ContentNetworkFile extends NodeRefNetworkFile /** * Helper method to create a {@link NetworkFile network file} given a node reference. */ - public static ContentNetworkFile createFile( - NodeService nodeService, - ContentService contentService, - MimetypeService mimetypeService, - CifsHelper cifsHelper, - NodeRef nodeRef, - FileOpenParams params) + public static ContentNetworkFile createFile( NodeService nodeService, ContentService contentService, MimetypeService mimetypeService, + CifsHelper cifsHelper, NodeRef nodeRef, FileOpenParams params, SrvSession sess) { String path = params.getPath(); - // Check write access - // TODO: Check access writes and compare to write requirements - // Create the file ContentNetworkFile netFile = null; - if ( isMSOfficeSpecialFile(path)) { + if ( isMSOfficeSpecialFile(path, sess, nodeService, nodeRef)) { // Create a file for special processing netFile = new MSOfficeContentNetworkFile( nodeService, contentService, mimetypeService, nodeRef, path); } + else if ( isOpenOfficeSpecialFile( path, sess, nodeService, nodeRef)) { + + // Create a file for special processing + + netFile = new OpenOfficeContentNetworkFile( nodeService, contentService, mimetypeService, nodeRef, path); + } else { // Create a normal content file @@ -172,6 +172,10 @@ public class ContentNetworkFile extends NodeRefNetworkFile netFile.setAttributes(fileInfo.getFileAttributes()); + // Set the owner process id + + netFile.setProcessId( params.getProcessId()); + // If the file is read-only then only allow read access if ( netFile.isReadOnly()) @@ -725,20 +729,62 @@ public class ContentNetworkFile extends NodeRefNetworkFile logger.debug("Flush file=" + this); } + /** + * Return the modified status + * + * @return boolean + */ + public final boolean isModified() { + return modified; + } + /** * Check if the file is an MS Office document type that needs special processing * * @param path String + * @param sess SrvSession + * @param nodeService NodeService + * @param nodeRef NodeRef * @return boolean */ - private static final boolean isMSOfficeSpecialFile(String path) { + private static final boolean isMSOfficeSpecialFile( String path, SrvSession sess, NodeService nodeService, NodeRef nodeRef) { // Check if the file extension indicates a problem MS Office format path = path.toLowerCase(); - if ( path.endsWith( ".xls")) - return true; + if ( path.endsWith( ".xls") && sess instanceof SMBSrvSession) { + + // Check if the file is versionable + + if ( nodeService.hasAspect( nodeRef, ContentModel.ASPECT_VERSIONABLE)) + return true; + } return false; } + + /** + * Check if the file is an OpenOffice document type that needs special processing + * + * @param path String + * @param sess SrvSession + * @param nodeService NodeService + * @param nodeRef NodeRef + * @return boolean + */ + private static final boolean isOpenOfficeSpecialFile( String path, SrvSession sess, NodeService nodeService, NodeRef nodeRef) { + + // Check if the file extension indicates a problem OpenOffice format + + path = path.toLowerCase(); + + if ( path.endsWith( ".odt") && sess instanceof SMBSrvSession) { + + // Check if the file is versionable + + if ( nodeService.hasAspect( nodeRef, ContentModel.ASPECT_VERSIONABLE)) + return true; + } + return false; + } } diff --git a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java index 5d2300f552..4d52acd3a9 100644 --- a/source/java/org/alfresco/filesys/repo/ContentSearchContext.java +++ b/source/java/org/alfresco/filesys/repo/ContentSearchContext.java @@ -404,11 +404,17 @@ public class ContentSearchContext extends SearchContext } } - // Check if the resume file name is the last file returned, no need to reposition the file index + // Check if the resume file name is the last file returned if ( m_lastFileName != null && info.getFileName().equalsIgnoreCase( m_lastFileName)) { + + // Reset the index/resume id - // DEBUG + index = index - 1; + resumeId = resId - 1; + donePseudoFiles = true; + + // DEBUG if ( logger.isDebugEnabled()) logger.debug("Fast search restart - " + m_lastFileName); diff --git a/source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java b/source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java index 25b7332955..11cc49b28c 100644 --- a/source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java +++ b/source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java @@ -22,6 +22,7 @@ package org.alfresco.filesys.repo; import java.io.IOException; import org.alfresco.jlan.server.filesys.FileInfo; +import org.alfresco.jlan.server.filesys.cache.FileState; import org.alfresco.jlan.smb.SeekType; import org.alfresco.service.cmr.repository.NodeRef; @@ -83,7 +84,9 @@ public class LinkMemoryNetworkFile extends NodeRefNetworkFile */ public void closeFile() throws java.io.IOException { - // Nothing to do + // Clear the file state + + setFileState( null); } /** @@ -247,4 +250,18 @@ public class LinkMemoryNetworkFile extends NodeRefNetworkFile { // Allow the write, just do not do anything } + + /** + * Return a dummy file state for this file + * + * @return FileState + */ + public FileState getFileState() { + + // Create a dummy file state + + if ( super.getFileState() == null) + setFileState(new FileState(getFullName())); + return super.getFileState(); + } } diff --git a/source/java/org/alfresco/filesys/repo/NodeRefNetworkFile.java b/source/java/org/alfresco/filesys/repo/NodeRefNetworkFile.java index 21a7d4051d..f3561557bb 100644 --- a/source/java/org/alfresco/filesys/repo/NodeRefNetworkFile.java +++ b/source/java/org/alfresco/filesys/repo/NodeRefNetworkFile.java @@ -18,7 +18,6 @@ package org.alfresco.filesys.repo; import org.alfresco.filesys.alfresco.AlfrescoNetworkFile; -import org.alfresco.jlan.server.filesys.NetworkFile; import org.alfresco.service.cmr.repository.NodeRef; @@ -122,12 +121,12 @@ public abstract class NodeRefNetworkFile extends AlfrescoNetworkFile { return --m_openCount; } - /** - * Return the open file count - * - * @return int - */ - public final int getOpenCount() { - return m_openCount; - } + /** + * Return the open file count + * + * @return int + */ + public final int getOpenCount() { + return m_openCount; + } } diff --git a/source/java/org/alfresco/filesys/repo/OpenOfficeContentNetworkFile.java b/source/java/org/alfresco/filesys/repo/OpenOfficeContentNetworkFile.java new file mode 100644 index 0000000000..d1c84dfd4c --- /dev/null +++ b/source/java/org/alfresco/filesys/repo/OpenOfficeContentNetworkFile.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.filesys.repo; + +import java.io.IOException; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * OpenOffice Content Network File Class + * + *

Provides special handling for OpenOffice file saves that open the file, truncate, close, then open the file + * again to write the data, as this causes multiple versions to be generated when the file is versionable. + * + * @author gkspencer + */ +public class OpenOfficeContentNetworkFile extends ContentNetworkFile { + + // Debug logging + + private static final Log logger = LogFactory.getLog(OpenOfficeContentNetworkFile.class); + + // Flag to indicate the last I/O operation was a truncate file to zero size + + private boolean m_truncateToZero; + + // Delayed file close count + + private int m_delayedClose; + + /** + * Class constructor + * + * @param transactionService TransactionService + * @param nodeService NodeService + * @param contentService ContentService + * @param nodeRef NodeRef + * @param name String + */ + protected OpenOfficeContentNetworkFile( + NodeService nodeService, + ContentService contentService, + MimetypeService mimetypeService, + NodeRef nodeRef, + String name) + { + super(nodeService, contentService, mimetypeService, nodeRef, name); + + // DEBUG + + if (logger.isDebugEnabled()) + logger.debug("Using OpenOffice network file for " + name + ", versionLabel=" + nodeService.getProperty( nodeRef, ContentModel.PROP_VERSION_LABEL)); + } + + /** + * Return the delayed close count + * + * @return int + */ + public final int getDelayedCloseCount() { + return m_delayedClose; + } + + /** + * Increment the delayed close count + */ + public final void incrementDelayedCloseCount() { + m_delayedClose++; + + // Clear the truncate to zero status + + m_truncateToZero = false; + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Increment delayed close count=" + getDelayedCloseCount() + ", path=" + getName()); + } + + /** + * Check if the last file operation was a truncate to zero length + * + * @return boolean + */ + public final boolean truncatedToZeroLength() { + return m_truncateToZero; + } + + /** + * Read from the file. + * + * @param buf byte[] + * @param len int + * @param pos int + * @param fileOff long + * @return Length of data read. + * @exception IOException + */ + public int readFile(byte[] buffer, int length, int position, long fileOffset) + throws IOException + { + // Clear the truncate flag + + m_truncateToZero = false; + + // Chain to the standard read + + return super.readFile( buffer, length, position, fileOffset); + } + + /** + * Write a block of data to the file. + * + * @param buf byte[] + * @param len int + * @param pos int + * @param fileOff long + * @exception IOException + */ + public void writeFile(byte[] buffer, int length, int position, long fileOffset) + throws IOException + { + // Clear the truncate flag + + m_truncateToZero = false; + + // Chain to the standard write + + super.writeFile( buffer, length, position, fileOffset); + } + + /** + * Truncate or extend the file to the specified length + * + * @param size long + * @exception IOException + */ + public void truncateFile(long size) + throws IOException + { + // Chain to the standard truncate + + super.truncateFile( size); + + // Check for a truncate to zero length + + if ( size == 0L) { + m_truncateToZero = true; + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("OpenOffice document truncated to zero length, path=" + getName()); + } + } + + /** + * Close the file + * + * @exception IOException + */ + public void closeFile() + throws IOException + { + // DEBUG + + if ( logger.isDebugEnabled()) { + logger.debug("Close OpenOffice file, " + getName() + ", delayed close count=" + getDelayedCloseCount() + ", writes=" + getWriteCount() + + ", modified=" + isModified()); + logger.debug(" Open count=" + getOpenCount() + ", fstate open=" + getFileState().getOpenCount()); + } + + // Chain to the standard close + + super.closeFile(); + } +} diff --git a/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java b/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java index ab00a0dde5..c823130779 100644 --- a/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java +++ b/source/java/org/alfresco/filesys/repo/desk/CheckInOutDesktopAction.java @@ -24,17 +24,19 @@ */ package org.alfresco.filesys.repo.desk; +import java.io.IOException; import java.io.Serializable; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.concurrent.Callable; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.alfresco.DesktopAction; import org.alfresco.filesys.alfresco.DesktopParams; import org.alfresco.filesys.alfresco.DesktopResponse; import org.alfresco.filesys.alfresco.DesktopTarget; +import org.alfresco.filesys.alfresco.AlfrescoDiskDriver.CallableIO; import org.alfresco.jlan.server.filesys.FileName; import org.alfresco.jlan.server.filesys.FileStatus; import org.alfresco.jlan.server.filesys.NotifyChange; @@ -90,14 +92,14 @@ public class CheckInOutDesktopAction extends DesktopAction { if ( params.numberOfTargetNodes() == 0) return new DesktopResponse(StsSuccess); - class WriteTxn implements Callable + class WriteTxn implements CallableIO { private List> fileChanges; /* (non-Javadoc) * @see java.util.concurrent.Callable#call() */ - public DesktopResponse call() throws Exception + public DesktopResponse call() throws IOException { // Initialize / reset the list of file changes fileChanges = new LinkedList>(); @@ -156,10 +158,17 @@ public class CheckInOutDesktopAction extends DesktopAction { } catch (Exception ex) { - // If this is a 'retryable' exception, pass it on + // Propagate retryable errors. Log the rest. if (RetryingTransactionHelper.extractRetryCause(ex) != null) { - throw ex; + if (ex instanceof RuntimeException) + { + throw (RuntimeException)ex; + } + else + { + throw new AlfrescoRuntimeException("Desktop action error", ex); + } } // Dump the error @@ -229,10 +238,17 @@ public class CheckInOutDesktopAction extends DesktopAction { } catch (Exception ex) { - // If this is a 'retryable' exception, pass it on + // Propagate retryable errors. Log the rest. if (RetryingTransactionHelper.extractRetryCause(ex) != null) { - throw ex; + if (ex instanceof RuntimeException) + { + throw (RuntimeException)ex; + } + else + { + throw new AlfrescoRuntimeException("Desktop action error", ex); + } } // Dump the error @@ -269,7 +285,16 @@ public class CheckInOutDesktopAction extends DesktopAction { // Process the transaction WriteTxn callback = new WriteTxn(); - DesktopResponse response = params.getDriver().doInWriteTransaction(params.getSession(), callback); + DesktopResponse response; + try + { + response = params.getDriver().doInWriteTransaction(params.getSession(), callback); + } + catch (IOException e) + { + // Should not happen + throw new AlfrescoRuntimeException("Desktop action error", e); + } // Queue file change notifications callback.notifyChanges(); diff --git a/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java b/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java index f091aa1fa6..dd7a1b6d44 100644 --- a/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java +++ b/source/java/org/alfresco/filesys/repo/desk/JavaScriptDesktopAction.java @@ -25,21 +25,21 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; -import java.util.concurrent.Callable; -import org.springframework.extensions.config.ConfigElement; import org.alfresco.filesys.alfresco.AlfrescoContext; import org.alfresco.filesys.alfresco.AlfrescoDiskDriver; import org.alfresco.filesys.alfresco.DesktopAction; import org.alfresco.filesys.alfresco.DesktopActionException; import org.alfresco.filesys.alfresco.DesktopParams; import org.alfresco.filesys.alfresco.DesktopResponse; +import org.alfresco.filesys.alfresco.AlfrescoDiskDriver.CallableIO; import org.alfresco.jlan.server.filesys.DiskSharedDevice; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.scripts.ScriptException; import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.util.ResourceFinder; import org.springframework.core.io.Resource; +import org.springframework.extensions.config.ConfigElement; /** * Javascript Desktop Action Class @@ -230,23 +230,19 @@ public class JavaScriptDesktopAction extends DesktopAction { if ( hasWebappURL()) model.put("webURL", getWebappURL()); - // Compute the response in a retryable write transaction - return params.getDriver().doInWriteTransaction(params.getSession(), new Callable() + try { - public DesktopResponse call() throws Exception + // Compute the response in a retryable write transaction + return params.getDriver().doInWriteTransaction(params.getSession(), new CallableIO() { - DesktopResponse response = new DesktopResponse(StsSuccess); - - // Run the script - - Object result = null; - - try + public DesktopResponse call() throws IOException { + DesktopResponse response = new DesktopResponse(StsSuccess); + // Run the script - result = scriptService.executeScriptString(getScript(), model); + Object result = scriptService.executeScriptString(getScript(), model); // Check the result @@ -294,23 +290,21 @@ public class JavaScriptDesktopAction extends DesktopAction { response.setStatus(sts, msgToken != null ? msgToken : ""); } } + + // Return the response + + return response; } - catch (ScriptException ex) - { - if (RetryingTransactionHelper.extractRetryCause(ex) != null) - { - throw ex; - } - - // Set the error response for the client - response.setStatus(StsError, ex.getMessage()); - } - - // Return the response - - return response; - } - }); + }); + } + catch (ScriptException ex) + { + return new DesktopResponse(StsError, ex.getMessage()); + } + catch (IOException ex) + { + return new DesktopResponse(StsError, ex.getMessage()); + } } else { diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java index 3fdacbe9d7..b0fdf93a41 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/ImapFoldersPatch.java @@ -32,6 +32,7 @@ import org.alfresco.repo.importer.ACPImportPackageHandler; import org.alfresco.repo.importer.ImporterBootstrap; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.admin.PatchException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -217,7 +218,7 @@ public class ImapFoldersPatch extends AbstractPatch if (imapConfigFolderNodeRef == null) { // import the content - RunAsWork importRunAs = new RunAsWork() + final RunAsWork importRunAs = new RunAsWork() { public Object doWork() throws Exception { @@ -227,7 +228,18 @@ public class ImapFoldersPatch extends AbstractPatch return null; } }; - AuthenticationUtil.runAs(importRunAs, authenticationContext.getSystemUserName()); + + RetryingTransactionCallback cb = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + AuthenticationUtil.runAs(importRunAs, authenticationContext.getSystemUserName()); + return null; + } + + }; + + transactionService.getRetryingTransactionHelper().doInTransaction(cb, false, true); msg = I18NUtil.getMessage(MSG_CREATED); } else diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index b04e68a496..3c0fa05f10 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -268,34 +268,6 @@ public class AVMStoreImpl implements AVMStore newChild.setAncestor(child); parent.putChild(parentName[1], newChild); } - - // TODO This leaves the behavior of LayeredFiles not quite - // right. - /* - String parentName[] = AVMNodeConverter.SplitBase(entry.getPath()); - parentName[0] = parentName[0].substring(parentName[0].indexOf(':') + 1); - lookup = lookupDirectory(-1, parentName[0], true); - DirectoryNode parent = (DirectoryNode)lookup.getCurrentNode(); - AVMNode child = parent.lookupChild(lookup, parentName[1], false); - // TODO For debugging. - if (child == null) - { - System.err.println("Yoiks!"); - } - // TODO This is funky. Need to look carefully to see that this call - // does exactly what's needed. - lookup.add(child, parentName[1], false); - AVMNode newChild = null; - if (child.getType() == AVMNodeType.LAYERED_DIRECTORY) - { - newChild = child.copy(lookup); - } - else - { - newChild = ((LayeredFileNode)child).copyLiterally(lookup); - } - parent.putChild(parentName[1], newChild); - */ } if (logger.isTraceEnabled()) @@ -305,7 +277,7 @@ public class AVMStoreImpl implements AVMStore // Clear out the new nodes. List allLayeredNodeIDs = AVMDAOs.Instance().fAVMNodeDAO.getNewLayeredInStoreIDs(me); - + AVMDAOs.Instance().fAVMNodeDAO.clearNewInStore(me); AVMDAOs.Instance().fAVMNodeDAO.clear(); @@ -313,7 +285,13 @@ public class AVMStoreImpl implements AVMStore for (Long layeredID : allLayeredNodeIDs) { Layered layered = (Layered)AVMDAOs.Instance().fAVMNodeDAO.getByID(layeredID); - String indirection = layered.getIndirection(); + + String indirection = null; + if (layered != null) + { + indirection = layered.getIndirection(); + } + if (indirection == null) { continue; diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index 15067d8a5f..3298cbde95 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -205,6 +205,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean private int maximumStringLength; private ThreadLocal executedStatementsThreadLocal = new ThreadLocal(); + private File xmlPreSchemaOutputFile; // This must be set if there are any executed statements public SchemaBootstrap() { @@ -912,6 +913,15 @@ public class SchemaBootstrap extends AbstractLifecycleBean StringBuilder executedStatements = executedStatementsThreadLocal.get(); if (executedStatements == null) { + // Dump the normalized, pre-upgrade Alfresco schema. We keep the file for later reporting. + xmlPreSchemaOutputFile = dumpSchema( + connection, + this.dialect, + TempFileProvider.createTempFile( + "AlfrescoSchema-" + this.dialect.getClass().getSimpleName() + "-", + "-Startup.xml").getPath(), + "Failed to dump normalized, pre-upgrade schema to file."); + // There is no lock at this stage. This process can fall out if the lock can't be applied. setBootstrapStarted(connection); executedStatements = new StringBuilder(8094); @@ -1256,15 +1266,6 @@ public class SchemaBootstrap extends AbstractLifecycleBean // Update the schema, if required. if (updateSchema) { - // Dump the normalized, pre-upgrade Alfresco schema. We keep the file for later reporting. - File xmlPreSchemaOutputFile = dumpSchema( - connection, - this.dialect, - TempFileProvider.createTempFile( - "AlfrescoSchema-" + this.dialect.getClass().getSimpleName() + "-", - "-Startup.xml").getPath(), - "Failed to dump normalized, pre-upgrade schema to file."); - // Retries are required here as the DB lock will be applied lazily upon first statement execution. // So if the schema is up to date (no statements executed) then the LockFailException cannot be // thrown. If it is thrown, the the update needs to be rerun as it will probably generate no SQL @@ -1333,34 +1334,37 @@ public class SchemaBootstrap extends AbstractLifecycleBean setBootstrapCompleted(connection); } - // Dump the normalized, post-upgrade Alfresco schema. - File xmlPostSchemaOutputFile = dumpSchema( - connection, - this.dialect, - TempFileProvider.createTempFile( - "AlfrescoSchema-" + this.dialect.getClass().getSimpleName() + "-", - ".xml").getPath(), - "Failed to dump normalized, post-upgrade schema to file."); - // Report normalized dumps - if (createdSchema) + if (executedStatements != null) { - // This is a new schema - if (xmlPostSchemaOutputFile != null) + // Dump the normalized, post-upgrade Alfresco schema. + File xmlPostSchemaOutputFile = dumpSchema( + connection, + this.dialect, + TempFileProvider.createTempFile( + "AlfrescoSchema-" + this.dialect.getClass().getSimpleName() + "-", + ".xml").getPath(), + "Failed to dump normalized, post-upgrade schema to file."); + + if (createdSchema) { - LogUtil.info(logger, MSG_NORMALIZED_SCHEMA, xmlPostSchemaOutputFile.getPath()); + // This is a new schema + if (xmlPostSchemaOutputFile != null) + { + LogUtil.info(logger, MSG_NORMALIZED_SCHEMA, xmlPostSchemaOutputFile.getPath()); + } } - } - else if (executedStatements != null) - { - // We upgraded, so have to report pre- and post- schema dumps - if (xmlPreSchemaOutputFile != null) + else { - LogUtil.info(logger, MSG_NORMALIZED_SCHEMA_PRE, xmlPreSchemaOutputFile.getPath()); - } - if (xmlPostSchemaOutputFile != null) - { - LogUtil.info(logger, MSG_NORMALIZED_SCHEMA_POST, xmlPostSchemaOutputFile.getPath()); + // We upgraded, so have to report pre- and post- schema dumps + if (xmlPreSchemaOutputFile != null) + { + LogUtil.info(logger, MSG_NORMALIZED_SCHEMA_PRE, xmlPreSchemaOutputFile.getPath()); + } + if (xmlPostSchemaOutputFile != null) + { + LogUtil.info(logger, MSG_NORMALIZED_SCHEMA_POST, xmlPostSchemaOutputFile.getPath()); + } } } } diff --git a/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java b/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java index 86f3bdba23..b1aaf1f8b1 100644 --- a/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java +++ b/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java @@ -665,7 +665,8 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial return; } } - String gid = "GROUP_" + gidAttribute.get(0); + String groupShortName = gidAttribute.get(0).toString(); + String gid = "GROUP_" + groupShortName; NodeDescription group = lookup.get(gid); if (group == null) @@ -718,7 +719,7 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial .toAttributes(); // Recognize user DNs - if (distinguishedName.startsWith(userDistinguishedNamePrefix) + if (distinguishedNameForComparison.startsWith(userDistinguishedNamePrefix) && (nameAttribute = nameAttributes .get(LDAPUserRegistry.this.userIdAttributeName)) != null) { @@ -727,7 +728,7 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial } // Recognize group DNs - if (distinguishedName.startsWith(groupDistinguishedNamePrefix) + if (distinguishedNameForComparison.startsWith(groupDistinguishedNamePrefix) && (nameAttribute = nameAttributes .get(LDAPUserRegistry.this.groupIdAttributeName)) != null) { @@ -801,20 +802,21 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial // Unresolvable name if (LDAPUserRegistry.this.errorOnMissingMembers) { - throw new AlfrescoRuntimeException("Failed to resolve distinguished name: " - + attribute, e); + throw new AlfrescoRuntimeException("Failed to resolve member of group '" + + groupShortName + "' with distinguished name: " + attribute, e); } - LDAPUserRegistry.logger.warn("Failed to resolve distinguished name: " - + attribute, e); + LDAPUserRegistry.logger.warn("Failed to resolve member of group '" + + groupShortName + "' with distinguished name: " + attribute, e); continue; } } if (LDAPUserRegistry.this.errorOnMissingMembers) { - throw new AlfrescoRuntimeException("Failed to resolve distinguished name: " - + attribute); + throw new AlfrescoRuntimeException("Failed to resolve member of group '" + + groupShortName + "' with distinguished name: " + attribute); } - LDAPUserRegistry.logger.warn("Failed to resolve distinguished name: " + attribute); + LDAPUserRegistry.logger.warn("Failed to resolve member of group '" + groupShortName + + "' with distinguished name: " + attribute); } catch (InvalidNameException e) { @@ -1077,19 +1079,22 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial */ private boolean hasAttributeValue(Attribute attribute, String value) throws NamingException { - NamingEnumeration values = attribute.getAll(); - while (values.hasMore()) + if (attribute != null) { - try + NamingEnumeration values = attribute.getAll(); + while (values.hasMore()) { - if (value.equalsIgnoreCase((String) values.next())) + try { - return true; + if (value.equalsIgnoreCase((String) values.next())) + { + return true; + } + } + catch (ClassCastException e) + { + // Not a string value. ignore and continue } - } - catch (ClassCastException e) - { - // Not a string value. ignore and continue } } return false;