...
 
Commits (2)
# AniHealth Installation
Die Anwendung AniHealth dient der Sammlung von digitalen Medien + Metadaten zu Tierkrankheiten. Die Basis für die Installation und Konfigurationen dieses Systems sollte eine Linux / UNIX System sein. Die Software sollte auch unter Windows laufen, die Funktionalität wurde aber dort nicht eingehend verifiziert. Als Betriebssystem-Shell wird /bin/bash angenommen. Getestet wurde diese Anleitung unter folgenden Bedingungen:
| Tool | Version |
| ------------- |-------------|
| Betriebssystem | openSUSE Leap 15.0, Ubuntu 18.04 |
| Java | OpenJDK version "11.x“ |
| Apache Maven | 3.6.0 |
| Apache Solr | 8.4.1 |
| PostgresSQL | 10.x |
| MyCoRe | LTS 2019.06 |
| dptbase | LTS 2019.06 |
**Wichtiger Hinweis: Sollten Sie ein System mit eingeschalteter Firewall betreiben, so müssen alle konfigurierten Ports, vor allem die für den Datei-Upload, entsprechend freigeschaltet sein.**
## Vorraussetzungen
Die aktuelle Java Version inklusive Developer Kit sollte installiert sein. Das Tool Apache Maven muss im Systempfad (z.B.: /usr/local/bin) eingebunden sein. Setzen Sie folgende Umgebungsvariablen (sinnvoller Weise gleich in Ihr Profile ~/.bashrc) entsprechend Ihres Zielsystems:
`export ANIHEALTH_HOME=$HOME/{workspace}/anihealth`
`export INSTALL_HOME=$ANIHEALTH_HOME`
## SQL-Datenbank
Für den Betrieb der Anwendung ist eine professionelle SQL-Datenbank erforderlich. Standardmäßig wird PostgreSQL unterstützt, aber auch andere Datenbankmanagementsysteme wie zum Beispiel DB2 oder MySQL sind denkbar, werden in diesem Beispiel aber nicht verwendet. Legen Sie die Datenbank mit der Zeichenkodierung UTF-8 an, um Umlaute oder auch fernöstliche Schriftzeichen nutzen zu können. Die Applikation erwartet den Zugang zu einer Datenbank `mymss-demoms` unter localhost (127.0.0.1).
### Nutzung von Postgresql 10.5 unter Ubuntu 18.04
Da nicht der Superuser postgres verwendet wird, muss ein neuer Datenbanknutzer inkl. eines Passworts angelegt werden. Diese Informationen sollten mit den Zugangsdaten in der Datei config/persistence.xml übereinstimmen. Mithilfe des Schalters -d kann der Nutzer Datenbanken anlegen.
`sudo -u postgres createuser -P -d NUTZERNAME`
Die eigentliche Datenbank für den entsprechenden Nutzer wird mit folgendem Befehl angelegt und sollte den Namen aus der persistence.xml haben.
`sudo -u postgres createdb -O NUTZERNAME mymss-demoms`
Standardmäßig wird die Datenbank mit der Zeichenkodierung UTF-8 angelegt. Gestartet wird PostGreSQL nun über die üblichen Start/Stop-Skripte:
`sudo /etc/init.d/postgresql (start|stop|reload|restart)`
___
## Einrichten eines Solr-Servers
Die aktuelle Version des Framesworks Apache Solr wird benötigt. Kontrollieren Sie im Anschluss daran, unter `http://localhost:8983/solr/#/` Ihre vorgenommenen Konfigurationen. Eine allgemeingültige Installationsanweisung für MyCoRe-Anwendungen finden Sie [hier](https://www.mycore.de/documentation/getting_started/gs_solr7/).
Erstellen Sie für die Solr-Anwendung einen eigenen Ordner (z.B. solr8), unter dem Sie wiederum die Ordner data und logs anlegen.
`mkdir -p ~/solr8/data`
`mkdir -p ~/solr8/logs`
Nun können Sie sich mit den folgenden Befehlen Solr8 in den Anwendungsordner solr laden und die tar-Datei entpacken. Die Anwendung ist nun im Ordner solr-8.4.1 zu finden.
`cd ~/solr8; wget http://www-eu.apache.org/dist/lucene/solr/8.4.1/solr-8.4.1.tgz`
`tar zxf solr-8.4.1.tgz`
Um Performance-Probleme zu vermeiden, folgen Sie nun bitte der Anleitung auf der [MyCoRe-Homepage](https://www.mycore.de/documentation/search/search_solr_use/), laden Sie die benötigten ConfigSets und starten Sie Solr.
Solr liefert für die weitere Installation bereits Beispielkonfigurationen mit. Unter solr-8.1.0/server/solr muss nun die Datei solr.xml in den bereits angelegten data Ordner kopiert werden. Für die entsprechenden MyCoRe-Anwendungen, wurden die Standardkonfigurationen von MyCoRe erweitert und müssen vom Leipziger Git-Server heruntergeladen werden:
`cd ~/solr8/solr-8.4.1/server/solr/configsets`
`git clone https://git.sc.uni-leipzig.de/mycore_applications/solr_core_templates/dptbase_solr_configset_main.git`
Solr starten: `~/solr8/solr-8.4.1/bin/solr start -s ~/solr8/data -p 8983 -m 2G -v -Dsolr.log.dir=~/solr8/logs`
Solr stopen: `~/solr8/solr-8.4.1/bin/solr stop`
Nun muss noch ein core angelegt werden. Der Name muss identisch mit der Beschreibung in den jeweiligen Anwendungskonfigurationen unter `{$DEMOMS_HOME}/config/mycore.private.properties` sein. Nach der Ausführung des Kommandos sollte im Ordner data der entsprechende Unterordner angelegt sein.
Core anlegen: `~/solr8/solr-8.4.1/bin/solr create -d dptbase_solr_configset_main -c {CORE_NAME}`
Core löschen: `~/solr8/solr-8.4.1/bin/solr delete -c {CORE_NAME}`
___
## Installation des Test-Systems
Wählen Sie vom Leipziger GitLab-Server das jeweilige Programmpaket und checken Sie diese aus.
Download der Anwendung AniHealth: `git checkout https://git.sc.uni-leipzig.de/mycore_applications/collection/anihealth.git`
**Alle weiteren Beschreibungen gehen immer davon aus, dass Sie sich nun im erstellten Verzeichnis der Anwendung befinden (siehe auch angelegte Umgebungsvariabele {$ANIHEALTH_HOME}!**
### Konfiguration des Systems
Im wesentlichen sind anfangs vier Konfigurationsdateien von Interesse, für die im config-Verzeichnis entsprechende Templates vorliegen. Diese können Sie kopieren und als Vorlage nehmen.
`cd config`
`cp log4j2.xml.template log4j2.xml`
`cp persistence.xml.template persistence.xml`
`cp pom.xml.template pom.xml`
`cp mycore.private.properties.template mycore.private.properties`
Bitte beachten Sie, dass diese Dateien bei einem Software-Update nicht mit überschrieben werden. Änderungen in den Templates müssen Sie per Hand nacharbeiten. Passen Sie die angegebenen Werte (vor allem Pfade) Ihrem aktuellen Systemstand an.
**log4j2.xml:** Legt den Log-Level fest, Standard ist DEBUG, andere Log-Level (z. B. Info) können Sie hier umschalten.
**persistence.xml:** Beschreibt die Datenbank-Anbindung. Hier muss die Datenbank-Connection entsprechend oben genannten Abschnitt angegeben werden.
**pom.xml:** Enthält die Abhängigkeiten anderer Bibliotheken. Hier ist der Pfad zu dem lokalen .m2-Verzeichnis anzugeben.
**mycore.private.properties:** Hier müssen die Pfade zu den entsprechenden Dateien und OCFL Verzeichnissen substituiert werden. Weiterhin ist der lokale Solr-Server auf Funktion zu überprüfen.
### Initialisieren des Systems
Nun können Sie das System initialisieren. Im Ergebnis dessen, sollten Sie ein vollständiges, betriebsbereites und leeres System haben. Führen Sie bitte folgende Schritte durch und prüfen Sie nach jedem den Erfolg Ihrer Arbeiten. Alle Ergebnisse des Build-Prozesses stehen in dem Verzeichnis build.
führt Build-Prozess aus: `ant resolve`
legt alle erforderlichen Arbeitsverzeichnisse an: `ant create.directories`
baut alle Commandline-Scripts: `ant create.scripts`
> Die folgenden Kommandos sind nur bei der Initialisierung oder bei Bedarf auszuführen.
> konfiguriert den Core mit der core_id main: `build/cli/bin/mycore.sh reload solr configuration main in core main`
> lädt alle Klassifikationen und initialisiert ggf. die Datenbank: `ant create.class`
> initalisiert das Benutzersystem: `ant create.users`
> lädt alle Klassifikationen: `ant create.default-rules`
erzeugt Ihnen Startup-Scripts für die Nutzung der Servlet-Engine Jetty: `ant create.jetty`
erzeugt schließlich die Web-Applikation: `ant create.webapp`
Jetzt sollten Sie bereits ein auf Kommandozeilen- und Web-Ebene arbeitsfähiges System haben. Bevor Sie die Anwendung starten, können Sie noch die Beispiel-Daten laden.
### Laden der Beispieldaten
Der Distribution wurde ein Skript zum Erzeugen von Testdaten hinzugefügt (`generateSamples.lua`). (`<MCR.savedir>` muss entsprechend ersetzt werden)
Zum Ausführen wird Lua benötigt. (Getestet wurde mit einer LuaVM in der Version 5.3)
```bash
cp generateSamples.lua `<MCR.savedir>`/generateSamples.lua
cd `<MCR.savedir>`
# Zum Anpassen der Anzahl an Objekten & Wahrscheinlichkeit eines Derivates mit Bild.
vi generateSamples.lua
mkdir ULBeeHealth_{anihealth,derivate}_Server
lua generateSamples.lua
```
Die hiermit erzeugten Testdaten können mit den folgenden Befehlen in die AniHealth-Anwendung geladen werden (`<MCR.savedir>` muss wieder entsprechend ersetzt werden):
```bash
./build/bin/mycore.sh delete all objects in topological order
./build/bin/mycore.sh load all objects in topological order from directory `<MCR.savedir>`/ULBeeHealth_anihealth_server/
./build/bin/mycore.sh load all derivates from directory `<MCR.savedir>`/ULBeeHealth_derivates_server/
```
## Start der WEB-Anwendung
Nachdem das System installiert ist, kann eine Servlet-Engine gestartet werden, welche die interaktive Anwendung verwaltet.
Vorher muss jedoch noch ein SSL-Key generiert werden. Dies geht mit
`mkdir -p $HOME/.mycore/anihealth`
`openssl rand 4096 > $HOME/.mycore/anihealth/jwt.secret`
Alle Daten der Anwendung werden per Default in `$HOME/.mycore/anihealth` abgelegt.
Für das Projekt wurde ein Server namens Jetty verwendet. Dieser liegt der Distribution bei. Starten Sie einfach das Script `build/cli/bin/jettystart.sh` . Anschließend können Sie mit einem Web-Browser auf die Anwendung zugreifen, indem Sie die URL bzw. das Property `MCR.baseurl` aus der Datei `config/mycore.private.properties` nutzen. Momentan ist dies:
`http://localhost:8611/`
Der Testnutzer heißt `administrator` mit dem MyCoRe-Default-Login `alleswirdgut`.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -60,13 +60,14 @@ import org.mycore.datamodel.ifs2.MCRStoreManager;
import org.mycore.datamodel.ifs2.MCRStoredMetadata;
import org.mycore.datamodel.ifs2.MCRVersionedMetadata;
import org.mycore.datamodel.ifs2.MCRVersioningMetadataStore;
import org.mycore.datamodel.ifs2.MCROCFLVersionedMetadata;
import org.mycore.datamodel.ifs2.MCROCFLVersioningMetadataStore;
import org.mycore.datamodel.metadata.MCRObject;
import org.mycore.datamodel.metadata.MCRObjectID;
import org.mycore.datamodel.metadata.history.MCRMetadataHistoryManager;
import org.xml.sax.SAXException;
import edu.wisc.library.ocfl.api.OcflRepository;
/**
* Manages persistence of MCRObject and MCRDerivate xml metadata. Provides
* methods to create, retrieve, update and delete object metadata using IFS2
......@@ -710,17 +711,35 @@ public class MCRXMLMetadataManager {
* given type
*/
public List<String> listIDsOfType(String type) {
try (Stream<Path> streamBasePath = list(basePath)) {
return streamBasePath.flatMap(projectPath -> {
final String project = projectPath.getFileName().toString();
return list(projectPath).flatMap(typePath -> {
if (type.equals(typePath.getFileName().toString())) {
final String base = getStoryKey(project, type);
return listIDsForBase(base).stream();
}
return Stream.empty();
});
// Treat OCFL store special
if (defaultClass == MCROCFLVersioningMetadataStore.class) {
OcflRepository ocflRepo = MCROCFLRepositoryHandler.instance().getRepository();
List<String> typeIDs = ocflRepo.listObjectIds().map(id -> {
final String typeSearch = "_" + type + "_";
if (id.contains(typeSearch)) {
return (id.substring(0, id.indexOf(typeSearch) + typeSearch.length() - 1));
} else {
return "";
}
}).distinct().filter(id -> !id.equals("")).flatMap(base -> {
return listIDsForBase(base).stream();
}).collect(Collectors.toList());
for (String typeID : typeIDs)
LOGGER.info(typeID);
return typeIDs;
} else {
try (Stream<Path> streamBasePath = list(basePath)) {
return streamBasePath.flatMap(projectPath -> {
final String project = projectPath.getFileName().toString();
return list(projectPath).flatMap(typePath -> {
if (type.equals(typePath.getFileName().toString())) {
final String base = getStoryKey(project, type);
return listIDsForBase(base).stream();
}
return Stream.empty();
});
}).collect(Collectors.toList());
}
}
}
......@@ -730,15 +749,23 @@ public class MCRXMLMetadataManager {
* @return list of all mycore identifiers found in the metadata store
*/
public List<String> listIDs() {
try (Stream<Path> streamBasePath = list(basePath)) {
return streamBasePath.flatMap(projectPath -> {
final String project = projectPath.getFileName().toString();
return list(projectPath).flatMap(typePath -> {
final String type = typePath.getFileName().toString();
final String base = getStoryKey(project, type);
return listIDsForBase(base).stream();
});
}).collect(Collectors.toList());
if (defaultClass == MCROCFLVersioningMetadataStore.class) {
OcflRepository ocflRepo = MCROCFLRepositoryHandler.instance().getRepository();
List<String> IDs = ocflRepo.listObjectIds().collect(Collectors.toList());
for (String ID : IDs)
LOGGER.info(ID);
return IDs;
} else {
try (Stream<Path> streamBasePath = list(basePath)) {
return streamBasePath.flatMap(projectPath -> {
final String project = projectPath.getFileName().toString();
return list(projectPath).flatMap(typePath -> {
final String type = typePath.getFileName().toString();
final String base = getStoryKey(project, type);
return listIDsForBase(base).stream();
});
}).collect(Collectors.toList());
}
}
}
......
......@@ -96,7 +96,7 @@ public class MCROCFLVersionedMetadata extends MCRVersionedMetadata {
.containsObject(getStoreOCFL().getID() + "_" + getStoreOCFL().idLeadingZeroes(id))) {
this.path = Paths.get(getStoreOCFL().getOCFLRepositoryPath().toString(), getStoreOCFL().getOCFLRepository()
.describeObject(getStoreOCFL().getID() + "_" + getStoreOCFL().idLeadingZeroes(id)).getHeadVersion()
.getFile(getStoreOCFL().getID() + "_" + getStoreOCFL().idLeadingZeroes(id) + ".xml")
.getFile(getStoreOCFL().getID() + "_" + getStoreOCFL().idLeadingZeroes(id) + getStoreOCFL().suffix)
.getStorageRelativePath());
LOGGER.debug("Path of {} updated to {}.", getStoreOCFL().getID() + "_" + getStoreOCFL().idLeadingZeroes(id),
this.path);
......@@ -135,7 +135,7 @@ public class MCROCFLVersionedMetadata extends MCRVersionedMetadata {
try (InputStream objectAsStream = new MCRJDOMContent(xml.asXML()).getInputStream()) {
repo.updateObject(ObjectVersionId.head(objName), new CommitInfo().setMessage(
"Init on " + MCRISO8601Date.now().format("yyyy-MM-dd HH:mm:ss", Locale.GERMAN)), init -> {
init.writeFile(objectAsStream, objName + ".xml");
init.writeFile(objectAsStream, objName + getStoreOCFL().suffix);
});
} catch (SAXException e) {
throw new MCRException(e);
......@@ -161,7 +161,7 @@ public class MCROCFLVersionedMetadata extends MCRVersionedMetadata {
new CommitInfo().setMessage(
"Update on " + MCRISO8601Date.now().format("yyyy-MM-dd HH:mm:ss", Locale.GERMAN)),
init -> {
init.writeFile(objectAsStream, objName + ".xml", OcflOption.OVERWRITE);
init.writeFile(objectAsStream, objName + getStoreOCFL().suffix, OcflOption.OVERWRITE);
});
} catch (SAXException e) {
throw new MCRException(e);
......@@ -255,7 +255,7 @@ public class MCROCFLVersionedMetadata extends MCRVersionedMetadata {
.get(getStoreOCFL().getOCFLRepositoryPath().toString(),
getStoreOCFL().getOCFLRepository().describeObject(objName)
.getVersion(new VersionId(revision.get().get().longValue()))
.getFile(objName + ".xml").getStorageRelativePath())
.getFile(objName + getStoreOCFL().suffix).getStorageRelativePath())
.toFile())));
objectContent.setDocType(versionedDocType);
return objectContent;
......@@ -295,7 +295,7 @@ public class MCROCFLVersionedMetadata extends MCRVersionedMetadata {
OcflRepository repo = getStoreOCFL().getOCFLRepository();
VersionDetails version = repo.describeObject(objName).getHeadVersion();
while (objVersion == -1L) {
if (version.containsFile(objName + ".xml")) {
if (version.containsFile(objName + getStoreOCFL().suffix)) {
objVersion = Long.valueOf(version.getVersionId().toString().substring(1));
break;
}
......
......@@ -22,8 +22,14 @@ import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
......@@ -68,8 +74,8 @@ public class MCROCFLVersioningMetadataStore extends MCRVersioningMetadataStore {
}
protected void setupSVN(String type) {
LOGGER.warn ("Unsupported: void setupSVN(String type)");
}
LOGGER.warn("Unsupported: void setupSVN(String type)");
}
protected void checkRepo() {
LOGGER.info("Testing if OCFL repository is configured correctly.");
......@@ -92,9 +98,9 @@ public class MCROCFLVersioningMetadataStore extends MCRVersioningMetadataStore {
@Override
public SVNRepository getRepository() {
throw new MCRException ("Unsupported: SVNRepository getRepository()");
throw new MCRException("Unsupported: SVNRepository getRepository()");
}
/**
* Returns the OCFL repository used to manage metadata versions in this store.
*
......@@ -147,6 +153,116 @@ public class MCROCFLVersioningMetadataStore extends MCRVersioningMetadataStore {
return (MCROCFLVersionedMetadata) super.create(xml);
}
/**
* Copied from MCRStore. Overridden to digest OCFL Repository.
*
* --
*
* Lists all IDs currently used in the store, in ascending or descending order
*
* @see #ASCENDING
* @see #DESCENDING
*
* @param order the order in which IDs should be returned.
* @return all IDs currently used in the store
*/
@Override
public Iterator<Integer> listIDs(final boolean order) {
return new Iterator<Integer>() {
/**
* List of files or directories in store not yet handled
*/
List<Integer> ids = new ArrayList<Integer>();
/**
* The last ID that was returned
*/
private int lastID = 0;
/**
* The order in which the IDs should be returned, ascending or descending
*/
@SuppressWarnings("unused")
private boolean order;
@Override
public boolean hasNext() {
return ids.size() != 0;
}
@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return findNextID();
}
@Override
public void remove() {
if (lastID == 0) {
throw new IllegalStateException();
}
try {
MCROCFLVersioningMetadataStore.this.delete(lastID);
} catch (final Exception ex) {
throw new MCRException(
"Could not delete " + MCROCFLVersioningMetadataStore.this.getID() + " " + lastID, ex);
}
lastID = 0;
}
/**
* Initializes the enumeration and searches for the first ID to return
*
* @param order the return order, ascending or descending
*/
Iterator<Integer> init(final boolean order) {
this.order = order;
Stream<String> ocflIDsStream = getOCFLRepository().listObjectIds().sorted();
Iterator<String> ocflIDsIterator;
if (order == MCRStore.ASCENDING) {
ocflIDsIterator = ocflIDsStream.iterator();
} else {
ocflIDsIterator = ocflIDsStream.collect(Collectors.toCollection(LinkedList::new))
.descendingIterator();
}
String storeID = MCROCFLVersioningMetadataStore.this.getID();
for (; ocflIDsIterator.hasNext();) {
String ocflID = ocflIDsIterator.next();
if (ocflID.contains(storeID + "_")) {
try {
int id = Integer.parseInt(ocflID.substring(storeID.length() + 1));
if (!retrieve(id).isDeleted()) {
ids.add(id);
}
} catch (IOException e) {
LOGGER.error("Failed to retrieve {}: {}", ocflID, e);
break;
}
}
}
return this;
}
/**
* Finds the next ID used in the store.
*
* @return the next ID, or 0 if there is no other ID any more
*/
private int findNextID() {
if (!hasNext()) {
return 0;
}
lastID = ids.remove(0);
return lastID;
}
}.init(order);
}
/**
* Returns the metadata stored under the given ID, or null. Note that this
* metadata may not exist currently in the store, it may be a deleted version,
......@@ -158,9 +274,9 @@ public class MCROCFLVersioningMetadataStore extends MCRVersioningMetadataStore {
*/
@Override
public MCRVersionedMetadata retrieve(int id) throws IOException {
LOGGER.debug("Retrieving {}_{} ({}).", getID(), idLeadingZeroes(id), id);
MCROCFLVersionedMetadata requestedMetadata = new MCROCFLVersionedMetadata(this, getSlot(id), id, super.forceDocType,
false);
LOGGER.debug("Retrieving {} ({}).", getObjectName(id), id);
MCROCFLVersionedMetadata requestedMetadata = new MCROCFLVersionedMetadata(this, getSlot(id), id,
super.forceDocType, false);
LOGGER.debug("Retrieved metadata object: {}", requestedMetadata);
LOGGER.debug("Contained metadata: {}", requestedMetadata.getMetadata().asString());
return requestedMetadata;
......@@ -168,7 +284,7 @@ public class MCROCFLVersioningMetadataStore extends MCRVersioningMetadataStore {
@Override
public void delete(int id) throws IOException {
delete(getID() + "_" + idLeadingZeroes(id));
delete(getObjectName(id));
}
public void delete(String objName) {
......@@ -177,14 +293,14 @@ public class MCROCFLVersioningMetadataStore extends MCRVersioningMetadataStore {
} else if (MCROCFLRepositoryHandler.instance().getAbsoluteDelete()) {
repo.purgeObject(objName);
} else {
if (!repo.describeObject(objName).getHeadVersion().containsFile(objName + ".xml")) {
if (!repo.describeObject(objName).getHeadVersion().containsFile(objName + this.suffix)) {
throw new MCRUsageException("Object does not exist (anymore?): " + objName);
} else {
repo.updateObject(ObjectVersionId.head(objName),
new CommitInfo().setMessage(
"Delete on " + MCRISO8601Date.now().format("yyyy-MM-dd HH:mm:ss", Locale.GERMAN)),
init -> {
init.removeFile(objName + ".xml");
init.removeFile(objName + this.suffix);
});
}
}
......@@ -199,7 +315,7 @@ public class MCROCFLVersioningMetadataStore extends MCRVersioningMetadataStore {
*/
@Override
public boolean exists(final int id) throws IOException {
return repo.containsObject(getID() + "_" + idLeadingZeroes(id));
return repo.containsObject(getObjectName(id));
}
protected MCROCFLVersionedMetadata buildMetadataObject(int id) {
......@@ -218,4 +334,17 @@ public class MCROCFLVersioningMetadataStore extends MCRVersioningMetadataStore {
return formatter.format(id);
}
public Path getObjectPath(int id) {
return getObjectPath(getObjectName(id));
}
public Path getObjectPath(String objName) {
return Paths.get(getOCFLRepositoryPath().toString(), getOCFLRepository().describeObject(objName)
.getHeadVersion().getFile(objName + this.suffix).getStorageRelativePath());
}
public String getObjectName(int id) {
return getID() + "_" + idLeadingZeroes(id);
}
}