diff --git a/solidify-oai-model/src/main/java/ch/unige/solidify/OAIConstants.java b/solidify-oai-model/src/main/java/ch/unige/solidify/OAIConstants.java index 2ef969f7ad513c7724e722060c5d0fec5d39e81c..fe2acee2f3be0925dc93656c822d1b1c192b34f0 100644 --- a/solidify-oai-model/src/main/java/ch/unige/solidify/OAIConstants.java +++ b/solidify-oai-model/src/main/java/ch/unige/solidify/OAIConstants.java @@ -9,12 +9,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. @@ -61,6 +61,7 @@ public class OAIConstants { // OAI Provider public static final String OAI_PMH = "OAI-PMH"; public static final String OAI_PMH_SCHEMA = "http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd"; + public static final String OAI_PMH_PREFIX_NAMESPACE = "oai-pmh"; public static final String OAI_PMH_NAMESPACE = "http://www.openarchives.org/OAI/2.0/"; // OAI Dublin Core public static final String OAI_DC = "oai_dc"; diff --git a/solidify-oai-model/src/main/java/ch/unige/solidify/OAIProperties.java b/solidify-oai-model/src/main/java/ch/unige/solidify/OAIProperties.java index 38cc63c1816c14c7706a1290afcfc7816e3910c8..e28850dac1b51737fc3f3bc4a483cdec2534b0b9 100644 --- a/solidify-oai-model/src/main/java/ch/unige/solidify/OAIProperties.java +++ b/solidify-oai-model/src/main/java/ch/unige/solidify/OAIProperties.java @@ -9,12 +9,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. @@ -32,6 +32,9 @@ public class OAIProperties { private int pageSize = 100; private int tokenLifeTimeMinutes = 5; + // Parameter that defines if metadata contents must be serialized with their XML namespaces + private boolean serializeMetadataWithNamespaces = false; + public int getPageSize() { return this.pageSize; } @@ -48,4 +51,11 @@ public class OAIProperties { this.tokenLifeTimeMinutes = tokenLifeTimeMinutes; } + public boolean isSerializeMetadataWithNamespaces() { + return this.serializeMetadataWithNamespaces; + } + + public void setSerializeMetadataWithNamespaces(boolean serializeMetadataWithNamespaces) { + this.serializeMetadataWithNamespaces = serializeMetadataWithNamespaces; + } } diff --git a/solidify-oai-model/src/main/java/ch/unige/solidify/OAIXmlNamespacePrefixMapper.java b/solidify-oai-model/src/main/java/ch/unige/solidify/OAIXmlNamespacePrefixMapper.java index d3911c1e136e4ebfcb9ef47dc83fb243579b8117..c39570df27fce3e6f265c0a294a86db0677be000 100644 --- a/solidify-oai-model/src/main/java/ch/unige/solidify/OAIXmlNamespacePrefixMapper.java +++ b/solidify-oai-model/src/main/java/ch/unige/solidify/OAIXmlNamespacePrefixMapper.java @@ -9,12 +9,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. @@ -29,6 +29,7 @@ public class OAIXmlNamespacePrefixMapper extends XmlNamespacePrefixMapper { public OAIXmlNamespacePrefixMapper() { super(); + this.addSchema(OAIConstants.OAI_PMH_NAMESPACE, OAIConstants.OAI_PMH_PREFIX_NAMESPACE); this.addSchema(OAIConstants.OAI_DC_NAMESPACE, OAIConstants.OAI_DC); this.addSchema(OAIConstants.OAI_ID_NAMESPACE, OAIConstants.OAI_ID); this.addSchema(OAIConstants.DC_NAMESPACE, OAIConstants.DC); diff --git a/solidify-oai-model/src/main/java/ch/unige/solidify/model/oai/MetadataContent.java b/solidify-oai-model/src/main/java/ch/unige/solidify/model/oai/MetadataContent.java new file mode 100644 index 0000000000000000000000000000000000000000..91e01ce085e6b3525a99b9e62e3371e3777f90d7 --- /dev/null +++ b/solidify-oai-model/src/main/java/ch/unige/solidify/model/oai/MetadataContent.java @@ -0,0 +1,34 @@ +/*- + * %%---------------------------------------------------------------------------------------------- + * Solidify Framework - Solidify OAI-PMH Model - MetadataContent.java + * SPDX-License-Identifier: GPL-2.0-or-later + * %----------------------------------------------------------------------------------------------% + * Copyright (C) 2017 - 2022 University of Geneva + * %----------------------------------------------------------------------------------------------% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-2.0.html>. + * ----------------------------------------------------------------------------------------------%% + */ +package ch.unige.solidify.model.oai; + +import java.io.Serializable; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlAccessorType(XmlAccessType.FIELD) +@XmlRootElement(name = "metadataContent") +public class MetadataContent implements Serializable { + private final static long serialVersionUID = 1L; +} diff --git a/solidify-oai-model/src/main/java/ch/unige/solidify/model/oai/OAIRepositoryInfo.java b/solidify-oai-model/src/main/java/ch/unige/solidify/model/oai/OAIRepositoryInfo.java index 98f815ce30637fd1f914d229976315aa4d1e39d9..cea32d01830005b076246ab1f68cc2ee72870943 100644 --- a/solidify-oai-model/src/main/java/ch/unige/solidify/model/oai/OAIRepositoryInfo.java +++ b/solidify-oai-model/src/main/java/ch/unige/solidify/model/oai/OAIRepositoryInfo.java @@ -9,12 +9,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. @@ -47,7 +47,7 @@ public interface OAIRepositoryInfo { JAXBElement<?> getRepositoryDefinition(); - String getRepositoryNamepsace(); + String getRepositoryNamespace(); String getRepositorySchema(); } diff --git a/solidify-oai-pmh/src/main/java/ch/unige/solidify/controller/oai/OAIProviderController.java b/solidify-oai-pmh/src/main/java/ch/unige/solidify/controller/oai/OAIProviderController.java index 83b98b87ad7c4a6e8992538b4a33f48572425cce..b8f99fdc3ec6fda47ade8e19be6339f58b44c0aa 100644 --- a/solidify-oai-pmh/src/main/java/ch/unige/solidify/controller/oai/OAIProviderController.java +++ b/solidify-oai-pmh/src/main/java/ch/unige/solidify/controller/oai/OAIProviderController.java @@ -9,12 +9,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 2 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-2.0.html>. @@ -31,7 +31,6 @@ import java.time.Instant; import java.time.ZoneOffset; import java.util.HashMap; import java.util.Map; - import javax.servlet.http.HttpServletRequest; import javax.xml.bind.JAXBException; @@ -118,7 +117,7 @@ public class OAIProviderController implements ControllerWithHateoasHome { // Force http usage, in order to be compatible with old XML validators final String httpOnlyOaiUrl = oaiUrl.replace("https://", "http://"); schemaLocations.put( - this.oaiService.getOaiRepositoryInfo().getRepositoryNamepsace(), + this.oaiService.getOaiRepositoryInfo().getRepositoryNamespace(), httpOnlyOaiUrl + SolidifyConstants.URL_SEP + SolidifyConstants.SCHEMA); break; case LIST_IDENTIFIERS: diff --git a/solidify-oai-pmh/src/main/java/ch/unige/solidify/service/OAIService.java b/solidify-oai-pmh/src/main/java/ch/unige/solidify/service/OAIService.java index e94cc744b6f4bcaf3c88f0dc1c256b171721e375..f04378e85851683a8491ce5186035262f1bd6833 100644 --- a/solidify-oai-pmh/src/main/java/ch/unige/solidify/service/OAIService.java +++ b/solidify-oai-pmh/src/main/java/ch/unige/solidify/service/OAIService.java @@ -51,6 +51,7 @@ import ch.unige.solidify.business.OAISetService; import ch.unige.solidify.controller.OAIController; import ch.unige.solidify.exception.OAIException; import ch.unige.solidify.exception.SolidifyRuntimeException; +import ch.unige.solidify.model.oai.MetadataContent; import ch.unige.solidify.model.oai.OAIMetadataPrefix; import ch.unige.solidify.model.oai.OAIRecord; import ch.unige.solidify.model.oai.OAIRepositoryInfo; @@ -79,7 +80,6 @@ import ch.unige.solidify.model.xml.oai.v2.oai_dc.ElementType; import ch.unige.solidify.model.xml.oai.v2.oai_dc.OaiDcType; import ch.unige.solidify.model.xml.oai.v2.oai_dc.ObjectFactory; import ch.unige.solidify.model.xml.oai.v2.oai_identifier.OaiIdentifierType; -import ch.unige.solidify.model.xml.xhtml.v5.Html; import ch.unige.solidify.rest.RestCollection; import ch.unige.solidify.rest.RestCollectionPage; import ch.unige.solidify.specification.OAIMetadataPrefixSpecification; @@ -92,6 +92,7 @@ import ch.unige.solidify.util.XMLTool; public class OAIService { private final JAXBContext jaxbContext; + private final int oaiPmhPageSize; private final int oaiPmhTokenLifeTime; @@ -106,6 +107,8 @@ public class OAIService { private final OAIRecordService recordService; private final OAIMetadataService metadataService; + boolean serializeMetadataWithNamespaces; + public OAIService( OAIProperties oaiProperties, OAIRepositoryInfo oaiRepositoryInfo, @@ -117,6 +120,7 @@ public class OAIService { this.oaiRepositoryInfo = oaiRepositoryInfo; this.oaiPmhPageSize = oaiProperties.getPageSize(); this.oaiPmhTokenLifeTime = oaiProperties.getTokenLifeTimeMinutes(); + this.serializeMetadataWithNamespaces = oaiProperties.isSerializeMetadataWithNamespaces(); this.oaiMetadataPrefixService = oaiMetadataPrefixService; this.oaiSetService = oaiSetService; this.searchService = searchService; @@ -125,7 +129,8 @@ public class OAIService { // JAXB contexts try { // Add project XML Classes - final List<Class<?>> xmlClasses = new ArrayList<>(Arrays.asList(OAIPMHtype.class, OaiDcType.class, OaiIdentifierType.class, Html.class)); + final List<Class<?>> xmlClasses = new ArrayList<>( + Arrays.asList(OAIPMHtype.class, OaiDcType.class, OaiIdentifierType.class, MetadataContent.class)); xmlClasses.addAll(this.metadataService.getOaiXmlClasses()); this.jaxbContext = JAXBContext.newInstance(xmlClasses.toArray(Class<?>[]::new)); } catch (JAXBException e) { @@ -355,11 +360,77 @@ public class OAIService { .append(schemaLocation.getValue()); } } + marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, schemaLocations.toString()); final JAXBElement<OAIPMHtype> root = this.getXML(oai); + + if (this.serializeMetadataWithNamespaces) { + return this.serializeXmlWithNamespacesInMetadata(oai, root, marshaller, schemaLocations.toString()); + } else { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + marshaller.marshal(root, bos); + return bos.toString(); + } + } + + private String serializeXmlWithNamespacesInMetadata(OAIPMHtype oai, JAXBElement<OAIPMHtype> root, Marshaller marshaller, + String schemaLocations) throws JAXBException { + // Get the list of metadata contents unmarshalled separately to get their XML forms including namespaces + List<String> metadataContents = this.getMetadataContents(oai, schemaLocations); + + // Replace metadata nodes content by an XML object that can be replaced later once serialized as String + MetadataContent metadataContentToReplace = new MetadataContent(); + List<RecordType> recordTypes = this.getRecordTypes(oai); + for (RecordType recordType : recordTypes) { + recordType.getMetadata().setAny(metadataContentToReplace); + } + + // Serialize OAI-PMH XML as String final ByteArrayOutputStream bos = new ByteArrayOutputStream(); marshaller.marshal(root, bos); - return bos.toString(); + String xml = bos.toString(); + + // Compute String that must be replaced in OAI-PMH XML + Marshaller metadataContentMarshaller = JAXBContext.newInstance(MetadataContent.class).createMarshaller(); + metadataContentMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); + final ByteArrayOutputStream metadataContentOutput = new ByteArrayOutputStream(); + metadataContentMarshaller.marshal(metadataContentToReplace, metadataContentOutput); + String metadataContentXml = metadataContentOutput.toString(); + + // Replace each metadata content by the original XML contents including namespaces + for (String metadataContent : metadataContents) { + xml = xml.replaceFirst(metadataContentXml, metadataContent); + } + return xml; + } + + private List<String> getMetadataContents(OAIPMHtype oai, String schemaLocations) throws JAXBException { + final Marshaller metadataMarshaller = this.metadataService.getMarshaller(this.jaxbContext); + metadataMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + metadataMarshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); + metadataMarshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, schemaLocations); + + List<RecordType> recordTypes = this.getRecordTypes(oai); + List<String> metadataFragments = new ArrayList<>(); + for (RecordType recordType : recordTypes) { + Object metadataXml = recordType.getMetadata().getAny(); + final ByteArrayOutputStream metadataOutput = new ByteArrayOutputStream(); + metadataMarshaller.marshal(metadataXml, metadataOutput); + metadataFragments.add(metadataOutput.toString().trim()); + } + return metadataFragments; + } + + private List<RecordType> getRecordTypes(OAIPMHtype oai) { + List<RecordType> recordTypes = new ArrayList<>(); + if (oai.getGetRecord() != null) { + recordTypes.add(oai.getGetRecord().getRecord()); + } else if (oai.getListRecords() != null) { + for (RecordType recordType : oai.getListRecords().getRecord()) { + recordTypes.add(recordType); + } + } + return recordTypes; } private void addOaiSetInfo(HeaderType hdr, String resId) {