Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Generalize fedora 3 node organization through a new java interface.
  • Loading branch information
Michael Durbin authored and Andrew Woods committed Oct 10, 2013
1 parent 2d51af6 commit 45af9e8
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 78 deletions.
18 changes: 11 additions & 7 deletions README.md
@@ -1,7 +1,15 @@
# Fedora 3 Federation Connector

This fedoration connector allows exposure of fedora 3 content in a running fedora 3 repository
to appear within a fedora 4 repository. To use this code, you'd need to have access to a fedora 3 repository.
This fedoration connector allows exposure of fedora 3 content in a running
fedora 3 repository to appear within a fedora 4 repository. To use this code,
you'd need to have access to a fedora 3 repository.

## Configuration requirements

1. Fedora 3 repository must be accessible through the REST API
2. Adminsitrative credentials must be provided that expose access to ALL content
3. The resource index must be enabled
4. The repository is expected to be unchanging while exposed through Fedora 4

## Organization

Expand Down Expand Up @@ -55,13 +63,9 @@ In the json file referenced in fcrepo4/fcrepo-webapp/src/main/resources/spring/r
"fedoraUrl" : "http://localhost-or-wherever-your-fedora3-is/fedora",
"username" : "your-fedora-username",
"password" : "your-fedora-password",
"pageSize" : 10
}
}

Note: pageSize is optional and represents the size of pages of objects that are the child nodes of the
repository node.

### Compile and install the code
For this project, then each of the components modified above:

