Commit
…y work. Resolves: https://www.pivotaltracker.com/story/show/61087856 and https://www.pivotaltracker.com/story/show/64112028
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>org.fcrepo</groupId> | ||
<artifactId>fcrepo</artifactId> | ||
<version>4.0.0-alpha-4-SNAPSHOT</version> | ||
</parent> | ||
<artifactId>fcrepo-connector-file</artifactId> | ||
<name>Fedora Repository FileSystem Connector Module</name> | ||
<description>The Fedora Commons repository filesystem connector module: Provides repository projection over hierarchical files/directories on the filesystem.</description> | ||
<packaging>jar</packaging> | ||
|
||
<dependencies> | ||
|
||
<dependency> | ||
<groupId>org.fcrepo</groupId> | ||
<artifactId>fcrepo-kernel</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>javax.jcr</groupId> | ||
<artifactId>jcr</artifactId> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.fcrepo</groupId> | ||
<artifactId>fcrepo-http-api</artifactId> | ||
<version>${project.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.fcrepo</groupId> | ||
<artifactId>fcrepo-http-commons</artifactId> | ||
<version>${project.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.fcrepo</groupId> | ||
<artifactId>fcrepo-http-commons</artifactId> | ||
<version>${project.version}</version> | ||
<type>test-jar</type> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>javax.inject</groupId> | ||
<artifactId>javax.inject</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>javax</groupId> | ||
<artifactId>javaee-api</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework</groupId> | ||
<artifactId>spring-test</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework</groupId> | ||
<artifactId>spring-beans</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>ch.qos.logback</groupId> | ||
<artifactId>logback-classic</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
|
||
</dependencies> | ||
|
||
</project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/** | ||
* Copyright 2013 DuraSpace, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* 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.fcrepo.connector.file; | ||
|
||
import org.fcrepo.kernel.utils.ContentDigest; | ||
import org.infinispan.schematic.document.Document; | ||
import org.modeshape.connector.filesystem.FileSystemConnector; | ||
import org.modeshape.jcr.api.JcrConstants; | ||
import org.modeshape.jcr.federation.spi.DocumentReader; | ||
import org.modeshape.jcr.federation.spi.DocumentWriter; | ||
import org.modeshape.jcr.value.BinaryValue; | ||
import org.modeshape.jcr.value.Name; | ||
import org.modeshape.jcr.value.Property; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.util.Map; | ||
|
||
import static org.fcrepo.jcr.FedoraJcrTypes.CONTENT_DIGEST; | ||
import static org.fcrepo.jcr.FedoraJcrTypes.CONTENT_SIZE; | ||
import static org.fcrepo.jcr.FedoraJcrTypes.FEDORA_BINARY; | ||
import static org.fcrepo.jcr.FedoraJcrTypes.FEDORA_DATASTREAM; | ||
import static org.fcrepo.jcr.FedoraJcrTypes.FEDORA_RESOURCE; | ||
import static org.fcrepo.jcr.FedoraJcrTypes.JCR_CREATED; | ||
import static org.fcrepo.jcr.FedoraJcrTypes.JCR_LASTMODIFIED; | ||
import static org.modeshape.jcr.api.JcrConstants.JCR_DATA; | ||
import static org.modeshape.jcr.api.JcrConstants.JCR_PRIMARY_TYPE; | ||
|
||
/** | ||
* This class extends the {@link FileSystemConnector} to enable the autocreation of Fedora-specific datastream and | ||
* content properties. | ||
* | ||
* @author Andrew Woods | ||
* Date: 1/30/14 | ||
*/ | ||
public class FedoraFileSystemConnector extends FileSystemConnector { | ||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(FedoraFileSystemConnector.class); | ||
|
||
|
||
/** | ||
* This method returns the object/document for the node with the federated arg 'id'. | ||
* <p/> | ||
* Additionally, this method adds Fedora datastream and content properties to the result of the parent class | ||
* implementation. | ||
*/ | ||
@Override | ||
public Document getDocumentById(String id) { | ||
LOGGER.debug("Getting Federated document: {}", id); | ||
if (null == id || id.isEmpty()) { | ||
LOGGER.warn("Can not get document with null id"); | ||
return null; | ||
} | ||
|
||
final Document doc = super.getDocumentById(id); | ||
|
||
final DocumentReader docReader = readDocument(doc); | ||
final DocumentWriter docWriter = writeDocument(doc); | ||
|
||
final String primaryType = docReader.getPrimaryTypeName(); | ||
|
||
if (!docReader.getMixinTypeNames().contains(FEDORA_RESOURCE)) { | ||
LOGGER.trace("Adding mixin: {}, to {}", FEDORA_RESOURCE, id); | ||
docWriter.addMixinType(FEDORA_RESOURCE); | ||
} | ||
|
||
// Is Fedora Datastream? | ||
if (primaryType.equals(JcrConstants.NT_FILE)) { | ||
decorateDatastreamNode(docReader, docWriter); | ||
|
||
// Is Fedora Content? | ||
} else if (primaryType.equals(JcrConstants.NT_RESOURCE)) { | ||
decorateContentNode(docReader, docWriter); | ||
} | ||
|
||
// Persist new properties | ||
if (!isReadonly()) { | ||
saveProperties(docReader); | ||
} | ||
|
||
return docWriter.document(); | ||
} | ||
|
||
private void decorateDatastreamNode(DocumentReader docReader, DocumentWriter docWriter) { | ||
if (!docReader.getMixinTypeNames().contains(FEDORA_DATASTREAM)) { | ||
LOGGER.trace("Adding mixin: {}, to {}", FEDORA_DATASTREAM, docReader.getDocumentId()); | ||
docWriter.addMixinType(FEDORA_DATASTREAM); | ||
} | ||
} | ||
|
||
private void decorateContentNode(DocumentReader docReader, DocumentWriter docWriter) { | ||
if (!docReader.getMixinTypeNames().contains(FEDORA_BINARY)) { | ||
LOGGER.trace("Adding mixin: {}, to {}", FEDORA_BINARY, docReader.getDocumentId()); | ||
docWriter.addMixinType(FEDORA_BINARY); | ||
} | ||
|
||
if (null == docReader.getProperty(CONTENT_DIGEST)) { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
awoods
|
||
final BinaryValue binaryValue = getBinaryValue(docReader); | ||
final String dsChecksum = binaryValue.getHexHash(); | ||
final String dsURI = ContentDigest.asURI("SHA-1", dsChecksum).toString(); | ||
|
||
LOGGER.trace("Adding {} property of {} to {}", CONTENT_DIGEST, dsURI, docReader.getDocumentId()); | ||
docWriter.addProperty(CONTENT_DIGEST, dsURI); | ||
} | ||
|
||
if (null == docReader.getProperty(CONTENT_SIZE)) { | ||
final BinaryValue binaryValue = getBinaryValue(docReader); | ||
final long binarySize = binaryValue.getSize(); | ||
|
||
LOGGER.trace("Adding {} property of {} to {}", CONTENT_SIZE, binarySize, docReader.getDocumentId()); | ||
docWriter.addProperty(CONTENT_SIZE, binarySize); | ||
} | ||
|
||
LOGGER.debug("Decorated data property at path: {}", docReader.getDocumentId()); | ||
} | ||
|
||
private BinaryValue getBinaryValue(DocumentReader docReader) { | ||
final Property binaryProperty = docReader.getProperty(JCR_DATA); | ||
return (BinaryValue) binaryProperty.getFirstValue(); | ||
} | ||
|
||
private void saveProperties(DocumentReader docReader) { | ||
LOGGER.trace("Persisting properties for {}", docReader.getDocumentId()); | ||
final Map<Name, Property> properties = docReader.getProperties(); | ||
final ExtraProperties extraProperties = extraPropertiesFor(docReader.getDocumentId(), true); | ||
extraProperties.addAll(properties).except(JCR_PRIMARY_TYPE, JCR_CREATED, JCR_LASTMODIFIED, JCR_DATA); | ||
extraProperties.save(); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
/** | ||
* Copyright 2013 DuraSpace, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* 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.fcrepo.connector.file; | ||
|
||
import org.junit.AfterClass; | ||
import org.junit.Assert; | ||
import org.fcrepo.http.commons.test.util.TestHelpers; | ||
import org.infinispan.schematic.document.Document; | ||
import org.junit.Before; | ||
import org.junit.BeforeClass; | ||
import org.junit.Test; | ||
import org.mockito.Mock; | ||
import org.mockito.Mockito; | ||
import org.mockito.MockitoAnnotations; | ||
import org.modeshape.jcr.ExecutionContext; | ||
import org.modeshape.jcr.api.JcrConstants; | ||
import org.modeshape.jcr.api.nodetype.NodeTypeManager; | ||
import org.modeshape.jcr.cache.document.DocumentTranslator; | ||
import org.modeshape.jcr.federation.spi.ExtraPropertiesStore; | ||
import org.modeshape.jcr.value.BinaryValue; | ||
import org.modeshape.jcr.value.NameFactory; | ||
import org.modeshape.jcr.value.Property; | ||
import org.modeshape.jcr.value.ValueFactories; | ||
import org.modeshape.jcr.value.basic.BasicName; | ||
|
||
import javax.jcr.NamespaceRegistry; | ||
|
||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
|
||
import static org.mockito.Matchers.any; | ||
import static org.mockito.Matchers.anyString; | ||
import static org.mockito.Mockito.when; | ||
import static org.modeshape.jcr.api.JcrConstants.JCR_DATA; | ||
|
||
/** | ||
* @author Andrew Woods | ||
* Date: 2/3/14 | ||
*/ | ||
public class FedoraFileSystemConnectorTest { | ||
|
||
private FedoraFileSystemConnector connector; | ||
|
||
private final static String directoryPath = System.getProperty("java.io.tmpdir"); | ||
private final static File tmpFile = new File(directoryPath, "fedora-filesystemtest.txt"); | ||
|
||
This comment has been minimized.
Sorry, something went wrong.
ajs6f
Contributor
|
||
@Mock | ||
private NamespaceRegistry mockRegistry; | ||
|
||
@Mock | ||
private NodeTypeManager mockNodeTypeManager; | ||
|
||
@Mock | ||
private DocumentTranslator mockTranslator; | ||
|
||
@Mock | ||
private NameFactory mockNameFactory; | ||
|
||
@Mock | ||
private ValueFactories mockValueFactories; | ||
|
||
@Mock | ||
private ExtraPropertiesStore mockExtraPropertiesStore; | ||
|
||
private ExecutionContext mockContext = new ExecutionContext(); | ||
|
||
|
||
@BeforeClass | ||
public static void beforeClass() | ||
{ | ||
try { | ||
FileOutputStream outputStream = new FileOutputStream(tmpFile); | ||
outputStream.write("hello".getBytes()); | ||
} catch (IOException e) { | ||
System.err.println("Error creating: " + tmpFile.getAbsolutePath() + " - " + e.getMessage()); | ||
} | ||
} | ||
|
||
@AfterClass | ||
public static void afterClass() { | ||
try { | ||
tmpFile.delete(); | ||
} catch (Exception e) { | ||
System.err.println("Error deleting: " + tmpFile.getAbsolutePath() + " - " + e.getMessage()); | ||
} | ||
} | ||
|
||
@Before | ||
public void setUp() throws Exception { | ||
MockitoAnnotations.initMocks(this); | ||
|
||
connector = new FedoraFileSystemConnector(); | ||
TestHelpers.setField(connector, "directoryPath", directoryPath); | ||
TestHelpers.setField(connector, "translator", mockTranslator); | ||
TestHelpers.setField(connector, "context", mockContext); | ||
TestHelpers.setField(connector, "extraPropertiesStore", mockExtraPropertiesStore); | ||
|
||
TestHelpers.setField(mockTranslator, "names", mockNameFactory); | ||
connector.initialize(mockRegistry, mockNodeTypeManager); | ||
This comment has been minimized.
Sorry, something went wrong.
ajs6f
Contributor
|
||
} | ||
|
||
@Test | ||
public void testGetDocumentByIdNull() throws Exception { | ||
Document doc = connector.getDocumentById(null); | ||
Assert.assertNull(doc); | ||
} | ||
|
||
@Test | ||
public void testGetDocumentByIdDatastream() throws Exception { | ||
when(mockTranslator.getPrimaryTypeName(any(Document.class))).thenReturn(JcrConstants.NT_FILE); | ||
when(mockNameFactory.create(anyString())).thenReturn(new BasicName("", tmpFile.getName())); | ||
|
||
Document doc = connector.getDocumentById("/" + tmpFile.getName()); | ||
Assert.assertNotNull(doc); | ||
} | ||
|
||
@Test | ||
public void testGetDocumentByIdContent() throws Exception { | ||
when(mockTranslator.getPrimaryTypeName(any(Document.class))).thenReturn(JcrConstants.NT_RESOURCE); | ||
when(mockNameFactory.create(anyString())).thenReturn(new BasicName("", tmpFile.getName())); | ||
|
||
Property binaryProperty = Mockito.mock(Property.class, "BinaryProperty"); | ||
BinaryValue binaryValue = Mockito.mock(BinaryValue.class, "BinayValue"); | ||
when(binaryProperty.getFirstValue()).thenReturn(binaryValue); | ||
when(mockTranslator.getProperty(any(Document.class), Mockito.eq(JCR_DATA))).thenReturn(binaryProperty); | ||
|
||
Document doc = connector.getDocumentById("/" + tmpFile.getName()); | ||
Assert.assertNotNull(doc); | ||
} | ||
|
||
} |
From this point forward, are we clear as to whether every getProperty call on the docReader is actually going back to the federated persistence (which could be really expensive and would make us want to pull all the properties at once) or whether MODE is unmarshalling them all at once and we don't need to worry about it?