/* * Copyright (c) 2004-2016 Matthew Altman & Stuart Boston * * This file is part of TheTVDB API. * * TheTVDB API is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * TheTVDB API 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with TheTVDB API. If not, see <http://www.gnu.org/licenses/>. * */ package com.omertron.thetvdbapi.tools; import com.omertron.thetvdbapi.TvDbException; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.SAXException; import org.yamj.api.common.exception.ApiExceptionType; import org.yamj.api.common.http.DigestedResponse; import org.yamj.api.common.http.DigestedResponseReader; /** * Generic set of routines to process the DOM model data * * @author Stuart.Boston * */ public class DOMHelper { private static final Logger LOG = LoggerFactory.getLogger(DOMHelper.class); private static final String YES = "yes"; private static final String DEFAULT_CHARSET = "UTF-8"; private static final Charset CHARSET = Charset.forName(DEFAULT_CHARSET); private static final int RETRY_COUNT = 5; // Milliseconds to retry private static final int RETRY_TIME = 250; private static HttpClient httpClient = null; // Constants private static final String ERROR_WRITING = "Error writing the document to {}"; private static final String ERROR_UNABLE_TO_PARSE = "Unable to parse TheTVDb response, please try again later."; private static final int HTTP_STATUS_300 = 300; private static final int HTTP_STATUS_500 = 500; // Hide the constructor protected DOMHelper() { // prevents calls from subclass throw new UnsupportedOperationException(); } public static void setHttpClient(HttpClient newHttpClient) { httpClient = newHttpClient; } /** * Gets the string value of the tag element name passed * * @param element * @param tagName * @return */ public static String getValueFromElement(Element element, String tagName) { NodeList elementNodeList = element.getElementsByTagName(tagName); if (elementNodeList == null) { return ""; } else { Element tagElement = (Element) elementNodeList.item(0); if (tagElement == null) { return ""; } NodeList tagNodeList = tagElement.getChildNodes(); if (tagNodeList == null || tagNodeList.getLength() == 0) { return ""; } return tagNodeList.item(0).getNodeValue(); } } /** * Get a DOM document from the supplied URL * * @param url * @return * @throws com.omertron.thetvdbapi.TvDbException */ public static synchronized Document getEventDocFromUrl(String url) throws TvDbException { Document doc = null; String webPage = getValidWebpage(url); // If the webpage returned is null or empty, quit if(StringUtils.isBlank(webPage)){ return null; } try (InputStream in = new ByteArrayInputStream(webPage.getBytes(CHARSET))) { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); doc = db.parse(in); doc.getDocumentElement().normalize(); } catch (UnsupportedEncodingException ex) { throw new TvDbException(ApiExceptionType.INVALID_URL, "Unable to encode URL", url, ex); } catch (ParserConfigurationException | SAXException | IOException error) { throw new TvDbException(ApiExceptionType.MAPPING_FAILED, ERROR_UNABLE_TO_PARSE, url, error); } return doc; } private static String getValidWebpage(String url) throws TvDbException { // Count the number of times we download the web page int retryCount = 0; // Is the web page valid boolean valid = false; DigestedResponse webPage; while (!valid && (retryCount < RETRY_COUNT)) { retryCount++; webPage = requestWebPage(url); final String content = webPage.getContent(); if (StringUtils.isNotBlank(content)) { // See if the ID is null if (!content.contains("<id>") || content.contains("<id></id>")) { // Wait an increasing amount of time the more retries that happen waiting(retryCount * RETRY_TIME); continue; } valid = true; } // Couldn't get a valid webPage so, quit. if (!valid) { throw new TvDbException(ApiExceptionType.UNKNOWN_CAUSE, webPage.getContent(), webPage.getStatusCode(), url); } return content; } return null; } /** * Convert a DOM document to a string * * @param doc * @return * @throws TransformerException */ public static String convertDocToString(Document doc) throws TransformerException { //set up a transformer TransformerFactory transfac = TransformerFactory.newInstance(); Transformer trans = transfac.newTransformer(); trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, YES); trans.setOutputProperty(OutputKeys.INDENT, YES); //create string from xml tree StringWriter sw = new StringWriter(); StreamResult result = new StreamResult(sw); DOMSource source = new DOMSource(doc); trans.transform(source, result); return sw.toString(); } /** * Write the Document out to a file using nice formatting * * @param doc The document to save * @param localFile The file to write to * @return */ public static boolean writeDocumentToFile(Document doc, String localFile) { try { TransformerFactory transfact = TransformerFactory.newInstance(); Transformer trans = transfact.newTransformer(); trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, YES); trans.setOutputProperty(OutputKeys.INDENT, YES); trans.transform(new DOMSource(doc), new StreamResult(new File(localFile))); return true; } catch (TransformerConfigurationException ex) { LOG.warn(ERROR_WRITING, localFile, ex); return false; } catch (TransformerException ex) { LOG.warn(ERROR_WRITING, localFile, ex); return false; } } /** * Add a child element to a parent element * * @param doc * @param parentElement * @param elementName * @param elementValue */ public static void appendChild(Document doc, Element parentElement, String elementName, String elementValue) { Element child = doc.createElement(elementName); Text text = doc.createTextNode(elementValue); child.appendChild(text); parentElement.appendChild(child); } /** * Wait for a few milliseconds * * @param milliseconds */ private static void waiting(int milliseconds) { long t0, t1; t0 = System.currentTimeMillis(); do { t1 = System.currentTimeMillis(); } while ((t1 - t0) < milliseconds); } private static DigestedResponse requestWebPage(String url) throws TvDbException { try { HttpGet httpGet = new HttpGet(url); httpGet.addHeader("accept", "application/xml"); final DigestedResponse response = DigestedResponseReader.requestContent(httpClient, httpGet, CHARSET); if (response.getStatusCode() == 0) { throw new TvDbException(ApiExceptionType.CONNECTION_ERROR, "Error retrieving URL", url); } else if (response.getStatusCode() >= HTTP_STATUS_500) { throw new TvDbException(ApiExceptionType.HTTP_503_ERROR, response.getContent(), response.getStatusCode(), url); } else if (response.getStatusCode() >= HTTP_STATUS_300) { throw new TvDbException(ApiExceptionType.HTTP_404_ERROR, response.getContent(), response.getStatusCode(), url); } return response; } catch (IOException ex) { throw new TvDbException(ApiExceptionType.CONNECTION_ERROR, "Error retrieving URL", url, ex); } } }