Java tutorial
/** * Copyright 2008 The University of North Carolina at Chapel Hill * * 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 edu.unc.lib.dl.util; import static edu.unc.lib.dl.util.ContentModelHelper.CDRProperty.replaceInvalidTerms; import static edu.unc.lib.dl.util.ContentModelHelper.Model.COLLECTION; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import edu.unc.lib.dl.fedora.AccessClient; import edu.unc.lib.dl.fedora.FedoraException; import edu.unc.lib.dl.fedora.ManagementClient; import edu.unc.lib.dl.fedora.PID; import edu.unc.lib.dl.util.ContentModelHelper.CDRProperty; import edu.unc.lib.dl.xml.VocabularyHelper; /** * Manages and initializes a set of vocabulary helper classes from a mapping of vocabulary types to helper classes. * Associates configuration of vocabularies with the original vocabulary content and application levels for specific * collections in the repository. Provides helper methods for determining which helpers are applicable and for * performing operations over all applicable helpers. * * @author bbpennel * @date Sep 30, 2014 */ public class VocabularyHelperManager { private static final Logger log = LoggerFactory.getLogger(VocabularyHelperManager.class); // Map of vocabulary type to helper classes private Map<String, Class<?>> helperClassMap; // Map of vocabulary type to helper objects private Map<String, VocabularyHelper> vocabHelperMap; // Map of collection pids to applicable vocabularies private Map<String, Set<VocabularyHelper>> pidToHelpers; // Map of vocabulary uris to fedora objects those keys apply to private Map<String, List<String>> vocabURIToPID; // Map of bound pids to application type per vocabulary private Map<String, Map<String, Set<String>>> pidToVocabApplication; // Configuration info per vocabulary, uri to properties map private Map<String, Map<String, String>> vocabInfoMap; private PID collectionsPID; @Autowired private TripleStoreQueryService queryService; @Autowired private ManagementClient managementClient; @Autowired private AccessClient accessClient; private Boolean initialized = false; public synchronized void init() { log.debug("Initializing vocabulary helpers"); initialized = true; // Scan fedora for objects with the vocabulary content model vocabInfoMap = queryService.fetchVocabularyInfo(); pidToVocabApplication = queryService.fetchVocabularyMapping(); // Instantiate helper classes per vocabulary definition instantiateVocabularyHelpers(); // Establish the associations between containers and vocabularies linkHelpersToPIDs(); // Load the contents of the vocabularies for (String vocabPID : vocabInfoMap.keySet()) { try { byte[] stream = accessClient.getDatastreamDissemination(new PID(vocabPID), ContentModelHelper.Datastream.DATA_FILE.getName(), null).getStream(); if (vocabInfoMap.containsKey(vocabPID)) { String vocabURI = vocabInfoMap.get(vocabPID).get("vocabURI"); vocabHelperMap.get(vocabURI).setContent(stream); } } catch (Exception e) { log.error("Failed to load vocabulary content for {}", vocabPID, e); } } if (log.isDebugEnabled()) { for (Entry<String, Map<String, Set<String>>> pidEntry : pidToVocabApplication.entrySet()) { log.debug("Vocabularies bound to {}", pidEntry.getKey()); for (Entry<String, Set<String>> vocabEntry : pidEntry.getValue().entrySet()) { log.debug(" {}: {}", vocabEntry.getKey(), vocabEntry.getValue()); } } } } private void instantiateVocabularyHelpers() { vocabHelperMap = new HashMap<>(); for (Map<String, String> info : vocabInfoMap.values()) { try { String vocabType = info.get("vocabType"); String vocabURI = info.get("vocabURI"); if (!helperClassMap.containsKey(vocabType)) { log.warn("Vocabulary {} specifies a vocabulary type that does not map to a helper class", vocabURI, vocabType); continue; } VocabularyHelper helperObject = (VocabularyHelper) helperClassMap.get(vocabType).getConstructor() .newInstance(); helperObject.setVocabularyURI(vocabURI); helperObject.setSelector(info.get("vocabularySelector")); vocabHelperMap.put(vocabURI, helperObject); } catch (Exception e) { log.error("Failed to instantiate vocabulary helper class for {}", info.get("vocabURI"), e); } } } private void linkHelpersToPIDs() { if (pidToVocabApplication == null) { pidToHelpers = null; return; } pidToHelpers = new HashMap<>(); vocabURIToPID = new HashMap<>(); for (Entry<String, Map<String, Set<String>>> entry : this.pidToVocabApplication.entrySet()) { String pid = entry.getKey(); Set<VocabularyHelper> helpers = new HashSet<>(); pidToHelpers.put(pid, helpers); log.debug("Storing pidtohelper {}", pid); for (String vocabUri : entry.getValue().keySet()) { helpers.add(vocabHelperMap.get(vocabUri)); List<String> linkedPIDs = vocabURIToPID.get(vocabUri); if (linkedPIDs == null) { linkedPIDs = new ArrayList<>(); vocabURIToPID.put(vocabUri, linkedPIDs); } linkedPIDs.add(pid); log.debug("Vocab {} has {}", vocabUri, linkedPIDs); } log.debug("Helpers so far: {}", helpers); } log.debug("pidtohelpers: {}", pidToHelpers); } /** * Updates an object's invalid term state in Fedora based on any invalid terms found in the given document * * @param pid * @param docElement * @throws FedoraException */ public void updateInvalidTermsRelations(PID pid, Element docElement) throws FedoraException { Set<VocabularyHelper> helpers = getHelpers(pid); if (helpers == null) return; String invalidTermPred = CDRProperty.invalidTerm.toString(); List<String> allExistingTerms = queryService.fetchBySubjectAndPredicate(pid, invalidTermPred); // Decompose triple values and group terms by vocabulary prefix Map<String, List<String>> termMap = new HashMap<>(); for (String term : allExistingTerms) { String parts[] = term.split("\\|", 2); List<String> terms = termMap.get(parts[0]); if (terms == null) { terms = new ArrayList<>(); termMap.put(parts[0], terms); } terms.add(term); } for (VocabularyHelper helper : helpers) { List<String> existingTerms = termMap.get(helper.getInvalidTermPrefix()); Set<String> invalidTerms; try { invalidTerms = helper.getInvalidTermsWithPrefix(docElement); } catch (JDOMException e) { log.error("Failed to extract invalid terms from {}", pid.getPid(), e); continue; } if (existingTerms != null && invalidTerms.size() == existingTerms.size() && invalidTerms.containsAll(existingTerms)) { continue; } if (existingTerms != null) { // Remove any terms which are no longer present List<String> removeTerms = new ArrayList<String>(existingTerms); removeTerms.removeAll(invalidTerms); for (String term : removeTerms) { managementClient.purgeLiteralStatement(pid, invalidTermPred, term, null); } // Calculate the set of newly invalid terms which need to be added invalidTerms.removeAll(existingTerms); } if (invalidTerms.size() > 0) { for (String term : invalidTerms) { managementClient.addLiteralStatement(pid, invalidTermPred, term, null); } } } } /** * Returns a map of invalid terms per vocabulary URI * * @param pid * @param docElement * @return */ public Map<String, Set<String>> getInvalidTerms(PID pid, Element docElement) { return getInvalidTerms(pid, docElement, false); } public Map<String, Set<String>> getInvalidTermsWithPrefix(PID pid, Element docElement) { return getInvalidTerms(pid, docElement, true); } private Map<String, Set<String>> getInvalidTerms(PID pid, Element docElement, boolean includePrefix) { Set<VocabularyHelper> helpers = getHelpers(pid); if (helpers == null) return null; Map<String, Set<String>> results = new LinkedHashMap<>(); for (VocabularyHelper helper : helpers) { if (helper != null) { try { Set<String> invalidTerms; if (includePrefix) { invalidTerms = helper.getInvalidTermsWithPrefix(docElement); } else { invalidTerms = helper.getInvalidTerms(docElement); } if (invalidTerms != null) results.put(helper.getVocabularyURI(), invalidTerms); } catch (JDOMException e) { log.error("Failed to get invalid vocabulary terms of {}", pid, e); } } } return results; } /** * Get the invalid term predicate for the given vocabulary * * @param vocabKey * @return */ public String getInvalidTermPrefix(String vocabKey) { VocabularyHelper helper = vocabHelperMap.get(vocabKey); if (helper == null) return null; return helper.getInvalidTermPrefix(); } /** * Returns the set of vocabulary helpers that apply to the provided pid, as defined on its parent collection * * @param pid * @return */ public Set<VocabularyHelper> getHelpers(PID pid) { return getHelpers(pid, null); } /** * Returns all helpers for the given pid which have invalid term remapping enabled * * @param pid * @param appLevel * @return */ public Set<VocabularyHelper> getRemappingHelpers(PID pid) { return getHelpers(pid, replaceInvalidTerms); } private Set<VocabularyHelper> getHelpers(PID pid, CDRProperty appLevel) { synchronized (initialized) { if (!initialized) init(); } PID parentCollectionPID = queryService.fetchParentCollection(pid); if (parentCollectionPID == null) { List<URI> models = queryService.lookupContentModels(pid); if (models.contains(COLLECTION.getURI())) { parentCollectionPID = pid; } } // Start helpers from the set of globals assigned to the collections object Set<VocabularyHelper> helpers = new HashSet<>(); Set<VocabularyHelper> rootHelpers = pidToHelpers.get(collectionsPID.getURI()); if (rootHelpers != null) helpers.addAll(rootHelpers); if (parentCollectionPID != null) { Set<VocabularyHelper> parentHelpers = pidToHelpers.get(parentCollectionPID.getURI()); if (parentHelpers != null) { helpers.addAll(parentHelpers); } } if (appLevel != null && helpers != null) filterHelperSet(parentCollectionPID, helpers, appLevel); if (log.isDebugEnabled()) log.debug("Helpers found for {} with {}: {}", new Object[] { pid, appLevel, helpers }); return helpers; } private void filterHelperSet(PID pid, Set<VocabularyHelper> helpers, CDRProperty appLevel) { if (helpers == null) return; Map<String, Set<String>> appLevelMap = pidToVocabApplication.get(collectionsPID.getURI()); if (pid != null) { Map<String, Set<String>> collectionLevel = pidToVocabApplication.get(pid.getURI()); if (collectionLevel != null) { if (appLevelMap == null) { appLevelMap = collectionLevel; } else { appLevelMap.putAll(collectionLevel); } } } if (appLevelMap == null) return; Iterator<VocabularyHelper> helperIt = helpers.iterator(); while (helperIt.hasNext()) { VocabularyHelper helper = helperIt.next(); Set<String> appLevels = appLevelMap.get(helper.getVocabularyURI()); if (!appLevels.contains(appLevel.toString())) { helperIt.remove(); } } } public VocabularyHelper getHelper(String vocabURI) { return this.vocabHelperMap.get(vocabURI); } /** * Sets the map of helper classes and generates the mapping of vocabulary types to instances of helpers * * @param helperClasses */ public void setHelperClasses(Map<String, String> helperClasses) { this.helperClassMap = new HashMap<>(helperClasses.size()); for (Entry<String, String> helperEntry : helperClasses.entrySet()) { try { this.helperClassMap.put(helperEntry.getKey(), Class.forName(helperEntry.getValue())); } catch (ClassNotFoundException e) { log.error("Failed to get class for helper {}", helperEntry.getValue(), e); } } } /** * Returns the authoritative vocabulary forms for selected fields from the given document using all applicable * helpers for the specified object. Terms are grouped by vocabulary URI. * * @param pid * @param doc * @return */ public Map<String, List<List<String>>> getAuthoritativeForms(PID pid, Document doc) { return getAuthoritativeForms(pid, doc.getRootElement()); } public Map<String, List<List<String>>> getAuthoritativeForms(PID pid, Element docElement) { Set<VocabularyHelper> helpers = getHelpers(pid); if (helpers == null) return null; Map<String, List<List<String>>> results = new HashMap<>(); for (VocabularyHelper helper : helpers) { try { List<List<String>> terms = helper.getAuthoritativeForms(docElement); if (terms != null && terms.size() > 0) results.put(helper.getVocabularyURI(), terms); } catch (JDOMException e) { log.error("Failed to get authoritative forms for vocabulary {} on object {}", new Object[] { helper.getVocabularyURI(), pid.getPid(), e }); } } return results; } public void setCollectionsPID(PID collectionsPID) { this.collectionsPID = collectionsPID; } }