/* * Data Hub Service (DHuS) - For Space data distribution. * Copyright (C) 2013,2014,2015 GAEL Systems * * This file is part of DHuS software sources. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.gael.dhus.service; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import javax.xml.bind.JAXBException; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.springframework.stereotype.Service; import com.hp.hpl.jena.ontology.OntClass; import com.hp.hpl.jena.util.iterator.ExtendedIterator; import fr.gael.dhus.service.metadata.ItemClassMetadataTypes; import fr.gael.dhus.service.metadata.MetadataType; import fr.gael.dhus.service.metadata.MetadataTypeParser; import fr.gael.dhus.service.metadata.SolrField; import fr.gael.drbx.cortex.DrbCortexItemClass; import fr.gael.drbx.cortex.DrbCortexModel; /** * A Service for retrieving DHuS metadata types attached to the Item Classes * defined in the DRB Cortex Ontology. * <p> * At initialization (constructor), the service scans all the item classes * defined in the DRB Cortex Ontology model and parse any attached metadata type * definitions i.e. <code>metadataTypes</code> property. All metadata type * definitions are parsed including those of the super classes. For * initialization time performance and low runtime memory footprint, the * metadata types definitions are parsed only once and cached cf. documentation * of the constructor {@link #MetadataTypeService()}. * </p> * <p> * Once initialized, the Service can be called at any time for retrieving the * metadata types defined for a given item class identified by its URI. The * metadata type can be retrieved either by its identifier * {@link #getMetadataTypeById(String, String)} or by its name * {@link #getMetadataTypeByName(String, String)}. The implementation make use * of multiple indexes to maximize the retrieval performances either by type * identifier or name. Concurrent requests from different thread are allowed. * </p> * <p> * Example of definition in the DRB Cortex Ontology: * * <pre> * <rdf:Description rdf:about=" <i> URI of the target item class </i> "> * <dhus:metadataTypes rdf:parseType="Literal"> * <metadataType id="platformName" * name="Satellite name" * contentType="text/plain" * category="platform"> * <solrField name="platformname" * type="text_general"/> * </metadataType> * * [... truncated for brevity ...] * * </dhus:metadataTypes * </rdf:Description * </pre> * * </p> */ @Service public class MetadataTypeService { /** * A logger for this class. */ private static final Logger LOGGER = LogManager.getLogger(MetadataTypeService.class); /** * The DHuS XML Namespace holding the property elements defining the metadata * types. */ public final static String DHUS_NAMESPACE = "http://www.gael.fr/dhus#"; /** * The name of the property elements defining the metadata types. */ public final static String METADATA_TYPES_PROPERTY = "metadataTypes"; /** * The name of the root element wrapping the metadata type definitions in the * metadata type XML Schema. */ public final static String METADATA_TYPES_ELEMENT_NAME = "metadataTypes"; /** * A Map of {#ItemClassMetadataTypes} instances denoting the DRB Cortex * classes and holding the list of associated DHuS metadata types. */ private final Map<String, ItemClassMetadataTypes> mapItemClassByUri; /** * Default constructor loading metadata types associated to all item classes * known by the current DRB Cortex model. * <p> * The access to the default DRB Cortex model is required during this * initialization. Otherwise, the initialization is aborted and the Service * will not return any metadata type. * </p> * <p> * The initialization loops among all item class of the DRB Cortex model. The * classes with no attached metadata type definitions i.e. * <code>metadataTypes</code> property, are discarded to same memory * footprint. For the others, the metadata type definitions in XML are parsed * and the resulting classes are stored in an instance of the multi-indexed * {@link ItemClassMetadataTypes} according to the type identifier and the * type name for performance purpose. Also for performance purpose, the * metadata type XML definitions are parsed only once and the result are * shared between {@link ItemClassMetadataTypes} thanks to a cache based on a * UUID hash. * </p> */ public MetadataTypeService() { // Log initialization start LOGGER.info("Initializing Metadata Typing Service..."); // Get a time stamps of the initialization start final Date start_time = new Date(); // Initialize an empty map this.mapItemClassByUri = new ConcurrentHashMap<String, ItemClassMetadataTypes>(); // Get current DRB Cortex Model DrbCortexModel model = null; try { model = DrbCortexModel.getDefaultModel(); } catch (final IOException exception) { LOGGER.error("Error while getting current DRB Cortex model " + "(aborting Metadata Typing service).", exception); LOGGER.info("Aborting MetadataTypeService inititialization " + "with no metadata type defined!"); return; } // Get an iterator over all DRB Cortex classes final ExtendedIterator ont_class_iterator = model.getCortexModel().getOntModel().listClasses(); // Prepare a map of parsed metadata type declarations final Map<UUID, List<MetadataType>> cached_metadata_types = new ConcurrentHashMap<UUID, List<MetadataType>>(); // Prepare a parser for XML definitions of metadata types MetadataTypeParser metadata_type_parser; try { metadata_type_parser = new MetadataTypeParser(); } catch (final JAXBException exception) { LOGGER.error("Cannot create a parser for the XML definitions of " + "metadata types.", exception); LOGGER.info("Aborting MetadataTypeService inititialization " + "with no metadata type defined!"); return; } // Loop among Ontology classes while (ont_class_iterator.hasNext()) { // Extracts current class from the iterator final OntClass ont_class = (OntClass) ont_class_iterator.next(); // Get current class URI final String uri = ont_class.getURI(); // Skip classes without URI - case unforeseen by theory if (uri == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skipping current Ontology class that has no URI " + "(local name = \"" + ont_class.getLocalName() + "\")."); } continue; } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Processing Ontology class \"" + uri + "\""); } // Get corresponding DRB Cortex class final DrbCortexItemClass item_class = DrbCortexItemClass.getCortexItemClassByName(uri); // Check resulting item class if (item_class == null) { LOGGER.error("Cannot derive DRB Cortex item class from URI " + "\"" + uri + "\"."); continue; } // Prepare a container for the current item class metadata types final ItemClassMetadataTypes class_metadata_types = new ItemClassMetadataTypes(uri, item_class.getLabel()); // Get all URIs of matadataTypes property describing the present item // class or any of its ancestors final Collection<String> xml_metadata_types_list = item_class.listPropertyStrings(DHUS_NAMESPACE + METADATA_TYPES_PROPERTY, false); // Loop immediately if the current item class has no attached // metadataTypes if ((xml_metadata_types_list == null) || xml_metadata_types_list.isEmpty()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Item class \"" + item_class.getLabel() + "\" (" + uri + ") has no attached metadata type definition " + "(skipped)."); } continue; } // Prepare the list of metadata types to be parsed or retrieved from // the cache final List<MetadataType> metadata_types = new ArrayList<>(); // Loop among XML definitions of metadata types for (final String xml_metadata_types : xml_metadata_types_list) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Found metadataTypes string \"" + xml_metadata_types.substring(0, 30) + "...\""); } // Get a unique identifier of the current xml content final UUID uuid = UUID.nameUUIDFromBytes(xml_metadata_types.getBytes()); // Get the already parsed types if cached if (cached_metadata_types.containsKey(uuid)) { LOGGER.debug("Metadata Types already parsed (copied from cache)"); metadata_types.addAll(cached_metadata_types.get(uuid)); } else { LOGGER.debug("Parsing XML definition of Metadata Types..."); List<MetadataType> parsed_metadata_types = null; final String rooted_xml_metadata_types = "<" + METADATA_TYPES_ELEMENT_NAME + ">" + xml_metadata_types + "</" + METADATA_TYPES_ELEMENT_NAME + ">"; try { parsed_metadata_types = metadata_type_parser.parse(rooted_xml_metadata_types); } catch (NullPointerException | IllegalStateException | JAXBException exception) { LOGGER.error("Cannot parse XML metadata type definition of " + "class \"" + uri + "\" (" + rooted_xml_metadata_types + ") - skipped.", exception); continue; } if (parsed_metadata_types != null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed " + parsed_metadata_types.size() + " metadata types."); } metadata_types.addAll(parsed_metadata_types); cached_metadata_types.put(uuid, parsed_metadata_types); } } } // Loop among XML definitions of metadata types // Index metadata types in the item class representative if (!metadata_types.isEmpty()) { class_metadata_types.addAllMetadataTypes(metadata_types); LOGGER.info("Item class \"" + class_metadata_types.getLabel() + "\" (" + class_metadata_types.getUri() + ") has " + metadata_types.size() + " metadata types."); this.mapItemClassByUri.put(uri, class_metadata_types); } } // Loop among Ontology classes LOGGER.info("Metadata Typing Service initialized in " + (new Date().getTime() - start_time.getTime()) + " ms with " + this.mapItemClassByUri.size() + " item class(es)."); } // End MetadataTypeService() constructor /** * Return the metadata type definition attached to an item class identified * by its URI and according to the type identifier. * <p> * This operation cannot retrieve a metadata type if one of the item class * URI or the metadata type identifier is null. If one or both are null, the * a null reference is returned. The operation may also return a null * reference if the item class did not exist at the initialization time, if * no metadata type is attached to the item class or if none of the attached * metadata type has the requested identifier. * </p> * <p> * Concurrent access from different threads are allowed for this operation. * </p> * * @param item_class_uri the URI of the item class to be searched. * @param type_identifier the identifier of the metadata type to be * retrieved. * @return the metadata type or null if none could be retrieved or if one of * the input parameter is null. */ public MetadataType getMetadataTypeById(final String item_class_uri, final String type_identifier) { // Return nothing if the input item class URI is a null reference if (item_class_uri == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Cannot get a metadata type with a null item class" + " URI."); } return null; } // Return nothing if the input item class URI is a null reference if (type_identifier == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Cannot get a metadata type with a null type" + " identifier."); } return null; } // Retrieve the metadata types attached to the item class final ItemClassMetadataTypes item_class_index = this.mapItemClassByUri.get(item_class_uri); // Return immediately if no metadata type is attached to the item class if (item_class_index == null) { return null; } // Return the requested metadata type, if any return item_class_index.getTypeById(type_identifier); } // End getTypeById(String, String) /** * Return the metadata type definition attached to an item class identified * by its URI and according to the type name. * <p> * This operation cannot retrieve a metadata type if one of the item class * URI or the metadata type name is null. If one or both are null, the a null * reference is returned. The operation may also return a null reference if * the item class did not exist at the initialization time, if no metadata * type is attached to the item class or if none of the attached metadata * type has the requested name. * </p> * <p> * Concurrent access from different threads are allowed for this operation. * </p> * * @param item_class_uri the URI of the item class to be searched. * @param type_name the name of the metadata type to be retrieved. * @return the metadata type or null if none could be retrieved or if one of * the input parameter is null. */ public MetadataType getMetadataTypeByName(final String item_class_uri, final String type_name) { // Return nothing if the input item class URI is a null reference if (item_class_uri == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Cannot get a metadata type with a null item class" + " URI."); } return null; } // Return nothing if the input item class URI is a null reference if (type_name == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Cannot get a metadata type with a null type" + " identifier."); } return null; } // Retrieve the metadata types attached to the item class final ItemClassMetadataTypes item_class_index = this.mapItemClassByUri.get(item_class_uri); // Return immediately if no metadata type is attached to the item class if (item_class_index == null) { return null; } // Return the requested metadata type, if any return item_class_index.getTypeByName(type_name); } // End getTypeById(String, String) /** * Retrieve the list of Solr fields declared into the metadataField * ontology description. If a field is declared twice or more, * only the last field is considered from the list. * @return a Map of solr fields indexed by their name. */ public Map<String, SolrField> getSolrFields() { Map<String, SolrField>fields = new HashMap<>(); for (ItemClassMetadataTypes icmt: this.mapItemClassByUri.values()) { Collection<MetadataType> mts = icmt.getAllMetadataTypes(); if (mts == null) { continue; } for (MetadataType mt: mts) { SolrField sf = mt.getSolrField(); if (sf == null) { continue; } fields.put(sf.getName(), sf); } } return fields; } } // End MetadataTypeService class