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>
</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">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
@ -194,6 +188,9 @@
<property name="avmStorePropertyDAO">
<ref bean="avmStorePropertyDAO"/>
</property>
<property name="contentService">
<ref bean="ContentService"/>
</property>
</bean>
<!-- The HibernateTransactionManager -->

View File

@ -16,16 +16,6 @@
</property>
</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"
depends-on="retryingTransaction,avmContext" init-method="init">
<property name="name">
@ -64,12 +54,6 @@
</property>
</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">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
@ -125,9 +109,6 @@
<property name="versionRootDAO">
<ref bean="versionRootDAO"/>
</property>
<property name="fileContentDAO">
<ref bean="fileContentDAO"/>
</property>
<property name="childEntryDAO">
<ref bean="childEntryDAO"/>
</property>
@ -217,9 +198,6 @@
<property name="nodeIssuer">
<ref bean="nodeIssuer"/>
</property>
<property name="contentIssuer">
<ref bean="contentIssuer"/>
</property>
<property name="layerIssuer">
<ref bean="layerIssuer"/>
</property>

View File

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

View File

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

View File

@ -15,6 +15,7 @@
<map>
<entry key="workspace"><ref bean="dbNodeService"></ref></entry>
<entry key="versionStore"><ref bean="versionNodeService"></ref></entry>
<entry key="avm"><ref bean="avmNodeService"/></entry>
</map>
</property>
</bean>
@ -58,6 +59,16 @@
</property>
</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 -->
<bean id="nodeIndexer" class="org.alfresco.repo.node.index.NodeIndexer" init-method="init">
<property name="policyComponent">

View File

