Skip to content
Open
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 @@ -59,9 +59,9 @@
* <li> The property suffix {@link MCRXMappingClassificationGeneratorBase#EVALUATOR_KEY} can be used to
* specify the evaluator used to obtain category IDs from.
* <li> For the evaluator, the property suffix {@link MCRSentinel#DEFAULT_KEY} can be used to
* exclude the evaluator from the configuration and use a default {@link MCRSimpleXMappingEvaluator} instead.
* exclude the evaluator from the configuration and use a default {@link MCRSimpleXMappingEvaluator} instead.
* <li> The property suffix {@link MCRXMappingClassificationGeneratorBase#ON_MISSING_MAPPED_CATEGORY_KEY} can be used to
* specify the behaviour, when a mapped category ID is missing.
* specify the behavior, when a mapped category ID is missing.
* </ul>
* Example:
* <pre><code>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@

/**
* A {@link MCRXPathClassificationMappingCondition} is a {@link Condition} that
* determines the condition of a MyCoRe object based on a configurable X-Path evaluating the
* determines the condition of a MyCoRe object based on a configurable XPath evaluating the
* XML representation of the MyCoRe object.
* <p>
* The following configuration options are available:
* <ul>
* <li> The property suffix {@link MCRXPathClassificationMappingCondition#X_PATH_KEY} can be used to
* specify the X-Path to be used.
* specify the XPath to be used.
* </ul>
* Example:
* <pre><code>
Expand All @@ -53,7 +53,7 @@ public final class MCRXPathClassificationMappingCondition implements Condition {
private final String xPath;

public MCRXPathClassificationMappingCondition(String xPath) {
this.xPath = Objects.requireNonNull(xPath, "X-Path must not be null");
this.xPath = Objects.requireNonNull(xPath, "XPath must not be null");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@
* <li> The property suffix {@link MCRXMappingClassificationGeneratorBase#EVALUATOR_KEY} can be used to
* specify the evaluator used to obtain category IDs from.
* <li> For the evaluator, the property suffix {@link MCRSentinel#DEFAULT_KEY} can be used to
* exclude the evaluator from the configuration and use a default {@link MCRSimpleXMappingEvaluator} instead.
* exclude the evaluator from the configuration and use a default {@link MCRSimpleXMappingEvaluator} instead.
* <li> The property suffix {@link MCRXMappingClassificationGeneratorBase#ON_MISSING_MAPPED_CATEGORY_KEY} can be used to
* specify the behaviour, when a mapped classification value is missing.
* specify the behavior, when a mapped classification value is missing.
* </ul>
* Example:
* <pre><code>
Expand Down
438 changes: 189 additions & 249 deletions mycore-pi/src/main/java/org/mycore/pi/MCRGenericPIGenerator.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.mycore.access.MCRAccessException;
import org.mycore.common.MCRException;
Expand All @@ -45,24 +44,32 @@ protected void handleObjectUpdated(MCREvent evt, MCRObject obj) {
private void processPIServices(MCRObject obj) {
List<MCRPIRegistrationInfo> registered = MCRPIManager.getInstance().getRegistered(obj);

final List<String> services = registered.stream().map(MCRPIRegistrationInfo::getService)
.collect(Collectors.toList());
final List<String> services = registered.stream().map(MCRPIRegistrationInfo::getService).toList();

List<MCRPIService<MCRPersistentIdentifier>> listOfServicesWithCreatablePIs = MCRPIServiceManager
.getInstance().getAutoCreationList().stream()
.filter(Predicate.not(s -> services.contains(s.getServiceID())))
.filter(Predicate.not(s -> MCRPIService.hasFlag(obj, "", s)))
.filter(s -> s.getCreationPredicate().test(obj))
.collect(Collectors.toList());
List<MCRPIService<MCRPersistentIdentifier>> autoCreatingPIServices = MCRPIServiceManager
.getInstance().getAutoCreationList();

listOfServicesWithCreatablePIs
.forEach((serviceToRegister) -> {
boolean mayCreatePI = true;
while (mayCreatePI) {

mayCreatePI = false;

List<MCRPIService<MCRPersistentIdentifier>> listOfServicesWithCreatablePIs = autoCreatingPIServices.stream()
.filter(Predicate.not(s -> services.contains(s.getServiceID())))
.filter(Predicate.not(s -> MCRPIService.hasFlag(obj, "", s)))
.filter(s -> s.getCreationPredicate().test(obj))
.toList();

for (MCRPIService<MCRPersistentIdentifier> serviceToRegister : listOfServicesWithCreatablePIs) {
try {
serviceToRegister.register(obj, "", false);
mayCreatePI = true;
} catch (MCRAccessException | MCRPersistentIdentifierException | ExecutionException
| InterruptedException e) {
throw new MCRException("Error while register pi for object " + obj.getId().toString(), e);
}
});
}
}

}
}
46 changes: 3 additions & 43 deletions mycore-pi/src/main/java/org/mycore/pi/MCRPIGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,59 +18,19 @@

package org.mycore.pi;

import static org.mycore.pi.MCRPIService.GENERATOR_CONFIG_PREFIX;

import java.util.Map;

import org.mycore.common.config.MCRConfigurationException;
import org.mycore.common.config.annotation.MCRPostConstruction;
import org.mycore.common.config.annotation.MCRRawProperties;
import org.mycore.datamodel.metadata.MCRBase;
import org.mycore.pi.exceptions.MCRPersistentIdentifierException;

public abstract class MCRPIGenerator<T extends MCRPersistentIdentifier> {

private String generatorID;

private Map<String, String> properties;

public final Map<String, String> getProperties() {
return properties;
}

@MCRPostConstruction
public void init(String property) {
generatorID = property.substring(GENERATOR_CONFIG_PREFIX.length());
}

@MCRRawProperties(namePattern = "*", required = false)
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public interface MCRPIGenerator<T extends MCRPersistentIdentifier> {

/**
* generates a {@link MCRPersistentIdentifier}
*
* @param mcrBase the mycore object for which the identifier is generated
* @param base the mycore object for which the identifier is generated
* @param additional additional information dedicated to the object like a mcrpath
* @return a unique persistence identifier
* @throws MCRPersistentIdentifierException if something goes wrong while generating
*/
public abstract T generate(MCRBase mcrBase, String additional) throws MCRPersistentIdentifierException;

/**
* checks if the property exists and throws a exception if not.
* @param propertyName to check
* @throws MCRConfigurationException if property does not exist
*/
protected void checkPropertyExists(final String propertyName) throws MCRConfigurationException {
if (!getProperties().containsKey(propertyName)) {
throw new MCRConfigurationException(
"Missing property " + GENERATOR_CONFIG_PREFIX + getGeneratorID() + "." + propertyName);
}
}
T generate(MCRBase base, String additional) throws MCRPersistentIdentifierException;

public String getGeneratorID() {
return generatorID;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,96 +18,127 @@

package org.mycore.pi.doi;

import java.util.Comparator;
import static org.mycore.pi.util.MCRPIGeneratorUtils.formatCount;
import static org.mycore.pi.util.MCRPIGeneratorUtils.getCountPattern;
import static org.mycore.pi.util.MCRPIGeneratorUtils.getCreateDate;
import static org.mycore.pi.util.MCRPIGeneratorUtils.readCountFromDatabase;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.function.Supplier;
import java.util.regex.Pattern;

import org.mycore.common.MCRException;
import org.mycore.common.MCRPersistenceException;
import org.mycore.common.config.MCRConfiguration2;
import org.mycore.common.config.annotation.MCRConfigurationProxy;
import org.mycore.common.config.annotation.MCRProperty;
import org.mycore.datamodel.common.MCRISO8601Date;
import org.mycore.datamodel.metadata.MCRBase;
import org.mycore.datamodel.metadata.MCRObjectService;
import org.mycore.pi.MCRPIGenerator;
import org.mycore.pi.MCRPIManager;
import org.mycore.pi.MCRPIRegistrationInfo;
import org.mycore.pi.exceptions.MCRPersistentIdentifierException;

/**
* {@link MCRCreateDateDOIGenerator} is a {@link MCRPIGenerator} for {@link MCRDigitalObjectIdentifier} identifiers
* that generates identifiers using a given prefix and the current date and a per-date counter for the suffix.
* <p>
* The following configuration options are available:
* <ul>
* <li> The property suffix {@link MCRCreateDateDOIGenerator#DATE_FORMAT_KEY} can be used to
* specify the date format to be used (optional, defaults to {@link MCRCreateDateDOIGenerator#DEFAULT_DATE_FORMAT}).
* <li> The property suffix {@link MCRCreateDateDOIGenerator#PREFIX_KEY} can be used to
* specify the prefix.
* <li> The property suffix {@link MCRCreateDateDOIGenerator#COUNT_PRECISION_KEY} can be used to
* specify number of digits to be used for the count (optional, defaults to <code>-1</code>,
* which uses the natural number of digits).
* </ul>
* Example:
* <pre><code>
* [...].Class=org.mycore.pi.doi.MCRCreateDateDOIGenerator
* [...].DateFormat=yyyy-MM-dd
* [...].Prefix=10.1234
* [...].CountPrecision=6
* </code></pre>
*/
@MCRConfigurationProxy(proxyClass = MCRCreateDateDOIGenerator.Factory.class)
public class MCRCreateDateDOIGenerator extends MCRDOIGeneratorBase {

public static final String DEFAULT_DATE_FORMAT = "yyyyMMdd-HHmmss";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just deprecate that thing, because MCRGenericPIGenerator can do that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, I will make this a small agenda item in today's web-call. Either

  • keep MCRCreateDateDOIGenerator and add MCRCreateDateDNBURNGenerator or
  • deprecate MCRCreateDateDOIGenerator and drop MCRCreateDateDNBURNGenerator

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not true actually, MCRGenericPIGenerator only supports the current date, not the object creation date.


public class MCRCreateDateDOIGenerator extends MCRPIGenerator<MCRDigitalObjectIdentifier> {
public static final Locale DEFAULT_DATE_LOCALE = Locale.ENGLISH;

private static final String DATE_PATTERN = "yyyyMMdd-HHmmss";
public static final String DATE_FORMAT_KEY = "DateFormat";

private static final String CREATE_DATE_PLACEHOLDER = "$createDate$";
public static final String PREFIX_KEY = "Prefix";

private static final String DATE_REGEXP = CREATE_DATE_PLACEHOLDER + "-([0-9]+)";
public static final String COUNT_PRECISION_KEY = "CountPrecision";

private static final Map<String, AtomicInteger> PATTERN_COUNT_MAP = new HashMap<>();

private final MCRDOIParser mcrdoiParser;
private final String dateFormat;

private final String prefix = MCRConfiguration2.getStringOrThrow("MCR.DOI.Prefix");
private final String prefix;

public MCRCreateDateDOIGenerator() {
super();
mcrdoiParser = new MCRDOIParser();
private final int countPrecision;

private final String countPattern;

public MCRCreateDateDOIGenerator(MCRDOIParser parser, String dateFormat, String prefix,
int countPrecision) {
super(parser);
this.dateFormat = Objects.requireNonNull(dateFormat, "Date format must not be null");
this.prefix = Objects.requireNonNull(prefix, "Prefix must not be null");
this.countPrecision = countPrecision;
this.countPattern = getCountPattern(countPrecision);
}

@Override
public MCRDigitalObjectIdentifier generate(MCRBase mcrObj, String additional) {
Date createdate = mcrObj.getService().getDate(MCRObjectService.DATE_TYPE_CREATEDATE);
if (createdate != null) {
MCRISO8601Date mcrdate = new MCRISO8601Date();
mcrdate.setDate(createdate);
String createDate = mcrdate.format(DATE_PATTERN, Locale.ENGLISH);
final int count = getCountForCreateDate(createDate);
Optional<MCRDigitalObjectIdentifier> parse = mcrdoiParser.parse(prefix + "/" + createDate + "-" + count);
return parse.orElseThrow(() -> new MCRException("Error while parsing default doi!"));
} else {
throw new MCRPersistenceException("The object " + mcrObj.getId() + " doesn't have a createdate!");
}
protected String buildDOI(MCRBase base, String additional) throws MCRPersistentIdentifierException {

String prefixWithDate = prefix + "/" + formatDate(getCreateDate(base)) + "-";
int count = getCount(Pattern.quote(prefixWithDate) + countPattern);

return prefixWithDate + formatCount(count, countPrecision);

}

private int getCountForCreateDate(String createDate) {
return getCount(prefix + "/" + DATE_REGEXP.replace(CREATE_DATE_PLACEHOLDER, createDate));
private String formatDate(Date date) {

MCRISO8601Date isoDate = new MCRISO8601Date();
isoDate.setDate(date);

return isoDate.format(dateFormat, DEFAULT_DATE_LOCALE);

}

private synchronized int getCount(final String pattern) {
AtomicInteger count = PATTERN_COUNT_MAP.computeIfAbsent(pattern, p -> {
Pattern regExpPattern = Pattern.compile(p);
Predicate<String> matching = regExpPattern.asPredicate();

List<MCRPIRegistrationInfo> list = MCRPIManager.getInstance()
.getList(MCRDigitalObjectIdentifier.TYPE, -1, -1);

Comparator<Integer> integerComparator = Integer::compareTo;
// extract the number of the PI
Optional<Integer> highestNumber = list.stream()
.map(MCRPIRegistrationInfo::getIdentifier)
.filter(matching)
.map(pi -> {
// extract the number of the PI
Matcher matcher = regExpPattern.matcher(pi);
if (matcher.find() && matcher.groupCount() == 1) {
String group = matcher.group(1);
return Integer.parseInt(group, 10);
} else {
return null;
}
}).filter(Objects::nonNull)
.max(integerComparator)
.map(n -> n + 1);
return new AtomicInteger(highestNumber.orElse(0));
});

return count.getAndIncrement();
return PATTERN_COUNT_MAP
.computeIfAbsent(pattern, p -> readCountFromDatabase(MCRDigitalObjectIdentifier.TYPE, p))
.getAndIncrement();
}

public static class Factory implements Supplier<MCRCreateDateDOIGenerator> {

@MCRProperty(name = DATE_FORMAT_KEY, required = false)
public String dateFormat;

@MCRProperty(name = PREFIX_KEY, defaultName = "MCR.DOI.Prefix")
public String prefix;

@MCRProperty(name = COUNT_PRECISION_KEY, required = false)
public String countPrecision = "-1";

@Override
public MCRCreateDateDOIGenerator get() {
return new MCRCreateDateDOIGenerator(new MCRDOIParser(), getDateFormat(), prefix,
Integer.parseInt(countPrecision));
}

private String getDateFormat() {
return dateFormat != null ? dateFormat : DEFAULT_DATE_FORMAT;
}

}

}
Loading
Loading