Java tutorial
/** * ESUP-Portail Lecture - Copyright (c) 2006 ESUP-Portail consortium * For any information please refer to http://esup-helpdesk.sourceforge.net * You may obtain a copy of the licence at http://www.esup-portail.org/license/ */ package org.esupportail.lecture.domain.model; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Node; import org.dom4j.XPath; import org.dom4j.XPathException; import org.esupportail.lecture.domain.DomainTools; import org.esupportail.lecture.exceptions.domain.ComputeItemsException; import org.esupportail.lecture.exceptions.domain.MappingNotFoundException; import org.esupportail.lecture.exceptions.domain.Xml2HtmlException; /** * Source element : a source can be a managed or personal one. * A source is got from an URL, given by source Profile. * This source contains an xmlStream that, after xslt parsing, provides a list * of items that can be displed on user interface * @author gbouteil * */ public abstract class Source implements Element, Serializable { /* *************************** PROPERTIES ******************************** */ /** * log instance. */ protected static final Log LOG = LogFactory.getLog(Source.class); /** * */ private static final long serialVersionUID = 1L; /** * profile Id of the source. */ private String profileId; /** * sourceProfile associated to this source. */ private SourceProfile profile; /** * Optional : DTD of the source (one of these parameter is required : xmlns, xmlType, dtd,rootElement). */ private String dtd; /** * Optional : xmlType of the source (one of these parameter is required : xmlns, xmlType, dtd,rootElement). */ private String xmlType; /** * Optional : xmlns of the source (one of these parameter is required : xmlns, xmlType, dtd,rootElement). */ private String xmlns; /** * Optionnal : rootElement of the xmlStream. * (one of these parameter is required : xmlns, xmlType, dtd,rootElement) */ private String rootElement; /** * xmlStream (XML content) of the source. */ private String xmlStream = ""; /** * flag used to know if computeXslt() used one time or not. */ private boolean xsltComputed; /** * URL of the xslt file to display xml content. */ private String xsltURL; /** * URL of the xslt file to display xml content on mobile. */ private String mobileXsltURL; /** * Xpath to access item in the XML source file correspoding to this source profile. */ private String itemXPath; // TODO (RB <-- GB) Not used ??? /** * Map of namespaces used by Xpath (key: NamesSpace prefix; value: NamaSpace URI). */ private HashMap<String, String> xPathNameSpaces; /** * Items List of this source. */ private List<Item> items = new ArrayList<Item>(); /** * flag used to know if items are computed. */ private boolean itemComputed; /* *************************** INIT ******************************** */ /** * Constructor. * @param sp sourceProfile associated to this source */ protected Source(final SourceProfile sp) { if (LOG.isDebugEnabled()) { LOG.debug("Source(" + sp.getId() + ")"); } profile = sp; profileId = sp.getId(); itemComputed = false; xPathNameSpaces = new HashMap<String, String>(); } /* *************************** METHODS ******************************** */ /** * Returns the source URL (defined in the source profile). * @return sourceURL */ private String getSourceURL() { return getProfile().getSourceURL(); } /** * @return the name of the source */ public String getName() { return getProfile().getName(); } /** * @return the ttl of the sourceProfile for this source */ public int getTtl() { return getProfile().getTtl(); } /** * @return Returns the itemXPath. * @throws MappingNotFoundException */ private String getItemXPath() throws MappingNotFoundException { if (LOG.isDebugEnabled()) { LOG.debug("id=" + this.profileId + " - getItemXPath()"); } computeXslt(); return itemXPath; } /** * @return Returns the xsltURL. * @throws MappingNotFoundException */ private String getXsltURL() throws MappingNotFoundException { if (LOG.isDebugEnabled()) { LOG.debug("id=" + this.profileId + " - getXsltURL()"); } computeXslt(); return xsltURL; } /** * @return Returns the xsltURL. * @throws MappingNotFoundException */ private String getMobileXsltURL() throws MappingNotFoundException { if (LOG.isDebugEnabled()) { LOG.debug("id=" + this.profileId + " - getMobileXsltURL()"); } computeXslt(); return mobileXsltURL; } /** * @return the hashMap containing xPathNameSpaces * @throws MappingNotFoundException */ private HashMap<String, String> getXPathNameSpaces() throws MappingNotFoundException { computeXslt(); return xPathNameSpaces; } /** * find item XPath and url of Xslt file, in list of Mappings in channel (from mapping file). * In fonction of dtd, xmlType, xmlns or XML root element of the source XML content * @throws MappingNotFoundException */ private void computeXslt() throws MappingNotFoundException { if (LOG.isDebugEnabled()) { LOG.debug("id = " + this.profileId + " - computeXslt()"); } Channel channel = DomainTools.getChannel(); if (LOG.isDebugEnabled()) { LOG.debug("Source::computeXslt() : " + profileId); LOG.debug("DTD : " + dtd); LOG.debug("xmlType : " + xmlType); LOG.debug("xmlns : " + xmlns); LOG.debug("rootElement : " + rootElement); } if (!xsltComputed) { SourceProfile p = getProfile(); xsltURL = p.getXsltURL(); mobileXsltURL = p.getMobileXsltURL(); itemXPath = p.getItemXPath(); xPathNameSpaces = p.getXPathNameSpaces(); if (xsltURL == null || itemXPath == null || xPathNameSpaces.size() == 0) { Mapping m = new Mapping(); String url = getSourceURL(); if (url != null) { //Try to find a mapping from url m = channel.getMappingBySourceURL(url); } else { LOG.error("Source " + this.profileId + "does not have any URL defined"); } //no mapping find from url so using XML content caracteristics if (m == null) { if (dtd != null) { m = channel.getMappingByDtd(dtd); } } if (m == null) { if (xmlType != null) { m = channel.getMappingByXmlType(xmlType); } } if (m == null) { if (xmlns != null) { m = channel.getMappingByXmlns(xmlns); } } if (m == null) { if (rootElement != null) { m = channel.getMappingByRootElement(rootElement); } } if (m == null) { LOG.warn("Source " + profileId + " does not have any entry key to find xslt information : " + "no dtd, xmlType, xmlns, rootElement"); String errorMsg = "Mapping not found for source " + profileId; LOG.error(errorMsg); throw new MappingNotFoundException("Mapping not found for source " + profileId); } if (xsltURL == null || xsltURL.equals("")) { xsltURL = m.getXsltUrl(); } if (mobileXsltURL == null || mobileXsltURL.equals("")) { mobileXsltURL = m.getMobileXsltUrl(); } if (itemXPath == null || itemXPath.equals("")) { itemXPath = m.getItemXPath(); } if (xPathNameSpaces.size() == 0) { xPathNameSpaces = m.getXPathNameSpaces(); } } xsltComputed = true; } } /** * Make Items objects in fonction of itemXPath, xsltURL, xmlStream. * @throws ComputeItemsException */ @SuppressWarnings("unchecked") synchronized private void computeItems() throws ComputeItemsException { if (LOG.isDebugEnabled()) { LOG.debug("id=" + this.profileId + " - computeItems()"); } if (!itemComputed) { try { //create dom4j document Document document = DocumentHelper.parseText(xmlStream); // get encoding String encoding = document.getXMLEncoding(); //lauch Xpath find String x = getItemXPath(); XPath xpath = document.createXPath(x); xpath.setNamespaceURIs(getXPathNameSpaces()); List<Node> list = xpath.selectNodes(document); //List<Node> list = document.selectNodes(getItemXPath()); Iterator<Node> iter = list.iterator(); while (iter.hasNext()) { Node node = iter.next(); Item item = new Item(this); StringBuffer xml = new StringBuffer("<?xml version=\"1.0\" encoding=\""); xml.append(encoding); xml.append("\" ?>"); xml.append(node.asXML()); String xmlAsString = xml.toString(); String htmlContent = xml2html(xmlAsString, getXsltURL(), encoding); item.setHtmlContent(htmlContent); String MobileHtmlContent = xml2html(xmlAsString, getMobileXsltURL(), encoding); item.setMobileHtmlContent(MobileHtmlContent); //find MD5 of item content for his ID byte[] hash = MessageDigest.getInstance("MD5").digest(xmlAsString.getBytes()); StringBuffer hashString = new StringBuffer(); for (int i = 0; i < hash.length; ++i) { String hex = Integer.toHexString(hash[i]); if (hex.length() == 1) { hashString.append('0'); hashString.append(hex.charAt(hex.length() - 1)); } else { hashString.append(hex.substring(hex.length() - 2)); } } item.setId(hashString.toString()); items.add(item); } } catch (DocumentException e) { String errorMsg = "Error parsing XML content of the source"; LOG.error(errorMsg, e); throw new ComputeItemsException(errorMsg, e); } catch (NoSuchAlgorithmException e) { String errorMsg = "MD5 algorithm not supported"; LOG.error(errorMsg, e); throw new ComputeItemsException(errorMsg, e); } catch (XPathException e) { String errorMsg = "Xpath with NameSpace not specified in mappings.xml"; LOG.error(errorMsg, e); throw new ComputeItemsException(errorMsg, e); } catch (MappingNotFoundException e) { String errorMsg = "Impossible to get itemXPath,XPathNameSpaces and xsltURL"; LOG.error(errorMsg, e); throw new ComputeItemsException(errorMsg, e); } catch (Xml2HtmlException e) { String errorMsg = "Impossible to make html content"; LOG.error(errorMsg, e); throw new ComputeItemsException(errorMsg, e); } itemComputed = true; } } /** * transform a xml String in a html String with an XSLT. * @param xml to transform * @param xsltFileURL URL of XSLT file * @param encoding of xml to transform * @return html content * @throws Xml2HtmlException */ private String xml2html(final String xml, final String xsltFileURL, final String encoding) throws Xml2HtmlException { if (LOG.isDebugEnabled()) { LOG.debug("id=" + this.profileId + " - xml2html(xml,xsltFileURL)"); } LOG.debug("voici le xsltFileUrl : " + xsltFileURL); String ret = null; try { // 1. Instantiate a TransformerFactory. TransformerFactory tFactory = TransformerFactory.newInstance(); // 2. Use the TransformerFactory to process the stylesheet Source and // generate a Transformer. Transformer transformer; String xsltFileContent = DomainTools.getXsltFile(xsltFileURL); //create dom4j document Document document = DocumentHelper.parseText(xsltFileContent); // get encoding String xslEncoding = document.getXMLEncoding(); if (xslEncoding == null) { xslEncoding = "UTF-8"; } LOG.debug("voici le xsltFileContent : " + xsltFileContent); ByteArrayInputStream inputXsltFile = new ByteArrayInputStream(xsltFileContent.getBytes(xslEncoding)); StreamSource sourceXsltFile = new StreamSource(inputXsltFile); transformer = tFactory.newTransformer(sourceXsltFile); // 3. Use the Transformer to transform an XML Source and send the // output to a Result object. ByteArrayInputStream inputStream = new ByteArrayInputStream(xml.getBytes(encoding)); StreamSource source = new StreamSource(inputStream); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); StreamResult result = new StreamResult(outputStream); transformer.transform(source, result); ret = outputStream.toString("UTF-8"); } catch (TransformerConfigurationException e) { String errorMsg = "Error in XSTL Transformation configuration"; LOG.error(errorMsg); throw new Xml2HtmlException(errorMsg, e); } catch (TransformerException e) { String errorMsg = "Error in XSTL Transformation"; LOG.error(errorMsg); throw new Xml2HtmlException(errorMsg, e); } catch (IOException e) { String errorMsg = "IO Error in xml2html"; LOG.error(errorMsg); throw new Xml2HtmlException(errorMsg, e); } catch (DocumentException e) { String errorMsg = "Error determining XSLT encoding"; LOG.error(errorMsg); throw new Xml2HtmlException(errorMsg, e); } return ret; } /** * get Items list of this source. * @return the items lits * @throws ComputeItemsException */ protected List<Item> getItems() throws ComputeItemsException { if (LOG.isDebugEnabled()) { LOG.debug("id=" + this.profileId + " - getItems()"); } computeItems(); return items; } /** * @see java.lang.Object#toString() */ @Override public String toString() { StringBuffer out = new StringBuffer(); out.append("\n dtd --> ").append(getDtd()); out.append("\n rootElement --> ").append(getRootElement()); out.append("\n xmlns --> ").append(getXmlns()); out.append("\n xmlType --> ").append(getXmlType()); out.append("\n xmlStream --> ").append(getXmlStream()); try { out.append("\n xsltURL --> ").append(getXsltURL()); out.append("\n itemXPath --> ").append(getItemXPath()); } catch (MappingNotFoundException e) { out.append("\n MappingNotFoundException !"); } return out.toString(); } /* ************************* ACCESSORS ******************************** */ /** * @return the ID of the sourceProfile associated to this source */ public String getProfileId() { return profileId; } /** * @return the sourceProfile associated to this source */ public SourceProfile getProfile() { return profile; } /** * @return the dtd of source XML content */ private String getDtd() { return dtd; } /** * set the dtd of source XML content. * @param dtd */ public synchronized void setDtd(final String dtd) { this.dtd = dtd; } /** * @return Returns the rootElement. */ private String getRootElement() { return rootElement; } /** * @param rootElement The rootElement to set. */ public void setRootElement(final String rootElement) { this.rootElement = rootElement; } /** * @return Returns the xmlns. */ private String getXmlns() { return xmlns; } /** * @param xmlns The xmlns to set. */ public void setXmlns(final String xmlns) { this.xmlns = xmlns; } /** * @return Returns the xmlType. */ private String getXmlType() { return xmlType; } /** * @param xmlType The xmlType to set. */ public void setXmlType(final String xmlType) { this.xmlType = xmlType; } /** * @return XML Stream (XML content) of the source */ private String getXmlStream() { return xmlStream; } /** * set XML Stream (XML content) of the source. * @param xmlStream */ public void setXmlStream(final String xmlStream) { this.xmlStream = xmlStream; } }