This changes the AVM service to use ContentService for storage. This gives us,

in theory, AVM working underneath (more or less) NodeService and ContentService.


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3481 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Britt Park 2006-08-10 21:50:34 +00:00
parent 9a2a67605c
commit 06df2021c8
28 changed files with 744 additions and 865 deletions

View File

@ -109,12 +109,6 @@
</property> </property>
</bean> </bean>
<bean id="fileContentDAO" class="org.alfresco.repo.avm.FileContentDAOHibernate">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="childEntryDAO" class="org.alfresco.repo.avm.ChildEntryDAOHibernate"> <bean id="childEntryDAO" class="org.alfresco.repo.avm.ChildEntryDAOHibernate">
<property name="sessionFactory"> <property name="sessionFactory">
<ref bean="sessionFactory"/> <ref bean="sessionFactory"/>
@ -194,6 +188,9 @@
<property name="avmStorePropertyDAO"> <property name="avmStorePropertyDAO">
<ref bean="avmStorePropertyDAO"/> <ref bean="avmStorePropertyDAO"/>
</property> </property>
<property name="contentService">
<ref bean="ContentService"/>
</property>
</bean> </bean>
<!-- The HibernateTransactionManager --> <!-- The HibernateTransactionManager -->

View File

@ -16,16 +16,6 @@
</property> </property>
</bean> </bean>
<bean id="contentIssuer" class="org.alfresco.repo.avm.Issuer"
depends-on="retryingTransaction,avmContext" init-method="init">
<property name="name">
<value>content</value>
</property>
<property name="retryingTransaction">
<ref bean="retryingTransaction"/>
</property>
</bean>
<bean id="layerIssuer" class="org.alfresco.repo.avm.Issuer" <bean id="layerIssuer" class="org.alfresco.repo.avm.Issuer"
depends-on="retryingTransaction,avmContext" init-method="init"> depends-on="retryingTransaction,avmContext" init-method="init">
<property name="name"> <property name="name">
@ -64,12 +54,6 @@
</property> </property>
</bean> </bean>
<bean id="fileContentDAO" class="org.alfresco.repo.avm.FileContentDAOHibernate">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="childEntryDAO" class="org.alfresco.repo.avm.ChildEntryDAOHibernate"> <bean id="childEntryDAO" class="org.alfresco.repo.avm.ChildEntryDAOHibernate">
<property name="sessionFactory"> <property name="sessionFactory">
<ref bean="sessionFactory"/> <ref bean="sessionFactory"/>
@ -125,9 +109,6 @@
<property name="versionRootDAO"> <property name="versionRootDAO">
<ref bean="versionRootDAO"/> <ref bean="versionRootDAO"/>
</property> </property>
<property name="fileContentDAO">
<ref bean="fileContentDAO"/>
</property>
<property name="childEntryDAO"> <property name="childEntryDAO">
<ref bean="childEntryDAO"/> <ref bean="childEntryDAO"/>
</property> </property>
@ -217,9 +198,6 @@
<property name="nodeIssuer"> <property name="nodeIssuer">
<ref bean="nodeIssuer"/> <ref bean="nodeIssuer"/>
</property> </property>
<property name="contentIssuer">
<ref bean="contentIssuer"/>
</property>
<property name="layerIssuer"> <property name="layerIssuer">
<ref bean="layerIssuer"/> <ref bean="layerIssuer"/>
</property> </property>

View File

@ -66,6 +66,9 @@
<property name="policyComponent"> <property name="policyComponent">
<ref bean="policyComponent" /> <ref bean="policyComponent" />
</property> </property>
<property name="avmService">
<ref bean="AVMService"/>
</property>
</bean> </bean>
<bean id="mimetypeConfigService" class="org.alfresco.config.xml.XMLConfigService" init-method="init"> <bean id="mimetypeConfigService" class="org.alfresco.config.xml.XMLConfigService" init-method="init">

View File

@ -81,6 +81,10 @@
<tokenised>true</tokenised> <tokenised>true</tokenised>
</index> </index>
</property> </property>
<property name="cm:readonly">
<type>d:boolean</type>
<mandatory>false</mandatory>
</property>
</properties> </properties>
</type> </type>

View File

@ -15,6 +15,7 @@
<map> <map>
<entry key="workspace"><ref bean="dbNodeService"></ref></entry> <entry key="workspace"><ref bean="dbNodeService"></ref></entry>
<entry key="versionStore"><ref bean="versionNodeService"></ref></entry> <entry key="versionStore"><ref bean="versionNodeService"></ref></entry>
<entry key="avm"><ref bean="avmNodeService"/></entry>
</map> </map>
</property> </property>
</bean> </bean>
@ -58,6 +59,16 @@
</property> </property>
</bean> </bean>
<!-- AVMNodeService -->
<bean id="avmNodeService" class="org.alfresco.repo.avm.AVMNodeService">
<property name="dictionaryService">
<ref bean="dictionaryService"/>
</property>
<property name="avmService">
<ref bean="AVMService"/>
</property>
</bean>
<!-- Handles policy callbacks to ensure that node hierarchy gets indexed --> <!-- Handles policy callbacks to ensure that node hierarchy gets indexed -->
<bean id="nodeIndexer" class="org.alfresco.repo.node.index.NodeIndexer" init-method="init"> <bean id="nodeIndexer" class="org.alfresco.repo.node.index.NodeIndexer" init-method="init">
<property name="policyComponent"> <property name="policyComponent">

View File

@ -92,6 +92,7 @@ public interface ContentModel
// content type and aspect constants // content type and aspect constants
static final QName TYPE_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"); static final QName TYPE_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content");
static final QName PROP_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"); static final QName PROP_CONTENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content");
static final QName PROP_READONLY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "readonly");
// title aspect // title aspect
static final QName ASPECT_TITLED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "titled"); static final QName ASPECT_TITLED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "titled");

View File

