mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
Various fixes for MS Office problems (two versions on edit/save, version generated when file opened and not changed (ACT 3691).
Added file access/change/modification timestamp tracking for open files via the file state cache. Added set file information support for setting creation and modification timestamps, used by MS Office to transfer original timestamp to new document. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@10007 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -346,7 +346,7 @@ public class AVMContext extends AlfrescoContext
|
||||
|
||||
// Update the file state modify time
|
||||
|
||||
rootState.setLastUpdated( System.currentTimeMillis());
|
||||
rootState.updateModifyDateTime();
|
||||
|
||||
// Send a change notification for the deleted folder
|
||||
|
||||
|
@@ -25,10 +25,8 @@ package org.alfresco.filesys.avm;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
@@ -41,7 +39,6 @@ import org.alfresco.jlan.server.SrvSession;
|
||||
import org.alfresco.jlan.server.auth.ClientInfo;
|
||||
import org.alfresco.jlan.server.core.DeviceContext;
|
||||
import org.alfresco.jlan.server.core.DeviceContextException;
|
||||
import org.alfresco.jlan.server.core.DeviceInterface;
|
||||
import org.alfresco.jlan.server.filesys.AccessDeniedException;
|
||||
import org.alfresco.jlan.server.filesys.DirectoryNotEmptyException;
|
||||
import org.alfresco.jlan.server.filesys.DiskInterface;
|
||||
@@ -81,14 +78,9 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
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.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
96
source/java/org/alfresco/filesys/repo/BufferedWrite.java
Normal file
96
source/java/org/alfresco/filesys/repo/BufferedWrite.java
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2008 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
|
||||
package org.alfresco.filesys.repo;
|
||||
|
||||
/**
|
||||
* Buffered Write Class
|
||||
*
|
||||
* <p>Contains the details and data for a buffered write.
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public class BufferedWrite {
|
||||
|
||||
// Write details
|
||||
|
||||
private long m_offset;
|
||||
private byte[] m_data;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param buf byte[]
|
||||
* @param offset long
|
||||
*/
|
||||
public BufferedWrite( byte[] buf, long offset) {
|
||||
m_data = buf;
|
||||
m_offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file offset
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public final long getOffset() {
|
||||
return m_offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the write data
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public final byte[] getData() {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data length
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getDataLength() {
|
||||
return m_data != null ? m_data.length : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the buffered write details as a string
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
|
||||
str.append("[Data len=");
|
||||
str.append(m_data.length);
|
||||
str.append(",Offset=");
|
||||
str.append(m_offset);
|
||||
str.append("]");
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
}
|
@@ -636,10 +636,6 @@ public class CifsHelper
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if the new file name is a temporary file name
|
||||
if ( newName.endsWith(".tmp") || newName.endsWith(".temp"))
|
||||
nodeService.addAspect(nodeToRenameRef, ContentModel.ASPECT_TEMPORARY, null);
|
||||
|
||||
fileFolderService.rename(nodeToRenameRef, newName);
|
||||
}
|
||||
catch (org.alfresco.service.cmr.model.FileExistsException e)
|
||||
|
@@ -27,6 +27,7 @@ package org.alfresco.filesys.repo;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.transaction.UserTransaction;
|
||||
@@ -606,8 +607,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// Debug
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
logger.info( "Added file state for pseudo files folder (getinfo) - " + paths[0]);
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug( "Added file state for pseudo files folder (getinfo) - " + paths[0]);
|
||||
}
|
||||
else if ( fstate.hasPseudoFiles() == false)
|
||||
{
|
||||
@@ -626,8 +627,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// Debug
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
logger.info( "Added pseudo files for folder (exists) - " + paths[0]);
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug( "Added pseudo files for folder (exists) - " + paths[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -657,6 +658,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
// Get the node ref for the path, chances are there is a file state in the cache
|
||||
|
||||
NodeRef nodeRef = getNodeForPath(tree, infoPath);
|
||||
|
||||
if ( nodeRef != null)
|
||||
{
|
||||
// Get the file information for the node
|
||||
@@ -665,7 +667,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("getInfo using cached noderef for path " + path);
|
||||
}
|
||||
|
||||
@@ -689,7 +691,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("getInfo using cached noderef for parent " + path);
|
||||
}
|
||||
}
|
||||
@@ -710,9 +712,25 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// Set the file id for the file using the relative path
|
||||
|
||||
if ( finfo != null)
|
||||
if ( finfo != null) {
|
||||
|
||||
// Set the file id
|
||||
|
||||
finfo.setFileId( path.hashCode());
|
||||
|
||||
// Copy cached timestamps, if available
|
||||
|
||||
FileState fstate = getStateForPath(tree, infoPath);
|
||||
if ( fstate != null) {
|
||||
if ( fstate.hasAccessDateTime())
|
||||
finfo.setAccessDateTime(fstate.getAccessDateTime());
|
||||
if ( fstate.hasChangeDateTime())
|
||||
finfo.setChangeDateTime(fstate.getChangeDateTime());
|
||||
if ( fstate.hasModifyDateTime())
|
||||
finfo.setModifyDateTime(fstate.getModifyDateTime());
|
||||
}
|
||||
}
|
||||
|
||||
// Return the file information
|
||||
|
||||
return finfo;
|
||||
@@ -1019,8 +1037,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// Debug
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
logger.info( "Added file state for pseudo files folder (exists) - " + paths[0]);
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug( "Added file state for pseudo files folder (exists) - " + paths[0]);
|
||||
}
|
||||
}
|
||||
else if ( fstate.hasPseudoFiles() == false)
|
||||
@@ -1044,8 +1062,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// Debug
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
logger.info( "Added pseudo files for folder (exists) - " + paths[0]);
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug( "Added pseudo files for folder (exists) - " + paths[0]);
|
||||
}
|
||||
|
||||
// Check if the path is to a pseudo file
|
||||
@@ -1061,8 +1079,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
{
|
||||
// Failed to find pseudo file
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
logger.info( "Failed to find pseudo file (exists) - " + name);
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug( "Failed to find pseudo file (exists) - " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1167,8 +1185,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// Debug
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
logger.info( "Added file state for pseudo files folder (open) - " + paths[0]);
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug( "Added file state for pseudo files folder (open) - " + paths[0]);
|
||||
}
|
||||
}
|
||||
else if ( fstate.hasPseudoFiles() == false)
|
||||
@@ -1179,8 +1197,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// Debug
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
logger.info( "Added pseudo files for folder (open) - " + paths[0]);
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug( "Added pseudo files for folder (open) - " + paths[0]);
|
||||
}
|
||||
|
||||
// Check if the path is to a pseudo file
|
||||
@@ -1196,8 +1214,8 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
{
|
||||
// Failed to find pseudo file
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
logger.info( "Failed to find pseudo file (open) - " + params.getPath());
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug( "Failed to find pseudo file (open) - " + params.getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1439,7 +1457,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isInfoEnabled())
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Create file using cached noderef for path " + paths[0]);
|
||||
}
|
||||
}
|
||||
@@ -2019,9 +2037,46 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
if ( sameFolder == true)
|
||||
{
|
||||
// Check if the new file name is a temporary file name
|
||||
|
||||
String newNameNorm = newName.toLowerCase();
|
||||
boolean isTempFile = false;
|
||||
|
||||
if ( newNameNorm.endsWith(".tmp") || newNameNorm.endsWith(".temp")) {
|
||||
|
||||
// Add the temporary aspect, also prevents versioning
|
||||
|
||||
nodeService.addAspect(nodeToMoveRef, ContentModel.ASPECT_TEMPORARY, null);
|
||||
|
||||
// Indicate that the new file is a temporary file
|
||||
|
||||
isTempFile = true;
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Added Temporary aspect to renamed file " + newName);
|
||||
}
|
||||
|
||||
// Rename the file/folder
|
||||
|
||||
cifsHelper.rename(nodeToMoveRef, name);
|
||||
|
||||
// Check if the temporary aspect should be removed from the renamed file
|
||||
|
||||
String oldNameNorm = oldName.toLowerCase();
|
||||
|
||||
if ( isTempFile == false && (oldNameNorm.endsWith(".tmp") || oldNameNorm.endsWith(".temp"))) {
|
||||
|
||||
// Remove the temporary aspect
|
||||
|
||||
nodeService.removeAspect(nodeToMoveRef, ContentModel.ASPECT_TEMPORARY);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Removed Temporary aspect from renamed file " + newName);
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
@@ -2151,6 +2206,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
// Get the file/folder node
|
||||
|
||||
NodeRef nodeRef = getNodeForPath(tree, name);
|
||||
FileState fstate = getStateForPath(tree, name);
|
||||
|
||||
// Check permissions on the file/folder node
|
||||
|
||||
@@ -2164,6 +2220,10 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
|
||||
if ( info.hasSetFlag(FileInfo.SetDeleteOnClose) && info.hasDeleteOnClose())
|
||||
{
|
||||
// Start a transaction
|
||||
|
||||
beginReadTransaction( sess);
|
||||
|
||||
// Check if the node is locked
|
||||
|
||||
if ( nodeService.hasAspect( nodeRef, ContentModel.ASPECT_LOCKABLE))
|
||||
@@ -2175,6 +2235,64 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
|
||||
if ( lockTypeStr != null)
|
||||
throw new AccessDeniedException("Node locked, cannot mark for delete");
|
||||
}
|
||||
|
||||
// Update the change date/time
|
||||
|
||||
if ( fstate != null)
|
||||
fstate.updateChangeDateTime();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Set deleteOnClose=true file=" + name);
|
||||
}
|
||||
|
||||
// Set the creation date/time
|
||||
|
||||
if ( info.hasSetFlag(FileInfo.SetCreationDate)) {
|
||||
|
||||
// Create the transaction
|
||||
|
||||
beginWriteTransaction( sess);
|
||||
|
||||
// Set the creation date on the file/folder node
|
||||
|
||||
Date createDate = new Date( info.getCreationDateTime());
|
||||
nodeService.setProperty( nodeRef, ContentModel.PROP_CREATED, createDate);
|
||||
|
||||
// Update the change date/time
|
||||
|
||||
if ( fstate != null)
|
||||
fstate.updateChangeDateTime();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Set creationDate=" + createDate + " file=" + name);
|
||||
}
|
||||
|
||||
// Set the modification date/time
|
||||
|
||||
if ( info.hasSetFlag(FileInfo.SetModifyDate)) {
|
||||
|
||||
// Create the transaction
|
||||
|
||||
beginWriteTransaction( sess);
|
||||
|
||||
// Set the creation date on the file/folder node
|
||||
|
||||
Date modifyDate = new Date( info.getModifyDateTime());
|
||||
nodeService.setProperty( nodeRef, ContentModel.PROP_MODIFIED, modifyDate);
|
||||
|
||||
// Update the change date/time
|
||||
|
||||
if ( fstate != null)
|
||||
fstate.updateChangeDateTime();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Set modifyDate=" + modifyDate + " file=" + name);
|
||||
}
|
||||
}
|
||||
catch (org.alfresco.repo.security.permissions.AccessDeniedException ex)
|
||||
|
@@ -69,6 +69,8 @@ public class ContentNetworkFile extends NodeRefNetworkFile
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(ContentNetworkFile.class);
|
||||
|
||||
// Services
|
||||
|
||||
private NodeService nodeService;
|
||||
private ContentService contentService;
|
||||
private MimetypeService mimetypeService;
|
||||
@@ -107,7 +109,20 @@ public class ContentNetworkFile extends NodeRefNetworkFile
|
||||
|
||||
// Create the file
|
||||
|
||||
ContentNetworkFile netFile = new ContentNetworkFile(nodeService, contentService, mimetypeService, nodeRef, path);
|
||||
ContentNetworkFile netFile = null;
|
||||
|
||||
if ( isMSOfficeSpecialFile(path)) {
|
||||
|
||||
// Create a file for special processing
|
||||
|
||||
netFile = new MSOfficeContentNetworkFile( nodeService, contentService, mimetypeService, nodeRef, path);
|
||||
}
|
||||
else {
|
||||
|
||||
// Create a normal content file
|
||||
|
||||
netFile = new ContentNetworkFile(nodeService, contentService, mimetypeService, nodeRef, path);
|
||||
}
|
||||
|
||||
// Set relevant parameters
|
||||
|
||||
@@ -182,7 +197,7 @@ public class ContentNetworkFile extends NodeRefNetworkFile
|
||||
* @param nodeRef NodeRef
|
||||
* @param name String
|
||||
*/
|
||||
private ContentNetworkFile(
|
||||
protected ContentNetworkFile(
|
||||
NodeService nodeService,
|
||||
ContentService contentService,
|
||||
MimetypeService mimetypeService,
|
||||
@@ -439,6 +454,11 @@ public class ContentNetworkFile extends NodeRefNetworkFile
|
||||
|
||||
setFileSize( size);
|
||||
|
||||
// Update the modification date/time
|
||||
|
||||
if ( getFileState() != null)
|
||||
getFileState().updateModifyDateTime();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
@@ -475,6 +495,11 @@ public class ContentNetworkFile extends NodeRefNetworkFile
|
||||
|
||||
setFileSize(channel.size());
|
||||
|
||||
// Update the modification date/time
|
||||
|
||||
if ( getFileState() != null)
|
||||
getFileState().updateModifyDateTime();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
@@ -507,6 +532,11 @@ public class ContentNetworkFile extends NodeRefNetworkFile
|
||||
count = 0; // doesn't obey the same rules, i.e. just returns the bytes read
|
||||
}
|
||||
|
||||
// Update the access date/time
|
||||
|
||||
if ( getFileState() != null)
|
||||
getFileState().updateAccessDateTime();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
@@ -574,6 +604,11 @@ public class ContentNetworkFile extends NodeRefNetworkFile
|
||||
break;
|
||||
}
|
||||
|
||||
// Update the access date/time
|
||||
|
||||
if ( getFileState() != null)
|
||||
getFileState().updateAccessDateTime();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
@@ -601,9 +636,31 @@ public class ContentNetworkFile extends NodeRefNetworkFile
|
||||
|
||||
channel.force(false);
|
||||
|
||||
// Update the access date/time
|
||||
|
||||
if ( getFileState() != null)
|
||||
getFileState().updateAccessDateTime();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Flush file=" + this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the file is an MS Office document type that needs special processing
|
||||
*
|
||||
* @param path String
|
||||
* @return boolean
|
||||
*/
|
||||
private static final boolean isMSOfficeSpecialFile(String path) {
|
||||
|
||||
// Check if the file extension indicates a problem MS Office format
|
||||
|
||||
path = path.toLowerCase();
|
||||
|
||||
if ( path.endsWith( ".xls"))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2008 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
|
||||
package org.alfresco.filesys.repo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Ms Office Content Network File Class
|
||||
*
|
||||
* <p>Provides special handling for MS Office files that are written to by the app even though the user does not
|
||||
* change the file.
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public class MSOfficeContentNetworkFile extends ContentNetworkFile {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(MSOfficeContentNetworkFile.class);
|
||||
|
||||
// Count of file reads
|
||||
|
||||
private int m_readCnt;
|
||||
|
||||
// Buffered write list
|
||||
|
||||
private List<BufferedWrite> m_writeList;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param transactionService TransactionService
|
||||
* @param nodeService NodeService
|
||||
* @param contentService ContentService
|
||||
* @param nodeRef NodeRef
|
||||
* @param name String
|
||||
*/
|
||||
protected MSOfficeContentNetworkFile(
|
||||
NodeService nodeService,
|
||||
ContentService contentService,
|
||||
MimetypeService mimetypeService,
|
||||
NodeRef nodeRef,
|
||||
String name)
|
||||
{
|
||||
super(nodeService, contentService, mimetypeService, nodeRef, name);
|
||||
|
||||
// Create the buffered write list
|
||||
|
||||
m_writeList = new ArrayList<BufferedWrite>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file read count
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getReadCount() {
|
||||
return m_readCnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
// Update the read count
|
||||
|
||||
m_readCnt++;
|
||||
|
||||
// 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
|
||||
{
|
||||
// Check if writes are being buffered
|
||||
|
||||
if ( m_writeList != null) {
|
||||
|
||||
// Check if the write should be buffered
|
||||
|
||||
if ( getReadCount() > 0 && m_writeList.size() < 2) {
|
||||
|
||||
// Buffer the write, looks like a file open update. Do not buffer zero length writes.
|
||||
|
||||
if ( length == 0) {
|
||||
byte[] data = new byte[ length];
|
||||
System.arraycopy(buffer, position, data, 0, length);
|
||||
|
||||
BufferedWrite bufWrite = new BufferedWrite( data, fileOffset);
|
||||
m_writeList.add( bufWrite);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("MSOfficeFile: Buffered write=" + bufWrite + ", cnt=" + m_writeList.size() + ", readCnt=" + getReadCount());
|
||||
}
|
||||
else if ( logger.isDebugEnabled())
|
||||
logger.debug("MSOfficeFile: Ignored zero length write");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if there are any buffered writes to be flushed
|
||||
|
||||
if ( m_writeList.size() > 0) {
|
||||
|
||||
// Write out the buffered writes first
|
||||
|
||||
while ( m_writeList.size() > 0) {
|
||||
|
||||
// Get the current buffered write
|
||||
|
||||
BufferedWrite bufWrite = m_writeList.remove( 0);
|
||||
|
||||
try {
|
||||
|
||||
// Write the buffered data to the file
|
||||
|
||||
super.writeFile( bufWrite.getData(), bufWrite.getDataLength(), 0, bufWrite.getOffset());
|
||||
}
|
||||
catch ( Exception ex) {
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("MSOfficeFile: Buffered write error, " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("MSOfficeFile: Buffered writes flushed");
|
||||
|
||||
// Disable any more buffered writes
|
||||
|
||||
m_writeList = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Now do the current write
|
||||
|
||||
super.writeFile(buffer, length, position, fileOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the file
|
||||
*
|
||||
* @exception IOException
|
||||
*/
|
||||
public void closeFile()
|
||||
throws IOException
|
||||
{
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled() && m_writeList != null)
|
||||
logger.debug("MSOfficeFile: Discarded buffered writes - " + m_writeList.size());
|
||||
|
||||
// Chain to the standard close
|
||||
|
||||
super.closeFile();
|
||||
}
|
||||
}
|
@@ -95,9 +95,11 @@ public class FileState
|
||||
|
||||
private PseudoFileList m_pseudoFiles;
|
||||
|
||||
// Last updated time
|
||||
// File timestamps updated only whilst file is open
|
||||
|
||||
private long m_lastUpdate;
|
||||
private long m_accessDate;
|
||||
private long m_modifyDate;
|
||||
private long m_changeDate;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
@@ -442,26 +444,6 @@ public class FileState
|
||||
m_path = normalizePath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last updated time
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public final long getLastUpdated()
|
||||
{
|
||||
return m_lastUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the last updated time
|
||||
*
|
||||
* @param updateTime long
|
||||
*/
|
||||
public final void setLastUpdated(long updateTime)
|
||||
{
|
||||
m_lastUpdate = updateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the count of active locks on this file
|
||||
*
|
||||
@@ -544,6 +526,91 @@ public class FileState
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the access date/time has been set
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasAccessDateTime() {
|
||||
return m_accessDate != 0L ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the access date/time
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public final long getAccessDateTime() {
|
||||
return m_accessDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the access date/time
|
||||
*/
|
||||
public final void updateAccessDateTime() {
|
||||
m_accessDate = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the change date/time has been set
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasChangeDateTime() {
|
||||
return m_changeDate != 0L ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the change date/time
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public final long getChangeDateTime() {
|
||||
return m_changeDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the change date/time
|
||||
*/
|
||||
public final void updateChangeDateTime() {
|
||||
m_changeDate = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the modification date/time has been set
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasModifyDateTime() {
|
||||
return m_modifyDate != 0L ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the modify date/time
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public final long getModifyDateTime() {
|
||||
return m_modifyDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the modify date/time
|
||||
*/
|
||||
public final void updateModifyDateTime() {
|
||||
m_modifyDate = System.currentTimeMillis();
|
||||
m_accessDate = m_modifyDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the modify date/time
|
||||
*
|
||||
* @param modTime long
|
||||
*/
|
||||
public final void updateModifyDateTime( long modTime) {
|
||||
m_modifyDate = modTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the file is readable for the specified section of the file and process id
|
||||
*
|
||||
|
@@ -127,7 +127,12 @@ public class FileStateTable
|
||||
*/
|
||||
public final synchronized FileState findFileState(String path)
|
||||
{
|
||||
return m_stateTable.get(FileState.normalizePath(path));
|
||||
FileState fstate = m_stateTable.get(FileState.normalizePath(path));
|
||||
|
||||
if ( fstate != null)
|
||||
fstate.updateAccessDateTime();
|
||||
|
||||
return fstate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,6 +173,11 @@ public class FileStateTable
|
||||
}
|
||||
}
|
||||
|
||||
// Update the access date/time if valid
|
||||
|
||||
if ( state != null)
|
||||
state.updateAccessDateTime();
|
||||
|
||||
// Return the file state
|
||||
|
||||
return state;
|
||||
@@ -182,7 +192,6 @@ public class FileStateTable
|
||||
*/
|
||||
public final synchronized FileState updateFileState(String oldName, String newName)
|
||||
{
|
||||
|
||||
// Find the current file state
|
||||
|
||||
FileState state = m_stateTable.remove(FileState.normalizePath(oldName));
|
||||
@@ -193,6 +202,10 @@ public class FileStateTable
|
||||
{
|
||||
state.setPath(newName);
|
||||
addFileState(state);
|
||||
|
||||
// Update the access date/time
|
||||
|
||||
state.updateAccessDateTime();
|
||||
}
|
||||
|
||||
// Return the updated file state
|
||||
@@ -246,6 +259,10 @@ public class FileStateTable
|
||||
|
||||
state.setPath(FileState.normalizePath(newPath));
|
||||
m_stateTable.put(state.getPath(), state);
|
||||
|
||||
// Updaet the access date/time
|
||||
|
||||
state.updateAccessDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -228,8 +228,9 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
|
||||
{
|
||||
if (this.nodeService.exists(nodeRef) == true && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true)
|
||||
{
|
||||
if (this.nodeService.exists(nodeRef) == true && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true
|
||||
&& this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY) == false)
|
||||
{
|
||||
Map<NodeRef, NodeRef> versionedNodeRefs = (Map)AlfrescoTransactionSupport.getResource(KEY_VERSIONED_NODEREFS);
|
||||
if (versionedNodeRefs == null || versionedNodeRefs.containsKey(nodeRef) == false)
|
||||
{
|
||||
|
Reference in New Issue
Block a user