Added support for link nodes in the repo filesystem driver.

Includes a patch to update existing link node file extensions from .lnk to .url, as CIFS requires .url files to generate the file:// links.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4947 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gary Spencer 2007-01-29 10:46:01 +00:00
parent 01fb4eac8a
commit 60ba6773c5
18 changed files with 892 additions and 40 deletions

View File

@ -123,3 +123,8 @@ patch.wcmFolders.webprojects.result.exists=The Web Projects folder already exist
patch.wcmFolders.webprojects.result.created=The Web Projects folder was successfully created: {0}
patch.wcmFolders.webforms.result.exists=The Web Forms folder already exists: {0}
patch.wcmFolders.webforms.result.created=The Web Forms folder was successfully created: {0}
patch.linkNodeExtension.description=Fixes link node file extensions to have a .url extension.
patch.linkNodeExtension.result=Fixed {0} link node file extensions. See file {1} for details.
patch.linkNodeExtension.err.unable_to_fix=Auto-fixing of link node file extensions failed. See file {0} for details.
patch.linkNodeExtension.rewritten=Name ''{0}'' rewritten to ''{1}''

View File

@ -61,6 +61,7 @@
<property name="permissionService"><ref bean="permissionService"/></property>
<property name="authenticationComponent"><ref bean="authenticationComponent"/></property>
<property name="authenticationService"><ref bean="authenticationService"/></property>
<property name="fileFolderService"><ref bean="FileFolderService" /></property>
<property name="serviceRegistry"><ref bean="ServiceRegistry"/></property>
<property name="stateReaper"><ref bean="fileStateReaper"/></property>
</bean>

View File

@ -585,5 +585,20 @@
</props>
</property>
</bean>
<bean id="patch.LinkNodeFileExtension" class="org.alfresco.repo.admin.patch.impl.LinkNodeFileExtensionPatch" parent="basePatch" >
<property name="id"><value>patch.LinkNodeFileExtension</value></property>
<property name="description"><value>patch.invalidNameEnding.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>33</value></property>
<property name="targetSchema"><value>34</value></property>
<!-- helper beans -->
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
<property name="nodeDaoService">
<ref bean="nodeDaoService" />
</property>
</bean>
</beans>

View File

@ -19,4 +19,4 @@ version.build=@build-number@
# Schema number
version.schema=33
version.schema=34

View File

@ -334,6 +334,16 @@ public abstract class AlfrescoContext extends DiskDeviceContext
*/
protected abstract IOControlHandler createIOHandler( DiskInterface filesysDriver);
/**
* Set the I/O control handler
*
* @param ioctlHandler IOControlHandler
*/
protected void setIOHandler( IOControlHandler ioctlHandler)
{
m_ioHandler = ioctlHandler;
}
/**
* Close the filesystem context
*/

View File

@ -29,7 +29,6 @@ import java.util.StringTokenizer;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.server.filesys.FileAttribute;
import org.alfresco.filesys.server.filesys.FileExistsException;
import org.alfresco.filesys.server.filesys.FileInfo;
import org.alfresco.filesys.server.filesys.FileName;
import org.alfresco.filesys.util.WildCard;
import org.alfresco.model.ContentModel;
@ -175,14 +174,12 @@ public class CifsHelper
* @return Returns the existing node reference
* @throws FileNotFoundException
*/
public FileInfo getFileInformation(NodeRef pathRootNodeRef, String path) throws FileNotFoundException
public ContentFileInfo getFileInformation(NodeRef pathRootNodeRef, String path) throws FileNotFoundException
{
// get the node being referenced
NodeRef nodeRef = getNodeRef(pathRootNodeRef, path);
FileInfo fileInfo = getFileInformation(nodeRef);
return fileInfo;
return getFileInformation(nodeRef);
}
/**
@ -196,13 +193,14 @@ public class CifsHelper
* @return Returns the file information pertinent to the node
* @throws FileNotFoundException if the path refers to a non-existent file
*/
public FileInfo getFileInformation(NodeRef nodeRef) throws FileNotFoundException
public ContentFileInfo getFileInformation(NodeRef nodeRef) throws FileNotFoundException
{
// get the file info
org.alfresco.service.cmr.model.FileInfo fileFolderInfo = fileFolderService.getFileInfo(nodeRef);
// retrieve required properties and create file info
FileInfo fileInfo = new FileInfo();
ContentFileInfo fileInfo = new ContentFileInfo();
fileInfo.setNodeRef(nodeRef);
// unset all attribute flags
int fileAttributes = 0;
@ -251,6 +249,11 @@ public class CifsHelper
fileInfo.setFileAttributes( attr);
}
// Check if it is a link node
if ( fileFolderInfo.isLink())
fileInfo.setLinkNodeRef( fileFolderInfo.getLinkNodeRef());
}
// created