@ -3,12 +3,18 @@
*/ */
package org.alfresco.repo.avm; package org.alfresco.repo.avm;
import org.alfresco.repo.content.ContentStore;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/** /**
* This is the (shudder) global context for AVM. It a rendezvous * This is the (shudder) global context for AVM. It a rendezvous
* point for access to needed global instances. * point for access to needed global instances.
* @author britt * @author britt
*/ */
class AVMContext class AVMContext implements ApplicationContextAware
{ {
/** /**
* The single instance of an AVMContext. * The single instance of an AVMContext.
@ -40,11 +46,6 @@ class AVMContext
*/ */
public VersionRootDAO fVersionRootDAO; public VersionRootDAO fVersionRootDAO;
/**
* The FileContentDAO.
*/
public FileContentDAO fFileContentDAO;
/** /**
* The ChildEntryDAO. * The ChildEntryDAO.
*/ */
@ -80,6 +81,36 @@ class AVMContext
*/ */
public AVMStorePropertyDAO fAVMStorePropertyDAO; public AVMStorePropertyDAO fAVMStorePropertyDAO;
/**
* The ContentService.
*/
private ContentService fContentService;
/**
* The Mimetype Service.
*/
private MimetypeService fMimetypeService;
/**
* The AVMService.
*/
private AVMService fAVMService;
/**
* The Content Store.
*/
private ContentStore fContentStore;
/**
* The application context.
*/
public ApplicationContext fAppContext;
public void setApplicationContext(ApplicationContext context)
{
fAppContext = context;
}
/** /**
* @param nodeDAO the fAVMNodeDAO to set * @param nodeDAO the fAVMNodeDAO to set
*/ */
@ -104,14 +135,6 @@ class AVMContext
fDeletedChildDAO = deletedChildDAO; fDeletedChildDAO = deletedChildDAO;
} }
/**
* @param fileContentDAO the fFileContentDAO to set
*/
public void setFileContentDAO(FileContentDAO fileContentDAO)
{
fFileContentDAO = fileContentDAO;
}
/** /**
* @param historyLinkDAO the fHistoryLinkDAO to set * @param historyLinkDAO the fHistoryLinkDAO to set
*/ */
@ -169,4 +192,56 @@ class AVMContext
{ {
fAVMStorePropertyDAO = avmStorePropertyDAO; fAVMStorePropertyDAO = avmStorePropertyDAO;
} }
/**
* Get the Content Service.
* @return The ContentService object.
*/
public ContentService getContentService()
{
if (fContentService == null)
{
fContentService = (ContentService)fAppContext.getBean("contentService");
}
return fContentService;
}
/**
* Get the mime type service.
* @return The mime type service.
*/
public MimetypeService getMimetypeService()
{
if (fMimetypeService == null)
{
fMimetypeService = (MimetypeService)fAppContext.getBean("mimetypeService");
}
return fMimetypeService;
}
/**
* Get the AVM Service.
* @return The AVMService instance.
*/
public AVMService getAVMService()
{
if (fAVMService == null)
{
fAVMService = (AVMService)fAppContext.getBean("AVMService");
}
return fAVMService;
}
/**
* Get the ContentStore.
* @return The content store.
*/
public ContentStore getContentStore()
{
if (fContentStore == null)
{
fContentStore = (ContentStore)fAppContext.getBean("fileContentStore");
}
return fContentStore;
}
} }

View File

