/* * Copyright 2015 Alidays S.p.A. * * 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 it.alidays.mapengine.core.fetch; import it.alidays.mapengine.core.fetch.function.FunctionFactory; import it.alidays.mapengine.core.fetch.function.FunctionFactoryException; import it.alidays.mapengine.enginedirectives.fetch.Bind; import it.alidays.mapengine.enginedirectives.fetch.Entity; import it.alidays.mapengine.enginedirectives.fetch.Fetch; import it.alidays.mapengine.enginedirectives.fetch.ForEach; import it.alidays.mapengine.util.PerformanceUtils; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Fetcher { private static final Logger logger = LoggerFactory.getLogger(Fetcher.class); private static String convertToXPath(String value, Boolean root) { StringBuilder result = new StringBuilder(); String[] pathElements = value.split("/"); for (int i = 0; i < pathElements.length; i++) { String pathElement = pathElements[i]; if (i == (pathElements.length - 1) && pathElement.charAt(0) == '@') { result.append(String.format("/@*[name() = '%s']", pathElement.substring(1))); } else { if (root && i == 0) { result.append(String.format("///%s", pathElement)); } else { result.append(String.format("/*[name() = '%s']", pathElement)); } } } result = result.deleteCharAt(0); return result.toString(); } private final ExecutorService executor; private final String basePath; private final Map<String, ToEntityMethod> toEntityMap; public Fetcher(Fetch fetch) throws FetcherException { this.executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); this.basePath = convertToXPath(fetch.getBasePath(), true); this.toEntityMap = new HashMap<>(); if (fetch.getEntities().isEmpty()) { throw new FetcherException("At least one 'to-entity' node is required"); } else { for (Entity toEntity : fetch.getEntities()) { String entityName = toEntity.getName(); ToEntityMethod toEntityMethod = null; if (toEntity.getForEach() != null) { ForEach forEach = toEntity.getForEach(); toEntityMethod = new ForEachMethod(convertToXPath(forEach.getValue(), false)); if (forEach.getBindings().isEmpty()) { throw new FetcherException(String.format("No bind found for to-entity '%s'", entityName)); } else { for (Bind bind : forEach.getBindings()) { ((ForEachMethod)toEntityMethod).addBinder(bind.getAttribute(), createBinder(bind)); } } } else { throw new FetcherException(String.format("ToEntityMethod not found for to-entity '%s'", entityName)); } this.toEntityMap.put(entityName, toEntityMethod); } } } public void shutdown() { this.executor.shutdownNow(); } public Map<String, List<Map<String, Object>>> run(InputStream inputStream, UUID vuid) throws FetcherException { logger.info("Fetcher started..."); PerformanceUtils.notifyStart(Fetcher.class, "run", vuid); Map<String, List<Map<String, Object>>> result = new HashMap<>(); try { PerformanceUtils.notifyStart(Fetcher.class, "run(Parse XML)", vuid); final SAXReader reader = new SAXReader(); final Document document = reader.read(inputStream); PerformanceUtils.notifyEnd(Fetcher.class, "run(Parse XML)", vuid, logger); PerformanceUtils.notifyStart(Fetcher.class, "run(Get BaseNode)", vuid); Element baseNode = (Element)document.selectSingleNode(this.basePath); PerformanceUtils.notifyEnd(Fetcher.class, "run(Get BaseNode)", vuid, logger); PerformanceUtils.notifyStart(Fetcher.class, "run(Fetch)", vuid); ExecutorCompletionService<FetchEntityResult> service = new ExecutorCompletionService<>(this.executor); int tasks = 0; for (String entity : this.toEntityMap.keySet()) { tasks++; service.submit(new FetchTask(entity, this.toEntityMap.get(entity), baseNode, vuid)); } while (tasks > 0) { FetchEntityResult fetchEntityResult = service.take().get(); result.put(fetchEntityResult.getEntity(), fetchEntityResult.getResult()); tasks--; } PerformanceUtils.notifyEnd(Fetcher.class, "run(Fetch)", vuid, logger); } catch (Exception e) { throw new FetcherException(e); } PerformanceUtils.notifyEnd(Fetcher.class, "run", vuid, logger); logger.info("Fetcher completed"); return result; } private Binder createBinder(Bind bind) throws FetcherException { Binder result = null; if (FunctionFactory.isFunction(bind.getValue())) { try { result = new FunctionBinder(FunctionFactory.makeFunction(bind.getValue())); } catch (FunctionFactoryException ffe) { throw new FetcherException("Failed to create function", ffe); } } else { result = new PathBinder(convertToXPath(bind.getValue(), false)); } return result; } }