/* * #%L * ===================================================== * _____ _ ____ _ _ _ _ * |_ _|_ __ _ _ ___| |_ / __ \| | | | ___ | | | | * | | | '__| | | / __| __|/ / _` | |_| |/ __|| |_| | * | | | | | |_| \__ \ |_| | (_| | _ |\__ \| _ | * |_| |_| \__,_|___/\__|\ \__,_|_| |_||___/|_| |_| * \____/ * * ===================================================== * * Hochschule Hannover * (University of Applied Sciences and Arts, Hannover) * Faculty IV, Dept. of Computer Science * Ricklinger Stadtweg 118, 30459 Hannover, Germany * * Email: trust@f4-i.fh-hannover.de * Website: http://trust.f4.hs-hannover.de/ * * This file is part of visitmeta-dataservice, version 0.6.0, * implemented by the Trust@HsH research group at the Hochschule Hannover. * %% * Copyright (C) 2012 - 2016 Trust@HsH * %% * 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. * #L% */ package de.hshannover.f4.trust.visitmeta.ifmap; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Callable; import org.apache.log4j.Logger; import org.w3c.dom.Document; import de.hshannover.f4.trust.ifmapj.channel.ARC; import de.hshannover.f4.trust.ifmapj.exception.IfmapErrorResult; import de.hshannover.f4.trust.ifmapj.messages.SearchResult; import de.hshannover.f4.trust.visitmeta.dataservice.factories.InternalMetadataFactory; import de.hshannover.f4.trust.visitmeta.dataservice.internalDatatypes.InternalIdentifier; import de.hshannover.f4.trust.visitmeta.dataservice.internalDatatypes.InternalMetadata; import de.hshannover.f4.trust.visitmeta.exceptions.ifmap.ConnectionException; import de.hshannover.f4.trust.visitmeta.interfaces.connections.MapServerConnection; /** * A <tt>PollTask</tt> executes one IF-MAP poll request. * * @author Ralf Steuerwald * */ class PollTask implements Callable<PollResult> { private static final Logger log = Logger.getLogger(PollTask.class); private MapServerConnection mConnection; private InternalMetadataFactory mMetadataFactory; private IfmapJHelper mIfmapJHelper; /** * Create a new <tt>PollTask</tt> which uses the given {@link ARC} for * polling. The received data gets returned as a {@link PollResult}. * * @param arc * the {@link ARC} used for polling * @param metadataFactory * the factory which is used to build internal metadata objects */ public PollTask(MapServerConnection connection, InternalMetadataFactory metadataFactory, IfmapJHelper helper) { if (connection == null) { throw new IllegalArgumentException("Connection cannot be null"); } if (metadataFactory == null) { throw new IllegalArgumentException("metadataFactory cannot be null"); } mConnection = connection; mMetadataFactory = metadataFactory; mIfmapJHelper = helper; } /** * Executes one single poll request and returns the received data. * * @throws ConnectionException * @throws PollException * if something goes wrong while polling for new results */ @Override public PollResult call() throws ConnectionException { log.debug("starting poll request ..."); de.hshannover.f4.trust.ifmapj.messages.PollResult pollResult = mConnection.poll(); printErrors(pollResult.getErrorResults()); Map<String, List<ResultItem>> searchResults = new HashMap<String, List<ResultItem>>(); for (SearchResult searchResult : pollResult.getResults()) { switch (searchResult.getType()) { case updateResult: log.debug("processing update list ..."); List<ResultItem> updateResultItems = transformResultItems(searchResult.getResultItems(), ResultItemTypeEnum.UPDATE); addResultItems(searchResults, searchResult.getName(), updateResultItems); break; case searchResult: log.debug("processing search list ..."); List<ResultItem> searchResultItems = transformResultItems(searchResult.getResultItems(), ResultItemTypeEnum.SEARCH); addResultItems(searchResults, searchResult.getName(), searchResultItems); break; case deleteResult: log.debug("processing delete list ..."); List<ResultItem> deleteResultItems = transformResultItems(searchResult.getResultItems(), ResultItemTypeEnum.DELETE); addResultItems(searchResults, searchResult.getName(), deleteResultItems); break; case notifyResult: log.debug("processing notify list ..."); List<ResultItem> notifyResultItems = transformResultItems(searchResult.getResultItems(), ResultItemTypeEnum.NOTIFY); addResultItems(searchResults, searchResult.getName(), notifyResultItems); break; default: log.info(searchResult.getType() + " result skipped"); break; } } List<ResultItem> results = filterDeleteResultItems(searchResults); log.debug("finish poll request."); return new PollResult(results); } private void printErrors(Collection<IfmapErrorResult> errorResults) { if (errorResults.size() > 0) { for (IfmapErrorResult error : errorResults) { log.error(error.toString()); } } } private List<ResultItem> filterDeleteResultItems(Map<String, List<ResultItem>> searchResults) { // find linked subscriptions List<Set<String>> linkedSubscriptionList = findLinkedSubscriptions(searchResults); // filter DELETE-ResultItems int filteredCount = 0; List<ResultItem> results = new ArrayList<>(); for (Entry<String, List<ResultItem>> entry : searchResults.entrySet()) { for (ResultItem item : entry.getValue()) { if (item.getType() == ResultItemTypeEnum.DELETE) { // only for DELETE-ResultItems Set<String> linkedSubSet = findLinkedSubscriptionSet(entry.getKey(), linkedSubscriptionList); if (linkedSubSet == null || containsOtherSubscrptions(searchResults, item, linkedSubSet)) { // check DELETE-ResultItems that are included in all linked subscriptions or is null results.add(item); } else { filteredCount++; } } else { results.add(item); } } } if (filteredCount > 0) { log.debug("removed " + filteredCount + " DELETE-ResultItem(s) because not included in the other SearchResults"); } return results; } private Set<String> findLinkedSubscriptionSet(String subscription, List<Set<String>> linkedSubscriptions) { for (Set<String> set : linkedSubscriptions) { if (set.contains(subscription)) { return set; } } return null; } private List<Set<String>> findLinkedSubscriptions(Map<String, List<ResultItem>> searchResults) { List<Set<String>> linkedSubscriptions = new ArrayList<Set<String>>(); for (Entry<String, List<ResultItem>> entry : searchResults.entrySet()) { for (ResultItem item : entry.getValue()) { Set<String> subscriptions = findLinkedSubscriptions(searchResults, item); if (subscriptions.size() > 1) { addSubscriptionSet(linkedSubscriptions, subscriptions); } } } return linkedSubscriptions; } private void addSubscriptionSet(List<Set<String>> linkedSubscriptions, Set<String> subscriptions) { for (Set<String> set : linkedSubscriptions) { for (String s : subscriptions) { if (set.contains(s)) { set.addAll(subscriptions); return; } } } linkedSubscriptions.add(subscriptions); } private Set<String> findLinkedSubscriptions(Map<String, List<ResultItem>> searchResults, ResultItem resultItem) { Set<String> subscriptions = new HashSet<String>(); for (Entry<String, List<ResultItem>> entry : searchResults.entrySet()) { if (entry.getValue().contains(resultItem)) { subscriptions.add(entry.getKey()); } } return subscriptions; } private boolean containsOtherSubscrptions(Map<String, List<ResultItem>> searchResults, ResultItem item, Set<String> subscriptions) { for (String key : searchResults.keySet()) { if (subscriptions.contains(key)) { // only for linked subscriptions if (!containsSubscription(searchResults, item, key)) { return false; } } } return true; } private boolean containsSubscription(Map<String, List<ResultItem>> searchResults, ResultItem item, String subscriptionName) { List<ResultItem> results = searchResults.get(subscriptionName); return results.contains(item); } private void addResultItems(Map<String, List<ResultItem>> searchResults, String subscriptionName, List<ResultItem> results) { if (subscriptionName == null) { log.warn("SearchResult name is null! Nothing stored!"); return; } if (searchResults.containsKey(subscriptionName)) { searchResults.get(subscriptionName).addAll(results); } else { searchResults.put(subscriptionName, results); } } /** * Maps a list of ifmapj * {@link de.hshannover.f4.trust.ifmapj.messages.ResultItem}s items to a * list of internal {@link ResultItem}s. */ List<ResultItem> transformResultItems(List<de.hshannover.f4.trust.ifmapj.messages.ResultItem> ifmapjResultItems, ResultItemTypeEnum type) { List<ResultItem> items = new ArrayList<>(); for (de.hshannover.f4.trust.ifmapj.messages.ResultItem item : ifmapjResultItems) { log.debug("processing result item '" + item + "'"); ResultItem resultItem = transformResultItem(item, type); if (resultItem.getMetadata().size() > 0) { items.add(resultItem); } } return items; } /** * Transforms a ifmapj * {@link de.hshannover.f4.trust.ifmapj.messages.ResultItem} into a internal * {@link ResultItem}. */ ResultItem transformResultItem(de.hshannover.f4.trust.ifmapj.messages.ResultItem ifmapjResultItem, ResultItemTypeEnum type) { List<Document> metadataDocuments = ifmapjResultItem.getMetadata(); List<InternalMetadata> metadata = new ArrayList<>(metadataDocuments.size()); for (Document d : metadataDocuments) { InternalMetadata m = mMetadataFactory.createMetadata(d); if(type == ResultItemTypeEnum.NOTIFY) { m.switchToNotify(); } metadata.add(m); } if (ifmapjResultItem.holdsLink()) { InternalIdentifier id1 = mIfmapJHelper.ifmapjIdentifierToInternalIdentifier(ifmapjResultItem .getIdentifier1()); InternalIdentifier id2 = mIfmapJHelper.ifmapjIdentifierToInternalIdentifier(ifmapjResultItem .getIdentifier2()); return new ResultItem(id1, id2, metadata, type); } else { InternalIdentifier id = mIfmapJHelper.extractSingleIdentifier(ifmapjResultItem); return new ResultItem(id, null, metadata, type); } } }