@ -216,8 +216,7 @@ class AVMCrawler implements Runnable
} }
catch (Exception e) catch (Exception e)
{ {
if (e instanceof AVMNotFoundException || if (e instanceof AVMException)
e instanceof AVMExistsException)
{ {
return; return;
} }

View File

@ -375,6 +375,7 @@ public class AVMInterpreter
out.println("Mod Time: " + new Date(desc.getModDate())); out.println("Mod Time: " + new Date(desc.getModDate()));
} }
} }
/*
else if (command[0].equals("catver")) else if (command[0].equals("catver"))
{ {
if (command.length != 4) if (command.length != 4)
@ -398,6 +399,7 @@ public class AVMInterpreter
} }
reader.close(); reader.close();
} }
*/
else if (command[0].equals("ca")) else if (command[0].equals("ca"))
{ {
if (command.length != 5) if (command.length != 5)

View File

@ -25,7 +25,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
* the <code>StoreRef</code>, <code>NodeRef</code> world. * the <code>StoreRef</code>, <code>NodeRef</code> world.
* @author britt * @author britt
*/ */
class AVMNodeConverter public class AVMNodeConverter
{ {
/** /**
* Get a NodeRef corresponding to the given path and version. * Get a NodeRef corresponding to the given path and version.

View File

@ -27,8 +27,6 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.SortedMap; import java.util.SortedMap;
import javax.naming.OperationNotSupportedException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.service.Auditable; import org.alfresco.service.Auditable;
@ -39,6 +37,7 @@ import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.AssociationExistsException; import org.alfresco.service.cmr.repository.AssociationExistsException;
import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.CyclicChildRelationshipException; import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException; import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.InvalidNodeRefException;
@ -80,6 +79,15 @@ public class AVMNodeService implements NodeService
fAVMService = service; fAVMService = service;
} }
/**
* Set the DictionaryService. For Spring.
* @param dictionaryService The DictionaryService instance.
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
fDictionaryService = dictionaryService;
}
/** /**
* Default constructor. * Default constructor.
*/ */
@ -281,6 +289,10 @@ public class AVMNodeService implements NodeService
Map<QName, PropertyValue> props = new HashMap<QName, PropertyValue>(); Map<QName, PropertyValue> props = new HashMap<QName, PropertyValue>();
for (QName qname : properties.keySet()) for (QName qname : properties.keySet())
{ {
if (isBuiltInProperty(qname))
{
continue;
}
props.put(qname, new PropertyValue(null, properties.get(qname))); props.put(qname, new PropertyValue(null, properties.get(qname)));
} }
fAVMService.setNodeProperties(newAVMPath, props); fAVMService.setNodeProperties(newAVMPath, props);
@ -645,6 +657,19 @@ public class AVMNodeService implements NodeService
result.put(ContentModel.PROP_MODIFIED, new Date(desc.getModDate())); result.put(ContentModel.PROP_MODIFIED, new Date(desc.getModDate()));
result.put(ContentModel.PROP_MODIFIER, desc.getLastModifier()); result.put(ContentModel.PROP_MODIFIER, desc.getLastModifier());
result.put(ContentModel.PROP_OWNER, desc.getOwner()); result.put(ContentModel.PROP_OWNER, desc.getOwner());
if (desc.isFile())
{
try
{
ContentData contentData = fAVMService.getContentDataForRead((Integer)avmVersionPath[0],
(String)avmVersionPath[1]);
result.put(ContentModel.PROP_CONTENT, contentData);
}
catch (AVMException e)
{
// TODO For now ignore.
}
}
return result; return result;
} }
@ -690,6 +715,21 @@ public class AVMNodeService implements NodeService
QName qName, QName qName,
NodeRef nodeRef) NodeRef nodeRef)
{ {
if (qName.equals(ContentModel.PROP_CONTENT))
{
try
{
ContentData contentData =
fAVMService.getContentDataForRead((Integer)avmVersionPath[0],
(String)avmVersionPath[1]);
return contentData;
}
catch (AVMException e)
{
// TODO For now, ignore.
return null;
}
}
AVMNodeDescriptor desc = null; AVMNodeDescriptor desc = null;
try try
{ {
@ -778,7 +818,8 @@ public class AVMNodeService implements NodeService
qName.equals(ContentModel.PROP_CREATOR) || qName.equals(ContentModel.PROP_CREATOR) ||
qName.equals(ContentModel.PROP_MODIFIED) || qName.equals(ContentModel.PROP_MODIFIED) ||
qName.equals(ContentModel.PROP_MODIFIER) || qName.equals(ContentModel.PROP_MODIFIER) ||
qName.equals(ContentModel.PROP_OWNER); qName.equals(ContentModel.PROP_OWNER) ||
qName.equals(ContentModel.PROP_CONTENT);
} }
/** /**
@ -795,12 +836,15 @@ public class AVMNodeService implements NodeService
*/ */
public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException
{ {
Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
// TODO Just until we can set built in properties on AVM Nodes. // TODO Just until we can set built in properties on AVM Nodes.
if (isBuiltInProperty(qname)) if (isBuiltInProperty(qname))
{ {
if (qname.equals(ContentModel.PROP_CONTENT))
{
}
return; return;
} }
Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
if ((Integer)avmVersionPath[0] >= 0) if ((Integer)avmVersionPath[0] >= 0)
{ {
throw new InvalidNodeRefException("Read only store.", nodeRef); throw new InvalidNodeRefException("Read only store.", nodeRef);

View File

@ -20,7 +20,6 @@ package org.alfresco.repo.avm;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -28,6 +27,10 @@ import java.util.Map;
import java.util.SortedMap; import java.util.SortedMap;
import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
/** /**
@ -52,11 +55,6 @@ class AVMRepository
*/ */
private Issuer fNodeIssuer; private Issuer fNodeIssuer;
/**
* The content id issuer;
*/
private Issuer fContentIssuer;
/** /**
* The layer id issuer. * The layer id issuer.
*/ */
@ -94,15 +92,6 @@ class AVMRepository
fNodeIssuer = nodeIssuer; fNodeIssuer = nodeIssuer;
} }
/**
* Set the content issuer. For Spring.
* @param contentIssuer The issuer.
*/
public void setContentIssuer(Issuer contentIssuer)
{
fContentIssuer = contentIssuer;
}
/** /**
* Set the layer issuer. For Spring. * Set the layer issuer. For Spring.
* @param layerIssuer The issuer. * @param layerIssuer The issuer.
@ -281,21 +270,6 @@ class AVMRepository
return rep.getOutputStream(pathParts[1]); 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);
AVMStore rep = getAVMStoreByName(pathParts[0]);
return rep.getRandomAccess(version, pathParts[1], access);
}
/** /**
* Rename a node. * Rename a node.
* @param srcPath Source containing directory. * @param srcPath Source containing directory.
@ -524,25 +498,30 @@ class AVMRepository
} }
/** /**
* Get an InputStream from a given version of a file. * Get a ContentReader from a file.
* @param desc The node descriptor. * @param version The version to look under.
* @return The InputStream. * @param path The path to the file.
* @return A ContentReader
*/ */
public InputStream getInputStream(AVMNodeDescriptor desc) public ContentReader getReader(int version, String path)
{ {
fLookupCount.set(1); fLookupCount.set(1);
AVMNode node = AVMContext.fgInstance.fAVMNodeDAO.getByID(desc.getId()); String [] pathParts = SplitPath(path);
if (node == null) AVMStore store = getAVMStoreByName(pathParts[0]);
{ return store.getReader(version, pathParts[1]);
throw new AVMNotFoundException("Not found.");
} }
if (node.getType() != AVMNodeType.PLAIN_FILE &&
node.getType() != AVMNodeType.LAYERED_FILE) /**
* Get a ContentWriter to a file.
* @param path The path to the file.
* @return A ContentWriter.
*/
public ContentWriter getWriter(String path)
{ {
throw new AVMWrongTypeException("Not a file."); fLookupCount.set(1);
} String [] pathParts = SplitPath(path);
FileNode file = (FileNode)node; AVMStore store = getAVMStoreByName(pathParts[0]);
return file.getContentForRead().getInputStream(); return store.getWriter(pathParts[1]);
} }
/** /**
@ -666,15 +645,6 @@ class AVMRepository
return fNodeIssuer.issue(); return fNodeIssuer.issue();
} }
/**
* Issue a content id.
* @return The new id.
*/
public long issueContentID()
{
return fContentIssuer.issue();
}
/** /**
* Issue a new layer id. * Issue a new layer id.
* @return The new id. * @return The new id.
@ -1112,6 +1082,46 @@ class AVMRepository
return null; return null;
} }
/**
* Get the ContentData for a file.
* @param version The version to look under.
* @param path The path to the file.
* @return The ContentData for the file.
*/
public ContentData getContentDataForRead(int version, String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
return store.getContentDataForRead(version, pathParts[1]);
}
/**
* Get the ContentData for a file for writing.
* @param path The path to the file.
* @return The ContentData object.
*/
public ContentData getContentDataForWrite(String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
return store.getContentDataForWrite(pathParts[1]);
}
/**
* Set the ContentData on a file.
* @param path The path to the file.
* @param data The content data to set.
*/
public void setContentData(String path, ContentData data)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
store.setContentData(pathParts[1], data);
}
/** /**
* Get the single instance of AVMRepository. * Get the single instance of AVMRepository.
* @return The single instance. * @return The single instance.

View File

@ -19,13 +19,15 @@ package org.alfresco.repo.avm;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SortedMap; import java.util.SortedMap;
import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
/** /**
@ -49,15 +51,6 @@ public interface AVMService
*/ */
public InputStream getFileInputStream(int version, String path); public InputStream getFileInputStream(int version, String path);
/**
* Get an input stream from a particular version of a file.
* @param desc The node descriptor pointing at the node.
* @return The InputStream.
* @throws AVMNotFoundException If <code>desc</code> is dangling or
* otherwise invalid.
*/
public InputStream getFileInputStream(AVMNodeDescriptor desc);
/** /**
* Get an output stream to a file node. The file must already exist. * Get an output stream to a file node. The file must already exist.
* @param path The simple absolute path to the file node. * @param path The simple absolute path to the file node.
@ -68,18 +61,23 @@ public interface AVMService
public OutputStream getFileOutputStream(String path); public OutputStream getFileOutputStream(String path);
/** /**
* Get a random access file to the given path. * Get a ContentReader for the given file.
* @param version The version to find. * @param version The version to look under.
* @param path The path to the file. * @param path The path to the file.
* @param access The access mode for RandomAccessFile. * @return A ContentReader.
* @return A RandomAccessFile * @throws AVMNotFoundException If <code>path</code> does not exist.
* @throws AVMNotFoundException If <code>path</code> is not found. * @throws AVMWrongTypeException if <code>path</code> is not a file.
* @throws AVMWrongTypeException If <code>path</code> contains a non-terminal
* component that is not a directory, or if <code>path</code> is not pointing
* at a file.
* @throws AVMException If trying to write to anything but the head version.
*/ */
public RandomAccessFile getRandomAccess(int version, String path, String access); public ContentReader getReader(int version, String path);
/**
* Get a ContentWriter to a file.
* @param path The path to the file.
* @return A ContentWriter.
* @throws AVMNotFoundException If <code>path</code> does not exist.
* @throws AVMWrongTypeException if <code>path</code> is not a file.
*/
public ContentWriter getWriter(String path);
/** /**
* Get a listing of a Folder by name. * Get a listing of a Folder by name.
@ -545,4 +543,35 @@ public interface AVMService
* does not exist. * does not exist.
*/ */
public void deleteStoreProperty(String store, QName name); public void deleteStoreProperty(String store, QName name);
/**
* Get the ContentData for a node. Only applies to a file.
* @param version The version to look under.
* @param path The path to the node.
* @return The ContentData object.
* @throws AVMNotFoundException If <code>path</code> does not exist.
* @throws AVMWrongTypeException If <code>path</code> does not
* point to a file.
*/
public ContentData getContentDataForRead(int version, String path);
/**
* Get the ContentData for a node.
* @param path The path to the node.
* @return The ContentData object.
* @throws AVMNotFoundException If <code>path</code> does not exist.
* @throws AVMWrongTypeException If <code>path</code> does not point
* to a file.
*/
public ContentData getContentDataForWrite(String path);
/**
* Set the content data on a file.
* @param path The path to the file.
* @param data The ContentData to set.
* @throws AVMNotFoundException If <code>path</code> does not exist.
* @throws AVMWrongTypeException If <code>path</code> does not point
* to a file.
*/
public void setContentData(String path, ContentData data);
} }

View File

@ -30,6 +30,9 @@ import java.util.SortedMap;
import org.alfresco.repo.avm.AVMRepository; import org.alfresco.repo.avm.AVMRepository;
import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -152,30 +155,57 @@ class AVMServiceImpl implements AVMService
} }
/** /**
* Get an InputStream from a particular version of a file. * Get a ContentReader for the given file.
* @param desc The node descriptor. * @param version The version to look under.
* @return The InputStream. * @param path The path to the file.
* @return A ContentReader.
*/ */
public InputStream getFileInputStream(final AVMNodeDescriptor desc) public ContentReader getReader(final int version, final String path)
{ {
if (desc == null) if (path == null)
{ {
throw new AVMBadArgumentException("Illegal null argument."); throw new AVMBadArgumentException("Null path.");
} }
class TxnCallback implements RetryingTransactionCallback class TxnCallback implements RetryingTransactionCallback
{ {
public InputStream in = null; public ContentReader reader;
public void perform() public void perform()
{ {
in = fAVMRepository.getInputStream(desc); reader = fAVMRepository.getReader(version, path);
} }
} }
TxnCallback doit = new TxnCallback(); TxnCallback doit = new TxnCallback();
fTransaction.perform(doit, false); fTransaction.perform(doit, false);
return doit.in; return doit.reader;
} }
/**
* Get a ContentWriter to a file.
* @param path The path to the file.
* @return A ContentWriter.
* @throws AVMNotFoundException If <code>path</code> does not exist.
* @throws AVMWrongTypeException if <code>path</code> is not a file.
*/
public ContentWriter getWriter(final String path)
{
if (path == null)
{
throw new AVMBadArgumentException("Null path.");
}
class TxnCallback implements RetryingTransactionCallback
{
public ContentWriter writer;
public void perform()
{
writer = fAVMRepository.getWriter(path);
}
}
TxnCallback doit = new TxnCallback();
fTransaction.perform(doit, true);
return doit.writer;
}
/** /**
* Get an output stream to a file. Triggers versioning. * Get an output stream to a file. Triggers versioning.
*/ */
@ -199,34 +229,6 @@ class AVMServiceImpl implements AVMService
return doit.out; 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 TxnCallback implements RetryingTransactionCallback
{
public RandomAccessFile file;
public void perform()
{
file = fAVMRepository.getRandomAccess(version, path, access);
}
}
TxnCallback doit = new TxnCallback();
fTransaction.perform(doit, true);
return doit.file;
}
/** /**
* Get a directory listing. * Get a directory listing.
* @param version The version id to lookup. * @param version The version id to lookup.
@ -1322,4 +1324,80 @@ class AVMServiceImpl implements AVMService
TxnCallback doit = new TxnCallback(); TxnCallback doit = new TxnCallback();
fTransaction.perform(doit, true); fTransaction.perform(doit, true);
} }
/**
* Get the ContentData for a node. Only applies to a file.
* @param version The version to look under.
* @param path The path to the node.
* @return The ContentData object.
*/
public ContentData getContentDataForRead(final int version, final String path)
{
if (path == null)
{
throw new AVMBadArgumentException("Null Path.");
}
class TxnCallback implements RetryingTransactionCallback
{
public ContentData content;
public void perform()
{
content = fAVMRepository.getContentDataForRead(version, path);
}
}
TxnCallback doit = new TxnCallback();
fTransaction.perform(doit, false);
return doit.content;
}
/**
* Get the Content data for writing.
* @param path The path to the node.
* @return The ContentData object.
*/
public ContentData getContentDataForWrite(final String path)
{
if (path == null)
{
throw new AVMBadArgumentException("Null Path.");
}
class TxnCallback implements RetryingTransactionCallback
{
public ContentData content;
public void perform()
{
content = fAVMRepository.getContentDataForWrite(path);
}
}
TxnCallback doit = new TxnCallback();
fTransaction.perform(doit, true);
return doit.content;
}
/**
* Set the content data on a file.
* @param path The path to the file.
* @param data The ContentData to set.
* @throws AVMNotFoundException If <code>path</code> does not exist.
* @throws AVMWrongTypeException If <code>path</code> does not point
* to a file.
*/
public void setContentData(final String path, final ContentData data)
{
if (path == null || data == null)
{
throw new AVMBadArgumentException("Null Path.");
}
class TxnCallback implements RetryingTransactionCallback
{
public void perform()
{
fAVMRepository.setContentData(path, data);
}
}
TxnCallback doit = new TxnCallback();
fTransaction.perform(doit, true);
}
} }