View File

@ -55,6 +55,10 @@ public class ContentContext extends AlfrescoContext
m_rootPath = rootPath;
m_rootNodeRef = rootNodeRef;
// Create the I/O control handler
setIOHandler( createIOHandler( null));
}
/**

View File

@ -26,7 +26,6 @@ import javax.transaction.UserTransaction;
import org.alfresco.config.ConfigElement;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
import org.alfresco.filesys.avm.AVMNetworkFile;
import org.alfresco.filesys.server.SrvSession;
import org.alfresco.filesys.server.core.DeviceContext;
import org.alfresco.filesys.server.core.DeviceContextException;
@ -56,6 +55,7 @@ import org.alfresco.filesys.util.WildCard;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.cmr.lock.NodeLockedException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@ -101,6 +101,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
private SearchService searchService;
private ContentService contentService;
private PermissionService permissionService;
private FileFolderService fileFolderService;
private AuthenticationComponent authComponent;
private AuthenticationService authService;
@ -253,6 +254,16 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
{
this.authService = authService;
}
/**
* Set the file folder server
*
* @param fileService FileFolderService
*/
public void setFileFolderService(FileFolderService fileService)
{
fileFolderService = fileService;
}
/**
* Parse and validate the parameter string and create a device context object for this instance
@ -466,6 +477,11 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
context.enableStateTable( true, getStateReaper());
// Initialize the I/O control handler
if ( context.hasIOHandler())
context.getIOHandler().initialize( this, context);
// Return the context for this shared filesystem
return context;
@ -1200,9 +1216,53 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
}
}
// Create the network file
// Check if the node is a link node
NetworkFile netFile = ContentNetworkFile.createFile(transactionService, nodeService, contentService, cifsHelper, nodeRef, params);
NodeRef linkRef = (NodeRef) nodeService.getProperty(nodeRef, ContentModel.PROP_LINK_DESTINATION);
NetworkFile netFile = null;
if ( linkRef == null)
{
// Create the network file
netFile = ContentNetworkFile.createFile(transactionService, nodeService, contentService, cifsHelper, nodeRef, params);
}
else
{
// 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( sess.getServer().getServerName());
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 = cifsHelper.getFileInformation( nodeRef);
// 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
@ -1619,12 +1679,12 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
if (file.hasDeleteOnClose())
{
// Check if the file is a content file
// Check if the file is a noderef based file
if ( file instanceof ContentNetworkFile)
if ( file instanceof NodeRefNetworkFile)
{
ContentNetworkFile contentNetFile = (ContentNetworkFile) file;
NodeRef nodeRef = contentNetFile.getNodeRef();
NodeRefNetworkFile nodeNetFile = (NodeRefNetworkFile) file;
NodeRef nodeRef = nodeNetFile.getNodeRef();
// We don't know how long the network file has had the reference, so check for existence
@ -1776,14 +1836,22 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
ContentContext ctx = (ContentContext) tree.getContext();
// Get the file/folder to move
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);
NodeRef targetFolderRef = getNodeForPath(tree, splitPaths[0]);
String name = splitPaths[1]; // the new file or folder name
String name = splitPaths[1];
// Update the state table
boolean relinked = false;
if ( ctx.hasStateTable())
{
@ -2195,6 +2263,44 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
return cifsHelper.getNodeRef(ctx.getRootNode(), path);
}
/**
* Convert a node into a share relative path
*
* @param tree TreeConnection
* @param nodeRef NodeRef
* @return String
* @exception FileNotFoundException
*/
public String getPathForNode( TreeConnection tree, NodeRef nodeRef)
throws FileNotFoundException
{
// Convert the target node to a path
ContentContext ctx = (ContentContext) tree.getContext();
List<org.alfresco.service.cmr.model.FileInfo> 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();
}
/**
* Get the file state for the specified path
*

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2005-2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.smb.server.repo;
import org.alfresco.filesys.server.filesys.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Content Disk Driver File Info Class
*
* <p>Adds fields for the file/folder NodeRef, and linked NodeRef for a link node.
*
* @author gkspencer
*/
public class ContentFileInfo extends FileInfo {
// Version id
private static final long serialVersionUID = 2518699645372408663L;
// File/folder node
private NodeRef m_nodeRef;
// Linked node
private NodeRef m_linkRef;
/**
* Return the file/folder node
*
* @return NodeRef
*/
public final NodeRef getNodeRef()
{
return m_nodeRef;
}
/**
* Check if this is a link node
*
* @return boolean
*/
public final boolean isLinkNode()
{
return m_linkRef != null ? true : false;
}
/**
* Return the link node, or null if this is not a link
*
* @return NodeRef
*/
public final NodeRef getLinkNodeRef()
{
return m_linkRef;
}
/**
* Set the node for this file/folder
*
* @param node NodeRef
*/
public final void setNodeRef(NodeRef node)
{
m_nodeRef = node;
}
/**
* Set the link node
*
* @param link NodeRef
*/
public final void setLinkNodeRef(NodeRef link)
{
m_linkRef = link;
}
}

