/* * This program is part of the OpenLMIS logistics management information system platform software. * Copyright © 2013 VillageReach * * 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.  For additional information contact info@OpenLMIS.org.  */ package org.openlmis.web.service; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import com.sun.syndication.feed.atom.Content; import com.sun.syndication.feed.atom.Entry; import com.sun.syndication.feed.atom.Feed; import com.sun.syndication.io.FeedException; import com.sun.syndication.io.WireFeedOutput; import org.apache.log4j.Logger; import org.ict4h.atomfeed.server.service.EventFeedService; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.openlmis.core.exception.DataException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import static java.lang.String.format; import static java.util.regex.Pattern.compile; import static org.apache.commons.lang3.StringUtils.EMPTY; import static org.apache.commons.lang3.StringUtils.isEmpty; /** * This service handles Atom feeds generation. */ @Service public class VendorEventFeedService { @Autowired EventFeedService eventFeedService; private static final Pattern XML_CDATA_PATTERN = compile("(?:<!\\[CDATA\\[)(.+)(?:\\]\\]>)"); private static final String VENDOR_MAPPING_TEMPLATE = "vendorMapping_%s_%s.xml"; private static final Logger logger = Logger.getLogger(VendorEventFeedService.class); public String getRecentFeed(String requestURL, String vendor, String category) { try { Feed feed = eventFeedService.getRecentFeed(new URI(requestURL), category); mapFeedBasedOnVendorAndCategory(feed, vendor, category); return new WireFeedOutput().outputString(feed); } catch (Exception e) { logger.error("error occurred while getting recent feeds", e); throw new RuntimeException("Unexpected error", e); //TODO } } public String getEventFeed(String requestURL, String vendor, String category, int feedNumber) { try { Feed feed = eventFeedService.getEventFeed(new URI(requestURL), category, feedNumber); try { mapFeedBasedOnVendorAndCategory(feed, vendor, category); } catch (IOException e) { logger.error(e); } return new WireFeedOutput().outputString(feed); } catch (URISyntaxException | FeedException e) { logger.error("error occurred while getting feed for feedNumber: " + feedNumber, e); throw new RuntimeException("Error serializing feed.", e); } } File vendorMappingTemplate(String templateName) { File vendorTemplate = null; try { vendorTemplate = new ClassPathResource(templateName).getFile(); } catch (IOException e) { logger.warn(e); } return vendorTemplate; } private void mapFeedBasedOnVendorAndCategory(Feed feed, String vendor, String category) throws IOException { String templateName = format(VENDOR_MAPPING_TEMPLATE, vendor, category); File templateFile; if (isEmpty(vendor) || (templateFile = vendorMappingTemplate(templateName)) == null) { logger.warn(format("using default template for vendor: %s and category: %s", feed.getTitle(), vendor, category)); return; } Map<String, String> map = createTemplateMap(templateFile); List<Entry> feedEntries = feed.getEntries(); for (Entry entry : feedEntries) { List<Content> contentList = entry.getContents(); for (Content content : contentList) { JsonNode rootNode = convertToTemplate(map, parseAtomFeedContent(content.getValue())); content.setValue("<![CDATA[" + rootNode.toString() + "]]>"); } } } String parseAtomFeedContent(String atomFeedContentValue) { Matcher matcher = XML_CDATA_PATTERN.matcher(atomFeedContentValue); String atomFeedContent = EMPTY; if (matcher.find() && matcher.groupCount() == 1) { atomFeedContent = matcher.group(1); } return atomFeedContent; } private JsonNode convertToTemplate(Map<String, String> map, String value) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); JsonNode rootNode = objectMapper.readTree(value); ObjectNode returnedNode = new ObjectNode(JsonNodeFactory.instance); Iterator<Map.Entry<String, JsonNode>> iterator = rootNode.fields(); while (iterator.hasNext()) { Map.Entry<String, JsonNode> mapEntry = iterator.next(); String fieldName = mapEntry.getKey(); String mappedName = map.get(fieldName); if (mappedName != null) { returnedNode.put(mappedName, mapEntry.getValue()); } else { returnedNode.put(fieldName, mapEntry.getValue()); } } return returnedNode; } private Map<String, String> createTemplateMap(File templateFile) { Document document; try { SAXBuilder saxBuilder = new SAXBuilder(); document = saxBuilder.build(templateFile); } catch (IOException | JDOMException e) { throw new DataException(e.getMessage()); } Map<String, String> map = new HashMap<>(); List<Element> children = document.getRootElement().getChildren(); for (Element child : children) { String openLmisName = child.getAttributeValue("openlmis-name"); String vendorName = child.getAttributeValue("vendor-name"); map.put(openLmisName, vendorName); } return map; } }