mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Replaced the file server code with the Alfresco JLAN project.
Restructured the file server code packages. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@7757 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
676
source/java/org/alfresco/filesys/repo/CifsHelper.java
Normal file
676
source/java/org/alfresco/filesys/repo/CifsHelper.java
Normal file
@@ -0,0 +1,676 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.FileNotFoundException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.jlan.server.filesys.FileAttribute;
|
||||
import org.alfresco.jlan.server.filesys.FileExistsException;
|
||||
import org.alfresco.jlan.server.filesys.FileName;
|
||||
import org.alfresco.jlan.util.WildCard;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.model.FileFolderService;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
||||
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.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.SearchLanguageConversion;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Class with supplying helper methods and potentially acting as a cache for
|
||||
* queries.
|
||||
*
|
||||
* @author derekh
|
||||
*/
|
||||
public class CifsHelper
|
||||
{
|
||||
// Logging
|
||||
private static Log logger = LogFactory.getLog(CifsHelper.class);
|
||||
|
||||
// Services
|
||||
private DictionaryService dictionaryService;
|
||||
private NodeService nodeService;
|
||||
private FileFolderService fileFolderService;
|
||||
private MimetypeService mimetypeService;
|
||||
private PermissionService permissionService;
|
||||
private boolean isReadOnly;
|
||||
|
||||
// Mark locked files as offline
|
||||
|
||||
private boolean lockedFilesAsOffline;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public CifsHelper()
|
||||
{
|
||||
isReadOnly = false;
|
||||
}
|
||||
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
public void setFileFolderService(FileFolderService fileFolderService)
|
||||
{
|
||||
this.fileFolderService = fileFolderService;
|
||||
}
|
||||
|
||||
public void setMimetypeService(MimetypeService mimetypeService)
|
||||
{
|
||||
this.mimetypeService = mimetypeService;
|
||||
}
|
||||
|
||||
public void setPermissionService(PermissionService permissionService)
|
||||
{
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns true if all files/folders should be treated as read-only
|
||||
*/
|
||||
public boolean isReadOnly()
|
||||
{
|
||||
return isReadOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the system allows files to be edited or not. The default is
|
||||
* to allow writes.
|
||||
* @param allowWrites true to allow writes, otherwise false for read-only mode
|
||||
*/
|
||||
public void setAllowWrites(boolean allowWrites)
|
||||
{
|
||||
this.isReadOnly = !allowWrites;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable marking of locked files as offline
|
||||
*
|
||||
* @param ena boolean
|
||||
*/
|
||||
public final void setMarkLockedFilesAsOffline(boolean ena)
|
||||
{
|
||||
lockedFilesAsOffline = ena;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if locked files should be marked as offline
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasLockedFilesAsOffline()
|
||||
{
|
||||
return lockedFilesAsOffline;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param serviceRegistry for repo connection
|
||||
* @param nodeRef
|
||||
* @return Returns true if the node is a subtype of {@link ContentModel#TYPE_FOLDER folder}
|
||||
* @throws AlfrescoRuntimeException if the type is neither related to a folder or content
|
||||
*/
|
||||
public boolean isDirectory(NodeRef nodeRef)
|
||||
{
|
||||
QName nodeTypeQName = nodeService.getType(nodeRef);
|
||||
if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_FOLDER))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (dictionaryService.isSubClass(nodeTypeQName, ContentModel.TYPE_CONTENT))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// it is not a directory, but what is it?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract a single node's file info, where the node is reference by
|
||||
* a path relative to an ancestor node.
|
||||
*
|
||||
* @param pathRootNodeRef
|
||||
* @param path
|
||||
* @return Returns the existing node reference
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public ContentFileInfo getFileInformation(NodeRef pathRootNodeRef, String path) throws FileNotFoundException
|
||||
{
|
||||
// get the node being referenced
|
||||
NodeRef nodeRef = getNodeRef(pathRootNodeRef, path);
|
||||
|
||||
return getFileInformation(nodeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to extract file info from a specific node.
|
||||
* <p>
|
||||
* This method goes direct to the repo for all information and no data is
|
||||
* cached here.
|
||||
*
|
||||
* @param nodeRef the node that the path is relative to
|
||||
* @param path the path to get info for
|
||||
* @return Returns the file information pertinent to the node
|
||||
* @throws FileNotFoundException if the path refers to a non-existent file
|
||||
*/
|
||||
public ContentFileInfo getFileInformation(NodeRef nodeRef) throws FileNotFoundException
|
||||
{
|
||||
// get the file info
|
||||
org.alfresco.service.cmr.model.FileInfo fileFolderInfo = fileFolderService.getFileInfo(nodeRef);
|
||||
|
||||
// retrieve required properties and create file info
|
||||
ContentFileInfo fileInfo = new ContentFileInfo();
|
||||
fileInfo.setNodeRef(nodeRef);
|
||||
|
||||
// unset all attribute flags
|
||||
int fileAttributes = 0;
|
||||
fileInfo.setFileAttributes(fileAttributes);
|
||||
|
||||
if (fileFolderInfo.isFolder())
|
||||
{
|
||||
// add directory attribute
|
||||
fileAttributes |= FileAttribute.Directory;
|
||||
fileInfo.setFileAttributes(fileAttributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Map<QName, Serializable> nodeProperties = fileFolderInfo.getProperties();
|
||||
|
||||
// Get the file size from the content
|
||||
|
||||
ContentData contentData = (ContentData) nodeProperties.get(ContentModel.PROP_CONTENT);
|
||||
long size = 0L;
|
||||
if (contentData != null)
|
||||
{
|
||||
size = contentData.getSize();
|
||||
}
|
||||
fileInfo.setSize(size);
|
||||
|
||||
// Set the allocation size by rounding up the size to a 512 byte block boundary
|
||||
|
||||
if ( size > 0)
|
||||
fileInfo.setAllocationSize((size + 512L) & 0xFFFFFFFFFFFFFE00L);
|
||||
|
||||
// Check the lock status of the file
|
||||
|
||||
String lockTypeStr = (String) nodeProperties.get(ContentModel.PROP_LOCK_TYPE);
|
||||
|
||||
if ( lockTypeStr != null )
|
||||
{
|
||||
// File is locked so mark it as read-only and offline
|
||||
|
||||
int attr = fileInfo.getFileAttributes();
|
||||
|
||||
if (( attr & FileAttribute.ReadOnly) == 0)
|
||||
attr += FileAttribute.ReadOnly;
|
||||
|
||||
if ( hasLockedFilesAsOffline())
|
||||
attr += FileAttribute.NTOffline;
|
||||
|
||||
fileInfo.setFileAttributes( attr);
|
||||
}
|
||||
|
||||
// Check if it is a link node
|
||||
|
||||
if ( fileFolderInfo.isLink())
|
||||
fileInfo.setLinkNodeRef( fileFolderInfo.getLinkNodeRef());
|
||||
}
|
||||
|
||||
// created
|
||||
Date createdDate = fileFolderInfo.getCreatedDate();
|
||||
if (createdDate != null)
|
||||
{
|
||||
long created = DefaultTypeConverter.INSTANCE.longValue(createdDate);
|
||||
fileInfo.setCreationDateTime(created);
|
||||
}
|
||||
// modified
|
||||
Date modifiedDate = fileFolderInfo.getModifiedDate();
|
||||
if (modifiedDate != null)
|
||||
{
|
||||
long modified = DefaultTypeConverter.INSTANCE.longValue(modifiedDate);
|
||||
fileInfo.setModifyDateTime(modified);
|
||||
fileInfo.setAccessDateTime(modified);
|
||||
}
|
||||
// name
|
||||
String name = fileFolderInfo.getName();
|
||||
if (name != null)
|
||||
{
|
||||
fileInfo.setFileName(name);
|
||||
}
|
||||
|
||||
// Read/write access
|
||||
|
||||
boolean deniedPermission = permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED;
|
||||
if (isReadOnly || deniedPermission)
|
||||
{
|
||||
int attr = fileInfo.getFileAttributes();
|
||||
if (( attr & FileAttribute.ReadOnly) == 0)
|
||||
{
|
||||
attr += FileAttribute.ReadOnly;
|
||||
fileInfo.setFileAttributes(attr);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the normal file attribute if no other attributes are set
|
||||
|
||||
if ( fileInfo.getFileAttributes() == 0)
|
||||
fileInfo.setFileAttributes(FileAttribute.NTNormal);
|
||||
|
||||
// Debug
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Fetched file info: \n" +
|
||||
" info: " + fileInfo);
|
||||
}
|
||||
|
||||
// Return the file information
|
||||
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file or directory using the given paths.
|
||||
* <p>
|
||||
* If the directory path doesn't exist, then all the parent directories will be created.
|
||||
* If the file path is <code>null</code>, then the file will not be created
|
||||
*
|
||||
* @param rootNodeRef the root node of the path
|
||||
* @param path the path to a node
|
||||
* @param isFile true if the node to be created must be a file
|
||||
* @return Returns a newly created file or folder node
|
||||
* @throws FileExistsException if the file or folder already exists
|
||||
*/
|
||||
public NodeRef createNode(NodeRef rootNodeRef, String path, boolean isFile) throws FileExistsException
|
||||
{
|
||||
// split the path up into its constituents
|
||||
StringTokenizer tokenizer = new StringTokenizer(path, FileName.DOS_SEPERATOR_STR, false);
|
||||
List<String> folderPathElements = new ArrayList<String>(10);
|
||||
String name = null;
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
String pathElement = tokenizer.nextToken();
|
||||
|
||||
if (!tokenizer.hasMoreTokens())
|
||||
{
|
||||
// the last token becomes the name
|
||||
name = pathElement;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add the path element to the parent folder path
|
||||
folderPathElements.add(pathElement);
|
||||
}
|
||||
}
|
||||
// ensure that the folder path exists
|
||||
NodeRef parentFolderNodeRef = rootNodeRef;
|
||||
if (folderPathElements.size() > 0)
|
||||
{
|
||||
parentFolderNodeRef = fileFolderService.makeFolders(
|
||||
rootNodeRef,
|
||||
folderPathElements,
|
||||
ContentModel.TYPE_FOLDER).getNodeRef();
|
||||
}
|
||||
// add the file or folder
|
||||
QName typeQName = isFile ? ContentModel.TYPE_CONTENT : ContentModel.TYPE_FOLDER;
|
||||
try
|
||||
{
|
||||
NodeRef nodeRef = fileFolderService.create(parentFolderNodeRef, name, typeQName).getNodeRef();
|
||||
|
||||
// done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Created node: \n" +
|
||||
" device root: " + rootNodeRef + "\n" +
|
||||
" path: " + path + "\n" +
|
||||
" is file: " + isFile + "\n" +
|
||||
" new node: " + nodeRef);
|
||||
}
|
||||
return nodeRef;
|
||||
}
|
||||
catch (org.alfresco.service.cmr.model.FileExistsException e)
|
||||
{
|
||||
throw new FileExistsException(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void addDescendents(List<NodeRef> pathRootNodeRefs, Stack<String> pathElements, List<NodeRef> results)
|
||||
{
|
||||
if (pathElements.isEmpty())
|
||||
{
|
||||
// if this method is called with an empty path element stack, then the
|
||||
// current context nodes are the results to be added
|
||||
results.addAll(pathRootNodeRefs);
|
||||
return;
|
||||
}
|
||||
|
||||
// take the first path element off the stack
|
||||
String pathElement = pathElements.pop();
|
||||
|
||||
// iterate over each path root node
|
||||
for (NodeRef pathRootNodeRef : pathRootNodeRefs)
|
||||
{
|
||||
// deal with cyclic relationships by not traversing down any node already in the results
|
||||
if (results.contains(pathRootNodeRef))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// get direct descendents along the path
|
||||
List<NodeRef> directDescendents = getDirectDescendents(pathRootNodeRef, pathElement);
|
||||
// recurse onto the descendents
|
||||
addDescendents(directDescendents, pathElements, results);
|
||||
}
|
||||
|
||||
// restore the path element stack
|
||||
pathElements.push(pathElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for the node or nodes that match the path element for the given parent node
|
||||
*/
|
||||
private List<NodeRef> getDirectDescendents(NodeRef pathRootNodeRef, String pathElement)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Getting direct descendents: \n" +
|
||||
" Path Root: " + pathRootNodeRef + "\n" +
|
||||
" Path Element: " + pathElement);
|
||||
}
|
||||
List<NodeRef> results = null;
|
||||
// if this contains no wildcards, then we can fasttrack it
|
||||
if (!WildCard.containsWildcards(pathElement))
|
||||
{
|
||||
// a specific name is required
|
||||
NodeRef foundNodeRef = fileFolderService.searchSimple(pathRootNodeRef, pathElement);
|
||||
if (foundNodeRef == null)
|
||||
{
|
||||
results = Collections.emptyList();
|
||||
}
|
||||
else
|
||||
{
|
||||
results = Collections.singletonList(foundNodeRef);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// escape for the Lucene syntax search
|
||||
String escapedPathElement = SearchLanguageConversion.convertCifsToLucene(pathElement);
|
||||
// do the lookup
|
||||
List<org.alfresco.service.cmr.model.FileInfo> childInfos = fileFolderService.search(
|
||||
pathRootNodeRef,
|
||||
escapedPathElement,
|
||||
false);
|
||||
// convert to noderefs
|
||||
results = new ArrayList<NodeRef>(childInfos.size());
|
||||
for (org.alfresco.service.cmr.model.FileInfo info : childInfos)
|
||||
{
|
||||
results.add(info.getNodeRef());
|
||||
}
|
||||
}
|
||||
// done
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the nodes being reference by the given directory and file paths.
|
||||
* <p>
|
||||
* Examples of the path are:
|
||||
* <ul>
|
||||
* <li>\New Folder\New Text Document.txt</li>
|
||||
* <li>\New Folder\Sub Folder</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param searchRootNodeRef the node from which to start the path search
|
||||
* @param path the search path to either a folder or file
|
||||
* @return Returns references to all matching nodes
|
||||
*/
|
||||
public List<NodeRef> getNodeRefs(NodeRef pathRootNodeRef, String path)
|
||||
{
|
||||
// tokenize the path and push into a stack in reverse order so that
|
||||
// the root directory gets popped first
|
||||
StringTokenizer tokenizer = new StringTokenizer(path, FileName.DOS_SEPERATOR_STR, false);
|
||||
String[] tokens = new String[tokenizer.countTokens()];
|
||||
int count = 0;
|
||||
while(tokenizer.hasMoreTokens())
|
||||
{
|
||||
tokens[count] = tokenizer.nextToken();
|
||||
count++;
|
||||
}
|
||||
Stack<String> pathElements = new Stack<String>();
|
||||
for (int i = tokens.length - 1; i >= 0; i--)
|
||||
{
|
||||
pathElements.push(tokens[i]);
|
||||
}
|
||||
|
||||
// start with a single parent node
|
||||
List<NodeRef> pathRootNodeRefs = Collections.singletonList(pathRootNodeRef);
|
||||
|
||||
// result storage
|
||||
List<NodeRef> results = new ArrayList<NodeRef>(5);
|
||||
|
||||
// kick off the path walking
|
||||
addDescendents(pathRootNodeRefs, pathElements, results);
|
||||
|
||||
// done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Retrieved node references for path: \n" +
|
||||
" path root: " + pathRootNodeRef + "\n" +
|
||||
" path: " + path + "\n" +
|
||||
" results: " + results);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to fetch a specific single node at the given path.
|
||||
*
|
||||
* @throws FileNotFoundException if the path can't be resolved to a node
|
||||
*
|
||||
* @see #getNodeRefs(NodeRef, String)
|
||||
*/
|
||||
public NodeRef getNodeRef(NodeRef pathRootNodeRef, String path) throws FileNotFoundException
|
||||
{
|
||||
// attempt to get the file/folder node using hierarchy walking
|
||||
List<NodeRef> nodeRefs = getNodeRefs(pathRootNodeRef, path);
|
||||
if (nodeRefs.size() == 0)
|
||||
{
|
||||
throw new FileNotFoundException(path);
|
||||
}
|
||||
else if (nodeRefs.size() > 1)
|
||||
{
|
||||
logger.warn("Multiple matching nodes: \n" +
|
||||
" search root: " + pathRootNodeRef + "\n" +
|
||||
" path: " + path);
|
||||
}
|
||||
// take the first one - not sure if it is possible for the path to refer to more than one
|
||||
NodeRef nodeRef = nodeRefs.get(0);
|
||||
// done
|
||||
return nodeRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relink the content data from a new node to an existing node to preserve the version history.
|
||||
*
|
||||
* @param oldNodeRef NodeRef
|
||||
* @param newNodeRef NodeRef
|
||||
*/
|
||||
public void relinkNode(NodeRef tempNodeRef, NodeRef nodeToMoveRef, NodeRef newParentNodeRef, String newName)
|
||||
throws FileNotFoundException, FileExistsException
|
||||
{
|
||||
// Get the properties for the old and new nodes
|
||||
org.alfresco.service.cmr.model.FileInfo tempFileInfo = fileFolderService.getFileInfo(tempNodeRef);
|
||||
org.alfresco.service.cmr.model.FileInfo fileToMoveInfo = fileFolderService.getFileInfo(nodeToMoveRef);
|
||||
|
||||
// Save the current name of the old node
|
||||
String tempName = tempFileInfo.getName();
|
||||
|
||||
try
|
||||
{
|
||||
// remove the tempory aspects from the nodes, this will be reapplied if the new name dictates it
|
||||
nodeService.removeAspect(tempNodeRef, ContentModel.ASPECT_TEMPORARY);
|
||||
|
||||
// rename temp file to the new name
|
||||
fileFolderService.rename(tempNodeRef, newName);
|
||||
|
||||
// rename new file to old name
|
||||
fileFolderService.rename(nodeToMoveRef, tempName);
|
||||
this.nodeService.addAspect(nodeToMoveRef, ContentModel.ASPECT_TEMPORARY, null);
|
||||
}
|
||||
catch (org.alfresco.service.cmr.model.FileNotFoundException e)
|
||||
{
|
||||
throw new FileNotFoundException(e.getMessage());
|
||||
}
|
||||
catch (org.alfresco.service.cmr.model.FileExistsException e)
|
||||
{
|
||||
throw new FileExistsException(e.getMessage());
|
||||
}
|
||||
|
||||
if (!tempFileInfo.isFolder() && !fileToMoveInfo.isFolder())
|
||||
{
|
||||
// swap the content between the two
|
||||
ContentData oldContentData = tempFileInfo.getContentData();
|
||||
if (oldContentData == null)
|
||||
{
|
||||
String mimetype = mimetypeService.guessMimetype(tempName);
|
||||
oldContentData = ContentData.setMimetype(null, mimetype);
|
||||
}
|
||||
|
||||
ContentData newContentData = fileToMoveInfo.getContentData();
|
||||
|
||||
// Reset the mime type
|
||||
|
||||
String mimetype = mimetypeService.guessMimetype(newName);
|
||||
newContentData = ContentData.setMimetype(newContentData, mimetype);
|
||||
|
||||
nodeService.setProperty(tempNodeRef, ContentModel.PROP_CONTENT, newContentData);
|
||||
nodeService.setProperty(nodeToMoveRef, ContentModel.PROP_CONTENT, oldContentData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a node
|
||||
*
|
||||
* @param nodeToMoveRef Node to be moved
|
||||
* @param newParentNodeRef New parent folder node
|
||||
* @param newName New name for the moved node
|
||||
* @throws FileExistsException
|
||||
*/
|
||||
public void move(NodeRef nodeToMoveRef, NodeRef newParentNodeRef, String newName) throws FileExistsException
|
||||
{
|
||||
try
|
||||
{
|
||||
fileFolderService.move(nodeToMoveRef, newParentNodeRef, newName);
|
||||
}
|
||||
catch (org.alfresco.service.cmr.model.FileExistsException e)
|
||||
{
|
||||
throw new FileExistsException(newName);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Move failed: \n" +
|
||||
" node to move: " + nodeToMoveRef + "\n" +
|
||||
" new parent: " + newParentNodeRef + "\n" +
|
||||
" new name: " + newName,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a node
|
||||
*
|
||||
* @param nodeToRenameRef Node to be renamed
|
||||
* @param newName New name for the node
|
||||
* @throws FileExistsException
|
||||
*/
|
||||
public void rename(NodeRef nodeToRenameRef, String newName) throws FileExistsException
|
||||
{
|
||||
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)
|
||||
{
|
||||
throw new FileExistsException(newName);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Rename failed: \n" +
|
||||
" node to rename: " + nodeToRenameRef + "\n" +
|
||||
" new name: " + newName,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file name for a node
|
||||
*
|
||||
* @param node NodeRef
|
||||
* @return String
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public String getFileName(NodeRef node)
|
||||
{
|
||||
String fname = null;
|
||||
|
||||
try
|
||||
{
|
||||
fname = (String) nodeService.getProperty( node, ContentModel.PROP_NAME);
|
||||
}
|
||||
catch (InvalidNodeRefException ex)
|
||||
{
|
||||
}
|
||||
|
||||
return fname;
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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 org.alfresco.filesys.CIFSServerBean;
|
||||
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
|
||||
import org.alfresco.jlan.server.filesys.FilesystemsConfigSection;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.util.BaseAlfrescoTestCase;
|
||||
|
||||
/**
|
||||
* Checks that the required configuration details are obtainable from the CIFS components.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public class CifsIntegrationTest extends BaseAlfrescoTestCase
|
||||
{
|
||||
|
||||
public void testGetServerName()
|
||||
{
|
||||
CIFSServerBean cifsServer = (CIFSServerBean) ctx.getBean("cifsServer");
|
||||
assertNotNull("No CIFS server available", cifsServer);
|
||||
// the server might, quite legitimately, not start
|
||||
if (!cifsServer.isStarted())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// get the server name
|
||||
String serverName = cifsServer.getConfiguration().getServerName();
|
||||
assertNotNull("No server name available", serverName);
|
||||
assertTrue("No server name available (zero length)", serverName.length() > 0);
|
||||
|
||||
// Get the primary filesystem, might be null if the home folder mapper is configured
|
||||
|
||||
FilesystemsConfigSection filesysConfig = (FilesystemsConfigSection) cifsServer.getConfiguration().getConfigSection(FilesystemsConfigSection.SectionName);
|
||||
DiskSharedDevice mainFilesys = (DiskSharedDevice) filesysConfig.getShares().enumerateShares().nextElement();
|
||||
|
||||
if ( mainFilesys != null)
|
||||
{
|
||||
// Check the share name
|
||||
|
||||
String shareName = mainFilesys.getName();
|
||||
assertNotNull("No share name available", shareName);
|
||||
assertTrue("No share name available (zero length)", shareName.length() > 0);
|
||||
|
||||
// Check that the context is valid
|
||||
|
||||
ContentContext filesysCtx = (ContentContext) mainFilesys.getContext();
|
||||
assertNotNull("Content context is null", filesysCtx);
|
||||
assertNotNull("Store id is null", filesysCtx.getStoreName());
|
||||
assertNotNull("Root path is null", filesysCtx.getRootPath());
|
||||
assertNotNull("Root node is null", filesysCtx.getRootNode());
|
||||
|
||||
// Check the root node
|
||||
|
||||
NodeService nodeService = (NodeService) ctx.getBean(ServiceRegistry.NODE_SERVICE.getLocalName());
|
||||
// get the share root node and check that it exists
|
||||
NodeRef shareNodeRef = filesysCtx.getRootNode();
|
||||
assertNotNull("No share root node available", shareNodeRef);
|
||||
assertTrue("Share root node doesn't exist", nodeService.exists(shareNodeRef));
|
||||
}
|
||||
}
|
||||
}
|
133
source/java/org/alfresco/filesys/repo/ContentContext.java
Normal file
133
source/java/org/alfresco/filesys/repo/ContentContext.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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 org.alfresco.filesys.alfresco.AlfrescoContext;
|
||||
import org.alfresco.filesys.alfresco.IOControlHandler;
|
||||
import org.alfresco.jlan.server.filesys.DiskInterface;
|
||||
import org.alfresco.jlan.server.filesys.FileSystem;
|
||||
import org.alfresco.service.cmr.repository.*;
|
||||
|
||||
/**
|
||||
* Content Filesystem Context Class
|
||||
*
|
||||
* <p>Contains per filesystem context.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class ContentContext extends AlfrescoContext
|
||||
{
|
||||
// Store and root path
|
||||
|
||||
private String m_storeName;
|
||||
private String m_rootPath;
|
||||
|
||||
// Root node
|
||||
|
||||
private NodeRef m_rootNodeRef;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
*@param filesysName String
|
||||
* @param storeName String
|
||||
* @param rootPath String
|
||||
* @param rootNodeRef NodeRef
|
||||
*/
|
||||
public ContentContext(String filesysName, String storeName, String rootPath, NodeRef rootNodeRef)
|
||||
{
|
||||
super(filesysName, rootNodeRef.toString());
|
||||
|
||||
m_storeName = storeName;
|
||||
m_rootPath = rootPath;
|
||||
|
||||
m_rootNodeRef = rootNodeRef;
|
||||
|
||||
// Create the I/O control handler
|
||||
|
||||
setIOHandler( createIOHandler( null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filesystem type, either FileSystem.TypeFAT or FileSystem.TypeNTFS.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getFilesystemType()
|
||||
{
|
||||
return FileSystem.TypeNTFS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the store name
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public final String getStoreName()
|
||||
{
|
||||
return m_storeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the root path
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public final String getRootPath()
|
||||
{
|
||||
return m_rootPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the root node
|
||||
*
|
||||
* @return NodeRef
|
||||
*/
|
||||
public final NodeRef getRootNode()
|
||||
{
|
||||
return m_rootNodeRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the filesystem context
|
||||
*/
|
||||
public void CloseContext() {
|
||||
|
||||
// Call the base class
|
||||
|
||||
super.CloseContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the I/O control handler for this filesystem type
|
||||
*
|
||||
* @param filesysDriver DiskInterface
|
||||
* @return IOControlHandler
|
||||
*/
|
||||
protected IOControlHandler createIOHandler( DiskInterface filesysDriver)
|
||||
{
|
||||
return new ContentIOControlHandler();
|
||||
}
|
||||
}
|
2445
source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
Normal file
2445
source/java/org/alfresco/filesys/repo/ContentDiskDriver.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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 org.alfresco.jlan.server.filesys.DiskInterface;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* Extended {@link org.alfresco.filesys.server.filesys.DiskInterface disk interface} to
|
||||
* allow access to some of the internal configuration properties.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public interface ContentDiskInterface extends DiskInterface
|
||||
{
|
||||
/**
|
||||
* Get the name of the shared path within the server. The share name is
|
||||
* equivalent in browse path to the {@link #getContextRootNodeRef() context root}.
|
||||
*
|
||||
* @return Returns the share name
|
||||
*/
|
||||
public String getShareName();
|
||||
|
||||
/**
|
||||
* Get a reference to the node that all CIFS paths are relative to
|
||||
*
|
||||
* @return Returns a node acting as the CIFS root
|
||||
*/
|
||||
public NodeRef getContextRootNodeRef();
|
||||
}
|
98
source/java/org/alfresco/filesys/repo/ContentFileInfo.java
Normal file
98
source/java/org/alfresco/filesys/repo/ContentFileInfo.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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 org.alfresco.jlan.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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,794 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.FileNotFoundException;
|
||||
|
||||
import org.alfresco.filesys.alfresco.AlfrescoClientInfo;
|
||||
import org.alfresco.filesys.alfresco.AlfrescoContext;
|
||||
import org.alfresco.filesys.alfresco.AlfrescoDiskDriver;
|
||||
import org.alfresco.filesys.alfresco.DesktopAction;
|
||||
import org.alfresco.filesys.alfresco.DesktopActionTable;
|
||||
import org.alfresco.filesys.alfresco.DesktopParams;
|
||||
import org.alfresco.filesys.alfresco.DesktopResponse;
|
||||
import org.alfresco.filesys.alfresco.DesktopTarget;
|
||||
import org.alfresco.filesys.alfresco.IOControl;
|
||||
import org.alfresco.filesys.alfresco.IOControlHandler;
|
||||
import org.alfresco.jlan.server.SrvSession;
|
||||
import org.alfresco.jlan.server.auth.ClientInfo;
|
||||
import org.alfresco.jlan.server.filesys.IOControlNotImplementedException;
|
||||
import org.alfresco.jlan.server.filesys.NetworkFile;
|
||||
import org.alfresco.jlan.server.filesys.TreeConnection;
|
||||
import org.alfresco.jlan.smb.SMBException;
|
||||
import org.alfresco.jlan.smb.SMBStatus;
|
||||
import org.alfresco.jlan.smb.nt.NTIOCtl;
|
||||
import org.alfresco.jlan.util.DataBuffer;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationException;
|
||||
import org.alfresco.service.cmr.lock.LockType;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.AuthenticationService;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Content Disk Driver I/O Control Handler Class
|
||||
*
|
||||
* <p>Provides the custom I/O control code handling used by the CIFS client interface application.
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public class ContentIOControlHandler implements IOControlHandler
|
||||
{
|
||||
// Logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ContentIOControlHandler.class);
|
||||
|
||||
// Filesystem driver and context
|
||||
|
||||
private ContentDiskDriver contentDriver;
|
||||
private ContentContext contentContext;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public ContentIOControlHandler()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initalize the I/O control handler
|
||||
*
|
||||
* @param filesysDriver AlfrescoDiskDriver
|
||||
* @param context AlfrescoContext
|
||||
*/
|
||||
public void initialize( AlfrescoDiskDriver filesysDriver, AlfrescoContext context)
|
||||
{
|
||||
this.contentDriver = (ContentDiskDriver) filesysDriver;
|
||||
this.contentContext = (ContentContext) context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CIFS helper
|
||||
*
|
||||
* @return CifsHelper
|
||||
*/
|
||||
public final CifsHelper getCifsHelper()
|
||||
{
|
||||
return contentDriver.getCifsHelper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the authentication service
|
||||
*
|
||||
* @return AuthenticationService
|
||||
*/
|
||||
public final AuthenticationService getAuthenticationService()
|
||||
{
|
||||
return contentDriver.getAuthenticationService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction service
|
||||
*
|
||||
* @return TransactionService
|
||||
*/
|
||||
public final TransactionService getTransactionService()
|
||||
{
|
||||
return contentDriver.getTransactionService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the node service
|
||||
*
|
||||
* @return NodeService
|
||||
*/
|
||||
public final NodeService getNodeService()
|
||||
{
|
||||
return contentDriver.getNodeService();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filesystem driver
|
||||
*
|
||||
* @return ContentDiskDriver
|
||||
*/
|
||||
public final ContentDiskDriver getContentDriver()
|
||||
{
|
||||
return contentDriver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filesystem context
|
||||
*
|
||||
* @return ContentContext
|
||||
*/
|
||||
public final ContentContext getContentContext()
|
||||
{
|
||||
return contentContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a filesystem I/O control request
|
||||
*
|
||||
* @param sess Server session
|
||||
* @param tree Tree connection.
|
||||
* @param ctrlCode I/O control code
|
||||
* @param fid File id
|
||||
* @param dataBuf I/O control specific input data
|
||||
* @param isFSCtrl true if this is a filesystem control, or false for a device control
|
||||
* @param filter if bit0 is set indicates that the control applies to the share root handle
|
||||
* @return DataBuffer
|
||||
* @exception IOControlNotImplementedException
|
||||
* @exception SMBException
|
||||
*/
|
||||
public org.alfresco.jlan.util.DataBuffer processIOControl(SrvSession sess, TreeConnection tree, int ctrlCode, int fid, DataBuffer dataBuf,
|
||||
boolean isFSCtrl, int filter)
|
||||
throws IOControlNotImplementedException, SMBException
|
||||
{
|
||||
// Validate the file id
|
||||
|
||||
NetworkFile netFile = tree.findFile(fid);
|
||||
if ( netFile == null || netFile.isDirectory() == false)
|
||||
throw new SMBException(SMBStatus.NTErr, SMBStatus.NTInvalidParameter);
|
||||
|
||||
// Split the control code
|
||||
|
||||
int devType = NTIOCtl.getDeviceType(ctrlCode);
|
||||
int ioFunc = NTIOCtl.getFunctionCode(ctrlCode);
|
||||
|
||||
// Check for I/O controls that require a success status
|
||||
|
||||
if ( devType == NTIOCtl.DeviceFileSystem)
|
||||
{
|
||||
// I/O control requests that require a success status
|
||||
//
|
||||
// Create or get object id
|
||||
|
||||
if ( ioFunc == NTIOCtl.FsCtlCreateOrGetObjectId)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if the I/O control looks like a custom I/O control request
|
||||
|
||||
if ( devType != NTIOCtl.DeviceFileSystem || dataBuf == null)
|
||||
throw new IOControlNotImplementedException();
|
||||
|
||||
// Check if the request has a valid signature for an Alfresco CIFS server I/O control
|
||||
|
||||
if ( dataBuf.getLength() < IOControl.Signature.length())
|
||||
throw new IOControlNotImplementedException("Bad request length");
|
||||
|
||||
String sig = dataBuf.getString(IOControl.Signature.length(), false);
|
||||
|
||||
if ( sig == null || sig.compareTo(IOControl.Signature) != 0)
|
||||
throw new IOControlNotImplementedException("Bad request signature");
|
||||
|
||||
// Get the node for the parent folder, make sure it is a folder
|
||||
|
||||
NodeRef folderNode = null;
|
||||
|
||||
try
|
||||
{
|
||||
folderNode = contentDriver.getNodeForPath(tree, netFile.getFullName());
|
||||
|
||||
if ( getCifsHelper().isDirectory( folderNode) == false)
|
||||
folderNode = null;
|
||||
}
|
||||
catch ( FileNotFoundException ex)
|
||||
{
|
||||
folderNode = null;
|
||||
}
|
||||
|
||||
// If the folder node is not valid return an error
|
||||
|
||||
if ( folderNode == null)
|
||||
throw new SMBException(SMBStatus.NTErr, SMBStatus.NTAccessDenied);
|
||||
|
||||
// Debug
|
||||
|
||||
if ( logger.isDebugEnabled()) {
|
||||
logger.debug("IO control func=0x" + Integer.toHexString(ioFunc) + ", fid=" + fid + ", buffer=" + dataBuf);
|
||||
logger.debug(" Folder nodeRef=" + folderNode);
|
||||
}
|
||||
|
||||
// Check if the I/O control code is one of our custom codes
|
||||
|
||||
DataBuffer retBuffer = null;
|
||||
|
||||
switch ( ioFunc)
|
||||
{
|
||||
// Probe to check if this is an Alfresco CIFS server
|
||||
|
||||
case IOControl.CmdProbe:
|
||||
|
||||
// Return a buffer with the signature and protocol version
|
||||
|
||||
retBuffer = new DataBuffer(IOControl.Signature.length());
|
||||
retBuffer.putFixedString(IOControl.Signature, IOControl.Signature.length());
|
||||
retBuffer.putInt(DesktopAction.StsSuccess);
|
||||
retBuffer.putInt(IOControl.Version);
|
||||
break;
|
||||
|
||||
// Get file information for a file within the current folder
|
||||
|
||||
case IOControl.CmdFileStatus:
|
||||
|
||||
// Process the file status request
|
||||
|
||||
retBuffer = procIOFileStatus( sess, tree, dataBuf, folderNode);
|
||||
break;
|
||||
|
||||
// Get action information for the specified executable path
|
||||
|
||||
case IOControl.CmdGetActionInfo:
|
||||
|
||||
// Process the get action information request
|
||||
|
||||
retBuffer = procGetActionInfo(sess, tree, dataBuf, folderNode, netFile);
|
||||
break;
|
||||
|
||||
// Run the named action
|
||||
|
||||
case IOControl.CmdRunAction:
|
||||
|
||||
// Process the run action request
|
||||
|
||||
retBuffer = procRunAction(sess, tree, dataBuf, folderNode, netFile);
|
||||
break;
|
||||
|
||||
// Return the authentication ticket
|
||||
|
||||
case IOControl.CmdGetAuthTicket:
|
||||
|
||||
// Process the get auth ticket request
|
||||
|
||||
retBuffer = procGetAuthTicket(sess, tree, dataBuf, folderNode, netFile);
|
||||
break;
|
||||
|
||||
// Unknown I/O control code
|
||||
|
||||
default:
|
||||
throw new IOControlNotImplementedException();
|
||||
}
|
||||
|
||||
// Return the reply buffer, may be null
|
||||
|
||||
return retBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the file status I/O request
|
||||
*
|
||||
* @param sess Server session
|
||||
* @param tree Tree connection
|
||||
* @param reqBuf Request buffer
|
||||
* @param folderNode NodeRef of parent folder
|
||||
* @return DataBuffer
|
||||
*/
|
||||
private final DataBuffer procIOFileStatus( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode)
|
||||
{
|
||||
// Start a transaction
|
||||
|
||||
contentDriver.beginReadTransaction( sess);
|
||||
|
||||
// Get the file name from the request
|
||||
|
||||
String fName = reqBuf.getString( true);
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug(" File status, fname=" + fName);
|
||||
|
||||
// Create a response buffer
|
||||
|
||||
DataBuffer respBuf = new DataBuffer(256);
|
||||
respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
|
||||
|
||||
// Get the node for the file/folder
|
||||
|
||||
NodeRef childNode = null;
|
||||
|
||||
try
|
||||
{
|
||||
childNode = getCifsHelper().getNodeRef( folderNode, fName);
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
}
|
||||
|
||||
// Check if the file/folder was found
|
||||
|
||||
if ( childNode == null)
|
||||
{
|
||||
// Return an error response
|
||||
|
||||
respBuf.putInt(DesktopAction.StsFileNotFound);
|
||||
return respBuf;
|
||||
}
|
||||
|
||||
// Check if this is a file or folder node
|
||||
|
||||
if ( getCifsHelper().isDirectory( childNode))
|
||||
{
|
||||
// Only return the status and node type for folders
|
||||
|
||||
respBuf.putInt(DesktopAction.StsSuccess);
|
||||
respBuf.putInt(IOControl.TypeFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Indicate that this is a file node
|
||||
|
||||
respBuf.putInt(DesktopAction.StsSuccess);
|
||||
respBuf.putInt(IOControl.TypeFile);
|
||||
|
||||
// Check if this file is a working copy
|
||||
|
||||
if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_WORKING_COPY))
|
||||
{
|
||||
// Indicate that this is a working copy
|
||||
|
||||
respBuf.putInt(IOControl.True);
|
||||
|
||||
// Get the owner username and file it was copied from
|
||||
|
||||
String owner = (String) getNodeService().getProperty( childNode, ContentModel.PROP_WORKING_COPY_OWNER);
|
||||
String copiedFrom = null;
|
||||
|
||||
if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_COPIEDFROM))
|
||||
{
|
||||
// Get the path of the file the working copy was generated from
|
||||
|
||||
NodeRef fromNode = (NodeRef) getNodeService().getProperty( childNode, ContentModel.PROP_COPY_REFERENCE);
|
||||
if ( fromNode != null)
|
||||
copiedFrom = (String) getNodeService().getProperty( fromNode, ContentModel.PROP_NAME);
|
||||
}
|
||||
|
||||
// Pack the owner and copied from values
|
||||
|
||||
respBuf.putString(owner != null ? owner : "", true, true);
|
||||
respBuf.putString(copiedFrom != null ? copiedFrom : "", true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a working copy
|
||||
|
||||
respBuf.putInt(IOControl.False);
|
||||
}
|
||||
|
||||
// Check the lock status of the file
|
||||
|
||||
if ( getNodeService().hasAspect( childNode, ContentModel.ASPECT_LOCKABLE))
|
||||
{
|
||||
// Get the lock type and owner
|
||||
|
||||
String lockTypeStr = (String) getNodeService().getProperty( childNode, ContentModel.PROP_LOCK_TYPE);
|
||||
String lockOwner = null;
|
||||
|
||||
if ( lockTypeStr != null)
|
||||
lockOwner = (String) getNodeService().getProperty( childNode, ContentModel.PROP_LOCK_OWNER);
|
||||
|
||||
// Pack the lock type, and owner if there is a lock on the file
|
||||
|
||||
if ( lockTypeStr == null)
|
||||
respBuf.putInt(IOControl.LockNone);
|
||||
else
|
||||
{
|
||||
LockType lockType = LockType.valueOf( lockTypeStr);
|
||||
|
||||
respBuf.putInt(lockType == LockType.READ_ONLY_LOCK ? IOControl.LockRead : IOControl.LockWrite);
|
||||
respBuf.putString(lockOwner != null ? lockOwner : "", true, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// File is not lockable
|
||||
|
||||
respBuf.putInt(IOControl.LockNone);
|
||||
}
|
||||
|
||||
// Get the content data details for the file
|
||||
|
||||
ContentData contentData = (ContentData) getNodeService().getProperty( childNode, ContentModel.PROP_CONTENT);
|
||||
|
||||
if ( contentData != null)
|
||||
{
|
||||
// Get the content mime-type
|
||||
|
||||
String mimeType = contentData.getMimetype();
|
||||
|
||||
// Pack the content length and mime-type
|
||||
|
||||
respBuf.putInt( IOControl.True);
|
||||
respBuf.putLong( contentData.getSize());
|
||||
respBuf.putString( mimeType != null ? mimeType : "", true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// File does not have any content
|
||||
|
||||
respBuf.putInt( IOControl.False);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the response
|
||||
|
||||
return respBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the get action information request
|
||||
*
|
||||
* @param sess Server session
|
||||
* @param tree Tree connection
|
||||
* @param reqBuf Request buffer
|
||||
* @param folderNode NodeRef of parent folder
|
||||
* @param netFile NetworkFile for the folder
|
||||
* @return DataBuffer
|
||||
*/
|
||||
private final DataBuffer procGetActionInfo( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
|
||||
NetworkFile netFile)
|
||||
{
|
||||
// Get the executable file name from the request
|
||||
|
||||
String exeName = reqBuf.getString( true);
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug(" Get action info, exe=" + exeName);
|
||||
|
||||
// Create a response buffer
|
||||
|
||||
DataBuffer respBuf = new DataBuffer(256);
|
||||
respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
|
||||
|
||||
// Get the desktop actions list
|
||||
|
||||
DesktopActionTable deskActions = contentContext.getDesktopActions();
|
||||
if ( deskActions == null)
|
||||
{
|
||||
respBuf.putInt(DesktopAction.StsNoSuchAction);
|
||||
return respBuf;
|
||||
}
|
||||
|
||||
// Convert the executable name to an action name
|
||||
|
||||
DesktopAction deskAction = deskActions.getActionViaPseudoName(exeName);
|
||||
if ( deskAction == null)
|
||||
{
|
||||
respBuf.putInt(DesktopAction.StsNoSuchAction);
|
||||
return respBuf;
|
||||
}
|
||||
|
||||
// Return the desktop action details
|
||||
|
||||
respBuf.putInt(DesktopAction.StsSuccess);
|
||||
respBuf.putString(deskAction.getName(), true);
|
||||
respBuf.putInt(deskAction.getAttributes());
|
||||
respBuf.putInt(deskAction.getPreProcessActions());
|
||||
|
||||
String confirmStr = deskAction.getConfirmationString();
|
||||
respBuf.putString(confirmStr != null ? confirmStr : "", true);
|
||||
|
||||
// Return the response
|
||||
|
||||
return respBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the run action request
|
||||
*
|
||||
* @param sess Server session
|
||||
* @param tree Tree connection
|
||||
* @param reqBuf Request buffer
|
||||
* @param folderNode NodeRef of parent folder
|
||||
* @param netFile NetworkFile for the folder
|
||||
* @return DataBuffer
|
||||
*/
|
||||
private final DataBuffer procRunAction( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
|
||||
NetworkFile netFile)
|
||||
{
|
||||
// Get the name of the action to run
|
||||
|
||||
String actionName = reqBuf.getString(true);
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug(" Run action, name=" + actionName);
|
||||
|
||||
// Create a response buffer
|
||||
|
||||
DataBuffer respBuf = new DataBuffer(256);
|
||||
respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
|
||||
|
||||
// Find the action handler
|
||||
|
||||
DesktopActionTable deskActions = contentContext.getDesktopActions();
|
||||
DesktopAction action = null;
|
||||
|
||||
if ( deskActions != null)
|
||||
action = deskActions.getAction(actionName);
|
||||
|
||||
if ( action == null)
|
||||
{
|
||||
respBuf.putInt(DesktopAction.StsNoSuchAction);
|
||||
respBuf.putString("", true);
|
||||
return respBuf;
|
||||
}
|
||||
|
||||
// Start a transaction
|
||||
|
||||
contentDriver.beginReadTransaction( sess);
|
||||
|
||||
// Get an authentication ticket for the client, or validate the existing ticket. The ticket can be used when
|
||||
// generating URLs for the client-side application so that the user does not have to re-authenticate
|
||||
|
||||
getTicketForClient( sess);
|
||||
|
||||
// Get the list of targets for the action
|
||||
|
||||
int targetCnt = reqBuf.getInt();
|
||||
DesktopParams deskParams = new DesktopParams(sess, contentDriver, folderNode, netFile);
|
||||
|
||||
while ( reqBuf.getAvailableLength() > 4 && targetCnt > 0)
|
||||
{
|
||||
// Get the desktop target details
|
||||
|
||||
int typ = reqBuf.getInt();
|
||||
String path = reqBuf.getString(true);
|
||||
|
||||
DesktopTarget target = new DesktopTarget(typ, path);
|
||||
deskParams.addTarget(target);
|
||||
|
||||
// Find the node for the target path
|
||||
|
||||
NodeRef childNode = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Check if the target path is relative to the folder we are working in or the root of the filesystem
|
||||
|
||||
if ( path.startsWith("\\"))
|
||||
{
|
||||
// Path is relative to the root of the filesystem
|
||||
|
||||
childNode = getCifsHelper().getNodeRef(contentContext.getRootNode(), path);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Path is relative to the folder we are working in
|
||||
|
||||
childNode = getCifsHelper().getNodeRef( folderNode, path);
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
}
|
||||
|
||||
// If the node is not valid then return an error status
|
||||
|
||||
if (childNode != null)
|
||||
{
|
||||
// Set the node ref for the target
|
||||
|
||||
target.setNode(childNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build an error response
|
||||
|
||||
respBuf.putInt(DesktopAction.StsFileNotFound);
|
||||
respBuf.putString("Cannot find noderef for path " + path, true);
|
||||
|
||||
return respBuf;
|
||||
}
|
||||
|
||||
// Update the target count
|
||||
|
||||
targetCnt--;
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(" Desktop params: " + deskParams.numberOfTargetNodes());
|
||||
for ( int i = 0; i < deskParams.numberOfTargetNodes(); i++) {
|
||||
DesktopTarget target = deskParams.getTarget(i);
|
||||
logger.debug(" " + target);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the desktop action
|
||||
|
||||
DesktopResponse deskResponse = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Run the desktop action
|
||||
|
||||
deskResponse = action.runAction(deskParams);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Create an error response
|
||||
|
||||
deskResponse = new DesktopResponse(DesktopAction.StsError, ex.getMessage());
|
||||
}
|
||||
|
||||
// Pack the action response
|
||||
|
||||
if ( deskResponse != null)
|
||||
{
|
||||
// Pack the status
|
||||
|
||||
respBuf.putInt(deskResponse.getStatus());
|
||||
respBuf.putString(deskResponse.hasStatusMessage() ? deskResponse.getStatusMessage() : "", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pack an error response
|
||||
|
||||
respBuf.putInt(DesktopAction.StsError);
|
||||
respBuf.putString("Action did not return response", true);
|
||||
}
|
||||
|
||||
// Return the response
|
||||
|
||||
return respBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the get authentication ticket request
|
||||
*
|
||||
* @param sess Server session
|
||||
* @param tree Tree connection
|
||||
* @param reqBuf Request buffer
|
||||
* @param folderNode NodeRef of parent folder
|
||||
* @param netFile NetworkFile for the folder
|
||||
* @return DataBuffer
|
||||
*/
|
||||
private final DataBuffer procGetAuthTicket( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
|
||||
NetworkFile netFile)
|
||||
{
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug(" Get Auth Ticket");
|
||||
|
||||
// Create a response buffer
|
||||
|
||||
DataBuffer respBuf = new DataBuffer(256);
|
||||
respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
|
||||
|
||||
// Start a transaction
|
||||
|
||||
contentDriver.beginReadTransaction( sess);
|
||||
|
||||
// Get an authentication ticket for the client, or validate the existing ticket. The ticket can be used when
|
||||
// generating URLs for the client-side application so that the user does not have to re-authenticate
|
||||
|
||||
getTicketForClient( sess);
|
||||
|
||||
// Pack the response
|
||||
|
||||
AlfrescoClientInfo cInfo = (AlfrescoClientInfo) sess.getClientInformation();
|
||||
|
||||
if ( cInfo != null && cInfo.getAuthenticationTicket() != null) {
|
||||
respBuf.putInt(DesktopAction.StsAuthTicket);
|
||||
respBuf.putString( cInfo.getAuthenticationTicket(), true);
|
||||
}
|
||||
else {
|
||||
respBuf.putInt(DesktopAction.StsError);
|
||||
respBuf.putString( "Client information invalid", true);
|
||||
}
|
||||
|
||||
// Return the response
|
||||
|
||||
return respBuf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get, or validate, an authentication ticket for the client
|
||||
*
|
||||
* @param sess SrvSession
|
||||
*/
|
||||
private final void getTicketForClient(SrvSession sess)
|
||||
{
|
||||
// Get the client information and check if there is a ticket allocated
|
||||
|
||||
AlfrescoClientInfo cInfo = (AlfrescoClientInfo) sess.getClientInformation();
|
||||
if ( cInfo == null)
|
||||
return;
|
||||
|
||||
boolean needTicket = true;
|
||||
|
||||
if ( cInfo.hasAuthenticationTicket())
|
||||
{
|
||||
// Validate the existing ticket, it may have expired
|
||||
|
||||
try
|
||||
{
|
||||
// Validate the existing ticket
|
||||
|
||||
getAuthenticationService().validate( cInfo.getAuthenticationTicket());
|
||||
needTicket = false;
|
||||
}
|
||||
catch ( AuthenticationException ex)
|
||||
{
|
||||
// Invalidate the current ticket
|
||||
|
||||
try
|
||||
{
|
||||
getAuthenticationService().invalidateTicket( cInfo.getAuthenticationTicket());
|
||||
cInfo.setAuthenticationTicket( null);
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Error during invalidate ticket", ex2);
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Auth ticket expired or invalid");
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a ticket needs to be allocated
|
||||
|
||||
if ( needTicket == true)
|
||||
{
|
||||
// Allocate a new ticket and store in the client information for this session
|
||||
|
||||
String ticket = getAuthenticationService().getCurrentTicket();
|
||||
cInfo.setAuthenticationTicket( ticket);
|
||||
}
|
||||
}
|
||||
}
|
604
source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
Normal file
604
source/java/org/alfresco/filesys/repo/ContentNetworkFile.java
Normal file
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.BufferedInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.i18n.I18NUtil;
|
||||
import org.alfresco.jlan.server.filesys.AccessDeniedException;
|
||||
import org.alfresco.jlan.server.filesys.DiskFullException;
|
||||
import org.alfresco.jlan.server.filesys.FileAttribute;
|
||||
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.model.ContentModel;
|
||||
import org.alfresco.repo.content.encoding.ContentCharsetFinder;
|
||||
import org.alfresco.repo.content.filestore.FileContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentAccessor;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.usage.ContentQuotaException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Implementation of the <tt>NetworkFile</tt> for direct interaction
|
||||
* with the channel repository.
|
||||
* <p>
|
||||
* This provides the interaction with the Alfresco Content Model file/folder structure.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public class ContentNetworkFile extends NodeRefNetworkFile
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(ContentNetworkFile.class);
|
||||
|
||||
private NodeService nodeService;
|
||||
private ContentService contentService;
|
||||
private MimetypeService mimetypeService;
|
||||
|
||||
// File channel to file content
|
||||
|
||||
private FileChannel channel;
|
||||
|
||||
// File content
|
||||
|
||||
private ContentAccessor content;
|
||||
|
||||
// Indicate if file has been written to or truncated/resized
|
||||
|
||||
private boolean modified;
|
||||
|
||||
// Flag to indicate if the file channel is writable
|
||||
|
||||
private boolean writableChannel;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
String path = params.getPath();
|
||||
|
||||
// Check write access
|
||||
// TODO: Check access writes and compare to write requirements
|
||||
|
||||
// Create the file
|
||||
|
||||
ContentNetworkFile netFile = new ContentNetworkFile(nodeService, contentService, mimetypeService, nodeRef, path);
|
||||
|
||||
// Set relevant parameters
|
||||
|
||||
if (params.isReadOnlyAccess())
|
||||
{
|
||||
netFile.setGrantedAccess(NetworkFile.READONLY);
|
||||
}
|
||||
else
|
||||
{
|
||||
netFile.setGrantedAccess(NetworkFile.READWRITE);
|
||||
}
|
||||
|
||||
// Check the type
|
||||
|
||||
FileInfo fileInfo;
|
||||
try
|
||||
{
|
||||
fileInfo = cifsHelper.getFileInformation(nodeRef, "");
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("File not found when creating network file: " + nodeRef, e);
|
||||
}
|
||||
|
||||
if (fileInfo.isDirectory())
|
||||
{
|
||||
netFile.setAttributes(FileAttribute.Directory);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the current size
|
||||
|
||||
netFile.setFileSize(fileInfo.getSize());
|
||||
}
|
||||
|
||||
// Set the file timestamps
|
||||
|
||||
if ( fileInfo.hasCreationDateTime())
|
||||
netFile.setCreationDate( fileInfo.getCreationDateTime());
|
||||
|
||||
if ( fileInfo.hasModifyDateTime())
|
||||
netFile.setModifyDate(fileInfo.getModifyDateTime());
|
||||
|
||||
if ( fileInfo.hasAccessDateTime())
|
||||
netFile.setAccessDate(fileInfo.getAccessDateTime());
|
||||
|
||||
// Set the file attributes
|
||||
|
||||
netFile.setAttributes(fileInfo.getFileAttributes());
|
||||
|
||||
// If the file is read-only then only allow read access
|
||||
|
||||
if ( netFile.isReadOnly())
|
||||
netFile.setGrantedAccess(NetworkFile.READONLY);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Create file node=" + nodeRef + ", param=" + params + ", netfile=" + netFile);
|
||||
|
||||
// Return the network file
|
||||
|
||||
return netFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param transactionService TransactionService
|
||||
* @param nodeService NodeService
|
||||
* @param contentService ContentService
|
||||
* @param nodeRef NodeRef
|
||||
* @param name String
|
||||
*/
|
||||
private ContentNetworkFile(
|
||||
NodeService nodeService,
|
||||
ContentService contentService,
|
||||
MimetypeService mimetypeService,
|
||||
NodeRef nodeRef,
|
||||
String name)
|
||||
{
|
||||
super(name, nodeRef);
|
||||
setFullName(name);
|
||||
this.nodeService = nodeService;
|
||||
this.contentService = contentService;
|
||||
this.mimetypeService = mimetypeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file details as a string
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
|
||||
str.append( "[");
|
||||
str.append( getNodeRef().getId());
|
||||
str.append( ",channel=");
|
||||
str.append( channel);
|
||||
if ( channel != null)
|
||||
str.append( writableChannel ? "(Write)" : "(Read)");
|
||||
if ( modified)
|
||||
str.append( ",modified");
|
||||
str.append( "]");
|
||||
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns true if the channel should be writable
|
||||
*
|
||||
* @see NetworkFile#getGrantedAccess()
|
||||
* @see NetworkFile#READONLY
|
||||
* @see NetworkFile#WRITEONLY
|
||||
* @see NetworkFile#READWRITE
|
||||
*/
|
||||
private boolean isWritable()
|
||||
{
|
||||
// Check that we are allowed to write
|
||||
|
||||
int access = getGrantedAccess();
|
||||
return (access == NetworkFile.READWRITE || access == NetworkFile.WRITEONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the file content data has been opened
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasContent()
|
||||
{
|
||||
return content != null ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the channel for reading or writing depending on the access mode.
|
||||
* <p>
|
||||
* If the channel is already open, it is left.
|
||||
*
|
||||
* @param write true if the channel must be writable
|
||||
* @param trunc true if the writable channel does not require the previous content data
|
||||
* @throws AccessDeniedException if this network file is read only
|
||||
* @throws AlfrescoRuntimeException if this network file represents a directory
|
||||
*
|
||||
* @see NetworkFile#getGrantedAccess()
|
||||
* @see NetworkFile#READONLY
|
||||
* @see NetworkFile#WRITEONLY
|
||||
* @see NetworkFile#READWRITE
|
||||
*/
|
||||
private void openContent(boolean write, boolean trunc)
|
||||
throws AccessDeniedException, AlfrescoRuntimeException
|
||||
{
|
||||
// Check if the file is a directory
|
||||
|
||||
if (isDirectory())
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Unable to open channel for a directory network file: " + this);
|
||||
}
|
||||
|
||||
// Check if write access is required and the current channel is read-only
|
||||
|
||||
else if ( write && writableChannel == false && channel != null)
|
||||
{
|
||||
// Close the existing read-only channel
|
||||
|
||||
try
|
||||
{
|
||||
channel.close();
|
||||
channel = null;
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("Error closing read-only channel", ex);
|
||||
}
|
||||
|
||||
// Debug
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Switching to writable channel for " + getName());
|
||||
}
|
||||
else if (channel != null)
|
||||
{
|
||||
// Already have channel open
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to create the channel
|
||||
|
||||
if (write && !isWritable())
|
||||
{
|
||||
throw new AccessDeniedException("The network file was created for read-only: " + this);
|
||||
}
|
||||
|
||||
content = null;
|
||||
if (write)
|
||||
{
|
||||
// Get a writeable channel to the content
|
||||
|
||||
content = contentService.getWriter( getNodeRef(), ContentModel.PROP_CONTENT, false);
|
||||
|
||||
// Indicate that we have a writable channel to the file
|
||||
|
||||
writableChannel = true;
|
||||
|
||||
// Get the writable channel, do not copy existing content data if the file is to be truncated
|
||||
|
||||
channel = ((ContentWriter) content).getFileChannel( trunc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get a read-only channel to the 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),
|
||||
getNodeRef(), content);
|
||||
|
||||
// Indicate that we only have a read-only channel to the data
|
||||
|
||||
writableChannel = false;
|
||||
|
||||
// Get the read-only channel
|
||||
|
||||
channel = ((ContentReader) content).getFileChannel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the file
|
||||
*
|
||||
* @exception IOException
|
||||
*/
|
||||
public void closeFile()
|
||||
throws IOException
|
||||
{
|
||||
// Check if this is a directory
|
||||
|
||||
if (isDirectory())
|
||||
{
|
||||
// Nothing to do
|
||||
|
||||
return;
|
||||
}
|
||||
else if (channel == null)
|
||||
{
|
||||
// File was not read/written so channel was not opened
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the file has been modified
|
||||
|
||||
if (modified)
|
||||
{
|
||||
// Take a guess at the mimetype
|
||||
channel.position(0);
|
||||
InputStream is = new BufferedInputStream(Channels.newInputStream(channel));
|
||||
ContentCharsetFinder charsetFinder = mimetypeService.getContentCharsetFinder();
|
||||
Charset charset = charsetFinder.getCharset(is, content.getMimetype());
|
||||
content.setEncoding(charset.name());
|
||||
|
||||
// Close the channel
|
||||
|
||||
channel.close();
|
||||
channel = null;
|
||||
|
||||
// Update node properties
|
||||
|
||||
ContentData contentData = content.getContentData();
|
||||
try
|
||||
{
|
||||
nodeService.setProperty( getNodeRef(), ContentModel.PROP_CONTENT, contentData);
|
||||
}
|
||||
catch (ContentQuotaException qe)
|
||||
{
|
||||
throw new DiskFullException(qe.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Close it - it was not modified
|
||||
|
||||
channel.close();
|
||||
channel = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate or extend the file to the specified length
|
||||
*
|
||||
* @param size long
|
||||
* @exception IOException
|
||||
*/
|
||||
public void truncateFile(long size)
|
||||
throws IOException
|
||||
{
|
||||
// If the content data channel has not been opened yet and the requested size is zero
|
||||
// then this is an open for overwrite so the existing content data is not copied
|
||||
|
||||
if ( hasContent() == false && size == 0L)
|
||||
{
|
||||
// Open content for overwrite, no need to copy existing content data
|
||||
|
||||
openContent(true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal open for write
|
||||
|
||||
openContent(true, false);
|
||||
|
||||
// Truncate or extend the channel
|
||||
|
||||
channel.truncate(size);
|
||||
}
|
||||
|
||||
// Set modification flag
|
||||
|
||||
modified = true;
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Truncate file=" + this + ", size=" + size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
// Open the channel for writing
|
||||
|
||||
openContent(true, false);
|
||||
|
||||
// Write to the channel
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, position, length);
|
||||
int count = channel.write(byteBuffer, fileOffset);
|
||||
|
||||
// Set modification flag
|
||||
|
||||
modified = true;
|
||||
|
||||
// Update the current file size
|
||||
|
||||
setFileSize(channel.size());
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Write file=" + this + ", size=" + count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
// Open the channel for reading
|
||||
|
||||
openContent(false, false);
|
||||
|
||||
// Read from the channel
|
||||
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, position, length);
|
||||
int count = channel.read(byteBuffer, fileOffset);
|
||||
if (count < 0)
|
||||
{
|
||||
count = 0; // doesn't obey the same rules, i.e. just returns the bytes read
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Read file=" + this + " read=" + count);
|
||||
|
||||
// Return the actual count of bytes read
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the file
|
||||
*
|
||||
* @param createFlag boolean
|
||||
* @exception IOException
|
||||
*/
|
||||
@Override
|
||||
public void openFile(boolean createFlag)
|
||||
throws IOException
|
||||
{
|
||||
// Wait for read/write before opening the content channel
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a new position in the file
|
||||
*
|
||||
* @param pos long
|
||||
* @param typ int
|
||||
* @return long
|
||||
*/
|
||||
@Override
|
||||
public long seekFile(long pos, int typ)
|
||||
throws IOException
|
||||
{
|
||||
// Open the file, if not already open
|
||||
|
||||
openContent( false, false);
|
||||
|
||||
// Check if the current file position is the required file position
|
||||
|
||||
long curPos = channel.position();
|
||||
|
||||
switch (typ) {
|
||||
|
||||
// From start of file
|
||||
|
||||
case SeekType.StartOfFile :
|
||||
if (curPos != pos)
|
||||
channel.position( pos);
|
||||
break;
|
||||
|
||||
// From current position
|
||||
|
||||
case SeekType.CurrentPos :
|
||||
channel.position( curPos + pos);
|
||||
break;
|
||||
|
||||
// From end of file
|
||||
|
||||
case SeekType.EndOfFile :
|
||||
{
|
||||
long newPos = channel.size() + pos;
|
||||
channel.position(newPos);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Seek file=" + this + ", pos=" + pos + ", type=" + typ);
|
||||
|
||||
// Return the new file position
|
||||
|
||||
return channel.position();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush and buffered data for this file
|
||||
*
|
||||
* @exception IOException
|
||||
*/
|
||||
@Override
|
||||
public void flushFile()
|
||||
throws IOException
|
||||
{
|
||||
// Open the channel for writing
|
||||
|
||||
openContent(true, false);
|
||||
|
||||
// Flush the channel - metadata flushing is not important
|
||||
|
||||
channel.force(false);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Flush file=" + this);
|
||||
}
|
||||
}
|
438
source/java/org/alfresco/filesys/repo/ContentSearchContext.java
Normal file
438
source/java/org/alfresco/filesys/repo/ContentSearchContext.java
Normal file
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.FileNotFoundException;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.jlan.server.filesys.FileAttribute;
|
||||
import org.alfresco.jlan.server.filesys.FileInfo;
|
||||
import org.alfresco.jlan.server.filesys.FileName;
|
||||
import org.alfresco.jlan.server.filesys.FileType;
|
||||
import org.alfresco.jlan.server.filesys.SearchContext;
|
||||
import org.alfresco.jlan.server.filesys.pseudo.PseudoFile;
|
||||
import org.alfresco.jlan.server.filesys.pseudo.PseudoFileList;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Wrapper for simple XPath searche against the node service. The search is performed statically
|
||||
* outside the context instance itself - this class merely maintains the state of the search
|
||||
* results across client connections.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
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;
|
||||
|
||||
// Pseudo file list blended into a wildcard folder search
|
||||
|
||||
private PseudoFileList pseudoList;
|
||||
private boolean donePseudoFiles = false;
|
||||
|
||||
// Resume id
|
||||
|
||||
private int resumeId;
|
||||
|
||||
// Relative path being searched
|
||||
|
||||
private String m_relPath;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param cifsHelper Filesystem helper class
|
||||
* @param results List of file/folder nodes that match the search pattern
|
||||
* @param searchStr Search path
|
||||
* @param pseudoList List of pseudo files to be blended into the returned list of files
|
||||
* @param relPath Relative path being searched
|
||||
*/
|
||||
protected ContentSearchContext(
|
||||
CifsHelper cifsHelper,
|
||||
List<NodeRef> results,
|
||||
String searchStr,
|
||||
PseudoFileList pseudoList,
|
||||
String relPath)
|
||||
{
|
||||
super();
|
||||
super.setSearchString(searchStr);
|
||||
this.cifsHelper = cifsHelper;
|
||||
this.results = results;
|
||||
this.pseudoList = pseudoList;
|
||||
|
||||
m_relPath = relPath;
|
||||
if ( m_relPath != null && m_relPath.endsWith( FileName.DOS_SEPERATOR_STR) == false)
|
||||
m_relPath = m_relPath + FileName.DOS_SEPERATOR_STR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the search as a string
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(60);
|
||||
sb.append("ContentSearchContext")
|
||||
.append("[ searchStr=").append(getSearchString())
|
||||
.append(", resultCount=").append(results.size())
|
||||
.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the resume id for the current file/directory in the search.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getResumeId()
|
||||
{
|
||||
return resumeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if there are more files for the active search.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean hasMoreFiles()
|
||||
{
|
||||
// Pseudo files are returned first
|
||||
|
||||
if ( donePseudoFiles == false && pseudoList != null && index < (pseudoList.numberOfFiles() - 1))
|
||||
return true;
|
||||
return index < (results.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return file information for the next file in the active search. Returns false if the search
|
||||
* is complete.
|
||||
*
|
||||
* @param info FileInfo to return the file information.
|
||||
* @return true if the file information is valid, else false
|
||||
*/
|
||||
public boolean nextFileInfo(FileInfo info)
|
||||
{
|
||||
// Check if there is anything else to return
|
||||
|
||||
if (!hasMoreFiles())
|
||||
return false;
|
||||
|
||||
// Increment the index and resume id
|
||||
|
||||
index++;
|
||||
resumeId++;
|
||||
|
||||
// If the pseudo file list is valid return the pseudo files first
|
||||
|
||||
if ( donePseudoFiles == false && pseudoList != null)
|
||||
{
|
||||
if ( index < pseudoList.numberOfFiles())
|
||||
{
|
||||
PseudoFile pfile = pseudoList.getFileAt( index);
|
||||
if ( pfile != null)
|
||||
{
|
||||
// Get the file information for the pseudo file
|
||||
|
||||
FileInfo pinfo = pfile.getFileInfo();
|
||||
|
||||
// Copy the file information to the callers file info
|
||||
|
||||
info.copyFrom( pinfo);
|
||||
|
||||
// Generate a file id for the current file
|
||||
|
||||
if ( info != null && info.getFileId() == -1)
|
||||
{
|
||||
StringBuilder pathStr = new StringBuilder( m_relPath);
|
||||
pathStr.append ( info.getFileName());
|
||||
|
||||
info.setFileId( pathStr.toString().hashCode());
|
||||
}
|
||||
|
||||
// Check if we have finished with the pseudo file list, switch to the normal file list
|
||||
|
||||
if ( index == (pseudoList.numberOfFiles() - 1))
|
||||
{
|
||||
// Switch to the main file list
|
||||
|
||||
donePseudoFiles = true;
|
||||
index = -1;
|
||||
}
|
||||
|
||||
// Indicate that the file information is valid
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the next file info from the node search
|
||||
|
||||
NodeRef nextNodeRef = results.get(index);
|
||||
|
||||
try
|
||||
{
|
||||
// Get the file information and copy across to the callers file info
|
||||
|
||||
ContentFileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, "");
|
||||
info.copyFrom(nextInfo);
|
||||
|
||||
// Generate a file id for the current file
|
||||
|
||||
StringBuilder pathStr = new StringBuilder( m_relPath);
|
||||
pathStr.append ( info.getFileName());
|
||||
|
||||
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);
|
||||
|
||||
// Set the file type to indicate a symbolic link
|
||||
|
||||
info.setFileType( FileType.SymbolicLink);
|
||||
}
|
||||
else
|
||||
info.setFileType( FileType.RegularFile);
|
||||
|
||||
// Indicate that the file information is valid
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
}
|
||||
|
||||
// File information is not valid
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file name of the next file in the active search. Returns null is the search is
|
||||
* complete.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String nextFileName()
|
||||
{
|
||||
// Check if there is anything else to return
|
||||
|
||||
if (!hasMoreFiles())
|
||||
return null;
|
||||
|
||||
// Increment the index and resume id
|
||||
|
||||
index++;
|
||||
resumeId++;
|
||||
|
||||
// If the pseudo file list is valid return the pseudo files first
|
||||
|
||||
if ( donePseudoFiles == false && pseudoList != null)
|
||||
{
|
||||
if ( index < pseudoList.numberOfFiles())
|
||||
{
|
||||
PseudoFile pfile = pseudoList.getFileAt( index);
|
||||
if ( pfile != null)
|
||||
{
|
||||
// Get the file information for the pseudo file
|
||||
|
||||
FileInfo pinfo = pfile.getFileInfo();
|
||||
|
||||
// Copy the file information to the callers file info
|
||||
|
||||
return pinfo.getFileName();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Switch to the main file list
|
||||
|
||||
donePseudoFiles = true;
|
||||
index = -1;
|
||||
|
||||
if ( results == null || results.size() == 0)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the next file info from the node search
|
||||
|
||||
NodeRef nextNodeRef = results.get(index);
|
||||
|
||||
try
|
||||
{
|
||||
// Get the file information and copy across to the callers file info
|
||||
|
||||
FileInfo nextInfo = cifsHelper.getFileInformation(nextNodeRef, "");
|
||||
|
||||
// Indicate that the file information is valid
|
||||
|
||||
return nextInfo.getFileName();
|
||||
}
|
||||
catch (FileNotFoundException e)
|
||||
{
|
||||
}
|
||||
|
||||
// No more files
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart a search at the specified resume point.
|
||||
*
|
||||
* @param resumeId Resume point id.
|
||||
* @return true if the search can be restarted, else false.
|
||||
*/
|
||||
public boolean restartAt(FileInfo info)
|
||||
{
|
||||
// Check if the resume point is in the pseudo file list
|
||||
|
||||
int resId = 0;
|
||||
|
||||
if (pseudoList != null)
|
||||
{
|
||||
while ( resId < pseudoList.numberOfFiles())
|
||||
{
|
||||
// Check if the current pseudo file matches the resume file name
|
||||
|
||||
PseudoFile pfile = pseudoList.getFileAt(resId);
|
||||
if ( pfile.getFileName().equals(info.getFileName()))
|
||||
{
|
||||
// Found the restart point
|
||||
|
||||
donePseudoFiles = false;
|
||||
index = resId - 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
resId++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the resume file is in the main file list
|
||||
|
||||
if ( results != null)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
while ( idx < results.size())
|
||||
{
|
||||
// Get the file name for the node
|
||||
|
||||
String fname = cifsHelper.getFileName( results.get( idx));
|
||||
if ( fname != null && fname.equals( info.getFileName()))
|
||||
{
|
||||
index = idx - 1;
|
||||
resumeId = resId - 1;
|
||||
donePseudoFiles = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
idx++;
|
||||
resId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to find resume file
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the current search at the specified file.
|
||||
*
|
||||
* @param info File to restart the search at.
|
||||
* @return true if the search can be restarted, else false.
|
||||
*/
|
||||
public boolean restartAt(int resumeId)
|
||||
{
|
||||
// Check if the resume point is in the pseudo file list
|
||||
|
||||
if (pseudoList != null)
|
||||
{
|
||||
if ( resumeId < pseudoList.numberOfFiles())
|
||||
{
|
||||
// Resume at a pseudo file
|
||||
|
||||
index = resumeId;
|
||||
donePseudoFiles = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Adjust the resume id so that it is an index into the main file list
|
||||
|
||||
resumeId -= pseudoList.numberOfFiles();
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the resume point is valid
|
||||
|
||||
if ( results != null && resumeId < results.size())
|
||||
{
|
||||
index = resumeId;
|
||||
donePseudoFiles = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Invalid resume point
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
383
source/java/org/alfresco/filesys/repo/HomeShareMapper.java
Normal file
383
source/java/org/alfresco/filesys/repo/HomeShareMapper.java
Normal file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.util.Enumeration;
|
||||
|
||||
import org.alfresco.jlan.server.SrvSession;
|
||||
import org.alfresco.jlan.server.auth.ClientInfo;
|
||||
import org.alfresco.jlan.server.auth.InvalidUserException;
|
||||
import org.alfresco.jlan.server.config.InvalidConfigurationException;
|
||||
import org.alfresco.jlan.server.config.ServerConfiguration;
|
||||
import org.alfresco.jlan.server.core.InvalidDeviceInterfaceException;
|
||||
import org.alfresco.jlan.server.core.ShareMapper;
|
||||
import org.alfresco.jlan.server.core.ShareType;
|
||||
import org.alfresco.jlan.server.core.SharedDevice;
|
||||
import org.alfresco.jlan.server.core.SharedDeviceList;
|
||||
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
|
||||
import org.alfresco.jlan.server.filesys.FilesystemsConfigSection;
|
||||
import org.alfresco.config.ConfigElement;
|
||||
import org.alfresco.filesys.alfresco.AlfrescoClientInfo;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Home Share Mapper Class
|
||||
*
|
||||
* <p>Maps disk share lookup requests to the list of shares defined in the server
|
||||
* configuration and provides a dynamic home share mapped to the users home node.
|
||||
*
|
||||
* @author GKSpencer
|
||||
*/
|
||||
public class HomeShareMapper implements ShareMapper
|
||||
{
|
||||
// Logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol");
|
||||
|
||||
// Home folder share name
|
||||
|
||||
public static final String HOME_FOLDER_SHARE = "HOME";
|
||||
|
||||
// Server configuration and sections
|
||||
|
||||
private ServerConfiguration m_config;
|
||||
private FilesystemsConfigSection m_filesysConfig;
|
||||
|
||||
// Filesystem driver to be used to create home shares
|
||||
|
||||
private ContentDiskDriver m_driver;
|
||||
|
||||
// Home folder share name
|
||||
|
||||
private String m_homeShareName = HOME_FOLDER_SHARE;
|
||||
|
||||
// Debug enable flag
|
||||
|
||||
private boolean m_debug;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public HomeShareMapper()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the share mapper
|
||||
*
|
||||
* @param config ServerConfiguration
|
||||
* @param params ConfigElement
|
||||
* @exception InvalidConfigurationException
|
||||
*/
|
||||
public void initializeMapper(ServerConfiguration config, ConfigElement params) throws InvalidConfigurationException
|
||||
{
|
||||
// Save the server configuration
|
||||
|
||||
m_config = config;
|
||||
m_filesysConfig = (FilesystemsConfigSection) m_config.getConfigSection(FilesystemsConfigSection.SectionName);
|
||||
|
||||
// Check if the home share name has been specified
|
||||
|
||||
String homeName = params.getAttribute("name");
|
||||
if ( homeName != null && homeName.length() > 0)
|
||||
m_homeShareName = homeName;
|
||||
|
||||
// Check if debug is enabled
|
||||
|
||||
if (params != null && params.getChild("debug") != null)
|
||||
m_debug = true;
|
||||
|
||||
// Search for a filesystem that uses the content driver to use the driver when creating the home shares
|
||||
|
||||
SharedDeviceList shares = m_filesysConfig.getShares();
|
||||
|
||||
if ( shares != null)
|
||||
{
|
||||
Enumeration<SharedDevice> shrEnum = shares.enumerateShares();
|
||||
|
||||
while ( shrEnum.hasMoreElements() && m_driver == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
SharedDevice curShare = shrEnum.nextElement();
|
||||
if ( curShare.getInterface() instanceof ContentDiskDriver)
|
||||
m_driver = (ContentDiskDriver) curShare.getInterface();
|
||||
}
|
||||
catch (InvalidDeviceInterfaceException ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if debug output is enabled
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasDebug()
|
||||
{
|
||||
return m_debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the home folder share name
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public final String getHomeFolderName()
|
||||
{
|
||||
return m_homeShareName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of available shares.
|
||||
*
|
||||
* @param host String
|
||||
* @param sess SrvSession
|
||||
* @param allShares boolean
|
||||
* @return SharedDeviceList
|
||||
*/
|
||||
public SharedDeviceList getShareList(String host, SrvSession sess, boolean allShares)
|
||||
{
|
||||
// Check if the user has a home folder, and the session does not currently have any
|
||||
// dynamic shares defined
|
||||
|
||||
if ( sess != null && sess.hasClientInformation() && sess.hasDynamicShares() == false &&
|
||||
sess.getClientInformation() instanceof AlfrescoClientInfo)
|
||||
{
|
||||
AlfrescoClientInfo alfClient = (AlfrescoClientInfo) sess.getClientInformation();
|
||||
if ( alfClient.hasHomeFolder())
|
||||
{
|
||||
// Create the home folder share
|
||||
|
||||
DiskSharedDevice homeShare = createHomeDiskShare( alfClient);
|
||||
sess.addDynamicShare(homeShare);
|
||||
|
||||
// Debug
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Added " + getHomeFolderName() + " share to list of shares for " + alfClient.getUserName());
|
||||
}
|
||||
}
|
||||
|
||||
// Make a copy of the global share list and add the per session dynamic shares
|
||||
|
||||
SharedDeviceList shrList = new SharedDeviceList(m_filesysConfig.getShares());
|
||||
|
||||
if ( sess != null && sess.hasDynamicShares()) {
|
||||
|
||||
// Add the per session dynamic shares
|
||||
|
||||
shrList.addShares(sess.getDynamicShareList());
|
||||
}
|
||||
|
||||
// Remove unavailable shares from the list and return the list
|
||||
|
||||
if ( allShares == false)
|
||||
shrList.removeUnavailableShares();
|
||||
return shrList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a share using the name and type for the specified client.
|
||||
*
|
||||
* @param host String
|
||||
* @param name String
|
||||
* @param typ int
|
||||
* @param sess SrvSession
|
||||
* @param create boolean
|
||||
* @return SharedDevice
|
||||
* @exception InvalidUserException
|
||||
*/
|
||||
public SharedDevice findShare(String tohost, String name, int typ, SrvSession sess, boolean create)
|
||||
throws Exception
|
||||
{
|
||||
|
||||
// Check for the special HOME disk share
|
||||
|
||||
SharedDevice share = null;
|
||||
|
||||
if (( typ == ShareType.DISK || typ == ShareType.UNKNOWN) && name.equalsIgnoreCase(getHomeFolderName()) &&
|
||||
sess.getClientInformation() != null && m_driver != null) {
|
||||
|
||||
// Get the client details
|
||||
|
||||
ClientInfo client = sess.getClientInformation();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug("Map share " + name + ", type=" + ShareType.TypeAsString(typ) + ", client=" + client);
|
||||
|
||||
// Check if the user has a home folder node
|
||||
|
||||
if ( client != null && client instanceof AlfrescoClientInfo) {
|
||||
|
||||
// Access the extended client information
|
||||
|
||||
AlfrescoClientInfo alfClient = (AlfrescoClientInfo) client;
|
||||
|
||||
if ( alfClient.hasHomeFolder()) {
|
||||
|
||||
// Check if the share has already been created for the session
|
||||
|
||||
if ( sess.hasDynamicShares()) {
|
||||
|
||||
// Check if the required share exists in the sessions dynamic share list
|
||||
|
||||
share = sess.getDynamicShareList().findShare(name, typ, false);
|
||||
|
||||
// DEBUG
|
||||
|
||||
if ( logger.isDebugEnabled())
|
||||
logger.debug(" Reusing existing dynamic share for " + name);
|
||||
}
|
||||
|
||||
// Check if we found a share, if not then create a new dynamic share for the home directory
|
||||
|
||||
if ( share == null && create == true) {
|
||||
|
||||
// Create the home share mapped to the users home folder
|
||||
|
||||
DiskSharedDevice diskShare = createHomeDiskShare( alfClient);
|
||||
|
||||
// Add the new share to the sessions dynamic share list
|
||||
|
||||
sess.addDynamicShare(diskShare);
|
||||
share = diskShare;
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug(" Mapped share " + name + " to " + alfClient.getHomeFolder());
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new InvalidUserException("No home directory");
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Find the required share by name/type. Use a case sensitive search first, if that fails use a case
|
||||
// insensitive search.
|
||||
|
||||
share = m_filesysConfig.getShares().findShare(name, typ, false);
|
||||
|
||||
if ( share == null) {
|
||||
|
||||
// Try a case insensitive search for the required share
|
||||
|
||||
share = m_filesysConfig.getShares().findShare(name, typ, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the share is available
|
||||
|
||||
if ( share != null && share.getContext() != null && share.getContext().isAvailable() == false)
|
||||
share = null;
|
||||
|
||||
// Return the shared device, or null if no matching device was found
|
||||
|
||||
return share;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete temporary shares for the specified session
|
||||
*
|
||||
* @param sess SrvSession
|
||||
*/
|
||||
public void deleteShares(SrvSession sess)
|
||||
{
|
||||
|
||||
// Check if the session has any dynamic shares
|
||||
|
||||
if ( sess.hasDynamicShares() == false)
|
||||
return;
|
||||
|
||||
// Delete the dynamic shares
|
||||
|
||||
SharedDeviceList shares = sess.getDynamicShareList();
|
||||
Enumeration<SharedDevice> enm = shares.enumerateShares();
|
||||
|
||||
while ( enm.hasMoreElements()) {
|
||||
|
||||
// Get the current share from the list
|
||||
|
||||
SharedDevice shr = enm.nextElement();
|
||||
|
||||
// Close the shared device
|
||||
|
||||
shr.getContext().CloseContext();
|
||||
|
||||
// DEBUG
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Deleted dynamic share " + shr);
|
||||
}
|
||||
|
||||
// Clear the dynamic share list
|
||||
|
||||
shares.removeAllShares();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the share mapper, release any resources.
|
||||
*/
|
||||
public void closeMapper()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a disk share for the home folder
|
||||
*
|
||||
* @param alfClient AlfrescoClientInfo
|
||||
* @return DiskSharedDevice
|
||||
*/
|
||||
private final DiskSharedDevice createHomeDiskShare(AlfrescoClientInfo alfClient)
|
||||
{
|
||||
// Make sure the client is an Alfresco client
|
||||
|
||||
if ( alfClient != null) {
|
||||
|
||||
// Create the disk driver and context
|
||||
|
||||
ContentContext diskCtx = new ContentContext( getHomeFolderName(), "", "", alfClient.getHomeFolder());
|
||||
diskCtx.enableStateTable( true, m_driver.getStateReaper());
|
||||
|
||||
// Create a temporary shared device for the users home directory
|
||||
|
||||
return new DiskSharedDevice(getHomeFolderName(), m_driver, diskCtx, SharedDevice.Temporary);
|
||||
}
|
||||
|
||||
// Invalid client type
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
256
source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java
Normal file
256
source/java/org/alfresco/filesys/repo/LinkMemoryNetworkFile.java
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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 org.alfresco.jlan.server.filesys.FileInfo;
|
||||
import org.alfresco.jlan.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
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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 org.alfresco.filesys.alfresco.AlfrescoNetworkFile;
|
||||
import org.alfresco.jlan.server.filesys.NetworkFile;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
|
||||
/**
|
||||
* NodeRef Based Network File Class
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public abstract class NodeRefNetworkFile extends AlfrescoNetworkFile {
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.desk;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
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.jlan.server.filesys.FileName;
|
||||
import org.alfresco.jlan.server.filesys.NotifyChange;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
|
||||
/**
|
||||
* Check In/Out Desktop Action Class
|
||||
*
|
||||
* <p>Provides check in/out functionality via CIFS using the desktop action interface.
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public class CheckInOutDesktopAction extends DesktopAction {
|
||||
|
||||
// Check in/out service
|
||||
|
||||
private CheckOutCheckInService m_checkInOutService;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public CheckInOutDesktopAction()
|
||||
{
|
||||
super( DesktopAction.AttrAnyFiles, DesktopAction.PreConfirmAction + DesktopAction.PreCopyToTarget + DesktopAction.PreLocalToWorkingCopy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the confirmation string to be displayed by the client
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@Override
|
||||
public String getConfirmationString() {
|
||||
return "Run check in/out action";
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the desktop action
|
||||
*
|
||||
* @param params DesktopParams
|
||||
* @return DesktopResponse
|
||||
*/
|
||||
@Override
|
||||
public DesktopResponse runAction(DesktopParams params) {
|
||||
|
||||
// Check if there are any files/folders to process
|
||||
|
||||
if ( params.numberOfTargetNodes() == 0)
|
||||
return new DesktopResponse(StsSuccess);
|
||||
|
||||
// Get required services
|
||||
|
||||
NodeService nodeService = getServiceRegistry().getNodeService();
|
||||
|
||||
// Start a transaction
|
||||
|
||||
params.getDriver().beginWriteTransaction( params.getSession());
|
||||
|
||||
// Process the list of target nodes
|
||||
|
||||
DesktopResponse response = new DesktopResponse(StsSuccess);
|
||||
|
||||
for ( int idx = 0; idx < params.numberOfTargetNodes(); idx++)
|
||||
{
|
||||
// Get the current target node
|
||||
|
||||
DesktopTarget target = params.getTarget(idx);
|
||||
|
||||
// Check if the node is a working copy
|
||||
|
||||
if ( nodeService.hasAspect( target.getNode(), ContentModel.ASPECT_WORKING_COPY))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check in the file, pass an empty version properties so that veriosnable nodes create a new version
|
||||
|
||||
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>();
|
||||
getCheckInOutService().checkin( target.getNode(), versionProperties, null, false);
|
||||
|
||||
// Check if there are any file/directory change notify requests active
|
||||
|
||||
if ( getContext().hasChangeHandler()) {
|
||||
|
||||
// Build the relative path to the checked in file
|
||||
|
||||
String fileName = null;
|
||||
|
||||
if ( target.getTarget().startsWith(FileName.DOS_SEPERATOR_STR))
|
||||
{
|
||||
// Path is already relative to filesystem root
|
||||
|
||||
fileName = target.getTarget();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Build a root relative path for the file
|
||||
|
||||
fileName = FileName.buildPath( params.getFolder().getFullName(), null, target.getTarget(), FileName.DOS_SEPERATOR);
|
||||
}
|
||||
|
||||
// Queue a file deleted change notification
|
||||
|
||||
getContext().getChangeHandler().notifyFileChanged(NotifyChange.ActionRemoved, fileName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Dump the error
|
||||
|
||||
if ( logger.isErrorEnabled())
|
||||
logger.error("Desktop action error", ex);
|
||||
|
||||
// Return an error status and message
|
||||
|
||||
response.setStatus(StsError, "Checkin failed for " + target.getTarget() + ", " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if the file is locked
|
||||
|
||||
if ( nodeService.hasAspect( target.getNode(), ContentModel.ASPECT_LOCKABLE)) {
|
||||
|
||||
// Get the lock type
|
||||
|
||||
String lockTypeStr = (String) nodeService.getProperty( target.getNode(), ContentModel.PROP_LOCK_TYPE);
|
||||
if ( lockTypeStr != null) {
|
||||
response.setStatus(StsError, "Checkout failed, file is locked");
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
// Check out the file
|
||||
|
||||
NodeRef workingCopyNode = getCheckInOutService().checkout( target.getNode());
|
||||
|
||||
// Get the working copy file name
|
||||
|
||||
String workingCopyName = (String) nodeService.getProperty( workingCopyNode, ContentModel.PROP_NAME);
|
||||
|
||||
// Check out was successful, pack the working copy name
|
||||
|
||||
response.setStatus(StsSuccess, "Checked out working copy " + workingCopyName);
|
||||
|
||||
// Check if there are any file/directory change notify requests active
|
||||
|
||||
if ( getContext().hasChangeHandler()) {
|
||||
|
||||
// Build the relative path to the checked in file
|
||||
|
||||
String fileName = FileName.buildPath( params.getFolder().getFullName(), null, workingCopyName, FileName.DOS_SEPERATOR);
|
||||
|
||||
// Queue a file added change notification
|
||||
|
||||
getContext().getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, fileName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Dump the error
|
||||
|
||||
if ( logger.isErrorEnabled())
|
||||
logger.error("Desktop action error", ex);
|
||||
|
||||
// Return an error status and message
|
||||
|
||||
response.setStatus(StsError, "Failed to checkout " + target.getTarget() + ", " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return a success status for now
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the check in/out service
|
||||
*
|
||||
* @return CheckOutCheckInService
|
||||
*/
|
||||
protected final CheckOutCheckInService getCheckInOutService()
|
||||
{
|
||||
// Check if the service has been cached
|
||||
|
||||
if ( m_checkInOutService == null)
|
||||
{
|
||||
m_checkInOutService = getServiceRegistry().getCheckOutCheckInService();
|
||||
}
|
||||
|
||||
// Return the check in/out service
|
||||
|
||||
return m_checkInOutService;
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.desk;
|
||||
|
||||
import org.alfresco.filesys.alfresco.DesktopAction;
|
||||
import org.alfresco.filesys.alfresco.DesktopParams;
|
||||
import org.alfresco.filesys.alfresco.DesktopResponse;
|
||||
|
||||
/**
|
||||
* Command Line Desktop Action Class
|
||||
*
|
||||
* <p>Simple desktop action that returns a test command line.
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public class CmdLineDesktopAction extends DesktopAction {
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public CmdLineDesktopAction()
|
||||
{
|
||||
super( 0, PreConfirmAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfirmationString() {
|
||||
return "Run commandline action";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DesktopResponse runAction(DesktopParams params) {
|
||||
|
||||
// Return a URL in the status message
|
||||
|
||||
return new DesktopResponse(StsCommandLine, "%SystemRoot%\\notepad.exe");
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.desk;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.alfresco.filesys.alfresco.DesktopAction;
|
||||
import org.alfresco.filesys.alfresco.DesktopParams;
|
||||
import org.alfresco.filesys.alfresco.DesktopResponse;
|
||||
|
||||
/**
|
||||
* Echo Desktop Action Class
|
||||
*
|
||||
* <p>Simple desktop action that echoes back the received string.
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public class EchoDesktopAction extends DesktopAction {
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public EchoDesktopAction()
|
||||
{
|
||||
super( 0, PreConfirmAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfirmationString() {
|
||||
return "Run echo action";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DesktopResponse runAction(DesktopParams params) {
|
||||
|
||||
// Return a text message
|
||||
|
||||
return new DesktopResponse(StsSuccess, "Test message from echo action at " + new Date());
|
||||
}
|
||||
}
|
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.desk;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.alfresco.config.ConfigElement;
|
||||
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.jlan.server.filesys.DiskSharedDevice;
|
||||
import org.alfresco.service.cmr.repository.ScriptException;
|
||||
import org.alfresco.service.cmr.repository.ScriptService;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
|
||||
/**
|
||||
* Javascript Desktop Action Class
|
||||
*
|
||||
* <p>Run a server-side script against the target node(s).
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public class JavaScriptDesktopAction extends DesktopAction {
|
||||
|
||||
// Script service
|
||||
|
||||
private ScriptService m_scriptService;
|
||||
|
||||
// Script name
|
||||
|
||||
private String m_scriptName;
|
||||
|
||||
// Script file details
|
||||
|
||||
private String m_scriptPath;
|
||||
private long m_lastModified;
|
||||
|
||||
// Script string
|
||||
|
||||
private String m_script;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public JavaScriptDesktopAction()
|
||||
{
|
||||
super( 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the confirmation string to be displayed by the client
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
@Override
|
||||
public String getConfirmationString()
|
||||
{
|
||||
return "Run Javascript action";
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the action
|
||||
*
|
||||
* @param global ConfigElement
|
||||
* @param config ConfigElement
|
||||
* @param fileSys DiskSharedDevice
|
||||
* @exception DesktopActionException
|
||||
*/
|
||||
@Override
|
||||
public void initializeAction(ConfigElement global, ConfigElement config, DiskSharedDevice fileSys)
|
||||
throws DesktopActionException
|
||||
{
|
||||
|
||||
// Perform standard initialization
|
||||
|
||||
super.initializeAction(global, config, fileSys);
|
||||
|
||||
// Get the script file name and check that it exists
|
||||
|
||||
ConfigElement elem = config.getChild("script");
|
||||
if ( elem != null && elem.getValue().length() > 0)
|
||||
{
|
||||
// Set the script name
|
||||
|
||||
setScriptName(elem.getValue());
|
||||
|
||||
// Check if the script exists on the classpath
|
||||
|
||||
URL scriptURL = this.getClass().getClassLoader().getResource(getScriptName());
|
||||
if ( scriptURL == null)
|
||||
throw new DesktopActionException("Failed to find script on classpath, " + getScriptName());
|
||||
|
||||
// Decode the URL path, it might contain escaped characters
|
||||
|
||||
String scriptURLPath = null;
|
||||
try
|
||||
{
|
||||
scriptURLPath = URLDecoder.decode( scriptURL.getFile(), "UTF-8");
|
||||
}
|
||||
catch ( UnsupportedEncodingException ex)
|
||||
{
|
||||
throw new DesktopActionException("Failed to decode script path, " + ex.getMessage());
|
||||
}
|
||||
|
||||
// Check that the script file exists
|
||||
|
||||
File scriptFile = new File(scriptURLPath);
|
||||
if ( scriptFile.exists() == false)
|
||||
throw new DesktopActionException("Script file not found, " + elem.getValue());
|
||||
|
||||
m_scriptPath = scriptFile.getAbsolutePath();
|
||||
m_lastModified =scriptFile.lastModified();
|
||||
|
||||
// Load the script
|
||||
|
||||
try
|
||||
{
|
||||
loadScript( scriptFile);
|
||||
}
|
||||
catch ( IOException ex)
|
||||
{
|
||||
throw new DesktopActionException( "Failed to load script, " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new DesktopActionException("Script name not specified");
|
||||
|
||||
// check if the desktop action attributes have been specified
|
||||
|
||||
elem = config.getChild("attributes");
|
||||
if ( elem != null)
|
||||
{
|
||||
// Check if the attribute string is empty
|
||||
|
||||
if ( elem.getValue().length() == 0)
|
||||
throw new DesktopActionException("Empty desktop action attributes");
|
||||
|
||||
// Parse the attribute string
|
||||
|
||||
int attr = 0;
|
||||
StringTokenizer tokens = new StringTokenizer( elem.getValue(), ",");
|
||||
|
||||
while ( tokens.hasMoreTokens())
|
||||
{
|
||||
// Get the current attribute token and validate
|
||||
|
||||
String token = tokens.nextToken().trim();
|
||||
|
||||
if ( token.equalsIgnoreCase( "targetFiles"))
|
||||
attr |= AttrTargetFiles;
|
||||
else if ( token.equalsIgnoreCase( "targetFolders"))
|
||||
attr |= AttrTargetFolders;
|
||||
else if ( token.equalsIgnoreCase( "clientFiles"))
|
||||
attr |= AttrClientFiles;
|
||||
else if ( token.equalsIgnoreCase( "clientFolders"))
|
||||
attr |= AttrClientFolders;
|
||||
else if ( token.equalsIgnoreCase( "alfrescoFiles"))
|
||||
attr |= AttrAlfrescoFiles;
|
||||
else if ( token.equalsIgnoreCase( "alfrescoFolders"))
|
||||
attr |= AttrAlfrescoFolders;
|
||||
else if ( token.equalsIgnoreCase( "multiplePaths"))
|
||||
attr |= AttrMultiplePaths;
|
||||
else if ( token.equalsIgnoreCase( "allowNoParams"))
|
||||
attr |= AttrAllowNoParams;
|
||||
else if ( token.equalsIgnoreCase( "anyFiles"))
|
||||
attr |= AttrAnyFiles;
|
||||
else if ( token.equalsIgnoreCase( "anyFolders"))
|
||||
attr |= AttrAnyFolders;
|
||||
else if ( token.equalsIgnoreCase( "anyFilesFolders"))
|
||||
attr |= AttrAnyFilesFolders;
|
||||
else
|
||||
throw new DesktopActionException("Unknown attribute, " + token);
|
||||
}
|
||||
|
||||
// Set the action attributes
|
||||
|
||||
setAttributes( attr);
|
||||
}
|
||||
|
||||
// Check if the desktop action pre-processing options have been specified
|
||||
|
||||
elem = config.getChild("preprocess");
|
||||
if ( elem != null)
|
||||
{
|
||||
// Check if the pre-process string is empty
|
||||
|
||||
int pre = 0;
|
||||
|
||||
if ( elem.getValue() != null && elem.getValue().length() > 0)
|
||||
{
|
||||
// Parse the pre-process string
|
||||
|
||||
StringTokenizer tokens = new StringTokenizer( elem.getValue(), ",");
|
||||
|
||||
while ( tokens.hasMoreTokens())
|
||||
{
|
||||
// Get the current pre-process token and validate
|
||||
|
||||
String token = tokens.nextToken().trim();
|
||||
|
||||
if ( token.equalsIgnoreCase( "copyToTarget"))
|
||||
pre |= PreCopyToTarget;
|
||||
else if ( token.equalsIgnoreCase( "confirm"))
|
||||
pre |= PreConfirmAction;
|
||||
else if ( token.equalsIgnoreCase( "localToWorkingCopy"))
|
||||
pre |= PreLocalToWorkingCopy;
|
||||
else
|
||||
throw new DesktopActionException("Unknown pre-processing flag, " + token);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the action pre-processing flags
|
||||
|
||||
setPreProcessActions( pre);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the desktop action
|
||||
*
|
||||
* @param params DesktopParams
|
||||
* @return DesktopResponse
|
||||
*/
|
||||
@Override
|
||||
public DesktopResponse runAction(DesktopParams params)
|
||||
throws DesktopActionException
|
||||
{
|
||||
// Check if the script file has been changed
|
||||
|
||||
DesktopResponse response = new DesktopResponse(StsSuccess);
|
||||
|
||||
File scriptFile = new File(m_scriptPath);
|
||||
if ( scriptFile.lastModified() != m_lastModified)
|
||||
{
|
||||
// Reload the script
|
||||
|
||||
m_lastModified = scriptFile.lastModified();
|
||||
|
||||
try
|
||||
{
|
||||
loadScript( scriptFile);
|
||||
}
|
||||
catch ( IOException ex)
|
||||
{
|
||||
response.setStatus(StsError, "Failed to reload script file, " + getScriptName());
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
// Start a transaction
|
||||
|
||||
params.getDriver().beginWriteTransaction( params.getSession());
|
||||
|
||||
// Access the script service
|
||||
|
||||
if ( getScriptService() != null)
|
||||
{
|
||||
// Create the objects to be passed to the script
|
||||
|
||||
Map<String, Object> model = new HashMap<String, Object>();
|
||||
model.put("deskParams", params);
|
||||
model.put("out", System.out);
|
||||
|
||||
// Add the webapp URL, if valid
|
||||
|
||||
if ( hasWebappURL())
|
||||
model.put("webURL", getWebappURL());
|
||||
|
||||
// Start a transaction
|
||||
|
||||
params.getDriver().beginWriteTransaction( params.getSession());
|
||||
|
||||
// Run the script
|
||||
|
||||
Object result = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Run the script
|
||||
|
||||
result = getScriptService().executeScriptString( getScript(), model);
|
||||
|
||||
// Check the result
|
||||
|
||||
if ( result != null)
|
||||
{
|
||||
// Check for a full response object
|
||||
|
||||
if ( result instanceof DesktopResponse)
|
||||
{
|
||||
response = (DesktopResponse) result;
|
||||
}
|
||||
|
||||
// Status code only response
|
||||
|
||||
else if ( result instanceof Double)
|
||||
{
|
||||
Double jsSts = (Double) result;
|
||||
response.setStatus( jsSts.intValue(), "");
|
||||
}
|
||||
|
||||
// Encoded response in the format '<stsCode>,<stsMessage>'
|
||||
|
||||
else if ( result instanceof String)
|
||||
{
|
||||
String responseMsg = (String) result;
|
||||
|
||||
// Parse the status message
|
||||
|
||||
StringTokenizer token = new StringTokenizer( responseMsg, ",");
|
||||
String stsToken = token.nextToken();
|
||||
String msgToken = token.nextToken();
|
||||
|
||||
int sts = -1;
|
||||
try
|
||||
{
|
||||
sts = Integer.parseInt( stsToken);
|
||||
}
|
||||
catch ( NumberFormatException ex)
|
||||
{
|
||||
response.setStatus( StsError, "Bad response from script");
|
||||
}
|
||||
|
||||
// Set the response
|
||||
|
||||
response.setStatus( sts, msgToken != null ? msgToken : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ScriptException ex)
|
||||
{
|
||||
// Set the error response for the client
|
||||
|
||||
response.setStatus( StsError, ex.getMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return an error response, script service not available
|
||||
|
||||
response.setStatus( StsError, "Script service not available");
|
||||
}
|
||||
|
||||
// Return the response
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the script service
|
||||
*
|
||||
* @return ScriptService
|
||||
*/
|
||||
protected final ScriptService getScriptService()
|
||||
{
|
||||
// Check if the script service has been initialized
|
||||
|
||||
if ( m_scriptService == null)
|
||||
{
|
||||
// Get the script service
|
||||
|
||||
m_scriptService = getServiceRegistry().getScriptService();
|
||||
}
|
||||
|
||||
// Return the script service
|
||||
|
||||
return m_scriptService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the script name
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public final String getScriptName()
|
||||
{
|
||||
return m_scriptName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the script data
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public final String getScript()
|
||||
{
|
||||
return m_script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the script name
|
||||
*
|
||||
* @param name String
|
||||
*/
|
||||
protected final void setScriptName(String name)
|
||||
{
|
||||
m_scriptName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load, or reload, the script
|
||||
*
|
||||
* @param scriptFile File
|
||||
*/
|
||||
private final void loadScript(File scriptFile)
|
||||
throws IOException
|
||||
{
|
||||
// Open the script file
|
||||
|
||||
BufferedReader scriptIn = new BufferedReader(new FileReader( scriptFile));
|
||||
StringBuilder scriptStr = new StringBuilder((int) scriptFile.length() + 256);
|
||||
|
||||
String inRec = scriptIn.readLine();
|
||||
|
||||
while ( inRec != null)
|
||||
{
|
||||
scriptStr.append( inRec);
|
||||
scriptStr.append( "\n");
|
||||
inRec = scriptIn.readLine();
|
||||
}
|
||||
|
||||
// Close the script file
|
||||
|
||||
scriptIn.close();
|
||||
|
||||
// Update the script string
|
||||
|
||||
m_script = scriptStr.toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 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.desk;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.alfresco.filesys.alfresco.DesktopAction;
|
||||
import org.alfresco.filesys.alfresco.DesktopParams;
|
||||
import org.alfresco.filesys.alfresco.DesktopResponse;
|
||||
|
||||
|
||||
/**
|
||||
* URL Desktop Action Class
|
||||
*
|
||||
* <p>Simple desktop action that returns a test URL.
|
||||
*
|
||||
* @author gkspencer
|
||||
*/
|
||||
public class URLDesktopAction extends DesktopAction {
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public URLDesktopAction()
|
||||
{
|
||||
super( 0, PreConfirmAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfirmationString() {
|
||||
return "Run URL action";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DesktopResponse runAction(DesktopParams params) {
|
||||
|
||||
// Get the local IP address
|
||||
|
||||
String ipAddr = null;
|
||||
|
||||
try
|
||||
{
|
||||
ipAddr = InetAddress.getLocalHost().getHostAddress();
|
||||
}
|
||||
catch (UnknownHostException ex)
|
||||
{
|
||||
}
|
||||
|
||||
// Return a URL in the status message to browse to the folder node
|
||||
|
||||
StringBuilder urlStr = new StringBuilder();
|
||||
|
||||
urlStr.append( "http://");
|
||||
urlStr.append(ipAddr);
|
||||
urlStr.append(":8080/alfresco/navigate/browse/workspace/SpacesStore/");
|
||||
urlStr.append( params.getFolderNode().getId());
|
||||
urlStr.append("?ticket=");
|
||||
urlStr.append(params.getTicket());
|
||||
|
||||
return new DesktopResponse(StsLaunchURL, urlStr.toString());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user