This checks in the current state of WCM development.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3128 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Britt Park
2006-06-16 16:59:15 +00:00
parent 6b52660c8a
commit 936f6d7021
57 changed files with 9563 additions and 3827 deletions

View File

@@ -43,8 +43,7 @@
</property>
</bean>
<bean id="permissionServiceImpl" class="org.alfresco.repo.security.permissions.impl.AllowPermissionServiceImpl">
<bean id="permissionServiceImpl" class="org.alfresco.repo.security.permissions.impl.PermissionServiceImpl">
<property name="nodeService">
<ref bean="nodeService" />
</property>
@@ -449,7 +448,7 @@
<!-- Lock and Unlock require the related aspect specific permissions. Querying the -->
<!-- lock status just requires read access to the node. -->
<bean id="LockService_security" class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="accessDecisionManager"/></property>
@@ -461,6 +460,7 @@
org.alfresco.service.cmr.lock.LockService.getLockStatus=ACL_NODE.0.sys:base.Read
org.alfresco.service.cmr.lock.LockService.getLockType=ACL_NODE.0.sys:base.Read
org.alfresco.service.cmr.lock.LockService.checkForLock=ACL_NODE.0.sys:base.Read
org.alfresco.service.cmr.lock.LockService.getLocks=ACL_NODE.0.sys:base.Read
</value>
</property>
</bean>

View File

@@ -3,87 +3,96 @@
<diskStore path="java.io.tmpdir"/>
<cacheManagerEventListenerFactory class="" properties=""/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
maxElementsInMemory="1000"
eternal="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.AVMNodeBeanImpl"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.ContentBeanImpl"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.Issuer"
maxElementsInMemory="10"
<cache name="org.alfresco.repo.avm.AVMNodeImpl"
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.RepositoryBeanImpl"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.FileContentImpl"
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.RepositoryImpl"
maxElementsInMemory="1000"
eternal="false"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.LayeredDirectoryNodeBeanImpl"
<cache name="org.alfresco.repo.avm.VersionRootImpl"
maxElementsInMemory="1000"
eternal="false"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.PlainDirectoryNodeBeanImpl"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.RepositoryBeanImpl.newNodes"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.LayeredDirectoryNodeBeanImpl.added"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.PlainDirectoryNodeBeanImpl.children"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.RepositoryBeanImpl.roots"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.alfresco.repo.avm.hibernate.LayeredDirectoryNodeBeanImpl.deleted"
maxElementsInMemory="1000"
eternal="false"
<cache name="org.alfresco.repo.avm.ChildEntryImpl"
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="ChildEntry.ByNameParent"
maxElementsInMemory="5000"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="ChildEntry.ByParent"
maxElementsInMemory="500"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="ChildEntry.ByParentChild"
maxElementsInMemory="1000"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="DeletedChild.ByParent"
maxElementsInMemory="1000"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="DeletedChild.ByNameParent"
maxElementsInMemory="1000"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.hibernate.cache.UpdateTimestampsCache"
maxElementsInMemory="500"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
<cache name="org.hibernate.cache.StandardQueryCache"
maxElementsInMemory="500"
eternal="true"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>

View File

@@ -3,22 +3,35 @@
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" >
<hibernate-configuration>
<session-factory>
<property name="connection.username">root</property>
<!--
<property name="connection.username">britt</property>
<property name="connection.password"></property>
<property name="connection.url">jdbc:postgresql://127.0.0.1/avm</property>
<property name="connection.driver_class">org.postgresql.Driver</property>
-->
<property name="connection.username">root</property>
<property name="connection.password"></property>
<property name="connection.url">jdbc:mysql://127.0.0.1/avm</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="current_session_context_class">thread</property>
<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<!-- <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property> -->
<property name="show_sql">false</property>
<property name="order_updates">false</property>
<property name="connection.isolation">2</property>
<property name="c3p0.min_size">5</property>
<property name="c3p0.max_size">20</property>
<property name="c3p0.timeout">900</property>
<property name="c3p0.max_statements">500</property>
<!--
<property name="connection.connection_provider">org.sciencething.cpool.ConnectionProvider</property>
<property name="cpool.size">20</property>
-->
<property name="c3p0.min_size">16</property>
<property name="c3p0.max_size">16</property>
<property name="c3p0.timeout">60</property>
<property name="c3p0.max_statements">0</property>
<property name="default_batch_fetch_size">16</property>
<property name="jdbc.batch_versioned_data">true</property>
<property name="cache.use_query_cache">false</property>
<property name="jdbc.batch_versioned_data">false</property>
<property name="jdbc.batch_size">0</property>
<property name="cach.use_second_level_cache">true</property>
<property name="cache.use_query_cache">true</property>
<mapping resource="org/alfresco/repo/avm/hibernate/AVM.hbm.xml"/>
</session-factory>
</hibernate-configuration>

View File

