Britt Park cf0685b7ef Change to mapping of AVM aspects.
This seems to make indexing a bit quicker, and at least doesn't make other things
slower. Bulk import now just sucks; it used to be an order of magnitude suckier.
98% of that is due to Andy's recent changes.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@6103 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2007-06-26 01:15:39 +00:00

2681 lines
85 KiB
Java

/*
* 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.repo.avm;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.domain.DbAccessControlList;
import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.service.cmr.avm.AVMBadArgumentException;
import org.alfresco.service.cmr.avm.AVMCycleException;
import org.alfresco.service.cmr.avm.AVMException;
import org.alfresco.service.cmr.avm.AVMExistsException;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
import org.alfresco.service.cmr.avm.AVMNotFoundException;
import org.alfresco.service.cmr.avm.AVMStoreDescriptor;
import org.alfresco.service.cmr.avm.AVMWrongTypeException;
import org.alfresco.service.cmr.avm.LayeringDescriptor;
import org.alfresco.service.cmr.avm.VersionDescriptor;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.apache.log4j.Logger;
/**
* This or AVMStore are
* the implementors of the operations specified by AVMService.
* @author britt
*/
public class AVMRepository
{
@SuppressWarnings("unused")
private static Logger fgLogger = Logger.getLogger(AVMRepository.class);
/**
* The single instance of AVMRepository.
*/
private static AVMRepository fgInstance;
/**
* The current lookup count.
*/
private ThreadLocal<Integer> fLookupCount;
/**
* The node id issuer.
*/
private Issuer fNodeIssuer;
/**
* The layer id issuer.
*/
private Issuer fLayerIssuer;
/**
* Reference to the ContentStoreImpl
*/
private ContentStore fContentStore;
/**
* The Lookup Cache instance.
*/
private LookupCache fLookupCache;
private AVMStoreDAO fAVMStoreDAO;
private AVMNodeDAO fAVMNodeDAO;
private VersionRootDAO fVersionRootDAO;
private VersionLayeredNodeEntryDAO fVersionLayeredNodeEntryDAO;
private AVMStorePropertyDAO fAVMStorePropertyDAO;
private ChildEntryDAO fChildEntryDAO;
// A bunch of TransactionListeners that do work for this.
/**
* One for create store.
*/
private CreateStoreTxnListener fCreateStoreTxnListener;
/**
* One for purge store.
*/
private PurgeStoreTxnListener fPurgeStoreTxnListener;
/**
* One for create version.
*/
private CreateVersionTxnListener fCreateVersionTxnListener;
/**
* One for purge version.
*/
private PurgeVersionTxnListener fPurgeVersionTxnListener;
/**
* Create a new one.
*/
public AVMRepository()
{
fLookupCount = new ThreadLocal<Integer>();
fgInstance = this;
}
/**
* Set the node issuer. For Spring.
* @param nodeIssuer The issuer.
*/
public void setNodeIssuer(Issuer nodeIssuer)
{
fNodeIssuer = nodeIssuer;
}
/**
* Set the layer issuer. For Spring.
* @param layerIssuer The issuer.
*/
public void setLayerIssuer(Issuer layerIssuer)
{
fLayerIssuer = layerIssuer;
}
/**
* Set the ContentService.
*/
public void setContentStore(ContentStore store)
{
fContentStore = store;
}
/**
* Set the Lookup Cache instance.
* @param cache The instance to set.
*/
public void setLookupCache(LookupCache cache)
{
fLookupCache = cache;
}
public void setCreateStoreTxnListener(CreateStoreTxnListener listener)
{
fCreateStoreTxnListener = listener;
}
public void setPurgeStoreTxnListener(PurgeStoreTxnListener listener)
{
fPurgeStoreTxnListener = listener;
}
public void setCreateVersionTxnListener(CreateVersionTxnListener listener)
{
fCreateVersionTxnListener = listener;
}
public void setPurgeVersionTxnListener(PurgeVersionTxnListener listener)
{
fPurgeVersionTxnListener = listener;
}
public void setAvmStoreDAO(AVMStoreDAO dao)
{
fAVMStoreDAO = dao;
}
public void setAvmNodeDAO(AVMNodeDAO dao)
{
fAVMNodeDAO = dao;
}
public void setVersionRootDAO(VersionRootDAO dao)
{
fVersionRootDAO = dao;
}
public void setVersionLayeredNodeEntryDAO(VersionLayeredNodeEntryDAO dao)
{
fVersionLayeredNodeEntryDAO = dao;
}
public void setAvmStorePropertyDAO(AVMStorePropertyDAO dao)
{
fAVMStorePropertyDAO = dao;
}
public void setChildEntryDAO(ChildEntryDAO dao)
{
fChildEntryDAO = dao;
}
/**
* Create a file.
* @param path The path to the containing directory.
* @param name The name for the new file.
*/
public OutputStream createFile(String path, String name)
{
fLookupCount.set(1);
try
{
String[] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null) {
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
OutputStream out = store.createFile(pathParts[1], name);
return out;
}
finally
{
fLookupCount.set(null);
}
}
/**
* Create a file with the given File as content.
* @param path The path to the containing directory.
* @param name The name to give the file.
* @param data The file contents.
*/
public void createFile(String path, String name, File data)
{
fLookupCount.set(1);
try
{
String[] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.createFile(pathParts[1], name, data);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Create a new directory.
* @param path The path to the containing directory.
* @param name The name to give the directory.
*/
public void createDirectory(String path, String name)
{
fLookupCount.set(1);
try
{
String[] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.createDirectory(pathParts[1], name);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Create a new directory. This assumes that the parent is already
* copied and therefore should only be used with great care.
* @param parent The parent node.
* @param name The name of the new directory.
* @return A descriptor for the newly created directory.
*/
public AVMNodeDescriptor createDirectory(AVMNodeDescriptor parent, String name)
{
AVMNode node = fAVMNodeDAO.getByID(parent.getId());
if (node == null)
{
throw new AVMNotFoundException(parent.getId() + " not found.");
}
if (!(node instanceof DirectoryNode))
{
throw new AVMWrongTypeException("Not a directory.");
}
// We need the store to do anything so...
String [] pathParts = SplitPath(parent.getPath());
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
DirectoryNode dir = (DirectoryNode)node;
DirectoryNode child = null;
if (dir instanceof LayeredDirectoryNode)
{
child = new LayeredDirectoryNodeImpl((String)null, store, null);
((LayeredDirectoryNode)child).setPrimaryIndirection(false);
((LayeredDirectoryNode)child).setLayerID(parent.getLayerID());
}
else
{
child = new PlainDirectoryNodeImpl(store);
}
dir.putChild(name, child);
fLookupCache.onWrite(pathParts[0]);
return child.getDescriptor(parent.getPath(), name, parent.getIndirection(), parent.getIndirectionVersion());
}
/**
* Create a new layered directory.
* @param srcPath The target indirection for the new layered directory.
* @param dstPath The path to the containing directory.
* @param name The name for the new directory.
*/
public void createLayeredDirectory(String srcPath, String dstPath,
String name)
{
if (dstPath.indexOf(srcPath) == 0)
{
throw new AVMCycleException("Cycle would be created.");
}
fLookupCount.set(1);
try
{
String[] pathParts = SplitPath(dstPath);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.createLayeredDirectory(srcPath, pathParts[1], name);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Create a new layered file.
* @param srcPath The target indirection for the new layered file.
* @param dstPath The path to the containing directory.
* @param name The name of the new layered file.
*/
public void createLayeredFile(String srcPath, String dstPath, String name)
{
fLookupCount.set(1);
try
{
String[] pathParts = SplitPath(dstPath);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.createLayeredFile(srcPath, pathParts[1], name);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Create a new AVMStore.
* @param name The name to give the new AVMStore.
*/
public void createAVMStore(String name)
{
AlfrescoTransactionSupport.bindListener(fCreateStoreTxnListener);
if (getAVMStoreByName(name) != null)
{
throw new AVMExistsException("AVMStore exists: " + name);
}
// Newing up the object causes it to be written to the db.
@SuppressWarnings("unused")
AVMStore rep = new AVMStoreImpl(this, name);
// Special handling for AVMStore creation.
rep.getRoot().setStoreNew(null);
fCreateStoreTxnListener.storeCreated(name);
}
/**
* Create a new branch.
* @param version The version to branch off.
* @param srcPath The path to make a branch from.
* @param dstPath The containing directory.
* @param name The name of the new branch.
*/
public void createBranch(int version, String srcPath, String dstPath, String name)
{
if (dstPath.indexOf(srcPath) == 0)
{
throw new AVMCycleException("Cycle would be created.");
}
// Lookup the src node.
fLookupCount.set(1);
String [] pathParts;
Lookup sPath;
List<VersionLayeredNodeEntry> layeredEntries = null;
try
{
pathParts = SplitPath(srcPath);
AVMStore srcRepo = getAVMStoreByName(pathParts[0]);
if (srcRepo == null)
{
throw new AVMNotFoundException("Store not found.");
}
if (version < 0)
{
fLookupCache.onSnapshot(pathParts[0]);
version =
srcRepo.createSnapshot("Branch Snapshot", null, new HashMap<String, Integer>()).get(pathParts[0]);
}
sPath = srcRepo.lookup(version, pathParts[1], false, false);
if (sPath == null)
{
throw new AVMNotFoundException("Path not found.");
}
VersionRoot lastVersion = fVersionRootDAO.getByVersionID(srcRepo, version);
layeredEntries = fVersionLayeredNodeEntryDAO.get(lastVersion);
}
finally
{
fLookupCount.set(null);
}
// Lookup the destination directory.
fLookupCount.set(1);
try
{
pathParts = SplitPath(dstPath);
AVMStore dstRepo = getAVMStoreByName(pathParts[0]);
if (dstRepo == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1], true);
if (dPath == null)
{
throw new AVMNotFoundException("Path not found.");
}
DirectoryNode dirNode = (DirectoryNode)dPath.getCurrentNode();
AVMNode srcNode = sPath.getCurrentNode();
AVMNode dstNode = null;
// We do different things depending on what kind of thing we're
// branching from. I'd be considerably happier if we disallowed
// certain scenarios, but Jon won't let me :P (bhp).
if (srcNode.getType() == AVMNodeType.PLAIN_DIRECTORY)
{
dstNode = new PlainDirectoryNodeImpl((PlainDirectoryNode)srcNode, dstRepo);
}
else if (srcNode.getType() == AVMNodeType.LAYERED_DIRECTORY)
{
dstNode =
new LayeredDirectoryNodeImpl((LayeredDirectoryNode)srcNode, dstRepo);
((LayeredDirectoryNode)dstNode).setLayerID(issueLayerID());
}
else if (srcNode.getType() == AVMNodeType.LAYERED_FILE)
{
dstNode = new LayeredFileNodeImpl((LayeredFileNode)srcNode, dstRepo);
}
else // This is a plain file.
{
dstNode = new PlainFileNodeImpl((PlainFileNode)srcNode, dstRepo);
}
// dstNode.setVersionID(dstRepo.getNextVersionID());
dstNode.setAncestor(srcNode);
dirNode.putChild(name, dstNode);
dirNode.updateModTime();
String beginingPath = AVMNodeConverter.NormalizePath(srcPath);
String finalPath = AVMNodeConverter.ExtendAVMPath(dstPath, name);
finalPath = AVMNodeConverter.NormalizePath(finalPath);
VersionRoot latestVersion = fVersionRootDAO.getMaxVersion(dstRepo);
for (VersionLayeredNodeEntry entry : layeredEntries)
{
String path = entry.getPath();
if (!path.startsWith(srcPath))
{
continue;
}
String newPath = finalPath + path.substring(beginingPath.length());
VersionLayeredNodeEntry newEntry =
new VersionLayeredNodeEntryImpl(latestVersion, newPath);
fVersionLayeredNodeEntryDAO.save(newEntry);
}
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get an output stream to a file.
* @param path The full path to the file.
* @return An OutputStream.
*/
public OutputStream getOutputStream(String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
OutputStream out = store.getOutputStream(pathParts[1]);
return out;
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get a content reader from a file node.
* @param version The version of the file.
* @param path The path to the file.
* @return A ContentReader.
*/
public ContentReader getContentReader(int version, String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found: " + pathParts[0]);
}
return store.getContentReader(version, pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get a ContentWriter to a file node.
* @param path The path to the file.
* @return A ContentWriter.
*/
public ContentWriter createContentWriter(String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found: " + pathParts[0]);
}
fLookupCache.onWrite(pathParts[0]);
ContentWriter writer = store.createContentWriter(pathParts[1]);
return writer;
}
finally
{
fLookupCount.set(null);
}
}
/**
* Rename a node.
* @param srcPath Source containing directory.
* @param srcName Source name.
* @param dstPath Destination containing directory.
* @param dstName Destination name.
*/
public void rename(String srcPath, String srcName, String dstPath,
String dstName)
{
// This is about as ugly as it gets.
if ((dstPath + "/").indexOf(srcPath + srcName + "/") == 0)
{
throw new AVMCycleException("Cyclic rename.");
}
fLookupCount.set(1);
String [] pathParts;
Lookup sPath;
DirectoryNode srcDir;
AVMNode srcNode;
try
{
pathParts = SplitPath(srcPath);
AVMStore srcRepo = getAVMStoreByName(pathParts[0]);
if (srcRepo == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onDelete(pathParts[0]);
sPath = srcRepo.lookupDirectory(-1, pathParts[1], true);
if (sPath == null)
{
throw new AVMNotFoundException("Path not found.");
}
srcDir = (DirectoryNode)sPath.getCurrentNode();
srcNode = srcDir.lookupChild(sPath, srcName, false);
if (srcNode == null)
{
throw new AVMNotFoundException("Not found: " + srcName);
}
}
finally
{
fLookupCount.set(null);
}
fLookupCount.set(1);
try
{
pathParts = SplitPath(dstPath);
AVMStore dstRepo = getAVMStoreByName(pathParts[0]);
if (dstRepo == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1], true);
if (dPath == null)
{
throw new AVMNotFoundException("Path not found.");
}
DirectoryNode dstDir = (DirectoryNode)dPath.getCurrentNode();
AVMNode child = dstDir.lookupChild(dPath, dstName, true);
if (child != null && child.getType() != AVMNodeType.DELETED_NODE)
{
throw new AVMExistsException("Node exists: " + dstName);
}
AVMNode dstNode = null;
// We've passed the check, so we can go ahead and do the rename.
if (srcNode.getType() == AVMNodeType.PLAIN_DIRECTORY)
{
// If the source is layered then the renamed thing needs to be layered also.
if (sPath.isLayered())
{
// If this is a rename happening in the same layer we make a new
// OverlayedDirectoryNode that is not a primary indirection layer.
// Otherwise we do make the new OverlayedDirectoryNode a primary
// Indirection layer. This complexity begs the question of whether
// we should allow renames from within one layer to within another
// layer. Allowing it makes the logic absurdly complex.
if (dPath.isLayered() && dPath.getTopLayer().equals(sPath.getTopLayer()))
{
dstNode = new LayeredDirectoryNodeImpl((PlainDirectoryNode)srcNode, dstRepo, sPath, false);
((LayeredDirectoryNode)dstNode).setLayerID(sPath.getTopLayer().getLayerID());
}
else
{
dstNode = new LayeredDirectoryNodeImpl((DirectoryNode)srcNode, dstRepo, sPath, srcName);
((LayeredDirectoryNode)dstNode).setLayerID(issueLayerID());
}
}
else
{
dstNode = new PlainDirectoryNodeImpl((PlainDirectoryNode)srcNode, dstRepo);
}
}
else if (srcNode.getType() == AVMNodeType.LAYERED_DIRECTORY)
{
if (!sPath.isLayered() || (sPath.isInThisLayer() &&
srcDir.getType() == AVMNodeType.LAYERED_DIRECTORY &&
((LayeredDirectoryNode)srcDir).directlyContains(srcNode)))
{
// Use the simple 'copy' constructor.
dstNode =
new LayeredDirectoryNodeImpl((LayeredDirectoryNode)srcNode, dstRepo);
((LayeredDirectoryNode)dstNode).setLayerID(((LayeredDirectoryNode)srcNode).getLayerID());
}
else
{
// If the source node is a primary indirection, then the 'copy' constructor
// is used. Otherwise the alternate constructor is called and its
// indirection is calculated from it's source context.
if (((LayeredDirectoryNode)srcNode).getPrimaryIndirection())
{
dstNode =
new LayeredDirectoryNodeImpl((LayeredDirectoryNode)srcNode, dstRepo);
}
else
{
dstNode =
new LayeredDirectoryNodeImpl((DirectoryNode)srcNode, dstRepo, sPath, srcName);
}
// What needs to be done here is dependent on whether the
// rename is to a layered context. If so then it should get the layer id
// of its destination parent. Otherwise it should get a new layer
// id.
if (dPath.isLayered())
{
((LayeredDirectoryNode)dstNode).setLayerID(dPath.getTopLayer().getLayerID());
}
else
{
((LayeredDirectoryNode)dstNode).setLayerID(issueLayerID());
}
}
}
else if (srcNode.getType() == AVMNodeType.LAYERED_FILE)
{
dstNode = new LayeredFileNodeImpl((LayeredFileNode)srcNode, dstRepo);
}
else // This is a plain file node.
{
dstNode = new PlainFileNodeImpl((PlainFileNode)srcNode, dstRepo);
}
srcDir.removeChild(sPath, srcName);
srcDir.updateModTime();
// dstNode.setVersionID(dstRepo.getNextVersionID());
if (child != null)
{
dstNode.setAncestor(child);
}
dstDir.updateModTime();
dstDir.putChild(dstName, dstNode);
if (child == null)
{
dstNode.setAncestor(srcNode);
}
}
finally
{
fLookupCount.set(null);
}
}
/**
* Uncover a deleted name in a layered directory.
* @param dirPath The path to the layered directory.
* @param name The name to uncover.
*/
public void uncover(String dirPath, String name)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(dirPath);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.uncover(pathParts[1], name);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Create a snapshot of a single AVMStore.
* @param store The name of the repository.
* @param tag The short description.
* @param description The thick description.
* @return The version id of the newly snapshotted repository.
*/
public Map<String, Integer> createSnapshot(String storeName, String tag, String description)
{
AlfrescoTransactionSupport.bindListener(fCreateVersionTxnListener);
AVMStore store = getAVMStoreByName(storeName);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onSnapshot(storeName);
Map<String, Integer> result = store.createSnapshot(tag, description, new HashMap<String, Integer>());
for (Map.Entry<String, Integer> entry : result.entrySet())
{
fCreateVersionTxnListener.versionCreated(entry.getKey(), entry.getValue());
}
return result;
}
/**
* Remove a node and everything underneath it.
* @param path The path to the containing directory.
* @param name The name of the node to remove.
*/
public void remove(String path, String name)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onDelete(pathParts[0]);
store.removeNode(pathParts[1], name);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get rid of all content that lives only in the given AVMStore.
* Also removes the AVMStore.
* @param name The name of the AVMStore to purge.
*/
@SuppressWarnings("unchecked")
public void purgeAVMStore(String name)
{
AlfrescoTransactionSupport.bindListener(fPurgeStoreTxnListener);
AVMStore store = getAVMStoreByName(name);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onDelete(name);
AVMNode root = store.getRoot();
root.setIsRoot(false);
List<VersionRoot> vRoots = fVersionRootDAO.getAllInAVMStore(store);
for (VersionRoot vr : vRoots)
{
AVMNode node = vr.getRoot();
node.setIsRoot(false);
fVersionLayeredNodeEntryDAO.delete(vr);
fVersionRootDAO.delete(vr);
}
List<AVMNode> newGuys = fAVMNodeDAO.getNewInStore(store);
for (AVMNode newGuy : newGuys)
{
newGuy.setStoreNew(null);
}
fAVMStorePropertyDAO.delete(store);
fAVMStoreDAO.delete(store);
fAVMStoreDAO.invalidateCache();
fPurgeStoreTxnListener.storePurged(name);
}
/**
* Remove all content specific to a AVMRepository and version.
* @param name The name of the AVMStore.
* @param version The version to purge.
*/
public void purgeVersion(String name, int version)
{
AlfrescoTransactionSupport.bindListener(fPurgeVersionTxnListener);
AVMStore store = getAVMStoreByName(name);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onDelete(name);
store.purgeVersion(version);
fPurgeVersionTxnListener.versionPurged(name, version);
}
/**
* Get an input stream from a file.
* @param version The version to look under.
* @param path The path to the file.
* @return An InputStream.
*/
public InputStream getInputStream(int version, String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getInputStream(version, pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
public InputStream getInputStream(AVMNodeDescriptor desc)
{
AVMNode node = fAVMNodeDAO.getByID(desc.getId());
if (!(node instanceof FileNode))
{
throw new AVMWrongTypeException(desc + " is not a File.");
}
FileNode file = (FileNode)node;
ContentData data = file.getContentData(null);
if (data == null)
{
throw new AVMException(desc + " has no content.");
}
ContentReader reader = fContentStore.getReader(data.getContentUrl());
return reader.getContentInputStream();
}
/**
* Get a listing of a directory.
* @param version The version to look under.
* @param path The path to the directory.
* @param includeDeleted Whether to see DeletedNodes.
* @return A List of FolderEntries.
*/
public SortedMap<String, AVMNodeDescriptor> getListing(int version, String path,
boolean includeDeleted)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getListing(version, pathParts[1], includeDeleted);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get the list of nodes directly contained in a directory.
* @param version The version to look under.
* @param path The path to the directory to list.
* @return A Map of names to descriptors.
*/
public SortedMap<String, AVMNodeDescriptor> getListingDirect(int version, String path,
boolean includeDeleted)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getListingDirect(version, pathParts[1], includeDeleted);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get the list of nodes directly contained in a directory.
* @param dir The descriptor to the directory node.
* @param includeDeleted Whether to include deleted children.
* @return A Map of names to descriptors.
*/
public SortedMap<String, AVMNodeDescriptor>
getListingDirect(AVMNodeDescriptor dir, boolean includeDeleted)
{
AVMNode node = fAVMNodeDAO.getByID(dir.getId());
if (node == null)
{
throw new AVMBadArgumentException("Invalid Node.");
}
if (node.getType() == AVMNodeType.PLAIN_DIRECTORY)
{
return getListing(dir, includeDeleted);
}
if (node.getType() != AVMNodeType.LAYERED_DIRECTORY)
{
throw new AVMWrongTypeException("Not a directory.");
}
LayeredDirectoryNode dirNode = (LayeredDirectoryNode)node;
return dirNode.getListingDirect(dir, includeDeleted);
}
/**
* Get a directory listing from a directory node descriptor.
* @param dir The directory node descriptor.
* @return A SortedMap listing.
*/
public SortedMap<String, AVMNodeDescriptor> getListing(AVMNodeDescriptor dir, boolean includeDeleted)
{
fLookupCount.set(1);
try
{
AVMNode node = fAVMNodeDAO.getByID(dir.getId());
if (node == null)
{
throw new AVMBadArgumentException("Invalid Node.");
}
if (node.getType() != AVMNodeType.LAYERED_DIRECTORY &&
node.getType() != AVMNodeType.PLAIN_DIRECTORY)
{
throw new AVMWrongTypeException("Not a directory.");
}
DirectoryNode dirNode = (DirectoryNode)node;
return dirNode.getListing(dir, includeDeleted);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get the names of deleted nodes in a directory.
* @param version The version to look under.
* @param path The path to the directory.
* @return A List of names.
*/
public List<String> getDeleted(int version, String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getDeleted(version, pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get descriptors of all AVMStores.
* @return A list of all descriptors.
*/
@SuppressWarnings("unchecked")
public List<AVMStoreDescriptor> getAVMStores()
{
List<AVMStore> l = fAVMStoreDAO.getAll();
List<AVMStoreDescriptor> result = new ArrayList<AVMStoreDescriptor>();
for (AVMStore store : l)
{
result.add(store.getDescriptor());
}
return result;
}
/**
* Get a descriptor for an AVMStore.
* @param name The name to get.
* @return The descriptor.
*/
public AVMStoreDescriptor getAVMStore(String name)
{
AVMStore store = getAVMStoreByName(name);
if (store == null)
{
return null;
}
return store.getDescriptor();
}
/**
* Get all version for a given AVMStore.
* @param name The name of the AVMStore.
* @return A Set will all the version ids.
*/
public List<VersionDescriptor> getAVMStoreVersions(String name)
{
AVMStore store = getAVMStoreByName(name);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getVersions();
}
/**
* Get the set of versions between (inclusive) of the given dates.
* From or to may be null but not both.
* @param name The name of the AVMRepository.
* @param from The earliest date.
* @param to The latest date.
* @return The Set of version IDs.
*/
public List<VersionDescriptor> getAVMStoreVersions(String name, Date from, Date to)
{
AVMStore store = getAVMStoreByName(name);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getVersions(from, to);
}
/**
* Issue a node id.
* @return The new id.
*/
public long issueID()
{
return fNodeIssuer.issue();
}
/**
* Issue a new layer id.
* @return The new id.
*/
public long issueLayerID()
{
return fLayerIssuer.issue();
}
/**
* Get the indirection path for a layered node.
* @param version The version to look under.
* @param path The path to the node.
* @return The indirection path.
*/
public String getIndirectionPath(int version, String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getIndirectionPath(version, pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get the next version id for the given AVMStore.
* @param name The name of the AVMStore.
* @return The next version id.
*/
public int getLatestVersionID(String name)
{
AVMStore store = getAVMStoreByName(name);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getNextVersionID();
}
/**
* Get the latest extant snapshotted version id.
* @param name The store name.
*/
public int getLatestSnapshotID(String name)
{
AVMStore store = getAVMStoreByName(name);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getLastVersionID();
}
/**
* Get an AVMStore by name.
* @param name The name of the AVMStore.
* @return The AVMStore.
*/
private AVMStore getAVMStoreByName(String name)
{
AVMStore store = fAVMStoreDAO.getByName(name);
return store;
}
/**
* Get a descriptor for an AVMStore root.
* @param version The version to get.
* @param name The name of the AVMStore.
* @return The descriptor for the root.
*/
public AVMNodeDescriptor getAVMStoreRoot(int version, String name)
{
AVMStore store = getAVMStoreByName(name);
if (store == null)
{
throw new AVMNotFoundException("Not found: " + name);
}
return store.getRoot(version);
}
// TODO Fix this awful mess regarding cycle detection.
/**
* Lookup a node.
* @param version The version to look under.
* @param path The path to lookup.
* @param includeDeleted Whether to see DeletedNodes.
* @return A lookup object.
*/
public Lookup lookup(int version, String path, boolean includeDeleted)
{
Integer count = fLookupCount.get();
try
{
if (count == null)
{
fLookupCount.set(1);
}
else
{
fLookupCount.set(count + 1);
}
if (fLookupCount.get() > 50)
{
throw new AVMCycleException("Cycle in lookup.");
}
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
return null;
}
return store.lookup(version, pathParts[1], false, includeDeleted);
}
finally
{
if (count == null)
{
fLookupCount.set(null);
}
}
}
/**
* Lookup a descriptor from a directory descriptor.
* @param dir The directory descriptor.
* @param name The name of the child to lookup.
* @return The child's descriptor.
*/
public AVMNodeDescriptor lookup(AVMNodeDescriptor dir, String name, boolean includeDeleted)
{
fLookupCount.set(1);
try
{
AVMNode node = fAVMNodeDAO.getByID(dir.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found: " + dir.getId());
}
if (node.getType() != AVMNodeType.LAYERED_DIRECTORY &&
node.getType() != AVMNodeType.PLAIN_DIRECTORY)
{
throw new AVMWrongTypeException("Not a directory.");
}
DirectoryNode dirNode = (DirectoryNode)node;
return dirNode.lookupChild(dir, name, includeDeleted);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get all the paths to a particular node.
* @param desc The node descriptor.
* @return The list of version, paths.
*/
public List<Pair<Integer, String>> getPaths(AVMNodeDescriptor desc)
{
AVMNode node = fAVMNodeDAO.getByID(desc.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found: " + desc.getPath());
}
List<Pair<Integer, String>> paths = new ArrayList<Pair<Integer, String>>();
List<String> components = new ArrayList<String>();
recursiveGetPaths(node, components, paths);
return paths;
}
/**
* Get a single valid path for a node.
* @param desc The node descriptor.
* @return A version, path
*/
public Pair<Integer, String> getAPath(AVMNodeDescriptor desc)
{
AVMNode node = fAVMNodeDAO.getByID(desc.getId());
if (node == null)
{
throw new AVMNotFoundException("Could not find node: " + desc);
}
List<String> components = new ArrayList<String>();
return recursiveGetAPath(node, components);
}
/**
* Get all paths for a node reachable by HEAD.
* @param desc The node descriptor.
* @return A List of all the version, path Pairs that match.
*/
public List<Pair<Integer, String>> getHeadPaths(AVMNodeDescriptor desc)
{
AVMNode node = fAVMNodeDAO.getByID(desc.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found: " + desc.getPath());
}
List<Pair<Integer, String>> paths = new ArrayList<Pair<Integer, String>>();
List<String> components = new ArrayList<String>();
recursiveGetHeadPaths(node, components, paths);
return paths;
}
/**
* Gets all the pass from to the given node starting from the give version root.
* @param version The version root.
* @param node The node to get the paths of.
* @return A list of all paths in the given version to the node.
*/
public List<String> getVersionPaths(VersionRoot version, AVMNode node)
{
List<String> paths = new ArrayList<String>();
List<String> components = new ArrayList<String>();
recursiveGetVersionPaths(node, components, paths, version.getRoot(), version.getAvmStore().getName());
return paths;
}
/**
* Helper to get all version paths.
* @param node The current node we are examining.
* @param components The current path components.
* @param paths The list to contain found paths.
* @param root The root node of the version.
* @param storeName The name of the store.
*/
private void recursiveGetVersionPaths(AVMNode node, List<String> components, List<String> paths, DirectoryNode root, String storeName)
{
if (node.equals(root))
{
paths.add(this.makePath(components, storeName));
return;
}
List<ChildEntry> entries = fChildEntryDAO.getByChild(node);
for (ChildEntry entry : entries)
{
String name = entry.getKey().getName();
components.add(name);
AVMNode parent = entry.getKey().getParent();
recursiveGetVersionPaths(parent, components, paths, root, storeName);
components.remove(components.size() - 1);
}
}
/**
* Get all paths in a particular store in the head version for
* a particular node.
* @param desc The node descriptor.
* @param store The name of the store.
* @return All matching paths.
*/
public List<Pair<Integer, String>> getPathsInStoreHead(AVMNodeDescriptor desc, String store)
{
AVMStore st = getAVMStoreByName(store);
if (st == null)
{
throw new AVMNotFoundException("Store not found: " + store);
}
AVMNode node = fAVMNodeDAO.getByID(desc.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found: " + desc.getPath());
}
List<Pair<Integer, String>> paths = new ArrayList<Pair<Integer, String>>();
List<String> components = new ArrayList<String>();
recursiveGetPathsInStoreHead(node, components, paths, st.getRoot(), store);
return paths;
}
/**
* Do the actual work.
* @param node The current node.
* @param components The currently accumulated path components.
* @param paths The list to put full paths in.
*/
private void recursiveGetPaths(AVMNode node, List<String> components,
List<Pair<Integer, String>> paths)
{
if (node.getIsRoot())
{
AVMStore store = fAVMStoreDAO.getByRoot(node);
if (store != null)
{
addPath(components, -1, store.getName(), paths);
}
VersionRoot vr = fVersionRootDAO.getByRoot(node);
if (vr != null)
{
addPath(components, vr.getVersionID(), vr.getAvmStore().getName(), paths);
}
return;
}
List<ChildEntry> entries = fChildEntryDAO.getByChild(node);
for (ChildEntry entry : entries)
{
String name = entry.getKey().getName();
components.add(name);
AVMNode parent = entry.getKey().getParent();
recursiveGetPaths(parent, components, paths);
components.remove(components.size() - 1);
}
}
/**
* Do the work of getting one path for a node.
* @param node The node to get the path of.
* @param components The storage for path components.
* @return A path or null.
*/
private Pair<Integer, String> recursiveGetAPath(AVMNode node, List<String> components)
{
if (node.getIsRoot())
{
AVMStore store = fAVMStoreDAO.getByRoot(node);
if (store != null)
{
return new Pair<Integer, String>(-1, makePath(components, store.getName()));
}
VersionRoot vr = fVersionRootDAO.getByRoot(node);
if (vr != null)
{
return new Pair<Integer, String>(vr.getVersionID(), makePath(components, vr.getAvmStore().getName()));
}
return null;
}
List<ChildEntry> entries = fChildEntryDAO.getByChild(node);
for (ChildEntry entry : entries)
{
String name = entry.getKey().getName();
components.add(name);
Pair<Integer, String> path = recursiveGetAPath(entry.getKey().getParent(), components);
if (path != null)
{
return path;
}
components.remove(components.size() - 1);
}
return null;
}
/**
* Do the actual work.
* @param node The current node.
* @param components The currently accumulated path components.
* @param paths The list to put full paths in.
*/
private void recursiveGetHeadPaths(AVMNode node, List<String> components,
List<Pair<Integer, String>> paths)
{
if (node.getIsRoot())
{
AVMStore store = fAVMStoreDAO.getByRoot(node);
if (store != null)
{
addPath(components, -1, store.getName(), paths);
return;
}
return;
}
List<ChildEntry> entries = fChildEntryDAO.getByChild(node);
for (ChildEntry entry : entries)
{
String name = entry.getKey().getName();
components.add(name);
AVMNode parent = entry.getKey().getParent();
recursiveGetHeadPaths(parent, components, paths);
components.remove(components.size() - 1);
}
}
/**
* Do the actual work.
* @param node The current node.
* @param components The currently accumulated path components.
* @param paths The list to put full paths in.
*/
private void recursiveGetPathsInStoreHead(AVMNode node, List<String> components,
List<Pair<Integer, String>> paths, DirectoryNode root,
String storeName)
{
if (node.equals(root))
{
addPath(components, -1, storeName, paths);
return;
}
List<ChildEntry> entries = fChildEntryDAO.getByChild(node);
for (ChildEntry entry : entries)
{
String name = entry.getKey().getName();
components.add(name);
AVMNode parent = entry.getKey().getParent();
recursiveGetHeadPaths(parent, components, paths);
components.remove(components.size() - 1);
}
}
/**
* Add a path to the list.
* @param components The path name components.
* @param version The version id.
* @param storeName The name of the
* @param paths The List to add to.
*/
private void addPath(List<String> components, int version, String storeName,
List<Pair<Integer, String>> paths)
{
paths.add(new Pair<Integer, String>(version, makePath(components, storeName)));
}
/**
* Alternate version.
* @param components
* @param storeName
* @param paths
*/
private void addPath(List<String> components, String storeName,
List<String> paths)
{
paths.add(makePath(components, storeName));
}
/**
* Helper for generating paths.
* @param components The path components.
* @param storeName The store that the path is in.
* @return The path.
*/
private String makePath(List<String> components, String storeName)
{
StringBuilder pathBuilder = new StringBuilder();
pathBuilder.append(storeName);
pathBuilder.append(":");
if (components.size() == 0)
{
pathBuilder.append("/");
return pathBuilder.toString();
}
for (int i = components.size() - 1; i >= 0; i--)
{
pathBuilder.append("/");
pathBuilder.append(components.get(i));
}
return pathBuilder.toString();
}
/**
* Get information about layering of a path.
* @param version The version to look under.
* @param path The full avm path.
* @return A LayeringDescriptor.
*/
public LayeringDescriptor getLayeringInfo(int version, String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
Lookup lookup = store.lookup(version, pathParts[1], false, false);
if (lookup == null)
{
throw new AVMNotFoundException("Path not found.");
}
return new LayeringDescriptor(!lookup.getDirectlyContained(),
lookup.getAVMStore().getDescriptor(),
lookup.getFinalStore().getDescriptor());
}
finally
{
fLookupCount.set(null);
}
}
/**
* Lookup a directory specifically.
* @param version The version to look under.
* @param path The path to lookup.
* @return A lookup object.
*/
public Lookup lookupDirectory(int version, String path)
{
fLookupCount.set(fLookupCount.get() + 1);
if (fLookupCount.get() > 50)
{
throw new AVMCycleException("Cycle in lookup.");
}
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
return null;
}
return store.lookupDirectory(version, pathParts[1], false);
}
/**
* Utility to split a path, foo:bar/baz into its repository and path parts.
* @param path The fully qualified path.
* @return The repository name and the repository path.
*/
private String[] SplitPath(String path)
{
String [] pathParts = path.split(":");
if (pathParts.length != 2)
{
throw new AVMException("Invalid path: " + path);
}
return pathParts;
}
/**
* Make a directory into a primary indirection.
* @param path The full path.
*/
public void makePrimary(String path)
{
fLookupCount.set(1);
try
{
String[] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.makePrimary(pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Change what a layered directory points at.
* @param path The full path to the layered directory.
* @param target The new target path.
*/
public void retargetLayeredDirectory(String path, String target)
{
fLookupCount.set(1);
try
{
String[] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.retargetLayeredDirectory(pathParts[1], target);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get the history chain for a node.
* @param desc The node to get history of.
* @param count The maximum number of ancestors to traverse. Negative means all.
* @return A List of ancestors.
*/
public List<AVMNodeDescriptor> getHistory(AVMNodeDescriptor desc, int count)
{
AVMNode node = fAVMNodeDAO.getByID(desc.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found.");
}
if (count < 0)
{
count = Integer.MAX_VALUE;
}
List<AVMNodeDescriptor> history = new ArrayList<AVMNodeDescriptor>();
for (int i = 0; i < count; i++)
{
node = node.getAncestor();
if (node == null)
{
break;
}
history.add(node.getDescriptor("UNKNOWN", "UNKNOWN", "UNKNOWN", -1));
}
return history;
}
/**
* Set the opacity of a layered directory. An opaque directory hides
* the things it points to via indirection.
* @param path The path to the layered directory.
* @param opacity True is opaque; false is not.
*/
public void setOpacity(String path, boolean opacity)
{
fLookupCount.set(1);
try
{
String[] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.setOpacity(pathParts[1], opacity);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Set a property on a node.
* @param path The path to the node.
* @param name The name of the property.
* @param value The value of the property.
*/
public void setNodeProperty(String path, QName name, PropertyValue value)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.setNodeProperty(pathParts[1], name, value);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Set a collection of properties at once.
* @param path The path to the node.
* @param properties The Map of QNames to PropertyValues.
*/
public void setNodeProperties(String path, Map<QName, PropertyValue> properties)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.setNodeProperties(pathParts[1], properties);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get a property by name for a node.
* @param version The version to look under.
* @param path The path to the node.
* @param name The name of the property.
* @return The PropertyValue or null if it does not exist.
*/
public PropertyValue getNodeProperty(int version, String path, QName name)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getNodeProperty(version, pathParts[1], name);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get a Map of all the properties of a node.
* @param version The version to look under.
* @param path The path to the node.
* @return A Map of QNames to PropertyValues.
*/
public Map<QName, PropertyValue> getNodeProperties(int version, String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getNodeProperties(version, pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Delete a single property from a node.
* @param path The path to the node.
* @param name The name of the property.
*/
public void deleteNodeProperty(String path, QName name)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.deleteNodeProperty(pathParts[1], name);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Delete all properties on a node.
* @param path The path to the node.
*/
public void deleteNodeProperties(String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.deleteNodeProperties(pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Set a property on a store. Overwrites if property exists.
* @param store The AVMStore.
* @param name The QName.
* @param value The PropertyValue to set.
*/
public void setStoreProperty(String store, QName name, PropertyValue value)
{
AVMStore st = getAVMStoreByName(store);
if (st == null)
{
throw new AVMNotFoundException("Store not found.");
}
st.setProperty(name, value);
}
/**
* Set a group of properties on a store. Overwrites any properties that exist.
* @param store The AVMStore.
* @param props The properties to set.
*/
public void setStoreProperties(String store, Map<QName, PropertyValue> props)
{
AVMStore st = getAVMStoreByName(store);
if (st == null)
{
throw new AVMNotFoundException("Store not found.");
}
st.setProperties(props);
}
/**
* Get a property from a store.
* @param store The name of the store.
* @param name The property
* @return The property value or null if non-existent.
*/
public PropertyValue getStoreProperty(String store, QName name)
{
if (store == null)
{
throw new AVMBadArgumentException("Null store name.");
}
AVMStore st = getAVMStoreByName(store);
if (st == null)
{
throw new AVMNotFoundException("Store not found.");
}
return st.getProperty(name);
}
/**
* Queries a given store for properties with keys that match a given pattern.
* @param store The name of the store.
* @param keyPattern The sql 'like' pattern, inserted into a QName.
* @return A Map of the matching key value pairs.
*/
public Map<QName, PropertyValue> queryStorePropertyKey(String store, QName keyPattern)
{
AVMStore st = getAVMStoreByName(store);
if (st == null)
{
throw new AVMNotFoundException("Store not found.");
}
List<AVMStoreProperty> matches =
fAVMStorePropertyDAO.queryByKeyPattern(st,
keyPattern);
Map<QName, PropertyValue> results = new HashMap<QName, PropertyValue>();
for (AVMStoreProperty prop : matches)
{
results.put(prop.getName(), prop.getValue());
}
return results;
}
/**
* Queries all AVM stores for properties with keys that matcha given pattern.
* @param keyPattern The sql 'like' pattern, inserted into a QName.
* @return A List of Pairs of Store name, Map.Entry.
*/
public Map<String, Map<QName, PropertyValue>>
queryStoresPropertyKeys(QName keyPattern)
{
List<AVMStoreProperty> matches =
fAVMStorePropertyDAO.queryByKeyPattern(keyPattern);
Map<String, Map<QName, PropertyValue>> results =
new HashMap<String, Map<QName, PropertyValue>>();
for (AVMStoreProperty prop : matches)
{
String storeName = prop.getStore().getName();
Map<QName, PropertyValue> pairs = null;
if ((pairs = results.get(storeName)) == null)
{
pairs = new HashMap<QName, PropertyValue>();
results.put(storeName, pairs);
}
pairs.put(prop.getName(), prop.getValue());
}
return results;
}
/**
* Get all the properties for a store.
* @param store The name of the Store.
* @return A Map of all the properties.
*/
public Map<QName, PropertyValue> getStoreProperties(String store)
{
if (store == null)
{
throw new AVMBadArgumentException("Null store name.");
}
AVMStore st = getAVMStoreByName(store);
if (st == null)
{
throw new AVMNotFoundException("Store not found.");
}
return st.getProperties();
}
/**
* Delete a property from a store.
* @param store The name of the store.
* @param name The name of the property.
*/
public void deleteStoreProperty(String store, QName name)
{
AVMStore st = getAVMStoreByName(store);
if (st == null)
{
throw new AVMNotFoundException("Store not found.");
}
st.deleteProperty(name);
}
/**
* Get the common ancestor of two nodes if one exists. Unfortunately
* this is a quadratic problem, taking time proportional to the product
* of the lengths of the left and right history chains.
* @param left The first node.
* @param right The second node.
* @return The common ancestor. There are four possible results. Null means
* that there is no common ancestor. Left returned means that left is strictly
* an ancestor of right. Right returned means that right is strictly an
* ancestor of left. Any other non null return is the common ancestor and
* indicates that left and right are in conflict.
*/
public AVMNodeDescriptor getCommonAncestor(AVMNodeDescriptor left,
AVMNodeDescriptor right)
{
AVMNode lNode = fAVMNodeDAO.getByID(left.getId());
AVMNode rNode = fAVMNodeDAO.getByID(right.getId());
if (lNode == null || rNode == null)
{
throw new AVMNotFoundException("Node not found.");
}
List<AVMNode> leftHistory = new ArrayList<AVMNode>();
List<AVMNode> rightHistory = new ArrayList<AVMNode>();
while (lNode != null || rNode != null)
{
boolean checkRight = false;
if (lNode != null)
{
leftHistory.add(lNode);
checkRight = true;
lNode = lNode.getAncestor();
}
boolean checkLeft = false;
if (rNode != null)
{
rightHistory.add(rNode);
checkLeft = true;
rNode = rNode.getAncestor();
}
if (checkRight)
{
AVMNode check = leftHistory.get(leftHistory.size() - 1);
for (AVMNode node : rightHistory)
{
if (node.equals(check))
{
return node.getDescriptor("", "", "", -1);
}
}
}
if (checkLeft)
{
AVMNode check = rightHistory.get(rightHistory.size() - 1);
for (AVMNode node : leftHistory)
{
if (node.equals(check))
{
return node.getDescriptor("", "", "", -1);
}
}
}
}
return null;
}
/**
* Get the ContentData for a file.
* @param version The version to look under.
* @param path The path to the file.
* @return The ContentData for the file.
*/
public ContentData getContentDataForRead(int version, String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
return store.getContentDataForRead(version, pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get the ContentData for a file for writing.
* @param path The path to the file.
* @return The ContentData object.
*/
public ContentData getContentDataForWrite(String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
ContentData result = store.getContentDataForWrite(pathParts[1]);
return result;
}
finally
{
fLookupCount.set(null);
}
}
/**
* Set the ContentData on a file.
* @param path The path to the file.
* @param data The content data to set.
*/
public void setContentData(String path, ContentData data)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.setContentData(pathParts[1], data);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get the single instance of AVMRepository.
* @return The single instance.
*/
public static AVMRepository GetInstance()
{
return fgInstance;
}
public void setMetaDataFrom(String path, AVMNodeDescriptor from)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found: " + pathParts[0]);
}
AVMNode fromNode = fAVMNodeDAO.getByID(from.getId());
if (fromNode == null)
{
throw new AVMNotFoundException("Node not found: " + from.getPath());
}
fLookupCache.onWrite(pathParts[0]);
store.setMetaDataFrom(pathParts[1], fromNode);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Add an aspect to an AVM Node.
* @param path The path to the node.
* @param aspectName The name of the aspect.
*/
public void addAspect(String path, QName aspectName)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.addAspect(pathParts[1], aspectName);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get all the aspects on an AVM node.
* @param version The version to look under.
* @param path The path to the node.
* @return A List of the QNames of the Aspects.
*/
public Set<QName> getAspects(int version, String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getAspects(version, pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Remove an aspect and all associated properties from a node.
* @param path The path to the node.
* @param aspectName The name of the aspect.
*/
public void removeAspect(String path, QName aspectName)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.removeAspect(pathParts[1], aspectName);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Does a node have a particular aspect.
* @param version The version to look under.
* @param path The path to the node.
* @param aspectName The name of the aspect.
* @return Whether the node has the aspect.
*/
public boolean hasAspect(int version, String path, QName aspectName)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.hasAspect(version, pathParts[1], aspectName);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Set the ACL on a node.
* @param path The path to the node.
* @param acl The ACL to set.
*/
public void setACL(String path, DbAccessControlList acl)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
store.setACL(pathParts[1], acl);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Get the ACL on a node.
* @param version The version to look under.
* @param path The path to the node.
* @return The ACL.
*/
public DbAccessControlList getACL(int version, String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
return store.getACL(version, pathParts[1]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Link a node into a directory, directly.
* @param parentPath The path to the parent.
* @param name The name to give the node.
* @param toLink The node to link.
*/
public void link(String parentPath, String name, AVMNodeDescriptor toLink)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(parentPath);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
store.link(pathParts[1], name, toLink);
fLookupCache.onWrite(pathParts[0]);
}
finally
{
fLookupCount.set(null);
}
}
/**
* This is the danger version of link. It must be called on
* a copied and unsnapshotted directory. It blithely inserts
* a child without checking if a child exists with a conflicting name.
* @param parent The parent directory.
* @param name The name to give the child.
* @param child The child to link in.
*/
public void link(AVMNodeDescriptor parent, String name, AVMNodeDescriptor child)
{
AVMNode node = fAVMNodeDAO.getByID(parent.getId());
if (!(node instanceof DirectoryNode))
{
throw new AVMWrongTypeException("Not a Directory.");
}
DirectoryNode dir = (DirectoryNode)node;
if (!dir.getIsNew())
{
throw new AVMException("Directory has not already been copied.");
}
dir.link(name, child);
}
/**
* Remove name without leaving behind a deleted node. Dangerous
* if used unwisely.
* @param path The path to the layered directory.
* @param name The name of the child.
*/
public void flatten(String path, String name)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onDelete(pathParts[0]);
Lookup lPath = store.lookup(-1, pathParts[1], true, false);
AVMNode node = lPath.getCurrentNode();
if (node == null)
{
throw new AVMNotFoundException("Path not found.");
}
if (!(node instanceof LayeredDirectoryNode))
{
throw new AVMWrongTypeException("Not a Layered Directory.");
}
LayeredDirectoryNode dir = (LayeredDirectoryNode)node;
dir.flatten(name);
fAVMNodeDAO.flush();
}
finally
{
fLookupCount.set(null);
}
}
/**
* Force a copy on write.
* @param path The path to force.
*/
public AVMNodeDescriptor forceCopy(String path)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found.");
}
fLookupCache.onWrite(pathParts[0]);
// Just force a copy if needed by looking up in write mode.
Lookup lPath = store.lookup(-1, pathParts[1], true, true);
if (lPath == null)
{
throw new AVMNotFoundException("Path not found.");
}
AVMNode node = lPath.getCurrentNode();
return node.getDescriptor(lPath);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Rename a store.
* @param sourceName The original name.
* @param destName The new name.
* @throws AVMNotFoundException
* @throws AVMExistsException
*/
public void renameStore(String sourceName, String destName)
{
AlfrescoTransactionSupport.bindListener(fPurgeStoreTxnListener);
AlfrescoTransactionSupport.bindListener(fCreateStoreTxnListener);
AVMStore store = getAVMStoreByName(sourceName);
if (store == null)
{
throw new AVMNotFoundException("Store Not Found: " + sourceName);
}
if (getAVMStoreByName(destName) != null)
{
throw new AVMExistsException("Store Already Exists: " + destName);
}
if (!FileNameValidator.IsValid(destName))
{
throw new AVMBadArgumentException("Bad store name: " + destName);
}
store.setName(destName);
store.createSnapshot("Rename Store", "Rename Store from " + sourceName + " to " + destName, new HashMap<String, Integer>());
fLookupCache.onDelete(sourceName);
fAVMStoreDAO.invalidateCache();
fPurgeStoreTxnListener.storePurged(sourceName);
fCreateStoreTxnListener.storeCreated(destName);
}
/**
* Revert a head path to a given version. This works by cloning
* the version to revert to, and then linking that new version into head.
* The reverted version will have the previous head version as ancestor.
* @param path The path to the parent directory.
* @param name The name of the node.
* @param toRevertTo The descriptor of the version to revert to.
*/
public void revert(String path, String name, AVMNodeDescriptor toRevertTo)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found: " + pathParts[0]);
}
fLookupCache.onWrite(pathParts[0]);
store.revert(pathParts[1], name, toRevertTo);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Set the GUID on a node.
* @param path
* @param guid
*/
public void setGuid(String path, String guid)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store not found:" + pathParts[0]);
}
fLookupCache.onWrite(pathParts[0]);
store.setGuid(pathParts[1], guid);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Set the encoding on a node.
* @param path
* @param encoding
*/
public void setEncoding(String path, String encoding)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store Not Found: " + pathParts[0]);
}
fLookupCache.onWrite(pathParts[0]);
store.setEncoding(pathParts[1], encoding);
}
finally
{
fLookupCount.set(null);
}
}
/**
* Set the mime type on a node.
* @param path
* @param encoding
*/
public void setMimeType(String path, String mimeType)
{
fLookupCount.set(1);
try
{
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
if (store == null)
{
throw new AVMNotFoundException("Store Not Found: " + pathParts[0]);
}
fLookupCache.onWrite(pathParts[0]);
store.setMimeType(pathParts[1], mimeType);
}
finally
{
fLookupCount.set(null);
}
}
public List<String> getPathsInStoreVersion(AVMNodeDescriptor desc, String store, int version)
{
AVMNode node = fAVMNodeDAO.getByID(desc.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found: " + desc.getPath());
}
List<String> paths = new ArrayList<String>();
List<String> components = new ArrayList<String>();
recursiveGetStoreVersionPaths(store, node, version, components, paths);
return paths;
}
/**
* Do the actual work.
* @param node The current node.
* @param components The currently accumulated path components.
* @param paths The list to put full paths in.
*/
private void recursiveGetStoreVersionPaths(String storeName, AVMNode node, int version, List<String> components,
List<String> paths)
{
if (node.getIsRoot())
{
VersionRoot versionRoot = fVersionRootDAO.getByRoot(node);
if (versionRoot.getAvmStore().getName().equals(storeName) &&
versionRoot.getVersionID() == version)
{
addPath(components, storeName, paths);
return;
}
return;
}
List<ChildEntry> entries = fChildEntryDAO.getByChild(node);
for (ChildEntry entry : entries)
{
String name = entry.getKey().getName();
components.add(name);
AVMNode parent = entry.getKey().getParent();
recursiveGetStoreVersionPaths(storeName, parent, version, components, paths);
components.remove(components.size() - 1);
}
}
}