/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.constellation.metadata.io.internal; import org.apache.sis.xml.Namespaces; import org.constellation.admin.SpringHelper; import org.constellation.business.IMetadataBusiness; import org.constellation.generic.database.Automatic; import org.constellation.metadata.io.CSWMetadataReader; import org.constellation.metadata.io.DomMetadataReader; import org.constellation.metadata.io.ElementSetType; import org.constellation.metadata.io.MetadataIoException; import org.constellation.metadata.io.MetadataType; import org.constellation.util.NodeUtilities; import org.geotoolkit.csw.xml.DomainValues; import org.geotoolkit.csw.xml.v202.DomainValuesType; import org.geotoolkit.csw.xml.v202.ListOfValuesType; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import javax.inject.Inject; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamException; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import static org.constellation.metadata.CSWQueryable.DUBLIN_CORE_QUERYABLE; import static org.constellation.metadata.CSWQueryable.ISO_QUERYABLE; import static org.geotoolkit.csw.xml.TypeNames.METADATA_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Creator_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Date_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Description_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Format_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Identifier_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Language_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Publisher_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Subject_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Title_QNAME; import static org.geotoolkit.dublincore.xml.v2.elements.ObjectFactory._Type_QNAME; import static org.geotoolkit.dublincore.xml.v2.terms.ObjectFactory._Abstract_QNAME; import static org.geotoolkit.dublincore.xml.v2.terms.ObjectFactory._Modified_QNAME; import static org.geotoolkit.ows.xml.OWSExceptionCode.INVALID_PARAMETER_VALUE; import static org.geotoolkit.ows.xml.OWSExceptionCode.OPERATION_NOT_SUPPORTED; import static org.geotoolkit.ows.xml.v100.ObjectFactory._BoundingBox_QNAME; // Geotoolkit dependencies /** * A CSW Metadata Reader. This reader does not require a database. * The CSW records are stored XML file in a directory . * * This reader can be used for test purpose or in case of small amount of record. * * @author Guilhem Legal (Geomatys) */ public class InternalMetadataReader extends DomMetadataReader implements CSWMetadataReader { private final boolean displayServiceMetadata = false; @Inject private IMetadataBusiness metadataBusiness; private final boolean partial; private final boolean onlyPublished; private final String id; /** * Build a new CSW File Reader. * * @param configuration A generic configuration object containing a directory path * in the configuration.dataDirectory field. * @param serviceID * * @throws MetadataIoException If the configuration object does * not contains an existing directory path in the configuration.dataDirectory field. * If the creation of a MarshallerPool throw a JAXBException. */ public InternalMetadataReader(final Automatic configuration, final String serviceID) throws MetadataIoException { super(true, false); this.id = serviceID; SpringHelper.injectDependencies(this); if (configuration.getEnableThread() != null && !configuration.getEnableThread().isEmpty()) { final boolean t = Boolean.parseBoolean(configuration.getEnableThread()); if (t) { LOGGER.info("parrallele treatment enabled"); } setIsThreadEnabled(t); } if (configuration.getEnableCache() != null && !configuration.getEnableCache().isEmpty()) { final boolean c = Boolean.parseBoolean(configuration.getEnableCache()); if (!c) { LOGGER.info("cache system have been disabled"); } setIsCacheEnabled(c); } this.partial = configuration.getBooleanParameter("partial", false); this.onlyPublished = configuration.getBooleanParameter("onlyPublished", false); } /** * {@inheritDoc} */ @Override public Node getMetadata(final String identifier, final MetadataType mode) throws MetadataIoException { return getMetadata(identifier, mode, ElementSetType.FULL, new ArrayList<QName>()); } /** * {@inheritDoc} */ @Override public Node getMetadata(final String identifier, final MetadataType mode, final ElementSetType type, final List<QName> elementName) throws MetadataIoException { if (partial && !metadataBusiness.isLinkedMetadataToCSW(identifier, id, displayServiceMetadata, onlyPublished)) { return null; } final String metadataString = metadataBusiness.searchMetadata(identifier, displayServiceMetadata, onlyPublished); if (metadataString != null) { final MetadataType metadataMode; try { metadataMode = getMetadataType(new StringReader(metadataString), false); } catch (IOException | XMLStreamException ex) { throw new MetadataIoException(ex); } final Node metadataNode = getNodeFromReader(new StringReader(metadataString)); if (metadataMode == MetadataType.ISO_19115 && mode == MetadataType.DUBLINCORE) { return translateISOtoDCNode(metadataNode, type, elementName); } else if (mode == MetadataType.DUBLINCORE && metadataMode == MetadataType.DUBLINCORE) { return applyElementSetNode(metadataNode, type, elementName); } else { return metadataNode; } } return null; } @Override public boolean existMetadata(final String identifier) throws MetadataIoException { if (partial) { return metadataBusiness.isLinkedMetadataToCSW(identifier, id, displayServiceMetadata, onlyPublished); } return metadataBusiness.existInternalMetadata(identifier, displayServiceMetadata, onlyPublished); } private Node translateISOtoDCNode(final Node metadata, final ElementSetType type, final List<QName> elementName) throws MetadataIoException { if (metadata != null) { final DocumentBuilder docBuilder; try { dbf.setNamespaceAware(true); docBuilder = dbf.newDocumentBuilder(); } catch (ParserConfigurationException ex) { throw new MetadataIoException(ex); } final Document document = docBuilder.newDocument(); final Element root = document.createElementNS(Namespaces.CSW, "Record"); /* * BRIEF part */ final List<String> identifierValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("Identifier")); final List<Node> identifiers = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "identifier", identifierValues, true); if (elementName != null && elementName.contains(_Identifier_QNAME)) { NodeUtilities.appendChilds(root, identifiers); } final List<String> titleValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("Title")); final List<Node> titles = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "title", titleValues, true); if (elementName != null && elementName.contains(_Title_QNAME)) { NodeUtilities.appendChilds(root, titles); } final List<String> dataTypeValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("Type")); final List<Node> dataTypes = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "type", dataTypeValues, false); if (elementName != null && elementName.contains(_Type_QNAME)) { NodeUtilities.appendChilds(root, dataTypes); } final List<String> westValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("WestBoundLongitude")); final List<String> eastValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("EastBoundLongitude")); final List<String> northValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("NorthBoundLatitude")); final List<String> southValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("SouthBoundLatitude")); final List<Node> bboxes = new ArrayList<>(); if (westValues.size() == eastValues.size() && eastValues.size() == northValues.size() && northValues.size() == southValues.size()) { for (int i = 0; i < westValues.size(); i++) { final Node bboxNode = document.createElementNS("http://www.opengis.net/ows", "BoundingBox"); final Node crsAtt = document.createAttribute("crs"); crsAtt.setTextContent("EPSG:4326"); bboxNode.getAttributes().setNamedItem(crsAtt); final Node dimAtt = document.createAttribute("dimensions"); dimAtt.setTextContent("2"); bboxNode.getAttributes().setNamedItem(dimAtt); final Node lower = document.createElementNS("http://www.opengis.net/ows", "LowerCorner"); lower.setTextContent(westValues.get(i) + " " + southValues.get(i)); bboxNode.appendChild(lower); final Node upper = document.createElementNS("http://www.opengis.net/ows", "UpperCorner"); upper.setTextContent(eastValues.get(i) + " " + northValues.get(i)); bboxNode.appendChild(upper); bboxes.add(bboxNode); } } else { LOGGER.warning("incoherent bboxes coordinate"); } if (ElementSetType.BRIEF.equals(type)) { final Element briefRoot = document.createElementNS(Namespaces.CSW, "BriefRecord"); NodeUtilities.appendChilds(briefRoot, identifiers); NodeUtilities.appendChilds(briefRoot, titles); NodeUtilities.appendChilds(briefRoot, dataTypes); NodeUtilities.appendChilds(briefRoot, bboxes); return briefRoot; } /* * SUMMARY part */ final List<String> abstractValues = NodeUtilities.getValuesFromPath(metadata, "/gmd:MD_Metadata/gmd:identificationInfo/*/gmd:abstract/gco:CharacterString"); final List<Node> abstracts = NodeUtilities.buildNodes(document, "http://purl.org/dc/terms/", "abstract", abstractValues, false); if (elementName != null && elementName.contains(_Abstract_QNAME)) { NodeUtilities.appendChilds(root, abstracts); } final List<String> kwValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("Subject")); final List<Node> subjects = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "subject", kwValues, false); if (elementName != null && elementName.contains(_Subject_QNAME)) { NodeUtilities.appendChilds(root, subjects); } final List<String> formValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("Format")); final List<Node> formats = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "format", formValues, false); if (elementName != null && elementName.contains(_Format_QNAME)) { NodeUtilities.appendChilds(root, formats); } final List<String> modValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("Modified")); final List<String> dateValues = new ArrayList<>(); for (String modValue : modValues) { dateValues.add(formatDate(modValue)); } final List<Node> modifieds = NodeUtilities.buildNodes(document, "http://purl.org/dc/terms/", "modified", dateValues, false); if (elementName != null && elementName.contains(_Modified_QNAME)) { NodeUtilities.appendChilds(root, modifieds); } if (ElementSetType.SUMMARY.equals(type)) { final Element sumRoot = document.createElementNS(Namespaces.CSW, "SummaryRecord"); NodeUtilities.appendChilds(sumRoot, identifiers); NodeUtilities.appendChilds(sumRoot, titles); NodeUtilities.appendChilds(sumRoot, dataTypes); NodeUtilities.appendChilds(sumRoot, subjects); NodeUtilities.appendChilds(sumRoot, formats); NodeUtilities.appendChilds(sumRoot, modifieds); NodeUtilities.appendChilds(sumRoot, abstracts); NodeUtilities.appendChilds(sumRoot, bboxes); return sumRoot; } final List<Node> dates = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "date", dateValues, false); if (elementName != null && elementName.contains(_Date_QNAME)) { NodeUtilities.appendChilds(root, dates); } final List<String> creaValues = NodeUtilities.getValuesFromPaths(metadata, DUBLIN_CORE_QUERYABLE.get("creator")); final List<Node> creators = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "creator", creaValues, false); if (elementName != null && elementName.contains(_Creator_QNAME)) { NodeUtilities.appendChilds(root, creators); } final List<String> desValues = NodeUtilities.getValuesFromPath(metadata, "/gmd:MD_Metadata/gmd:identificationInfo/*/gmd:graphicOverview/gmd:MD_BrowseGraphic/gmd:fileName/gmx:FileName/@src"); final List<Node> descriptions = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "description", desValues, false); if (!descriptions.isEmpty() && elementName != null && elementName.contains(_Description_QNAME)) { NodeUtilities.appendChilds(root, descriptions); } final List<String> paths = new ArrayList<>(); paths.add("/gmd:MD_Metadata/gmd:distributionInfo/gmd:MD_Distribution/gmd:distributor/gmd:MD_Distributor/gmd:distributorContact/gmd:CI_ResponsibleParty/gmd:organisationName/gco:CharacterString"); paths.add("/gmd:MD_Metadata/gmd:distributionInfo/gmd:MD_Distribution/gmd:distributor/gmd:MD_Distributor/gmd:distributorContact/gmd:CI_ResponsibleParty/gmd:organisationName/gmx:Anchor"); paths.add("/gmi:MI_Metadata/gmd:distributionInfo/gmd:MD_Distribution/gmd:distributor/gmd:MD_Distributor/gmd:distributorContact/gmd:CI_ResponsibleParty/gmd:organisationName/gco:CharacterString"); paths.add("/gmi:MI_Metadata/gmd:distributionInfo/gmd:MD_Distribution/gmd:distributor/gmd:MD_Distributor/gmd:distributorContact/gmd:CI_ResponsibleParty/gmd:organisationName/gmx:Anchor"); final List<String> distValues = NodeUtilities.getValuesFromPaths(metadata, paths); final List<Node> distributors = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "publisher", distValues, false); if (elementName != null && elementName.contains(_Publisher_QNAME)) { NodeUtilities.appendChilds(root, distributors); } final List<String> langValues = NodeUtilities.getValuesFromPaths(metadata, ISO_QUERYABLE.get("Language")); final List<Node> languages = NodeUtilities.buildNodes(document, "http://purl.org/dc/elements/1.1/", "language", langValues, false); if (elementName != null && elementName.contains(_Language_QNAME)) { NodeUtilities.appendChilds(root, languages); } if (elementName != null && elementName.contains(_BoundingBox_QNAME)) { NodeUtilities.appendChilds(root, bboxes); } /* TODO final SimpleLiteral spatial = null; final SimpleLiteral references = null;*/ if (ElementSetType.FULL.equals(type)) { final Element recRoot = document.createElementNS(Namespaces.CSW, "Record"); NodeUtilities.appendChilds(recRoot, identifiers); NodeUtilities.appendChilds(recRoot, titles); NodeUtilities.appendChilds(recRoot, dataTypes); NodeUtilities.appendChilds(recRoot, subjects); NodeUtilities.appendChilds(recRoot, formats); NodeUtilities.appendChilds(recRoot, languages); NodeUtilities.appendChilds(recRoot, creators); NodeUtilities.appendChilds(recRoot, modifieds); NodeUtilities.appendChilds(recRoot, dates); NodeUtilities.appendChilds(recRoot, abstracts); NodeUtilities.appendChilds(recRoot, distributors); NodeUtilities.appendChilds(recRoot, descriptions); NodeUtilities.appendChilds(recRoot, bboxes); //NodeUtilities.appendChilds(recRoot, spatials); //NodeUtilities.appendChilds(recRoot, references); return recRoot; } document.appendChild(root); return root; } return null; } /** * {@inheritDoc} */ @Override public List<DomainValues> getFieldDomainofValues(final String propertyNames) throws MetadataIoException { final List<DomainValues> responseList = new ArrayList<>(); final StringTokenizer tokens = new StringTokenizer(propertyNames, ","); while (tokens.hasMoreTokens()) { final String token = tokens.nextToken().trim(); final List<String> paths; if (ISO_QUERYABLE.get(token) != null) { paths = ISO_QUERYABLE.get(token); } else if (DUBLIN_CORE_QUERYABLE.get(token) != null) { paths = DUBLIN_CORE_QUERYABLE.get(token); } else { throw new MetadataIoException("The property " + token + " is not queryable", INVALID_PARAMETER_VALUE, "propertyName"); } if (!paths.isEmpty()) { final List<String> values = getAllValuesFromPaths(paths); final ListOfValuesType listValues = new ListOfValuesType(values); final DomainValuesType value = new DomainValuesType(null, token, listValues, METADATA_QNAME); responseList.add(value); } else { throw new MetadataIoException("The property " + token + " is not queryable for now", INVALID_PARAMETER_VALUE, "propertyName"); } } return responseList; } /** * {@inheritDoc} */ @Override public void destroy() { } /** * {@inheritDoc} */ @Override public String[] executeEbrimSQLQuery(final String sqlQuery) throws MetadataIoException { throw new MetadataIoException("Ebrim query are not supported int the FILESYSTEM mode.", OPERATION_NOT_SUPPORTED); } /** * {@inheritDoc} */ @Override public List<Node> getAllEntries() throws MetadataIoException { final List<Node> result = new ArrayList<>(); final List<String> metadataIds; if (partial) { metadataIds = metadataBusiness.getLinkedMetadataIDs(id, displayServiceMetadata, onlyPublished); } else { metadataIds = metadataBusiness.getInternalMetadataIds(displayServiceMetadata, onlyPublished); } for (String metadataID : metadataIds) { final String meta = metadataBusiness.searchMetadata(metadataID, displayServiceMetadata, onlyPublished); result.add(getNodeFromReader(new StringReader(meta))); } return result; } @Override public int getEntryCount() throws MetadataIoException { if (partial) { return metadataBusiness.getLinkedMetadataCount(id, displayServiceMetadata, onlyPublished); } return metadataBusiness.getInternalMetadataCount(displayServiceMetadata, onlyPublished); } /** * {@inheritDoc} */ @Override public List<String> getAllIdentifiers() throws MetadataIoException { if (partial) { return metadataBusiness.getLinkedMetadataIDs(id, displayServiceMetadata, onlyPublished); } return metadataBusiness.getInternalMetadataIds(displayServiceMetadata, onlyPublished); } @Override public Iterator<String> getIdentifierIterator() throws MetadataIoException { if (partial) { return metadataBusiness.getLinkedMetadataIDs(id, displayServiceMetadata, onlyPublished).iterator(); } return metadataBusiness.getInternalMetadataIds(displayServiceMetadata, onlyPublished).iterator(); } /** * {@inheritDoc} */ @Override public List<MetadataType> getSupportedDataTypes() { return Arrays.asList(MetadataType.ISO_19115, MetadataType.DUBLINCORE, MetadataType.EBRIM, MetadataType.ISO_19110); } /** * Return the list of Additional queryable element. */ @Override public List<QName> getAdditionalQueryableQName() { return new ArrayList<>(); } /** * {@inheritDoc} */ @Override public Map<String, List<String>> getAdditionalQueryablePathMap() { return new HashMap<>(); } }