@ -92,6 +92,7 @@ public interface ContentModel
// content type and aspect constants
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_READONLY = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "readonly");
// title aspect
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;
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
* point for access to needed global instances.
* @author britt
*/
class AVMContext
class AVMContext implements ApplicationContextAware
{
/**
* The single instance of an AVMContext.
@ -40,11 +46,6 @@ class AVMContext
*/
public VersionRootDAO fVersionRootDAO;
/**
* The FileContentDAO.
*/
public FileContentDAO fFileContentDAO;
/**
* The ChildEntryDAO.
*/
@ -80,6 +81,36 @@ class AVMContext
*/
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
*/
@ -104,14 +135,6 @@ class AVMContext
fDeletedChildDAO = deletedChildDAO;
}
/**
* @param fileContentDAO the fFileContentDAO to set
*/
public void setFileContentDAO(FileContentDAO fileContentDAO)
{
fFileContentDAO = fileContentDAO;
}
/**
* @param historyLinkDAO the fHistoryLinkDAO to set
*/
@ -169,4 +192,56 @@ class AVMContext
{
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)
{
if (e instanceof AVMNotFoundException ||
e instanceof AVMExistsException)
if (e instanceof AVMException)
{
return;
}

View File

@ -375,6 +375,7 @@ public class AVMInterpreter
out.println("Mod Time: " + new Date(desc.getModDate()));
}
}
/*
else if (command[0].equals("catver"))
{
if (command.length != 4)
@ -398,6 +399,7 @@ public class AVMInterpreter
}
reader.close();
}
*/
else if (command[0].equals("ca"))
{
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.
* @author britt
*/
class AVMNodeConverter
public class AVMNodeConverter
{
/**
* 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.SortedMap;
import javax.naming.OperationNotSupportedException;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.PropertyValue;
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.AssociationRef;
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.InvalidChildAssociationRefException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
@ -80,6 +79,15 @@ public class AVMNodeService implements NodeService
fAVMService = service;
}
/**
* Set the DictionaryService. For Spring.
* @param dictionaryService The DictionaryService instance.
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
fDictionaryService = dictionaryService;
}
/**
* Default constructor.
*/
@ -281,6 +289,10 @@ public class AVMNodeService implements NodeService
Map<QName, PropertyValue> props = new HashMap<QName, PropertyValue>();
for (QName qname : properties.keySet())
{
if (isBuiltInProperty(qname))
{
continue;
}
props.put(qname, new PropertyValue(null, properties.get(qname)));
}
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_MODIFIER, desc.getLastModifier());
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;
}
@ -690,6 +715,21 @@ public class AVMNodeService implements NodeService
QName qName,
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;
try
{
@ -778,7 +818,8 @@ public class AVMNodeService implements NodeService
qName.equals(ContentModel.PROP_CREATOR) ||
qName.equals(ContentModel.PROP_MODIFIED) ||
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
{
Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
// TODO Just until we can set built in properties on AVM Nodes.
if (isBuiltInProperty(qname))
{
if (qname.equals(ContentModel.PROP_CONTENT))
{
}
return;
}
Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
if ((Integer)avmVersionPath[0] >= 0)
{
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.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@ -28,6 +27,10 @@ import java.util.Map;
import java.util.SortedMap;
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;
/**
@ -52,11 +55,6 @@ class AVMRepository
*/
private Issuer fNodeIssuer;
/**
* The content id issuer;
*/
private Issuer fContentIssuer;
/**
* The layer id issuer.
*/
@ -94,15 +92,6 @@ class AVMRepository
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.
* @param layerIssuer The issuer.
@ -281,21 +270,6 @@ class AVMRepository
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.
* @param srcPath Source containing directory.
@ -524,25 +498,30 @@ class AVMRepository
}
/**
* Get an InputStream from a given version of a file.
* @param desc The node descriptor.
* @return The InputStream.
* Get a ContentReader from a file.
* @param version The version to look under.
* @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);
AVMNode node = AVMContext.fgInstance.fAVMNodeDAO.getByID(desc.getId());
if (node == null)
{
throw new AVMNotFoundException("Not found.");
}
if (node.getType() != AVMNodeType.PLAIN_FILE &&
node.getType() != AVMNodeType.LAYERED_FILE)
{
throw new AVMWrongTypeException("Not a file.");
}
FileNode file = (FileNode)node;
return file.getContentForRead().getInputStream();
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
return store.getReader(version, pathParts[1]);
}
/**
* Get a ContentWriter to a file.
* @param path The path to the file.
* @return A ContentWriter.
*/
public ContentWriter getWriter(String path)
{
fLookupCount.set(1);
String [] pathParts = SplitPath(path);
AVMStore store = getAVMStoreByName(pathParts[0]);
return store.getWriter(pathParts[1]);
}
/**
@ -666,15 +645,6 @@ class AVMRepository
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.
@ -1112,6 +1082,46 @@ class AVMRepository
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.
* @return The single instance.

View File

@ -19,13 +19,15 @@ package org.alfresco.repo.avm;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
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;
/**
@ -49,15 +51,6 @@ public interface AVMService
*/
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.
* @param path The simple absolute path to the file node.
@ -68,18 +61,23 @@ public interface AVMService
public OutputStream getFileOutputStream(String path);
/**
* Get a random access file to the given path.
* @param version The version to find.
* Get a ContentReader for the given file.
* @param version The version to look under.
* @param path The path to the file.
* @param access The access mode for RandomAccessFile.
* @return A RandomAccessFile
* @throws AVMNotFoundException If <code>path</code> is not found.
* @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.
* @return A ContentReader.
* @throws AVMNotFoundException If <code>path</code> does not exist.
* @throws AVMWrongTypeException if <code>path</code> is not a file.
*/
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.
@ -545,4 +543,35 @@ public interface AVMService
* does not exist.
*/
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.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.apache.log4j.Logger;
@ -152,30 +155,57 @@ class AVMServiceImpl implements AVMService
}
/**
* Get an InputStream from a particular version of a file.
* @param desc The node descriptor.
* @return The InputStream.
* Get a ContentReader for the given file.
* @param version The version to look under.
* @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
{
public InputStream in = null;
public ContentReader reader;
public void perform()
{
in = fAVMRepository.getInputStream(desc);
reader = fAVMRepository.getReader(version, path);
}
}
TxnCallback doit = new TxnCallback();
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.
*/
@ -199,34 +229,6 @@ class AVMServiceImpl implements AVMService
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.
* @param version The version id to lookup.
@ -1322,4 +1324,80 @@ class AVMServiceImpl implements AVMService
TxnCallback doit = new TxnCallback();
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.
*/
/*
public void testRandomAccess()
{
try
@ -1914,6 +1915,7 @@ public class AVMServiceTest extends AVMServiceTestBase
fail();
}
}
*/
/**
* Test COW during long operations.

View File

@ -19,13 +19,15 @@ package org.alfresco.repo.avm;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
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;
/**
@ -102,7 +104,22 @@ interface AVMStore
* @return An InputStream
*/
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.
* @param version The version to look under.
@ -134,15 +151,6 @@ interface AVMStore
*/
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.
* @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.
*/
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.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
@ -33,6 +32,10 @@ import java.util.TreeMap;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.PropertyValue;
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;
/**
@ -231,7 +234,12 @@ class AVMStoreImpl implements AVMStore, Serializable
file.setVersionID(getNextVersionID());
dir.putChild(name, file);
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);
}
PlainFileNodeImpl file = new PlainFileNodeImpl(this, data);
PlainFileNodeImpl file = new PlainFileNodeImpl(this);
file.setVersionID(getNextVersionID());
dir.putChild(name, file);
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)
{
ContentReader reader = getReader(version, path);
return reader.getContentInputStream();
/*
Lookup lPath = lookup(version, path, false);
AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.PLAIN_FILE &&
@ -294,8 +312,35 @@ class AVMStoreImpl implements AVMStore, Serializable
FileNode file = (FileNode)node;
FileContent content = file.getContentForRead();
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;
}
/**
* Get a listing from a directory.
* @param version The version to look under.
@ -368,6 +413,9 @@ class AVMStoreImpl implements AVMStore, Serializable
*/
public OutputStream getOutputStream(String path)
{
ContentWriter writer = getWriter(path);
return writer.getContentOutputStream();
/*
Lookup lPath = lookup(-1, path, true);
AVMNode node = lPath.getCurrentNode();
if (node.getType() != AVMNodeType.PLAIN_FILE &&
@ -379,41 +427,7 @@ class AVMStoreImpl implements AVMStore, Serializable
FileContent content = file.getContentForWrite();
file.updateModTime();
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);
}
/**
* 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;
import org.alfresco.service.cmr.repository.ContentData;
/**
* Interface for the generic idea of a file.
* @author britt
@ -23,15 +25,15 @@ package org.alfresco.repo.avm;
interface FileNode extends AVMNode
{
/**
* Get the content object associated with this node, for reading.
* @return A FileContent object.
* Set the ContentData for this file.
* @param contentData The value to set.
*/
public FileContent getContentForRead();
public void setContentData(ContentData contentData);
/**
* Get the content object for writing. This will do COW
* as needed.
* @return A FileContent object.
* Get the ContentData for this file.
* @param lPath The Lookup used to get here.
* @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;
import org.alfresco.service.cmr.repository.ContentData;
/**
* A LayeredFileNode behaves like a copy on write symlink.
* @author britt
@ -81,13 +83,10 @@ class LayeredFileNodeImpl extends FileNodeImpl implements LayeredFileNode
{
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();
PlainFileNodeImpl newMe = new PlainFileNodeImpl(this,
content,
lPath.getAVMStore(),
getBasicAttributes());
PlainFileNodeImpl newMe = new PlainFileNodeImpl(lPath.getAVMStore(),
getBasicAttributes(),
getContentData(lPath),
getProperties());
newMe.setAncestor(this);
return newMe;
}
@ -101,33 +100,6 @@ class LayeredFileNodeImpl extends FileNodeImpl implements LayeredFileNode
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.
* @param lookup The Lookup. (Unused here.)
@ -256,4 +228,29 @@ class LayeredFileNodeImpl extends FileNodeImpl implements LayeredFileNode
{
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);
}
else if (node.getType() == AVMNodeType.PLAIN_FILE)
{
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);
}
}
// TODO Need to properly clean up deleted files.
else
{
AVMContext.fgInstance.fAVMNodeDAO.delete(node);

View File

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

View File

@ -17,7 +17,12 @@
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;
/**
* 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.
@ -48,27 +68,12 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
public PlainFileNodeImpl(AVMStore store)
{
super(store.getAVMRepository().issueID(), store);
fContent = new FileContentImpl(AVMRepository.GetInstance().issueContentID());
// AVMContext.fgInstance.fAVMNodeDAO.flush();
AVMContext.fgInstance.fAVMNodeDAO.save(this);
AVMContext.fgInstance.fAVMNodeDAO.flush();
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.
* @param other The node we are being copied from.
@ -78,8 +83,9 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
AVMStore store)
{
super(store.getAVMRepository().issueID(), store);
fContent = other.getContent();
fContent.setRefCount(fContent.getRefCount() + 1);
// The null is OK because the Lookup argument is only use by
// layered files.
setContentData(other.getContentData(null));
AVMContext.fgInstance.fAVMNodeDAO.save(this);
AVMContext.fgInstance.fAVMNodeDAO.flush();
AVMContext.fgInstance.fNewInAVMStoreDAO.save(new NewInAVMStoreImpl(store, this));
@ -87,24 +93,26 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
}
/**
* Constructor that takes a FileContent to share. Called by LayeredFileNodeImpl.copy().
* @param content The FileContent to share.
* @param store The AVMStore.
* Construct a new one. This is called when a LayeredFileNode
* is copied.
* @param store
* @param attrs
* @param content
*/
public PlainFileNodeImpl(LayeredFileNode other,
FileContent content,
AVMStore store,
BasicAttributes oAttrs)
public PlainFileNodeImpl(AVMStore store,
BasicAttributes attrs,
ContentData content,
Map<QName, PropertyValue> props)
{
super(store.getAVMRepository().issueID(), store);
fContent = content;
fContent.setRefCount(fContent.getRefCount() + 1);
setContentData(content);
setBasicAttributes(attrs);
AVMContext.fgInstance.fAVMNodeDAO.save(this);
AVMContext.fgInstance.fAVMNodeDAO.flush();
this.setProperties(props);
AVMContext.fgInstance.fNewInAVMStoreDAO.save(new NewInAVMStoreImpl(store, this));
copyProperties(other);
}
/**
* Copy on write logic.
* @param lPath The lookup path.
@ -125,25 +133,6 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
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.
* @param lPath The Lookup.
@ -187,7 +176,7 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
false,
-1,
false,
getContentForRead().getLength());
getLength());
}
/**
@ -214,7 +203,7 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
false,
-1,
false,
getContentForRead().getLength());
getFileLength());
}
/**
@ -243,25 +232,115 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode
false,
-1,
false,
getContentForRead().getLength());
getFileLength());
}
/**
* Get the file content of this node.
* @return The file content object.
* Get the Content URL.
* @return The content URL.
*/
public FileContent getContent()
public String getContentURL()
{
return fContent;
return fContentURL;
}
/**
* Set the Content URL.
* @param contentURL
*/
protected void setContentURL(String contentURL)
{
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;
}
/**
* Set the FileContent for this file.
* @param content
* Get the actual file length.
* @return The actual file length;
*/
protected void setContent(FileContent content)
private long getFileLength()
{
fContent = content;
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. -->
<subclass discriminator-value="plainfile"
name="PlainFileNodeImpl" proxy="PlainFileNode" lazy="true">
<many-to-one name="content" column="content_id"
class="FileContentImpl" fetch="join" cascade="save-update">
</many-to-one>
<property name="contentURL" column="content_url" type="string" length="128"/>
<property name="mimeType" column="mime_type" type="string" length="32"/>
<property name="encoding" column="encoding" type="string" length="16"/>
<property name="length" column="length" type="long"/>
</subclass>
<!-- Layered files are almost exactly copy on write symlinks. -->
<subclass name="LayeredFileNodeImpl"
@ -89,19 +90,6 @@
</subclass>
</subclass>
</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.
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

View File

@ -23,6 +23,9 @@ import java.util.Map;
import java.util.Set;
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.OnContentUpdatePolicy;
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.NodeRef;
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.QName;
import org.alfresco.service.transaction.TransactionService;
@ -66,6 +70,8 @@ public class RoutingContentService implements ContentService
private TransactionService transactionService;
private DictionaryService dictionaryService;
private NodeService nodeService;
private AVMService avmService;
/** a registry of all available content transformers */
private ContentTransformerRegistry transformerRegistry;
/** TEMPORARY until we have a map to choose from at runtime */
@ -122,6 +128,11 @@ public class RoutingContentService implements ContentService
this.policyComponent = policyComponent;
}
public void setAvmService(AVMService service)
{
this.avmService = service;
}
/**
* Service initialise
*/
@ -304,6 +315,7 @@ public class RoutingContentService implements ContentService
public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update)
{
// check for an existing URL - the get of the reader will perform type checking
ContentReader existingContentReader = getReader(nodeRef, propertyQName, false);
@ -313,8 +325,18 @@ public class RoutingContentService implements ContentService
// can be wherever the store decides.
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
Serializable contentValue = nodeService.getProperty(nodeRef, propertyQName);
if (contentValue != null && contentValue instanceof ContentData)
{
ContentData contentData = (ContentData)contentValue;