Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add implementation of: https://ocfl.github.io/extensions/0006-flat-om… #43

Merged
merged 3 commits into from
Jul 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@

package edu.wisc.library.ocfl.core.extension;

import edu.wisc.library.ocfl.api.OcflConstants;
import edu.wisc.library.ocfl.api.exception.OcflExtensionException;
import edu.wisc.library.ocfl.api.util.Enforce;
import edu.wisc.library.ocfl.core.extension.storage.layout.FlatLayoutExtension;
import edu.wisc.library.ocfl.core.extension.storage.layout.FlatOmitPrefixLayoutExtension;
import edu.wisc.library.ocfl.core.extension.storage.layout.HashedNTupleIdEncapsulationLayoutExtension;
import edu.wisc.library.ocfl.core.extension.storage.layout.HashedNTupleLayoutExtension;
import org.slf4j.Logger;
Expand All @@ -36,7 +36,6 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
* Registry for mapping extensions to their implementations. The following out-of-the-box extensions are pre-registered
Expand All @@ -55,7 +54,8 @@ public final class OcflExtensionRegistry {
private static final Map<String, Class<? extends OcflExtension>> REGISTRY = new HashMap<>(Map.of(
HashedNTupleLayoutExtension.EXTENSION_NAME, HashedNTupleLayoutExtension.class,
HashedNTupleIdEncapsulationLayoutExtension.EXTENSION_NAME, HashedNTupleIdEncapsulationLayoutExtension.class,
FlatLayoutExtension.EXTENSION_NAME, FlatLayoutExtension.class
FlatLayoutExtension.EXTENSION_NAME, FlatLayoutExtension.class,
FlatOmitPrefixLayoutExtension.EXTENSION_NAME, FlatOmitPrefixLayoutExtension.class
));

private OcflExtensionRegistry() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 University of Wisconsin Board of Regents
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package edu.wisc.library.ocfl.core.extension.storage.layout;

import edu.wisc.library.ocfl.api.OcflConstants;
import edu.wisc.library.ocfl.api.exception.OcflExtensionException;
import edu.wisc.library.ocfl.core.extension.OcflExtensionConfig;
import edu.wisc.library.ocfl.core.extension.storage.layout.config.FlatOmitPrefixLayoutConfig;

/**
* Implementation of the <a href="https://ocfl.github.io/extensions/0006-flat-omit-prefix-storage-layout.html">
* Flat Omit Prefix Storage Layout</a> extension.
*
* @author awoods
* @since 2021-06-22
*/
public class FlatOmitPrefixLayoutExtension implements OcflStorageLayoutExtension {

public static final String EXTENSION_NAME = "0006-flat-omit-prefix-storage-layout";

private String delim;

/**
* {@inheritDoc}
*/
@Override
public String getExtensionName() {
return EXTENSION_NAME;
}

/**
* {@inheritDoc}
*/
@Override
public String getDescription() {
return "The OCFL object identifiers are expected to contain prefixes which are removed in the mapping to " +
"directory names. The OCFL object identifier prefix is defined as all characters before and including a " +
"configurable delimiter.";
}

/**
* {@inheritDoc}
*/
@Override
public synchronized void init(OcflExtensionConfig config) {
// Only set this.config if it is uninitialized
if (this.delim == null) {

// Is arg config null?
if (config == null) {
throw new IllegalArgumentException("Arg config must not be null!");
}

if (!(config instanceof FlatOmitPrefixLayoutConfig)) {
throw new OcflExtensionException(String.format("This extension only supports %s configuration. Received: %s",
getExtensionConfigClass(), config));
}

FlatOmitPrefixLayoutConfig castConfig = (FlatOmitPrefixLayoutConfig) config;

validateConfig(castConfig);
this.delim = castConfig.getDelimiter().toLowerCase();
}
}

private static void validateConfig(FlatOmitPrefixLayoutConfig config) {
if (config != null) {
String delimiter = config.getDelimiter();
if (delimiter == null || delimiter.isEmpty()) {
throw new OcflExtensionException("Digest configuration must not be empty!");
}
}
}

@Override
public Class<? extends OcflExtensionConfig> getExtensionConfigClass() {
return FlatOmitPrefixLayoutConfig.class;
}

/**
* {@inheritDoc}
*/
@Override
public String mapObjectId(String objectId) {
if (delim == null) {
throw new OcflExtensionException("This extension must be initialized before it can be used.");
}

// Use lowercase of delimiter and objectId, per specification
String id = objectId.toLowerCase();

int index = id.lastIndexOf(delim);
String dir = objectId.substring(index + delim.length());

if (OcflConstants.EXTENSIONS_DIR.equals(dir) || dir.isEmpty()) {
throw new OcflExtensionException(String.format("The object id <%s> is incompatible with layout extension " +
"%s because it is empty or conflicts with the extensions directory.", objectId, EXTENSION_NAME));
}

return dir;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 University of Wisconsin Board of Regents
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package edu.wisc.library.ocfl.core.extension.storage.layout.config;

import com.fasterxml.jackson.annotation.JsonIgnore;
import edu.wisc.library.ocfl.api.util.Enforce;
import edu.wisc.library.ocfl.core.extension.OcflExtensionConfig;
import edu.wisc.library.ocfl.core.extension.storage.layout.FlatOmitPrefixLayoutExtension;

import java.util.Objects;

/**
* Configuration for the <a href="https://ocfl.github.io/extensions/0006-flat-omit-prefix-storage-layout.html">
* Flat Omit Prefix Storage Layout</a> extension.
*
* @author awoods
* @since 2021-06-22
*/
public class FlatOmitPrefixLayoutConfig implements OcflExtensionConfig {

private String delimiter;

@JsonIgnore
@Override
public String getExtensionName() {
return FlatOmitPrefixLayoutExtension.EXTENSION_NAME;
}

@JsonIgnore
@Override
public boolean hasParameters() {
return true;
}

/**
* @return the delimiter marking end of prefix
*/
public String getDelimiter() {
return delimiter;
}

/**
* The case-insensitive, delimiter marking the end of the OCFL object identifier prefix; MUST consist of a
* character string of length one or greater. If the delimiter is found multiple times in the OCFL object
* identifier, its last occurence (right-most) will be used to select the termination of the prefix.
*
* @param delimiter marking the end of prefix
*/
public FlatOmitPrefixLayoutConfig setDelimiter(String delimiter) {
Enforce.notNull(delimiter, "delimiter cannot be null");
if (delimiter.isEmpty()) {
throw new IllegalArgumentException("Arg must not be empty: 'delimiter'");
}
this.delimiter = delimiter;
return this;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FlatOmitPrefixLayoutConfig that = (FlatOmitPrefixLayoutConfig) o;
return delimiter.equals(that.delimiter);
}

@Override
public int hashCode() {
return Objects.hash(delimiter);
}

@Override
public String toString() {
return "FlatOmitPrefixLayoutConfig{ delimiter='" + delimiter + "' }";
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# OCFL Community Extension 0006: Flat Omit Prefix Storage Layout

* **Extension Name:** 0006-flat-omit-prefix-storage-layout
* **Authors:** Andrew Woods
* **Minimum OCFL Version:** 1.0
* **OCFL Community Extensions Version:** 1.0
* **Obsoletes:** n/a
* **Obsoleted by:** n/a

## Overview

This storage root extension describes a flat OCFL storage layout. The OCFL object directories are direct children of
the OCFL storage root directory.
The OCFL object identifiers are expected to contain prefixes which are removed in the mapping to directory names. The
OCFL object identifier prefix is defined as all characters before and including a configurable delimiter.

The limitations of this layout are filesystem dependent, but are generally as follows:

* The size of object identifiers, minus the length of the prefix, cannot exceed the maximum allowed directory name size
(eg. 255 characters)
* Object identifiers cannot include characters that are illegal in directory names
* Performance may degrade as the size of a repository increases because every object is a direct child of the storage root

## Parameters

### Summary

* **Name:** `delimiter`
* **Description:** The case-insensitive, delimiter marking the end of the OCFL object identifier prefix; MUST consist
of a character string of length one or greater. If the delimiter is found multiple times in the OCFL object
identifier, its last occurence (right-most) will be used to select the termination of the prefix.
* **Type:** string
* **Constraints:** Must not be empty
* **Default:**

## Examples

### Example 1

This example demonstrates mappings where the single-character delimiter is found one or more times in the OCFL object
identifier.

#### Parameters

There is no default configuration; therefore, configuration parameters must be provided.

```json
{
"extensionName": "0006-flat-omit-prefix-storage-layout",
"delimiter": ":"
}
```

#### Mappings

| Object ID | Object Root Path |
| --- | --- |
| namespace:12887296 | `12887296` |
| urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66 | `6e8bc430-9c3a-11d9-9669-0800200c9a66` |

#### Storage Hierarchy

```
[storage_root]/
├── 0=ocfl_1.0
├── ocfl_layout.json
├── 12887296/
│ ├── 0=ocfl_object_1.0
│ ├── inventory.json
│ ├── inventory.json.sha512
│ └── v1 [...]
└── 6e8bc430-9c3a-11d9-9669-0800200c9a66/
├── 0=ocfl_object_1.0
├── inventory.json
├── inventory.json.sha512
└── v1 [...]
```

### Example 2

This example demonstrates mappings where the multi-character delimiter is found one or more times in the OCFL object
identifier.

#### Parameters

There is no default configuration; therefore, configuration parameters must be provided.

```json
{
"extensionName": "0006-flat-omit-prefix-storage-layout",
"delimiter": "edu/"
}
```

#### Mappings

| Object ID | Object Root Path |
| --- | --- |
| https://institution.edu/3448793 | `3448793` |
| https://institution.edu/abc/edu/f8.05v | `f8.05v` |

#### Storage Hierarchy

```
[storage_root]/
├── 0=ocfl_1.0
├── ocfl_layout.json
├── 3448793/
│ ├── 0=ocfl_object_1.0
│ ├── inventory.json
│ ├── inventory.json.sha512
│ └── v1 [...]
└── f8.05v/
├── 0=ocfl_object_1.0
├── inventory.json
├── inventory.json.sha512
└── v1 [...]
```

### Example 3

This example demonstrates mappings that produce directory names that are invalid on unix filesystems; therefore this
layout cannot be used in a repository that needs to be able to store objects with identifiers like these.

#### Parameters

There is no default configuration; therefore, configuration parameters must be provided.

```json
{
"extensionName": "0006-flat-omit-prefix-storage-layout",
"delimiter": "info:"
}
```

#### Mappings

| Object ID | Object Root Path |
| --- | --- |
| info:fedora/object-01 | `fedora/object-01` |
| https://example.org/info:/12345/x54xz321/s3/f8.05v | `/12345/x54xz321/s3/f8.05v` |
Loading