View File

@ -175,6 +175,20 @@ public class ContentIOControlHandler implements IOControlHandler
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();
@ -260,7 +274,7 @@ public class ContentIOControlHandler implements IOControlHandler
retBuffer = procRunAction(sess, tree, dataBuf, folderNode, netFile);
break;
// Unknown I/O control code
default:

View File

@ -50,14 +50,13 @@ import org.apache.commons.logging.LogFactory;
*
* @author Derek Hulley
*/
public class ContentNetworkFile extends NetworkFile
public class ContentNetworkFile extends NodeRefNetworkFile
{
private static final Log logger = LogFactory.getLog(ContentNetworkFile.class);
private TransactionService transactionService;
private NodeService nodeService;
private ContentService contentService;
private NodeRef nodeRef;
// File channel to file content
@ -175,12 +174,11 @@ public class ContentNetworkFile extends NetworkFile
NodeRef nodeRef,
String name)
{
super(name);
super(name, nodeRef);
setFullName(name);
this.transactionService = transactionService;
this.nodeService = nodeService;
this.contentService = contentService;
this.nodeRef = nodeRef;
}
/**
@ -193,7 +191,7 @@ public class ContentNetworkFile extends NetworkFile
StringBuilder str = new StringBuilder();
str.append( "[");
str.append( nodeRef.getId());
str.append( getNodeRef().getId());
str.append( ",channel=");
str.append( channel);
if ( channel != null)
@ -205,14 +203,6 @@ public class ContentNetworkFile extends NetworkFile
return str.toString();
}
/**
* @return Returns the node reference representing this file
*/
public NodeRef getNodeRef()
{
return nodeRef;
}
/**
* @return Returns true if the channel should be writable
*
@ -304,7 +294,7 @@ public class ContentNetworkFile extends NetworkFile
{
// Get a writeable channel to the content
content = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, false);
content = contentService.getWriter( getNodeRef(), ContentModel.PROP_CONTENT, false);
// Indicate that we have a writable channel to the file
@ -318,14 +308,14 @@ public class ContentNetworkFile extends NetworkFile
{
// Get a read-only channel to the content
content = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
content = contentService.getReader( getNodeRef(), ContentModel.PROP_CONTENT);
// Ensure that the content we are going to read is valid
content = FileContentReader.getSafeContentReader(
(ContentReader) content,
I18NUtil.getMessage(FileContentReader.MSG_MISSING_CONTENT),
nodeRef, content);
getNodeRef(), content);
// Indicate that we only have a read-only channel to the data
@ -372,7 +362,7 @@ public class ContentNetworkFile extends NetworkFile
// Update node properties
ContentData contentData = content.getContentData();
nodeService.setProperty(nodeRef, ContentModel.PROP_CONTENT, contentData);
nodeService.setProperty( getNodeRef(), ContentModel.PROP_CONTENT, contentData);
}
else
{

View File

@ -19,6 +19,7 @@ package org.alfresco.filesys.smb.server.repo;
import java.io.FileNotFoundException;
import java.util.List;
import org.alfresco.filesys.server.filesys.FileAttribute;
import org.alfresco.filesys.server.filesys.FileInfo;
import org.alfresco.filesys.server.filesys.FileName;
import org.alfresco.filesys.server.filesys.SearchContext;
@ -37,8 +38,18 @@ import org.apache.commons.logging.LogFactory;
*/
public class ContentSearchContext extends SearchContext
{
// Debug logging
private static final Log logger = LogFactory.getLog(ContentSearchContext.class);
// Constants
//
// Link file size, actual size will be set if/when the link is opened
public final static int LinkFileSize = 512;
// List of nodes returned from the folder search
private CifsHelper cifsHelper;
private List<NodeRef> results;
private int index = -1;
@ -193,7 +204,7 @@ public class ContentSearchContext extends SearchContext
{
// Get the file information and copy across to the callers file info
FileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, "");
ContentFileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, "");
info.copyFrom(nextInfo);
// Generate a file id for the current file
@ -203,6 +214,20 @@ public class ContentSearchContext extends SearchContext
info.setFileId( pathStr.toString().hashCode());
// Check if this is a link node
if ( nextInfo.isLinkNode())
{
// Set a dummy file size for the link data that will be generated if/when the file is opened
info.setFileSize( LinkFileSize);
// Make the link read-only
if ( info.isReadOnly() == false)
info.setFileAttributes( info.getFileAttributes() + FileAttribute.ReadOnly);
}
// Indicate that the file information is valid
return true;