View File

@ -1890,6 +1890,7 @@ public class AVMServiceTest extends AVMServiceTestBase
/** /**
* The random access. * The random access.
*/ */
/*
public void testRandomAccess() public void testRandomAccess()
{ {
try try
@ -1914,6 +1915,7 @@ public class AVMServiceTest extends AVMServiceTestBase
fail(); fail();
} }
} }
*/
/** /**
* Test COW during long operations. * Test COW during long operations.

View File

@ -19,13 +19,15 @@ package org.alfresco.repo.avm;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.SortedMap; import java.util.SortedMap;
import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
/** /**
@ -103,6 +105,21 @@ interface AVMStore
*/ */
public InputStream getInputStream(int version, String path); public InputStream getInputStream(int version, String path);
/**
* Get a ContentReader from a file.
* @param version The version to look under.
* @param path The path to the file.
* @return A ContentReader.
*/
public ContentReader getReader(int version, String path);
/**
* Get a ContentWriter to a file.
* @param path The path to the file.
* @return A ContentWriter.
*/
public ContentWriter getWriter(String path);
/** /**
* Get a listing of the designated directory. * Get a listing of the designated directory.
* @param version The version to look under. * @param version The version to look under.
@ -134,15 +151,6 @@ interface AVMStore
*/ */
public OutputStream getOutputStream(String path); public OutputStream getOutputStream(String path);
/**
* 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. * Remove a node and all of its contents.
* @param path The path to the node's parent directory. * @param path The path to the node's parent directory.
@ -336,4 +344,26 @@ interface AVMStore
* @param name The name of the property to delete. * @param name The name of the property to delete.
*/ */
public void deleteProperty(QName name); public void deleteProperty(QName name);
/**
* Get the ContentData on a file.
* @param version The version to look under.
* @param path The path to the file.
* @return The ContentData corresponding to the file.
*/
public ContentData getContentDataForRead(int version, String path);
/**
* Get the ContentData for writing.
* @param path The path to the file.
* @return The ContentData object.
*/
public ContentData getContentDataForWrite(String path);
/**
* Set the ContentData for a file.
* @param path The path to the file.
* @param data The ContentData to set.
*/
public void setContentData(String path, ContentData data);
} }

