mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
17332: ETHREEOH-2020: Original tag disappears after a new one has been added to a wiki page 17711: Fix ETHREEOH-3720 - blocker (upgrade from 2.x) 17712: Build/test fix (FeedCleanerTest.testConcurrentAccessAndRemoval) 17726: DownloadContentServlet wraps call in read-only RetryingTransactionHelper 17736: Fix ETHREEOH-2821 - deployment of layered file (fix for 3.2 deployment) 17745: Fixed broken PostgreSQL (AVM, audit, etc) and Oracle (audit) 17746: Merged V3.1 to V3.2 17290: Merged V2.2 to V3.1 17282: (record-only) Fix for ETWOTWO-1387: Node deletions transactions not properly detected ... 17381: (record-only) Putting explicit version name on PDFRenderer.jar 17413: (record-only) Removed dev from label 17749: Type handler fixes for PostgreSQL: Use LONGVARBINARY 17750: Fixed propval mappings for DB2 17754: Fixed "invalid character" in Oracle port of propval 17757: Re-added fix for ETHREEOH-2789 that got lost in a merge. 17762: Fix FeedCleanerTest (for Oracle 10g) + exposed feed cleaner issue (maxSize) 17765: Assorted minor build fixes for Oracle 17767: Empty String property values are persisted as '.empty' 17768: Merged DEV/BELARUS/V3.2-2009_11_24 to V3.2 17758: ETHREEOH-3757: Oracle upgrade issue: failed "inviteEmailTemplate" patch 17772: Fix build (JBPMEngineUnitTest) - fallout from r17765 17774: Fix JSONtoFmModelTest 17781: Build/test fix (AVMCrawlTestP) 17791: Fix for ETHREEOH-3793: Oracle unit test failure for JobLockServiceTest - Missing unique indexes for all DB ports 17795: ETHREEOH-3696: Attempt to navigate into any existent web-project or create new one after upgrade from Enterprise 2.1.7 ... 17799: ETHREEOH-3796: org.alfresco.repo.importer.ImporterComponentTest fails 17807: Fix ETHREEOH-3723 - MT blocker (can't restart) 17816: Merged DEV/BELARUS/V3.2-2009_12_15 to V3.2 17814: ETHREEOH-3808: Upgrade fails if content cm:name contains curly brackets ('{}') 17822: Fix for ETHREEOH-3828: Mistake porting AlfrescoPostCreate-3.2-PropertyValueTables.sql script to mssql dialect 17823: Fix for ETHREEOH-3829: SQLServer index limit reached for 'idx_alf_props_str' 17831: Fix ETHREEOH-3818 - upgrade from Community 3.2r2 to Enterprise 3.2 17862: Merged V3.1 to V3.2 17722: Fix ETHREEOH-3643 - WCM layered folders (flatten) - also add unit test 17835: ETHREEOH-3763 - fix for WCM layered folders - also add unit test 17871: Merged V3.1 to V3.2 17855: ACT 12915 - create web project performance (-> setPermission -> buildIndirections) 17863: ETHREEOH-3852 - perf tweak (WCM layered files) ------------------------------------- Modified: svn:mergeinfo Merged /alfresco/BRANCHES/DEV/BELARUS/V3.2-2009_11_24:r17758 Merged /alfresco/BRANCHES/DEV/BELARUS/V3.2-2009_12_15:r17814 Merged /alfresco/BRANCHES/V2.2:r17282 Merged /alfresco/BRANCHES/V3.1:r17290,17381,17413,17722,17835,17855,17863 Merged /alfresco/BRANCHES/V3.2:r17332,17711-17712,17726,17736,17745-17746,17749-17750,17754,17757,17762,17765,17767-17768,17772,17774,17781,17791,17795,17799,17807,17816,17822-17823,17831,17862,17871 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18169 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
586 lines
18 KiB
Java
586 lines
18 KiB
Java
/*
|
|
* Copyright (C) 2005-2009 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.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import org.alfresco.repo.avm.util.AVMUtil;
|
|
import org.springframework.extensions.surf.util.Pair;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
/**
|
|
* This holds all the information necessary to perform operations
|
|
* on AVMNodes, and is structured internally as a list of path components
|
|
* from the root directory of a repository.
|
|
* @author britt
|
|
*/
|
|
class Lookup implements Serializable
|
|
{
|
|
private static final long serialVersionUID = -2844833688622561L;
|
|
|
|
private static Log logger = LogFactory.getLog(Lookup.class);
|
|
|
|
/**
|
|
* Is this lookup valid?
|
|
*/
|
|
private boolean fValid;
|
|
|
|
/**
|
|
* The AVMStore.
|
|
*/
|
|
private AVMStore fAVMStore;
|
|
|
|
/**
|
|
* The name of the AVMStore.
|
|
*/
|
|
private String fStoreName;
|
|
|
|
/**
|
|
* The components that make up this path.
|
|
*/
|
|
private List<LookupComponent> fComponents;
|
|
|
|
/**
|
|
* The final store in resolving layers.
|
|
*/
|
|
private AVMStore fFinalStore;
|
|
|
|
/**
|
|
* Whether, at this point, a layered node has been hit.
|
|
* Used while building a Lookup.
|
|
*/
|
|
private boolean fLayeredYet;
|
|
|
|
/**
|
|
* Whether we are directly contained at this point.
|
|
*/
|
|
private boolean fDirectlyContained;
|
|
|
|
/**
|
|
* The first LayeredDirectoryNode in the path.
|
|
*/
|
|
private LayeredDirectoryNode fTopLayer;
|
|
|
|
/**
|
|
* The path index of the top LayeredDirectoryNode in the path.
|
|
*/
|
|
private int fTopLayerIndex;
|
|
|
|
/**
|
|
* The lowest layered directory node's index seen so far.
|
|
*/
|
|
private int fLowestLayerIndex;
|
|
|
|
/**
|
|
* The current component being looked at by this lookup.
|
|
*/
|
|
private int fPosition;
|
|
|
|
/**
|
|
* Whether a needs-to-be-copied component has been seen.
|
|
*/
|
|
private boolean fNeedsCopying;
|
|
|
|
/**
|
|
* The version that is being looked up.
|
|
*/
|
|
private int fVersion;
|
|
|
|
public Lookup(Lookup other, AVMNodeDAO nodeDAO, AVMStoreDAO storeDAO)
|
|
{
|
|
fValid = true;
|
|
fAVMStore = storeDAO.getByID(other.fAVMStore.getId());
|
|
fVersion = other.fVersion;
|
|
if (fAVMStore == null)
|
|
{
|
|
fValid = false;
|
|
return;
|
|
}
|
|
fStoreName = fAVMStore.getName();
|
|
fComponents = new ArrayList<LookupComponent>();
|
|
fLayeredYet = other.fLayeredYet;
|
|
if (other.fTopLayer != null)
|
|
{
|
|
fTopLayer = (LayeredDirectoryNode)nodeDAO.getByID(other.fTopLayer.getId());
|
|
if (fTopLayer == null)
|
|
{
|
|
fValid = false;
|
|
return;
|
|
}
|
|
}
|
|
fPosition = other.fPosition;
|
|
fTopLayerIndex = other.fTopLayerIndex;
|
|
fLowestLayerIndex = other.fLowestLayerIndex;
|
|
fNeedsCopying = other.fNeedsCopying;
|
|
fDirectlyContained = other.fDirectlyContained;
|
|
if (fLayeredYet)
|
|
{
|
|
for (LookupComponent comp : other.fComponents)
|
|
{
|
|
LookupComponent newComp = new LookupComponent();
|
|
newComp.setName(comp.getName());
|
|
newComp.setIndirection(comp.getIndirection());
|
|
newComp.setIndirectionVersion(comp.getIndirectionVersion());
|
|
newComp.setNode(nodeDAO.getByID(comp.getNode().getId()));
|
|
if (newComp.getNode() == null)
|
|
{
|
|
fValid = false;
|
|
return;
|
|
}
|
|
fComponents.add(newComp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If this is not a layered lookup then we do not
|
|
// need to reload any of the actual nodes except for
|
|
// the last.
|
|
int i = 0;
|
|
for (; i < fPosition; ++i)
|
|
{
|
|
LookupComponent comp = other.fComponents.get(i);
|
|
LookupComponent newComp = new LookupComponent();
|
|
newComp.setName(comp.getName());
|
|
fComponents.add(newComp);
|
|
}
|
|
LookupComponent comp = other.fComponents.get(i);
|
|
LookupComponent newComp = new LookupComponent();
|
|
newComp.setName(comp.getName());
|
|
newComp.setNode(nodeDAO.getByID(comp.getNode().getId()));
|
|
if (newComp.getNode() == null)
|
|
{
|
|
fValid = false;
|
|
return;
|
|
}
|
|
fComponents.add(newComp);
|
|
}
|
|
fFinalStore = storeDAO.getByID(other.fFinalStore.getId());
|
|
if (fFinalStore == null)
|
|
{
|
|
fValid = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new one.
|
|
* @param store The AVMStore that's being looked in.
|
|
* @param storeName The name of that AVMStore.
|
|
*/
|
|
public Lookup(AVMStore store, String storeName, int version)
|
|
{
|
|
fValid = true;
|
|
fAVMStore = store;
|
|
fStoreName = storeName;
|
|
fVersion = version;
|
|
fComponents = new ArrayList<LookupComponent>();
|
|
fLayeredYet = false;
|
|
fTopLayer = null;
|
|
fPosition = -1;
|
|
fTopLayerIndex = -1;
|
|
fLowestLayerIndex = -1;
|
|
fNeedsCopying = false;
|
|
fDirectlyContained = true;
|
|
fFinalStore = store;
|
|
}
|
|
|
|
/**
|
|
* Is this a valid lookup?
|
|
*/
|
|
public boolean isValid()
|
|
{
|
|
return fValid;
|
|
}
|
|
|
|
// TODO This is badly in need of cleanup.
|
|
/**
|
|
* Add a new node to the lookup.
|
|
* @param node The node to add.
|
|
* @param name The name of the node in the path.
|
|
* @param write Whether this is in the context of
|
|
* a write operation.
|
|
*/
|
|
public void add(AVMNode node, String name, boolean directlyContained, boolean write)
|
|
{
|
|
LookupComponent comp = new LookupComponent();
|
|
comp.setName(name);
|
|
comp.setNode(node);
|
|
if (fPosition >= 0 && fDirectlyContained &&
|
|
getCurrentNode().getType() == AVMNodeType.LAYERED_DIRECTORY)
|
|
{
|
|
// if (directlyContained != ((DirectoryNode)fComponents.get(fPosition).getNode()).directlyContains(node))
|
|
// {
|
|
// System.err.println("Bloody Murder!");
|
|
// }
|
|
fDirectlyContained = directlyContained;
|
|
|
|
if (logger.isTraceEnabled())
|
|
{
|
|
logger.trace("add: fDirectlyContained = "+directlyContained);
|
|
}
|
|
}
|
|
if (!write)
|
|
{
|
|
if (node.getType() == AVMNodeType.LAYERED_DIRECTORY)
|
|
{
|
|
LayeredDirectoryNode oNode = (LayeredDirectoryNode)node;
|
|
if (oNode.getPrimaryIndirection())
|
|
{
|
|
comp.setIndirection(oNode.getIndirection());
|
|
comp.setIndirectionVersion(oNode.getIndirectionVersion());
|
|
}
|
|
else
|
|
{
|
|
Pair<String, Integer> ind = computeIndirection(name);
|
|
comp.setIndirection(ind.getFirst());
|
|
comp.setIndirectionVersion(ind.getSecond());
|
|
}
|
|
fLayeredYet = true;
|
|
// Record the first layer seen.
|
|
if (fTopLayer == null)
|
|
{
|
|
fTopLayer = oNode;
|
|
fTopLayerIndex = fPosition + 1;
|
|
}
|
|
fLowestLayerIndex = fPosition + 1;
|
|
}
|
|
fComponents.add(comp);
|
|
fPosition++;
|
|
return;
|
|
}
|
|
if (!node.getIsNew())
|
|
{
|
|
fNeedsCopying = true;
|
|
|
|
if (logger.isTraceEnabled())
|
|
{
|
|
logger.trace("add-cow: "+this+" ("+fPosition+") (not new)");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (fPosition >= 0 && !fDirectlyContained)
|
|
{
|
|
fNeedsCopying = true;
|
|
|
|
if (logger.isTraceEnabled())
|
|
{
|
|
logger.trace("add: COW: "+this+" ("+fPosition+") (new, not directly contained)");
|
|
}
|
|
}
|
|
}
|
|
// Record various things if this is layered.
|
|
if (node.getType() == AVMNodeType.LAYERED_DIRECTORY)
|
|
{
|
|
LayeredDirectoryNode oNode = (LayeredDirectoryNode)node;
|
|
// Record the indirection path that should be used.
|
|
if (oNode.getPrimaryIndirection())
|
|
{
|
|
comp.setIndirection(oNode.getIndirection());
|
|
comp.setIndirectionVersion(-1);
|
|
}
|
|
else
|
|
{
|
|
Pair<String, Integer> ind = computeIndirection(name);
|
|
comp.setIndirection(ind.getFirst());
|
|
comp.setIndirectionVersion(-1);
|
|
}
|
|
fLayeredYet = true;
|
|
// Record the first layer seen.
|
|
if (fTopLayer == null)
|
|
{
|
|
fTopLayer = oNode;
|
|
fTopLayerIndex = fPosition + 1;
|
|
}
|
|
fLowestLayerIndex = fPosition + 1;
|
|
}
|
|
// In a write context a plain directory contained in a layer will
|
|
// be copied so we will need to compute an indirection path.
|
|
else if (fLayeredYet)
|
|
{
|
|
Pair<String, Integer> ind = computeIndirection(name);
|
|
comp.setIndirection(ind.getFirst());
|
|
comp.setIndirectionVersion(-1);
|
|
}
|
|
fComponents.add(comp);
|
|
fPosition++;
|
|
// If we are in a write context do copy on write.
|
|
if (fNeedsCopying)
|
|
{
|
|
node = node.copy(this);
|
|
getCurrentLookupComponent().setNode(node);
|
|
if (fPosition == 0)
|
|
{
|
|
// Inform the store of a new root.
|
|
fAVMStore.setNewRoot((DirectoryNode)node);
|
|
|
|
((DirectoryNode)node).setIsRoot(true);
|
|
AVMDAOs.Instance().fAVMNodeDAO.update(((DirectoryNode)node));
|
|
AVMDAOs.Instance().fAVMStoreDAO.update(fAVMStore);
|
|
return;
|
|
}
|
|
// Not the root. Check if we are the top layer and insert this into it's parent.
|
|
if (fPosition == fTopLayerIndex)
|
|
{
|
|
fTopLayer = (LayeredDirectoryNode)node;
|
|
}
|
|
((DirectoryNode)fComponents.get(fPosition - 1).getNode()).putChild(name, node);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A helper for keeping track of indirection.
|
|
* @param name The name of the being added node.
|
|
* @return The indirection for the being added node.
|
|
*/
|
|
private Pair<String, Integer> computeIndirection(String name)
|
|
{
|
|
String parentIndirection = getCurrentIndirection();
|
|
int parentIndirectionVersion = getCurrentIndirectionVersion();
|
|
if (parentIndirection.endsWith("/"))
|
|
{
|
|
return new Pair<String, Integer>(parentIndirection + name, parentIndirectionVersion);
|
|
}
|
|
else
|
|
{
|
|
return new Pair<String, Integer>(parentIndirection + "/" + name, parentIndirectionVersion);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the parent node of the current node in the look up context or null if there is not one yet
|
|
* @return
|
|
*/
|
|
public DirectoryNode getCurrentNodeDirectory()
|
|
{
|
|
int position = fPosition -1;
|
|
if( (position >= 0) && (position < fComponents.size()))
|
|
{
|
|
return ((DirectoryNode)fComponents.get(fPosition - 1).getNode());
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private LookupComponent getCurrentLookupComponent()
|
|
{
|
|
return fComponents.get(fPosition);
|
|
}
|
|
|
|
/**
|
|
* Get the current node we're looking at.
|
|
* @return The current node.
|
|
*/
|
|
public AVMNode getCurrentNode()
|
|
{
|
|
return getCurrentLookupComponent().getNode();
|
|
}
|
|
|
|
/**
|
|
* Is the current path layered.
|
|
* @return Whether the current position in the path is layered.
|
|
*/
|
|
public boolean isLayered()
|
|
{
|
|
return fLayeredYet;
|
|
}
|
|
|
|
/**
|
|
* Determine if a node is directly in this layer.
|
|
* @return Whether this node is directly in this layer.
|
|
*/
|
|
public boolean isInThisLayer()
|
|
{
|
|
return fLayeredYet && fDirectlyContained;
|
|
}
|
|
|
|
/**
|
|
* Get the number of nodes.
|
|
* @return The number of nodes.
|
|
*/
|
|
public int size()
|
|
{
|
|
return fComponents.size();
|
|
}
|
|
|
|
/**
|
|
* Calculate the indirection path at this node.
|
|
* @return The indirection path all the way down to the current node.
|
|
*/
|
|
public String getIndirectionPath()
|
|
{
|
|
// The path is the underlying path of the lowest layer (in the path sense)
|
|
// that is directly contained by the top layer and is a primary indirection node.
|
|
int pos = fLowestLayerIndex;
|
|
AVMNode node = fComponents.get(pos).getNode();
|
|
LayeredDirectoryNode oNode = null;
|
|
while (pos >= fTopLayerIndex && (node.getType() != AVMNodeType.LAYERED_DIRECTORY ||
|
|
(oNode = (LayeredDirectoryNode)node).getLayerID() != fTopLayer.getLayerID() ||
|
|
!oNode.getPrimaryIndirection()))
|
|
{
|
|
pos--;
|
|
node = fComponents.get(pos).getNode();
|
|
}
|
|
oNode = (LayeredDirectoryNode)node;
|
|
// We've found it.
|
|
StringBuilder builder = new StringBuilder();
|
|
builder.append(oNode.getIndirection());
|
|
for (int i = pos + 1; i <= fPosition; i++)
|
|
{
|
|
builder.append("/");
|
|
builder.append(fComponents.get(i).getName());
|
|
}
|
|
return builder.toString();
|
|
}
|
|
|
|
/**
|
|
* Get the computed indirection for the current node.
|
|
* @return The indirection.
|
|
*/
|
|
public String getCurrentIndirection()
|
|
{
|
|
return getCurrentLookupComponent().getIndirection();
|
|
}
|
|
|
|
/**
|
|
* Get the computed indirection version for the current node.
|
|
* @return The indirection version.
|
|
*/
|
|
public int getCurrentIndirectionVersion()
|
|
{
|
|
return getCurrentLookupComponent().getIndirectionVersion();
|
|
}
|
|
|
|
/**
|
|
* Get the topmost Layered directory node. Topmost in the
|
|
* path lookup sense.
|
|
* @return The topmost layered directory node.
|
|
*/
|
|
public LayeredDirectoryNode getTopLayer()
|
|
{
|
|
return fTopLayer;
|
|
}
|
|
|
|
/**
|
|
* Get the store that this path is in.
|
|
* @return The store.
|
|
*/
|
|
public AVMStore getAVMStore()
|
|
{
|
|
return fAVMStore;
|
|
}
|
|
|
|
/**
|
|
* Get the path represented by this lookup.
|
|
* @return The canonical path for this lookup.
|
|
*/
|
|
public String getRepresentedPath()
|
|
{
|
|
if (fComponents.size() == 1)
|
|
{
|
|
return AVMUtil.buildAVMPath(fStoreName, AVMUtil.AVM_PATH_SEPARATOR); // root;
|
|
}
|
|
StringBuilder builder = new StringBuilder();
|
|
builder.append(fStoreName);
|
|
builder.append(AVMUtil.AVM_STORE_SEPARATOR_CHAR);
|
|
int count = fComponents.size();
|
|
for (int i = 1; i < count; i++)
|
|
{
|
|
builder.append(AVMUtil.AVM_PATH_SEPARATOR_CHAR);
|
|
builder.append(fComponents.get(i).getName());
|
|
}
|
|
return builder.toString();
|
|
}
|
|
|
|
/**
|
|
* Gets the final name in the lookup.
|
|
* @return The final name in the lookup.
|
|
*/
|
|
public String getBaseName()
|
|
{
|
|
return getCurrentLookupComponent().getName();
|
|
}
|
|
|
|
/**
|
|
* Set the final store the lookup occurred in.
|
|
* @param store The store to set.
|
|
*/
|
|
public void setFinalStore(AVMStore store)
|
|
{
|
|
fFinalStore = store;
|
|
}
|
|
|
|
/**
|
|
* Get the final store traversed during lookup.
|
|
* @return The final store traversed.
|
|
*/
|
|
public AVMStore getFinalStore()
|
|
{
|
|
return fFinalStore;
|
|
}
|
|
|
|
/**
|
|
* Get whether the node looked up is directly contained from the
|
|
* original root.
|
|
* @return Whether the node looked up is directly contained.
|
|
*/
|
|
public boolean getDirectlyContained()
|
|
{
|
|
return fDirectlyContained;
|
|
}
|
|
|
|
/**
|
|
* Get the version id that this is a lookup for.
|
|
* @return The version id.
|
|
*/
|
|
public int getVersion()
|
|
{
|
|
return fVersion;
|
|
}
|
|
|
|
// for debug
|
|
@Override
|
|
public String toString()
|
|
{
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
builder.append(getRepresentedPath());
|
|
builder.append(" [");
|
|
|
|
for (int i = 0; i < fComponents.size(); i++)
|
|
{
|
|
builder.append(AVMUtil.AVM_PATH_SEPARATOR_CHAR);
|
|
builder.append(fComponents.get(i).toString());
|
|
}
|
|
|
|
builder.append("]");
|
|
|
|
return builder.toString();
|
|
}
|
|
}
|