Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add FedoraFileSystemConnector, so that federated properties and fixit…
…y work. Resolves: https://www.pivotaltracker.com/story/show/61087856 and https://www.pivotaltracker.com/story/show/64112028
- Loading branch information
Andrew Woods
committed
Feb 6, 2014
1 parent
d842135
commit fbb2520
Showing
9 changed files
with
718 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
144 changes: 144 additions & 0 deletions
144
fcrepo-connector-file/src/main/java/org/fcrepo/connector/file/FedoraFileSystemConnector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
/** | ||
* 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 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.fcrepo.kernel.utils.ContentDigest.asURI; | ||
import static org.modeshape.jcr.api.JcrConstants.JCR_DATA; | ||
import static org.modeshape.jcr.api.JcrConstants.JCR_PRIMARY_TYPE; | ||
import static org.modeshape.jcr.api.JcrConstants.NT_FILE; | ||
import static org.modeshape.jcr.api.JcrConstants.NT_RESOURCE; | ||
|
||
import java.util.Map; | ||
|
||
import org.infinispan.schematic.document.Document; | ||
import org.modeshape.connector.filesystem.FileSystemConnector; | ||
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; | ||
|
||
/** | ||
* 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(final 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(NT_FILE)) { | ||
decorateDatastreamNode(docReader, docWriter); | ||
|
||
// Is Fedora Content? | ||
} else if (primaryType.equals(NT_RESOURCE)) { | ||
decorateContentNode(docReader, docWriter); | ||
} | ||
|
||
// Persist new properties | ||
if (!isReadonly()) { | ||
saveProperties(docReader); | ||
} | ||
|
||
return docWriter.document(); | ||
} | ||
|
||
private static void decorateDatastreamNode(final DocumentReader docReader, final DocumentWriter docWriter) { | ||
if (!docReader.getMixinTypeNames().contains(FEDORA_DATASTREAM)) { | ||
LOGGER.trace("Adding mixin: {}, to {}", FEDORA_DATASTREAM, docReader.getDocumentId()); | ||
docWriter.addMixinType(FEDORA_DATASTREAM); | ||
} | ||
} | ||
|
||
private static void decorateContentNode(final DocumentReader docReader, final 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)) { | ||
final BinaryValue binaryValue = getBinaryValue(docReader); | ||
final String dsChecksum = binaryValue.getHexHash(); | ||
final String dsURI = 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 static BinaryValue getBinaryValue(final DocumentReader docReader) { | ||
final Property binaryProperty = docReader.getProperty(JCR_DATA); | ||
return (BinaryValue) binaryProperty.getFirstValue(); | ||
} | ||
|
||
private void saveProperties(final 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(); | ||
} | ||
|
||
} |
168 changes: 168 additions & 0 deletions
168
...connector-file/src/test/java/org/fcrepo/connector/file/FedoraFileSystemConnectorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
/** | ||
* 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.infinispan.schematic.document.Document; | ||
import org.junit.Before; | ||
import org.junit.BeforeClass; | ||
import org.junit.Test; | ||
import org.mockito.Mock; | ||
import org.modeshape.jcr.ExecutionContext; | ||
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 org.slf4j.Logger; | ||
|
||
import javax.jcr.NamespaceRegistry; | ||
|
||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
|
||
import static java.nio.file.Files.createTempDirectory; | ||
import static java.nio.file.Files.createTempFile; | ||
import static org.fcrepo.http.commons.test.util.TestHelpers.setField; | ||
import static org.junit.Assert.assertNotNull; | ||
import static org.junit.Assert.assertNull; | ||
import static org.mockito.Matchers.any; | ||
import static org.mockito.Matchers.anyString; | ||
import static org.mockito.Mockito.when; | ||
import static org.mockito.Mockito.eq; | ||
import static org.mockito.MockitoAnnotations.initMocks; | ||
import static org.modeshape.jcr.api.JcrConstants.JCR_DATA; | ||
import static org.modeshape.jcr.api.JcrConstants.NT_FILE; | ||
import static org.modeshape.jcr.api.JcrConstants.NT_RESOURCE; | ||
import static org.slf4j.LoggerFactory.getLogger; | ||
|
||
/** | ||
* @author Andrew Woods | ||
* Date: 2/3/14 | ||
*/ | ||
public class FedoraFileSystemConnectorTest { | ||
|
||
private FedoraFileSystemConnector connector; | ||
|
||
private static Path directoryPath; | ||
|
||
private static File tmpFile; | ||
|
||
@Mock | ||
private NamespaceRegistry mockRegistry; | ||
|
||
@Mock | ||
private NodeTypeManager mockNodeTypeManager; | ||
|
||
@Mock | ||
private DocumentTranslator mockTranslator; | ||
|
||
@Mock | ||
private NameFactory mockNameFactory; | ||
|
||
@Mock | ||
private ValueFactories mockValueFactories; | ||
|
||
@Mock | ||
private ExtraPropertiesStore mockExtraPropertiesStore; | ||
|
||
@Mock | ||
private Property binaryProperty; | ||
|
||
@Mock | ||
private BinaryValue binaryValue; | ||
|
||
private ExecutionContext mockContext = new ExecutionContext(); | ||
|
||
private static final Logger logger = | ||
getLogger(FedoraFileSystemConnectorTest.class); | ||
|
||
@BeforeClass | ||
public static void beforeClass() throws IOException { | ||
directoryPath = createTempDirectory("fedora-filesystemtest"); | ||
tmpFile = | ||
createTempFile(directoryPath, "fedora-filesystemtestfile", | ||
"txt").toFile(); | ||
try (FileOutputStream outputStream = new FileOutputStream(tmpFile)) { | ||
outputStream.write("hello".getBytes()); | ||
} catch (final IOException e) { | ||
logger.error("Error creating: {} - {}", tmpFile.getAbsolutePath(), | ||
e.getMessage()); | ||
} | ||
} | ||
|
||
@AfterClass | ||
public static void afterClass() { | ||
try { | ||
tmpFile.delete(); | ||
} catch (final Exception e) { | ||
logger.error("Error deleting: " + tmpFile.getAbsolutePath() | ||
+ " - " + e.getMessage()); | ||
} | ||
} | ||
|
||
@Before | ||
public void setUp() throws Exception { | ||
initMocks(this); | ||
|
||
connector = new FedoraFileSystemConnector(); | ||
setField(connector, "directoryPath", directoryPath.toString()); | ||
setField(connector, "translator", mockTranslator); | ||
setField(connector, "context", mockContext); | ||
setField(connector, "extraPropertiesStore", mockExtraPropertiesStore); | ||
setField(mockTranslator, "names", mockNameFactory); | ||
connector.initialize(mockRegistry, mockNodeTypeManager); | ||
} | ||
|
||
@Test | ||
public void testGetDocumentByIdNull() throws Exception { | ||
final Document doc = connector.getDocumentById(null); | ||
assertNull(doc); | ||
} | ||
|
||
@Test | ||
public void testGetDocumentByIdDatastream() throws Exception { | ||
when(mockTranslator.getPrimaryTypeName(any(Document.class))) | ||
.thenReturn(NT_FILE); | ||
when(mockNameFactory.create(anyString())).thenReturn( | ||
new BasicName("", tmpFile.getName())); | ||
|
||
final Document doc = connector.getDocumentById("/" + tmpFile.getName()); | ||
assertNotNull(doc); | ||
} | ||
|
||
@Test | ||
public void testGetDocumentByIdContent() throws Exception { | ||
when(mockTranslator.getPrimaryTypeName(any(Document.class))) | ||
.thenReturn(NT_RESOURCE); | ||
when(mockNameFactory.create(anyString())).thenReturn( | ||
new BasicName("", tmpFile.getName())); | ||
|
||
when(binaryProperty.getFirstValue()).thenReturn(binaryValue); | ||
when(mockTranslator.getProperty(any(Document.class), eq(JCR_DATA))) | ||
.thenReturn(binaryProperty); | ||
|
||
final Document doc = connector.getDocumentById("/" + tmpFile.getName()); | ||
assertNotNull(doc); | ||
} | ||
|
||
} |
Oops, something went wrong.