@@ -6,4 +6,4 @@ log4j.appender.stdout.layout.ConversionPattern=%d %5p %c{1}:%m%n
### Set log levels.
log4j.rootLogger=warn, stdout
log4j.logger.org.hibernate=info
log4j.logger.org.hibernate=fatal

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* This is thrown when bad or illegal arguments are passed.
* @author britt
*/
public class AVMBadArgumentException extends AVMException
{
private static final long serialVersionUID = -3651428546518806565L;
/**
* @param msgId
*/
public AVMBadArgumentException(String msgId)
{
super(msgId);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
*/
public AVMBadArgumentException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param cause
*/
public AVMBadArgumentException(String msgId, Throwable cause)
{
super(msgId, cause);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
* @param cause
*/
public AVMBadArgumentException(String msgId, Object[] msgParams,
Throwable cause)
{
super(msgId, msgParams, cause);
// TODO Auto-generated constructor stub
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* This is thrown when a cycle is detected during lookup.
* @author britt
*/
public class AVMCycleException extends AVMException
{
private static final long serialVersionUID = -7390775107865356648L;
/**
* @param msgId
*/
public AVMCycleException(String msgId)
{
super(msgId);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
*/
public AVMCycleException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param cause
*/
public AVMCycleException(String msgId, Throwable cause)
{
super(msgId, cause);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
* @param cause
*/
public AVMCycleException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
// TODO Auto-generated constructor stub
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Class for generic AVM Exceptions.
* @author britt
*/
public class AVMException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = -4894449240293309025L;
/**
* @param msgId
*/
public AVMException(String msgId)
{
super(msgId);
}
/**
* @param msgId
* @param msgParams
*/
public AVMException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
}
/**
* @param msgId
* @param cause
*/
public AVMException(String msgId, Throwable cause)
{
super(msgId, cause);
}
/**
* @param msgId
* @param msgParams
* @param cause
*/
public AVMException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* This is thrown when a node exists and shouldn't.
* @author britt
*/
public class AVMExistsException extends AVMException
{
private static final long serialVersionUID = -5079803858722700975L;
/**
* @param msgId
*/
public AVMExistsException(String msgId)
{
super(msgId);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
*/
public AVMExistsException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param cause
*/
public AVMExistsException(String msgId, Throwable cause)
{
super(msgId, cause);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
* @param cause
*/
public AVMExistsException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
// TODO Auto-generated constructor stub
}
}

View File

@@ -14,256 +14,137 @@
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import org.alfresco.repo.avm.hibernate.AVMNodeBean;
import org.alfresco.repo.avm.hibernate.DirectoryNodeBean;
/**
* Base class for all repository file system like objects.
* The Interface for versionable objects.
* @author britt
*/
public abstract class AVMNode
interface AVMNode
{
/**
* The AVMNodeBean that contains our data.
*/
private AVMNodeBean fData;
/**
* Default constructor.
*/
protected AVMNode()
{
fData = null;
}
/**
* Set the ancestor of this node.
* @param ancestor The ancestor to set.
*/
public void setAncestor(AVMNode ancestor)
{
fData.setAncestor(ancestor.getDataBean());
}
public void setAncestor(AVMNode ancestor);
/**
* Get the ancestor of this node.
* @return The ancestor of this node.
*/
public AVMNode getAncestor()
{
return AVMNodeFactory.CreateFromBean(fData.getAncestor());
}
public AVMNode getAncestor();
/**
* Set the merged from node.
* @param mergedFrom The merged from node.
*/
public void setMergedFrom(AVMNode mergedFrom)
{
fData.setMergedFrom(mergedFrom.getDataBean());
}
public void setMergedFrom(AVMNode mergedFrom);
/**
* Get the node this was merged from.
* @return The node this was merged from.
*/
public AVMNode getMergedFrom()
{
return AVMNodeFactory.CreateFromBean(fData.getMergedFrom());
}
/**
* Should this be copied on modification.
*/
public boolean shouldBeCopied()
{
return !fData.getIsNew();
}
/**
* Set to need copying or not.
* @param copyable Whether this should be copied.
*/
public void setShouldBeCopied(boolean copyable)
{
fData.setIsNew(!copyable);
}
public AVMNode getMergedFrom();
/**
* Get the version number.
* @return The version number.
*/
public long getVersion()
{
return fData.getVersionID();
}
public int getVersionID();
/**
* Set the version number.
* @param version The version number to set.
*/
public void setVersion(int version)
{
fData.setVersionID(version);
}
/**
* Get the branch id of this node.
* @return The branch id.
*/
public long getBranchID()
{
return fData.getBranchID();
}
/**
* Set the branch id on this node.
* @param branchID The id to set.
*/
public void setBranchID(long branchID)
{
fData.setBranchID(branchID);
}
public void setVersionID(int version);
/**
* Get the (possibly null) parent.
* @return The parent or null.
*/
public DirectoryNode getParent()
{
return (DirectoryNode)AVMNodeFactory.CreateFromBean(fData.getParent());
}
public DirectoryNode getParent();
/**
* Set the parent of this node.
* @param parent The DirectoryNode to set.
*/
public void setParent(DirectoryNode parent)
{
fData.setParent((DirectoryNodeBean)parent.getDataBean());
}
public void setParent(DirectoryNode parent);
/**
* Perform a COW if required.
* @param lPath The lookup path.
* @return A 'copied' version of this node.
*/
public AVMNode copyOnWrite(Lookup lPath)
{
// Call the subclass's copy on write logic.
AVMNode newMe = possiblyCopy(lPath);
// No copying needed, so short circuit.
if (newMe == null)
{
return this;
}
String myName = lPath.getName();
lPath.upCurrentNode();
Repository repos = lPath.getRepository();
newMe.setVersion(repos.getLatestVersion() + 1);
// Get our parent directory if we have one.
DirectoryNode parent = null;
if (getParent() != null)
{
parent = (DirectoryNode)lPath.getCurrentNode();
}
if (parent != null)
{
// Recursive invocation.
DirectoryNode newParent =
(DirectoryNode)parent.copyOnWrite(lPath);
newParent.putChild(myName, newMe);
newMe.setParent(newParent);
}
else // Null parent means root of repository.
{
repos.setNewRoot((DirectoryNode)newMe);
}
newMe.setRepository(repos);
newMe.setShouldBeCopied(false);
repos.setNew(newMe);
return newMe;
}
public AVMNode copyOnWrite(Lookup lPath);
/**
* Possibly copy ourselves.
* @param lPath The Lookup for this node.
* @return A copy of ourself or null if no copy was necessary.
*/
public abstract AVMNode possiblyCopy(Lookup lPath);
/**
* Handle any after recursive copy processing.
*
* @param parent The DirectoryNode that is the parent of
* this copied node, after recursive copying.
*/
public abstract void handlePostCopy(DirectoryNode parent);
public AVMNode possiblyCopy(Lookup lPath);
/**
* Set the repository for a node.
* @param repo The Repository to set.
*/
public void setRepository(Repository repo)
{
fData.setRepository(repo.getDataBean());
}
public void setRepository(Repository repo);
/**
* Get the data bean in this node.
* @return The data bean.
*/
public AVMNodeBean getDataBean()
{
return fData;
}
/**
* Set the data bean in this node.
* @param bean The data bean to set.
*/
public void setDataBean(AVMNodeBean bean)
{
fData = bean;
}
/**
* Get the type of this node.
*/
public abstract int getType();
public int getType();
/**
* Get the descriptor for this node.
* @param The Lookup.
* @return The descriptor for this node.
*/
public AVMNodeDescriptor getDescriptor(Lookup lPath);
/**
* Get a debugging string representation.
* Get a node descriptor for this node.
* @param parentPath The parent path.
* @param name The name looked up as.
* @param parentIndirection The indirection of the parent.
* @return The descriptor for this node.
*/
public AVMNodeDescriptor getDescriptor(String parentPath, String name, String parentIndirection);
/**
* Get the object id.
* @return The object id.
*/
public long getId();
/**
* Set this node's newness.
* @param isNew The newness.
*/
public void setIsNew(boolean isNew);
/**
* Get the newnews.
* @return Whether the node is new.
*/
public boolean getIsNew();
/**
* Get a string representation for debugging.
* @param lPath The Lookup.
* @return A String representation.
*/
public abstract String toString(Lookup lPath);
public String toString(Lookup lPath);
/**
* Set whether this node to be a root of a Repository.
* @param isRoot
*/
public void setIsRoot(boolean isRoot);
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
/**
* Get whether this node is a root of a Repository.
* @return Whether this node is a root.
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof AVMNode))
{
return false;
}
return getDataBean().equals(((AVMNode)obj).getDataBean());
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
return getDataBean().hashCode();
}
}
public boolean getIsRoot();
}

View File

@@ -0,0 +1,366 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* This class describes an AVM node object.
* It serves a similar purpose to the data structure
* returned by the stat() system call in UNIX.
*
* @author britt
*/
public class AVMNodeDescriptor
{
/**
* The path that this was looked up with.
*/
private String fPath;
/**
* The type of this node. AVMNodeType constants.
*/
private int fType;
/**
* The Owner.
*/
private String fOwner;
/**
* The Creator.
*/
private String fCreator;
/**
* The last modifier.
*/
private String fLastModifier;
/**
* The Create date.
*/
private long fCreateDate;
/**
* The Modification date.
*/
private long fModDate;
/**
* The Access date.
*/
private long fAccessDate;
/**
* The object id.
*/
private long fID;
/**
* The version number.
*/
private int fVersionID;
/**
* The indirection if this is a layer.
*/
private String fIndirection;
/**
* Is this a primary indirection node.
*/
private boolean fIsPrimary;
/**
* The layer id or -1 if this is not a layered node.
*/
private long fLayerID;
/**
* The length, if this is a file or -1 otherwise.
*/
private long fLength;
/**
* Make one up.
* @param path The looked up path.
* @param type The type of the node.
* @param creator The creator of the node.
* @param owner The owner of the node.
* @param lastModifier The last modifier of the node.
* @param createDate The creation date.
* @param modDate The modification date.
* @param accessDate The access date.
* @param id The object id.
* @param versionID The version id.
* @param indirection The indirection.
* @param isPrimary Whether this is a primary indirection.
* @param layerID The layer id.
* @param length The file length.
*/
public AVMNodeDescriptor(String path,
int type,
String creator,
String owner,
String lastModifier,
long createDate,
long modDate,
long accessDate,
long id,
int versionID,
String indirection,
boolean isPrimary,
long layerID,
long length)
{
fPath = path;
fType = type;
fCreator = creator;
fOwner = owner;
fLastModifier = lastModifier;
fCreateDate = createDate;
fModDate = modDate;
fAccessDate = accessDate;
fID = id;
fVersionID = versionID;
fIndirection = indirection;
fIsPrimary = isPrimary;
fLayerID = layerID;
fLength = length;
}
/**
* Get the last access date in java milliseconds.
* @return The last access date.
*/
public long getAccessDate()
{
return fAccessDate;
}
/**
* Get the creation date in java milliseconds.
* @return The creation date.
*/
public long getCreateDate()
{
return fCreateDate;
}
/**
* Get the user who created this.
* @return The creator.
*/
public String getCreator()
{
return fCreator;
}
/**
* Get the indirection path if this is layered or null.
* @return The indirection path or null.
*/
public String getIndirection()
{
return fIndirection;
}
/**
* Is this a primary indirection node. Will always
* be false for non-layered nodes.
* @return Whether this is a primary indirection node.
*/
public boolean isPrimary()
{
return fIsPrimary;
}
/**
* Determines whether this node corresponds to
* either a plain or layered file.
*
* @return true if AVMNodeDescriptor is a plain or layered file,
* otherwise false.
*/
public boolean isFile()
{
return ( fType == AVMNodeType.PLAIN_FILE ||
fType == AVMNodeType.LAYERED_FILE
);
}
/**
* Determines whether this node corresponds to
* a plain (non-layered) file.
*
* @return true if AVMNodeDescriptor is a plain file, otherwise false.
*/
public boolean isPlainFile()
{
return (fType == AVMNodeType.PLAIN_FILE);
}
/**
* Determines whether this node corresponds to
* a layered file.
*
* @return true if AVMNodeDescriptor is a layered file,
* otherwise false.
*/
public boolean isLayeredFile()
{
return (fType == AVMNodeType.LAYERED_FILE);
}
/**
* Determines whether this node corresponds to
* either a plain or layered directory.
*
* @return true if AVMNodeDescriptor is a plain or layered directory,
* otherwise false.
*/
public boolean isDirectory()
{
return ( fType == AVMNodeType.PLAIN_DIRECTORY ||
fType == AVMNodeType.LAYERED_DIRECTORY
);
}
/**
* Determines whether this node corresponds to
* a plain (non-layered) directory.
*
* @return true if AVMNodeDescriptor is a plain directory, otherwise false.
*/
public boolean isPlainDirectory()
{
return (fType == AVMNodeType.PLAIN_DIRECTORY );
}
/**
* Determines whether this node corresponds to
* a layered directory.
*
* @return true if AVMNodeDescriptor is a layered directory,
* otherwise false.
*/
public boolean isLayeredDirectory()
{
return (fType == AVMNodeType.LAYERED_DIRECTORY );
}
/**
* Get the user who last modified this node.
* @return Who last modified this node.
*/
public String getLastModifier()
{
return fLastModifier;
}
/**
* Get the layer id of this node.
* @return
*/
public long getLayerID()
{
return fLayerID;
}
/**
* Get the modification date of this node.
* @return The modification date.
*/
public long getModDate()
{
return fModDate;
}
/**
* Get the owner of this node.
* @return The owner of this node.
*/
public String getOwner()
{
return fOwner;
}
/**
* Get the path that this node was looked up by.
* @return The path by which this was looked up.
*/
public String getPath()
{
return fPath;
}
/**
* Get the type of this node. AVMNodeType constants.
* @return The type node.
*/
public int getType()
{
return fType;
}
/**
* Get the version id of this node.
* @return The version id of this node.
*/
public int getVersionID()
{
return fVersionID;
}
/**
* Get the object id.
* @return The object id.
*/
public long getId()
{
return fID;
}
public long getLength()
{
return fLength;
}
/**
* Get a debuggable string representation of this.
* @return A string representation of this.
*/
@Override
public String toString()
{
switch (fType)
{
case AVMNodeType.PLAIN_FILE :
return "[PF:" + fID + "]";
case AVMNodeType.PLAIN_DIRECTORY :
return "[PD:" + fID + "]";
case AVMNodeType.LAYERED_FILE :
return "[LF:" + fID + ":" + fIndirection + "]";
case AVMNodeType.LAYERED_DIRECTORY :
return "[LD:" + fID + ":" + fIndirection + "]";
default :
throw new AVMException("Internal Error.");
}
}
}

View File

@@ -0,0 +1,360 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.Serializable;
/**
* Base class for all repository file system like objects.
* @author britt
*/
abstract class AVMNodeImpl implements AVMNode, Serializable
{
/**
* The Object ID.
*/
private long fID;
/**
* The Version ID.
*/
private int fVersionID;
/**
* The ancestor of this.
*/
private AVMNode fAncestor;
/**
* The node that was merged into this.
*/
private AVMNode fMergedFrom;
/**
* The parent of this.
*/
private DirectoryNode fParent;
/**
* The Repository that owns this.
*/
private Repository fRepository;
/**
* The basic attributes of this. Owner, creator, mod time, etc.
*/
private BasicAttributes fBasicAttributes;
/**
* Whether this node is new (and should therefore not be COWed).
*/
private boolean fIsNew;
/**
* The version number (for concurrency control).
*/
private long fVers;
/**
* The rootness of this node.
*/
private boolean fIsRoot;
/**
* Default constructor.
*/
protected AVMNodeImpl()
{
}
/**
* Constructor used when creating a new concrete subclass instance.
* @param id The object id.
* @param repo The Repository that owns this.
*/
protected AVMNodeImpl(long id,
Repository repo)
{
fID = id;
fVersionID = -1;
fAncestor = null;
fMergedFrom = null;
fParent = null;
fRepository = repo;
fIsRoot = false;
long time = System.currentTimeMillis();
fBasicAttributes = new BasicAttributesImpl("britt",
"britt",
"britt",
time,
time,
time);
fIsNew = true;
}
/**
* Set the ancestor of this node.
* @param ancector The ancestor to set.
*/
public void setAncestor(AVMNode ancestor)
{
fAncestor = ancestor;
}
/**
* Get the ancestor of this node.
* @return The ancestor of this node.
*/
public AVMNode getAncestor()
{
return fAncestor;
}
/**
* Set the node that was merged into this.
* @param mergedFrom The node that was merged into this.
*/
public void setMergedFrom(AVMNode mergedFrom)
{
fMergedFrom = mergedFrom;
}
/**
* Get the node that was merged into this.
* @return The node that was merged into this.
*/
public AVMNode getMergedFrom()
{
return fMergedFrom;
}
/**
* Get the canonical parent of this node. This parent
* is the one under which this node was created.
* @return The canonical parent.
*/
public DirectoryNode getParent()
{
return fParent;
}
/**
* Set the canonical parent of this node.
* @param parent The canonical parent.
*/
public void setParent(DirectoryNode parent)
{
fParent = parent;
}
/**
* Perform a copy on write on this node and recursively
* up to the repository root. This is a template method
* which farms out work to possiblyCopy().
* @param lPath The Lookup.
*/
public AVMNode copyOnWrite(Lookup lPath)
{
// Call the subclass's copy on write logic.
AVMNode newMe = possiblyCopy(lPath);
// No copying needed, so short circuit.
if (newMe == null)
{
return this;
}
String myName = lPath.getName();
lPath.upCurrentNode();
Repository repos = lPath.getRepository();
newMe.setVersionID(repos.getNextVersionID());
// Get our parent directory if we have one.
DirectoryNode parent = null;
if (getParent() != null)
{
parent = (DirectoryNode)lPath.getCurrentNode();
}
if (parent != null)
{
// Recursive invocation.
DirectoryNode newParent =
(DirectoryNode)parent.copyOnWrite(lPath);
newParent.putChild(myName, newMe);
newMe.setParent(newParent);
}
else // Null parent means root of repository.
{
repos.setNewRoot((DirectoryNode)newMe);
}
newMe.setRepository(repos);
newMe.setIsNew(true);
return newMe;
}
/**
* Set the owning repository for this.
* @param repo The owning repository.
*/
public void setRepository(Repository repo)
{
fRepository = repo;
}
/**
* Get the repository that owns this.
* @return The repository.
*/
public Repository getRepository()
{
return fRepository;
}
/**
* Equality based on object ids.
* @param obj The thing to compare against.
* @return Equality.
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof AVMNode))
{
System.err.println("Failing AVMNodeImpl.equals");
return false;
}
return fID == ((AVMNode)obj).getId();
}
/**
* Get a reasonable hash value.
* @return The hash code.
*/
@Override
public int hashCode()
{
return (int)fID;
}
/**
* Set the object id. For Hibernate.
* @param id The id to set.
*/
protected void setId(long id)
{
fID = id;
}
/**
* Get the id of this node.
* @return The object id.
*/
public long getId()
{
return fID;
}
/**
* Set the versionID for this node.
* @param versionID The id to set.
*/
public void setVersionID(int versionID)
{
fVersionID = versionID;
}
/**
* Get the version id of this node.
* @return The version id.
*/
public int getVersionID()
{
return fVersionID;
}
/**
* Set the basic attributes. For Hibernate.
* @param attrs
*/
protected void setBasicAttributes(BasicAttributes attrs)
{
fBasicAttributes = attrs;
}
/**
* Get the basic attributes.
* @return The basic attributes.
*/
public BasicAttributes getBasicAttributes()
{
return fBasicAttributes;
}
/**
* Set whether this is new.
* @param isNew Whether this is new.
*/
public void setIsNew(boolean isNew)
{
fIsNew = isNew;
}
/**
* Get whether this is a new node.
* @return Whether this is new.
*/
public boolean getIsNew()
{
return fIsNew;
}
/**
* Set the version for concurrency control
* @param vers
*/
protected void setVers(long vers)
{
fVers = vers;
}
/**
* Get the version for concurrency control.
* @return
*/
protected long getVers()
{
return fVers;
}
/**
* @return
*/
public boolean getIsRoot()
{
return fIsRoot;
}
/**
* @param isRoot
*/
public void setIsRoot(boolean isRoot)
{
fIsRoot = isRoot;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* This is the exception thrown when a node is not found.
* @author britt
*/
public class AVMNotFoundException extends AVMException
{
private static final long serialVersionUID = -8131080195448129281L;
/**
* @param msgId
* @param msgParams
* @param cause
*/
public AVMNotFoundException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
*/
public AVMNotFoundException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param cause
*/
public AVMNotFoundException(String msgId, Throwable cause)
{
super(msgId, cause);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
*/
public AVMNotFoundException(String msgId)
{
super(msgId);
// TODO Auto-generated constructor stub
}
}

View File

@@ -19,7 +19,9 @@ package org.alfresco.repo.avm;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
@@ -27,6 +29,7 @@ import java.util.Set;
* Model. It specifies methods that are close in functionality to the underlying
* implementation, and is intended as both a first class Alfresco service and an
* aid in creating new implementations of existing services.
* Paths are of the form repositoryname:/foo/bar/baz
* @author britt
*/
public interface AVMService
@@ -40,11 +43,20 @@ public interface AVMService
public InputStream getFileInputStream(int version, String path);
/**
* Get an output stream to a file node.
* Get an output stream to a file node. The file must already exist.
* @param path The simple absolute path to the file node.
*/
public OutputStream getFileOutputStream(String path);
/**
* Get a random access file to the given path.
* @param version The version to find.
* @param path The path to the file.
* @param access The access mode for RandomAccessFile.
* @return A RandomAccessFile
*/
public RandomAccessFile getRandomAccess(int version, String path, String access);
/**
* Get a listing of a Folder by name.
* @param version The version id to look in.
@@ -54,14 +66,22 @@ public interface AVMService
public List<FolderEntry> getDirectoryListing(int version, String path);
/**
* Create a new File.
* @param parent The simple absolute path to the parent.
* @param name The name to give the file.
* Get a directory listing from a node descriptor.
* @param dir The directory node descriptor.
* @return A Map of names to node descriptors.
*/
public void createFile(String path, String name);
public Map<String, AVMNodeDescriptor> getDirectoryListing(AVMNodeDescriptor dir);
/**
* Create a new Folder.
* Create a new File. Fails if the file already exists.
* @param path The simple absolute path to the parent.
* @param name The name to give the file.
* @param source A possibly null stream to draw initial contents from.
*/
public OutputStream createFile(String path, String name);
/**
* Create a new directory.
* @param parent The simple absolute path to the parent.
* @param name The name to give the folder.
*/
@@ -69,26 +89,26 @@ public interface AVMService
/**
* Create a new layered file.
* @param srcPath The simple absolute path that the new file will shadow.
* @param srcPath The simple absolute path that the new file will point at.
* @param parent The simple absolute path to the parent.
* @param name The name to give the new file.
*/
public void createLayeredFile(String srcPath, String parent, String name);
/**
* Create a new layered folder.
* @param srcPath The simple absolute path that the new folder will shadow.
* Create a new layered directory.
* @param srcPath The simple absolute path that the new folder will point at.
* @param parent The simple absolute path to the parent.
* @param name The name to give the new folder.
*/
public void createLayeredDirectory(String srcPath, String parent, String name);
/**
* Retarget a layered folder.
* Retarget a layered directory.
* @param path Path to the layered directory.
* @param target The new target to aim at.
*/
public void retargetLayeredFolder(String path, String target);
public void retargetLayeredDirectory(String path, String target);
/**
* Create a new Repository. All Repositories are top level in a hierarchical
@@ -124,14 +144,13 @@ public interface AVMService
public void rename(String srcParent, String srcName, String dstParent, String dstName);
/**
* Slide a directory from one place in a layer to another uncovering what was
* underneath it.
* @param srcParent The simple absolute path to the parent folder.
* @param srcName The name of the node in the src Folder.
* @param dstParent The simple absolute path to the destination folder.
* @param dstName The name that the node will have in the destination folder.
* Uncover a name in a layered directory. That is, if the layered
* directory has a deleted entry of the given name remove that
* name from the deleted list.
* @param dirPath The path to the layered directory.
* @param name The name to uncover.
*/
public void slide(String srcParent, String srcName, String dstParent, String dstName);
public void uncover(String dirPath, String name);
/**
* Get the latest version id of the repository.
@@ -162,13 +181,35 @@ public interface AVMService
*/
public Set<Integer> getRepositoryVersions(String name);
/**
* Get the names of all repositories.
* @return A List of all names.
*/
public List<String> getRepositoryNames();
/**
* Get the specified root of a repository.
* @param version The version to look up.
* @param name The name of the repository.
* @return A descriptor for the specified root.
*/
public AVMNodeDescriptor getRepositoryRoot(int version, String name);
/**
* Lookup a node by version ids and path.
* @param version The version id to look under.
* @param path The simple absolute path to the parent directory.
* @return A Lookup object.
* @return An AVMNodeDescriptor.
*/
public Lookup lookup(int version, String path);
public AVMNodeDescriptor lookup(int version, String path);
/**
* Lookup a node from a directory node.
* @param dir The descriptor for the directory node.
* @param name The name to lookup.
* @return The descriptor for the child.
*/
public AVMNodeDescriptor lookup(AVMNodeDescriptor dir, String name);
/**
* Get the indirection path for a layered file or directory.
@@ -179,10 +220,10 @@ public interface AVMService
public String getIndirectionPath(int version, String path);
/**
* Destroy a repository. This is a complete wipe of a Repository.
* Purge a repository. This is a complete wipe of a Repository.
* @param name The name of the Repository.
*/
public void destroyRepository(String name);
public void purgeRepository(String name);
/**
* Purge a version from a repository. Deletes everything that lives in
@@ -191,4 +232,10 @@ public interface AVMService
* @param name The name of the Repository from which to purge it.
*/
public void purgeVersion(int version, String name);
/**
* Make a directory into a primary indirection node.
* @param path The full path.
*/
public void makePrimary(String path);
}

View File

@@ -0,0 +1,755 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.repo.avm.SuperRepository;
import org.alfresco.repo.avm.hibernate.HibernateHelper;
import org.alfresco.repo.avm.hibernate.HibernateTxn;
import org.alfresco.repo.avm.hibernate.HibernateTxnCallback;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.tool.hbm2ddl.SchemaExport;
/**
* Implements the AVMService. Stub.
* @author britt
*/
public class AVMServiceImpl implements AVMService
{
/**
* The Hibernate SessionFactory.
*/
private SessionFactory fSessionFactory;
/**
* The HibernateTxn.
*/
private HibernateTxn fTransaction;
/**
* The SuperRepository for each service thread.
*/
private SuperRepository fSuperRepository;
/**
* The storage directory.
*/
private String fStorage;
/**
* The node id issuer.
*/
private Issuer fNodeIssuer;
/**
* The content id issuer.
*/
private Issuer fContentIssuer;
/**
* The layer id issuer.
*/
private Issuer fLayerIssuer;
/**
* Basic constructor for the service.
* @param createTables Flag for whether tables should be created.
*/
public AVMServiceImpl()
{
fSessionFactory = HibernateHelper.GetSessionFactory();
fTransaction = new HibernateTxn(fSessionFactory);
}
/**
* Final initialization of the service. Must be called only on a
* fully initialized instance.
* @param createTables Whether we should create tables, and a default
* repository.
*/
public void init(boolean createTables)
{
if (createTables)
{
SchemaExport se = new SchemaExport(HibernateHelper.GetConfiguration());
se.drop(false, true);
se.create(false, true);
File storage = new File(fStorage);
storage.mkdirs();
fNodeIssuer = new Issuer(fStorage + File.separator + "node", 0L);
fContentIssuer = new Issuer(fStorage + File.separator + "content", 0L);
fLayerIssuer = new Issuer(fStorage + File.separator + "layer", 0L);
fSuperRepository = new SuperRepository(fNodeIssuer,
fContentIssuer,
fLayerIssuer,
fStorage);
createRepository("main");
}
else
{
try
{
fNodeIssuer = new Issuer(fStorage + File.separator + "node");
fContentIssuer = new Issuer(fStorage + File.separator + "content");
fLayerIssuer = new Issuer(fStorage + File.separator + "layer");
fSuperRepository = new SuperRepository(fNodeIssuer,
fContentIssuer,
fLayerIssuer,
fStorage);
}
catch (Exception e)
{
// TODO Log this and abort in some useful way.
}
}
}
/**
* Set the location of file storage.
* @param storage
*/
public void setStorage(String storage)
{
fStorage = storage;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getFileInputStream(int, java.lang.String)
*/
public InputStream getFileInputStream(final int version, final String path)
{
if (path == null)
{
throw new AVMBadArgumentException("Null path.");
}
class HTxnCallback implements HibernateTxnCallback
{
public InputStream in = null;
public void perform(Session session)
{
fSuperRepository.setSession(session);
in = fSuperRepository.getInputStream(version, path);
}
};
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.in;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getFileOutputStream(java.lang.String)
*/
public OutputStream getFileOutputStream(final String path)
{
if (path == null)
{
throw new AVMBadArgumentException("Null path.");
}
class HTxnCallback implements HibernateTxnCallback
{
public OutputStream out = null;
public void perform(Session session)
{
fSuperRepository.setSession(session);
out = fSuperRepository.getOutputStream(path);
}
};
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
return doit.out;
}
/**
* Get a random access file to the given file.
* @param version The version to look for (read-only)
* @param path The path to the file.
* @param access The access mode for RandomAccessFile
* @return A Random Access File.
*/
public RandomAccessFile getRandomAccess(final int version, final String path, final String access)
{
if (path == null || access == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public RandomAccessFile file;
public void perform(Session session)
{
fSuperRepository.setSession(session);
file = fSuperRepository.getRandomAccess(version, path, access);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
return doit.file;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getFolderListing(int, java.lang.String)
*/
public List<FolderEntry> getDirectoryListing(final int version, final String path)
{
if (path == null)
{
throw new AVMBadArgumentException("Null path.");
}
class HTxnCallback implements HibernateTxnCallback
{
public List<FolderEntry> listing;
public void perform(Session session)
{
fSuperRepository.setSession(session);
listing = fSuperRepository.getListing(version, path);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.listing;
}
/**
* Get a directory listing from a node descriptor.
* @param dir The directory node descriptor.
* @return A Map of names to node descriptors.
*/
public Map<String, AVMNodeDescriptor> getDirectoryListing(final AVMNodeDescriptor dir)
{
if (dir == null)
{
throw new AVMBadArgumentException("Null descriptor.");
}
class HTxnCallback implements HibernateTxnCallback
{
public Map<String, AVMNodeDescriptor> listing;
public void perform(Session session)
{
fSuperRepository.setSession(session);
listing = fSuperRepository.getListing(dir);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.listing;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createFile(java.lang.String, java.lang.String)
*/
public OutputStream createFile(final String path, final String name)
{
if (path == null || name == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public OutputStream out;
public void perform(Session session)
{
fSuperRepository.setSession(session);
out = fSuperRepository.createFile(path, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
return doit.out;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createFolder(java.lang.String, java.lang.String)
*/
public void createDirectory(final String path, final String name)
{
if (path == null || name == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.createDirectory(path, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createLayeredFile(java.lang.String, java.lang.String, java.lang.String)
*/
public void createLayeredFile(final String srcPath, final String parent, final String name)
{
if (srcPath == null || parent == null || name == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.createLayeredFile(srcPath, parent, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createLayeredFolder(java.lang.String, java.lang.String, java.lang.String)
*/
public void createLayeredDirectory(final String srcPath, final String parent, final String name)
{
if (srcPath == null || parent == null || name == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.createLayeredDirectory(srcPath, parent, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createRepository(java.lang.String)
*/
public void createRepository(final String name)
{
if (name == null)
{
throw new AVMBadArgumentException("Name is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.createRepository(name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createBranch(int, java.lang.String, java.lang.String, java.lang.String)
*/
public void createBranch(final int version, final String srcPath, final String dstPath,
final String name)
{
if (srcPath == null || dstPath == null || name == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.createBranch(version, srcPath, dstPath, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#removeNode(java.lang.String, java.lang.String)
*/
public void removeNode(final String parent, final String name)
{
if (parent == null || name == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.remove(parent, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#rename(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void rename(final String srcParent, final String srcName, final String dstParent,
final String dstName)
{
if (srcParent == null || srcName == null || dstParent == null || dstName == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.rename(srcParent, srcName, dstParent, dstName);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/**
* 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(final String dirPath, final String name)
{
if (dirPath == null || name == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.uncover(dirPath, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getLatestVersionID(java.lang.String)
*/
public int getLatestVersionID(final String repName)
{
if (repName == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public int latestVersionID;
public void perform(Session session)
{
fSuperRepository.setSession(session);
latestVersionID = fSuperRepository.getLatestVersionID(repName);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.latestVersionID;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createSnapshot(java.util.List)
*/
public void createSnapshot(final List<String> repositories)
{
if (repositories == null)
{
throw new AVMBadArgumentException("Repositories is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.createSnapshot(repositories);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createSnapshot(java.lang.String)
*/
public void createSnapshot(final String repository)
{
if (repository == null)
{
throw new AVMBadArgumentException("Repository is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.createSnapshot(repository);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#lookup(int, java.lang.String)
*/
public AVMNodeDescriptor lookup(final int version, final String path)
{
if (path == null)
{
throw new AVMBadArgumentException("Path is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public AVMNodeDescriptor descriptor;
public void perform(Session session)
{
fSuperRepository.setSession(session);
Lookup lookup = fSuperRepository.lookup(version, path);
descriptor = lookup.getCurrentNode().getDescriptor(lookup);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.descriptor;
}
/**
* Lookup a node descriptor from a directory node descriptor.
* @param dir The node descriptor of the directory.
* @param name The name to lookup.
* @return The node descriptor of the child.
*/
public AVMNodeDescriptor lookup(final AVMNodeDescriptor dir, final String name)
{
if (dir == null || name == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public AVMNodeDescriptor child;
public void perform(Session session)
{
fSuperRepository.setSession(session);
child = fSuperRepository.lookup(dir, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.child;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#destroyRepository(java.lang.String)
*/
public void purgeRepository(final String name)
{
if (name == null)
{
throw new AVMBadArgumentException("Name is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.purgeRepository(name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#purgeVersion(int, java.lang.String)
*/
public void purgeVersion(final int version, final String name)
{
if (name == null)
{
throw new AVMBadArgumentException("Name is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.purgeVersion(name, version);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getIndirectionPath(java.lang.String)
*/
public String getIndirectionPath(final int version, final String path)
{
if (path == null)
{
throw new AVMBadArgumentException("Path is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public String indirectionPath;
public void perform(Session session)
{
fSuperRepository.setSession(session);
indirectionPath = fSuperRepository.getIndirectionPath(version, path);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.indirectionPath;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getRepositoryVersions(java.lang.String)
*/
public Set<Integer> getRepositoryVersions(final String name)
{
if (name == null)
{
throw new AVMBadArgumentException("Name is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public Set<Integer> versions;
public void perform(Session session)
{
fSuperRepository.setSession(session);
versions = fSuperRepository.getRepositoryVersions(name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.versions;
}
/**
* Change what a layered directory points to.
*/
public void retargetLayeredDirectory(final String path, final String target)
{
if (path == null || target == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.retargetLayeredDirectory(path, target);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
/**
* Make the indicated directory a primary indirection.
* @param path The absolute path.
*/
public void makePrimary(final String path)
{
if (path == null)
{
throw new AVMBadArgumentException("Path is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.setSession(session);
fSuperRepository.makePrimary(path);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
}
public List<String> getRepositoryNames()
{
class HTxnCallback implements HibernateTxnCallback
{
public List<String> names;
public void perform(Session session)
{
fSuperRepository.setSession(session);
names = fSuperRepository.getRepositoryNames();
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.names;
}
/**
* Get a descriptor for the specified repository root.
* @param version The version to get.
* @param name The name of the repository.
* @return The root descriptor.
*/
public AVMNodeDescriptor getRepositoryRoot(final int version, final String name)
{
if (name == null)
{
throw new AVMBadArgumentException("Name is null.");
}
class HTxnCallback implements HibernateTxnCallback
{
public AVMNodeDescriptor root;
public void perform(Session session)
{
fSuperRepository.setSession(session);
root = fSuperRepository.getRepositoryRoot(version, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
return doit.root;
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.PrintStream;
/**
* Performance test(s).
* @author britt
*/
public class AVMServicePerfTest extends AVMServiceTestBase
{
/**
* Test adding 100 files to each directory.
*/
public void testAdd100a()
{
try
{
String [] dirs = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" };
for (String dir : dirs)
{
fService.createDirectory("main:/", dir);
String ndir = "main:/" + dir;
fService.createDirectory(ndir, dir);
ndir = ndir + "/" + dir;
for (int i = 0; i < 100; i++)
{
PrintStream out = new PrintStream(fService.createFile(ndir, "file" + i));
out.println("I am " + ndir + "/file" + i);
System.out.println(ndir + "/file" + i);
out.close();
}
fService.createSnapshot("main");
}
// System.out.println(recursiveList("main", -1));
}
catch (Exception e)
{
e.printStackTrace(System.err);
fail();
}
}
/**
* Test adding 100 files to each directory.
*/
public void testAdd100b()
{
try
{
String [] dirs = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" };
for (String dir : dirs)
{
fService.createDirectory("main:/", dir);
String ndir = "main:/" + dir;
fService.createDirectory(ndir, dir);
ndir = ndir + "/" + dir;
for (int i = 0; i < 100; i++)
{
PrintStream out = new PrintStream(fService.createFile(ndir, "file" + i));
out.println("I am " + ndir + "/file" + i);
System.out.println(ndir + "/file" + i);
out.close();
}
fService.createSnapshot("main");
}
// System.out.println(recursiveList("main", -1));
}
catch (Exception e)
{
e.printStackTrace(System.err);
fail();
}
}
/**
* Test adding 100 files to each directory.
*/
public void testAdd100c()
{
try
{
String [] dirs = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" };
for (String dir : dirs)
{
fService.createDirectory("main:/", dir);
String ndir = "main:/" + dir;
fService.createDirectory(ndir, dir);
ndir = ndir + "/" + dir;
for (int i = 0; i < 100; i++)
{
PrintStream out = new PrintStream(fService.createFile(ndir, "file" + i));
out.println("I am " + ndir + "/file" + i);
System.out.println(ndir + "/file" + i);
out.close();
}
fService.createSnapshot("main");
}
// System.out.println(recursiveList("main", -1));
}
catch (Exception e)
{
e.printStackTrace(System.err);
fail();
}
}
/**
* Test adding 100 files to each directory.
*/
public void testAdd100d()
{
try
{
String [] dirs = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" };
for (String dir : dirs)
{
fService.createDirectory("main:/", dir);
String ndir = "main:/" + dir;
fService.createDirectory(ndir, dir);
ndir = ndir + "/" + dir;
for (int i = 0; i < 100; i++)
{
PrintStream out = new PrintStream(fService.createFile(ndir, "file" + i));
out.println("I am " + ndir + "/file" + i);
out.close();
System.out.println(ndir + "/file" + i);
}
fService.createSnapshot("main");
}
// System.out.println(recursiveList("main", -1));
}
catch (Exception e)
{
e.printStackTrace(System.err);
fail();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;
import org.alfresco.repo.avm.hibernate.HibernateHelper;
import org.hibernate.stat.Statistics;
import junit.framework.TestCase;
/**
* Base class for AVMService tests.
* @author britt
*/
public class AVMServiceTestBase extends TestCase
{
/**
* The AVMService we are testing.
*/
protected AVMService fService;
/**
* The start time of actual work for a test.
*/
private long fStartTime;
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
@Override
protected void setUp() throws Exception
{
HibernateHelper.GetSessionFactory().getStatistics().setStatisticsEnabled(true);
AVMServiceImpl service = new AVMServiceImpl();
service.setStorage("build/test-results/storage");
service.init(true);
fStartTime = System.currentTimeMillis();
fService = service;
}
/* (non-Javadoc)
* @see junit.framework.TestCase#tearDown()
*/
@Override
protected void tearDown() throws Exception
{
long now = System.currentTimeMillis();
System.out.println("Timing: " + (now - fStartTime) + "ms");
Statistics stats = HibernateHelper.GetSessionFactory().getStatistics();
stats.logSummary();
stats.clear();
HibernateHelper.Reset();
}
/**
* Get the recursive contents of the given path and version.
* @param path
* @param version
* @return
*/
protected String recursiveContents(String path, int version, boolean followLinks)
{
String val = recursiveList(path, version, 0, followLinks);
return val.substring(val.indexOf('\n'));
}
/**
* Helper to write a recursive listing of a repository at a given version.
* @param repoName The name of the repository.
* @param version The version to look under.
*/
protected String recursiveList(String repoName, int version, boolean followLinks)
{
return recursiveList(repoName + ":/", version, 0, followLinks);
}
/**
* Recursive list the given path.
* @param path The path.
* @param version The version.
* @param indent The current indent level.
*/
protected String recursiveList(String path, int version, int indent, boolean followLinks)
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < indent; i++)
{
builder.append(' ');
}
builder.append(path.substring(path.lastIndexOf('/') + 1));
builder.append(' ');
AVMNodeDescriptor desc = fService.lookup(version, path);
builder.append(desc.toString());
builder.append('\n');
if (desc.getType() == AVMNodeType.PLAIN_DIRECTORY ||
(desc.getType() == AVMNodeType.LAYERED_DIRECTORY && followLinks))
{
String basename = path.endsWith("/") ? path : path + "/";
List<FolderEntry> listing = fService.getDirectoryListing(version, path);
for (FolderEntry entry : listing)
{
builder.append(recursiveList(basename + entry.getName(), version, indent + 2, followLinks));
}
}
return builder.toString();
}
/**
* Setup a basic tree.
*/
protected void setupBasicTree()
throws IOException
{
fService.createDirectory("main:/", "a");
fService.createDirectory("main:/a", "b");
fService.createDirectory("main:/a/b", "c");
fService.createDirectory("main:/", "d");
fService.createDirectory("main:/d", "e");
fService.createDirectory("main:/d/e", "f");
fService.createFile("main:/a/b/c", "foo").close();
PrintStream out = new PrintStream(fService.getFileOutputStream("main:/a/b/c/foo"));
out.println("I am main:/a/b/c/foo");
out.flush();
out.close();
fService.createFile("main:/a/b/c", "bar").close();
out = new PrintStream(fService.getFileOutputStream("main:/a/b/c/bar"));
out.println("I am main:/a/b/c/bar");
out.flush();
out.close();
ArrayList<String> toSnapshot = new ArrayList<String>();
toSnapshot.add("main");
fService.createSnapshot(toSnapshot);
}
/**
* Check that history has not been screwed up.
*/
protected void checkHistory(TreeMap<Integer, String> history, String repName)
{
for (Integer i : history.keySet())
{
assertEquals(history.get(i), recursiveList(repName, i, false));
}
int latest = fService.getLatestVersionID(repName);
history.put(latest - 1, recursiveList(repName, -1, false));
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.repo.avm.util.BulkLoad;
/**
* This is a stress test for the AVM repository.
* @author britt
*/
public class AVMStressTest extends AVMServiceTestBase
{
/**
* Test N threads
*/
public void testNThreads()
{
try
{
BulkLoad loader = new BulkLoad(fService);
loader.recursiveLoad("source", "main:/");
List<AVMTester> testers = new ArrayList<AVMTester>();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 1; i++)
{
AVMTester tester
= new AVMTester(400, // create file.
10, // create dir,
0, // rename
2, // create layered dir
5, // create layered file
10, // remove node
20, // modify file.
3600, // read file
10, // snapshot
80000, // # ops
fService,
"" + i);
tester.Refresh();
Thread thread = new Thread(tester);
testers.add(tester);
threads.add(thread);
}
for (Thread thread : threads)
{
thread.start();
}
int exited = 0;
while (exited != 1)
{
try
{
Thread.sleep(2000);
for (int i = 0; i < 1; i++)
{
if (threads.get(i) == null)
{
continue;
}
threads.get(i).join(1000);
if (!threads.get(i).isAlive())
{
threads.set(i, null);
if (testers.get(i).getError())
{
for (AVMTester tester : testers)
{
tester.setExit();
}
fail();
}
exited++;
}
}
}
catch (InterruptedException e)
{
// Do nothing.
}
}
}
catch (Exception e)
{
e.printStackTrace(System.err);
fail();
}
}
}

View File

@@ -0,0 +1,610 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
/**
* This is a Runnable which randomly performs operations on an AVM Repository.
* It's purpose is to act as a single thread in a multithreaded stress tester.
* @author britt
*/
class AVMTester implements Runnable
{
// Operation codes.
private static final int CREATE_FILE = 0;
private static final int CREATE_DIR = 1;
private static final int RENAME = 2;
private static final int CREATE_LAYERED_DIR = 3;
private static final int CREATE_LAYERED_FILE = 4;
private static final int REMOVE_NODE = 5;
private static final int MODIFY_FILE = 6;
private static final int READ_FILE = 7;
private static final int SNAPSHOT = 8;
private static List<String> fgAllPaths;
private static List<String> fgAllDirectories;
private static List<String> fgAllFiles;
private static boolean fgFrozen = false;
/**
* The operation table.
*/
private int [] fOpTable;
/**
* The number of operations to perform.
*/
private int fOpCount;
/**
* The AVMService instance.
*/
private AVMService fService;
/**
* The random number generators.
*/
private static Random fgRandom = new Random();
/**
* Names for nodes.
*/
private String[] fNames;
/**
* The id of this thread.
*/
private String fID;
/**
* Flag for whether this thread errored out.
*/
private boolean fError;
/**
* Flag for whether this thread should exit.
*/
private boolean fExit;
/**
* Initialize this with the relative frequencies of differents operations.
* @param createFile
* @param createDir
* @param rename
* @param createLayeredDir
* @param createLayeredFile
* @param removeNode
* @param modifyFile
* @param readFile
* @param snapshot
* @param opCount The number of operations to perform.
* @param service The instance of AVMService.
*/
public AVMTester(int createFile,
int createDir,
int rename,
int createLayeredDir,
int createLayeredFile,
int removeNode,
int modifyFile,
int readFile,
int snapshot,
int opCount,
AVMService service,
String id)
{
fError = false;
fExit = false;
fID = id;
fService = service;
fOpCount = opCount;
int count = createFile + createDir + rename + createLayeredDir +
createLayeredFile + removeNode + modifyFile + readFile +
snapshot;
fOpTable = new int[count];
int off = 0;
for (int i = 0; i < createFile; i++)
{
fOpTable[off + i] = CREATE_FILE;
}
off += createFile;
for (int i = 0; i < createDir; i++)
{
fOpTable[off + i] = CREATE_DIR;
}
off += createDir;
for (int i = 0; i < rename; i++)
{
fOpTable[off + i] = RENAME;
}
off += rename;
for (int i = 0; i < createLayeredDir; i++)
{
fOpTable[off + i] = CREATE_LAYERED_DIR;
}
off += createLayeredDir;
for (int i = 0; i < createLayeredFile; i++)
{
fOpTable[off + i] = CREATE_LAYERED_FILE;
}
off += createLayeredFile;
for (int i = 0; i < removeNode; i++)
{
fOpTable[off + i] = REMOVE_NODE;
}
off += removeNode;
for (int i = 0; i < modifyFile; i++)
{
fOpTable[off + i] = MODIFY_FILE;
}
off += modifyFile;
for (int i = 0; i < readFile; i++)
{
fOpTable[off + i] = READ_FILE;
}
off += readFile;
for (int i = 0; i < snapshot; i++)
{
fOpTable[off + i] = SNAPSHOT;
}
off += snapshot;
// Generate a bunch of names.
String [] letters = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
"k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "x", "y", "z" };
fNames = new String[26 * 26];
for (int i = 0; i < 26; i++)
{
for (int j = 0; j < 26; j++)
{
fNames[i * 26 + j] = letters[i] + letters[j];
}
}
}
/**
* It's off.
*/
public void run()
{
try
{
long startTime = System.currentTimeMillis();
for (int i = 0; i < fOpCount; i++)
{
if (fgFrozen)
{
Thread.sleep(3600000);
}
if (fExit)
{
return;
}
System.out.print(fID + ":" + i + ":");
int which = fgRandom.nextInt(fOpTable.length);
switch (fOpTable[which])
{
case CREATE_FILE :
createFile();
break;
case CREATE_DIR :
createDirectory();
break;
case RENAME :
rename();
break;
case CREATE_LAYERED_DIR :
createLayeredDir();
break;
case CREATE_LAYERED_FILE :
createLayeredFile();
break;
case REMOVE_NODE :
removeNode();
break;
case MODIFY_FILE :
modifyFile();
break;
case READ_FILE :
readFile();
break;
case SNAPSHOT :
snapshot();
break;
}
}
System.out.println(fgAllPaths.size() + " fses in " + (System.currentTimeMillis() - startTime) +
"ms");
}
catch (Exception e)
{
e.printStackTrace(System.err);
fgFrozen = true;
fError = true;
}
}
private void createFile()
{
String name = "PF" + fNames[fgRandom.nextInt(26 * 26)];
String path = RandomDirectory();
try
{
System.out.println("create " + path + " " + name);
PrintStream out = new PrintStream(fService.createFile(path, name));
out.println(path + "/" + name);
out.close();
AddFile(appendPath(path, name));
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void createDirectory()
{
String name = "PD" + fNames[fgRandom.nextInt(26 * 26)];
String path = RandomDirectory();
try
{
System.out.println("mkdir " + path + " " + name);
fService.createDirectory(path, name);
AddDirectory(appendPath(path, name));
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void rename()
{
String name = fNames[fgRandom.nextInt(26 * 26)];
String path = RandomPath();
AVMNodeDescriptor desc = fService.lookup(-1, path);
if (path.equals("main:/"))
{
return;
}
int lastSlash = path.lastIndexOf('/');
String srcPath = path.substring(0, lastSlash);
if (srcPath.equals("main:"))
{
srcPath = srcPath + "/";
}
String srcName = path.substring(lastSlash + 1);
String dstPath = RandomDirectory();
try
{
System.out.println("rename " + srcPath + " " + srcName + " " + dstPath + " " + name);
fService.rename(srcPath, srcName, dstPath, name);
RemovePath(path);
if (desc.isDirectory())
{
AddDirectory(appendPath(dstPath, name));
}
else
{
AddFile(appendPath(dstPath, name));
}
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void createLayeredDir()
{
String name = "LD" + fNames[fgRandom.nextInt(26 * 26)];
String path = RandomDirectory();
String target = RandomDirectory();
try
{
System.out.println("mklayereddir " + path + " " + name + " " + target);
fService.createLayeredDirectory(target, path, name);
AddDirectory(appendPath(path, name));
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void createLayeredFile()
{
String name = "LF" + fNames[fgRandom.nextInt(26 * 26)];
String path = RandomDirectory();
String target = RandomFile();
try
{
System.out.println("createlayered " + path + " " + name + " " + target);
fService.createLayeredFile(target, path, name);
AddFile(appendPath(path, name));
}
catch (AVMException ae)
{
if (ae instanceof AVMExistsException ||
ae instanceof AVMNotFoundException ||
ae instanceof AVMWrongTypeException ||
ae instanceof AVMCycleException)
{
return;
}
throw ae;
}
}
private void removeNode()
{
String target = RandomPath();
int lastSlash = target.lastIndexOf('/');
String path = target.substring(0, lastSlash);
if (path.equals("main:"))
{
path = path + "/";
}
String name = target.substring(lastSlash + 1);
try
{
System.out.println("remove " + target);
fService.removeNode(path, name);
RemovePath(target);
}
catch (AVMException e)
{
if (e instanceof AVMNotFoundException ||
e instanceof AVMWrongTypeException ||
e instanceof AVMCycleException)
{
return;
}
throw e;
}
}
private void modifyFile()
{
String path = RandomFile();
try
{
System.out.println("modify " + path);
PrintStream out =
new PrintStream(fService.getFileOutputStream(path));
out.println("I am " + path);
out.close();
}
catch (AVMException e)
{
if (e instanceof AVMNotFoundException ||
e instanceof AVMWrongTypeException ||
e instanceof AVMCycleException)
{
return;
}
throw e;
}
}
private void readFile()
{
String path = RandomFile();
try
{
System.out.println("read " + path);
BufferedReader reader =
new BufferedReader(new InputStreamReader(fService.getFileInputStream(-1, path)));
String line = reader.readLine();
System.out.println(line);
reader.close();
}
catch (AVMException e)
{
if (e instanceof AVMNotFoundException ||
e instanceof AVMWrongTypeException ||
e instanceof AVMCycleException)
{
return;
}
throw e;
}
catch (IOException e)
{
throw new AVMException("I/O Error.", e);
}
}
public void Refresh()
{
System.out.println("refresh");
fgAllPaths = new ArrayList<String>();
fgAllDirectories = new ArrayList<String>();
fgAllFiles = new ArrayList<String>();
fgAllPaths.add("main:/");
fgAllDirectories.add("main:/");
Set<Long> visited = new HashSet<Long>();
AVMNodeDescriptor root = fService.getRepositoryRoot(-1, "main");
RecursiveRefresh(root, visited);
}
private void RecursiveRefresh(AVMNodeDescriptor dir, Set<Long> visited)
{
try
{
String baseName = dir.getPath().endsWith("/") ? dir.getPath() : dir.getPath() + "/";
Map<String, AVMNodeDescriptor> listing = fService.getDirectoryListing(dir);
for (String name : listing.keySet())
{
String path = baseName + name;
AVMNodeDescriptor desc = listing.get(name);
switch (desc.getType())
{
case AVMNodeType.LAYERED_DIRECTORY :
case AVMNodeType.PLAIN_DIRECTORY :
{
if (visited.contains(desc.getId()))
{
continue;
}
visited.add(desc.getId());
fgAllPaths.add(path);
fgAllDirectories.add(path);
RecursiveRefresh(desc, visited);
break;
}
case AVMNodeType.LAYERED_FILE :
case AVMNodeType.PLAIN_FILE :
{
fgAllPaths.add(path);
fgAllFiles.add(path);
break;
}
}
}
}
catch (AVMException e)
{
if (e instanceof AVMNotFoundException ||
e instanceof AVMWrongTypeException ||
e instanceof AVMCycleException)
{
return;
}
throw e;
}
}
private void snapshot()
{
System.out.println("snapshot");
fService.createSnapshot("main");
}
public boolean getError()
{
return fError;
}
public void setExit()
{
fExit = true;
}
private static synchronized void AddDirectory(String path)
{
fgAllDirectories.add(path);
fgAllPaths.add(path);
}
private static synchronized void AddFile(String path)
{
fgAllFiles.add(path);
fgAllPaths.add(path);
}
private static synchronized void RemovePath(String path)
{
List<String> allPaths = new ArrayList<String>();
List<String> allDirectories = new ArrayList<String>();
List<String> allFiles = new ArrayList<String>();
for (String p : fgAllPaths)
{
if (p.indexOf(path) != 0)
{
allPaths.add(p);
}
}
for (String p : fgAllDirectories)
{
if (p.indexOf(path) != 0)
{
allDirectories.add(p);
}
}
for (String p : fgAllFiles)
{
if (p.indexOf(path) != 0)
{
allFiles.add(p);
}
}
fgAllPaths = allPaths;
fgAllDirectories = allDirectories;
fgAllFiles = allFiles;
}
private String appendPath(String path, String name)
{
return path.endsWith("/") ? path + name : path + "/" + name;
}
private static synchronized String RandomDirectory()
{
return fgAllDirectories.get(fgRandom.nextInt(fgAllDirectories.size()));
}
private static synchronized String RandomFile()
{
return fgAllFiles.get(fgRandom.nextInt(fgAllFiles.size()));
}
private static synchronized String RandomPath()
{
return fgAllPaths.get(fgRandom.nextInt(fgAllPaths.size()));
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* Thrown when an object of the wrong type is looked up.
* @author britt
*/
public class AVMWrongTypeException extends AVMException
{
private static final long serialVersionUID = -8799318236851345536L;
/**
* @param msgId
*/
public AVMWrongTypeException(String msgId)
{
super(msgId);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
*/
public AVMWrongTypeException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param cause
*/
public AVMWrongTypeException(String msgId, Throwable cause)
{
super(msgId, cause);
// TODO Auto-generated constructor stub
}
/**
* @param msgId
* @param msgParams
* @param cause
*/
public AVMWrongTypeException(String msgId, Object[] msgParams,
Throwable cause)
{
super(msgId, msgParams, cause);
// TODO Auto-generated constructor stub
}
}

View File

@@ -0,0 +1,83 @@
/**
*
*/
package org.alfresco.repo.avm;
/**
* Ownership, timestamps, later perhaps ACLs
* @author britt
*/
public interface BasicAttributes
{
/**
* Set the creator of the node.
* @param creator The creator to set.
*/
public void setCreator(String creator);
/**
* Get the creator of the node.
* @return The creator.
*/
public String getCreator();
/**
* Set the owner of the node.
* @param owner The owner to set.
*/
public void setOwner(String owner);
/**
* Get the owner of the node.
* @return The owner.
*/
public String getOwner();
/**
* Set the last modifier of the node.
* @param lastModifier
*/
public void setLastModifier(String lastModifier);
/**
* Get the last modifier of the node.
* @return The last modifier.
*/
public String getLastModifier();
/**
* Set the create date.
* @param createDate The date to set.
*/
public void setCreateDate(long createDate);
/**
* Get the create date.
* @return The create date.
*/
public long getCreateDate();
/**
* Set the modification date.
* @param modDate The date to set.
*/
public void setModDate(long modDate);
/**
* Get the modification date.
* @return The modification date.
*/
public long getModDate();
/**
* Set the access date of the node.
* @param accessDate The access date.
*/
public void setAccessDate(long accessDate);
/**
* Get the access date of the node.
* @return The access date.
*/
public long getAccessDate();
}

View File

@@ -0,0 +1,200 @@
/**
*
*/
package org.alfresco.repo.avm;
import java.io.Serializable;
/**
* Implementation of the BasicAttributesBean.
* @author britt
*/
public class BasicAttributesImpl implements BasicAttributes, Serializable
{
private static final long serialVersionUID = -3796354564923670005L;
/**
* The creator.
*/
private String fCreator;
/**
* The owner.
*/
private String fOwner;
/**
* The last modifier.
*/
private String fLastModifier;
/**
* The creation date.
*/
private long fCreateDate;
/**
* The modification date.
*/
private long fModDate;
/**
* The access date.
*/
private long fAccessDate;
/**
* Default constructor.
*/
public BasicAttributesImpl()
{
}
/**
* A Copy constructor.
* @param other
*/
public BasicAttributesImpl(BasicAttributes other)
{
fCreator = other.getCreator();
fOwner = other.getOwner();
fLastModifier = other.getLastModifier();
fCreateDate = other.getCreateDate();
fModDate = other.getModDate();
fAccessDate = other.getAccessDate();
}
/**
* Fill in the blanks constructor.
* @param creator
* @param owner
* @param modifier
* @param createDate
* @param modDate
* @param accessDate
*/
public BasicAttributesImpl(String creator,
String owner,
String modifier,
long createDate,
long modDate,
long accessDate)
{
fCreator = creator;
fOwner = owner;
fLastModifier = modifier;
fCreateDate = createDate;
fModDate = modDate;
fAccessDate = accessDate;
}
/**
* Set the creator.
* @param creator
*/
public void setCreator(String creator)
{
fCreator = creator;
}
/**
* Get the creator.
* @return The creator.
*/
public String getCreator()
{
return fCreator;
}
/**
* Set the owner.
* @param owner
*/
public void setOwner(String owner)
{
fOwner = owner;
}
/**
* Get the owner.
* @return The owner.
*/
public String getOwner()
{
return fOwner;
}
/**
* Set the last modifier.
* @param lastModifier
*/
public void setLastModifier(String lastModifier)
{
fLastModifier = lastModifier;
}
/**
* Get the last modifier.
* @return The last modifier.
*/
public String getLastModifier()
{
return fLastModifier;
}
/**
* Set the create date.
* @param createDate
*/
public void setCreateDate(long createDate)
{
fCreateDate = createDate;
}
/**
* Get the create date.
* @return The create date.
*/
public long getCreateDate()
{
return fCreateDate;
}
/**
* Set the modification date.
* @param modDate
*/
public void setModDate(long modDate)
{
fModDate = modDate;
}
/**
* Get the modification date.
* @return modDate
*/
public long getModDate()
{
return fModDate;
}
// TODO Do we want this?
/**
* Set the access date.
* @param accessDate
*/
public void setAccessDate(long accessDate)
{
fAccessDate = accessDate;
}
/**
* Get the access date.
* @return The access date.
*/
public long getAccessDate()
{
return fAccessDate;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* This interface represents an entry in a directory.
* @author britt
*/
public interface ChildEntry
{
/**
* Set the name of the child.
* @param name
*/
public void setName(String name);
/**
* Get the name of the child.
* @return The child's name.
*/
public String getName();
/**
* Set the parent in this entry.
* @param parent
*/
public void setParent(DirectoryNode parent);
/**
* Get the parent of this child.
* @return The parent.
*/
public DirectoryNode getParent();
/**
* Set the child in this entry.
* @param child
*/
public void setChild(AVMNode child);
/**
* Get the child in this entry.
* @return The child.
*/
public AVMNode getChild();
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.Serializable;
/**
* An entry in a directory. Contains a name, parent, and child.
* @author britt
*/
public class ChildEntryImpl implements ChildEntry, Serializable
{
private static final long serialVersionUID = -307752114272916930L;
/**
* The name of the entry.
*/
private String fName;
/**
* The parent.
*/
private DirectoryNode fParent;
/**
* The child.
*/
private AVMNode fChild;
/**
* Default constructor for Hibernate.
*/
protected ChildEntryImpl()
{
}
/**
* Make up a brand new entry.
* @param name
* @param parent
* @param child
*/
public ChildEntryImpl(String name,
DirectoryNode parent,
AVMNode child)
{
fName = name;
fParent = parent;
fChild = child;
}
/**
* Set the name of this entry.
* @param name
*/
public void setName(String name)
{
fName = name;
}
/**
* Get the name of this entry.
* @return
*/
public String getName()
{
return fName;
}
/**
* Set the parent in this entry.
* @param parent
*/
public void setParent(DirectoryNode parent)
{
fParent = parent;
}
/**
* Get the parent in this entry.
* @return
*/
public DirectoryNode getParent()
{
return fParent;
}
/**
* Set the child in this entry.
* @param child
*/
public void setChild(AVMNode child)
{
fChild = child;
}
/**
* Get the child in this entry.
* @return
*/
public AVMNode getChild()
{
return fChild;
}
/**
* @param obj
* @return
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof ChildEntry))
{
return false;
}
ChildEntry other = (ChildEntry)obj;
return fName.equals(other.getName()) && fParent.equals(other.getParent());
}
/**
* @return
*/
@Override
public int hashCode()
{
return fName.hashCode() + fParent.hashCode();
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* Interface to a deleted directory entry in a layered directory.
* @author britt
*/
public interface DeletedChild
{
/**
* Get the name of the deleted child.
* @return The name.
*/
public String getName();
/**
* Get the parent of this deleted child
* @return The parent.
*/
public LayeredDirectoryNode getParent();
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.Serializable;
/**
* Represents a deleted child in a layered directory.
* @author britt
*/
public class DeletedChildImpl implements DeletedChild, Serializable
{
private static final long serialVersionUID = 4997060636280774719L;
/**
* The name of the deleted child.
*/
private String fName;
/**
* The parent directory.
*/
private LayeredDirectoryNode fParent;
/**
* Default constructor. For Hibernate.
*/
protected DeletedChildImpl()
{
}
/**
* Create a new one.
* @param name
* @param parent
*/
public DeletedChildImpl(String name,
LayeredDirectoryNode parent)
{
fName = name;
fParent = parent;
}
/**
* Set the name of the deleted child. For Hibernate.
* @param name
*/
protected void setName(String name)
{
fName = name;
}
/**
* Get the name of the deleted child.
* @return The name.
*/
public String getName()
{
return fName;
}
/**
* Set the parent directory.
* @param parent
*/
protected void setParent(LayeredDirectoryNode parent)
{
fParent = parent;
}
/**
* Get the parent of the deleted child.
* @return The parent.
*/
public LayeredDirectoryNode getParent()
{
return fParent;
}
/**
* Equality in the database entity sense.
* @param obj
* @return
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof DeletedChild))
{
return false;
}
DeletedChild dc = (DeletedChild)obj;
return fParent.equals(dc.getParent()) && fName.equals(dc.getName());
}
/**
* @return A hash code.
*/
@Override
public int hashCode()
{
return fParent.hashCode() + fName.hashCode();
}
}

View File

@@ -14,41 +14,48 @@
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.avm.hibernate.DirectoryEntry;
/**
* Base class for Directories.
* The interface for Directory Nodes.
* @author britt
*/
public abstract class DirectoryNode extends AVMNode
interface DirectoryNode extends AVMNode
{
/**
* Does this directory directly contain the specified node.
* @param node The node to check.
* @return Whether it does.
*/
public abstract boolean directlyContains(AVMNode node);
public boolean directlyContains(AVMNode node);
/**
* Put child into this directory directly. No copy on write.
* @param name The name to give it.
* @param node The child.
*/
public abstract void putChild(String name, AVMNode node);
public void putChild(String name, AVMNode node);
/**
* Lookup a child node.
* @param lPath The Lookup so far.
* @param name The name of the child to lookup.
* @param version The version to look under.
* @param visited A Set of full paths visited. Used for cycle checking.
*/
public abstract AVMNode lookupChild(Lookup lPath, String name, int version);
public AVMNode lookupChild(Lookup lPath, String name, int version);
/**
* Lookup a child node using an AVMNodeDescriptor as context.
* @param mine The node descriptor for this.
* @param name The name of the child to lookup.
* @return The descriptor for the looked up child.
*/
public AVMNodeDescriptor lookupChild(AVMNodeDescriptor mine, String name);
/**
* Add a child node. Fails if child already exists.
* Copy is possible.
@@ -56,28 +63,60 @@ public abstract class DirectoryNode extends AVMNode
* @param child The child to add.
* @param The lookup path.
*/
public abstract boolean addChild(String name, AVMNode child,
Lookup lPath);
public boolean addChild(String name, AVMNode child, Lookup lPath);
/**
* Remove a child node. Fails if child does not exist.
* Copy is possible.
* @param name The name of the child to remove.
* @param lPath The lookup path.
*/
public abstract boolean removeChild(String name, Lookup lPath);
public boolean removeChild(String name, Lookup lPath);
/**
* Remove a child directly. No copy is possible.
* @param name The name of the child to remove.
*/
public abstract void rawRemoveChild(String name);
public void rawRemoveChild(String name);
/**
* Get a directory listing.
* @param lPath The lookup context.
* @param version The version to look under.
* @return A Map of names to DirectoryEntries.
*/
public abstract Map<String, DirectoryEntry> getListing(Lookup lPath, int version);
}
public Map<String, AVMNode> getListing(Lookup lPath);
/**
* Get a listing from a directory specified by an AVMNodeDescriptor.
* @param dir The directory to list.
* @return A Map of names to node descriptors
*/
public Map<String, AVMNodeDescriptor> getListing(AVMNodeDescriptor dir);
/**
* Set the directory, which must be in a layer, into a primary
* indirection taking its indirection from the Lookup.
* @param lPath The Lookup.
*/
public void turnPrimary(Lookup lPath);
/**
* Retarget a layered directory.
* @param lPath The Lookup.
* @param target The target path.
*/
public void retarget(Lookup lPath, String target);
/**
* Get all the directly contained children of a node.
* @return A List of Child entries.
*/
public List<ChildEntry> getChildren();
/**
* Set whether this node is a root node.
* @param isRoot
*/
public void setIsRoot(boolean isRoot);
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
/**
* Base class for Directories.
* @author britt
*/
abstract class DirectoryNodeImpl extends AVMNodeImpl implements DirectoryNode
{
/**
* Default constructor.
*/
protected DirectoryNodeImpl()
{
}
/**
* A pass through constructor. Called when a new concrete subclass
* instance is created.
* @param id
* @param repo
*/
protected DirectoryNodeImpl(long id, Repository repo)
{
super(id, repo);
}
/**
* Retrieves the ChildEntry in this directory with the given name.
* @param name The name to look for.
* @return The ChildEntry or null if not found.
*/
@SuppressWarnings("unchecked")
protected ChildEntry getChild(String name)
{
Session sess = SuperRepository.GetInstance().getSession();
return (ChildEntry)sess.get(ChildEntryImpl.class, new ChildEntryImpl(name, this, null));
}
/**
* Get all the children of this directory. NB, this should
* really be considered an internal method but it needs to be
* exposed through the interface.
* @return A List of ChildEntries.
*/
@SuppressWarnings("unchecked")
public List<ChildEntry> getChildren()
{
Session sess = SuperRepository.GetInstance().getSession();
Query query = sess.getNamedQuery("ChildEntry.ByParent");
query.setEntity("parent", this);
query.setCacheable(true);
query.setCacheRegion("ChildEntry.ByParent");
List<ChildEntry> found = (List<ChildEntry>)query.list();
return found;
}
/**
* Get the ChildEntry that has the given child.
* @param child The child node to look for.
* @return The ChildEntry or null if not found.
*/
@SuppressWarnings("unchecked")
protected ChildEntry getChild(AVMNode child)
{
Session sess = SuperRepository.GetInstance().getSession();
Query query = sess.getNamedQuery("ChildEntry.ByParentChild");
query.setEntity("parent", this);
query.setEntity("child", child);
query.setCacheable(true);
query.setCacheRegion("ChildEntry.ByParentChild");
List<ChildEntry> found = (List<ChildEntry>)query.list();
if (found.size() == 0)
{
return null;
}
return found.get(0);
}
}

View File

@@ -14,213 +14,62 @@
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Formatter;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.avm.hibernate.ContentBean;
import org.alfresco.repo.avm.hibernate.ContentBeanImpl;
import java.io.RandomAccessFile;
/**
* Content that is readable and writeable.
* Interface for file content. FileContent can be shared between files.
* @author britt
*/
public class FileContent
interface FileContent
{
/**
* The data containing bean.
*/
private ContentBean fData;
/**
* The name of the file.
*/
private String fName;
/**
* The directory path of the file.
*/
private String fPath;
/**
* Make one from a bean.
* @param data The Bean with the data.
*/
public FileContent(ContentBean data)
{
fData = data;
}
/**
* Make a brand new one.
* @param superRepo The SuperRepository.
*/
public FileContent(SuperRepository superRepo)
{
fData = new ContentBeanImpl(superRepo.issueContentID());
fData.setRefCount(1);
// Make an empty file.
try
{
getOutputStream(superRepo).close();
}
catch (IOException ie)
{
throw new AlfrescoRuntimeException("Couldn't close file.", ie);
}
superRepo.getSession().save(fData);
}
/**
* Copy constructor, sort of.
* @param other The content to copy from.
* @param superRepo The SuperRepository.
*/
public FileContent(FileContent other, SuperRepository superRepo)
{
fData = new ContentBeanImpl(superRepo.issueContentID());
fData.setRefCount(1);
// Copy the contents from other to this.
BufferedInputStream in = new BufferedInputStream(other.getInputStream(superRepo));
BufferedOutputStream out = new BufferedOutputStream(this.getOutputStream(superRepo));
try
{
byte [] buff = new byte[4096]; // Nyah, nyah.
int bytesRead;
while ((bytesRead = in.read(buff)) != -1)
{
out.write(buff, 0, bytesRead);
}
out.close();
in.close();
}
catch (IOException ie)
{
throw new AlfrescoRuntimeException("I/O failure in Copy on Write.", ie);
}
superRepo.getSession().save(fData);
}
/**
* Get the number of files that refer to this content.
* @return The reference count.
*/
public int getRefCount()
{
return fData.getRefCount();
}
public int getRefCount();
/**
* Set the reference count.
* @param count The count to set.
*/
public void setRefCount(int count)
{
fData.setRefCount(count);
}
public void setRefCount(int count);
/**
* Get an input stream from the content.
* @param superRepo The SuperRepository.
* @return An InputStream.
*/
public InputStream getInputStream(SuperRepository superRepo)
{
try
{
return new FileInputStream(getContentPath(superRepo));
}
catch (IOException ie)
{
throw new AlfrescoRuntimeException("Could not open for reading: " + getContentPath(superRepo), ie);
}
}
public InputStream getInputStream(SuperRepository superRepo);
/**
* Get an output stream to the content.
* @param superRepo The SuperRepository.
* @return an OutputStream.
*/
public OutputStream getOutputStream(SuperRepository superRepo)
{
try
{
File dir = new File(getDirectoryPath(superRepo));
if (!dir.exists())
{
dir.mkdirs();
}
return new FileOutputStream(getContentPath(superRepo));
}
catch (IOException ie)
{
throw new AlfrescoRuntimeException("Could not open for writing: " + getContentPath(superRepo), ie);
}
}
/**
* Get the underlying data bean. Don't abuse the privilege.
* @return The data bean.
*/
public ContentBean getDataBean()
{
return fData;
}
public OutputStream getOutputStream(SuperRepository superRepo);
/**
* Retrieve the full path for this content.
* Get a random access file to this content.
* @param superRepo The SuperRepository.
* @param access The mode to open the file in.
* @return A RandomAccessFile.
*/
public RandomAccessFile getRandomAccess(SuperRepository superRepo, String access);
/**
* Get the length of the file.
* @param superRepo
* @return The full path for this content.
* @return The length of the file.
*/
private String getContentPath(SuperRepository superRepo)
{
if (fName == null)
{
calcPathData(superRepo);
}
return fName;
}
public long getLength(SuperRepository superRepo);
/**
* Get the directory path for this content.
* @param superRepo
* @return The directory path.
* Get the object id.
* @return object id.
*/
private String getDirectoryPath(SuperRepository superRepo)
{
if (fPath == null)
{
calcPathData(superRepo);
}
return fPath;
}
/**
* Calculate the path data.
*/
private void calcPathData(SuperRepository superRepo)
{
long id = fData.getId();
Formatter form = new Formatter(new StringBuilder());
form.format("%016x", id);
String name = form.toString();
form = new Formatter(new StringBuilder());
form.format("/%02x/%02x/%02x",
(id & 0xff000000) >> 24,
(id & 0xff0000) >> 16,
(id & 0xff00) >> 8);
String dir = form.toString();
fPath = superRepo.getStorageRoot() + dir;
fName = fPath + "/" + name;
}
}
public long getId();
}

View File

@@ -0,0 +1,325 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.Formatter;
/**
* Content that is readable and writeable.
* @author britt
*/
class FileContentImpl implements FileContent, Serializable
{
static final long serialVersionUID = -7450825236235397307L;
/**
* The Object ID.
*/
private long fID;
/**
* The reference count of this FileContent.
*/
private int fRefCount;
/**
* The version (for concurrency control).
*/
private long fVers;
/**
* The name of the file.
*/
private String fName;
/**
* The directory path of the file.
*/
private String fPath;
/**
* Default constructor.
*/
public FileContentImpl()
{
fName = null;
fPath = null;
}
/**
* Make a brand new one.
* @param superRepo The SuperRepository.
* @param source A possibly null stream to get data from.
*/
public FileContentImpl(SuperRepository superRepo)
{
fID = superRepo.issueContentID();
fRefCount = 1;
// Initialize the contents.
try
{
OutputStream out = getOutputStream(superRepo);
out.close();
}
catch (IOException ie)
{
throw new AVMException("File data error.", ie);
}
superRepo.getSession().save(this);
}
/**
* Copy constructor, sort of.
* @param other The content to copy from.
* @param superRepo The SuperRepository.
*/
public FileContentImpl(FileContent other, SuperRepository superRepo)
{
fID = superRepo.issueContentID();
fRefCount = 1;
// Copy the contents from other to this.
BufferedInputStream in = new BufferedInputStream(other.getInputStream(superRepo));
BufferedOutputStream out = new BufferedOutputStream(this.getOutputStream(superRepo));
try
{
byte [] buff = new byte[4096]; // Nyah, nyah.
int bytesRead;
while ((bytesRead = in.read(buff)) != -1)
{
out.write(buff, 0, bytesRead);
}
out.flush();
out.close();
in.close();
}
catch (IOException ie)
{
throw new AVMException("I/O failure in Copy on Write.", ie);
}
superRepo.getSession().save(this);
}
/**
* Get this FileContent's reference count.
* @return The reference count.
*/
public int getRefCount()
{
return fRefCount;
}
/**
* Set the reference count on this.
* @param count The reference count to set.
*/
public void setRefCount(int count)
{
fRefCount = count;
}
/**
* Get an InputStream from this FileContent.
* @param superRepo The SuperRepository (to get backing store location from).
* @return An InputStream.
*/
public InputStream getInputStream(SuperRepository superRepo)
{
try
{
return new FileInputStream(getContentPath(superRepo));
}
catch (IOException ie)
{
throw new AVMException("Could not open for reading: " + getContentPath(superRepo), ie);
}
}
/**
* Gets an ouptut stream to this node.
* @param superRepo The SuperRepository.
* @return An OutputStream.
*/
public OutputStream getOutputStream(SuperRepository superRepo)
{
try
{
File dir = new File(getDirectoryPath(superRepo));
if (!dir.exists())
{
dir.mkdirs();
}
return new FileOutputStream(getContentPath(superRepo));
}
catch (IOException ie)
{
throw new AVMException("Could not open for writing: " + getContentPath(superRepo), ie);
}
}
/**
* Get a random access file from this content. It's the responsibility of
* the caller of this to insure that this object has been copied if the
* access argument is a write mode.
* @param superRepo The SuperRepository.
* @param access The access more for RandomAccessFile.
* @return A RandomAccessFile.
*/
public RandomAccessFile getRandomAccess(SuperRepository superRepo, String access)
{
try
{
return new RandomAccessFile(getContentPath(superRepo), access);
}
catch (IOException ie)
{
throw new AVMException("Could not open for random access: " + getContentPath(superRepo), ie);
}
}
/**
* Get the length of this content.
* @param superRepo The SuperRepository
* @return The length of the content.
*/
public long getLength(SuperRepository superRepo)
{
File file = new File(getContentPath(superRepo));
return file.length();
}
/**
* Retrieve the full path for this content.
* @param superRepo
* @return The full path for this content.
*/
private synchronized String getContentPath(SuperRepository superRepo)
{
if (fName == null)
{
calcPathData(superRepo);
}
return fName;
}
/**
* Get the directory path for this content.
* @param superRepo
* @return The directory path.
*/
private synchronized String getDirectoryPath(SuperRepository superRepo)
{
if (fPath == null)
{
calcPathData(superRepo);
}
return fPath;
}
/**
* Calculate the path data.
*/
private void calcPathData(SuperRepository superRepo)
{
Formatter form = new Formatter(new StringBuilder());
form.format("%016x", fID);
String name = form.toString();
form = new Formatter(new StringBuilder());
form.format("/%02x/%02x/%02x",
(fID & 0xff000000) >> 24,
(fID & 0xff0000) >> 16,
(fID & 0xff00) >> 8);
String dir = form.toString();
fPath = superRepo.getStorageRoot() + dir;
fName = fPath + "/" + name;
}
/**
* Set the version for concurrency control.
* @param vers The value to set.
*/
protected void setVers(long vers)
{
fVers = vers;
}
/**
* Get the version for concurrency control.
* @return The version.
*/
protected long getVers()
{
return fVers;
}
/**
* Set the object id. For Hibernate.
* @param id
*/
protected void setId(long id)
{
fID = id;
}
/**
* Get the object id.
* @return The object id.
*/
public long getId()
{
return fID;
}
/**
* Equals predicate. Based on object ID.
* @param obj The obect to compare against.
* @return
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof FileContent))
{
return false;
}
return fID == ((FileContent)obj).getId();
}
/**
* Generate a hashCode.
* @return The hashCode.
*/
@Override
public int hashCode()
{
return (int)fID;
}
}

View File

@@ -14,25 +14,26 @@
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* Base class for file objects.
* Interface for the generic idea of a file.
* @author britt
*/
public abstract class FileNode extends AVMNode
interface FileNode extends AVMNode
{
/**
* Get the content object associated with this node, for reading.
* @param version The version to get in.
* @return A FileContent object.
*/
public abstract FileContent getContentForRead(int version, Repository repo);
public FileContent getContentForRead(Repository repo);
/**
* Get the content object for writing. This will do COW
* as needed.
* @param The repository.
* @return A FileContent object.
*/
public abstract FileContent getContentForWrite(Repository repo);
}
public FileContent getContentForWrite(Repository repo);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* Base class for file objects.
* @author britt
*/
abstract class FileNodeImpl extends AVMNodeImpl implements FileNode
{
/**
* Default constructor.
*/
protected FileNodeImpl()
{
}
/**
* Pass through constructor.
* @param id The newly assigned object id.
* @param repo The repository we belong to.
*/
public FileNodeImpl(long id, Repository repo)
{
super(id, repo);
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* This is a helper class that knows how to issue identifiers.
* @author britt
*/
class Issuer
{
/**
* The path to this issuers persistent storage.
*/
private String fPath;
/**
* The next number to issue.
*/
private long fNext;
/**
* Constructor for an already existing Issuer.
* @param path The path to this issuers persistent store.
*/
public Issuer(String path)
{
fPath = path;
try
{
DataInputStream in = new DataInputStream(new FileInputStream(fPath + ".new"));
fNext = in.readLong();
fNext += 257;
in.close();
save();
return;
}
catch (IOException ie)
{
// Do nothing.
}
try
{
DataInputStream in = new DataInputStream(new FileInputStream(fPath));
fNext = in.readLong();
fNext += 257;
in.close();
save();
return;
}
catch (IOException ie)
{
// Do nothing.
}
// Last resort.
try
{
DataInputStream in = new DataInputStream(new FileInputStream(fPath + ".old"));
fNext = in.readLong();
fNext += 513;
in.close();
save();
return;
}
catch (IOException ie)
{
// TODO Log this situation.
throw new AVMException("Could not restore issuer" + fPath, ie);
}
}
/**
* Rich constructor.
* @param path The path to this issuers persistent store.
* @param next The next number to issue.
*/
public Issuer(String path, long next)
{
fPath = path;
fNext = next;
save();
}
/**
* Issue the next number.
* @return A serial number.
*/
public synchronized long issue()
{
long val = fNext++;
if (fNext % 128 == 0)
{
save();
}
return val;
}
/**
* Persist this issuer.
*/
public void save()
{
while (true)
{
try
{
FileOutputStream fileOut = new FileOutputStream(fPath + ".new");
DataOutputStream out = new DataOutputStream(fileOut);
out.writeLong(fNext);
out.flush();
// Force data to physical storage.
fileOut.getChannel().force(true);
out.close();
File from = new File(fPath);
File to = new File(fPath + ".old");
from.renameTo(to);
from = new File(fPath + ".new");
to = new File(fPath);
from.renameTo(to);
break;
}
catch (IOException ie)
{
// TODO Log this situation.
}
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import junit.framework.TestCase;
/**
* Simple sanity test for Issuer;
* @author britt
*/
public class IssuerTest extends TestCase
{
/**
* Sanity check issuer logic.
*/
public void testSanity()
{
Issuer issuer = new Issuer("test", 0L);
for (int i = 0; i < 500; i++)
{
issuer.issue();
}
issuer = new Issuer("test");
assertTrue(issuer.issue() >= 500);
}
}

View File

@@ -7,7 +7,7 @@ package org.alfresco.repo.avm;
* Layered nodes share this method.
* @author britt
*/
public interface Layered
interface Layered
{
/**
* Get the indirection, or underlying path that this
@@ -17,4 +17,11 @@ public interface Layered
* @return
*/
public String getUnderlying(Lookup lookup);
/**
* Get the raw indirection of a layered node.
* @return The raw indirection, which will be null for
* LayeredDirectoryNodes that are not primary indirections.
*/
public String getIndirection();
}

View File

@@ -1,489 +1,80 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.avm.hibernate.BasicAttributesBean;
import org.alfresco.repo.avm.hibernate.BasicAttributesBeanImpl;
import org.alfresco.repo.avm.hibernate.DirectoryEntry;
import org.alfresco.repo.avm.hibernate.LayeredDirectoryNodeBean;
import org.alfresco.repo.avm.hibernate.LayeredDirectoryNodeBeanImpl;
import java.util.List;
/**
* A layered directory node. A layered directory node points at
* an underlying directory, which may or may not exist. The visible
* contents of a layered directory node is the contents of the underlying node
* pointed at plus those nodes added to or modified in the layered directory node minus
* those nodes which have been deleted in the layered directory node.
* Interface for Layered Directories.
* @author britt
*/
public class LayeredDirectoryNode extends DirectoryNode implements Layered
interface LayeredDirectoryNode extends DirectoryNode, Layered
{
/**
* The underlying bean data.
*/
private LayeredDirectoryNodeBean fData;
/**
* Make one up from Bean data.
* @param data The bean with the persistent data.
*/
public LayeredDirectoryNode(LayeredDirectoryNodeBean data)
{
fData = data;
setDataBean(data);
}
/**
* Make a new one from a specified indirection path.
* @param indirection The indirection path to set.
* @param repository The repository that owns this node.
*/
public LayeredDirectoryNode(String indirection, Repository repos)
{
// Set up basic attributes for this node.
long time = System.currentTimeMillis();
// TODO We'll fix this up when Britt understands user management.
BasicAttributesBean attrs = new BasicAttributesBeanImpl("britt",
"britt",
"britt",
time,
time,
time);
fData = new LayeredDirectoryNodeBeanImpl(repos.getSuperRepository().issueID(),
-1,
0,
null,
null,
null,
repos.getDataBean(),
attrs,
-1,
true,
indirection);
setDataBean(fData);
repos.getSuperRepository().getSession().save(fData);
}
/**
* Kind of copy constructor, sort of.
* @param other The LayeredDirectoryNode we are copied from.
* @param repos The Repository object we use.
*/
public LayeredDirectoryNode(LayeredDirectoryNode other,
Repository repos)
{
LayeredDirectoryNodeBean thatBean = (LayeredDirectoryNodeBean)other.getDataBean();
// Copy the basic attributes and update.
BasicAttributesBean attrs = new BasicAttributesBeanImpl(thatBean.getBasicAttributes());
long time = System.currentTimeMillis();
attrs.setCreateDate(time);
attrs.setModDate(time);
attrs.setAccessDate(time);
attrs.setLastModifier("britt");
fData = new LayeredDirectoryNodeBeanImpl(repos.getSuperRepository().issueID(),
-1,
0,
null,
null,
null,
repos.getDataBean(),
attrs,
-1,
thatBean.getPrimaryIndirection(),
other.getUnderlying());
setDataBean(fData);
fData.setAdded(new HashMap<String, DirectoryEntry>(thatBean.getAdded()));
fData.setDeleted(new HashSet<String>(thatBean.getDeleted()));
// fData.setPrimaryIndirection(thatBean.getPrimaryIndirection());
repos.getSuperRepository().getSession().save(fData);
}
/**
* Construct one from a PlainDirectoryNode. Called when a COW is performed in a layered
* context.
* @param other The PlainDirectoryNode.
* @param repos The Repository we should belong to.
* @param lPath The Lookup object.
*/
public LayeredDirectoryNode(PlainDirectoryNode other,
Repository repos,
Lookup lPath)
{
// TODO Fix this yada, yada.
BasicAttributesBean attrs = new BasicAttributesBeanImpl(other.getDataBean().getBasicAttributes());
long time = System.currentTimeMillis();
attrs.setModDate(time);
attrs.setAccessDate(time);
attrs.setLastModifier("britt");
fData = new LayeredDirectoryNodeBeanImpl(repos.getSuperRepository().issueID(),
-1,
0,
null,
null,
null,
repos.getDataBean(),
attrs,
-1,
false,
null);
setDataBean(fData);
// TODO Is this right? I don't think so.
// fData.setAdded(other.getListing(lPath, -1));
// fData.setPrimaryIndirection(false);
repos.getSuperRepository().getSession().save(fData);
}
/**
* Create a new layered directory based on a directory we are being named from
* that is in not in the layer of the source lookup.
* @param dir The directory
* @param repo The repository
* @param srcLookup The source lookup.
* @param name The name of the target.
*/
public LayeredDirectoryNode(DirectoryNode dir,
Repository repo,
Lookup srcLookup,
String name)
{
// Make BasicAttributes and set them correctly.
BasicAttributesBean attrs = new BasicAttributesBeanImpl(dir.getDataBean().getBasicAttributes());
long time = System.currentTimeMillis();
attrs.setCreateDate(time);
attrs.setModDate(time);
attrs.setAccessDate(time);
attrs.setCreator("britt");
attrs.setLastModifier("britt");
fData = new LayeredDirectoryNodeBeanImpl(repo.getSuperRepository().issueID(),
-1,
0,
null,
null,
null,
repo.getDataBean(),
attrs,
-1,
true,
srcLookup.getIndirectionPath() + "/" + name);
setDataBean(fData);
repo.getSuperRepository().getSession().save(fData);
}
/**
* Does this node have a primary indirection.
* @returns Whether this is a primary indirection.
*/
public boolean hasPrimaryIndirection()
{
return fData.getPrimaryIndirection();
}
public boolean getPrimaryIndirection();
/**
* Set whether this has a primary indirection.
* @param has Whether this has a primary indirection.
*/
public void setPrimaryIndirection(boolean has)
{
fData.setPrimaryIndirection(has);
}
/**
* Get the raw underlying indirection. Only meaningful
* for a node that hasPrimaryIndirection().
*/
public String getUnderlying()
{
return fData.getIndirection();
}
/**
* Get the underlying indirection in the context of a Lookup.
* @param lPath The lookup path.
*/
public String getUnderlying(Lookup lPath)
{
if (fData.getPrimaryIndirection())
{
return fData.getIndirection();
}
return lPath.getCurrentIndirection();
}
public void setPrimaryIndirection(boolean has);
/**
* Get the layer id for this node.
* @return The layer id.
*/
public long getLayerID()
{
return fData.getLayerID();
}
public long getLayerID();
/**
* Set the layer id for this node.
* @param layerID The id to set.
*/
public void setLayerID(long id)
{
fData.setLayerID(id);
}
/**
* Handle post copy on write details.
* @param parent
*/
public void handlePostCopy(DirectoryNode parent)
{
if (parent instanceof LayeredDirectoryNode)
{
LayeredDirectoryNode dir = (LayeredDirectoryNode)parent;
setLayerID(dir.getLayerID());
}
}
public void setLayerID(long id);
/**
* Copy on write logic.
* Set this to be a primary indirection from the path
* passed in.
* @param path The indirection path.
*/
public void rawSetPrimary(String path);
/**
* Turn this node into a primary indirection node with the indirection
* taken from the Lookup passed in.
* Performs a copy on write.
* @param lPath
* @return The copy or null.
*/
public AVMNode possiblyCopy(Lookup lPath)
{
if (!shouldBeCopied())
{
return null;
}
// Capture the repository.
Repository repo = lPath.getRepository();
// Otherwise we do an actual copy.
LayeredDirectoryNode newMe = null;
long newBranchID = lPath.getHighestBranch();
if (!lPath.isInThisLayer())
{
if (hasPrimaryIndirection())
{
newMe = new LayeredDirectoryNode(lPath.getIndirectionPath(),
repo);
}
else
{
newMe = new LayeredDirectoryNode((String)null,
repo);
newMe.setPrimaryIndirection(false);
}
}
else
{
newMe = new LayeredDirectoryNode(this,
repo);
newMe.setLayerID(getLayerID());
}
newMe.setAncestor(this);
newMe.setBranchID(newBranchID);
return newMe;
}
public void turnPrimary(Lookup lPath);
// TODO Start around here.
/**
* Retarget this directory.
* @param lPath The Lookup.
* @param target The new target path.
*/
public void retarget(Lookup lPath, String target);
/**
* Make visible a node deleted in a layer.
* @param lPath The Lookup.
* @param name The name to make visible.
*/
public void uncover(Lookup lPath, String name);
/**
* Insert a child node without COW.
* @param name The name to give the child.
* Set the indirection.
* @param indirection
*/
public void putChild(String name, AVMNode node)
{
DirectoryEntry entry = new DirectoryEntry(node.getType(), node.getDataBean());
fData.getAdded().put(name, entry);
fData.getDeleted().remove(name);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.DirectoryNode#addChild(java.lang.String, org.alfresco.repo.avm.AVMNode, org.alfresco.repo.avm.Lookup)
*/
public boolean addChild(String name, AVMNode child, Lookup lPath)
{
if (fData.getAdded().containsKey(name))
{
return false;
}
if (!fData.getDeleted().contains(name))
{
try
{
Lookup lookup = lPath.getRepository().getSuperRepository().lookupDirectory(-1, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
if (dir.lookupChild(lookup, name, -1) != null)
{
return false;
}
}
catch (AlfrescoRuntimeException re)
{
// Do nothing.
}
}
DirectoryNode toModify = (DirectoryNode)copyOnWrite(lPath);
toModify.putChild(name, child);
child.setParent(toModify);
child.setRepository(lPath.getRepository());
return true;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.DirectoryNode#directlyContains(org.alfresco.repo.avm.AVMNode)
*/
public boolean directlyContains(AVMNode node)
{
DirectoryEntry entry = new DirectoryEntry(node.getType(),
node.getDataBean());
return fData.getAdded().containsValue(entry);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.DirectoryNode#getListing(org.alfresco.repo.avm.Lookup, int)
*/
public Map<String, DirectoryEntry> getListing(Lookup lPath, int version)
{
Map<String, DirectoryEntry> baseListing = null;
try
{
Lookup lookup = lPath.getRepository().getSuperRepository().lookupDirectory(version, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
baseListing = dir.getListing(lookup, version);
}
catch (AlfrescoRuntimeException re)
{
baseListing = new HashMap<String, DirectoryEntry>();
}
Map<String, DirectoryEntry> listing = new TreeMap<String, DirectoryEntry>();
for (String name : baseListing.keySet())
{
if (fData.getDeleted().contains(name))
{
continue;
}
listing.put(name, baseListing.get(name));
}
for (String name : fData.getAdded().keySet())
{
listing.put(name, fData.getAdded().get(name));
}
return listing;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.DirectoryNode#lookupChild(org.alfresco.repo.avm.Lookup, java.lang.String, int)
*/
public AVMNode lookupChild(Lookup lPath, String name, int version)
{
// TODO revisit the order in this.
if (fData.getAdded().containsKey(name))
{
return AVMNodeFactory.CreateFromBean(fData.getAdded().get(name).getChild());
}
AVMNode child = null;
try
{
Lookup lookup = lPath.getRepository().getSuperRepository().lookupDirectory(version, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
child = dir.lookupChild(lookup, name, version);
}
catch (AlfrescoRuntimeException re)
{
return null;
}
if (child ==null)
{
return null;
}
if (fData.getDeleted().contains(name))
{
return null;
}
return child;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.DirectoryNode#rawRemoveChild(java.lang.String)
*/
public void rawRemoveChild(String name)
{
fData.getAdded().remove(name);
fData.getDeleted().add(name);
}
public void setIndirection(String indirection);
/**
* Needed for the slide operation.
* @param name The name of the child to remove.
* Get the indirection by another name.
*/
public void rawRemoveChildNoGhost(String name)
{
fData.getAdded().remove(name);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.DirectoryNode#removeChild(java.lang.String, org.alfresco.repo.avm.Lookup)
public String getUnderlying();
/**
* Get all deleted children for this directory.
* @return All deleted children.
*/
public boolean removeChild(String name, Lookup lPath)
{
if (fData.getDeleted().contains(name))
{
return false;
}
if (!fData.getAdded().containsKey(name))
{
try
{
Lookup lookup = lPath.getRepository().getSuperRepository().lookupDirectory(-1, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
if (dir.lookupChild(lookup, name, -1) == null)
{
return false;
}
}
catch (AlfrescoRuntimeException re)
{
return false;
}
}
LayeredDirectoryNode toModify =
(LayeredDirectoryNode)copyOnWrite(lPath);
toModify.rawRemoveChild(name);
return true;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMNode#getType()
*/
public int getType()
{
return AVMNodeType.LAYERED_DIRECTORY;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString(Lookup lPath)
{
return "[LD:" + fData.getId() + ":" + getUnderlying(lPath) + "]";
}
}
public List<DeletedChild> getDeleted();
}

View File

@@ -0,0 +1,749 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.proxy.HibernateProxy;
/**
* A layered directory node. A layered directory node points at
* an underlying directory, which may or may not exist. The visible
* contents of a layered directory node is the contents of the underlying node
* pointed at plus those nodes added to or modified in the layered directory node minus
* those nodes which have been deleted in the layered directory node.
* @author britt
*/
class LayeredDirectoryNodeImpl extends DirectoryNodeImpl implements LayeredDirectoryNode
{
static final long serialVersionUID = 4623043057918181724L;
/**
* The layer id.
*/
private long fLayerID;
/**
* The pointer to the underlying directory.
*/
private String fIndirection;
/**
* Whether this is a primary indirection node.
*/
private boolean fPrimaryIndirection;
/**
* Default constructor. Called by Hibernate.
*/
protected LayeredDirectoryNodeImpl()
{
}
/**
* Make a new one from a specified indirection path.
* @param indirection The indirection path to set.
* @param repository The repository that owns this node.
*/
public LayeredDirectoryNodeImpl(String indirection, Repository repos)
{
super(repos.getSuperRepository().issueID(), repos);
fLayerID = -1;
fIndirection = indirection;
fPrimaryIndirection = true;
repos.getSuperRepository().getSession().save(this);
}
/**
* Kind of copy constructor, sort of.
* @param other The LayeredDirectoryNode we are copied from.
* @param repos The Repository object we use.
*/
@SuppressWarnings("unchecked")
public LayeredDirectoryNodeImpl(LayeredDirectoryNode other,
Repository repos)
{
super(repos.getSuperRepository().issueID(), repos);
Session sess = repos.getSuperRepository().getSession();
fIndirection = other.getUnderlying();
fPrimaryIndirection = other.getPrimaryIndirection();
fLayerID = -1;
sess.save(this);
sess.flush();
for (ChildEntry child : other.getChildren())
{
ChildEntryImpl newChild = new ChildEntryImpl(child.getName(),
this,
child.getChild());
repos.getSuperRepository().getSession().save(newChild);
}
for (DeletedChild dc : other.getDeleted())
{
DeletedChild newDel = new DeletedChildImpl(dc.getName(),
this);
sess.save(newDel);
}
}
/**
* Construct one from a PlainDirectoryNode. Called when a COW is performed in a layered
* context.
* @param other The PlainDirectoryNode.
* @param repos The Repository we should belong to.
* @param lPath The Lookup object.
*/
@SuppressWarnings("unchecked")
public LayeredDirectoryNodeImpl(PlainDirectoryNode other,
Repository repos,
Lookup lPath,
boolean copyContents)
{
super(repos.getSuperRepository().issueID(), repos);
fIndirection = null;
fPrimaryIndirection = false;
fLayerID = -1;
Session sess = repos.getSuperRepository().getSession();
sess.save(this);
if (copyContents)
{
sess.flush();
for (ChildEntry child : other.getChildren())
{
ChildEntryImpl newChild = new ChildEntryImpl(child.getName(),
this,
child.getChild());
sess.save(newChild);
}
}
}
/**
* Create a new layered directory based on a directory we are being named from
* that is in not in the layer of the source lookup.
* @param dir The directory
* @param repo The repository
* @param srcLookup The source lookup.
* @param name The name of the target.
*/
public LayeredDirectoryNodeImpl(DirectoryNode dir,
Repository repo,
Lookup srcLookup,
String name)
{
super(repo.getSuperRepository().issueID(), repo);
fIndirection = srcLookup.getIndirectionPath() + "/" + name;
fPrimaryIndirection = true;
fLayerID = -1;
repo.getSuperRepository().getSession().save(this);
}
/**
* Is this a primary indirection node.
* @return Whether this is a primary indirection.
*/
public boolean getPrimaryIndirection()
{
return fPrimaryIndirection;
}
/**
* Set the primary indirection state of this.
* @param has Whether this is a primary indirection node.
*/
public void setPrimaryIndirection(boolean has)
{
fPrimaryIndirection = has;
}
/**
* Get the indirection path.
* @return The indirection path.
*/
public String getIndirection()
{
return fIndirection;
}
public String getUnderlying()
{
return fIndirection;
}
/**
* Get the underlying path in the Lookup's context.
* @param lPath The Lookup.
* @return The underlying path.
*/
public String getUnderlying(Lookup lPath)
{
if (fPrimaryIndirection)
{
return fIndirection;
}
return lPath.getCurrentIndirection();
}
/**
* Get the layer id.
* @return The layer id.
*/
public long getLayerID()
{
return fLayerID;
}
/**
* Set the layer id.
* @param id The id to set.
*/
public void setLayerID(long id)
{
fLayerID = id;
}
/**
* Copy on write logic.
* @param lPath
* @return The copy or null.
*/
public AVMNodeImpl possiblyCopy(Lookup lPath)
{
if (!lPath.needsCopying())
{
return null;
}
// Capture the repository.
Repository repo = lPath.getRepository();
// Otherwise we do an actual copy.
LayeredDirectoryNodeImpl newMe = null;
if (!lPath.isInThisLayer())
{
// This means that this is being seen indirectly through the topmost
// layer. The following creates a node that will inherit its
// indirection from its parent.
newMe = new LayeredDirectoryNodeImpl((String)null,
repo);
newMe.setPrimaryIndirection(false);
newMe.setLayerID(lPath.getTopLayer().getLayerID());
}
else
{
// A simple copy is made.
newMe = new LayeredDirectoryNodeImpl(this,
repo);
newMe.setLayerID(getLayerID());
}
newMe.setAncestor(this);
return newMe;
}
/**
* Insert a child node without COW.
* @param name The name to give the child.
*/
public void putChild(String name, AVMNode node)
{
Session sess = SuperRepository.GetInstance().getSession();
// sess.lock(this, LockMode.UPGRADE);
ChildEntry entry = new ChildEntryImpl(name, this, node);
ChildEntry existing = (ChildEntry)sess.get(ChildEntryImpl.class, (Serializable)entry);
if (existing != null)
{
existing.setChild(node);
}
else
{
sess.save(entry);
}
DeletedChild dc = getDeleted("name");
if (dc != null)
{
sess.delete(dc);
}
}
/**
* Add a child to this directory and possibly COW.
* @param name The name of the child to add.
* @param child The child to add.
* @param lPath The Lookup.
* @return Whether the child was successfully added.
*/
public boolean addChild(String name, AVMNode child, Lookup lPath)
{
if (getChild(name) != null)
{
return false;
}
if (getDeleted(name) == null)
{
try
{
Lookup lookup = lPath.getRepository().getSuperRepository().lookupDirectory(-1, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
if (dir.lookupChild(lookup, name, -1) != null)
{
return false;
}
}
catch (AVMException re)
{
if (re instanceof AVMCycleException)
{
throw re;
}
// Do nothing.
}
}
DirectoryNode toModify = (DirectoryNode)copyOnWrite(lPath);
toModify.putChild(name, child);
child.setParent(toModify);
child.setRepository(lPath.getRepository());
return true;
}
/**
* Does this node directly contain the indicated node.
* @param node The node we are checking.
* @return Whether node is directly contained.
*/
public boolean directlyContains(AVMNode node)
{
return getChild(node) != null;
}
/**
* Get a listing of the virtual contents of this directory.
* @param lPath The Lookup.
* @return A Map from names to nodes. This is a sorted Map.
*/
@SuppressWarnings("unchecked")
public Map<String, AVMNode> getListing(Lookup lPath)
{
// Get the base listing from the thing we indirect to.
Map<String, AVMNode> baseListing = null;
try
{
Lookup lookup = lPath.getRepository().getSuperRepository().lookupDirectory(-1, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
baseListing = dir.getListing(lookup);
}
catch (AVMException re)
{
if (re instanceof AVMCycleException)
{
throw re;
}
// It's OK for an indirection to dangle.
baseListing = new HashMap<String, AVMNode>();
}
// Filter the base listing by taking out anything in the deleted Set.
Map<String, AVMNode> listing = new TreeMap<String, AVMNode>();
for (String name : baseListing.keySet())
{
if (getDeleted(name) != null)
{
continue;
}
listing.put(name, baseListing.get(name));
}
for (ChildEntry entry : getChildren())
{
listing.put(entry.getName(), entry.getChild());
}
return listing;
}
/**
* Get a listing from a directory node descriptor.
* @param dir The directory node descriptor.
* @return A Map of names to node descriptors.
*/
public Map<String, AVMNodeDescriptor> getListing(AVMNodeDescriptor dir)
{
if (dir.getPath() == null || dir.getIndirection() == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
Map<String, AVMNodeDescriptor> baseListing = new TreeMap<String, AVMNodeDescriptor>();
try
{
Lookup lookup = SuperRepository.GetInstance().lookupDirectory(-1, dir.getIndirection());
DirectoryNode dirNode = (DirectoryNode)lookup.getCurrentNode();
Map<String, AVMNode> listing = dirNode.getListing(lookup);
for (String name : listing.keySet())
{
baseListing.put(name,
listing.get(name).getDescriptor(dir.getPath(), name,
lookup.getCurrentIndirection()));
}
}
catch (AVMException e)
{
if (e instanceof AVMCycleException)
{
throw e;
}
}
List<DeletedChild> deleted = getDeleted();
for (DeletedChild child : deleted)
{
baseListing.remove(child.getName());
}
List<ChildEntry> children = getChildren();
for (ChildEntry child : children)
{
baseListing.put(child.getName(),
child.getChild().getDescriptor(dir.getPath(),
child.getName(),
dir.getIndirection()));
}
return baseListing;
}
/**
* Lookup a child by name.
* @param lPath The Lookup.
* @param name The name we are looking.
* @param version The version in which we are looking.
* @return The child or null if not found.
*/
@SuppressWarnings("unchecked")
public AVMNode lookupChild(Lookup lPath, String name, int version)
{
// If the name has been deleted quickly return.
if (getDeleted(name) != null)
{
return null;
}
ChildEntry entry = getChild(name);
if (entry != null)
{
AVMNode child = entry.getChild();
if (child instanceof HibernateProxy)
{
HibernateProxy proxy = (HibernateProxy)child;
return (AVMNode)proxy.getHibernateLazyInitializer().getImplementation();
}
return child;
}
// Not here so check our indirection.
try
{
Lookup lookup = lPath.getRepository().getSuperRepository().lookupDirectory(-1, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
return dir.lookupChild(lookup, name, -1);
}
catch (AVMException re)
{
if (re instanceof AVMCycleException)
{
throw re;
}
return null;
}
}
/**
* Lookup a child using a node descriptor as context.
* @param mine The node descriptor for this,
* @param name The name to lookup,
* @return The node descriptor.
*/
public AVMNodeDescriptor lookupChild(AVMNodeDescriptor mine, String name)
{
if (mine.getPath() == null || mine.getIndirection() == null)
{
throw new AVMBadArgumentException("Illegal null argument.");
}
if (getDeleted(name) != null)
{
return null;
}
ChildEntry entry = getChild(name);
if (entry != null)
{
return entry.getChild().getDescriptor(mine.getPath(),
name,
mine.getIndirection());
}
try
{
Lookup lookup = SuperRepository.GetInstance().lookupDirectory(-1, mine.getIndirection());
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
AVMNode child = dir.lookupChild(lookup, name, -1);
if (child == null)
{
return null;
}
return child.getDescriptor(lookup);
}
catch (AVMException e)
{
if (e instanceof AVMCycleException)
{
throw e;
}
return null;
}
}
/**
* Directly remove a child. Do not COW. Do not pass go etc.
* @param name The name of the child to remove.
*/
@SuppressWarnings("unchecked")
public void rawRemoveChild(String name)
{
ChildEntry entry = getChild(name);
if (entry != null)
{
SuperRepository.GetInstance().getSession().delete(entry);
}
DeletedChild dc = new DeletedChildImpl(name,
this);
SuperRepository.GetInstance().getSession().save(dc);
}
/**
* Remove a child by name. Possibly COW.
* @param name The name of the child to remove.
* @param lPath The Lookup.
* @return Whether the child was successfully removed.
*/
@SuppressWarnings("unchecked")
public boolean removeChild(String name, Lookup lPath)
{
// Can't delete something that is already deleted.
if (getDeleted(name) != null)
{
return false;
}
ChildEntry entry = getChild(name);
if (entry == null)
{
// See if the name is seen via indirection.
try
{
Lookup lookup = lPath.getRepository().getSuperRepository().lookupDirectory(-1, getUnderlying(lPath));
DirectoryNode dir = (DirectoryNode)lookup.getCurrentNode();
if (dir.lookupChild(lookup, name, -1) == null)
{
return false;
}
}
catch (AVMException re)
{
if (re instanceof AVMCycleException)
{
throw re;
}
return false;
}
}
LayeredDirectoryNode toModify =
(LayeredDirectoryNode)copyOnWrite(lPath);
toModify.rawRemoveChild(name);
return true;
}
/**
* Get the type of this node.
* @return The type of this node.
*/
public int getType()
{
return AVMNodeType.LAYERED_DIRECTORY;
}
/**
* For diagnostics. Get a String representation.
* @param lPath The Lookup.
* @return A String representation.
*/
public String toString(Lookup lPath)
{
return "[LD:" + getId() + ":" + getUnderlying(lPath) + "]";
}
/**
* Set the primary indirection. No COW.
* @param path The indirection path.
*/
public void rawSetPrimary(String path)
{
fIndirection = path;
fPrimaryIndirection = true;
}
/**
* Make this node become a primary indirection. COW.
* @param lPath The Lookup.
*/
public void turnPrimary(Lookup lPath)
{
String path = lPath.getCurrentIndirection();
LayeredDirectoryNode toModify = (LayeredDirectoryNode)copyOnWrite(lPath);
toModify.rawSetPrimary(path);
}
/**
* Make this point at a new target.
* @param lPath The Lookup.
*/
public void retarget(Lookup lPath, String target)
{
LayeredDirectoryNode toModify = (LayeredDirectoryNode)copyOnWrite(lPath);
toModify.rawSetPrimary(target);
}
/**
* Let anything behind name in this become visible.
* @param lPath The Lookup.
* @param name The name to uncover.
*/
public void uncover(Lookup lPath, String name)
{
LayeredDirectoryNodeImpl toModify = (LayeredDirectoryNodeImpl)copyOnWrite(lPath);
DeletedChild dc = toModify.getDeleted(name);
if (dc != null)
{
lPath.getRepository().getSuperRepository().getSession().delete(dc);
}
}
/**
* Get the descriptor for this node.
* @param The Lookup.
* @return A descriptor.
*/
public AVMNodeDescriptor getDescriptor(Lookup lPath)
{
BasicAttributes attrs = getBasicAttributes();
return new AVMNodeDescriptor(lPath.getRepresentedPath(),
AVMNodeType.LAYERED_DIRECTORY,
attrs.getCreator(),
attrs.getOwner(),
attrs.getLastModifier(),
attrs.getCreateDate(),
attrs.getModDate(),
attrs.getAccessDate(),
getId(),
getVersionID(),
getUnderlying(lPath),
fPrimaryIndirection,
fLayerID,
-1);
}
/**
* Get a descriptor for this.
* @param parentPath The parent path.
* @param name The name this was looked up with.
* @param parentIndirection The indirection of the parent.
* @return
*/
public AVMNodeDescriptor getDescriptor(String parentPath, String name, String parentIndirection)
{
BasicAttributes attrs = getBasicAttributes();
String path = parentPath.endsWith("/") ? parentPath + name : parentPath + "/" + name;
String indirection = null;
if (fPrimaryIndirection)
{
indirection = fIndirection;
}
else
{
indirection = parentIndirection.endsWith("/") ? parentIndirection + name :
parentIndirection + "/" + name;
}
return new AVMNodeDescriptor(path,
AVMNodeType.LAYERED_DIRECTORY,
attrs.getCreator(),
attrs.getOwner(),
attrs.getLastModifier(),
attrs.getCreateDate(),
attrs.getModDate(),
attrs.getAccessDate(),
getId(),
getVersionID(),
indirection,
fPrimaryIndirection,
fLayerID,
-1);
}
/**
* Set the indirection.
* @param indirection
*/
public void setIndirection(String indirection)
{
fIndirection = indirection;
}
/**
* Get the deleted child entry for a given name.
* @param name The name to look for.
* @return A DeletedChild object.
*/
@SuppressWarnings("unchecked")
private DeletedChild getDeleted(String name)
{
Query query = SuperRepository.GetInstance().getSession().getNamedQuery("DeletedChild.ByNameParent");
query.setString("name", name);
query.setEntity("parent", this);
query.setCacheable(true);
query.setCacheRegion("DeletedChild.ByNameParent");
List<DeletedChild> dc = (List<DeletedChild>)query.list();
if (dc.size() == 0)
{
return null;
}
return dc.get(0);
}
/**
* Get all the deleted entries in this directory.
* @return A List of DeletedEntry objects.
*/
@SuppressWarnings("unchecked")
public List<DeletedChild> getDeleted()
{
Query query = SuperRepository.GetInstance().getSession().getNamedQuery("DeletedChild.ByParent");
query.setEntity("parent", this);
query.setCacheable(true);
query.setCacheRegion("DeletedChild.ByParent");
return (List<DeletedChild>)query.list();
}
/**
* Does nothing because LayeredDirectoryNodes can't be roots.
* @param isRoot
*/
public void setIsRoot(boolean isRoot)
{
}
}

View File

@@ -14,204 +14,12 @@
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.avm.hibernate.BasicAttributesBean;
import org.alfresco.repo.avm.hibernate.BasicAttributesBeanImpl;
import org.alfresco.repo.avm.hibernate.LayeredFileNodeBean;
import org.alfresco.repo.avm.hibernate.LayeredFileNodeBeanImpl;
/**
* A LayeredFileNode behaves like a copy on write symlink.
* Interface for a layered file node.
* @author britt
*/
public class LayeredFileNode extends FileNode implements Layered
interface LayeredFileNode extends FileNode, Layered
{
/**
* The data bean.
*/
private LayeredFileNodeBean fData;
/**
* Construct one from its data bean.
*/
public LayeredFileNode(LayeredFileNodeBean data)
{
fData = data;
setDataBean(fData);
}
// TODO Is this ever used?
/**
* Basically a copy constructor.
* @param other The file to make a copy of.
* @param repo The repository that contains us.
*/
public LayeredFileNode(LayeredFileNode other, Repository repo)
{
long time = System.currentTimeMillis();
BasicAttributesBean attrs =
new BasicAttributesBeanImpl(other.getDataBean().getBasicAttributes());
attrs.setCreateDate(time);
attrs.setModDate(time);
attrs.setAccessDate(time);
attrs.setCreator("britt");
attrs.setLastModifier("britt");
fData =
new LayeredFileNodeBeanImpl(repo.getSuperRepository().issueID(),
-1,
0L,
null,
null,
null,
repo.getDataBean(),
attrs,
other.fData.getIndirection());
repo.getSuperRepository().getSession().save(fData);
setDataBean(fData);
}
// TODO I'm not at all sure that these are the right semantics.
/**
* Create a new one in a layered context, when it is the result of
* a renaming.
* @param file The node we are being made from.
* @param repos The Repository.
* @param srcLookup The lookup for the source parent directory. We
* need this to get calculate the correct indirection information.
* @param name The name
*/
public LayeredFileNode(FileNode file,
Repository repos,
Lookup srcLookup,
String name)
{
long time = System.currentTimeMillis();
BasicAttributesBean attrs =
new BasicAttributesBeanImpl(file.getDataBean().getBasicAttributes());
attrs.setCreateDate(time);
attrs.setModDate(time);
attrs.setAccessDate(time);
attrs.setCreator("britt");
attrs.setLastModifier("britt");
fData =
new LayeredFileNodeBeanImpl(repos.getSuperRepository().issueID(),
-1,
0L,
null,
null,
null,
repos.getDataBean(),
attrs,
srcLookup.getIndirectionPath() + "/" + name);
repos.getSuperRepository().getSession().save(fData);
setDataBean(fData);
}
/**
* Make a brand new layered file node.
* @param indirection The thing we point to.
* @param repo The repository we belong to.
*/
public LayeredFileNode(String indirection, Repository repo)
{
long time = System.currentTimeMillis();
BasicAttributesBean attrs = new BasicAttributesBeanImpl("britt",
"britt",
"britt",
time,
time,
time);
fData = new LayeredFileNodeBeanImpl(repo.getSuperRepository().issueID(),
-1,
0L,
null,
null,
null,
repo.getDataBean(),
attrs,
indirection);
repo.getSuperRepository().getSession().save(fData);
setDataBean(fData);
}
/**
* Set the repository after a copy.
* @param parent The parent after copying.
*/
public void handlePostCopy(DirectoryNode parent)
{
}
/**
* Copy on write logic.
* @param lPath The path by which this was found.
*/
public AVMNode possiblyCopy(Lookup lPath)
{
Lookup lookup = lPath.getRepository().getSuperRepository().lookup(-1, fData.getIndirection());
AVMNode indirect = lookup.getCurrentNode();
if (!(indirect instanceof FileNode))
{
throw new AlfrescoRuntimeException("Unbacked layered file node.");
}
// This is a mildly dirty trick. We use getContentForRead so as not to startle
// the ultimate destination content into copying itself prematurely.
FileContent content = ((FileNode)indirect).getContentForRead(-1, lPath.getRepository());
PlainFileNode newMe = new PlainFileNode(content, lPath.getRepository(), fData.getBasicAttributes());
newMe.setAncestor(this);
return newMe;
}
/**
* Get the type of this node.
* @return The type.
*/
public int getType()
{
return AVMNodeType.LAYERED_FILE;
}
/**
* Get the content of the specified version.
* @return A FileContent object.
*/
public FileContent getContentForRead(int version, Repository repo)
{
Lookup lookup = repo.getSuperRepository().lookup(version, fData.getIndirection());
AVMNode node = lookup.getCurrentNode();
if (!(node instanceof FileNode))
{
throw new AlfrescoRuntimeException("Missing Link.");
}
FileNode file = (FileNode)node;
return file.getContentForRead(version, repo);
}
/**
* Get File Content for writing. Should never be called.
*/
public FileContent getContentForWrite(Repository repo)
{
assert false : "Never happens";
return null;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Layered#getUnderlying(org.alfresco.repo.avm.Lookup)
*/
public String getUnderlying(Lookup lookup)
{
return fData.getIndirection();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString(Lookup lPath)
{
return "[LF:" + fData.getId() + ":" + fData.getIndirection() + "]";
}
}

View File

@@ -0,0 +1,213 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* A LayeredFileNode behaves like a copy on write symlink.
* @author britt
*/
class LayeredFileNodeImpl extends FileNodeImpl implements LayeredFileNode
{
static final long serialVersionUID = 9208423010479156363L;
/**
* The indirection.
*/
private String fIndirection;
/**
* Anonymous constructor.
*/
protected LayeredFileNodeImpl()
{
}
/**
* Basically a copy constructor. Used when a branch is created
* from a layered file.
* @param other The file to make a copy of.
* @param repo The repository that contains us.
*/
public LayeredFileNodeImpl(LayeredFileNode other, Repository repo)
{
super(repo.getSuperRepository().issueID(), repo);
fIndirection = other.getIndirection();
repo.getSuperRepository().getSession().save(this);
}
/**
* Make a brand new layered file node.
* @param indirection The thing we point to.
* @param repo The repository we belong to.
*/
public LayeredFileNodeImpl(String indirection, Repository repo)
{
super(repo.getSuperRepository().issueID(), repo);
fIndirection = indirection;
repo.getSuperRepository().getSession().save(this);
}
/**
* Copy on write logic.
* @param lPath The path by which this was found.
*/
public AVMNode possiblyCopy(Lookup lPath)
{
// LayeredFileNodes are always copied.
Lookup lookup = lPath.getRepository().getSuperRepository().lookup(-1, fIndirection);
AVMNode indirect = lookup.getCurrentNode();
if (indirect.getType() != AVMNodeType.LAYERED_FILE &&
indirect.getType() != AVMNodeType.PLAIN_FILE)
{
throw new AVMException("Unbacked layered file node.");
}
// This is a mildly dirty trick. We use getContentForRead so as not to startle
// the ultimate destination content into copying itself prematurely.
FileContent content = ((FileNode)indirect).getContentForRead(lPath.getRepository());
PlainFileNodeImpl newMe = new PlainFileNodeImpl(content, lPath.getRepository(), getBasicAttributes());
newMe.setAncestor(this);
return newMe;
}
/**
* Get the type of this node.
* @return The type.
*/
public int getType()
{
return AVMNodeType.LAYERED_FILE;
}
/**
* Get the content of the specified version.
* @param repo The Repository.
* @return A FileContent object.
*/
public FileContent getContentForRead(Repository repo)
{
Lookup lookup = repo.getSuperRepository().lookup(-1, fIndirection);
AVMNode node = lookup.getCurrentNode();
if (node.getType() != AVMNodeType.LAYERED_FILE &&
node.getType() != AVMNodeType.PLAIN_FILE)
{
throw new AVMException("Missing Link.");
}
FileNode file = (FileNode)node;
return file.getContentForRead(repo);
}
/**
* Get File Content for writing. Should never be called.
* @param repo The Repository.
* @return Always null.
*/
public FileContent getContentForWrite(Repository repo)
{
assert false : "Never happens";
return null;
}
/**
* Get the underlying path.
* @param lookup The Lookup. (Unused here.)
* @return The underlying path.
*/
public String getUnderlying(Lookup lookup)
{
return fIndirection;
}
/**
* Get a diagnostic String representation.
* @param lPath The Lookup.
* @return A diagnostic String representation.
*/
public String toString(Lookup lPath)
{
return "[LF:" + getId() + ":" + fIndirection + "]";
}
/**
* Get the descriptor for this node.
* @param The Lookup.
* @return A descriptor.
*/
public AVMNodeDescriptor getDescriptor(Lookup lPath)
{
BasicAttributes attrs = getBasicAttributes();
return new AVMNodeDescriptor(lPath.getRepresentedPath(),
AVMNodeType.LAYERED_FILE,
attrs.getCreator(),
attrs.getOwner(),
attrs.getLastModifier(),
attrs.getCreateDate(),
attrs.getModDate(),
attrs.getAccessDate(),
getId(),
getVersionID(),
getUnderlying(lPath),
false,
-1,
0);
}
/**
* Get the descriptor for this node.
* @param parentPath The parent path.
* @param name The name this was looked up with.
* @param parentIndirection The parent indirection.
* @return
*/
public AVMNodeDescriptor getDescriptor(String parentPath, String name, String parentIndirection)
{
BasicAttributes attrs = getBasicAttributes();
String path = parentPath.endsWith("/") ? parentPath + name : parentPath + "/" + name;
return new AVMNodeDescriptor(path,
AVMNodeType.LAYERED_FILE,
attrs.getCreator(),
attrs.getOwner(),
attrs.getLastModifier(),
attrs.getCreateDate(),
attrs.getModDate(),
attrs.getAccessDate(),
getId(),
getVersionID(),
fIndirection,
false,
-1,
0);
}
/**
* Get the indirection.
* @return The indirection.
*/
public String getIndirection()
{
return fIndirection;
}
/**
* Set the indirection.
* @param indirection
*/
public void setIndirection(String indirection)
{
fIndirection = indirection;
}
}

View File

@@ -20,22 +20,25 @@ package org.alfresco.repo.avm;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.Session;
/**
* This holds all the information necessary to perform operations
* on AVMNodes.
* on AVMNodes, and is internall structured as a list of path components
* from the root directory of a repository.
* @author britt
*/
public class Lookup
class Lookup
{
/**
* The Repository.
*/
private Repository fRepository;
private RepositoryImpl fRepository;
/**
* The name of the Repository.
*/
@SuppressWarnings("unused")
private String fRepName;
/**
@@ -59,11 +62,6 @@ public class Lookup
*/
private int fTopLayerIndex;
/**
* The highest branch id seen in the lookup.
*/
private long fHighestBranchID;
/**
* The lowest layered directory node's index seen so far.
*/
@@ -74,22 +72,27 @@ public class Lookup
*/
private int fPosition;
/**
* Whether a needs-to-be-copied component has been seen.
*/
private boolean fNeedsCopying;
/**
* Create a new one.
* @param repository The Repository that's being looked in.
* @param repName The name of that Repsository.
*/
public Lookup(Repository repository, String repName)
public Lookup(RepositoryImpl repository, String repName)
{
fRepository = repository;
fRepName = repName;
fComponents = new ArrayList<LookupComponent>();
fLayeredYet = false;
fTopLayer = null;
fHighestBranchID = 0;
fPosition = -1;
fTopLayerIndex = -1;
fLowestLayerIndex = -1;
fNeedsCopying = false;
}
/**
@@ -102,26 +105,37 @@ public class Lookup
LookupComponent comp = new LookupComponent();
comp.setName(name);
comp.setNode(node);
// Bump up the highest branch id seen if necessary.
if (node.getBranchID() > fHighestBranchID)
// SuperRepository.GetInstance().getSession().lock(node, LockMode.READ);
if (!node.getIsNew())
{
fHighestBranchID = node.getBranchID();
fNeedsCopying = true;
}
// Set the highest branch id seen by this component in the lookup.
comp.setHighestBranch(fHighestBranchID);
else
{
if (fPosition >= 0 && (!((DirectoryNode)fComponents.get(fPosition).getNode()).directlyContains(node) ||
!isDirectlyContained()))
{
fNeedsCopying = true;
}
}
comp.setNeedsCopy(fNeedsCopying);
// Record various things if this is layered.
if (node instanceof LayeredDirectoryNode)
if (node.getType() == AVMNodeType.LAYERED_DIRECTORY)
{
LayeredDirectoryNode oNode = (LayeredDirectoryNode)node;
// Record the indirection path that should be used.
if (oNode.hasPrimaryIndirection())
if (oNode.getPrimaryIndirection())
{
comp.setIndirection(oNode.getUnderlying());
}
else
{
String parentIndirection = fComponents.get(fPosition).getIndirection();
if (parentIndirection.endsWith("/")) // TODO This currently is impossible because
if (parentIndirection == null)
{
System.out.println("Oink!");
}
if (parentIndirection.endsWith("/")) // This currently is impossible because
// root dirs are always plain.
{
comp.setIndirection(parentIndirection + name);
@@ -173,6 +187,28 @@ public class Lookup
assert fPosition >= 0;
return fComponents.get(fPosition).isLayered();
}
/**
* Determine if a node is directly contained.
*/
private boolean isDirectlyContained()
{
if (!isLayered())
{
return true;
}
int pos = fPosition;
while (pos > 1)
{
DirectoryNode dir = (DirectoryNode)fComponents.get(pos - 1).getNode();
if (!dir.directlyContains(fComponents.get(pos).getNode()))
{
return false;
}
pos--;
}
return true;
}
/**
* Determine if a node is directly in this layer.
@@ -199,7 +235,7 @@ public class Lookup
{
return false;
}
if (dir == fTopLayer)
if (dir.equals(fTopLayer))
{
return true;
}
@@ -208,15 +244,6 @@ public class Lookup
return false;
}
/**
* Get the highest branch traversed in this lookup at the current position.
* @return The highest branch traversed.
*/
public long getHighestBranch()
{
return fComponents.get(fPosition).getHighestBranch();
}
/**
* Get the name of the current component.
* @return The name.
@@ -256,14 +283,14 @@ public class Lookup
for (int pos = lowestLayerIndex; pos >= fTopLayerIndex; pos--)
{
AVMNode node = fComponents.get(pos).getNode();
if (!(node instanceof LayeredDirectoryNode))
if (node.getType() != AVMNodeType.LAYERED_DIRECTORY)
{
continue;
}
LayeredDirectoryNode oNode =
(LayeredDirectoryNode)node;
if (oNode.getLayerID() == fTopLayer.getLayerID() &&
oNode.hasPrimaryIndirection())
oNode.getPrimaryIndirection())
{
StringBuilder builder = new StringBuilder();
builder.append(oNode.getUnderlying());
@@ -308,4 +335,47 @@ public class Lookup
{
return fRepository;
}
/**
* Get the path represented by this lookup.
* @return The canonical path for this lookup.
*/
public String getRepresentedPath()
{
if (fComponents.size() == 1)
{
return fRepName + ":/";
}
StringBuilder builder = new StringBuilder();
builder.append(fRepName);
builder.append(':');
int count = fComponents.size();
for (int i = 1; i < count; i++)
{
builder.append('/');
builder.append(fComponents.get(i).getName());
}
return builder.toString();
}
/**
* Get whether the current node needs copying.
* @return Whether the current node needs copying.
*/
public boolean needsCopying()
{
return fComponents.get(fPosition).getNeedsCopy();
}
/**
* Acquire locks for writing, in path lookup order.
*/
// public void acquireLocks()
// {
// Session sess = SuperRepository.GetInstance().getSession();
// for (LookupComponent comp : fComponents)
// {
// sess.lock(comp.getNode(), LockMode.UPGRADE);
// }
// }
}

View File

@@ -21,7 +21,7 @@ package org.alfresco.repo.avm;
* Represents a path component in a lookup.
* @author britt
*/
public class LookupComponent
class LookupComponent
{
/**
* The name of this component.
@@ -33,11 +33,6 @@ public class LookupComponent
*/
private AVMNode fNode;
/**
* The highest branch seen by this component.
*/
private long fHighestBranch;
/**
* The indirection path (if any) for this node.
*/
@@ -52,14 +47,11 @@ public class LookupComponent
* Whether this node is in a layer.
*/
private boolean fLayered;
/**
* @return the highestBranch
* Whether this needs copying.
*/
public long getHighestBranch()
{
return fHighestBranch;
}
private boolean fNeedsCopy;
/**
* Create a new empty lookup component.
@@ -69,14 +61,7 @@ public class LookupComponent
}
/**
* @param highestBranch the highestBranch to set
*/
public void setHighestBranch(long highestBranch)
{
fHighestBranch = highestBranch;
}
/**
* Get the indirection.
* @return the indirection
*/
public String getIndirection()
@@ -85,6 +70,7 @@ public class LookupComponent
}
/**
* Set the indirection.
* @param indirection the indirection to set
*/
public void setIndirection(String indirection)
@@ -93,7 +79,9 @@ public class LookupComponent
}
/**
* @return the layered
* Is this component layered. I.e. has it seen a layer yet in
* its lookup.
* @return Whether this component is layered.
*/
public boolean isLayered()
{
@@ -101,7 +89,8 @@ public class LookupComponent
}
/**
* @param layered the layered to set
* Set whether this node is layered.
* @param layered
*/
public void setLayered(boolean layered)
{
@@ -109,6 +98,8 @@ public class LookupComponent
}
/**
* Get the index of the lowest (in the path lookup sense) layer
* seen at this component's point in the lookup.
* @return the lowestLayerIndex
*/
public int getLowestLayerIndex()
@@ -117,6 +108,8 @@ public class LookupComponent
}
/**
* Set the index of the lowest (in the path lookup sense) layer
* seen at this components's point in the lookup.
* @param lowestLayerIndex the lowestLayerIndex to set
*/
public void setLowestLayerIndex(int lowestLayerIndex)
@@ -125,6 +118,7 @@ public class LookupComponent
}
/**
* Get the path component name.
* @return the name
*/
public String getName()
@@ -133,6 +127,7 @@ public class LookupComponent
}
/**
* Set the path component name.
* @param name the name to set
*/
public void setName(String name)
@@ -141,6 +136,7 @@ public class LookupComponent
}
/**
* Get the looked up node for this component.
* @return the node
*/
public AVMNode getNode()
@@ -149,10 +145,29 @@ public class LookupComponent
}
/**
* Set the node for this component.
* @param node the node to set
*/
public void setNode(AVMNode node)
{
fNode = node;
}
/**
* Set the needs copy bit.
* @param needs Whether this component needs to be copied.
*/
public void setNeedsCopy(boolean needs)
{
fNeedsCopy = needs;
}
/**
* Does this component need a copy.
* @return Whether it does.
*/
public boolean getNeedsCopy()
{
return fNeedsCopy;
}
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* This is the background thread for reaping no longer referenced nodes
* in the AVM repository. These orphans arise from purge operations.
* @author britt
*/
class OrphanReaper implements Runnable
{
/**
* Inactive base sleep interval.
*/
private long fInactiveBaseSleep;
/**
* Active base sleep interval.
*/
private long fActiveBaseSleep;
/**
* Batch size.
*/
private int fBatchSize;
/**
* Flag for shutting down this.
*/
private boolean fDone;
/**
* The thread for this.
*/
private Thread fThread;
/**
* Create one with default parameters.
*/
public OrphanReaper()
{
fInactiveBaseSleep = 30000;
fActiveBaseSleep = 2000;
fBatchSize = 50;
fDone = false;
}
// Setters for configuration.
/**
* Set the Inactive Base Sleep interval.
* @param interval The interval to set in ms.
*/
public void setInactiveBaseSleep(long interval)
{
fInactiveBaseSleep = interval;
}
/**
* Set the active base sleep interval.
* @param interval The interval to set in ms.
*/
public void setActiveBaseSleep(long interval)
{
fActiveBaseSleep = interval;
}
/**
* Set the batch size.
* @param size The batch size to set.
*/
public void setBatchSize(int size)
{
fBatchSize = size;
}
/**
* Start things up after configuration is complete.
*/
public void init()
{
fThread = new Thread(this);
fThread.start();
}
/**
* Shutdown the reaper. This needs to be called when
* the application shuts down.
*/
public void shutDown()
{
fDone = true;
try
{
fThread.join();
}
catch (InterruptedException ie)
{
// Do nothing.
}
}
/**
* Sit in a loop, periodically querying for orphans. When orphans
* are found, unhook them in bite sized batches.
*/
public void run()
{
}
}

View File

@@ -1,267 +1,10 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import org.alfresco.repo.avm.hibernate.BasicAttributesBean;
import org.alfresco.repo.avm.hibernate.BasicAttributesBeanImpl;
import org.alfresco.repo.avm.hibernate.DirectoryEntry;
import org.alfresco.repo.avm.hibernate.PlainDirectoryNodeBean;
import org.alfresco.repo.avm.hibernate.PlainDirectoryNodeBeanImpl;
/**
* A plain directory. No monkey tricks except for possiblyCopy.
* Interface for PlainDirectoryNodes.
* @author britt
*/
public class PlainDirectoryNode extends DirectoryNode
interface PlainDirectoryNode extends DirectoryNode
{
/**
* The Bean data.
*/
private PlainDirectoryNodeBean fData;
/**
* Make up a new directory with nothing in it.
* @param repo
*/
public PlainDirectoryNode(Repository repo)
{
// Make up initial BasicAttributes.
long time = System.currentTimeMillis();
// TODO figure out how to get user information from context.
BasicAttributesBean attrs = new BasicAttributesBeanImpl("britt",
"britt",
"britt",
time,
time,
time);
fData = new PlainDirectoryNodeBeanImpl(repo.getSuperRepository().issueID(),
repo.getLatestVersion(),
0L,
null,
null,
null,
repo.getDataBean(),
attrs,
false);
repo.getSuperRepository().getSession().save(fData);
setDataBean(fData);
}
/**
* Make one up from its bean data. Used when a PlainDirectory is
* restored from the database.
* @param data The bean data.
*/
public PlainDirectoryNode(PlainDirectoryNodeBean data)
{
fData = data;
setDataBean(data);
}
/**
* Copy like constructor.
* @param other The other directory.
* @param repos The Repository Object that will own us.
*/
public PlainDirectoryNode(PlainDirectoryNode other,
Repository repos)
{
// Make up appropriate BasicAttributes.
long time = System.currentTimeMillis();
// TODO Need to figure out how to get user information from context.
BasicAttributesBean attrs = new BasicAttributesBeanImpl(other.getDataBean().getBasicAttributes());
attrs.setModDate(time);
attrs.setCreateDate(time);
attrs.setAccessDate(time);
attrs.setCreator("britt");
attrs.setLastModifier("britt");
fData = new PlainDirectoryNodeBeanImpl(repos.getSuperRepository().issueID(),
-1,
0,
null,
null,
null,
repos.getDataBean(),
attrs,
false);
setDataBean(fData);
fData.setChildren(new HashMap<String, DirectoryEntry>(((PlainDirectoryNodeBean)other.getDataBean()).getChildren()));
}
/**
* Add a child to this directory, possibly doing a copy.
* @param name The name of the child.
* @param child The child node.
* @param lPath The lookup path to this directory.
* @return Success or failure.
*/
public boolean addChild(String name, AVMNode child, Lookup lPath)
{
// No, if a child with the given name exists. Note that uniqueness
// of names is built into the AVM, as opposed to being configurable.
if (fData.getChildren().containsKey(name))
{
return false;
}
DirectoryNode toModify = (DirectoryNode)copyOnWrite(lPath);
toModify.putChild(name, child);
child.setParent(toModify);
child.setRepository(lPath.getRepository());
return true;
}
/**
* Does this directory directly contain the given node.
* @param node The node to check.
* @return Whether it was found.
*/
public boolean directlyContains(AVMNode node)
{
// TODO This is inefficient; maybe use a two way map.
DirectoryEntry entry = new DirectoryEntry(node.getType(), node.getDataBean());
return fData.getChildren().containsValue(entry);
}
/**
* Get a directory listing.
* @param lPath The lookup path.
* @param version Which version.
* @return The listing.
*/
public Map<String, DirectoryEntry> getListing(Lookup lPath, int version)
{
// Maybe this is pointless, but it's nice to be able to iterate
// over entries in a defined order.
return new TreeMap<String, DirectoryEntry>(fData.getChildren());
}
/**
* Lookup a child by name.
* @param lPath The lookup path so far.
* @param name The name to lookup.
* @param version The version to look under.
* @return The child or null.
*/
public AVMNode lookupChild(Lookup lPath, String name, int version)
{
DirectoryEntry child = fData.getChildren().get(name);
if (child == null)
{
return null;
}
return AVMNodeFactory.CreateFromBean(child.getChild());
}
/**
* Remove a child, no copying.
* @param name The name of the child to remove.
*/
public void rawRemoveChild(String name)
{
fData.getChildren().remove(name);
}
/**
* Remove a child. Possibly copy.
* @param name The name of the child to remove.
* @param lPath The lookup path.
* @return Success or failure.
*/
public boolean removeChild(String name, Lookup lPath)
{
// Can't remove it if it's not there.
if (!fData.getChildren().containsKey(name))
{
return false;
}
DirectoryNode toModify = (DirectoryNode)copyOnWrite(lPath);
toModify.rawRemoveChild(name);
return true;
}
/**
* Put a new child node into this directory. No copy.
* @param name The name of the child.
* @param node The node to add.
*/
public void putChild(String name, AVMNode node)
{
fData.getChildren().put(name, new DirectoryEntry(node.getType(), node.getDataBean()));
}
// TODO I don't think this is at all necessary in the world without
// mounted VirtualRepositories.
/**
* Set repository after copy on write.
* @param parent The parent after copy on write.
*/
public void handlePostCopy(DirectoryNode parent)
{
}
/**
* Copy on write logic.
* @param lPath The lookup path.
* @return
*/
public AVMNode possiblyCopy(Lookup lPath)
{
if (!shouldBeCopied())
{
return null;
}
// Otherwise do an actual copy.
DirectoryNode newMe = null;
long newBranchID = lPath.getHighestBranch();
// In a layered context a copy on write creates a new
// layered directory.
if (lPath.isLayered())
{
newMe = new LayeredDirectoryNode(this, lPath.getRepository(), lPath);
}
else
{
newMe = new PlainDirectoryNode(this, lPath.getRepository());
}
newMe.setAncestor(this);
newMe.setBranchID(newBranchID);
return newMe;
}
/**
* Get the type of this node.
* @return The type of this node.
*/
public int getType()
{
return AVMNodeType.PLAIN_DIRECTORY;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMNode#toString(org.alfresco.repo.avm.Lookup)
*/
@Override
public String toString(Lookup lPath)
{
return "[PD:" + fData.getId() + "]";
}
}
}

View File

@@ -0,0 +1,379 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.proxy.HibernateProxy;
/**
* A plain directory. No monkey tricks except for possiblyCopy.
* @author britt
*/
class PlainDirectoryNodeImpl extends DirectoryNodeImpl implements PlainDirectoryNode
{
static final long serialVersionUID = 9423813734583003L;
/**
* Whether this is a root node.
*/
private boolean fIsRoot;
/**
* Make up a new directory with nothing in it.
* @param repo
*/
public PlainDirectoryNodeImpl(Repository repo)
{
super(repo.getSuperRepository().issueID(), repo);
fIsRoot = false;
repo.getSuperRepository().getSession().save(this);
SuperRepository.GetInstance().getSession().flush();
}
/**
* Anonymous constructor.
*/
protected PlainDirectoryNodeImpl()
{
}
/**
* Copy like constructor.
* @param other The other directory.
* @param repos The Repository Object that will own us.
*/
@SuppressWarnings("unchecked")
public PlainDirectoryNodeImpl(PlainDirectoryNode other,
Repository repos)
{
super(repos.getSuperRepository().issueID(), repos);
Session sess = repos.getSuperRepository().getSession();
sess.save(this);
sess.flush();
for (ChildEntry child : other.getChildren())
{
ChildEntry newChild = new ChildEntryImpl(child.getName(),
this,
child.getChild());
sess.save(newChild);
}
}
/**
* Add a child to this directory, possibly doing a copy.
* @param name The name of the child.
* @param child The child node.
* @param lPath The lookup path to this directory.
* @return Success or failure.
*/
public boolean addChild(String name, AVMNode child, Lookup lPath)
{
// No, if a child with the given name exists. Note that uniqueness
// of names is built into the AVM, as opposed to being configurable.
if (getChild(name) != null)
{
return false;
}
DirectoryNode toModify = (DirectoryNode)copyOnWrite(lPath);
toModify.putChild(name, child);
child.setParent(toModify);
child.setRepository(lPath.getRepository());
return true;
}
/**
* Does this directory directly contain the given node.
* @param node The node to check.
* @return Whether it was found.
*/
public boolean directlyContains(AVMNode node)
{
return getChild(node) != null;
}
/**
* Get a directory listing.
* @param lPath The lookup path.
* @param version Which version.
* @return The listing.
*/
@SuppressWarnings("unchecked")
public Map<String, AVMNode> getListing(Lookup lPath)
{
TreeMap<String, AVMNode> result = new TreeMap<String, AVMNode>();
List<ChildEntry> children = getChildren();
for (ChildEntry child : children)
{
result.put(child.getName(), child.getChild());
}
return result;
}
/**
* Get a listing of from a directory node descriptor.
* @param dir The directory node descriptor.
* @return A Map of names to node descriptors.
*/
public Map<String, AVMNodeDescriptor> getListing(AVMNodeDescriptor dir)
{
if (dir.getPath() == null)
{
throw new AVMBadArgumentException("Path is null.");
}
TreeMap<String, AVMNodeDescriptor> result = new TreeMap<String, AVMNodeDescriptor>();
List<ChildEntry> children = getChildren();
for (ChildEntry child : children)
{
result.put(child.getName(),
child.getChild().getDescriptor(dir.getPath(), child.getName(), dir.getIndirection()));
}
return result;
}
/**
* Lookup a child by name.
* @param lPath The lookup path so far.
* @param name The name to lookup.
* @param version The version to look under.
* @return The child or null.
*/
@SuppressWarnings("unchecked")
public AVMNode lookupChild(Lookup lPath, String name, int version)
{
// We're doing the hand unrolling of the proxy because
// Hibernate/CGLIB proxies are broken.
ChildEntry entry = getChild(name);
if (entry == null)
{
return null;
}
AVMNode child = entry.getChild();
if (child instanceof HibernateProxy)
{
HibernateProxy proxy = (HibernateProxy)child;
return (AVMNode)proxy.getHibernateLazyInitializer().getImplementation();
}
return child;
}
/**
* Lookup a child using a node descriptor as context.
* @param mine The node descriptor for this.
* @param name The name of the child to lookup.
* @return A node descriptor for the child.
*/
public AVMNodeDescriptor lookupChild(AVMNodeDescriptor mine, String name)
{
if (mine.getPath() == null)
{
throw new AVMBadArgumentException("Path is null.");
}
ChildEntry entry = getChild(name);
if (entry == null)
{
return null;
}
return entry.getChild().getDescriptor(mine.getPath(), name, (String)null);
}
/**
* Remove a child, no copying.
* @param name The name of the child to remove.
*/
@SuppressWarnings("unchecked")
public void rawRemoveChild(String name)
{
ChildEntry entry = getChild(name);
if (entry != null)
{
SuperRepository.GetInstance().getSession().delete(entry);
}
}
/**
* Remove a child. Possibly copy.
* @param name The name of the child to remove.
* @param lPath The lookup path.
* @return Success or failure.
*/
public boolean removeChild(String name, Lookup lPath)
{
// Can't remove it if it's not there.
if (getChild(name) == null)
{
return false;
}
DirectoryNode toModify = (DirectoryNode)copyOnWrite(lPath);
toModify.rawRemoveChild(name);
return true;
}
/**
* Put a new child node into this directory. No copy.
* @param name The name of the child.
* @param node The node to add.
*/
public void putChild(String name, AVMNode node)
{
Session sess = SuperRepository.GetInstance().getSession();
// sess.lock(this, LockMode.UPGRADE);
sess.flush();
ChildEntry entry = new ChildEntryImpl(name, this, node);
ChildEntry existing = (ChildEntry)sess.get(ChildEntryImpl.class, (Serializable)entry);
if (existing != null)
{
existing.setChild(node);
}
else
{
sess.save(entry);
}
}
/**
* Copy on write logic.
* @param lPath The lookup path.
* @return
*/
public AVMNode possiblyCopy(Lookup lPath)
{
if (!lPath.needsCopying())
{
return null;
}
// Otherwise do an actual copy.
DirectoryNode newMe = null;
// In a layered context a copy on write creates a new
// layered directory.
if (lPath.isLayered())
{
// Subtlety warning: This distinguishes the case of a
// Directory that was branched into the layer and one
// that is indirectly seen in this layer.
newMe = new LayeredDirectoryNodeImpl(this, lPath.getRepository(), lPath,
lPath.isInThisLayer());
((LayeredDirectoryNodeImpl)newMe).setLayerID(lPath.getTopLayer().getLayerID());
}
else
{
newMe = new PlainDirectoryNodeImpl(this, lPath.getRepository());
}
newMe.setAncestor(this);
return newMe;
}
/**
* Get the type of this node.
* @return The type of this node.
*/
public int getType()
{
return AVMNodeType.PLAIN_DIRECTORY;
}
/**
* Get a diagnostic String representation.
* @param lPath The Lookup.
* @return A diagnostic String representation.
*/
public String toString(Lookup lPath)
{
return "[PD:" + getId() + "]";
}
/**
* Turn this into a primary indirection. This must be in a
* layered context.
* @param lPath The Lookup.
*/
public void turnPrimary(Lookup lPath)
{
LayeredDirectoryNode toModify = (LayeredDirectoryNode)copyOnWrite(lPath);
Lookup lookup = lPath.getRepository().getSuperRepository().lookup(-1, lPath.getRepresentedPath());
toModify.rawSetPrimary(lookup.getCurrentIndirection());
}
/**
* Retarget this directory. lPath must be in a layered context.
* @param lPath The Lookup.
* @param target The target path.
*/
public void retarget(Lookup lPath, String target)
{
LayeredDirectoryNode toModify = (LayeredDirectoryNode)copyOnWrite(lPath);
toModify.rawSetPrimary(target);
}
/**
* Get the descriptor for this node.
* @param The Lookup.
* @return A descriptor.
*/
public AVMNodeDescriptor getDescriptor(Lookup lPath)
{
BasicAttributes attrs = getBasicAttributes();
return new AVMNodeDescriptor(lPath.getRepresentedPath(),
AVMNodeType.PLAIN_DIRECTORY,
attrs.getCreator(),
attrs.getOwner(),
attrs.getLastModifier(),
attrs.getCreateDate(),
attrs.getModDate(),
attrs.getAccessDate(),
getId(),
getVersionID(),
null,
false,
-1,
-1);
}
/**
* Get this node's descriptor.
* @param parentPath The parent path.
* @param name The name that we were looked up under.
* @param parentIndirection The parent indirection.
* @return This node's node descriptor
*/
public AVMNodeDescriptor getDescriptor(String parentPath, String name, String parentIndirection)
{
BasicAttributes attrs = getBasicAttributes();
String path = parentPath.endsWith("/") ? parentPath + name : parentPath + "/" + name;
return new AVMNodeDescriptor(path,
AVMNodeType.PLAIN_DIRECTORY,
attrs.getCreator(),
attrs.getOwner(),
attrs.getLastModifier(),
attrs.getCreateDate(),
attrs.getModDate(),
attrs.getAccessDate(),
getId(),
getVersionID(),
null,
false,
-1,
-1);
}
}

View File

@@ -1,204 +1,10 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import org.alfresco.repo.avm.hibernate.BasicAttributesBean;
import org.alfresco.repo.avm.hibernate.BasicAttributesBeanImpl;
import org.alfresco.repo.avm.hibernate.PlainFileNodeBean;
import org.alfresco.repo.avm.hibernate.PlainFileNodeBeanImpl;
/**
* A plain old file. Contains a Content object.
* Interface for Plain file nodes.
* @author britt
*/
public class PlainFileNode extends FileNode
interface PlainFileNode extends FileNode
{
/**
* The data bean.
*/
private PlainFileNodeBean fData;
/**
* Construct one from its data bean.
* @param data The data bean.
*/
public PlainFileNode(PlainFileNodeBean data)
{
fData = data;
setDataBean(data);
}
/**
* Make one from just a repository.
* This is the constructor used when a brand new plain file is being made.
* @param repos A Repository.
*/
public PlainFileNode(Repository repos)
{
FileContent content = new FileContent(repos.getSuperRepository());
long time = System.currentTimeMillis();
BasicAttributesBean attrs = new BasicAttributesBeanImpl("britt",
"britt",
"britt",
time,
time,
time);
fData = new PlainFileNodeBeanImpl(repos.getSuperRepository().issueID(),
-1,
0,
null,
null,
null,
repos.getDataBean(),
attrs,
content.getDataBean());
content.setRefCount(1);
// Transitive persistence should take care of content.
repos.getSuperRepository().getSession().save(fData);
setDataBean(fData);
}
/**
* Copy on write constructor.
* @param other The node we are being copied from.
* @param repos The Repository.
*/
public PlainFileNode(PlainFileNode other,
Repository repos)
{
// Setup sensible BasicAttributes.
long time = System.currentTimeMillis();
// TODO Figure out how to get user from context.
BasicAttributesBean attrs = new BasicAttributesBeanImpl(other.getDataBean().getBasicAttributes());
attrs.setCreateDate(time);
attrs.setModDate(time);
attrs.setAccessDate(time);
attrs.setCreator("britt");
attrs.setLastModifier("britt");
fData = new PlainFileNodeBeanImpl(repos.getSuperRepository().issueID(),
-1,
0,
null,
null,
null,
repos.getDataBean(),
attrs,
other.fData.getContent());
repos.getSuperRepository().getSession().save(fData);
fData.getContent().setRefCount(fData.getContent().getRefCount() + 1);
setDataBean(fData);
}
/**
* Constructor that takes a FileContent to share.
* @param content The FileContent to share.
* @param repos The Repository.
*/
public PlainFileNode(FileContent content,
Repository repos,
BasicAttributesBean oAttrs)
{
// Setup sensible BasicAttributes.
long time = System.currentTimeMillis();
// TODO Figure out how to get user from context.
BasicAttributesBean attrs = new BasicAttributesBeanImpl(oAttrs);
attrs.setCreateDate(time);
attrs.setModDate(time);
attrs.setAccessDate(time);
attrs.setCreator("britt");
attrs.setLastModifier("britt");
fData = new PlainFileNodeBeanImpl(repos.getSuperRepository().issueID(),
-1,
0,
null,
null,
null,
repos.getDataBean(),
attrs,
content.getDataBean());
repos.getSuperRepository().getSession().save(fData);
fData.getContent().setRefCount(fData.getContent().getRefCount() + 1);
setDataBean(fData);
}
/**
* Copy on write logic.
* @param lPath The lookup path.
*/
public AVMNode possiblyCopy(Lookup lPath)
{
if (!shouldBeCopied())
{
return null;
}
PlainFileNode newMe = new PlainFileNode(this, lPath.getRepository());
newMe.setAncestor(this);
newMe.setBranchID(lPath.getHighestBranch());
return newMe;
}
/**
* Get the type of this node.
* @return The type.
*/
public int getType()
{
return AVMNodeType.PLAIN_FILE;
}
/**
* Get content for reading.
*/
public FileContent getContentForRead(int version, Repository repo)
{
return new FileContent(fData.getContent());
}
/**
* Get content for writing.
* @param repo The Repository.
*/
public FileContent getContentForWrite(Repository repo)
{
FileContent fc = new FileContent(fData.getContent());
if (fData.getContent().getRefCount() > 1)
{
fc = new FileContent(fc, repo.getSuperRepository());
fData.setContent(fc.getDataBean());
}
return fc;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMNode#toString(org.alfresco.repo.avm.Lookup)
*/
@Override
public String toString(Lookup lPath)
{
return "[PF:" + fData.getId() + "]";
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMNode#handlePostCopy(org.alfresco.repo.avm.DirectoryNode)
*/
@Override
public void handlePostCopy(DirectoryNode parent)
{
}
public FileContent getContent();
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* A plain old file. Contains a Content object.
* @author britt
*/
class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
{
static final long serialVersionUID = 8720376837929735294L;
/**
* The file content.
*/
private FileContent fContent;
/**
* Default constructor.
*/
protected PlainFileNodeImpl()
{
}
/**
* Make one from just a repository.
* This is the constructor used when a brand new plain file is being made.
* @param repos A Repository.
* @param source A possibly null stream from which to get data.
*/
public PlainFileNodeImpl(Repository repos)
{
super(repos.getSuperRepository().issueID(), repos);
fContent = new FileContentImpl(repos.getSuperRepository());
repos.getSuperRepository().getSession().save(this);
}
/**
* Copy on write constructor.
* @param other The node we are being copied from.
* @param repos The Repository.
*/
public PlainFileNodeImpl(PlainFileNode other,
Repository repos)
{
super(repos.getSuperRepository().issueID(), repos);
fContent = other.getContent();
fContent.setRefCount(fContent.getRefCount() + 1);
repos.getSuperRepository().getSession().save(this);
}
/**
* Constructor that takes a FileContent to share.
* @param content The FileContent to share.
* @param repos The Repository.
*/
public PlainFileNodeImpl(FileContent content,
Repository repos,
BasicAttributes oAttrs)
{
super(repos.getSuperRepository().issueID(), repos);
fContent = content;
fContent.setRefCount(fContent.getRefCount() + 1);
repos.getSuperRepository().getSession().save(this);
}
/**
* Copy on write logic.
* @param lPath The lookup path.
*/
public AVMNodeImpl possiblyCopy(Lookup lPath)
{
if (!lPath.needsCopying())
{
return null;
}
PlainFileNodeImpl newMe = new PlainFileNodeImpl(this, lPath.getRepository());
newMe.setAncestor(this);
return newMe;
}
/**
* Get the type of this node.
* @return The type.
*/
public int getType()
{
return AVMNodeType.PLAIN_FILE;
}
/**
* Get content for reading.
*/
public FileContent getContentForRead(Repository repo)
{
return fContent;
}
/**
* Get content for writing.
* @param repo The Repository.
*/
public FileContent getContentForWrite(Repository repo)
{
if (fContent.getRefCount() > 1)
{
fContent = new FileContentImpl(fContent, repo.getSuperRepository());
}
return fContent;
}
/**
* Get a diagnostic string representation.
* @param lPath The Lookup.
* @return A diagnostic String representation.
*/
// @Override
public String toString(Lookup lPath)
{
return "[PF:" + getId() + "]";
}
/**
* Get the descriptor for this node.
* @param The Lookup.
* @return A descriptor.
*/
public AVMNodeDescriptor getDescriptor(Lookup lPath)
{
BasicAttributes attrs = getBasicAttributes();
return new AVMNodeDescriptor(lPath.getRepresentedPath(),
AVMNodeType.PLAIN_FILE,
attrs.getCreator(),
attrs.getOwner(),
attrs.getLastModifier(),
attrs.getCreateDate(),
attrs.getModDate(),
attrs.getAccessDate(),
getId(),
getVersionID(),
null,
false,
-1,
getContentForRead(
lPath.getRepository())
.getLength(lPath.getRepository().getSuperRepository()));
}
/**
* Get the descriptor for this.
* @param parentPath The parent path.
* @param name The name this was looked up with.
* @param parentIndirection The parent indirection.
* @return The descriptor for this.
*/
public AVMNodeDescriptor getDescriptor(String parentPath, String name, String parentIndirection)
{
BasicAttributes attrs = getBasicAttributes();
String path = parentPath.endsWith("/") ? parentPath + name : parentPath + "/" + name;
return new AVMNodeDescriptor(path,
AVMNodeType.PLAIN_FILE,
attrs.getCreator(),
attrs.getOwner(),
attrs.getLastModifier(),
attrs.getCreateDate(),
attrs.getModDate(),
attrs.getAccessDate(),
getId(),
getVersionID(),
null,
false,
-1,
getContentForRead(
getRepository())
.getLength(getRepository().getSuperRepository()));
}
/**
* Get the file content of this node.
* @return The file content object.
*/
public FileContent getContent()
{
return fContent;
}
/**
* Set the FileContent for this file.
* @param content
*/
protected void setContent(FileContent content)
{
fContent = content;
}
}

View File

@@ -14,158 +14,193 @@
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.avm.hibernate.RepositoryBean;
/**
* This is the interface between low level repository objects
* and the high level implementation of the repository.
* The repository interface. Methods for filesystem like, versioning,
* and layering operations.
* @author britt
*/
public interface Repository
interface Repository
{
/**
* Inform this Repository that a Node is new and should
* therefore not be copied.
* @param node The node that is reporting itself.
* This returns the next version in this repository that will be snapshotted.
* @return The next version to be snapshotted.
*/
public void setNew(AVMNode node);
public int getNextVersionID();
/**
* Get the latest version id.
* @return The latest version.
*/
public int getLatestVersion();
/**
* Inform this Repository that the root node is new.
* @param root The new root directory node.
* Set a new root for this repository.
* @param root The root to set.
*/
public void setNewRoot(DirectoryNode root);
/**
* Make a snapshot. Equivalent of subversion end of commit. A new
* version number is issued.
* Snapshots this repository. This sets all nodes in the
* the repository to the should be copied state, and creates
* a new version root.
*/
public void createSnapshot();
/**
* Create a new directory.
* @param path The path to the directory for creation.
* @param name The name for the new directory.
* @param path The path to the parent directory.
* @param name The name to give the new directory.
*/
public void createDirectory(String path, String name);
/**
* Create a layered directory over srcPath at dstPath/name.
* @param srcPath Fully qualified path.
* @param dstPath Repository path to target directory.
* @param name What the new layered directory should be called.
* Create a new layered directory.
* @param srcPath The path that the new layered directory will point at.
* @param dstPath The path to the directory to create the new layered directory in.
* @param name The name of the new layered directory.
*/
public void createLayeredDirectory(String srcPath, String dstPath, String name);
public void createLayeredDirectory(String srcPath, String dstPath,
String name);
/**
* Create a new empty file.
* @param path The path to the directory for creation.
* @param name The name for the new file.
* Create a new file. The designated file cannot already exist.
* @param path The path to the directory to contain the new file.
* @param name The name to give the new file.
* @param source An InputStream of data to put in the file. May be null.
*/
public void createFile(String path, String name);
public OutputStream createFile(String path, String name);
/**
* Create a layered file over srcPath at dstPath/name
* @param srcPath Fully qualified path.
* @param dstPath Repository path.
* @param name The name the new layered file should have.
* Create a new layered file.
* @param srcPath The target path for the new file.
* @param dstPath The path to the directory to make the new file in.
* @param name The name of the new file.
*/
public void createLayeredFile(String srcPath, String dstPath, String name);
/**
* Get an input stream from an existing file.
* TODO Figure out nio way of doing things.
* @param version The version id to look under.
* Get an InputStream from a file.
* @param version The version to look under.
* @param path The path to the file.
* @return An InputStream.
* @return An InputStream
*/
public InputStream getInputStream(int version, String path);
/**
* Get a directory listing.
* Get a listing of the designated directory.
* @param version The version to look under.
* @param path The path to the directory.
* @return A List of FolderEntries.
* @return A listing.
*/
public List<FolderEntry> getListing(int version, String path);
/**
* Get an OutputStream to an existing file. This may trigger a copy.
* Get an output stream to a file.
* @param path The path to the file.
* @return An OutputStream
*/
public OutputStream getOutputStream(String path);
/**
* Remove a node from a directory.
* @param path The path to the directory.
* Get a random access file to the given file.
* @param version The version id (read-only if not -1)
* @param path The path to the file.
* @param access The access for RandomAccessFile.
* @return A RandomAccessFile.
*/
public RandomAccessFile getRandomAccess(int version, String path, String access);
/**
* Remove a node and all of its contents.
* @param path The path to the node's parent directory.
* @param name The name of the node to remove.
*/
public void removeNode(String path, String name);
/**
* This moves a node from one place in a layer to another place in
* the same layer without leaving a deleted entry in the source directory.
* @param srcPath The path containing the src node.
* @param srcName The name of the src node.
* @param dstPath The destination container.
* @param dstName The name of the destination node.
* Uncover a whited out node.
* @param dirPath The path to the directory.
* @param name The name to uncover.
*/
public void slide(String srcPath, String srcName, String dstPath, String dstName);
public void uncover(String dirPath, String name);
// TODO This is problematic. As time goes on this returns
// larger and larger data sets. Perhaps what we should do is
// provide methods for getting versions by date range, n most
// recent etc.
/**
* Get the version ids that this Repository has.
* @return A Set of Version IDs.
* Get all the version ids for this repository.
* @return A Set of all versions.
*/
public Set<Integer> getVersions();
/**
* Get the data bean.
* @return The data bean.
*/
public RepositoryBean getDataBean();
/**
* Get the super repository.
* @return The SuperRepository.
*/
public SuperRepository getSuperRepository();
/**
* Get a lookup object for a path.
* Lookup a node.
* @param version The version to look under.
* @param path The Repository path.
* @return The Lookup.
* @param path The path to the node.
* @return A Lookup object.
*/
public Lookup lookup(int version, String path);
/**
* Get a lookup object for a path. Directory only.
* Lookup a directory.
* @param version The version to look under.
* @param path The Repository path.
* @return The Lookup.
* @param path The path to the directory.
* @return A Lookup object.
*/
public Lookup lookupDirectory(int version, String path);
/**
* Get the indirection path of a layered node.
* @param version The version id.
* @param path The Repository path.
* For a layered node, get its indirection.
* @param version The version to look under.
* @param path The path to the node.
* @return The indirection.
*/
public String getIndirectionPath(int version, String path);
}
/**
* Make the indicated directory a primary indirection.
* @param path The Repository relative path.
*/
public void makePrimary(String path);
/**
* Change the target of a layered directory.
* @param path The path to the layered directory.
* @param target The new target path.
*/
public void retargetLayeredDirectory(String path, String target);
/**
* Get the root directory of this Repository.
* @return The root directory.
*/
public DirectoryNode getRoot();
/**
* Get the specified root as a descriptor.
* @param version The version to get (-1 for head).
* @return The specified root or null.
*/
public AVMNodeDescriptor getRoot(int version);
/**
* Get the name of this repository.
* @return The name.
*/
public String getName();
/**
* Purge all the nodes reachable only by the given version.
* @param version
*/
public void purgeVersion(int version);
}

View File

@@ -0,0 +1,732 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.hibernate.Query;
import org.hibernate.proxy.HibernateProxy;
/**
* A Repository contains a current root directory and a list of
* root versions. Each root version corresponds to a separate snapshot
* operation.
* @author britt
*/
class RepositoryImpl implements Repository, Serializable
{
static final long serialVersionUID = -1485972568675732904L;
/**
* The name of this repository.
*/
private String fName;
/**
* The current root directory.
*/
private DirectoryNode fRoot;
/**
* The next version id.
*/
private int fNextVersionID;
/**
* The version (for concurrency control).
*/
private long fVers;
/**
* The super repository.
*/
transient private SuperRepository fSuper;
/**
* Default constructor.
*/
protected RepositoryImpl()
{
fSuper = SuperRepository.GetInstance();
}
/**
* Make a brand new repository.
* @param superRepo The SuperRepository.
* @param name The name of the Repository.
*/
public RepositoryImpl(SuperRepository superRepo, String name)
{
// Make ourselves up and save.
fSuper = superRepo;
fName = name;
fNextVersionID = 0;
fRoot = null;
fSuper.getSession().save(this);
// Make up the initial version record and save.
long time = System.currentTimeMillis();
fRoot = new PlainDirectoryNodeImpl(this);
fRoot.setIsNew(false);
fRoot.setIsRoot(true);
fSuper.getSession().save(fRoot);
VersionRoot versionRoot = new VersionRootImpl(this,
fRoot,
fNextVersionID,
time,
"britt");
fNextVersionID++;
fSuper.getSession().save(versionRoot);
}
/**
* Set a new root for this.
* @param root
*/
public void setNewRoot(DirectoryNode root)
{
fRoot = root;
fRoot.setIsRoot(true);
}
/**
* Snapshot this repository. This creates a new version record.
*/
@SuppressWarnings("unchecked")
public void createSnapshot()
{
// Clear out the new nodes.
Query query =
fSuper.getSession().getNamedQuery("AVMNode.ByNewInRepo");
query.setEntity("repo", this);
for (AVMNode newNode : (List<AVMNode>)query.list())
{
newNode.setIsNew(false);
}
// Make up a new version record.
VersionRoot versionRoot = new VersionRootImpl(this,
fRoot,
fNextVersionID,
System.currentTimeMillis(),
"britt");
fSuper.getSession().save(versionRoot);
// Increment the version id.
fNextVersionID++;
}
/**
* Create a new directory.
* @param path The path to the containing directory.
* @param name The name of the new directory.
*/
public void createDirectory(String path, String name)
{
Lookup lPath = lookupDirectory(-1, path);
// lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null)
{
throw new AVMExistsException("Child exists: " + name);
}
DirectoryNode newDir = null;
if (lPath.isLayered()) // Creating a directory in a layered context creates
// a LayeredDirectoryNode that gets its indirection from
// its parent.
{
newDir = new LayeredDirectoryNodeImpl((String)null, this);
((LayeredDirectoryNodeImpl)newDir).setPrimaryIndirection(false);
((LayeredDirectoryNodeImpl)newDir).setLayerID(lPath.getTopLayer().getLayerID());
}
else
{
newDir = new PlainDirectoryNodeImpl(this);
}
newDir.setVersionID(getNextVersionID());
dir.addChild(name, newDir, lPath);
}
/**
* Create a new layered directory.
* @param srcPath The target indirection for a layered node.
* @param dstPath The containing directory for the new node.
* @param name The name of the new node.
*/
public void createLayeredDirectory(String srcPath, String dstPath,
String name)
{
Lookup lPath = lookupDirectory(-1, dstPath);
// lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null)
{
throw new AVMExistsException("Child exists: " + name);
}
LayeredDirectoryNode newDir =
new LayeredDirectoryNodeImpl(srcPath, this);
if (lPath.isLayered())
{
// When a layered directory is made inside of a layered context,
// it gets its layer id from the topmost layer in its lookup
// path.
LayeredDirectoryNode top = lPath.getTopLayer();
newDir.setLayerID(top.getLayerID());
}
else
{
// Otherwise we issue a brand new layer id.
newDir.setLayerID(fSuper.issueLayerID());
}
dir.addChild(name, newDir, lPath);
newDir.setVersionID(getNextVersionID());
}
/**
* Create a new file.
* @param path The path to the directory to contain the new file.
* @param name The name to give the new file.
* @param source A (possibly null) InputStream from which to get
* initial content.
*/
public OutputStream createFile(String path, String name)
{
Lookup lPath = lookupDirectory(-1, path);
// lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null)
{
throw new AVMExistsException("Child exists: " + name);
}
PlainFileNodeImpl file = new PlainFileNodeImpl(this);
file.setVersionID(getNextVersionID());
dir.addChild(name, file, lPath);
return file.getContentForWrite(this).getOutputStream(fSuper);
}
/**
* Create a new layered file.
* @param srcPath The target indirection for the layered file.
* @param dstPath The path to the directory to contain the new file.
* @param name The name of the new file.
*/
public void createLayeredFile(String srcPath, String dstPath, String name)
{
Lookup lPath = lookupDirectory(-1, dstPath);
// lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null)
{
throw new AVMExistsException("Child exists: " + name);
}
// TODO Reexamine decision to not check validity of srcPath.
LayeredFileNodeImpl newFile =
new LayeredFileNodeImpl(srcPath, this);
dir.addChild(name, newFile, lPath);
newFile.setVersionID(getNextVersionID());
}
/**
* Get an input stream from a file.
* @param version The version id to look under.
* @param path The path to the file.
* @return An InputStream.
*/
public InputStream getInputStream(int version, String path)
{
Lookup lPath = lookup(version, path);
AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.PLAIN_FILE &&
node.getType() != AVMNodeType.LAYERED_FILE)
{
throw new AVMExistsException("Not a file: " + path + " r " + version);
}
FileNode file = (FileNode)node;
FileContent content = file.getContentForRead(this);
return content.getInputStream(fSuper);
}
/**
* Get a listing from a directory.
* @param version The version to look under.
* @param path The path to the directory.
* @return A List of FolderEntries.
*/
public List<FolderEntry> getListing(int version, String path)
{
Lookup lPath = lookupDirectory(version, path);
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
Map<String, AVMNode> listing = dir.getListing(lPath);
ArrayList<FolderEntry> results = new ArrayList<FolderEntry>();
for (String name : listing.keySet())
{
AVMNode child = listing.get(name);
FolderEntry item = new FolderEntry();
item.setName(name);
item.setType(child.getType());
results.add(item);
}
return results;
}
/**
* Get an output stream to a file.
* @param path The path to the file.
* @return An OutputStream.
*/
public OutputStream getOutputStream(String path)
{
Lookup lPath = lookup(-1, path);
// lPath.acquireLocks();
AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.PLAIN_FILE &&
node.getType() != AVMNodeType.LAYERED_FILE)
{
throw new AVMWrongTypeException("Not a file: " + path);
}
FileNode file = (FileNode)node;
file = (FileNode)file.copyOnWrite(lPath);
FileContent content = file.getContentForWrite(this);
return content.getOutputStream(fSuper); // TODO Do we really need fSuper?
}
/**
* Get a RandomAccessFile to a file node.
* @param version The version.
* @param path The path to the file.
* @param access The access mode for RandomAccessFile.
* @return A RandomAccessFile.
*/
public RandomAccessFile getRandomAccess(int version, String path, String access)
{
boolean write = access.indexOf("rw") == 0;
if (write && version >= 0)
{
throw new AVMException("Access denied: " + path);
}
Lookup lPath = lookup(version, path);
if (write)
{
// lPath.acquireLocks();
}
AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.PLAIN_FILE &&
node.getType() != AVMNodeType.LAYERED_FILE)
{
throw new AVMWrongTypeException("Not a file: " + path);
}
FileNode file = (FileNode)node;
FileContent content = null;
if (write)
{
file = (FileNode)file.copyOnWrite(lPath);
content = file.getContentForWrite(this);
}
else
{
content = file.getContentForRead(this);
}
return content.getRandomAccess(fSuper, access);
}
/**
* 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 removeNode(String path, String name)
{
// TODO Are we double checking for existence?
Lookup lPath = lookupDirectory(-1, path);
// lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) == null)
{
throw new AVMNotFoundException("Does not exist: " + name);
}
dir.removeChild(name, lPath);
}
/**
* Allow a name which has been deleted to be visible through that layer.
* @param dirPath The path to the containing directory.
* @param name The name to uncover.
*/
public void uncover(String dirPath, String name)
{
Lookup lPath = lookup(-1, dirPath);
// lPath.acquireLocks();
AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.LAYERED_DIRECTORY)
{
throw new AVMWrongTypeException("Not a layered directory: " + dirPath);
}
((LayeredDirectoryNode)node).uncover(lPath, name);
}
// TODO This is problematic. As time goes on this returns
// larger and larger data sets. Perhaps what we should do is
// provide methods for getting versions by date range, n most
// recent etc.
/**
* Get the set of all extant version ids for this Repository.
* @return A Set of version ids.
*/
@SuppressWarnings("unchecked")
public Set<Integer> getVersions()
{
Query query = fSuper.getSession().createQuery("select v.versionID from VersionRootImpl v");
return new TreeSet<Integer>((List<Integer>)query.list());
}
/**
* Get the SuperRepository.
* @return The SuperRepository
*/
public SuperRepository getSuperRepository()
{
return fSuper;
}
/**
* Lookup up a path.
* @param version The version to look in.
* @param path The path to look up.
* @return A Lookup object.
*/
public Lookup lookup(int version, String path)
{
// Make up a Lookup to hold the results.
Lookup result = new Lookup(this, fName);
if (path.length() == 0)
{
throw new AVMException("Invalid path: " + path);
}
if (path.length() > 1)
{
path = path.substring(1);
}
String[] pathElements = path.split("/");
// Grab the root node to start the lookup.
DirectoryNode dir = null;
// Versions less than 0 mean get current.
if (version < 0)
{
if (fRoot instanceof HibernateProxy)
{
dir = (DirectoryNode)((HibernateProxy)fRoot).getHibernateLazyInitializer().getImplementation();
}
else
{
dir = fRoot;
}
}
else
{
Query query =
fSuper.getSession().getNamedQuery("VersionRoot.GetVersionRoot");
query.setEntity("rep", this);
query.setInteger("version", version);
dir = (DirectoryNode)query.uniqueResult();
if (dir == null)
{
throw new AVMException("Invalid version: " + version);
}
}
// fSuper.getSession().lock(dir, LockMode.READ);
// Add an entry for the root.
result.add(dir, "");
if (pathElements.length == 0)
{
return result;
}
// Now look up each path element in sequence up to one
// before the end.
for (int i = 0; i < pathElements.length - 1; i++)
{
AVMNode child = dir.lookupChild(result, pathElements[i], version);
if (child == null)
{
throw new AVMNotFoundException("Not found: " + pathElements[i]);
}
// Every element that is not the last needs to be a directory.
if (child.getType() != AVMNodeType.PLAIN_DIRECTORY &&
child.getType() != AVMNodeType.LAYERED_DIRECTORY)
{
throw new AVMWrongTypeException("Not a directory: " + pathElements[i]);
}
dir = (DirectoryNode)child;
// fSuper.getSession().lock(dir, LockMode.READ);
result.add(dir, pathElements[i]);
}
// Now look up the last element.
AVMNode child = dir.lookupChild(result, pathElements[pathElements.length - 1], version);
if (child == null)
{
throw new AVMNotFoundException("Not found: " + pathElements[pathElements.length - 1]);
}
// fSuper.getSession().lock(child, LockMode.READ);
result.add(child, pathElements[pathElements.length - 1]);
return result;
}
/**
* Get the root node descriptor.
* @param version The version to get.
* @return The descriptor.
*/
public AVMNodeDescriptor getRoot(int version)
{
AVMNode root = null;
if (version < 0)
{
root = fRoot;
}
else
{
Query query =
fSuper.getSession().getNamedQuery("VersionRoot.GetVersionRoot");
query.setEntity("rep", this);
query.setInteger("version", version);
root = (AVMNode)query.uniqueResult();
if (root == null)
{
throw new AVMException("Invalid version: " + version);
}
}
return root.getDescriptor("main:", "", null);
}
/**
* Lookup a node and insist that it is a directory.
* @param version The version to look under.
* @param path The path to the directory.
* @return A Lookup object.
*/
public Lookup lookupDirectory(int version, String path)
{
// Just do a regular lookup and assert that the last element
// is a directory.
Lookup lPath = lookup(version, path);
if (lPath.getCurrentNode().getType() != AVMNodeType.PLAIN_DIRECTORY &&
lPath.getCurrentNode().getType() != AVMNodeType.LAYERED_DIRECTORY)
{
throw new AVMWrongTypeException("Not a directory: " + path);
}
return lPath;
}
/**
* Get the effective indirection path for a layered node.
* @param version The version to look under.
* @param path The path to the node.
* @return The effective indirection.
*/
public String getIndirectionPath(int version, String path)
{
Lookup lPath = lookup(version, path);
AVMNode node = lPath.getCurrentNode();
if (node.getType() == AVMNodeType.LAYERED_DIRECTORY)
{
return ((LayeredDirectoryNode)node).getUnderlying(lPath);
}
if (node.getType() == AVMNodeType.LAYERED_FILE)
{
return ((LayeredFileNode)node).getUnderlying(lPath);
}
throw new AVMWrongTypeException("Not a layered node: " + path);
}
/**
* Make the indicated node a primary indirection.
* @param path The path to the node.
*/
public void makePrimary(String path)
{
Lookup lPath = lookupDirectory(-1, path);
// lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (!lPath.isLayered())
{
throw new AVMException("Not in a layered context: " + path);
}
dir.turnPrimary(lPath);
}
/**
* Change the indirection of a layered directory.
* @param path The path to the layered directory.
* @param target The target indirection to set.
*/
public void retargetLayeredDirectory(String path, String target)
{
Lookup lPath = lookupDirectory(-1, path);
// lPath.acquireLocks();
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (!lPath.isLayered())
{
throw new AVMException("Not in a layered context: " + path);
}
dir.retarget(lPath, target);
}
/**
* Set the name of this repository. Hibernate.
* @param name
*/
protected void setName(String name)
{
fName = name;
}
/**
* Get the name of this Repository.
* @return The name.
*/
public String getName()
{
return fName;
}
/**
* Set the next version id.
* @param nextVersionID
*/
protected void setNextVersionID(int nextVersionID)
{
fNextVersionID = nextVersionID;
}
/**
* Get the next version id.
* @return The next version id.
*/
public int getNextVersionID()
{
return fNextVersionID;
}
/**
* Set the root directory. Hibernate.
* @param root
*/
protected void setRoot(DirectoryNode root)
{
fRoot = root;
}
/**
* Get the root directory.
* @return The root directory.
*/
public DirectoryNode getRoot()
{
return fRoot;
}
/**
* Set the version (for concurrency control). Hibernate.
* @param vers
*/
protected void setVers(long vers)
{
fVers = vers;
}
/**
* Get the version (for concurrency control). Hibernate.
* @return The version.
*/
protected long getVers()
{
return fVers;
}
/**
* @param obj
* @return
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof Repository))
{
return false;
}
return fName.equals(((Repository)obj).getName());
}
/**
* @return
*/
@Override
public int hashCode()
{
// TODO Auto-generated method stub
return super.hashCode();
}
/**
* Purge all nodes reachable only via this version and repostory.
* @param version
*/
@SuppressWarnings("unchecked")
public void purgeVersion(int version)
{
if (version == 0)
{
throw new AVMBadArgumentException("Cannot purge initial version");
}
Query query = fSuper.getSession().getNamedQuery("VersionRoot.VersionByID");
query.setEntity("rep", this);
query.setInteger("version", version);
VersionRoot vRoot = (VersionRoot)query.uniqueResult();
AVMNode root = vRoot.getRoot();
root.setIsRoot(false);
fSuper.getSession().delete(vRoot);
if (root.equals(fRoot))
{
// We have to set a new current root.
fSuper.getSession().flush();
query = fSuper.getSession().createQuery("select max(vr.versionID) from VersionRootImpl vr");
int latest = (Integer)query.uniqueResult();
query = fSuper.getSession().getNamedQuery("VersionRoot.VersionByID");
query.setEntity("rep", this);
query.setInteger("version", latest);
vRoot = (VersionRoot)query.uniqueResult();
fRoot = vRoot.getRoot();
}
query = fSuper.getSession().getNamedQuery("FindOrphans");
Iterator<AVMNode> iter = (Iterator<AVMNode>)query.iterate();
while (iter.hasNext())
{
AVMNode node = iter.next();
System.err.println(node.getId());
}
}
}

View File

@@ -19,234 +19,764 @@ package org.alfresco.repo.avm;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
/**
* A SuperRepository is responsible for the high level implemenation of all
* operations on Repositories. It is responsible for issuing Node ids, branch ids,
* and layer ids. Repositories themselves are responsible for issuing version ids.
* Paths in super repositories are of the form "repositoryname:a/b/c/d".
* This or Repository are
* the implementors of the operations specified by AVMService.
* @author britt
*/
public interface SuperRepository
class SuperRepository
{
// Modify operations.
/**
* The single instance of SuperRepository.
*/
private static SuperRepository fgInstance;
/**
* Create a new file.
* @param path The path to create the file in.
* @param name The name to give the new file.
* The Hibernate Session associated with the current operation.
*/
public void createFile(String path, String name);
private ThreadLocal<Session> fSession;
/**
* Create a new Directory.
* @param path The path to create the directory in.
* @param name The name to give the new directory.
* The current lookup count.
*/
public void createDirectory(String path, String name);
private ThreadLocal<Integer> fLookupCount;
/**
* Create a new LayeredDirectory.
* @param srcPath The source path that the LayeredDirectory refers to.
* @param dstPath The path to create the new LayeredDirectory in.
* @param name The name to give the new LayeredDirectory.
* The node id issuer.
*/
public void createLayeredDirectory(String srcPath, String dstPath, String name);
private Issuer fNodeIssuer;
/**
* Create a new LayeredFile.
* @param srcPath The source path that the LayeredFile refers to.
* @param dstPath The path to create the new LayeredFile in.
* @param name The name to give the new LayeredFile.
* The content id issuer;
*/
public void createLayeredFile(String srcPath, String dstPath, String name);
private Issuer fContentIssuer;
/**
* Create a new Repository.
* @param name The name to give the repository.
* The layer id issuer.
*/
public void createRepository(String name);
/**
* Create a branch.
* @param version The version to look under.
* @param srcPath The path to the source node for the branch.
* @param dstPath The directory to put the branch in.
* @param name The name for the new branch.
*/
public void createBranch(int version, String srcPath, String dstPath, String name);
// Modify operations.
/**
* Get an OutputStream to a file.
* @param path The path to the file.
* @return An OutputStream.
*/
public OutputStream getOutputStream(String path);
/**
* Rename a node.
* @param srcPath The source path.
* @param srcName The name of the node to rename.
* @param dstPath The destination directory for the rename.
* @param dstName The name to give the renamed node.
*/
public void rename(String srcPath, String srcName, String dstPath, String dstName);
/**
* Slide a directory in a layer to another location in the same
* layer uncovering what was originally shadowed.
* @param srcPath The source path.
* @param srcName The name of the directory to slide.
* @param dstPath The destination directory for the slide.
* @param dstName The name of the directory after sliding.
*/
public void slide(String srcPath, String srcName, String dstPath, String dstName);
private Issuer fLayerIssuer;
/**
* Create a snapshot of the given repositories.
* @param repositories A List of Repository names.
* The file storage directory.
*/
public void createSnapshot(List<String> repositories);
private String fStorage;
/**
* Create a new one. It's given issuers and a storage directory.
* @param nodeIssuer
* @param contentIssuer
* @param branchIssuer
* @param layerIssuer
* @param storage
*/
public SuperRepository(Issuer nodeIssuer,
Issuer contentIssuer,
Issuer layerIssuer,
String storage)
{
fStorage = storage;
fNodeIssuer = nodeIssuer;
fContentIssuer = contentIssuer;
fLayerIssuer = layerIssuer;
fSession = new ThreadLocal<Session>();
fLookupCount = new ThreadLocal<Integer>();
fgInstance = this;
}
/**
* Set the (thread local) Hibernate session.
* @param session The Session to set.
*/
public void setSession(Session session)
{
fSession.set(session);
fLookupCount.set(0);
}
/**
* Create a file.
* @param path The path to the containing directory.
* @param name The name for the new file.
* @param source A (possibly null) InputStream with content for the new file.
*/
public OutputStream createFile(String path, String name)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.UPGRADE);
return rep.createFile(pathParts[1], name);
}
/**
* 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);
String[] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.UPGRADE);
rep.createDirectory(pathParts[1], name);
}
/**
* 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);
String[] pathParts = SplitPath(dstPath);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.UPGRADE);
rep.createLayeredDirectory(srcPath, pathParts[1], name);
}
/**
* 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);
String[] pathParts = SplitPath(dstPath);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.UPGRADE);
rep.createLayeredFile(srcPath, pathParts[1], name);
}
/**
* Create a new repository.
* @param name The name to give the new repository.
*/
public void createRepository(String name)
{
// TODO need to check for repository existence first.
// Newing up the object causes it to be written to the db.
@SuppressWarnings("unused")
Repository rep = new RepositoryImpl(this, name);
// Special handling for repository creation.
// TODO is this needed.
rep.getRoot().setIsNew(false);
}
/**
* 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 = SplitPath(srcPath);
Repository srcRepo = getRepositoryByName(pathParts[0]);
fSession.get().lock(srcRepo, LockMode.READ);
Lookup sPath = srcRepo.lookup(version, pathParts[1]);
// Lookup the destination directory.
fLookupCount.set(1);
pathParts = SplitPath(dstPath);
Repository dstRepo = getRepositoryByName(pathParts[0]);
fSession.get().lock(dstRepo, LockMode.UPGRADE);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1]);
// dPath.acquireLocks();
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.addChild(name, dstNode, dPath);
}
/**
* 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);
String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.UPGRADE);
return rep.getOutputStream(pathParts[1]);
}
/**
* Get a random access file from a file node.
* @param version The version id (read-only if not -1)
* @param path The path to the file.
* @param access The access mode for RandomAccessFile.
* @return A RandomAccessFile.
*/
public RandomAccessFile getRandomAccess(int version, String path, String access)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.UPGRADE);
return rep.getRandomAccess(version, pathParts[1], access);
}
/**
* 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 = SplitPath(srcPath);
Repository srcRepo = getRepositoryByName(pathParts[0]);
fSession.get().lock(srcRepo, LockMode.UPGRADE);
Lookup sPath = srcRepo.lookupDirectory(-1, pathParts[1]);
// sPath.acquireLocks();
DirectoryNode srcDir = (DirectoryNode)sPath.getCurrentNode();
AVMNode srcNode = srcDir.lookupChild(sPath, srcName, -1);
if (srcNode == null)
{
throw new AVMNotFoundException("Not found: " + srcName);
}
fLookupCount.set(1);
pathParts = SplitPath(dstPath);
Repository dstRepo = getRepositoryByName(pathParts[0]);
fSession.get().lock(dstRepo, LockMode.UPGRADE);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1]);
// dPath.acquireLocks();
DirectoryNode dstDir = (DirectoryNode)dPath.getCurrentNode();
AVMNode dstNode = dstDir.lookupChild(dPath, dstName, -1);
if (dstNode != null)
{
throw new AVMExistsException("Node exists: " + dstName);
}
// 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)
{
// TODO I think I need to subdivide this logic again.
// based on whether the destination is a layer or not.
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(srcName, sPath);
fLookupCount.set(1);
pathParts = SplitPath(dstPath);
dPath = dstRepo.lookup(-1, pathParts[1]);
// dPath.acquireLocks();
dstDir = (DirectoryNode)dPath.getCurrentNode();
dstNode.setVersionID(dstRepo.getNextVersionID());
dstDir.addChild(dstName, dstNode, dPath);
dstNode.setAncestor(srcNode);
}
/**
* 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);
String [] pathParts = SplitPath(dirPath);
Repository repo = getRepositoryByName(pathParts[0]);
fSession.get().lock(repo, LockMode.UPGRADE);
repo.uncover(pathParts[1], name);
}
/**
* Snapshot the given repositories.
* @param repositories The list of repository name to snapshot.
*/
public void createSnapshot(List<String> repositories)
{
for (String repName : repositories)
{
Repository repo = getRepositoryByName(repName);
fSession.get().lock(repo, LockMode.UPGRADE);
repo.createSnapshot();
}
}
/**
* Create a snapshot of a single repository.
* @param repository The name of the repsository.
* @param repository The name of the repository.
*/
public void createSnapshot(String repository);
// Different flavors of deletions.
public void createSnapshot(String repository)
{
Repository repo = getRepositoryByName(repository);
fSession.get().lock(repo, LockMode.UPGRADE);
repo.createSnapshot();
}
/**
* Delete a node.
* 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);
/**
* Purge a repository.
* @param name
*/
public void destroyRepository(String name);
/**
* Purge a version in a repository.
* @param name The name of the repository.
* @param version The id of the version to purge.
*/
public void purgeVersion(String name, int version);
public void remove(String path, String name)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
Repository repo = getRepositoryByName(pathParts[0]);
fSession.get().lock(repo, LockMode.UPGRADE);
repo.removeNode(pathParts[1], name);
}
// Read operations.
/**
* Get rid of all content that lives only in the given repository.
* Also removes the repository.
* @param name The name of the repository to purge.
*/
@SuppressWarnings("unchecked")
public void purgeRepository(String name)
{
Repository rep = getRepositoryByName(name);
fSession.get().lock(rep, LockMode.UPGRADE);
AVMNode root = rep.getRoot();
root.setIsRoot(false);
Query query = fSession.get().createQuery("from VersionRootImpl vr where vr.repository = :rep");
query.setEntity("rep", rep);
List<VersionRoot> vRoots = (List<VersionRoot>)query.list();
for (VersionRoot vr : vRoots)
{
AVMNode node = vr.getRoot();
node.setIsRoot(false);
fSession.get().delete(vr);
}
query = fSession.get().createQuery("from AVMNodeImpl an where an.repository = :rep");
query.setEntity("rep", rep);
Iterator<AVMNode> iter = (Iterator<AVMNode>)query.iterate();
while (iter.hasNext())
{
AVMNode node = iter.next();
node.setRepository(null);
}
fSession.get().flush();
fSession.get().delete(rep);
query = fSession.get().getNamedQuery("FindOrphans");
List<AVMNode> nodes = (List<AVMNode>)query.list();
for (AVMNode node : nodes)
{
System.err.println(node.getId());
}
}
/**
* Get an InputStream from a File.
* Remove all content specific to a repository and version.
* @param name The name of the repository.
* @param version The version to purge.
*/
public void purgeVersion(String name, int version)
{
Repository rep = getRepositoryByName(name);
fSession.get().lock(rep, LockMode.UPGRADE);
rep.purgeVersion(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);
public InputStream getInputStream(int version, String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
Repository repo = getRepositoryByName(pathParts[0]);
fSession.get().lock(repo, LockMode.READ);
return repo.getInputStream(version, pathParts[1]);
}
/**
* Get the listing for a directory.
* @param version The version to get a listing of.
* Get a listing of a directory.
* @param version The version to look under.
* @param path The path to the directory.
* @return A List of FolderEntries.
*/
public List<FolderEntry> getListing(int version, String path);
/**
* Get a listing of all repository names.
* @return A List of repository names.
*/
public List<String> getRepositoryNames();
public List<FolderEntry> getListing(int version, String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
Repository repo = getRepositoryByName(pathParts[0]);
fSession.get().lock(repo, LockMode.READ);
return repo.getListing(version, pathParts[1]);
}
/**
* Get a Set of version IDs for a given Repository.
* Get a directory listing from a directory node descriptor.
* @param dir The directory node descriptor.
* @return
*/
public Map<String, AVMNodeDescriptor> getListing(AVMNodeDescriptor dir)
{
fLookupCount.set(1);
AVMNode node = (AVMNode)fSession.get().get(AVMNodeImpl.class, 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.getListing(dir);
}
/**
* Get the names of all repositories.
* @return A list of names.
*/
@SuppressWarnings("unchecked")
public List<String> getRepositoryNames()
{
Query query = fSession.get().createQuery("select r.name from RepositoryImpl r");
return (List<String>)query.list();
}
/**
* Get all version ids for a given repository.
* @param name The name of the repository.
* @return A Set of IDs.
* @return A Set will all the version ids.
*/
public Set<Integer> getRepositoryVersions(String name);
public Set<Integer> getRepositoryVersions(String name)
{
Repository rep = getRepositoryByName(name);
fSession.get().lock(rep, LockMode.READ);
return rep.getVersions();
}
/**
* Issue a node id.
* @return The new id.
*/
public long issueID()
{
return fNodeIssuer.issue();
}
/**
* Issue a content id.
* @return The new id.
*/
public long issueContentID()
{
return fContentIssuer.issue();
}
/**
* Issue a new layer id.
* @return The new id.
*/
public long issueLayerID()
{
return fLayerIssuer.issue();
}
/**
* Issue a unique identifier for a new node.
* @return A new identifier.
* Get the (thread local) Hibernate session.
* @return The Session.
*/
public long issueID();
/**
* Issue an ID for content.
* @return A new content ID.
*/
public long issueContentID();
/**
* Issue an ID for the next layer.
* @return The layer id.
*/
public long issueLayerID();
/**
* Get the current Hibernate Session.
* @return The Hibernate Session object.
*/
public Session getSession();
/**
* Return an OutputStream for the content object of the given id. This
* creates the directory path if needed.
* @param path The path to the file.
* @return An OutputStream.
*/
public OutputStream createContentOutputStream(String path);
/**
* Gets an input stream from the content with the given id.
* @param version The version id.
* @param path The path to the file.
* @return An InputStream.
*/
public InputStream getContentInputStream(int version, String path);
/**
* Get the latest version id for a given repository.
* @param name The name of the repository.
* @return The latest version id for the given repository.
*/
public int getLatestVersionID(String name);
public Session getSession()
{
return fSession.get();
}
/**
* 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);
public String getIndirectionPath(int version, String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.READ);
return rep.getIndirectionPath(version, pathParts[1]);
}
/**
* Get the next version id for the given repository.
* @param name The name of the repository.
* @return The next version id.
*/
public int getLatestVersionID(String name)
{
Repository rep = getRepositoryByName(name);
return rep.getNextVersionID();
}
/**
* Get a lookup object for a path.
* Get a repository by name.
* @param name The name of the repository.
* @return The Repository.
*/
private Repository getRepositoryByName(String name)
{
return (Repository)fSession.get().get(RepositoryImpl.class, name);
}
/**
* Get a descriptor for a repository root.
* @param version The version to get.
* @param name The name of the repository.
* @return The descriptor for the root.
*/
public AVMNodeDescriptor getRepositoryRoot(int version, String name)
{
Repository rep = getRepositoryByName(name);
if (rep == null)
{
throw new AVMNotFoundException("Not found: " + name);
}
fSession.get().lock(rep, LockMode.READ);
return rep.getRoot(version);
}
/**
* Lookup a node.
* @param version The version to look under.
* @param path The full path.
* @return The Lookup.
* @param path The path to lookup.
* @return A lookup object.
*/
public Lookup lookup(int version, String path);
public Lookup lookup(int version, String path)
{
fLookupCount.set(fLookupCount.get() + 1);
if (fLookupCount.get() > 10)
{
throw new AVMCycleException("Cycle in lookup.");
}
String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.READ);
return rep.lookup(version, pathParts[1]);
}
/**
* Get a lookup object for a path. Directory only.
* 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)
{
fLookupCount.set(0);
AVMNode node = (AVMNode)fSession.get().get(AVMNodeImpl.class, 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);
}
/**
* Lookup a directory specifically.
* @param version The version to look under.
* @param path The full path.
* @return The Lookup.
* @param path The path to lookup.
* @return A lookup object.
*/
public Lookup lookupDirectory(int version, String path);
public Lookup lookupDirectory(int version, String path)
{
fLookupCount.set(fLookupCount.get() + 1);
if (fLookupCount.get() > 10)
{
throw new AVMCycleException("Cycle in lookup.");
}
String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.READ);
return rep.lookupDirectory(version, pathParts[1]);
}
/**
* 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;
}
/**
* Get the path to file storage.
* @return The root path of file storage.
*/
public String getStorageRoot()
{
return fStorage;
}
/**
* Get the root directory in which file data is stored.
* @return The root directory of storage.
* Make a directory into a primary indirection.
* @param path The full path.
*/
public String getStorageRoot();
public void makePrimary(String path)
{
fLookupCount.set(1);
String[] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.UPGRADE);
rep.makePrimary(pathParts[1]);
}
/**
* 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);
String[] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
fSession.get().lock(rep, LockMode.UPGRADE);
rep.retargetLayeredDirectory(pathParts[1], target);
}
/**
* Get the single instance of SuperRepository.
* @return
*/
public static SuperRepository GetInstance()
{
return fgInstance;
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
/**
* Represents a single version root.
* @author britt
*/
public interface VersionRoot
{
/**
* @return the createDate
*/
public long getCreateDate();
/**
* @param createDate the createDate to set
*/
public void setCreateDate(long createDate);
/**
* @return the creator
*/
public String getCreator();
/**
* @param creator the creator to set
*/
public void setCreator(String creator);
/**
* @return the id
*/
public long getId();
/**
* @param id the id to set
*/
public void setId(long id);
/**
* @return the repository
*/
public Repository getRepository();
/**
* @param repository the repository to set
*/
public void setRepository(Repository repository);
/**
* @return the root
*/
public DirectoryNode getRoot();
/**
* @param root the root to set
*/
public void setRoot(DirectoryNode root);
/**
* Set the version id.
* @param versionID
*/
public void setVersionID(int versionID);
/**
* Get the version id.
* @return The version id.
*/
public int getVersionID();
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm;
import java.io.Serializable;
/**
* Hold a single version root.
* @author britt
*/
public class VersionRootImpl implements VersionRoot, Serializable
{
static final long serialVersionUID = 8826954538210455917L;
/**
* The object id
*/
private Long fID;
/**
* The version id.
*/
private int fVersionID;
/**
* The creation date.
*/
private long fCreateDate;
/**
* The creator.
*/
private String fCreator;
/**
* The Repository.
*/
private Repository fRepository;
/**
* The root node.
*/
private DirectoryNode fRoot;
/**
* A default constructor.
*/
public VersionRootImpl()
{
}
/**
* Rich constructor.
* @param repository
* @param root
* @param versionID
* @param createDate
* @param creator
*/
public VersionRootImpl(Repository repository,
DirectoryNode root,
int versionID,
long createDate,
String creator)
{
fRepository = repository;
fRoot = root;
fVersionID = versionID;
fCreateDate = createDate;
fCreator = creator;
}
public long getCreateDate()
{
return fCreateDate;
}
public void setCreateDate(long createDate)
{
fCreateDate = createDate;
}
public String getCreator()
{
return fCreator;
}
public void setCreator(String creator)
{
fCreator = creator;
}
public long getId()
{
return fID;
}
public void setId(long id)
{
fID = id;
}
public Repository getRepository()
{
return fRepository;
}
public void setRepository(Repository repository)
{
fRepository = repository;
}
public DirectoryNode getRoot()
{
return fRoot;
}
public void setRoot(DirectoryNode root)
{
fRoot = root;
}
/**
* Set the versionID.
* @param versionID
*/
public void setVersionID(int versionID)
{
fVersionID = versionID;
}
/**
* Get the version id.
* @return The version id.
*/
public int getVersionID()
{
return fVersionID;
}
/**
* Check equality. Based on Repository equality and version id equality.
* @param obj
* @return
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof VersionRoot))
{
return false;
}
VersionRoot other = (VersionRoot)obj;
return fRepository.equals(other.getRepository())
&& fVersionID == other.getVersionID();
}
/**
* Generate a hash code.
* @return The hash code.
*/
@Override
public int hashCode()
{
return fRepository.hashCode() + fVersionID;
}
}

View File

@@ -1,50 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="org.alfresco.repo.avm.hibernate">
<hibernate-mapping package="org.alfresco.repo.avm">
<!-- AVMNodeBean is the abstract base for filesystem like objects.
We're using the one table per class hierarchy strategy to implement
polymorphism. -->
<class table="avm_nodes" abstract="true"
name="AVMNodeBeanImpl"
proxy="AVMNodeBean"
optimistic-lock="version" lazy="true">
name="AVMNodeImpl"
proxy="AVMNode"
optimistic-lock="version"
lazy="true">
<cache usage="read-write"/>
<!-- The id is set programmatically using an Issuer. See below. -->
<id name="id" column="id" type="long"/>
<id name="id"
column="id"
type="long"/>
<!-- I suppose this would be more efficient to encode type by an int.
We'll see if using a string makes a difference. -->
<discriminator column="class_type" type="string" length="20"/>
<discriminator column="class_type"
type="string"
length="20"/>
<!-- We're using hibernate's own versioning scheme for concurrency control.
I don't know how well that will play with a full Spring-JTA stack. -->
<version column="vers" name="vers" type="long"/>
<version column="vers"
name="vers"
type="long"/>
<!-- BasicAttributes are attributes that pretty much all AVMNodes will
have. -->
<many-to-one name="ancestor" column="ancestor_id"
class="AVMNodeBeanImpl"/>
class="AVMNodeImpl"/>
<!-- Nothing does anything with this yet. We'll need it for
complete versioning semantics. -->
<many-to-one name="mergedFrom" column="merged_from"
class="AVMNodeBeanImpl"/>
class="AVMNodeImpl"/>
<!-- Parent is a very specific notion of containment. The parent
of an AVMNode is the parent in which that node was created. -->
<many-to-one name="parent" column="parent_id"
class="DirectoryNodeBeanImpl"/>
class="DirectoryNodeImpl"/>
<!-- This should really be not null, but I haven't figured out
the right way to build the relation so that nullability constraints
won't cause violations in the db during saves. -->
<many-to-one name="repository" column="repository"
class="RepositoryBeanImpl" cascade="save-update"/>
class="RepositoryImpl"/>
<property name="versionID" type="int" column="version_id"
not-null="true"/>
<!-- The branch id is always 0 for nodes that are not part of
a branch. -->
<property name="branchID" type="long" column="branch_id"
not-null="true"/>
<!-- This is, in fact a duplication of data, but an extremely
convenient one. -->
<property name="isNew" column="is_new" type="boolean"
not-null="true"/>
<component name="basicAttributes" class="BasicAttributesBeanImpl">
<component name="basicAttributes" class="BasicAttributesImpl">
<property name="creator" type="string" not-null="true"/>
<property name="owner" type="string" not-null="true"/>
<property name="lastModifier" type="string" not-null="true"/>
@@ -52,19 +53,19 @@
<property name="modDate" type="long" not-null="true"/>
<property name="accessDate" type="long" not-null="true"/>
</component>
<property name="isRoot" column="is_root" type="boolean"/>
<!-- Directories, two flavors. -->
<subclass name="DirectoryNodeBeanImpl"
proxy="DirectoryNodeBean"
<subclass name="DirectoryNodeImpl"
proxy="DirectoryNode"
abstract="true"
lazy="true">
<!-- A Layered Directory is our smart symlink thingy. -->
<subclass name="LayeredDirectoryNodeBeanImpl"
proxy="LayeredDirectoryNodeBean"
<subclass name="LayeredDirectoryNodeImpl"
proxy="LayeredDirectoryNode"
discriminator-value="layereddirectory" lazy="true">
<!-- The layer id is an implementation trick to disambiguate
exactly what layer is being refered to in various circumstances. -->
<property name="layerID" column="layer_id" type="long"
not-null="true" />
<property name="layerID" column="layer_id" type="long"/>
<!-- The is the moral equivalent of the value of a symlink. -->
<property name="indirection" column="indirection"
type="string" length="511" />
@@ -72,74 +73,36 @@
it points at (true) or inheriting what it points at from its
container (false). -->
<property name="primaryIndirection"
column="primary_indirection" type="boolean" />
column="primary_indirection" type="boolean"/>
<!-- Map of names to DirectoryEntries. -->
<map name="added" cascade="all" lazy="true">
<cache usage="read-write"/>
<key column="directory_id" />
<map-key type="string" column="name" />
<!-- A DirectoryEntry is a (node)type AVMNode reference pair.
Should probably convert type into an integer code. -->
<composite-element class="DirectoryEntry">
<property name="type" column="type_code"
type="int" not-null="true" />
<many-to-one name="child"
class="AVMNodeBeanImpl" cascade="save-update"
not-null="true">
</many-to-one>
</composite-element>
</map>
<!-- This is just the set of names of children deleted (and therefore
hidden) in this layer. -->
<set name="deleted" table="deleted_children"
fetch="join" cascade="all">
<cache usage="read-write"/>
<key column="directory_id" />
<element type="string" column="name" />
</set>
</subclass>
<!-- Just plain directories. -->
<subclass name="PlainDirectoryNodeBeanImpl"
discriminator-value="plaindirectory" proxy="PlainDirectoryNodeBean" lazy="true">
<subclass name="PlainDirectoryNodeImpl"
discriminator-value="plaindirectory" proxy="PlainDirectoryNode" lazy="true">
<!-- For reference count based garbage collection of purged nodes
to work we need to specially mark the root directory of
a Repository as special so that its absence of parents will
not cause it to be vacuumed away. TODO: for ease of queries,
this probably wants to be in the base class. -->
<property name="isRoot" column="is_root" type="boolean"/>
<!-- A map of names to DirectoryEntries. In the AVM world, it makes sense
that nodes don't know there own names, only their containers do. -->
<map name="children" cascade="all" lazy="true">
<cache usage="read-write"/>
<key column="directory_id"/>
<map-key type="string" column="name"/>
<composite-element
class="DirectoryEntry">
<property name="type" type="int"
not-null="true" column="type_code" />
<many-to-one name="child"
class="AVMNodeBeanImpl"
not-null="true" cascade="save-update">
</many-to-one>
</composite-element>
</map>
</subclass>
</subclass>
<!-- There are two kinds of files, plain and symlinky. -->
<subclass name="FileNodeBeanImpl"
proxy="FileNodeBean"
<subclass name="FileNodeImpl"
proxy="FileNode"
abstract="true"
lazy="false">
<!-- Plain files just have a reference to a Content object. -->
<subclass discriminator-value="plainfile"
name="PlainFileNodeBeanImpl" proxy="PlainFileNodeBean" lazy="true">
name="PlainFileNodeImpl" proxy="PlainFileNode" lazy="true">
<many-to-one name="content" column="content_id"
class="ContentBeanImpl" fetch="join" cascade="save-update">
class="FileContentImpl" fetch="join" cascade="save-update">
</many-to-one>
</subclass>
<!-- Layered files are almost exactly copy on write symlinks. -->
<subclass name="LayeredFileNodeBeanImpl"
discriminator-value="layeredfile" proxy="LayeredFileNodeBean" lazy="true">
<subclass name="LayeredFileNodeImpl"
discriminator-value="layeredfile" proxy="LayeredFileNode" lazy="true">
<property name="indirection" type="string" length="511"
column="indirection" />
</subclass>
@@ -147,7 +110,7 @@
</class>
<!-- Contents are objects to hang actual bytestreams off. They are explicitly reference
counted. -->
<class table="contents" name="ContentBeanImpl" proxy="ContentBean"
<class table="contents" name="FileContentImpl" proxy="FileContent"
optimistic-lock="version">
<cache usage="read-write" />
<id name="id" column="id" type="long"/>
@@ -157,51 +120,128 @@
scheme. -->
<property name="refCount" column="ref_count" type="int"
not-null="true"/>
</class>
<!-- Issuers issue ids. They are explicit primary key generators. They seem more
convenient than using 'native' generators, and may be more efficient. Come
to think of it I'm not convinced that this is true. However I had a reason for
doing things this way... -->
<class name="Issuer" table="issuers" lazy="false"
optimistic-lock="version">
<cache usage="read-write" />
<id name="name" column="name" type="string" length="20"/>
<version name="vers" column="vers" type="long"/>
<property name="next" column="next" type="long"
not-null="true"/>
</class>
<!-- A Repository is the what we used to call a virtual repository.
Each Repository has it's own branch ids and layer ids but shares node ids
with other repositories. The physical repository is structured this way
for better scaling. -->
<class table="repositories" name="RepositoryBeanImpl"
proxy="RepositoryBean" optimistic-lock="version">
<cache usage="read-write" />
<class table="repositories" name="RepositoryImpl"
proxy="Repository" optimistic-lock="version">
<cache usage="read-write"/>
<id name="name" column="name" type="string"/>
<version name="vers" column="vers" type="long"/>
<property type="int" name="nextVersionID"
column="next_version_id" not-null="true"/>
<!-- Every Repository has a root directory that is the current root directory. -->
<!-- This should be not-null but hibernate (or my own idiocy) makes that difficult. -->
<many-to-one name="root" class="DirectoryNodeBeanImpl"
<many-to-one name="root" class="DirectoryNodeImpl"
column="current_root_id" unique="true" cascade="save-update">
</many-to-one>
<!-- In addition to the current root directory, a Repository maintains a set
of versioned root directories. -->
<map name="roots" table="repository_roots">
<cache usage="read-write"/>
<key column="repository_id"/>
<map-key type="int" column="version_id"/>
<many-to-many class="DirectoryNodeBeanImpl"
column="directory_id"/>
</map>
<!-- NewNodes keeps track of those nodes created since the last
'snapshot' operation. -->
<set table="new_nodes" name="newNodes" fetch="join" lazy="true" cascade="all">
<cache usage="read-write"/>
<key column="repository_id"/>
<many-to-many class="AVMNodeBeanImpl"
column="new_node_id"/>
</set>
</class>
<class name="VersionRootImpl" proxy="VersionRoot" table="version_roots">
<!-- <cache usage="read-write"/> -->
<id column="id" type="long">
<generator class="native"></generator>
</id>
<property name="versionID" type="int" not-null="true"
column="version_id" index="version_roots_version_id_index">
</property>
<property name="createDate" type="long" not-null="true" column="create_date">
</property>
<property name="creator" type="string" column="creator"
not-null="true">
</property>
<many-to-one name="repository" column="repository_id"
class="RepositoryImpl" not-null="true">
</many-to-one>
<many-to-one name="root" class="DirectoryNodeImpl"
column="root_id" not-null="true">
</many-to-one>
</class>
<class name="ChildEntryImpl" proxy="ChildEntry" table="child_entries">
<cache usage="read-write"/>
<composite-id>
<key-property name="name" type="string" column="name">
</key-property>
<key-many-to-one name="parent" column="parent_id" class="DirectoryNodeImpl">
</key-many-to-one>
</composite-id>
<many-to-one name="child" column="child_id" class="AVMNodeImpl"
not-null="true">
</many-to-one>
</class>
<class table="deleted_children" name="DeletedChildImpl" proxy="DeletedChild">
<cache usage="read-write"/>
<composite-id>
<key-property name="name" column="name" type="string"/>
<key-many-to-one name="parent" class="LayeredDirectoryNodeImpl"
column="parent_id"/>
</composite-id>
</class>
<query name="ChildEntry.ByNameParent">
<![CDATA[
from ChildEntryImpl ce
where
ce.name = :name and ce.parent = :parent
]]>
</query>
<query name="ChildEntry.ByParent">
<![CDATA[
from ChildEntryImpl ce
where
ce.parent = :parent
]]>
</query>
<query name="ChildEntry.ByParentChild">
<![CDATA[
from ChildEntryImpl ce
where
ce.child = :child and ce.parent = :parent
]]>
</query>
<query name="AVMNode.ByNewInRepo">
<![CDATA[
from AVMNodeImpl a
where
a.repository = :repo and a.isNew = true
]]>
</query>
<query name="VersionRoot.GetVersionRoot">
<![CDATA[
select v.root
from VersionRootImpl v
where
v.repository = :rep and v.versionID = :version
]]>
</query>
<query name="VersionRoot.VersionByID">
<![CDATA[
from VersionRootImpl v
where
v.repository = :rep and v.versionID = :version
]]>
</query>
<query name="DeletedChild.ByNameParent">
<![CDATA[
from DeletedChildImpl dc
where
dc.parent = :parent and dc.name = :name
]]>
</query>
<query name="DeletedChild.ByParent">
<![CDATA[
from DeletedChildImpl dc
where
dc.parent = :parent
]]>
</query>
<query name="FindOrphans">
<![CDATA[
from AVMNodeImpl an
where
an not in ( select ce.child
from ChildEntryImpl ce )
and an.isRoot = false
]]>
</query>
</hibernate-mapping>

View File

@@ -17,10 +17,16 @@ package org.alfresco.repo.avm.hibernate;
* License.
*/
import java.util.Random;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.alfresco.repo.avm.AVMException;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StaleStateException;
import org.hibernate.Transaction;
import org.hibernate.exception.GenericJDBCException;
/**
* Helper for DAOs.
@@ -33,6 +39,16 @@ public class HibernateTxn
*/
private SessionFactory fSessionFactory;
/**
* The random number generator for inter-retry sleep.
*/
private Random fRandom;
/**
* The BFL.
*/
private ReentrantReadWriteLock fLock;
/**
* Make one up.
* @param sessionFactory The SessionFactory.
@@ -40,52 +56,110 @@ public class HibernateTxn
public HibernateTxn(SessionFactory sessionFactory)
{
fSessionFactory = sessionFactory;
fRandom = new Random();
fLock = new ReentrantReadWriteLock(true); // Make the lock fair.
}
/**
* Perform a set of operations under a single Hibernate transaction.
* Keep trying if the operation fails because of a concurrency issue.
* @param callback The worker.
* @param write Whether this is a write operation.
* @return Whether the operation finished with a commit.
*/
public boolean perform(HibernateTxnCallback callback)
public void perform(HibernateTxnCallback callback, boolean write)
{
Session sess = null;
Transaction txn = null;
try
while (true)
{
sess = fSessionFactory.openSession();
txn = sess.beginTransaction();
callback.perform(sess);
txn.commit();
return true;
}
catch (Throwable t)
{
t.printStackTrace(System.err);
if (txn != null)
try
{
try
/*
if (write)
{
txn.rollback();
fLock.writeLock().lock();
}
catch (HibernateException he)
else
{
// Do nothing.
fLock.readLock().lock();
}
*/
sess = fSessionFactory.openSession();
txn = sess.beginTransaction();
callback.perform(sess);
txn.commit();
return;
}
return false;
}
finally
{
if (sess != null)
catch (Throwable t)
{
try
// TODO Add appropriate logging.
if (txn != null)
{
sess.close();
try
{
txn.rollback();
}
catch (HibernateException he)
{
// Do nothing.
}
// If we've lost a race or we've deadlocked, retry.
if (t instanceof StaleStateException ||
t instanceof GenericJDBCException)
{
if (t instanceof StaleStateException)
{
System.err.println("Lost Race");
continue;
}
System.err.println("Deadlock");
try
{
long interval;
synchronized (fRandom)
{
interval = fRandom.nextInt(1000);
}
Thread.sleep(interval);
}
catch (InterruptedException ie)
{
// Do nothing.
}
continue;
}
}
catch (HibernateException he)
if (t instanceof AVMException)
{
// Do nothing.
throw (AVMException)t;
}
// TODO Crack t into more useful exception types.
// Otherwise nothing we can do except throw.
throw new AVMException("Unrecoverable error", t);
}
finally
{
/*
if (write)
{
fLock.writeLock().unlock();
}
else
{
fLock.readLock().unlock();
}
*/
if (sess != null)
{
try
{
sess.close();
}
catch (HibernateException he)
{
// Do nothing.
}
}
}
}

View File

@@ -1,484 +0,0 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm.impl;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.avm.AVMService;
import org.alfresco.repo.avm.FolderEntry;
import org.alfresco.repo.avm.Lookup;
import org.alfresco.repo.avm.SuperRepository;
import org.alfresco.repo.avm.hibernate.HibernateHelper;
import org.alfresco.repo.avm.hibernate.HibernateTxn;
import org.alfresco.repo.avm.hibernate.HibernateTxnCallback;
import org.alfresco.repo.avm.hibernate.Issuer;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.tool.hbm2ddl.SchemaExport;
/**
* Implements the AVMService. Stub.
* @author britt
*/
public class AVMServiceImpl implements AVMService
{
/**
* The Hibernate SessionFactory.
*/
private SessionFactory fSessionFactory;
/**
* The HibernateTxn.
*/
private HibernateTxn fTransaction;
/**
* The SuperRepository for each service thread.
*/
private ThreadLocal<SuperRepository> fSuperRepository;
/**
* The storage directory.
*/
private String fStorage;
/**
* Basic constructor for the service.
* @param createTables Flag for whether tables should be created.
*/
public AVMServiceImpl()
{
fSuperRepository = new ThreadLocal<SuperRepository>();
fSessionFactory = HibernateHelper.GetSessionFactory();
fTransaction = new HibernateTxn(fSessionFactory);
}
/**
* Final initialization of the service. Must be called only on a
* fully initialized instance.
* @param createTables Whether we should create tables, and a default
* repository.
*/
public void init(boolean createTables)
{
if (createTables)
{
SchemaExport se = new SchemaExport(HibernateHelper.GetConfiguration());
se.drop(false, true);
se.create(false, true);
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
new Issuer("node", 0L, session);
new Issuer("content", 0L, session);
new Issuer("branch", 0L, session);
new Issuer("layer", 0L, session);
}
};
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
createRepository("main");
}
}
/**
* Set the location of file storage.
* @param storage
*/
public void setStorage(String storage)
{
fStorage = storage;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getFileInputStream(int, java.lang.String)
*/
public InputStream getFileInputStream(final int version, final String path)
{
class HTxnCallback implements HibernateTxnCallback
{
public InputStream in = null;
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
in = fSuperRepository.get().getInputStream(version, path);
}
};
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
return doit.in;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getFileOutputStream(java.lang.String)
*/
public OutputStream getFileOutputStream(final String path)
{
class HTxnCallback implements HibernateTxnCallback
{
public OutputStream out = null;
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
out = fSuperRepository.get().getOutputStream(path);
}
};
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
return doit.out;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getFolderListing(int, java.lang.String)
*/
public List<FolderEntry> getDirectoryListing(final int version, final String path)
{
class HTxnCallback implements HibernateTxnCallback
{
public List<FolderEntry> listing;
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
listing = fSuperRepository.get().getListing(version, path);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
return doit.listing;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createFile(java.lang.String, java.lang.String)
*/
public void createFile(final String path, final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().createFile(path, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createFolder(java.lang.String, java.lang.String)
*/
public void createDirectory(final String path, final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().createDirectory(path, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createLayeredFile(java.lang.String, java.lang.String, java.lang.String)
*/
public void createLayeredFile(final String srcPath, final String parent, final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().createLayeredFile(srcPath, parent, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createLayeredFolder(java.lang.String, java.lang.String, java.lang.String)
*/
public void createLayeredDirectory(final String srcPath, final String parent, final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().createLayeredDirectory(srcPath, parent, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createRepository(java.lang.String)
*/
public void createRepository(final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().createRepository(name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createBranch(int, java.lang.String, java.lang.String, java.lang.String)
*/
public void createBranch(final int version, final String srcPath, final String dstPath,
final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().createBranch(version, srcPath, dstPath, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#removeNode(java.lang.String, java.lang.String)
*/
public void removeNode(final String parent, final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().remove(parent, name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#rename(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void rename(final String srcParent, final String srcName, final String dstParent,
final String dstName)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().rename(srcParent, srcName, dstParent, dstName);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#slide(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void slide(final String srcParent, final String srcName, final String dstParent,
final String dstName)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().slide(srcParent, srcName, dstParent, dstName);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getLatestVersionID(java.lang.String)
*/
public int getLatestVersionID(final String repName)
{
class HTxnCallback implements HibernateTxnCallback
{
public int latestVersionID;
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
latestVersionID = fSuperRepository.get().getLatestVersionID(repName);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
return doit.latestVersionID;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createSnapshot(java.util.List)
*/
public void createSnapshot(final List<String> repositories)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().createSnapshot(repositories);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createSnapshot(java.lang.String)
*/
public void createSnapshot(final String repository)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().createSnapshot(repository);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#lookup(int, java.lang.String)
*/
public Lookup lookup(final int version, final String path)
{
class HTxnCallback implements HibernateTxnCallback
{
public Lookup lookup;
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
lookup = fSuperRepository.get().lookup(version, path);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
return doit.lookup;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#destroyRepository(java.lang.String)
*/
public void destroyRepository(final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().destroyRepository(name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#purgeVersion(int, java.lang.String)
*/
public void purgeVersion(final int version, final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
fSuperRepository.get().purgeVersion(name, version);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getIndirectionPath(java.lang.String)
*/
public String getIndirectionPath(final int version, final String path)
{
class HTxnCallback implements HibernateTxnCallback
{
public String indirectionPath;
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
indirectionPath = fSuperRepository.get().getIndirectionPath(version, path);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
return doit.indirectionPath;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#getRepositoryVersions(java.lang.String)
*/
public Set<Integer> getRepositoryVersions(final String name)
{
class HTxnCallback implements HibernateTxnCallback
{
public Set<Integer> versions;
public void perform(Session session)
{
fSuperRepository.set(new SuperRepositoryImpl(session, fStorage));
versions = fSuperRepository.get().getRepositoryVersions(name);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit);
return doit.versions;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#retargetLayeredFolder(java.lang.String, java.lang.String)
*/
public void retargetLayeredFolder(String path, String target)
{
// TODO Auto-generated method stub
}
}

View File

@@ -1,493 +0,0 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm.impl;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.avm.AVMNode;
import org.alfresco.repo.avm.AVMNodeFactory;
import org.alfresco.repo.avm.DirectoryNode;
import org.alfresco.repo.avm.FileContent;
import org.alfresco.repo.avm.FileNode;
import org.alfresco.repo.avm.FolderEntry;
import org.alfresco.repo.avm.Layered;
import org.alfresco.repo.avm.LayeredDirectoryNode;
import org.alfresco.repo.avm.LayeredFileNode;
import org.alfresco.repo.avm.Lookup;
import org.alfresco.repo.avm.PlainDirectoryNode;
import org.alfresco.repo.avm.PlainFileNode;
import org.alfresco.repo.avm.Repository;
import org.alfresco.repo.avm.SuperRepository;
import org.alfresco.repo.avm.hibernate.AVMNodeBean;
import org.alfresco.repo.avm.hibernate.BasicAttributesBean;
import org.alfresco.repo.avm.hibernate.BasicAttributesBeanImpl;
import org.alfresco.repo.avm.hibernate.DirectoryEntry;
import org.alfresco.repo.avm.hibernate.DirectoryNodeBean;
import org.alfresco.repo.avm.hibernate.PlainDirectoryNodeBean;
import org.alfresco.repo.avm.hibernate.PlainDirectoryNodeBeanImpl;
import org.alfresco.repo.avm.hibernate.RepositoryBean;
import org.alfresco.repo.avm.hibernate.RepositoryBeanImpl;
/**
* @author britt
*
*/
public class RepositoryImpl implements Repository
{
/**
* The data bean.
*/
private RepositoryBean fData;
/**
* The super repository.
*/
private SuperRepository fSuper;
/**
* Make a brand new repository.
* @param superRepo The SuperRepository.
* @param name The name of the Repository.
*/
public RepositoryImpl(SuperRepository superRepo, String name)
{
fSuper = superRepo;
fData = new RepositoryBeanImpl(name, null);
fSuper.getSession().save(fData);
long time = System.currentTimeMillis();
// TODO Obviously we have to figure out how to get users from context.
BasicAttributesBean attrs = new BasicAttributesBeanImpl("britt",
"britt",
"britt",
time,
time,
time);
PlainDirectoryNodeBean rootBean =
new PlainDirectoryNodeBeanImpl(fSuper.issueID(),
fData.getNextVersionID(),
0L,
null,
null,
null,
fData,
attrs,
true // is root
);
fSuper.getSession().save(rootBean);
fData.setRoot(rootBean);
fData.getRoots().put(fData.getNextVersionID(), rootBean);
fData.setNextVersionID(fData.getNextVersionID());
fSuper.getSession().save(fData);
}
/**
* Make one from a data bean.
* @param data The data.
*/
public RepositoryImpl(SuperRepository superRepo, RepositoryBean data)
{
fData = data;
fSuper = superRepo;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#setNew(org.alfresco.repo.avm.AVMNode)
*/
public void setNew(AVMNode node)
{
fData.getNewNodes().add(node.getDataBean());
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#getLatestVersion()
*/
public int getLatestVersion()
{
return fData.getNextVersionID();
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#setNewRoot(org.alfresco.repo.avm.DirectoryNode)
*/
public void setNewRoot(DirectoryNode root)
{
fData.setRoot((DirectoryNodeBean)root.getDataBean());
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#createSnapshot()
*/
public void createSnapshot()
{
// Walk through the new nodes and mark them not new.
for (AVMNodeBean newBean : fData.getNewNodes())
{
newBean.setIsNew(false);
}
// Clear out the new nodes.
fData.getNewNodes().clear();
// Add the current root to the root history.
fData.getRoots().put(fData.getNextVersionID(), fData.getRoot());
// Increment the version id.
fData.setNextVersionID(fData.getNextVersionID() + 1);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#createDirectory(java.lang.String, java.lang.String)
*/
public void createDirectory(String path, String name)
{
Lookup lPath = lookupDirectory(-1, path);
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null)
{
throw new AlfrescoRuntimeException("Child exists: " + name);
}
DirectoryNode newDir = null;
if (lPath.isLayered()) // Creating a directory in a layered context creates
// a LayeredDirectoryNode that gets its indirection from
// its parent.
{
newDir = new LayeredDirectoryNode((String)null, this);
((LayeredDirectoryNode)newDir).setPrimaryIndirection(false);
}
else
{
newDir = new PlainDirectoryNode(this);
}
newDir.setVersion(getLatestVersion() + 1);
this.setNew(newDir);
dir.addChild(name, newDir, lPath);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#createLayeredDirectory(java.lang.String, java.lang.String, java.lang.String)
*/
public void createLayeredDirectory(String srcPath, String dstPath,
String name)
{
Lookup lPath = lookupDirectory(-1, dstPath);
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null)
{
throw new AlfrescoRuntimeException("Child exists: " + name);
}
LayeredDirectoryNode newDir =
new LayeredDirectoryNode(srcPath, this);
if (lPath.isLayered())
{
// When a layered directory is made inside of a layered context,
// it gets its layer id from the topmost layer in its lookup
// path.
LayeredDirectoryNode top = lPath.getTopLayer();
newDir.setLayerID(top.getLayerID());
}
else
{
// Otherwise we issue a brand new layer id.
newDir.setLayerID(fSuper.issueLayerID());
}
dir.addChild(name, newDir, lPath);
newDir.setVersion(getLatestVersion() + 1);
setNew(newDir);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#createFile(java.lang.String, java.lang.String)
*/
public void createFile(String path, String name)
{
Lookup lPath = lookupDirectory(-1, path);
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null)
{
throw new AlfrescoRuntimeException("Child exists: " + name);
}
PlainFileNode file = new PlainFileNode(this);
file.setVersion(getLatestVersion() + 1);
setNew(file);
dir.addChild(name, file, lPath);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#createLayeredFile(java.lang.String, java.lang.String, java.lang.String)
*/
public void createLayeredFile(String srcPath, String dstPath, String name)
{
Lookup lPath = lookupDirectory(-1, dstPath);
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) != null)
{
throw new AlfrescoRuntimeException("Child exists: " + name);
}
// TODO Reexamine decision to not check validity of srcPath.
LayeredFileNode newFile =
new LayeredFileNode(srcPath, this);
dir.addChild(name, newFile, lPath);
newFile.setVersion(getLatestVersion() + 1);
setNew(newFile);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#getInputStream(int, java.lang.String)
*/
public InputStream getInputStream(int version, String path)
{
Lookup lPath = lookup(version, path);
AVMNode node = lPath.getCurrentNode();
if (!(node instanceof FileNode))
{
throw new AlfrescoRuntimeException("Not a file: " + path + " r " + version);
}
FileNode file = (FileNode)node;
FileContent content = file.getContentForRead(version, this);
return content.getInputStream(fSuper);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#getListing(int, java.lang.String)
*/
public List<FolderEntry> getListing(int version, String path)
{
Lookup lPath = lookupDirectory(version, path);
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
Map<String, DirectoryEntry> listing = dir.getListing(lPath, version);
ArrayList<FolderEntry> results = new ArrayList<FolderEntry>();
for (String name : listing.keySet())
{
FolderEntry item = new FolderEntry();
item.setName(name);
item.setType(listing.get(name).getType());
results.add(item);
}
return results;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#getOutputStream(java.lang.String)
*/
public OutputStream getOutputStream(String path)
{
Lookup lPath = lookup(-1, path);
AVMNode node = lPath.getCurrentNode();
if (!(node instanceof FileNode))
{
throw new AlfrescoRuntimeException("Not a file: " + path);
}
FileNode file = (FileNode)node;
file = (FileNode)file.copyOnWrite(lPath);
FileContent content = file.getContentForWrite(this);
return content.getOutputStream(fSuper); // TODO Do we really need fSuper?
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#removeNode(java.lang.String, java.lang.String)
*/
public void removeNode(String path, String name)
{
Lookup lPath = lookupDirectory(-1, path);
DirectoryNode dir = (DirectoryNode)lPath.getCurrentNode();
if (dir.lookupChild(lPath, name, -1) == null)
{
throw new AlfrescoRuntimeException("Does not exist: " + name);
}
dir.removeChild(name, lPath);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#slide(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void slide(String srcPath, String srcName, String dstPath,
String dstName)
{
Lookup sPath = lookup(-1, srcPath);
if (!sPath.isLayered())
{
throw new AlfrescoRuntimeException("Slide not allowed from non-layered directory.");
}
AVMNode node = sPath.getCurrentNode();
if (!(node instanceof LayeredDirectoryNode))
{
throw new AlfrescoRuntimeException("Not a layered directory: " + srcPath);
}
LayeredDirectoryNode srcDir = (LayeredDirectoryNode)node;
AVMNode srcNode = srcDir.lookupChild(sPath, srcName, -1);
if (srcNode == null)
{
throw new AlfrescoRuntimeException("Not found: " + srcName);
}
if (!(srcNode instanceof LayeredDirectoryNode))
{
throw new AlfrescoRuntimeException("Not a layered directory:" + srcName);
}
if (!sPath.isInThisLayer() || !srcDir.directlyContains(srcNode))
{
throw new AlfrescoRuntimeException("Not in this layer: " + srcName);
}
Lookup dPath = lookupDirectory(-1, dstPath);
if (!dPath.isLayered() || !dPath.getTopLayer().equals(sPath.getTopLayer()))
{
throw new AlfrescoRuntimeException("Destination must be in same layer: " + dstPath);
}
DirectoryNode dstDir = (DirectoryNode)dPath.getCurrentNode();
if (dstDir.lookupChild(dPath, dstName, -1) != null)
{
throw new AlfrescoRuntimeException("Destination exists: " + dstName);
}
// Remove child from src without leaving a ghost.
LayeredDirectoryNode srcDirCopy =
(LayeredDirectoryNode)srcDir.copyOnWrite(sPath);
srcDirCopy.rawRemoveChildNoGhost(srcName);
// Make a new version of source directly to be slid.
LayeredDirectoryNode dstNode =
new LayeredDirectoryNode((LayeredDirectoryNode)srcNode, this);
// Relookup the destination, since the lookup have been invalidated
// by the src copy on write.
dPath = lookup(-1, dstPath);
dstDir = (DirectoryNode)dPath.getCurrentNode();
dstDir.addChild(dstName, dstNode, dPath);
}
// TODO This is problematic. As time goes on this returns
// larger and larger data sets. Perhaps what we should do is
// provide methods for getting versions by date range, n most
// recent etc.
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#getVersions()
*/
public Set<Integer> getVersions()
{
return fData.getRoots().keySet();
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#getDataBean()
*/
public RepositoryBean getDataBean()
{
return fData;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#getSuperRepository()
*/
public SuperRepository getSuperRepository()
{
return fSuper;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#lookup(int, java.lang.String)
*/
public Lookup lookup(int version, String path)
{
// Make up a Lookup to hold the results.
Lookup result = new Lookup(this, fData.getName()); // TODO redundant.
if (path.length() == 0)
{
throw new AlfrescoRuntimeException("Invalid path: " + path);
}
if (path.length() > 1)
{
path = path.substring(1);
}
String[] pathElements = path.split("/");
// Grab the root node to start the lookup.
DirectoryNode dir = null;
// Versions less than 0 mean get current.
if (version < 0)
{
dir = (DirectoryNode)AVMNodeFactory.CreateFromBean(fData.getRoot());
}
else
{
AVMNodeBean bean = fData.getRoots().get(version);
if (bean == null)
{
throw new AlfrescoRuntimeException("Invalid version: " + version);
}
dir = (DirectoryNode)AVMNodeFactory.CreateFromBean(bean);
}
// Add an entry for the root.
result.add(dir, "");
if (pathElements.length == 0)
{
return result;
}
// Now look up each path element in sequence up to one
// before the end.
for (int i = 0; i < pathElements.length - 1; i++)
{
AVMNode child = dir.lookupChild(result, pathElements[i], version);
if (child == null)
{
throw new AlfrescoRuntimeException("Not found: " + pathElements[i]);
}
// Every element that is not the last needs to be a directory.
if (!(child instanceof DirectoryNode))
{
throw new AlfrescoRuntimeException("Not a directory: " + pathElements[i]);
}
dir = (DirectoryNode)child;
result.add(dir, pathElements[i]);
}
// Now look up the last element.
AVMNode child = dir.lookupChild(result, pathElements[pathElements.length - 1], version);
if (child == null)
{
throw new AlfrescoRuntimeException("Not found: " + pathElements[pathElements.length - 1]);
}
result.add(child, pathElements[pathElements.length - 1]);
return result;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#lookupDirectory(int, java.lang.String)
*/
public Lookup lookupDirectory(int version, String path)
{
// Just do a regular lookup and assert that the last element
// is a directory.
Lookup lPath = lookup(version, path);
if (!(lPath.getCurrentNode() instanceof DirectoryNode))
{
throw new AlfrescoRuntimeException("Not a directory: " + path);
}
return lPath;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.Repository#getIndirectionPath(int, java.lang.String)
*/
public String getIndirectionPath(int version, String path)
{
Lookup lPath = lookup(version, path);
AVMNode node = lPath.getCurrentNode();
if (node instanceof Layered)
{
return ((Layered)node).getUnderlying(lPath);
}
throw new AlfrescoRuntimeException("Not a layered node: " + path);
}
}

View File

@@ -1,571 +0,0 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm.impl;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.avm.AVMNode;
import org.alfresco.repo.avm.DirectoryNode;
import org.alfresco.repo.avm.FileNode;
import org.alfresco.repo.avm.FolderEntry;
import org.alfresco.repo.avm.LayeredDirectoryNode;
import org.alfresco.repo.avm.LayeredFileNode;
import org.alfresco.repo.avm.Lookup;
import org.alfresco.repo.avm.PlainDirectoryNode;
import org.alfresco.repo.avm.PlainFileNode;
import org.alfresco.repo.avm.Repository;
import org.alfresco.repo.avm.SuperRepository;
import org.alfresco.repo.avm.hibernate.Issuer;
import org.alfresco.repo.avm.hibernate.RepositoryBean;
import org.alfresco.repo.avm.hibernate.RepositoryBeanImpl;
import org.hibernate.Query;
import org.hibernate.Session;
/**
* Implementation of a SuperRepository. This is a per thread
* class.
* @author britt
*/
public class SuperRepositoryImpl implements SuperRepository
{
/**
* The Hibernate Session associated with the current operation.
*/
private Session fSession;
/**
* The node id issuer.
*/
private Issuer fNodeIssuer;
/**
* The content id issuer;
*/
private Issuer fContentIssuer;
/**
* The branch id issuer.
*/
private Issuer fBranchIssuer;
/**
* The layer id issuer.
*/
private Issuer fLayerIssuer;
/**
* The file storage directory.
*/
private String fStorage;
// TODO Issuers are handled in a repugnant manner here. Something better
// would be nice.
/**
* Make a new one, initialized with the session.
* @param session The session for this operation.
* @param storage Where file data gets stored.
*/
public SuperRepositoryImpl(Session session, String storage)
{
fSession = session;
fStorage = storage;
fNodeIssuer = null;
fContentIssuer = null;
fBranchIssuer = null;
fLayerIssuer = null;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#createFile(java.lang.String, java.lang.String)
*/
public void createFile(String path, String name)
{
String[] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
rep.createFile(pathParts[1], name);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#createDirectory(java.lang.String, java.lang.String)
*/
public void createDirectory(String path, String name)
{
String[] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
rep.createDirectory(pathParts[1], name);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#createLayeredDirectory(java.lang.String, java.lang.String, java.lang.String)
*/
public void createLayeredDirectory(String srcPath, String dstPath,
String name)
{
String[] pathParts = SplitPath(dstPath);
Repository rep = getRepositoryByName(pathParts[0]);
rep.createLayeredDirectory(srcPath, pathParts[1], name);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#createLayerdFile(java.lang.String, java.lang.String, java.lang.String)
*/
public void createLayeredFile(String srcPath, String dstPath, String name)
{
String[] pathParts = SplitPath(dstPath);
Repository rep = getRepositoryByName(pathParts[0]);
rep.createLayeredFile(srcPath, pathParts[1], name);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#createRepository(java.lang.String)
*/
public void createRepository(String name)
{
// Newing up the object causes it to be written to the db.
@SuppressWarnings("unused")
Repository rep = new RepositoryImpl(this, name);
// Special handling for repository creation.
rep.getDataBean().getRoot().setIsNew(false);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#createBranch(int, java.lang.String, java.lang.String, java.lang.String)
*/
public void createBranch(int version, String srcPath, String dstPath, String name)
{
// Lookup the src node.
String [] pathParts = SplitPath(srcPath);
Repository srcRepo = getRepositoryByName(pathParts[0]);
Lookup sPath = srcRepo.lookup(version, pathParts[1]);
// Lookup the destination directory.
pathParts = SplitPath(dstPath);
Repository dstRepo = getRepositoryByName(pathParts[0]);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1]);
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 instanceof PlainDirectoryNode)
{
dstNode = new PlainDirectoryNode((PlainDirectoryNode)srcNode, dstRepo);
}
else if (srcNode instanceof LayeredDirectoryNode)
{
dstNode =
new LayeredDirectoryNode((LayeredDirectoryNode)srcNode, dstRepo);
}
else if (srcNode instanceof LayeredFileNode)
{
dstNode = new LayeredFileNode((LayeredFileNode)srcNode, dstRepo);
}
else // This is a plain file.
{
dstNode = new PlainFileNode((PlainFileNode)srcNode, dstRepo);
}
dstNode.setVersion(dstRepo.getLatestVersion() + 1);
dstRepo.setNew(dstNode);
dstNode.setAncestor(srcNode);
dstNode.setBranchID(issueBranchID());
dirNode.addChild(name, dstNode, dPath);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getOutputStream(java.lang.String)
*/
public OutputStream getOutputStream(String path)
{
String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
return rep.getOutputStream(pathParts[1]);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#rename(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void rename(String srcPath, String srcName, String dstPath,
String dstName)
{
// This is about as ugly as it gets.
String [] pathParts = SplitPath(srcPath);
Repository srcRepo = getRepositoryByName(pathParts[0]);
Lookup sPath = srcRepo.lookupDirectory(-1, pathParts[1]);
DirectoryNode srcDir = (DirectoryNode)sPath.getCurrentNode();
AVMNode srcNode = srcDir.lookupChild(sPath, srcName, -1);
if (srcNode == null)
{
throw new AlfrescoRuntimeException("Not found: " + srcName);
}
pathParts = SplitPath(dstPath);
Repository dstRepo = getRepositoryByName(pathParts[0]);
Lookup dPath = dstRepo.lookupDirectory(-1, pathParts[1]);
DirectoryNode dstDir = (DirectoryNode)dPath.getCurrentNode();
AVMNode dstNode = dstDir.lookupChild(dPath, dstName, -1);
if (dstNode != null)
{
throw new AlfrescoRuntimeException("Node exists: " + dstName);
}
// We've passed the check, so we can go ahead and do the rename.
if (srcNode instanceof PlainDirectoryNode)
{
// 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() == sPath.getTopLayer())
{
dstNode = new LayeredDirectoryNode((PlainDirectoryNode)srcNode, dstRepo, sPath);
}
else
{
dstNode = new LayeredDirectoryNode((DirectoryNode)srcNode, dstRepo, sPath, srcName);
}
}
else
{
dstNode = new PlainDirectoryNode((PlainDirectoryNode)srcNode, dstRepo);
}
}
else if (srcNode instanceof LayeredDirectoryNode)
{
// TODO I think I need to subdivide this logic again.
// based on whether the destination is a layer or not.
if (!sPath.isLayered() || (sPath.isInThisLayer() &&
srcDir instanceof LayeredDirectoryNode &&
((LayeredDirectoryNode)srcDir).directlyContains(srcNode)))
{
// Use the simple 'copy' constructor.
dstNode =
new LayeredDirectoryNode((LayeredDirectoryNode)srcNode, dstRepo);
((LayeredDirectoryNode)dstNode).setLayerID(((LayeredDirectoryNode)srcNode).getLayerID());
}
else
{
// The thing we are renaming does not belong to sPath's layer and therefore
// we need to compute the indirection path for this layer after the rename.
dstNode =
new LayeredDirectoryNode((DirectoryNode)srcNode, dstRepo, sPath, srcName);
((LayeredDirectoryNode)dstNode).setLayerID(issueLayerID());
}
}
else if (srcNode instanceof LayeredFileNode)
{
if (!sPath.isLayered() || (sPath.isInThisLayer() &&
srcDir instanceof LayeredDirectoryNode &&
((LayeredDirectoryNode)srcDir).directlyContains(srcNode)))
{
// Use the simple 'copy' constructor.
dstNode =
new LayeredFileNode((LayeredFileNode)srcNode, dstRepo);
}
else
{
// Calculate the indirection path, because srcNode was not in this layer.
dstNode =
new LayeredFileNode((FileNode)srcNode, dstRepo, sPath, srcName);
}
}
else // This is a plain file node.
{
dstNode = new PlainFileNode((PlainFileNode)srcNode, dstRepo);
}
dstNode.setVersion(dstRepo.getLatestVersion() + 1);
dstRepo.setNew(dstNode);
dstDir.addChild(dstName, dstNode, dPath);
dstNode.setAncestor(srcNode);
pathParts = SplitPath(srcPath);
sPath = srcRepo.lookup(-1, pathParts[1]);
srcDir = (DirectoryNode)sPath.getCurrentNode();
srcDir.removeChild(srcName, sPath);
}
// TODO Should we allow cross-repository sliding. Tentatively no, because
// it serves no earthly purpose. God knows we need to trim the combinatorial
// tree of possibilities.
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#slide(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void slide(String srcPath, String srcName, String dstPath,
String dstName)
{
String[] srcPathParts = SplitPath(srcPath);
String[] dstPathParts = SplitPath(dstPath);
if (!srcPathParts[0].equals(dstPathParts[0]))
{
throw new AlfrescoRuntimeException("Argument must be in same Repository: " + srcPathParts[0] + "!=" +
dstPathParts[0]);
}
Repository repo = getRepositoryByName(srcPathParts[0]);
repo.slide(srcPathParts[1], srcName, dstPathParts[1], dstName);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#createSnapshot(java.util.List)
*/
public void createSnapshot(List<String> repositories)
{
for (String repName : repositories)
{
Repository repo = getRepositoryByName(repName);
repo.createSnapshot();
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#createSnapshot(java.lang.String)
*/
public void createSnapshot(String repository)
{
Repository repo = getRepositoryByName(repository);
repo.createSnapshot();
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#remove(java.lang.String, java.lang.String)
*/
public void remove(String path, String name)
{
String [] pathParts = SplitPath(path);
Repository repo = getRepositoryByName(pathParts[0]);
repo.removeNode(pathParts[1], name);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#purgeRepository(java.lang.String)
*/
public void purgeRepository(String name)
{
// TODO Leave until later. Need to set up GC thread to handle this.
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#purgeVersion(java.lang.String, int)
*/
public void purgeVersion(String name, int version)
{
// TODO Leave until later. Need to set up GC thread to handle this.
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getInputStream(java.lang.String)
*/
public InputStream getInputStream(int version, String path)
{
String [] pathParts = SplitPath(path);
Repository repo = getRepositoryByName(pathParts[0]);
return repo.getInputStream(version, pathParts[1]);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getListing(java.lang.String)
*/
public List<FolderEntry> getListing(int version, String path)
{
String [] pathParts = SplitPath(path);
Repository repo = getRepositoryByName(pathParts[0]);
return repo.getListing(version, pathParts[1]);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getRepositoryNames()
*/
@SuppressWarnings("unchecked")
public List<String> getRepositoryNames()
{
Query query = fSession.createQuery("select r.name from RepositoryBeanImpl r");
return (List<String>)query.list();
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getRepositoryVersions(java.lang.String)
*/
public Set<Integer> getRepositoryVersions(String name)
{
Repository rep = getRepositoryByName(name);
return rep.getVersions();
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#issueID()
*/
public long issueID()
{
if (fNodeIssuer == null)
{
fNodeIssuer = (Issuer)fSession.get(Issuer.class, "node");
}
return fNodeIssuer.issue();
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#issueContentID()
*/
public long issueContentID()
{
if (fContentIssuer == null)
{
fContentIssuer = (Issuer)fSession.get(Issuer.class, "content");
}
return fContentIssuer.issue();
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#issueLayerID()
*/
public long issueLayerID()
{
if (fLayerIssuer == null)
{
fLayerIssuer = (Issuer)fSession.get(Issuer.class, "layer");
}
return fLayerIssuer.issue();
}
private long issueBranchID()
{
if (fBranchIssuer == null)
{
fBranchIssuer = (Issuer)fSession.get(Issuer.class, "branch");
}
return fBranchIssuer.issue();
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getSession()
*/
public Session getSession()
{
return fSession;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#createContentOutputStream(int)
*/
public OutputStream createContentOutputStream(String path)
{
String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
return rep.getOutputStream(pathParts[1]);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getContentInputStream(int)
*/
public InputStream getContentInputStream(int version, String path)
{
String[] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
return rep.getInputStream(version, pathParts[1]);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#destroyRepository(java.lang.String)
*/
public void destroyRepository(String name)
{
// TODO Auto-generated method stub
// Leave this until we have GC in place.
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getIndirectionPath(int, java.lang.String)
*/
public String getIndirectionPath(int version, String path)
{
String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
return rep.getIndirectionPath(version, pathParts[1]);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getLatestVersionID(java.lang.String)
*/
public int getLatestVersionID(String name)
{
Repository rep = getRepositoryByName(name);
return rep.getLatestVersion();
}
/**
* Get a repository by name.
* @param name The name of the repository.
* @return The Repository.
*/
private Repository getRepositoryByName(String name)
{
/*
Query query = fSession.createQuery("from RepositoryBeanImpl r where r.name = :name");
query.setString("name", name);
return new RepositoryImpl(this, (RepositoryBean)query.uniqueResult());
*/
return new RepositoryImpl(this, (RepositoryBean)fSession.get(RepositoryBeanImpl.class, name));
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#lookup(int, java.lang.String)
*/
public Lookup lookup(int version, String path)
{
String [] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
return rep.lookup(version, pathParts[1]);
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#lookupDirectory(int, java.lang.String)
*/
public Lookup lookupDirectory(int version, String path)
{
String[] pathParts = SplitPath(path);
Repository rep = getRepositoryByName(pathParts[0]);
return rep.lookupDirectory(version, pathParts[1]);
}
/**
* 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 AlfrescoRuntimeException("Invalid path: " + path);
}
return pathParts;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.SuperRepository#getStorageRoot()
*/
public String getStorageRoot()
{
return fStorage;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2006 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific
* language governing permissions and limitations under the
* License.
*/
package org.alfresco.repo.avm.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.alfresco.repo.avm.AVMService;
import org.alfresco.repo.avm.AVMServiceImpl;
/**
* This takes a filesystem directory path and a repository path and name
* and bulk loads recursively from the filesystem.
* @author britt
*/
public class BulkLoad
{
private AVMService fService;
/**
* Bulk load from a filesystem directory.
* Syntax : BulkLoad storagepath (new|old) fspath reppath name
* @param args
*/
public static void main(String[] args)
{
if (args.length != 4)
{
System.err.println("Syntax: BulkLoad storagepath (new|old) fspath reppath");
System.exit(1);
}
AVMServiceImpl service = new AVMServiceImpl();
service.setStorage(args[0]);
service.init(args[1].equals("new"));
BulkLoad loader = new BulkLoad(service);
loader.recursiveLoad(args[2], args[3]);
service.createSnapshot("main");
}
/**
* Create a new one.
* @param service
*/
public BulkLoad(AVMService service)
{
fService = service;
}
/**
* Recursively load content.
* @param fsPath The path in the filesystem.
* @param repPath
*/
public void recursiveLoad(String fsPath, String repPath)
{
System.out.println(fsPath);
File file = new File(fsPath);
String name = file.getName();
if (file.isDirectory())
{
fService.createDirectory(repPath, name);
String[] children = file.list();
String baseName = repPath.endsWith("/") ? repPath + name : repPath + "/" + name;
for (String child : children)
{
recursiveLoad(fsPath + "/" + child, baseName);
}
}
else
{
try
{
InputStream in = new FileInputStream(file);
OutputStream out = fService.createFile(repPath, name);
byte[] buff = new byte[8192];
int read = 0;
while ((read = in.read(buff)) != -1)
{
out.write(buff, 0, read);
}
out.close();
in.close();
}
catch (IOException e)
{
e.printStackTrace(System.err);
System.exit(1);
}
}
}
}