Coverage Summary for Class: ImportService (org.kitodo.production.services.data)
Class |
Method, %
|
Line, %
|
ImportService |
70,2%
(40/57)
|
58,1%
(332/571)
|
ImportService$1 |
100%
(1/1)
|
100%
(1/1)
|
Total |
70,7%
(41/58)
|
58,2%
(333/572)
|
/*
* (c) Kitodo. Key to digital objects e. V. <contact@kitodo.org>
*
* This file is part of the Kitodo project.
*
* It is licensed under GNU General Public License version 3 or later.
*
* For the full copyright and license information, please read the
* GPL3-License.txt file that was distributed with this source code.
*/
package org.kitodo.production.services.data;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kitodo.api.MdSec;
import org.kitodo.api.Metadata;
import org.kitodo.api.MetadataEntry;
import org.kitodo.api.dataeditor.rulesetmanagement.FunctionalMetadata;
import org.kitodo.api.dataeditor.rulesetmanagement.MetadataViewInterface;
import org.kitodo.api.dataeditor.rulesetmanagement.RulesetManagementInterface;
import org.kitodo.api.dataeditor.rulesetmanagement.StructuralElementViewInterface;
import org.kitodo.api.dataformat.Workpiece;
import org.kitodo.api.externaldatamanagement.DataImport;
import org.kitodo.api.externaldatamanagement.ExternalDataImportInterface;
import org.kitodo.api.externaldatamanagement.ImportConfigurationType;
import org.kitodo.api.externaldatamanagement.SearchInterfaceType;
import org.kitodo.api.externaldatamanagement.SearchResult;
import org.kitodo.api.schemaconverter.DataRecord;
import org.kitodo.api.schemaconverter.ExemplarRecord;
import org.kitodo.api.schemaconverter.FileFormat;
import org.kitodo.api.schemaconverter.MetadataFormat;
import org.kitodo.api.schemaconverter.SchemaConverterInterface;
import org.kitodo.config.ConfigCore;
import org.kitodo.config.ConfigProject;
import org.kitodo.config.enums.ParameterCore;
import org.kitodo.data.database.beans.ImportConfiguration;
import org.kitodo.data.database.beans.MappingFile;
import org.kitodo.data.database.beans.Process;
import org.kitodo.data.database.beans.Ruleset;
import org.kitodo.data.database.beans.SearchField;
import org.kitodo.data.database.beans.Task;
import org.kitodo.data.database.beans.Template;
import org.kitodo.data.database.beans.UrlParameter;
import org.kitodo.data.database.beans.User;
import org.kitodo.data.database.enums.TaskEditType;
import org.kitodo.data.database.enums.TaskStatus;
import org.kitodo.data.database.exceptions.DAOException;
import org.kitodo.data.exceptions.DataException;
import org.kitodo.exceptions.CatalogException;
import org.kitodo.exceptions.CommandException;
import org.kitodo.exceptions.ConfigException;
import org.kitodo.exceptions.DoctypeMissingException;
import org.kitodo.exceptions.ImportException;
import org.kitodo.exceptions.InvalidMetadataValueException;
import org.kitodo.exceptions.NoRecordFoundException;
import org.kitodo.exceptions.NoSuchMetadataFieldException;
import org.kitodo.exceptions.ParameterNotFoundException;
import org.kitodo.exceptions.ProcessGenerationException;
import org.kitodo.exceptions.UnsupportedFormatException;
import org.kitodo.production.dto.ProcessDTO;
import org.kitodo.production.forms.createprocess.ProcessBooleanMetadata;
import org.kitodo.production.forms.createprocess.ProcessDetail;
import org.kitodo.production.forms.createprocess.ProcessFieldedMetadata;
import org.kitodo.production.forms.createprocess.ProcessSelectMetadata;
import org.kitodo.production.forms.createprocess.ProcessTextMetadata;
import org.kitodo.production.helper.Helper;
import org.kitodo.production.helper.ProcessHelper;
import org.kitodo.production.helper.TempProcess;
import org.kitodo.production.helper.XMLUtils;
import org.kitodo.production.metadata.MetadataEditor;
import org.kitodo.production.process.ProcessGenerator;
import org.kitodo.production.process.ProcessValidator;
import org.kitodo.production.services.ServiceManager;
import org.kitodo.production.workflow.KitodoNamespaceContext;
import org.kitodo.serviceloader.KitodoServiceLoader;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class ImportService {
private static final Logger logger = LogManager.getLogger(ImportService.class);
public static final String ACQUISITION_STAGE_CREATE = "create";
private static volatile ImportService instance = null;
private static ExternalDataImportInterface importModule;
private static final String KITODO_NAMESPACE = "http://meta.kitodo.org/v1/";
private static final String KITODO_STRING = "kitodo";
private ProcessGenerator processGenerator;
private static final String REPLACE_ME = "REPLACE_ME";
// default value for identifierMetadata if no OPAC specific metadata has been configured in kitodo_opac.xml
private static final String PARENT_XPATH = "//kitodo:metadata[@name='" + REPLACE_ME + "']";
private static final String PARENTHESIS_TRIM_MODE = "parenthesis";
private LinkedList<ExemplarRecord> exemplarRecords;
private static final String PERSON = "Person";
private static final String ROLE = "Role";
private static final String AUTHOR = "Author";
private static final String FIRST_NAME = "FirstName";
private static final String LAST_NAME = "LastName";
private static final String MONOGRAPH = "Monograph";
private static final String VOLUME = "Volume";
private static final String MULTI_VOLUME_WORK = "MultiVolumeWork";
private String tiffDefinition = "";
private boolean usingTemplates;
private TempProcess parentTempProcess;
private static final String CATALOG_IDENTIFIER = "CatalogIDDigital";
private static final String SRU_OPERATION = "operation";
private static final String SRU_SEARCH_RETRIEVE = "searchRetrieve";
private static final String SRU_VERSION = "version";
private static final String SRU_RECORD_SCHEMA = "recordSchema";
private static final String OAI_VERB = "verb";
private static final String OAI_GET_RECORD = "GetRecord";
private static final String OAI_METADATA_PREFIX = "metadataPrefix";
/**
* Return singleton variable of type ImportService.
*
* @return unique instance of ImportService
*/
public static ImportService getInstance() {
ImportService localReference = instance;
if (Objects.isNull(localReference)) {
synchronized (ImportService.class) {
localReference = instance;
if (Objects.isNull(localReference)) {
localReference = new ImportService();
instance = localReference;
}
}
}
return localReference;
}
/**
* Load ExternalDataImportInterface implementation with KitodoServiceLoader and perform given query string
* with loaded module.
*
* @param searchField field to query
* @param searchTerm given search term
* @param importConfiguration ImportConfiguration to use
* @param start index of first record returned
* @param rows number of records returned
* @return search result
*/
public SearchResult performSearch(String searchField, String searchTerm, ImportConfiguration importConfiguration,
int start, int rows) {
importModule = initializeImportModule();
searchTerm = getSearchTermWithDelimiter(searchTerm, importConfiguration);
return importModule.search(createDataImportFromImportConfiguration(importConfiguration), searchField,
searchTerm, start, rows);
}
private ExternalDataImportInterface initializeImportModule() {
KitodoServiceLoader<ExternalDataImportInterface> loader =
new KitodoServiceLoader<>(ExternalDataImportInterface.class);
return loader.loadModule();
}
/**
* Load search fields from provided ImportConfiguration and return them as a list of Strings.
*
* @param importConfiguration ImportConfiguration to use
* @return list containing search fields
*/
public List<String> getAvailableSearchFields(ImportConfiguration importConfiguration) {
try {
if (SearchInterfaceType.FTP.name().equals(importConfiguration.getInterfaceType())) {
// FTP server do not support query parameters but only use the filename for OPAC search!
return Collections.singletonList(Helper.getTranslation("filename"));
} else if (SearchInterfaceType.OAI.name().equals(importConfiguration.getInterfaceType())) {
// OAI PMH interfaces do not support query parameters but only use the ID of the record to retrieve it!
return Collections.singletonList(Helper.getTranslation("recordId"));
} else {
List<String> fields = new ArrayList<>();
List<SearchField> searchFields = importConfiguration.getSearchFields();
if (Objects.nonNull(searchFields)) {
for (SearchField searchField : searchFields) {
if (!searchField.isDisplayed()) {
continue;
}
fields.add(searchField.getLabel());
}
}
return fields;
}
} catch (IllegalArgumentException e) {
logger.error(e.getLocalizedMessage());
throw new IllegalArgumentException("Error retrieving search fields from ImportConfiguration '"
+ importConfiguration.getTitle() + "': " + e.getMessage());
}
}
/**
* Retrieve default search field label of given ImportConfiguration.
*
* @param importConfiguration ImportConfiguration
* @return label of default search field
*/
public static String getDefaultSearchField(ImportConfiguration importConfiguration) {
if (SearchInterfaceType.FTP.name().equals(importConfiguration.getInterfaceType())) {
return Helper.getTranslation("filename");
} else if (SearchInterfaceType.OAI.name().equals(importConfiguration.getInterfaceType())) {
return Helper.getTranslation("recordId");
} else if (Objects.nonNull(importConfiguration.getDefaultSearchField())) {
return importConfiguration.getDefaultSearchField().getLabel();
} else if (!importConfiguration.getSearchFields().isEmpty()) {
return importConfiguration.getSearchFields().get(0).getLabel();
}
return "";
}
/**
* Check and return whether to skip hit list for given ImportConfiguration and search field or not.
* Hit list is skipped either if SearchInterfaceType of given ImportConfiguration does not support
* hit lists (e.g. OAI interfaces in their current implementation) or if the provided search field
* equals the ID search field of the given ImportConfiguration.
* @param configuration ImportConfiguration to check
* @param field value of SearchField to check
* @return whether to skip hit list or not
*/
public static boolean skipHitlist(ImportConfiguration configuration, String field) {
if (SearchInterfaceType.FTP.name().equals(configuration.getInterfaceType())) {
return false;
}
else if (SearchInterfaceType.OAI.name().equals(configuration.getInterfaceType())
|| field.equals(configuration.getIdSearchField().getLabel())) {
return true;
}
return (Objects.isNull(configuration.getMetadataRecordIdXPath())
|| Objects.isNull(configuration.getMetadataRecordTitleXPath()));
}
/**
* Get default import depth for given import configuration.
*
* @param importConfiguration ImportConfiguration
* @return default import depth of given import configuration
*/
public int getDefaultImportDepth(ImportConfiguration importConfiguration) {
int depth = importConfiguration.getDefaultImportDepth();
if (depth < 0 || depth > 5) {
return 2;
} else {
return depth;
}
}
private LinkedList<ExemplarRecord> extractExemplarRecords(DataRecord record,
ImportConfiguration importConfiguration)
throws XPathExpressionException,
ParserConfigurationException, SAXException, IOException {
LinkedList<ExemplarRecord> exemplarRecords = new LinkedList<>();
String exemplarXPath = importConfiguration.getItemFieldXpath();
String ownerXPath = importConfiguration.getItemFieldOwnerSubPath();
String signatureXPath = importConfiguration.getItemFieldSignatureSubPath();
if (!StringUtils.isBlank(exemplarXPath) && !StringUtils.isBlank(ownerXPath)
&& !StringUtils.isBlank(signatureXPath) && record.getOriginalData() instanceof String) {
String xmlString = (String) record.getOriginalData();
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new KitodoNamespaceContext());
Document doc = XMLUtils.parseXMLString(xmlString);
NodeList exemplars = (NodeList) xPath.compile(exemplarXPath).evaluate(doc, XPathConstants.NODESET);
for (int i = 0; i < exemplars.getLength(); i++) {
Node exemplar = exemplars.item(i);
Node ownerNode = (Node) xPath.compile(ownerXPath).evaluate(exemplar, XPathConstants.NODE);
Node signatureNode = (Node) xPath.compile(signatureXPath).evaluate(exemplar, XPathConstants.NODE);
if (Objects.nonNull(ownerNode) && Objects.nonNull(signatureNode)) {
String owner = ownerNode.getTextContent();
String signature = signatureNode.getTextContent();
if (!StringUtils.isBlank(owner) && !StringUtils.isBlank(signature)) {
exemplarRecords.add(new ExemplarRecord(owner, signature));
}
}
}
}
return exemplarRecords;
}
/**
* Iterate over "SchemaConverterInterface" implementations using KitodoServiceLoader and return
* first implementation that supports the Metadata and File formats of the given DataRecord object
* as source formats and the Kitodo internal format and XML as target formats, respectively.
*
* @param record
* Record whose metadata and return formats are used to filter the SchemaConverterInterface implementations
*
* @return List of SchemaConverterInterface implementations that support the metadata and return formats of the
* given Record.
*
* @throws UnsupportedFormatException when no SchemaConverter module with matching formats could be found
*/
private SchemaConverterInterface getSchemaConverter(DataRecord record) throws UnsupportedFormatException {
KitodoServiceLoader<SchemaConverterInterface> loader =
new KitodoServiceLoader<>(SchemaConverterInterface.class);
List<SchemaConverterInterface> converterModules = loader.loadModules().stream()
.filter(converter -> converter.supportsSourceFileFormat(record.getFileFormat())
&& converter.supportsTargetFileFormat(FileFormat.XML))
.collect(Collectors.toList());
if (converterModules.isEmpty()) {
throw new UnsupportedFormatException("No SchemaConverter found that supports '"
+ record.getMetadataFormat() + "' and '" + record.getFileFormat() + "'!");
}
return converterModules.get(0);
}
/**
* Get docType form imported record.
* @param record imported record
* as Document
* @return docType as String
*/
private String getRecordDocType(Document record, Ruleset ruleset) throws IOException {
Collection<String> doctypes = getDocTypeMetadata(ruleset);
Element root = record.getDocumentElement();
NodeList kitodoNodes = root.getElementsByTagNameNS(KITODO_NAMESPACE, KITODO_STRING);
if (kitodoNodes.getLength() > 0 && !doctypes.isEmpty()) {
NodeList importedMetadata = kitodoNodes.item(0).getChildNodes();
for (int i = 0; i < importedMetadata.getLength(); i++) {
Node metadataNode = importedMetadata.item(i);
Element metadataElement = (Element) metadataNode;
if (doctypes.contains(metadataElement.getAttribute("name"))) {
return metadataElement.getTextContent();
}
}
}
return "";
}
/**
* Get the parent ID from the document.
* @param document Document to parse
* @param higherLevelIdentifier the given identifier
* @param trimMode trim mode for parent id
* @return parent ID
*/
public String getParentID(Document document, String higherLevelIdentifier, String trimMode)
throws XPathExpressionException {
XPath parentIDXpath = XPathFactory.newInstance().newXPath();
parentIDXpath.setNamespaceContext(new KitodoNamespaceContext());
NodeList nodeList = (NodeList) parentIDXpath.compile(PARENT_XPATH.replace(REPLACE_ME, higherLevelIdentifier))
.evaluate(document, XPathConstants.NODESET);
if (nodeList.getLength() == 1) {
Node parentIDNode = nodeList.item(0);
if (PARENTHESIS_TRIM_MODE.equals(trimMode)) {
return parentIDNode.getTextContent().replaceAll("\\([^)]+\\)", "");
} else {
return parentIDNode.getTextContent();
}
} else {
return null;
}
}
/**
* Creates a temporary Process from the given document with templateID und projectID.
* @param document the given document
* @param templateID the template to use
* @param projectID the project to use
* @return a temporary process
*/
public TempProcess createTempProcessFromDocument(ImportConfiguration importConfiguration, Document document,
int templateID, int projectID)
throws ProcessGenerationException, IOException, TransformerException, InvalidMetadataValueException,
NoSuchMetadataFieldException {
Process process = null;
// "processGenerator" needs to be initialized when function is called for the first time
if (Objects.isNull(processGenerator)) {
processGenerator = new ProcessGenerator();
}
if (processGenerator.generateProcess(templateID, projectID)) {
process = processGenerator.getGeneratedProcess();
}
TempProcess tempProcess;
if (importConfiguration.getPrestructuredImport()) {
// logical structure is created by import XSLT file!
Workpiece workpiece = ServiceManager.getMetsService().loadWorkpiece(document);
tempProcess = new TempProcess(process, workpiece);
} else if (Objects.nonNull(process) && Objects.nonNull(process.getRuleset())) {
String docType = getRecordDocType(document, process.getRuleset());
NodeList metadataNodes = extractMetadataNodeList(document);
tempProcess = new TempProcess(process, metadataNodes, docType);
} else {
throw new ProcessGenerationException("Ruleset missing!");
}
return tempProcess;
}
private String importProcessAndReturnParentID(String recordId, LinkedList<TempProcess> allProcesses,
ImportConfiguration importConfiguration, int projectID,
int templateID, boolean isParentInRecord, String parentIdMetadata)
throws IOException, ProcessGenerationException, XPathExpressionException, ParserConfigurationException,
NoRecordFoundException, UnsupportedFormatException, URISyntaxException, SAXException, TransformerException,
InvalidMetadataValueException, NoSuchMetadataFieldException {
Document internalDocument = importDocument(importConfiguration, recordId, allProcesses.isEmpty(), isParentInRecord);
TempProcess tempProcess = createTempProcessFromDocument(importConfiguration, internalDocument, templateID, projectID);
// Workaround for classifying MultiVolumeWorks with insufficient information
if (!allProcesses.isEmpty()) {
String childDocType = allProcesses.getLast().getWorkpiece().getLogicalStructure().getType();
Workpiece workpiece = tempProcess.getWorkpiece();
if (Objects.nonNull(workpiece) && Objects.nonNull(workpiece.getLogicalStructure())) {
String docType = workpiece.getLogicalStructure().getType();
if ((MONOGRAPH.equals(childDocType) || VOLUME.equals(childDocType)) && MONOGRAPH.equals(docType)) {
tempProcess.getWorkpiece().getLogicalStructure().setType(MULTI_VOLUME_WORK);
allProcesses.getFirst().getWorkpiece().getLogicalStructure().setType(VOLUME);
}
}
}
allProcesses.add(tempProcess);
if (!isParentInRecord && StringUtils.isNotBlank(parentIdMetadata)) {
return getParentID(internalDocument, parentIdMetadata,importConfiguration.getParentElementTrimMode());
}
return null;
}
/**
* Returns the searchTerm with configured Delimiter.
* @param searchTerm the search term to add delimiters.
* @param importConfiguration the ImportConfiguration to use
* @return searchTermWithDelimiter
*/
public String getSearchTermWithDelimiter(String searchTerm, ImportConfiguration importConfiguration) {
String searchTermWithDelimiter = searchTerm;
String queryDelimiter = importConfiguration.getQueryDelimiter();
if (Objects.nonNull(queryDelimiter)) {
searchTermWithDelimiter = queryDelimiter + searchTermWithDelimiter + queryDelimiter;
}
return searchTermWithDelimiter;
}
/**
* Import a record identified by the given ID 'recordId'.
* Additionally, import all ancestors of the given process referenced in the original data of the process imported
* from the OPAC selected in the given CreateProcessForm instance.
* Return the list of processes as a LinkedList of TempProcess.
*
* @param recordId identifier of the process to import
* @param importConfiguration ImportConfiguration used to import the record
* @param projectId the ID of the project for which a process is created
* @param templateId the ID of the template from which a process is created
* @param importDepth the number of hierarchical processes that will be imported from the catalog
* @param parentIdMetadata names of Metadata types holding parent IDs of structure elements in internal format
* @return List of TempProcess
*/
public LinkedList<TempProcess> importProcessHierarchy(String recordId, ImportConfiguration importConfiguration,
int projectId, int templateId, int importDepth,
Collection<String> parentIdMetadata)
throws IOException, ProcessGenerationException, XPathExpressionException, ParserConfigurationException,
NoRecordFoundException, UnsupportedFormatException, URISyntaxException, SAXException, DAOException,
TransformerException, InvalidMetadataValueException, NoSuchMetadataFieldException {
importModule = initializeImportModule();
processGenerator = new ProcessGenerator();
LinkedList<TempProcess> processes = new LinkedList<>();
String parentMetadataKey = "";
if (parentIdMetadata.isEmpty()) {
if (importDepth > 1) {
Helper.setErrorMessage("newProcess.catalogueSearch.parentIDMetadataMissing");
importDepth = 1;
}
} else {
parentMetadataKey = parentIdMetadata.toArray()[0].toString();
}
String parentID = importProcessAndReturnParentID(recordId, processes, importConfiguration, projectId,
templateId, false, parentMetadataKey);
Template template = ServiceManager.getTemplateService().getById(templateId);
if (Objects.isNull(template.getRuleset())) {
throw new ProcessGenerationException("Ruleset of template " + template.getId() + " is null!");
}
importParents(recordId, importConfiguration, projectId, templateId, importDepth, processes, parentID, template,
parentMetadataKey);
ListIterator<TempProcess> processesIterator = processes.listIterator();
while (processesIterator.hasNext()) {
int fromIndex = processesIterator.nextIndex() + 1;
List<TempProcess> parents = new ArrayList<>();
if (fromIndex < processes.size()) {
parents = processes.subList(fromIndex, processes.size());
}
ProcessHelper.generateAtstslFields(processesIterator.next(), parents, ACQUISITION_STAGE_CREATE, false);
}
return processes;
}
private void importParents(String recordId, ImportConfiguration importConfiguration, int projectId, int templateId,
int importDepth, LinkedList<TempProcess> processes, String parentID, Template template,
String parentIdMetadata)
throws ProcessGenerationException, IOException, XPathExpressionException, ParserConfigurationException,
NoRecordFoundException, UnsupportedFormatException, URISyntaxException, SAXException, DAOException,
InvalidMetadataValueException, NoSuchMetadataFieldException {
int level = 1;
this.parentTempProcess = null;
while (Objects.nonNull(parentID) && level < importDepth) {
try {
Process parentProcess = loadParentProcess(template.getRuleset(), projectId, parentID);
if (Objects.isNull(parentProcess)) {
if (Objects.nonNull(importConfiguration.getParentMappingFile())) {
parentID = importProcessAndReturnParentID(recordId, processes, importConfiguration, projectId,
templateId, true, parentIdMetadata);
} else {
parentID = importProcessAndReturnParentID(parentID, processes, importConfiguration, projectId,
templateId, false, parentIdMetadata);
}
level++;
} else {
logger.info("Process with ID '{}' already in database. Stop hierarchical import.", parentID);
URI workpieceUri = ServiceManager.getProcessService().getMetadataFileUri(parentProcess);
Workpiece parentWorkpiece = ServiceManager.getMetsService().loadWorkpiece(workpieceUri);
this.parentTempProcess = new TempProcess(parentProcess, parentWorkpiece);
break;
}
} catch (SAXParseException | DAOException | TransformerException e) {
// this happens for example if a document is part of a "Virtueller Bestand" in
// Kalliope for which a
// proper "record" is not returned from its SRU interface
logger.error(e.getLocalizedMessage());
break;
}
}
// always try to find a parent for last imported process (e.g. level ==
// importDepth) in the database!
if (Objects.nonNull(parentID) && level == importDepth) {
checkForParent(parentID, template.getRuleset(), projectId);
}
}
/**
* Check if there already is a parent process in Database.
*/
public void checkForParent(String parentID, Ruleset ruleset, int projectID)
throws DAOException, IOException, ProcessGenerationException {
if (Objects.isNull(parentID)) {
this.parentTempProcess = null;
return;
}
Process parentProcess = loadParentProcess(ruleset, projectID, parentID);
if (Objects.nonNull(parentProcess)) {
logger.info("Linking last imported process to parent process with ID {} in database!", parentID);
URI workpieceUri = ServiceManager.getProcessService().getMetadataFileUri(parentProcess);
Workpiece parentWorkpiece = ServiceManager.getMetsService().loadWorkpiece(workpieceUri);
this.parentTempProcess = new TempProcess(parentProcess, parentWorkpiece);
return;
}
this.parentTempProcess = null;
}
private List<DataRecord> searchChildRecords(ImportConfiguration config, String parentId, int numberOfRows) {
SearchField parenIDSearchField = config.getParentSearchField();
if (Objects.isNull(parenIDSearchField)) {
throw new ConfigException("Unable to find parent ID search field for catalog '" + config.getTitle() + "'!");
}
return importModule.getMultipleFullRecordsFromQuery(createDataImportFromImportConfiguration(config),
parenIDSearchField.getLabel(), parentId, numberOfRows);
}
/**
* Get number of child records of record with ID 'parentId' from catalog 'opac'.
*
* @param importConfiguration ImportConfiguration to use
* @param parentId ID of the parent record
* @return number of child records
*/
public int getNumberOfChildren(ImportConfiguration importConfiguration, String parentId) {
SearchField parentIDSearchField = importConfiguration.getParentSearchField();
if (Objects.isNull(parentIDSearchField)) {
throw new ConfigException("Unable to find parent ID search field for catalog '"
+ importConfiguration.getTitle() + "'!");
}
SearchResult searchResult = performSearch(parentIDSearchField.getLabel(), parentId, importConfiguration, 0, 0);
if (Objects.nonNull(searchResult)) {
return searchResult.getNumberOfHits();
} else {
Helper.setErrorMessage("Error retrieving number of children for record with ID " + parentId + " from OPAC "
+ importConfiguration.getTitle() + "!");
return 0;
}
}
/**
* Search child records of record with ID 'elementID' from catalog 'opac', transform them into a list of
* 'TempProcess' and return the list.
*
* @param importConfiguration ImportConfiguration to use
* @param elementID ID of record for which child records are retrieved
* @param projectId ID of project for which processes are created
* @param templateId ID of template with which processes are created
* @param rows number of child records to retrieve from catalog
* @param parentProcesses parent processes of the children
* @return list of TempProcesses containing the retrieved child records.
*/
public LinkedList<TempProcess> getChildProcesses(ImportConfiguration importConfiguration, String elementID,
int projectId, int templateId, int rows, List<TempProcess> parentProcesses)
throws SAXException, UnsupportedFormatException, URISyntaxException, ParserConfigurationException,
NoRecordFoundException, IOException, ProcessGenerationException, TransformerException,
InvalidMetadataValueException, NoSuchMetadataFieldException {
importModule = initializeImportModule();
List<DataRecord> childRecords = searchChildRecords(importConfiguration, elementID, rows);
LinkedList<TempProcess> childProcesses = new LinkedList<>();
if (!childRecords.isEmpty()) {
SchemaConverterInterface converter = getSchemaConverter(childRecords.get(0));
List<File> mappingFiles = getMappingFiles(importConfiguration);
for (DataRecord childRecord : childRecords) {
DataRecord internalRecord = converter.convert(childRecord, MetadataFormat.KITODO, FileFormat.XML, mappingFiles);
Document childDocument = XMLUtils.parseXMLString((String)internalRecord.getOriginalData());
TempProcess tempProcess = createTempProcessFromDocument(importConfiguration, childDocument,
templateId, projectId);
ProcessHelper.generateAtstslFields(tempProcess, parentProcesses, ACQUISITION_STAGE_CREATE, false);
childProcesses.add(tempProcess);
}
// TODO: sort child processes (by what? catalog ID? Signature?)
return childProcesses;
} else {
throw new NoRecordFoundException("No child records found for data record with ID '" + elementID
+ "' in OPAC '" + importConfiguration.getTitle() + "'!");
}
}
private Document importDocument(ImportConfiguration importConfiguration, String identifier,
boolean extractExemplars, boolean isParentInRecord)
throws NoRecordFoundException, UnsupportedFormatException, URISyntaxException, IOException,
XPathExpressionException, ParserConfigurationException, SAXException, ProcessGenerationException {
// ################ IMPORT #################
importModule = initializeImportModule();
DataRecord dataRecord = importModule.getFullRecordById(
createDataImportFromImportConfiguration(importConfiguration),
getSearchTermWithDelimiter(identifier, importConfiguration));
if (extractExemplars) {
exemplarRecords = extractExemplarRecords(dataRecord, importConfiguration);
}
return convertDataRecordToInternal(dataRecord, importConfiguration, isParentInRecord);
}
/**
* Converts a given dataRecord to an internal document.
* @param dataRecord the dataRecord to convert.
* @param importConfiguration the import configuration to use
* @param isParentInRecord if parentRecord is in childRecord
* @return the converted Document
*/
public Document convertDataRecordToInternal(DataRecord dataRecord, ImportConfiguration importConfiguration,
boolean isParentInRecord)
throws UnsupportedFormatException, URISyntaxException, IOException, ParserConfigurationException,
SAXException, XPathExpressionException, ProcessGenerationException {
SchemaConverterInterface converter = getSchemaConverter(dataRecord);
List<File> mappingFiles = getMappingFiles(importConfiguration, isParentInRecord);
// transform dataRecord to Kitodo internal format using appropriate SchemaConverter!
File debugFolder = ConfigCore.getKitodoDebugDirectory();
if (Objects.nonNull(debugFolder)) {
FileUtils.writeStringToFile(new File(debugFolder, "catalogRecord.xml"),
(String) dataRecord.getOriginalData(), StandardCharsets.UTF_8);
}
DataRecord internalRecord = converter.convert(dataRecord, MetadataFormat.KITODO, FileFormat.XML, mappingFiles);
if (Objects.nonNull(debugFolder)) {
FileUtils.writeStringToFile(new File(debugFolder, "internalRecord.xml"),
(String) internalRecord.getOriginalData(), StandardCharsets.UTF_8);
}
if (!(internalRecord.getOriginalData() instanceof String)) {
throw new UnsupportedFormatException("Original metadata of internal record has to be an XML String, '"
+ internalRecord.getOriginalData().getClass().getName() + "' found!");
}
Document resultDocument = null;
try {
resultDocument = XMLUtils.parseXMLString((String) internalRecord.getOriginalData());
} catch (SAXParseException e) {
String interfaceName = importConfiguration.getInterfaceType();
if (Arrays.stream(SearchInterfaceType.values()).anyMatch(sit -> sit.name().equals(interfaceName))) {
SearchInterfaceType searchInterfaceType = SearchInterfaceType.valueOf(interfaceName);
String errorMessageXpath = searchInterfaceType.getErrorMessageXpath();
if (Objects.nonNull(errorMessageXpath) && dataRecord.getOriginalData() instanceof String) {
Element originalDocument = XMLUtils.parseXMLString((String) dataRecord.getOriginalData()).getDocumentElement();
String errorMessage = XPathFactory.newInstance().newXPath().evaluate(errorMessageXpath, originalDocument);
if (StringUtils.isNotBlank(errorMessage)) {
errorMessage = interfaceName.toUpperCase() + " error: '" + errorMessage + "'";
throw new CatalogException(errorMessage);
}
}
} else {
throw e;
}
}
if (Objects.isNull(resultDocument)) {
throw new ProcessGenerationException(Helper.getTranslation("importError.emptyDocument"));
}
return resultDocument;
}
private NodeList extractMetadataNodeList(Document document) throws ProcessGenerationException {
NodeList kitodoNodes = document.getElementsByTagNameNS(KITODO_NAMESPACE, KITODO_STRING);
if (kitodoNodes.getLength() != 1) {
throw new ProcessGenerationException("Number of 'kitodo' nodes unequal to '1' => unable to generate process!");
}
Node kitodoNode = kitodoNodes.item(0);
return kitodoNode.getChildNodes();
}
private List<File> getMappingFiles(ImportConfiguration importConfiguration, boolean forParentInRecord)
throws URISyntaxException {
List<File> mappingFiles = new ArrayList<>();
List<String> mappingFileNames;
try {
if (forParentInRecord) {
mappingFileNames = Collections.singletonList(importConfiguration.getParentMappingFile().getFile());
} else {
mappingFileNames = importConfiguration.getMappingFiles().stream().map(MappingFile::getFile)
.collect(Collectors.toList());
}
for (String mappingFileName : mappingFileNames) {
URI xsltFile = Paths.get(ConfigCore.getParameter(ParameterCore.DIR_XSLT)).toUri()
.resolve(new URI(mappingFileName.trim()));
mappingFiles.add(ServiceManager.getFileService().getFile(xsltFile));
}
} catch (IllegalArgumentException e) {
logger.error(e.getMessage());
}
return mappingFiles;
}
private List<File> getMappingFiles(ImportConfiguration importConfiguration) throws URISyntaxException {
return getMappingFiles(importConfiguration, false);
}
/**
* Get the value of a specific processDetail in the processDetails.
*
* @param processDetail
* as ProcessDetail
* @return the value as a java.lang.String
*/
public static String getProcessDetailValue(ProcessDetail processDetail) {
String value = "";
if (processDetail instanceof ProcessTextMetadata) {
return ((ProcessTextMetadata) processDetail).getValue();
} else if (processDetail instanceof ProcessBooleanMetadata) {
return String.valueOf(((ProcessBooleanMetadata) processDetail).isActive());
} else if (processDetail instanceof ProcessSelectMetadata) {
return String.join(", ", ((ProcessSelectMetadata) processDetail).getSelectedItems());
} else if (processDetail instanceof ProcessFieldedMetadata && processDetail.getMetadataID().equals(PERSON)) {
value = getCreator(((ProcessFieldedMetadata) processDetail).getRows());
}
return value;
}
/**
* Set the value of a specific process detail in processDetails.
* @param processDetail the specific process detail whose value should be set to the param value
* as ProcessDetail
* @param value
* as a java.lang.String
*/
public static void setProcessDetailValue(ProcessDetail processDetail, String value) {
if (processDetail instanceof ProcessTextMetadata) {
// TODO: incorporate "initstart" and "initend" values from kitodo_projects.xml like AddtionalField!
((ProcessTextMetadata) processDetail).setValue(value);
} else if (processDetail instanceof ProcessBooleanMetadata) {
((ProcessBooleanMetadata) processDetail).setActive(Boolean.parseBoolean(value));
} else if (processDetail instanceof ProcessSelectMetadata) {
((ProcessSelectMetadata) processDetail).setSelectedItem(value);
}
}
/**
* Get all creators names.
* @param processDetailsList the list of elements in processDetails
* as a list of processDetail
* @return all creators names as a String
*/
public static String getListOfCreators(List<ProcessDetail> processDetailsList) {
String listofAuthors = "";
for (ProcessDetail detail : processDetailsList) {
if (detail instanceof ProcessFieldedMetadata
&& PERSON.equals(detail.getMetadataID())) {
ProcessFieldedMetadata tableRow = (ProcessFieldedMetadata) detail;
for (ProcessDetail detailsTableRow : tableRow.getRows()) {
if (ROLE.equals(detailsTableRow.getMetadataID())
&& AUTHOR.equals(getProcessDetailValue(detailsTableRow))) {
listofAuthors = listofAuthors.concat(getCreator(tableRow.getRows()));
break;
}
}
}
}
return listofAuthors;
}
private static String getCreator(List<ProcessDetail> processDetailList) {
String author = "";
for (ProcessDetail detail : processDetailList) {
String detailMetadataID = detail.getMetadataID();
String detailValue = getProcessDetailValue(detail);
if ((FIRST_NAME.equals(detailMetadataID)
|| LAST_NAME.equals(detailMetadataID))
&& !StringUtils.isBlank(detailValue)) {
author = author.concat(detailValue);
}
}
return author;
}
/**
* Prepare.
* @param projectTitle
* title of the project
* @throws IOException when trying to create a 'ConfigProject' instance.
* @throws DoctypeMissingException when trying to load TifDefinition fails
*/
public void prepare(String projectTitle) throws IOException, DoctypeMissingException {
ConfigProject configProject = new ConfigProject(projectTitle);
usingTemplates = configProject.isUseTemplates();
tiffDefinition = configProject.getTifDefinition();
}
/**
* Get useTemplate.
*
* @return value of useTemplate
*/
public boolean isUsingTemplates() {
return usingTemplates;
}
/**
* Set useTemplate.
*
* @param usingTemplates as boolean
*/
public void setUsingTemplates(boolean usingTemplates) {
this.usingTemplates = usingTemplates;
}
/**
* Get tiffDefinition.
*
* @return value of tifDefinition
*/
public String getTiffDefinition() {
return tiffDefinition;
}
/**
* Get exemplarRecords.
*
* @return value of exemplarRecords
*/
public LinkedList<ExemplarRecord> getExemplarRecords() {
return exemplarRecords;
}
/**
* Set selected exemplar record data.
* @param exemplarRecord
* selected exemplar record
* @param importConfiguration
* ImportConfiguration
* @param metadata
* list of metadata fields
* @throws ParameterNotFoundException if a parameter required for exemplar record extraction is missing
*/
public static void setSelectedExemplarRecord(ExemplarRecord exemplarRecord, ImportConfiguration importConfiguration,
List<ProcessDetail> metadata) throws ParameterNotFoundException {
String ownerMetadataName = importConfiguration.getItemFieldOwnerMetadata();
String signatureMetadataName = importConfiguration.getItemFieldSignatureMetadata();
if (StringUtils.isBlank(ownerMetadataName)) {
throw new ParameterNotFoundException("ownerMetadata");
} else if (StringUtils.isBlank(signatureMetadataName)) {
throw new ParameterNotFoundException("signatureMetadata");
}
for (ProcessDetail processDetail : metadata) {
if (ownerMetadataName.equals(processDetail.getMetadataID())) {
ImportService.setProcessDetailValue(processDetail, exemplarRecord.getOwner());
} else if (signatureMetadataName.equals(processDetail.getMetadataID())) {
ImportService.setProcessDetailValue(processDetail, exemplarRecord.getSignature());
}
}
}
/**
* Get parentTempProcess.
*
* @return value of parentTempProcess
*/
public TempProcess getParentTempProcess() {
return parentTempProcess;
}
private Process loadParentProcess(Ruleset ruleset, int projectId, String parentId)
throws ProcessGenerationException, DAOException, IOException {
Process parentProcess = null;
for (String identifierMetadata : getFunctionalMetadata(ruleset, FunctionalMetadata.RECORD_IDENTIFIER)) {
if (Objects.isNull(parentProcess)) {
HashMap<String, String> parentIDMetadata = new HashMap<>();
parentIDMetadata.put(identifierMetadata, parentId);
try {
for (ProcessDTO processDTO : ServiceManager.getProcessService().findByMetadata(parentIDMetadata, true)) {
Process process = ServiceManager.getProcessService().getById(processDTO.getId());
if (Objects.isNull(process.getRuleset()) || Objects.isNull(process.getRuleset().getId())) {
throw new ProcessGenerationException("Ruleset or ruleset ID of potential parent process "
+ process.getId() + " is null!");
}
if (process.getProject().getId() == projectId
&& process.getRuleset().getId().equals(ruleset.getId())) {
parentProcess = process;
break;
}
}
} catch (DataException e) {
logger.error(e.getLocalizedMessage());
}
}
}
return parentProcess;
}
/**
* Check and return whether the "parentIdSearchField" is configured in the current ImportConfiguration.
*
* @param importConfiguration name of the OPAC to check
* @return whether "parentIdSearchField" is configured for current ImportConfiguration
* @throws ConfigException thrown if configuration for OPAC 'catalogName' could not be found
*/
public boolean isParentIdSearchFieldConfigured(ImportConfiguration importConfiguration) throws ConfigException {
return Objects.nonNull(importConfiguration.getParentSearchField());
}
/**
* Ensure all processes in given list 'tempProcesses' have a non empty title.
*
* @param tempProcesses list of TempProcesses to be checked
* @return whether a title was changed or not
* @throws IOException if the meta.xml file of a process could not be loaded
*/
public static boolean ensureNonEmptyTitles(LinkedList<TempProcess> tempProcesses) throws IOException {
boolean changedTitle = false;
for (TempProcess tempProcess : tempProcesses) {
Process process = tempProcess.getProcess();
if (Objects.nonNull(process) && StringUtils.isEmpty(process.getTitle())) {
// FIXME:
// if metadataFileUri is null or no meta.xml can be found, the tempProcess has not
// yet been saved to disk and contains the workpiece directly, instead!
URI metadataFileUri = ServiceManager.getProcessService().getMetadataFileUri(process);
Workpiece workpiece = ServiceManager.getMetsService().loadWorkpiece(metadataFileUri);
Collection<Metadata> metadata = workpiece.getLogicalStructure().getMetadata();
String processTitle = "[" + Helper.getTranslation("process") + " " + process.getId() + "]";
for (Metadata metadatum : metadata) {
if (CATALOG_IDENTIFIER.equals(metadatum.getKey())) {
processTitle = ((MetadataEntry) metadatum).getValue();
}
}
process.setTitle(processTitle);
changedTitle = true;
}
}
return changedTitle;
}
/**
* Process list of child processes.
*
* @param mainProcess main process to which list of child processes are attached
* @param childProcesses list of child processes that are attached to the main process
* @throws DataException thrown if saving a process fails
* @throws InvalidMetadataValueException thrown if process workpiece contains invalid metadata
* @throws NoSuchMetadataFieldException thrown if process workpiece contains undefined metadata
* @throws ProcessGenerationException thrown if process title cannot be created
*/
public static void processProcessChildren(Process mainProcess, LinkedList<TempProcess> childProcesses,
RulesetManagementInterface managementInterface, String acquisitionStage,
List<Locale.LanguageRange> priorityList)
throws DataException, InvalidMetadataValueException, NoSuchMetadataFieldException,
ProcessGenerationException, IOException {
for (TempProcess tempProcess : childProcesses) {
if (Objects.isNull(tempProcess) || Objects.isNull(tempProcess.getProcess())) {
logger.error("Child process {} is null => Skip!", childProcesses.indexOf(tempProcess) + 1);
continue;
}
processTempProcess(tempProcess, managementInterface, acquisitionStage, priorityList, null);
Process childProcess = tempProcess.getProcess();
ServiceManager.getProcessService().save(childProcess, true);
ProcessService.setParentRelations(mainProcess, childProcess);
}
}
/**
* Add workpiece and template properties to given Process 'process'.
*
* @param tempProcess
* TempProcess that will be processed
* @param template
* Template of process
* @param processDetails
* metadata of process
* @param docType
* String containing document type
* @param imageDescription
* String containing image description
*/
public static void addProperties(TempProcess tempProcess, Template template, List<ProcessDetail> processDetails,
String docType, String imageDescription) {
Process process = tempProcess.getProcess();
addMetadataProperties(processDetails, process);
ProcessGenerator.addPropertyForWorkpiece(process, "TSL/ATS", tempProcess.getAtstsl());
ProcessGenerator.addPropertyForWorkpiece(process, "DocType", docType);
ProcessGenerator.addPropertyForWorkpiece(process, "TifHeaderImagedescription", imageDescription);
ProcessGenerator.addPropertyForWorkpiece(process, "TifHeaderDocumentname", process.getTitle());
if (Objects.nonNull(template)) {
ProcessGenerator.addPropertyForProcess(process, "Template", template.getTitle());
ProcessGenerator.addPropertyForProcess(process, "TemplateID", String.valueOf(template.getId()));
}
}
private static void addMetadataProperties(List<ProcessDetail> processDetailList, Process process) {
try {
for (ProcessDetail processDetail : processDetailList) {
Collection<Metadata> processMetadata = processDetail.getMetadataWithFilledValues();
if (!processMetadata.isEmpty() && processMetadata.toArray()[0] instanceof Metadata) {
String metadataValue = ImportService.getProcessDetailValue(processDetail);
Metadata metadata = (Metadata) processMetadata.toArray()[0];
if (Objects.nonNull(metadata.getDomain())) {
switch (metadata.getDomain()) {
case DMD_SEC:
ProcessGenerator.addPropertyForWorkpiece(process, processDetail.getLabel(), metadataValue);
break;
case SOURCE_MD:
ProcessGenerator.addPropertyForTemplate(process, processDetail.getLabel(), metadataValue);
break;
case TECH_MD:
ProcessGenerator.addPropertyForProcess(process, processDetail.getLabel(), metadataValue);
break;
default:
logger.info("Don't save metadata '{}' with domain '{}' to property.",
processDetail.getMetadataID(), metadata.getDomain());
break;
}
} else {
ProcessGenerator.addPropertyForWorkpiece(process, processDetail.getLabel(), metadataValue);
}
}
}
} catch (InvalidMetadataValueException e) {
logger.error(e.getLocalizedMessage());
}
}
/**
* Update tasks of given Process 'process'.
*
* @param process Process whose tasks are updated
*/
public static void updateTasks(Process process) {
for (Task task : process.getTasks()) {
task.setProcessingTime(process.getCreationDate());
task.setEditType(TaskEditType.AUTOMATIC);
if (task.getProcessingStatus() == TaskStatus.DONE) {
task.setProcessingBegin(process.getCreationDate());
Date date = new Date();
task.setProcessingTime(date);
task.setProcessingEnd(date);
}
}
}
/**
* Process given TempProcess 'tempProcess' by creating the metadata, doc type and properties for the process and
* updating the process' tasks.
*
* @param tempProcess TempProcess that will be processed
* @param managementInterface RulesetManagementInterface to create metadata and tiff header
* @param acquisitionStage String containing the acquisition stage
* @param priorityList List of LanguageRange objects
* @throws InvalidMetadataValueException thrown if the process contains invalid metadata
* @throws NoSuchMetadataFieldException thrown if the process contains undefined metadata
* @throws ProcessGenerationException thrown if process title could not be generated
*/
public static void processTempProcess(TempProcess tempProcess, RulesetManagementInterface managementInterface,
String acquisitionStage, List<Locale.LanguageRange> priorityList, TempProcess parentTempProcess)
throws InvalidMetadataValueException, NoSuchMetadataFieldException, ProcessGenerationException,
IOException {
List<ProcessDetail> processDetails = ProcessHelper.transformToProcessDetails(tempProcess, managementInterface,
acquisitionStage, priorityList);
String docType = tempProcess.getWorkpiece().getLogicalStructure().getType();
List<TempProcess> parentTempProcesses = new ArrayList<>();
if (Objects.nonNull(parentTempProcess)) {
parentTempProcesses.add(parentTempProcess);
}
ProcessHelper.generateAtstslFields(tempProcess, processDetails, parentTempProcesses, docType,
managementInterface, acquisitionStage, priorityList);
if (!ProcessValidator.isProcessTitleCorrect(tempProcess.getProcess().getTitle())) {
throw new ProcessGenerationException("Unable to create process");
}
Process process = tempProcess.getProcess();
process.setSortHelperImages(tempProcess.getGuessedImages());
addProperties(tempProcess, tempProcess.getProcess().getTemplate(), processDetails, docType,
tempProcess.getProcess().getTitle());
ProcessService.checkTasks(process, docType);
updateTasks(process);
}
/**
* Imports a process and saves it to database.
* @param ppn the ppn to import
* @param projectId the projectId
* @param templateId the templateId
* @param importConfiguration the selected import configuration
* @param presetMetadata Map containing preset metadata with keys as metadata keys and values as metadata values
* @return the importedProcess
*/
public Process importProcess(String ppn, int projectId, int templateId, ImportConfiguration importConfiguration,
Map<String, String> presetMetadata) throws ImportException {
LinkedList<TempProcess> processList = new LinkedList<>();
TempProcess tempProcess;
Template template;
try {
template = ServiceManager.getTemplateService().getById(templateId);
String parentMetadataKey = "";
List<String> higherLevelIdentifiers = new ArrayList<>(
getHigherLevelIdentifierMetadata(template.getRuleset()));
if (!higherLevelIdentifiers.isEmpty()) {
parentMetadataKey = higherLevelIdentifiers.get(0);
}
final String parentId = importProcessAndReturnParentID(ppn, processList, importConfiguration, projectId,
templateId, false, parentMetadataKey);
setParentProcess(parentId, projectId, template);
tempProcess = processList.get(0);
String metadataLanguage = ServiceManager.getUserService().getCurrentUser().getMetadataLanguage();
tempProcess.getWorkpiece().getLogicalStructure().getMetadata().addAll(createMetadata(presetMetadata));
processTempProcess(tempProcess, ServiceManager.getRulesetService().openRuleset(template.getRuleset()),
"create", Locale.LanguageRange.parse(metadataLanguage.isEmpty() ? "en" : metadataLanguage),
parentTempProcess);
String title = tempProcess.getProcess().getTitle();
String validateRegEx = ConfigCore.getParameterOrDefaultValue(ParameterCore.VALIDATE_PROCESS_TITLE_REGEX);
if (StringUtils.isBlank(title)) {
throw new ProcessGenerationException(Helper.getTranslation("processTitleEmpty"));
} else if (!title.matches(validateRegEx)) {
throw new ProcessGenerationException(Helper.getTranslation("processTitleInvalid", title));
} else if (ServiceManager.getProcessService().findNumberOfProcessesWithTitle(title) > 0) {
throw new ProcessGenerationException(Helper.getTranslation("processTitleAlreadyInUse", title));
}
ServiceManager.getProcessService().save(tempProcess.getProcess(), true);
URI processBaseUri = ServiceManager.getFileService().createProcessLocation(tempProcess.getProcess());
tempProcess.getProcess().setProcessBaseUri(processBaseUri);
OutputStream out = ServiceManager.getFileService()
.write(ServiceManager.getProcessService().getMetadataFileUri(tempProcess.getProcess()));
tempProcess.getWorkpiece().setId(tempProcess.getProcess().getId().toString());
ServiceManager.getMetsService().save(tempProcess.getWorkpiece(), out);
linkToParent(tempProcess);
ServiceManager.getProcessService().save(tempProcess.getProcess());
} catch (DAOException | IOException | ProcessGenerationException | XPathExpressionException
| ParserConfigurationException | NoRecordFoundException | UnsupportedFormatException
| URISyntaxException | SAXException | InvalidMetadataValueException | NoSuchMetadataFieldException
| DataException | CommandException | TransformerException | CatalogException e) {
logger.error(e);
throw new ImportException(e.getLocalizedMessage());
}
return tempProcess.getProcess();
}
private void linkToParent(TempProcess tempProcess) throws DAOException, ProcessGenerationException, IOException {
if (Objects.nonNull(parentTempProcess) && Objects.nonNull(parentTempProcess.getProcess())) {
URI parentProcessUri = ServiceManager.getProcessService()
.getMetadataFileUri(parentTempProcess.getProcess());
Workpiece workpiece = ServiceManager.getMetsService().loadWorkpiece(parentProcessUri);
if (Objects.isNull(workpiece)) {
throw new ProcessGenerationException("Workpiece of parent process is null!");
}
MetadataEditor.addLink(workpiece.getLogicalStructure(), tempProcess.getProcess().getId());
try (OutputStream outputStream = ServiceManager.getFileService().write(parentProcessUri)) {
ServiceManager.getMetsService().save(workpiece, outputStream);
}
ProcessService.setParentRelations(parentTempProcess.getProcess(), tempProcess.getProcess());
}
}
private void setParentProcess(String parentId, int projectId, Template template)
throws DAOException, IOException, ProcessGenerationException {
parentTempProcess = null;
if (StringUtils.isNotBlank(parentId)) {
checkForParent(parentId, template.getRuleset(), projectId);
}
}
private static Collection<String> getFunctionalMetadata(Ruleset ruleset, FunctionalMetadata metadata)
throws IOException {
RulesetManagementInterface rulesetManagement = ServiceManager.getRulesetManagementService()
.getRulesetManagement();
String rulesetDir = ConfigCore.getParameter(ParameterCore.DIR_RULESETS);
String rulesetPath = Paths.get(rulesetDir, ruleset.getFile()).toString();
rulesetManagement.load(new File(rulesetPath));
return rulesetManagement.getFunctionalKeys(metadata);
}
private List<MetadataEntry> createMetadata(Map<String, String> presetMetadata) {
List<MetadataEntry> metadata = new LinkedList<>();
for (Map.Entry<String, String> presetMetadataEntry : presetMetadata.entrySet()) {
MetadataEntry metadataEntry = new MetadataEntry();
metadataEntry.setKey(presetMetadataEntry.getKey());
metadataEntry.setValue(presetMetadataEntry.getValue());
metadataEntry.setDomain(MdSec.DMD_SEC);
metadata.add(metadataEntry);
}
return metadata;
}
/**
* Load doc type metadata keys from provided ruleset.
* @param ruleset Ruleset from which doc type metadata keys are loaded and returned
* @return list of Strings containing the IDs of the doc type metadata defined in the provided ruleset.
* @throws IOException thrown if ruleset file cannot be loaded
*/
public static Collection<String> getDocTypeMetadata(Ruleset ruleset) throws IOException {
return getFunctionalMetadata(ruleset, FunctionalMetadata.DOC_TYPE);
}
/**
* Load and return higher level identifier metadata keys from provided ruleset.
* @param ruleset Ruleset from which higher level identifier metadata keys are loaded and returned
* @return list of String containing the keys of metadata defined as higher level identifier
* @throws IOException thrown if ruleset file cannot be loaded
*/
public static Collection<String> getHigherLevelIdentifierMetadata(Ruleset ruleset) throws IOException {
return getFunctionalMetadata(ruleset, FunctionalMetadata.HIGHERLEVEL_IDENTIFIER);
}
private DataImport createDataImportFromImportConfiguration(ImportConfiguration importConfiguration) {
String configType = importConfiguration.getConfigurationType();
if (!ImportConfigurationType.OPAC_SEARCH.name().equals(configType)) {
throw new ConfigException("Configuration error: given import configuration '"
+ importConfiguration.getTitle() + "' is of type '" + configType
+ "' (OPAC_SEARCH expected instead)!");
}
DataImport dataImport = new DataImport();
dataImport.setTitle(importConfiguration.getTitle());
dataImport.setSearchInterfaceType(SearchInterfaceType.valueOf(importConfiguration.getInterfaceType()));
dataImport.setReturnFormat(FileFormat.valueOf(importConfiguration.getReturnFormat()));
dataImport.setMetadataFormat(MetadataFormat.valueOf(importConfiguration.getMetadataFormat()));
dataImport.setScheme(importConfiguration.getScheme());
dataImport.setHost(importConfiguration.getHost());
dataImport.setPath(importConfiguration.getPath());
if (Objects.nonNull(importConfiguration.getPort())) {
dataImport.setPort(importConfiguration.getPort());
}
dataImport.setIdPrefix(importConfiguration.getIdPrefix());
dataImport.setUsername(importConfiguration.getUsername());
dataImport.setPassword(importConfiguration.getPassword());
dataImport.setAnonymousAccess(importConfiguration.isAnonymousAccess());
if (Objects.nonNull(importConfiguration.getIdSearchField())) {
dataImport.setIdParameter(importConfiguration.getIdSearchField().getValue());
}
HashMap<String, String> searchFields = new HashMap<>();
for (SearchField searchField : importConfiguration.getSearchFields()) {
searchFields.put(searchField.getLabel(), searchField.getValue());
}
dataImport.setSearchFields(searchFields);
dataImport.setUrlParameters(getUrlParameters(importConfiguration));
dataImport.setRecordIdXPath(importConfiguration.getMetadataRecordIdXPath());
dataImport.setRecordTitleXPath(importConfiguration.getMetadataRecordTitleXPath());
return dataImport;
}
private HashMap<String, String> getUrlParameters(ImportConfiguration importConfiguration) {
HashMap<String, String> urlParameters = new HashMap<>();
if (SearchInterfaceType.SRU.name().equals(importConfiguration.getInterfaceType())) {
urlParameters.put(SRU_OPERATION, SRU_SEARCH_RETRIEVE);
if (Objects.isNull(importConfiguration.getSruVersion())
|| Objects.isNull(importConfiguration.getSruRecordSchema())) {
throw new ConfigException("Either SRU version or SRU record schema is null!");
}
urlParameters.put(SRU_VERSION, importConfiguration.getSruVersion());
urlParameters.put(SRU_RECORD_SCHEMA, importConfiguration.getSruRecordSchema());
}
if (SearchInterfaceType.OAI.name().equals(importConfiguration.getInterfaceType())) {
urlParameters.put(OAI_VERB, OAI_GET_RECORD);
if (Objects.isNull(importConfiguration.getOaiMetadataPrefix())) {
throw new ConfigException("OAI metadata prefix is null!");
}
urlParameters.put(OAI_METADATA_PREFIX, importConfiguration.getOaiMetadataPrefix());
}
if (SearchInterfaceType.CUSTOM.name().equals(importConfiguration.getInterfaceType())) {
for (UrlParameter parameter : importConfiguration.getUrlParameters()) {
urlParameters.put(parameter.getParameterKey(), parameter.getParameterValue());
}
}
return urlParameters;
}
/**
* Check and return whether the functional metadata 'recordIdentifier' is configured for all top level doc struct
* types in the given RulesetManagementInterface or not.
* @param rulesetManagementInterface RulesetManagementInterface to use
* @return whether 'recordIdentifier' is seht for all doc struct types
*/
public boolean isRecordIdentifierMetadataConfigured(RulesetManagementInterface rulesetManagementInterface) {
User user = ServiceManager.getUserService().getCurrentUser();
String metadataLanguage = user.getMetadataLanguage();
List<Locale.LanguageRange> languages = Locale.LanguageRange.parse(metadataLanguage.isEmpty()
? Locale.ENGLISH.getCountry() : metadataLanguage);
Map<String, String> structuralElements = rulesetManagementInterface.getStructuralElements(languages);
Collection<String> recordIdentifierMetadata = rulesetManagementInterface
.getFunctionalKeys(FunctionalMetadata.RECORD_IDENTIFIER);
for (Map.Entry<String, String> division : structuralElements.entrySet()) {
StructuralElementViewInterface viewInterface = rulesetManagementInterface
.getStructuralElementView(division.getKey(), ACQUISITION_STAGE_CREATE, languages);
List<String> allowedMetadataKeys = viewInterface.getAllowedMetadata().stream()
.map(MetadataViewInterface::getId).collect(Collectors.toList());
allowedMetadataKeys.retainAll(recordIdentifierMetadata);
if (allowedMetadataKeys.isEmpty()) {
return false;
}
}
return true;
}
}