View Javadoc
1   /*
2    * Copyright (C) 2021 B3Partners B.V.
3    */
4   package nl.b3p.brmo.loader.util;
5   
6   import java.io.IOException;
7   import java.io.StringReader;
8   import java.io.StringWriter;
9   import java.sql.SQLException;
10  import javax.xml.XMLConstants;
11  import javax.xml.parsers.DocumentBuilder;
12  import javax.xml.parsers.DocumentBuilderFactory;
13  import javax.xml.parsers.ParserConfigurationException;
14  import javax.xml.transform.OutputKeys;
15  import javax.xml.transform.Result;
16  import javax.xml.transform.Transformer;
17  import javax.xml.transform.TransformerConfigurationException;
18  import javax.xml.transform.TransformerException;
19  import javax.xml.transform.TransformerFactory;
20  import javax.xml.transform.dom.DOMSource;
21  import javax.xml.transform.stream.StreamResult;
22  import javax.xml.xpath.XPathConstants;
23  import javax.xml.xpath.XPathExpression;
24  import javax.xml.xpath.XPathExpressionException;
25  import javax.xml.xpath.XPathFactory;
26  import nl.b3p.brmo.loader.StagingProxy;
27  import nl.b3p.brmo.loader.entity.Bericht;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.atteo.xmlcombiner.XmlCombiner;
31  import org.w3c.dom.Document;
32  import org.w3c.dom.Element;
33  import org.xml.sax.InputSource;
34  import org.xml.sax.SAXException;
35  
36  public class RsgbWOZTransformer extends RsgbTransformer {
37    private static final Log LOG = LogFactory.getLog(RsgbWOZTransformer.class);
38    private static final String STUF_GEEN_WAARDE = "geenWaarde";
39    private final StagingProxy staging;
40  
41    public RsgbWOZTransformer(String pathToXsl, StagingProxy staging)
42        throws TransformerConfigurationException, ParserConfigurationException {
43      super(pathToXsl);
44      this.staging = staging;
45    }
46  
47    protected static Document merge(String oldDBxml, String newDBxml)
48        throws XPathExpressionException, ParserConfigurationException, IOException, SAXException {
49      final XPathExpression expression = XPathFactory.newInstance().newXPath().compile("/root/data");
50  
51      DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
52      // to prevent XXE
53      docBuilderFactory.setExpandEntityReferences(false);
54      docBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
55      docBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
56      docBuilderFactory.setIgnoringElementContentWhitespace(true);
57      docBuilderFactory.setNamespaceAware(false);
58  
59      DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
60      Document base = docBuilder.parse(new InputSource(new StringReader(oldDBxml)));
61  
62      Element old = (Element) expression.evaluate(base, XPathConstants.NODE);
63      if (old == null) {
64        throw new IOException(oldDBxml + ": expression does not evaluate to node");
65      }
66      LOG.trace("oldDBxml bericht is: " + oldDBxml);
67      LOG.trace("newDBxml bericht is: " + newDBxml);
68      Document merge = docBuilder.parse(new InputSource(new StringReader(newDBxml)));
69  
70      /*
71         (1)Voor elke node in merge, kijk of hij bestaat in base
72             zo nee, importeer
73             zo ja, kijk of dit het een na diepste niveau is
74                 zo ja,
75                     ga voor elk childnode na of deze bestaat
76                         zo nee, importeer
77                         zo ja, overschrijf waarde
78                 zo nee, recurse in (1)
79      */
80      XmlCombiner combiner = new XmlCombiner();
81      combiner.combine(base);
82      combiner.combine(merge);
83      return combiner.buildDocument();
84    }
85  
86    protected static String print(Document doc) throws TransformerException {
87      TransformerFactory transformerFactory = TransformerFactory.newInstance();
88      // to prevent XXE
89      transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
90      transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
91      transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
92  
93      Transformer transformer = transformerFactory.newTransformer();
94      transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
95      transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
96      transformer.setOutputProperty(OutputKeys.INDENT, "no");
97      DOMSource source = new DOMSource(doc);
98      StringWriter sw = new StringWriter();
99      Result result = new StreamResult(sw);
100     transformer.transform(source, result);
101     return sw.toString();
102   }
103 
104   @Override
105   public String transformToDbXml(Bericht bericht)
106       throws SAXException, IOException, TransformerException {
107     String current = super.transformToDbXml(bericht);
108     StringBuilder loadLog = new StringBuilder();
109 
110     LOG.debug("actuele bericht is: " + bericht);
111     try {
112       Bericht previousBericht = staging.getPreviousBericht(bericht, loadLog);
113       if (previousBericht != null) {
114         LOG.debug("vorige bericht is: " + previousBericht);
115         if (null == previousBericht.getDbXml()) {
116           // TODO mogelijk op te lossen door previousBericht nogmaals te transformeren via
117           //      oldDBXml = super.transformToDbXml(previousBericht);
118           //      voor nu zetten we de pipelining.enabled op false in de
119           // web.xml/context.xml
120           //      .
121           //      Het probleem is dat het vorige bericht nog in de pipeline kan zitten en
122           // niet committed
123           //      is naar de bericht tabel
124           LOG.warn("Er is wel een vorig bericht, maar de DB_XML ontbreekt");
125         }
126         Document d = merge(previousBericht.getDbXml(), current);
127 
128         String mergedDBXML = print(d);
129         bericht.setDbXml(mergedDBXML);
130         LOG.trace("mergedDBXML bericht is: " + mergedDBXML);
131         current = mergedDBXML;
132       }
133     } catch (SQLException
134         | XPathExpressionException
135         | ParserConfigurationException
136         | IOException
137         | SAXException ex) {
138       LOG.error("Vorige bericht kon niet worden opgehaald: ", ex);
139     }
140     LOG.debug(loadLog);
141     return current;
142   }
143 }