View File

@ -20,7 +20,6 @@ package org.alfresco.repo.avm;
import java.io.File; import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
@ -33,6 +32,10 @@ import java.util.TreeMap;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
/** /**
@ -231,7 +234,12 @@ class AVMStoreImpl implements AVMStore, Serializable
file.setVersionID(getNextVersionID()); file.setVersionID(getNextVersionID());
dir.putChild(name, file); dir.putChild(name, file);
dir.updateModTime(); dir.updateModTime();
return file.getContentForWrite().getOutputStream(); file.setContentData(new ContentData(null,
AVMContext.fgInstance.getMimetypeService().guessMimetype(name),
-1,
"UTF-8"));
ContentWriter writer = getWriter(AVMNodeConverter.ExtendAVMPath(path, name));
return writer.getContentOutputStream();
} }
/** /**
@ -248,10 +256,17 @@ class AVMStoreImpl implements AVMStore, Serializable
{ {
throw new AVMExistsException("Child exists: " + name); throw new AVMExistsException("Child exists: " + name);
} }
PlainFileNodeImpl file = new PlainFileNodeImpl(this, data); PlainFileNodeImpl file = new PlainFileNodeImpl(this);
file.setVersionID(getNextVersionID()); file.setVersionID(getNextVersionID());
dir.putChild(name, file); dir.putChild(name, file);
dir.updateModTime(); dir.updateModTime();
file.setContentData(new ContentData(null,
AVMContext.fgInstance.getMimetypeService().guessMimetype(name),
-1,
"UTF-8"));
ContentWriter writer = getWriter(AVMNodeConverter.ExtendAVMPath(path, name));
writer.putContent(data);
file.setContentData(writer.getContentData());
} }
/** /**
@ -284,6 +299,9 @@ class AVMStoreImpl implements AVMStore, Serializable
*/ */
public InputStream getInputStream(int version, String path) public InputStream getInputStream(int version, String path)
{ {
ContentReader reader = getReader(version, path);
return reader.getContentInputStream();
/*
Lookup lPath = lookup(version, path, false); Lookup lPath = lookup(version, path, false);
AVMNode node = lPath.getCurrentNode(); AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.PLAIN_FILE && if (node.getType() != AVMNodeType.PLAIN_FILE &&
@ -294,6 +312,33 @@ class AVMStoreImpl implements AVMStore, Serializable
FileNode file = (FileNode)node; FileNode file = (FileNode)node;
FileContent content = file.getContentForRead(); FileContent content = file.getContentForRead();
return content.getInputStream(); return content.getInputStream();
*/
}
/**
* Get a ContentReader from a file.
* @param version The version to look under.
* @param path The path to the file.
* @return A ContentReader.
*/
public ContentReader getReader(int version, String path)
{
NodeRef nodeRef = AVMNodeConverter.ToNodeRef(version, fName + ":" + path);
return AVMContext.fgInstance.getContentService().getReader(nodeRef, ContentModel.PROP_CONTENT);
}
/**
* Get a ContentWriter to a file.
* @param path The path to the file.
* @return A ContentWriter.
*/
public ContentWriter getWriter(String path)
{
NodeRef nodeRef = AVMNodeConverter.ToNodeRef(-1, fName + ":" + path);
ContentWriter writer =
AVMContext.fgInstance.getContentService().getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
setContentData(path, writer.getContentData());
return writer;
} }
/** /**
@ -368,6 +413,9 @@ class AVMStoreImpl implements AVMStore, Serializable
*/ */
public OutputStream getOutputStream(String path) public OutputStream getOutputStream(String path)
{ {
ContentWriter writer = getWriter(path);
return writer.getContentOutputStream();
/*
Lookup lPath = lookup(-1, path, true); Lookup lPath = lookup(-1, path, true);
AVMNode node = lPath.getCurrentNode(); AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.PLAIN_FILE && if (node.getType() != AVMNodeType.PLAIN_FILE &&
@ -379,41 +427,7 @@ class AVMStoreImpl implements AVMStore, Serializable
FileContent content = file.getContentForWrite(); FileContent content = file.getContentForWrite();
file.updateModTime(); file.updateModTime();
return content.getOutputStream(); return content.getOutputStream();
}
/**
* 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, write);
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)
{
content = file.getContentForWrite();
file.updateModTime();
}
else
{
content = file.getContentForRead();
}
return content.getRandomAccess(access);
} }
/** /**
@ -968,4 +982,53 @@ class AVMStoreImpl implements AVMStore, Serializable
{ {
AVMContext.fgInstance.fAVMStorePropertyDAO.delete(this, name); AVMContext.fgInstance.fAVMStorePropertyDAO.delete(this, name);
} }
/**
* Get the ContentData on a file.
* @param version The version to look under.
* @param path The path to the file.
* @return The ContentData corresponding to the file.
*/
public ContentData getContentDataForRead(int version, String path)
{
Lookup lPath = lookup(version, path, false);
AVMNode node = lPath.getCurrentNode();
if (!(node instanceof FileNode))
{
throw new AVMWrongTypeException("File Expected.");
}
return ((FileNode)node).getContentData(lPath);
}
/**
* Get the ContentData on a file for writing.
* @param path The path to the file.
* @return The ContentData corresponding to the file.
*/
public ContentData getContentDataForWrite(String path)
{
Lookup lPath = lookup(-1, path, true);
AVMNode node = lPath.getCurrentNode();
if (!(node instanceof FileNode))
{
throw new AVMWrongTypeException("File Expected.");
}
return ((FileNode)node).getContentData(lPath);
}
/**
* Set the ContentData for a file.
* @param path The path to the file.
* @param data The ContentData to set.
*/
public void setContentData(String path, ContentData data)
{
Lookup lPath = lookup(-1, path, true);
AVMNode node = lPath.getCurrentNode();
if (!(node instanceof FileNode))
{
throw new AVMWrongTypeException("File Expected.");
}
((FileNode)node).setContentData(data);
}
} }

View File

@ -1,76 +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;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
/**
* Interface for file content. FileContent can be shared between files.
* @author britt
*/
interface FileContent
{
/**
* Get the number of files that refer to this content.
* @return The reference count.
*/
public int getRefCount();
/**
* Set the reference count.
* @param count The count to set.
*/
public void setRefCount(int count);
/**
* Get an input stream from the content.
* @return An InputStream.
*/
public InputStream getInputStream();
/**
* Get an output stream to the content.
* @return an OutputStream.
*/
public OutputStream getOutputStream();
/**
* Get a random access file to this content.
* @param access The mode to open the file in.
* @return A RandomAccessFile.
*/
public RandomAccessFile getRandomAccess(String access);
/**
* Delete the contents of this from the backing store.
*/
public void delete();
/**
* Get the length of the file.
* @return The length of the file.
*/
public long getLength();
/**
* Get the object id.
* @return object id.
*/
public long getId();
}