View File

@ -0,0 +1,248 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.smb.server.repo;
import java.io.IOException;
import org.alfresco.filesys.server.filesys.FileInfo;
import org.alfresco.filesys.smb.SeekType;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Link Node In Memory Network File Class
*
* <p>In memory network file implementation that uses a memory buffer for the file data.
*
* @author gkspencer
*/
public class LinkMemoryNetworkFile extends NodeRefNetworkFile
{
// Current file position
private long m_filePos;
// File data
private byte[] m_data;
/**
* Class constructor.
*
* @param name String
* @param localPath String
* @param finfo FileInfo
* @param nodeRef NodeRef
*/
public LinkMemoryNetworkFile(String name, byte[] data, FileInfo finfo, NodeRef nodeRef)
{
super( name, nodeRef);
// Set the file data
m_data = data;
if ( m_data == null)
m_data = new byte[0];
// Set the file size
setFileSize( m_data.length);
// Set the creation and modification date/times
setModifyDate( finfo.getModifyDateTime());
setCreationDate( finfo.getCreationDateTime());
// Set the file id and relative path
if ( finfo.getPath() != null)
{
setFileId( finfo.getPath().hashCode());
setFullName( finfo.getPath());
}
}
/**
* Close the network file.
*/
public void closeFile() throws java.io.IOException
{
// Nothing to do
}
/**
* Return the current file position.
*
* @return long
*/
public long currentPosition()
{
return m_filePos;
}
/**
* Flush the file.
*
* @exception IOException
*/
public void flushFile() throws IOException
{
// Nothing to do
}
/**
* Determine if the end of file has been reached.
*
* @return boolean
*/
public boolean isEndOfFile() throws java.io.IOException
{
// Check if we reached end of file
if ( m_filePos == m_data.length)
return true;
return false;
}
/**
* Open the file.
*
* @param createFlag boolean
* @exception IOException
*/
public void openFile(boolean createFlag) throws java.io.IOException
{
// Indicate that the file is open
setClosed(false);
}
/**
* 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[] buf, int len, int pos, long fileOff) throws java.io.IOException
{
// Check if the read is within the file data range
long fileLen = (long) m_data.length;
if ( fileOff >= fileLen)
return 0;
// Calculate the actual read length
if (( fileOff + len) > fileLen)
len = (int) ( fileLen - fileOff);
// Copy the data to the user buffer
System.arraycopy( m_data, (int) fileOff, buf, pos, len);
// Update the current file position
m_filePos = fileOff + len;
// Return the actual length of data read
return len;
}
/**
* Seek to the specified file position.
*
* @param pos long
* @param typ int
* @return long
* @exception IOException
*/
public long seekFile(long pos, int typ) throws IOException
{
// Seek to the required file position
switch (typ)
{
// From start of file
case SeekType.StartOfFile:
if (currentPosition() != pos)
m_filePos = pos;
break;
// From current position
case SeekType.CurrentPos:
m_filePos += pos;
break;
// From end of file
case SeekType.EndOfFile:
m_filePos += pos;
if ( m_filePos < 0)
m_filePos = 0L;
break;
}
// Return the new file position
return currentPosition();
}
/**
* Truncate the file
*
* @param siz long
* @exception IOException
*/
public void truncateFile(long siz) throws IOException
{
// Allow the truncate, do not alter the pseduo file data
}
/**
* Write a block of data to the file.
*
* @param buf byte[]
* @param len int
* @exception IOException
*/
public void writeFile(byte[] buf, int len, int pos) throws java.io.IOException
{
// Allow the write, just do not do anything
}
/**
* Write a block of data to the file.
*
* @param buf byte[]
* @param len int
* @param pos int
* @param offset long
* @exception IOException
*/
public void writeFile(byte[] buf, int len, int pos, long offset) throws java.io.IOException
{
// Allow the write, just do not do anything
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2005-2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.filesys.smb.server.repo;
import org.alfresco.filesys.server.filesys.NetworkFile;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* NodeRef Based Network File Class
*
* @author gkspencer
*/
public abstract class NodeRefNetworkFile extends NetworkFile {
// Associated node ref
protected NodeRef m_nodeRef;
/**
* Create a network file object with the specified file/directory name.
*
* @param name File name string.
*/
public NodeRefNetworkFile(String name)
{
super( name);
}
/**
* Create a network file object with the specified file/directory name.
*
* @param name File name string.
* @param node NodeRef
*/
public NodeRefNetworkFile(String name, NodeRef node)
{
super( name);
m_nodeRef = node;
}
/**
* Return the node ref
*
* @return NodeRef
*/
public NodeRef getNodeRef()
{
return m_nodeRef;
}
/**
* set the node ref
*
* @param nodeRef NodeRef
*/
public void setNodeRef( NodeRef nodeRef)
{
m_nodeRef = nodeRef;
}
}

View File

@ -0,0 +1,223 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.admin.patch.impl;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Date;
import java.util.List;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.patch.AbstractPatch;
import org.alfresco.repo.domain.hibernate.NodeImpl;
import org.alfresco.repo.node.db.NodeDaoService;
import org.alfresco.service.cmr.admin.PatchException;
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
/**
* Checks that all names do not end with ' ' or '.'
*
* @author David Caruana
*/
public class LinkNodeFileExtensionPatch extends AbstractPatch
{
private static final String MSG_SUCCESS = "patch.linkNodeExtension.result";
private static final String MSG_REWRITTEN = "patch.linkNodeExtension.rewritten";
private static final String ERR_UNABLE_TO_FIX = "patch.linkNodeExtension.err.unable_to_fix";
private SessionFactory sessionFactory;
private NodeDaoService nodeDaoService;
/**
* Default constructor
*
*/
public LinkNodeFileExtensionPatch()
{
}
/**
* Set the session factory
*
* @param sessionFactory SessionFactory
*/
public void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
/**
* @param nodeDaoService The service that generates the CRC values
*/
public void setNodeDaoService(NodeDaoService nodeDaoService)
{
this.nodeDaoService = nodeDaoService;
}
@Override
protected void checkProperties()
{
super.checkProperties();
checkPropertyNotNull(sessionFactory, "sessionFactory");
checkPropertyNotNull(nodeDaoService, "nodeDaoService");
}
@Override
protected String applyInternal() throws Exception
{
// Initialise the helper
HibernateHelper helper = new HibernateHelper();
helper.setSessionFactory(sessionFactory);
try
{
// Fix the link node file names
return helper.fixNames();
}
finally
{
helper.closeWriter();
}
}
private class HibernateHelper extends HibernateDaoSupport
{
private File logFile;
private FileChannel channel;
private HibernateHelper() throws IOException
{
// Open a log file
logFile = new File("./LinkNodeExtensionPatch.log");
RandomAccessFile outputFile = new RandomAccessFile(logFile, "rw");
channel = outputFile.getChannel();
// Append to the end of the file
channel.position(channel.size());
writeLine("").writeLine("");
writeLine("LinkNodeExtensionPatch executing on " + new Date());
}
private HibernateHelper write(Object obj) throws IOException
{
channel.write(ByteBuffer.wrap(obj.toString().getBytes()));
return this;
}
private HibernateHelper writeLine(Object obj) throws IOException
{
write(obj);
write("\n");
return this;
}
private void closeWriter()
{
try { channel.close(); } catch (Throwable e) {}
}
public String fixNames() throws Exception
{
// Get the list of nodes to be updated
@SuppressWarnings("unused")
List<NodeImpl> nodes = getInvalidNames();
int updated = 0;
for (NodeImpl node : nodes)
{
// Check that the node is a link node
NodeRef nodeRef = node.getNodeRef();
if ( nodeService.getProperty(nodeRef, ContentModel.PROP_LINK_DESTINATION) != null)
{
// Get the current file name
String name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
if (name != null && name.length() >= 4 && name.endsWith(".lnk"))
{
// Update the name string, replace '.lnk' with '.url'
String updatedName = name.substring(0, name.length() - 4) + ".url";
int idx = 0;
boolean applied = false;
while (!applied)
{
try
{
nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, updatedName);
applied = true;
}
catch(DuplicateChildNodeNameException e)
{
idx++;
if (idx > 10)
{
writeLine(I18NUtil.getMessage(ERR_UNABLE_TO_FIX, name, updatedName));
throw new PatchException(ERR_UNABLE_TO_FIX, logFile);
}
updatedName += "_" + idx;
}
}
writeLine(I18NUtil.getMessage(MSG_REWRITTEN, name ,updatedName));
updated++;
getSession().flush();
getSession().clear();
}
}
}
String msg = I18NUtil.getMessage(MSG_SUCCESS, updated, logFile);
return msg;
}
@SuppressWarnings("unchecked")
private List<NodeImpl> getInvalidNames()
{
HibernateCallback callback = new HibernateCallback()
{
public Object doInHibernate(Session session)
{
Query query = session
.createQuery(
"select node from org.alfresco.repo.domain.hibernate.NodeImpl as node " +
"join node.properties prop where " +
" prop.stringValue like '%.lnk' ");
return query.list();
}
};
List<NodeImpl> results = (List<NodeImpl>) getHibernateTemplate().execute(callback);
return results;
}
}
}

View File

@ -68,7 +68,8 @@ public class FileFolderServiceImpl implements FileFolderService
"./*" +
"[like(@cm:name, $cm:name, false)" +
" and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" +
" and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]";
" and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "')" +
" and (subtypeOf('" + ContentModel.TYPE_LINK + "'))]";
/** Shallow search for all files and folders */
private static final String LUCENE_QUERY_SHALLOW_ALL =
@ -77,6 +78,7 @@ public class FileFolderServiceImpl implements FileFolderService
"+(" +
"TYPE:\"" + ContentModel.TYPE_CONTENT + "\" " +
"TYPE:\"" + ContentModel.TYPE_FOLDER + "\" " +
"TYPE:\"" + ContentModel.TYPE_LINK + "\" " +
")";
/** Shallow search for all files and folders */
@ -96,7 +98,8 @@ public class FileFolderServiceImpl implements FileFolderService
".//*" +
"[like(@cm:name, $cm:name, false)" +
" and not (subtypeOf('" + ContentModel.TYPE_SYSTEM_FOLDER + "'))" +
" and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "'))]";
" and (subtypeOf('" + ContentModel.TYPE_FOLDER + "') or subtypeOf('" + ContentModel.TYPE_CONTENT + "')" +
" and (subtypeOf('" + ContentModel.TYPE_LINK + "'))]";
/** empty parameters */
private static final QueryParameterDefinition[] PARAMS_ANY_NAME = new QueryParameterDefinition[1];
@ -246,7 +249,8 @@ public class FileFolderServiceImpl implements FileFolderService
}
return true;
}
else if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT))
else if (dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT) ||
dictionaryService.isSubClass(typeQName, ContentModel.TYPE_LINK))
{
// it is a regular file
return false;

View File

@ -35,7 +35,9 @@ import org.alfresco.service.namespace.QName;
public class FileInfoImpl implements FileInfo
{
private NodeRef nodeRef;
private NodeRef linkNodeRef;
private boolean isFolder;
private boolean isLink;
private Map<QName, Serializable> properties;
/**
@ -46,6 +48,14 @@ public class FileInfoImpl implements FileInfo
this.nodeRef = nodeRef;
this.isFolder = isFolder;
this.properties = properties;
// Check if this is a link node
if ( properties.containsKey( ContentModel.PROP_LINK_DESTINATION))
{
isLink = true;
linkNodeRef = (NodeRef) properties.get( ContentModel.PROP_LINK_DESTINATION);
}
}
@Override
@ -55,8 +65,15 @@ public class FileInfoImpl implements FileInfo
sb.append("FileInfo")
.append("[name=").append(getName())
.append(", isFolder=").append(isFolder)
.append(", nodeRef=").append(nodeRef)
.append("]");
.append(", nodeRef=").append(nodeRef);
if ( isLink())
{
sb.append(", linkref=");
sb.append(linkNodeRef);
}
sb.append("]");
return sb.toString();
}
@ -70,6 +87,16 @@ public class FileInfoImpl implements FileInfo
return isFolder;
}
public boolean isLink()
{
return isLink;
}
public NodeRef getLinkNodeRef()
{
return linkNodeRef;
}
public String getName()
{
return (String) properties.get(ContentModel.PROP_NAME);

View File

@ -43,6 +43,16 @@ public interface FileInfo
*/
public boolean isFolder();
/**
* @return true if this instance represents a link to a node
*/
public boolean isLink();
/**
* @return Return the reference to the node that this node is linked to
*/
public NodeRef getLinkNodeRef();
/**
* @return Returns the name of the file or folder within the parent folder
*/