Expand All @@ -79,6 +83,6 @@ You can see the federation over your fedora 3 content at [http://localhost:8080/

## Caveats

* right now, the number of objects in the repository that are exposed is reduced to 21 to simplify testing
* versions are not presented
* only a subset of repository content is visible

Expand Up @@ -16,6 +16,8 @@

package org.fcrepo.connector.fedora3;

import java.util.List;

/**
* An base class that encapsulates the logic to access content from a fedora 3
* repository. This abstract class, while exposing the methods neccessary to
Expand All @@ -41,7 +43,13 @@ public interface Fedora3DataInterface {
/**
* Gets a page of object pids that exist in the repository.
*/
public String[] getObjectPids(int offset, int pageSize);
public List<String> getObjectPids(int offset, int pageSize);

/**
* Gets the size (total number of objects) of the underlying fedora 3
* repository.
*/
public long getSize();

/**
* Gets information about a given datastream for a given pid.
Expand Down
Expand Up @@ -16,15 +16,14 @@

package org.fcrepo.connector.fedora3;

import org.fcrepo.connector.fedora3.organizers.FlatTruncatedOrganizer;
import org.fcrepo.connector.fedora3.rest.RESTFedora3DataImpl;
import org.fcrepo.jcr.FedoraJcrTypes;
import org.fcrepo.kernel.utils.ContentDigest;
import org.infinispan.schematic.document.Document;
import org.modeshape.jcr.api.JcrConstants;
import org.modeshape.jcr.api.nodetype.NodeTypeManager;
import org.modeshape.jcr.federation.spi.DocumentWriter;
import org.modeshape.jcr.federation.spi.PageKey;
import org.modeshape.jcr.federation.spi.Pageable;
import org.modeshape.jcr.federation.spi.ReadOnlyConnector;
import org.modeshape.jcr.value.BinaryKey;
import org.modeshape.jcr.value.BinaryValue;
Expand All @@ -48,14 +47,15 @@
* @author Michael Durbin
*/
public class Fedora3FederationConnector extends ReadOnlyConnector
implements Pageable, FedoraJcrTypes {
implements FedoraJcrTypes {

private static final Logger LOGGER
= LoggerFactory.getLogger(Fedora3FederationConnector.class);

private static final String NT_F3_REPOSITORY = "f3:repository";
private static final String NT_F3_OBJECT = "f3:object";
private static final String NT_F3_DATASTREAM = "f3:datastream";
private static final String NT_F3_GROUP = "f3:group";

private static final String F3_PID = "f3:pid";
private static final String F3_OBJ_STATE = "f3:objState";
Expand Down Expand Up @@ -105,12 +105,7 @@ public class Fedora3FederationConnector extends ReadOnlyConnector
*/
protected String password;

/**
* Set by reflection to the value in the ModeShape repository configuration
* json file, this member variables controls the size of the pages
* requested from the fedora 3 instance when listing children.
*/
protected int pageSize = 20;
protected RepositoryOrganizer organizer;

/**
* {@inheritDoc}
Expand Down Expand Up @@ -145,6 +140,7 @@ public void initialize(NamespaceRegistry registry,
throw new RepositoryException("Error starting fedora connector!",
t);
}
organizer = new FlatTruncatedOrganizer(f3);
LOGGER.trace("Initialized");
}

Expand All @@ -160,7 +156,19 @@ public Document getDocumentById(String idStr) {
// return a root object
writer.setPrimaryType(JcrConstants.NT_FOLDER);
writer.addMixinType(NT_F3_REPOSITORY);
addRepositoryChildren(writer, idStr, 0, pageSize);
addRepositoryChildren(writer, idStr);
return writer.document();
} else if (organizer.isOrganizationalNode(idStr) && !id.isRootID()) {
writer.setPrimaryType(JcrConstants.NT_FOLDER);
writer.addMixinType(NT_F3_GROUP);
for (String childId : organizer.getChildrenForId(idStr)) {
if (organizer.isOrganizationalNode(childId)) {
writer.addChild(childId, childId);
} else {
writer.addChild(ID.objectID(childId).getId(),
ID.objectID(childId).getName());
}
}
return writer.document();
} else if (id.isObjectID()) {
// return an object node
Expand Down Expand Up @@ -193,22 +201,14 @@ public Document getDocumentById(String idStr) {
}
}

private void addRepositoryChildren(DocumentWriter writer, String idStr,
int offset, int pageSize) {
private void addRepositoryChildren(DocumentWriter writer, String idStr) {
ID id = new ID(idStr);
String[] childPids = f3.getObjectPids(offset, pageSize + 1);
List<String> childPids = organizer.getChildrenForId(idStr);
for (String childPid : childPids) {
ID childId = ID.objectID(childPid);
LOGGER.trace("Added child " + childId.getId());
writer.addChild(childId.getId(), childId.getName());
}
// FIXME
// this is commented out because the UI requests all objects,
// which for most fedora repositories is too big a set.
//if (childPids.length <= pageSize + 1) {
// writer.addPage(id, offset + pageSize, pageSize,
// PageWriter.UNKNOWN_TOTAL_SIZE);
//}
}

/**
Expand Down Expand Up @@ -327,18 +327,61 @@ private void addObjectChildren(DocumentWriter writer,
}
}

@Override
/**
* {@inheritDoc}
*/
public String getDocumentId(String externalPath) {
LOGGER.info("getDocumentId {}", externalPath);
return externalPath;
return getIdFromPath(externalPath);
}

/**
* The root path is always '/', organizational node paths are made up up
* any number of organizational node ids concatenated together, while all
* other nodes are made up of any number of organizational node paths
* concatenated together followed by an object, datastream or content ID.
*
* This method uses that to determine the id of the node at the given path.
*/
protected String getIdFromPath(String path) {
int nextBreak = path.indexOf('/', 1);
if (nextBreak == -1) {
return path;
} else {
String nextChunk = path.substring(0, nextBreak);
if (organizer.isOrganizationalNode(nextChunk)) {
return getIdFromPath(path.substring(nextBreak));
} else {
return path;
}
}
}

@Override
public Collection<String> getDocumentPathsById(String id) {
LOGGER.info("getDocumentPathsById {}", id);
return Collections.singletonList(id);
return Collections.singletonList(buildPath(id, id));
}

/**
* The root path is always '/', organizational node paths are made up up
* any number of organizational node ids concatenated together, while all
* other nodes are made up of any number of organizational node paths
* concatenated together followed by an object, datastream or content ID.
*
* This method uses that to build a path from an ID.
*/
protected String buildPath(String path, String currentId) {
String parentId = organizer.getParentForId(currentId);
if (parentId == null) {
return path;
} else {
return buildPath(parentId + path, parentId);
}
}



/**
* Checks if a document with the given id exists.
* @param idStr a {@code non-null} string representing the identifier
Expand All @@ -347,29 +390,16 @@ public Collection<String> getDocumentPathsById(String id) {
*/
public boolean hasDocument(String idStr) {
LOGGER.info("hasDocument {}", idStr);
if (organizer.isOrganizationalNode(idStr)) {
return true;
}
ID id = new ID(idStr);
return (id.isRootID()
|| (id.isObjectID() && f3.doesObjectExist(id.getPid())
|| ((id.isDatastreamID() || id.isContentID())
&& f3.doesDatastreamExist(id.getPid(), id.getDSID()))));
}

@Override
public Document getChildren(PageKey pageKey) {
LOGGER.info("getChildren {}", pageKey);
ID parentId = new ID(pageKey.getParentId());
if (parentId.isRootID()) {
DocumentWriter writer = newDocument(parentId.getId());
writer.setPrimaryType(NT_F3_REPOSITORY);
addRepositoryChildren(writer, parentId.getId(),
pageKey.getOffsetInt(), (int) pageKey.getBlockSize());
return writer.document();
} else {
// get the datastreams
throw new UnsupportedOperationException();
}
}

/**
* Gets an ExternalBinaryValue that exposes access to content nodes in
* the federation.
Expand Down
@@ -0,0 +1,50 @@
/**
* 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.fedora3;

import java.util.List;

/**
* An interface that defines methods to expose an arbitrary organization of
* objects (from a fedora 3 repository) in a federation.
*
* @author Michael Durbin
*/
public interface RepositoryOrganizer {

/**
* Gets the id values of all children nodes for the node with the given id.
* @throws IllegalArgumentException if the id is not an organizational node
*/
public List<String> getChildrenForId(String id);

/**
* Gets the id of the parent of the node with the given id.
* @throws IllegalArgumentException if the id has never been returned by a
* call to getChildrenForId.
*/
public String getParentForId(String id);

/**
* Determine whether a given node (identified by id) is an organizational
* node fabricated by this RepositoryOrganizer implementation.
* @throws IllegalArgumentException if the id has never been returned by a
* call to getChildrenForId.
*/
public boolean isOrganizationalNode(String id);

}
@@ -0,0 +1,73 @@
/**
* 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.fedora3.organizers;

import org.fcrepo.connector.fedora3.Fedora3DataInterface;
import org.fcrepo.connector.fedora3.ID;
import org.fcrepo.connector.fedora3.RepositoryOrganizer;

import java.util.List;

/**
* This is a dummy implementation that does exactly what the hard-coded
* organization from previous versions of Fedora3FederationConnector did.
* It's useful only a step on the way towards a more useful implementation.
*
* @author Michael Durbin
*/
public class FlatTruncatedOrganizer implements RepositoryOrganizer {

private Fedora3DataInterface f3;

/**
* A constructor that accepts the Fedora3DataInterface whose fedora objects
* will be organized by this instance.
*/
public FlatTruncatedOrganizer(Fedora3DataInterface fedora3Data) {
f3 = fedora3Data;
}

/**
* Returns the first 10 objects in the repository when invoked for the
* root ID; otherwise throws an IllegalArgumentException.
*/
public List<String> getChildrenForId(String id) {
if (id.equals(ID.ROOT_ID.getId())) {
return f3.getObjectPids(0, 10);
} else {
throw new IllegalArgumentException();
}
}

/**
* Returns null if the id isn't the root ID, and otherwise the root id.
*/
public String getParentForId(String id) {
if (ID.ROOT_ID.getId().equals(id)) {
return null;
} else {
return ID.ROOT_ID.getId();
}
}

/**
* Returns true for the root id, false otherwise.
*/
public boolean isOrganizationalNode(String id) {
return ID.ROOT_ID.getId().equals(id);
}
}

0 comments on commit 45af9e8

Please sign in to comment.