View File

@ -1,37 +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;
/**
* DAO for FileContent objects.
* @author britt
*/
interface FileContentDAO
{
/**
* Save one.
* @param content To be saved.
*/
public void save(FileContent content);
/**
* Delete one.
* @param content To be deleted.
*/
public void delete(FileContent content);
}

View File

@ -1,54 +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;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
/**
* @author britt
*
*/
class FileContentDAOHibernate extends HibernateDaoSupport implements
FileContentDAO
{
/**
* Do nothing constructor.
*/
public FileContentDAOHibernate()
{
super();
}
/**
* Save one.
* @param content The one to save.
*/
public void save(FileContent content)
{
getSession().save(content);
}
/**
* Delete one.
* @param content To be deleted.
*/
public void delete(FileContent content)
{
getSession().delete(content);
}
}

View File

@ -1,357 +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;
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 id The id for this content.
*/
public FileContentImpl(long id)
{
fID = id;
fRefCount = 1;
// Initialize the contents.
try
{
OutputStream out = getOutputStream();
out.close();
}
catch (IOException ie)
{
throw new AVMException("File data error.", ie);
}
AVMContext.fgInstance.fFileContentDAO.save(this);
}
/**
* Initialize with the given content.
* @param id
* @param content
*/
public FileContentImpl(long id, File content)
{
fID = id;
fRefCount = 1;
// Initialize the contents.
try
{
OutputStream out = getOutputStream();
InputStream in = new FileInputStream(content);
byte [] buff = new byte[8192];
int count;
while ((count = in.read(buff)) != -1)
{
out.write(buff, 0, count);
}
out.close();
in.close();
}
catch (IOException ie)
{
throw new AVMException("I/O Error.", ie);
}
AVMContext.fgInstance.fFileContentDAO.save(this);
}
/**
* Copy constructor, sort of.
* @param other The content to copy from.
* @param id The id for this content.
*/
public FileContentImpl(FileContent other, long id)
{
fID = id;
fRefCount = 1;
// Copy the contents from other to this.
BufferedInputStream in = new BufferedInputStream(other.getInputStream());
BufferedOutputStream out = new BufferedOutputStream(getOutputStream());
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);
}
AVMContext.fgInstance.fFileContentDAO.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.
* @return An InputStream.
*/
public InputStream getInputStream()
{
try
{
return new FileInputStream(getContentPath());
}
catch (IOException ie)
{
throw new AVMException("Could not open for reading: " + getContentPath(), ie);
}
}
/**
* Gets an ouptut stream to this node.
* @return An OutputStream.
*/
public OutputStream getOutputStream()
{
try
{
File dir = new File(getDirectoryPath());
if (!dir.exists())
{
dir.mkdirs();
}
return new FileOutputStream(getContentPath());
}
catch (IOException ie)
{
throw new AVMException("Could not open for writing: " + getContentPath(), 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 access The access more for RandomAccessFile.
* @return A RandomAccessFile.
*/
public RandomAccessFile getRandomAccess(String access)
{
try
{
return new RandomAccessFile(getContentPath(), access);
}
catch (IOException ie)
{
throw new AVMException("Could not open for random access: " + getContentPath(), ie);
}
}
/**
* Delete the contents of this file from the backing store.
*/
public void delete()
{
File file = new File(getContentPath());
file.delete();
}
/**
* Get the length of this content.
* @return The length of the content.
*/
public long getLength()
{
File file = new File(getContentPath());
return file.length();
}
/**
* Retrieve the full path for this content.
* @return The full path for this content.
*/
private synchronized String getContentPath()
{
if (fName == null)
{
calcPathData();
}
return fName;
}
/**
* Get the directory path for this content.
* @return The directory path.
*/
private synchronized String getDirectoryPath()
{
if (fPath == null)
{
calcPathData();
}
return fPath;
}
/**
* Calculate the path data.
*/
private void calcPathData()
{
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 = AVMRepository.GetInstance().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 Equality.
*/
@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

@ -16,6 +16,8 @@
*/ */
package org.alfresco.repo.avm; package org.alfresco.repo.avm;
import org.alfresco.service.cmr.repository.ContentData;
/** /**
* Interface for the generic idea of a file. * Interface for the generic idea of a file.
* @author britt * @author britt
@ -23,15 +25,15 @@ package org.alfresco.repo.avm;
interface FileNode extends AVMNode interface FileNode extends AVMNode
{ {
/** /**
* Get the content object associated with this node, for reading. * Set the ContentData for this file.
* @return A FileContent object. * @param contentData The value to set.
*/ */
public FileContent getContentForRead(); public void setContentData(ContentData contentData);
/** /**
* Get the content object for writing. This will do COW * Get the ContentData for this file.
* as needed. * @param lPath The Lookup used to get here.
* @return A FileContent object. * @return The ContentData object for this file.
*/ */
public FileContent getContentForWrite(); public ContentData getContentData(Lookup lPath);
} }

View File

@ -17,6 +17,8 @@
package org.alfresco.repo.avm; package org.alfresco.repo.avm;
import org.alfresco.service.cmr.repository.ContentData;
/** /**
* A LayeredFileNode behaves like a copy on write symlink. * A LayeredFileNode behaves like a copy on write symlink.
* @author britt * @author britt
@ -81,13 +83,10 @@ class LayeredFileNodeImpl extends FileNodeImpl implements LayeredFileNode
{ {
throw new AVMException("Unbacked layered file node."); throw new AVMException("Unbacked layered file node.");
} }
// This is a mildly dirty trick. We use getContentForRead so as not to startle PlainFileNodeImpl newMe = new PlainFileNodeImpl(lPath.getAVMStore(),
// the ultimate destination content into copying itself prematurely. getBasicAttributes(),
FileContent content = ((FileNode)indirect).getContentForRead(); getContentData(lPath),
PlainFileNodeImpl newMe = new PlainFileNodeImpl(this, getProperties());
content,
lPath.getAVMStore(),
getBasicAttributes());
newMe.setAncestor(this); newMe.setAncestor(this);
return newMe; return newMe;
} }
@ -101,33 +100,6 @@ class LayeredFileNodeImpl extends FileNodeImpl implements LayeredFileNode
return AVMNodeType.LAYERED_FILE; return AVMNodeType.LAYERED_FILE;
} }
/**
* Get the content of the specified version.
* @return A FileContent object.
*/
public FileContent getContentForRead()
{
Lookup lookup = AVMRepository.GetInstance().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();
}
/**
* Get File Content for writing. Should never be called.
* @return Always null.
*/
public FileContent getContentForWrite()
{
assert false : "Never happens";
return null;
}
/** /**
* Get the underlying path. * Get the underlying path.
* @param lookup The Lookup. (Unused here.) * @param lookup The Lookup. (Unused here.)
@ -256,4 +228,29 @@ class LayeredFileNodeImpl extends FileNodeImpl implements LayeredFileNode
{ {
fIndirection = indirection; fIndirection = indirection;
} }
/**
* Set the ContentData for this file.
* @param contentData The value to set.
*/
public void setContentData(ContentData contentData)
{
throw new AVMException("Should not be called.");
}
/**
* Get the ContentData for this file.
* @return The ContentData object for this file.
*/
public ContentData getContentData(Lookup lPath)
{
Lookup lookup = lPath.getAVMStore().getAVMRepository().lookup(-1, getIndirection());
AVMNode node = lookup.getCurrentNode();
if (!(node instanceof FileNode))
{
throw new AVMException("Invalid target.");
}
FileNode file = (FileNode)node;
return file.getContentData(lookup);
}
} }

View File

@ -295,17 +295,7 @@ class OrphanReaper implements Runnable
} }
AVMContext.fgInstance.fAVMNodeDAO.delete(node); AVMContext.fgInstance.fAVMNodeDAO.delete(node);
} }
else if (node.getType() == AVMNodeType.PLAIN_FILE) // TODO Need to properly clean up deleted files.
{
AVMContext.fgInstance.fAVMNodeDAO.delete(node);
// FileContent should be purged if nobody else references it.
FileContent content = ((PlainFileNode)node).getContent();
if (content.getRefCount() == 1)
{
content.delete();
AVMContext.fgInstance.fFileContentDAO.delete(content);
}
}
else else
{ {
AVMContext.fgInstance.fAVMNodeDAO.delete(node); AVMContext.fgInstance.fAVMNodeDAO.delete(node);

View File

@ -6,5 +6,4 @@ package org.alfresco.repo.avm;
*/ */
interface PlainFileNode extends FileNode interface PlainFileNode extends FileNode
{ {
public FileContent getContent();
} }

View File

@ -17,7 +17,12 @@
package org.alfresco.repo.avm; package org.alfresco.repo.avm;
import java.io.File; import java.util.Map;
import org.alfresco.repo.domain.PropertyValue;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.namespace.QName;
/** /**
@ -29,9 +34,24 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
static final long serialVersionUID = 8720376837929735294L; static final long serialVersionUID = 8720376837929735294L;
/** /**
* The file content. * The Content URL.
*/ */
private FileContent fContent; private String fContentURL;
/**
* The Mime type.
*/
private String fMimeType;
/**
* The character encoding.
*/
private String fEncoding;
/**
* The length of the file.
*/
private long fLength;
/** /**
* Default constructor. * Default constructor.
@ -48,27 +68,12 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
public PlainFileNodeImpl(AVMStore store) public PlainFileNodeImpl(AVMStore store)
{ {
super(store.getAVMRepository().issueID(), store); super(store.getAVMRepository().issueID(), store);
fContent = new FileContentImpl(AVMRepository.GetInstance().issueContentID());
// AVMContext.fgInstance.fAVMNodeDAO.flush(); // AVMContext.fgInstance.fAVMNodeDAO.flush();
AVMContext.fgInstance.fAVMNodeDAO.save(this); AVMContext.fgInstance.fAVMNodeDAO.save(this);
AVMContext.fgInstance.fAVMNodeDAO.flush(); AVMContext.fgInstance.fAVMNodeDAO.flush();
AVMContext.fgInstance.fNewInAVMStoreDAO.save(new NewInAVMStoreImpl(store, this)); AVMContext.fgInstance.fNewInAVMStoreDAO.save(new NewInAVMStoreImpl(store, this));
} }
/**
* Create a new plain file with given content.
* @param store The store.
* @param content The content to set.
*/
public PlainFileNodeImpl(AVMStore store, File content)
{
super(store.getAVMRepository().issueID(), store);
fContent = new FileContentImpl(AVMRepository.GetInstance().issueContentID(), content);
AVMContext.fgInstance.fAVMNodeDAO.save(this);
AVMContext.fgInstance.fAVMNodeDAO.flush();
AVMContext.fgInstance.fNewInAVMStoreDAO.save(new NewInAVMStoreImpl(store, this));
}
/** /**
* Copy on write constructor. * Copy on write constructor.
* @param other The node we are being copied from. * @param other The node we are being copied from.
@ -78,8 +83,9 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
AVMStore store) AVMStore store)
{ {
super(store.getAVMRepository().issueID(), store); super(store.getAVMRepository().issueID(), store);
fContent = other.getContent(); // The null is OK because the Lookup argument is only use by
fContent.setRefCount(fContent.getRefCount() + 1); // layered files.
setContentData(other.getContentData(null));
AVMContext.fgInstance.fAVMNodeDAO.save(this); AVMContext.fgInstance.fAVMNodeDAO.save(this);
AVMContext.fgInstance.fAVMNodeDAO.flush(); AVMContext.fgInstance.fAVMNodeDAO.flush();
AVMContext.fgInstance.fNewInAVMStoreDAO.save(new NewInAVMStoreImpl(store, this)); AVMContext.fgInstance.fNewInAVMStoreDAO.save(new NewInAVMStoreImpl(store, this));
@ -87,22 +93,24 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
} }
/** /**
* Constructor that takes a FileContent to share. Called by LayeredFileNodeImpl.copy(). * Construct a new one. This is called when a LayeredFileNode
* @param content The FileContent to share. * is copied.
* @param store The AVMStore. * @param store
* @param attrs
* @param content
*/ */
public PlainFileNodeImpl(LayeredFileNode other, public PlainFileNodeImpl(AVMStore store,
FileContent content, BasicAttributes attrs,
AVMStore store, ContentData content,
BasicAttributes oAttrs) Map<QName, PropertyValue> props)
{ {
super(store.getAVMRepository().issueID(), store); super(store.getAVMRepository().issueID(), store);
fContent = content; setContentData(content);
fContent.setRefCount(fContent.getRefCount() + 1); setBasicAttributes(attrs);
AVMContext.fgInstance.fAVMNodeDAO.save(this); AVMContext.fgInstance.fAVMNodeDAO.save(this);
AVMContext.fgInstance.fAVMNodeDAO.flush(); AVMContext.fgInstance.fAVMNodeDAO.flush();
this.setProperties(props);
AVMContext.fgInstance.fNewInAVMStoreDAO.save(new NewInAVMStoreImpl(store, this)); AVMContext.fgInstance.fNewInAVMStoreDAO.save(new NewInAVMStoreImpl(store, this));
copyProperties(other);
} }
/** /**
@ -125,25 +133,6 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
return AVMNodeType.PLAIN_FILE; return AVMNodeType.PLAIN_FILE;
} }
/**
* Get content for reading.
*/
public FileContent getContentForRead()
{
return fContent;
}
/**
* Get content for writing.
*/
public FileContent getContentForWrite()
{
if (fContent.getRefCount() > 1)
{
fContent = new FileContentImpl(fContent, AVMRepository.GetInstance().issueContentID());
}
return fContent;
}
/** /**
* Get a diagnostic string representation. * Get a diagnostic string representation.
* @param lPath The Lookup. * @param lPath The Lookup.
@ -187,7 +176,7 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
false, false,
-1, -1,
false, false,
getContentForRead().getLength()); getLength());
} }
/** /**
@ -214,7 +203,7 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
false, false,
-1, -1,
false, false,
getContentForRead().getLength()); getFileLength());
} }
/** /**
@ -243,25 +232,115 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
false, false,
-1, -1,
false, false,
getContentForRead().getLength()); getFileLength());
} }
/** /**
* Get the file content of this node. * Get the Content URL.
* @return The file content object. * @return The content URL.
*/ */
public FileContent getContent() public String getContentURL()
{ {
return fContent; return fContentURL;
} }
/** /**
* Set the FileContent for this file. * Set the Content URL.
* @param content * @param contentURL
*/ */
protected void setContent(FileContent content) protected void setContentURL(String contentURL)
{ {
fContent = content; fContentURL = contentURL;
}
/**
* Get the character encoding.
* @return The encoding.
*/
public String getEncoding()
{
return fEncoding;
}
/**
* Set the character encoding.
* @param encoding The encoding to set.
*/
protected void setEncoding(String encoding)
{
fEncoding = encoding;
}
/**
* Get the file length.
* @return The file length or null if unknown.
*/
public long getLength()
{
return fLength;
}
/**
* Get the actual file length.
* @return The actual file length;
*/
private long getFileLength()
{
ContentReader reader = AVMContext.fgInstance.getContentStore().getReader(fContentURL);
return reader.getSize();
}
/**
* Set the file length.
* @param length The length of the file.
*/
protected void setLength(long length)
{
fLength = length;
}
/**
* Get the mime type of the content.
* @return The Mime Type of the content.
*/
public String getMimeType()
{
return fMimeType;
}
/**
* Set the Mime Type of the content.
* @param mimeType The Mime Type to set.
*/
protected void setMimeType(String mimeType)
{
fMimeType = mimeType;
}
/**
* Set the ContentData for this file.
* @param contentData The value to set.
*/
public void setContentData(ContentData contentData)
{
fContentURL = contentData.getContentUrl();
fMimeType = contentData.getMimetype();
if (fMimeType == null)
{
throw new AVMException("Null mime type.");
}
fEncoding = contentData.getEncoding();
fLength = contentData.getSize();
}
/**
* Get the ContentData for this file.
* @param lPath The lookup path used to get here. Unused here.
* @return The ContentData object for this file.
*/
public ContentData getContentData(Lookup lPath)
{
return new ContentData(fContentURL, fMimeType, fLength, fEncoding);
} }
} }

View File

@ -77,9 +77,10 @@
<!-- Plain files just have a reference to a Content object. --> <!-- Plain files just have a reference to a Content object. -->
<subclass discriminator-value="plainfile" <subclass discriminator-value="plainfile"
name="PlainFileNodeImpl" proxy="PlainFileNode" lazy="true"> name="PlainFileNodeImpl" proxy="PlainFileNode" lazy="true">
<many-to-one name="content" column="content_id" <property name="contentURL" column="content_url" type="string" length="128"/>
class="FileContentImpl" fetch="join" cascade="save-update"> <property name="mimeType" column="mime_type" type="string" length="32"/>
</many-to-one> <property name="encoding" column="encoding" type="string" length="16"/>
<property name="length" column="length" type="long"/>
</subclass> </subclass>
<!-- Layered files are almost exactly copy on write symlinks. --> <!-- Layered files are almost exactly copy on write symlinks. -->
<subclass name="LayeredFileNodeImpl" <subclass name="LayeredFileNodeImpl"
@ -89,19 +90,6 @@
</subclass> </subclass>
</subclass> </subclass>
</class> </class>
<!-- Contents are objects to hang actual bytestreams off. They are explicitly reference
counted. -->
<class table="avm_contents" name="FileContentImpl" proxy="FileContent"
optimistic-lock="version">
<cache usage="read-write" />
<id name="id" column="id" type="long"/>
<version name="vers" column="vers" type="long"/>
<!-- The reference count. Contents are explicitly reference counted for now,
however it make sense to fold this into a generalized garbage collection
scheme. -->
<property name="refCount" column="ref_count" type="int"
not-null="true"/>
</class>
<!-- A store is the what we used to call a virtual repository. <!-- A store is the what we used to call a virtual repository.
Each store has it's own branch ids and layer ids but shares node ids Each store has it's own branch ids and layer ids but shares node ids
with other repositories. The physical repository is structured this way with other repositories. The physical repository is structured this way

View File

@ -23,6 +23,9 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.repo.avm.AVMService;
import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy; import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy;
import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy; import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy;
import org.alfresco.repo.content.filestore.FileContentStore; import org.alfresco.repo.content.filestore.FileContentStore;
@ -44,6 +47,7 @@ import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NoTransformerException; import org.alfresco.service.cmr.repository.NoTransformerException;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
@ -66,6 +70,8 @@ public class RoutingContentService implements ContentService
private TransactionService transactionService; private TransactionService transactionService;
private DictionaryService dictionaryService; private DictionaryService dictionaryService;
private NodeService nodeService; private NodeService nodeService;
private AVMService avmService;
/** a registry of all available content transformers */ /** a registry of all available content transformers */
private ContentTransformerRegistry transformerRegistry; private ContentTransformerRegistry transformerRegistry;
/** TEMPORARY until we have a map to choose from at runtime */ /** TEMPORARY until we have a map to choose from at runtime */
@ -122,6 +128,11 @@ public class RoutingContentService implements ContentService
this.policyComponent = policyComponent; this.policyComponent = policyComponent;
} }
public void setAvmService(AVMService service)
{
this.avmService = service;
}
/** /**
* Service initialise * Service initialise
*/ */
@ -304,6 +315,7 @@ public class RoutingContentService implements ContentService
public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update) public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update)
{ {
// check for an existing URL - the get of the reader will perform type checking // check for an existing URL - the get of the reader will perform type checking
ContentReader existingContentReader = getReader(nodeRef, propertyQName, false); ContentReader existingContentReader = getReader(nodeRef, propertyQName, false);
@ -313,8 +325,18 @@ public class RoutingContentService implements ContentService
// can be wherever the store decides. // can be wherever the store decides.
ContentWriter writer = store.getWriter(existingContentReader, null); ContentWriter writer = store.getWriter(existingContentReader, null);
// Special case for AVM repository.
Serializable contentValue = null;
if (nodeRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM))
{
Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
contentValue = avmService.getContentDataForWrite((String)avmVersionPath[1]);
}
else
{
contentValue = nodeService.getProperty(nodeRef, propertyQName);
}
// set extra data on the reader if the property is pre-existing // set extra data on the reader if the property is pre-existing
Serializable contentValue = nodeService.getProperty(nodeRef, propertyQName);
if (contentValue != null && contentValue instanceof ContentData) if (contentValue != null && contentValue instanceof ContentData)
{ {
ContentData contentData = (ContentData)contentValue; ContentData contentData = (ContentData)contentValue;