001package runwar.undertow;
002
003import java.io.File;
004import java.util.HashMap;
005
006import javax.xml.parsers.DocumentBuilder;
007import javax.xml.parsers.DocumentBuilderFactory;
008
009import org.w3c.dom.Document;
010
011import io.undertow.servlet.api.DeploymentInfo;
012import io.undertow.servlet.api.FilterInfo;
013import io.undertow.servlet.api.ListenerInfo;
014import io.undertow.servlet.api.MimeMapping;
015import io.undertow.servlet.api.ServletInfo;
016
017import java.util.EventListener;
018import java.util.Map;
019
020import runwar.logging.Logger;
021
022import javax.servlet.Filter;
023import javax.servlet.Servlet;
024import javax.servlet.DispatcherType;
025
026import org.w3c.dom.Element;
027import org.w3c.dom.Node;
028import org.w3c.dom.NodeList;
029
030public class WebXMLParser {
031
032    private static Logger log = Logger.getLogger("RunwarLogger");
033
034        /**
035         * Parses the web.xml and configures the context.
036         *
037         * @param webxml
038         * @param info
039         */
040        @SuppressWarnings("unchecked")
041        public static void parseWebXml(File webxml, DeploymentInfo info) {
042                if (!webxml.exists() || !webxml.canRead()) {
043                        log.error("Error reading web.xml! exists:"+webxml.exists()+"readable:"+webxml.canRead());
044                }
045                try {
046                    String webinfPath = webxml.getParentFile().getCanonicalPath();
047                    if (File.separatorChar=='\\') {
048                        webinfPath = webinfPath.replace("\\", "\\\\");
049                    }
050                    trace("parsing %s",webxml.getCanonicalPath());
051                        DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
052                        DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
053                        Document doc = docBuilder.parse(webxml);
054                        // normalize text representation
055                        doc.getDocumentElement().normalize();
056
057                        trace("Root element of the doc is %s", doc.getDocumentElement().getNodeName());
058                        // to hold our servlets
059                        Map<String, ServletInfo> servletMap = new HashMap<String, ServletInfo>();
060                        // to hold our filters
061                        Map<String, FilterInfo> filterMap = new HashMap<String, FilterInfo>();
062                        // do context-param - available to the entire scope of the web
063                        // application
064                        NodeList listOfElements = doc.getElementsByTagName("context-param");
065                        int totalElements = listOfElements.getLength();
066
067                        trace("Total no of context-params: %s", totalElements);
068                        for (int s = 0; s < totalElements; s++) {
069                                Node fstNode = listOfElements.item(s);
070                                if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
071                                        Element fstElmnt = (Element) fstNode;
072                                        NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("param-name");
073                                        Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
074                                        NodeList fstNm = fstNmElmnt.getChildNodes();
075                                        String pName = (fstNm.item(0)).getNodeValue().trim();
076                                        trace("context param name: %s", pName);
077                                        NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("param-value");
078                                        Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
079                                        NodeList lstNm = lstNmElmnt.getChildNodes();
080                                        String pValue = (lstNm.item(0)).getNodeValue().trim();
081                                        // replace any /WEB-INF paths with the real path
082                                        // this was for adobe CF, which is just nuts... undoing for now
083                                        //pValue = pValue.replaceAll(".?/WEB-INF", webinfPath);
084                                        trace("context param value: %s", pValue);
085                                        info.addServletContextAttribute(pName, pValue);
086                                        info.addInitParameter(pName, pValue);
087                                }
088                        }
089                        // do listener
090                        listOfElements = doc.getElementsByTagName("listener");
091                        totalElements = listOfElements.getLength();
092                        trace("Total no of listeners: %s", totalElements);
093                        for (int s = 0; s < totalElements; s++) {
094                                Node fstNode = listOfElements.item(s);
095                                if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
096                                        Element fstElmnt = (Element) fstNode;
097                                        NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("listener-class");
098                                        Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
099                                        NodeList fstNm = fstNmElmnt.getChildNodes();
100                                        String pName = (fstNm.item(0)).getNodeValue().trim();
101                                        trace("Param name: %s", pName);
102                                        ListenerInfo listener = new ListenerInfo((Class<? extends EventListener>) info.getClassLoader()
103                                                        .loadClass(pName));
104                                        info.addListener(listener);
105                                }
106                        }
107                        // do filter
108                        listOfElements = doc.getElementsByTagName("filter");
109                        totalElements = listOfElements.getLength();
110                        trace("Total no of filters: %s", totalElements);
111                        for (int s = 0; s < totalElements; s++) {
112                                Node fstNode = listOfElements.item(s);
113                                if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
114                                        Element fstElmnt = (Element) fstNode;
115                                        NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("filter-name");
116                                        Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
117                                        NodeList fstNm = fstNmElmnt.getChildNodes();
118                                        String pName = (fstNm.item(0)).getNodeValue().trim();
119                                        trace("Filter name: %s", pName);
120                                        NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("filter-class");
121                                        Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
122                                        NodeList lstNm = lstNmElmnt.getChildNodes();
123                                        String pValue = (lstNm.item(0)).getNodeValue().trim();
124                                        trace("Filter class: %s", pValue);
125                                        // create the filter
126                                        FilterInfo filter = new FilterInfo(pName, (Class<? extends Filter>) info.getClassLoader()
127                                                        .loadClass(pValue));
128                                        // do init-param - available in the context of a servlet
129                                        // or filter in the web application
130                                        NodeList listOfInitParams = fstElmnt.getElementsByTagName("init-param");
131                                        int totalInitParams = listOfInitParams.getLength();
132                                        trace("Total no of init-params: %s", totalInitParams);
133                                        for (int i = 0; i < totalInitParams; i++) {
134                                                Node inNode = listOfInitParams.item(i);
135                                                if (inNode.getNodeType() == Node.ELEMENT_NODE) {
136                                                        Element inElmnt = (Element) inNode;
137                                                        NodeList inNmElmntLst = inElmnt.getElementsByTagName("param-name");
138                                                        Element inNmElmnt = (Element) inNmElmntLst.item(0);
139                                                        NodeList inNm = inNmElmnt.getChildNodes();
140                                                        String inName = (inNm.item(0)).getNodeValue().trim();
141                                                        trace("Param name: %s", inName);
142                                                        NodeList inValElmntLst = inElmnt.getElementsByTagName("param-value");
143                                                        Element inValElmnt = (Element) inValElmntLst.item(0);
144                                                        NodeList inVal = inValElmnt.getChildNodes();
145                                                        String inValue = (inVal.item(0)).getNodeValue().trim();
146                                                        trace("Param value: %s", inValue);
147                                                        // add the param
148                                                        filter.addInitParam(inName, inValue);
149                                                }
150                                        }
151                                        // do async-supported
152                                        NodeList ldElmntLst = fstElmnt.getElementsByTagName("async-supported");
153                                        if (ldElmntLst != null && ldElmntLst.getLength()>0) {
154                                                Element ldElmnt = (Element) ldElmntLst.item(0);
155                                                NodeList ldNm = ldElmnt.getChildNodes();
156                                                String pAsync = (ldNm.item(0)).getNodeValue().trim();
157                                                trace("Async supported: %s", pAsync);
158                                                filter.setAsyncSupported(Boolean.valueOf(pAsync));
159                                        }
160                                        // add to map
161                                        filterMap.put(pName, filter);
162                                }
163                                // add filters
164                                info.addFilters(filterMap.values());
165                        }
166                        // do filter mappings
167                        if (!filterMap.isEmpty()) {
168                                listOfElements = doc.getElementsByTagName("filter-mapping");
169                                totalElements = listOfElements.getLength();
170                                trace("Total no of filter-mappings: %s", totalElements);
171                                for (int s = 0; s < totalElements; s++) {
172                                        Node fstNode = listOfElements.item(s); 
173                                        if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
174                                                Element fstElmnt = (Element) fstNode;
175                                                NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("filter-name");
176                                                Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
177                                                NodeList fstNm = fstNmElmnt.getChildNodes();
178                                                String pName = (fstNm.item(0)).getNodeValue().trim();
179                                                trace("Param name: %s", pName);
180                                                // lookup the filter info
181                                                FilterInfo filter = filterMap.get(pName);
182                                                // add the mapping
183                                                if (filter != null) {
184                                                        NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("url-pattern");
185                                                        if(lstNmElmntLst != null &&  lstNmElmntLst.item(0) != null) {
186                                                                Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
187                                                                NodeList lstNm = lstNmElmnt.getChildNodes();
188                                                                String pValue = (lstNm.item(0)).getNodeValue().trim();
189                                                                NodeList dstNmElmntLst = fstElmnt.getElementsByTagName("dispatcher");
190                                                                
191                                                                if ( dstNmElmntLst == null || dstNmElmntLst.getLength() == 0 ){
192                                                                        trace("Request FilterURL Mapping: " + pName + " is %s", pValue);
193                                                                        info.addFilterUrlMapping( pName, pValue, DispatcherType.valueOf( "REQUEST") );
194                                                                } else {
195                                                                        int totalDispatchers = dstNmElmntLst.getLength();
196                                                                        for(int i = 0; i < totalDispatchers; i++){
197                                                                                Element dstNmElmnt = (Element) dstNmElmntLst.item(i);
198                                                                                NodeList dstNm = dstNmElmnt.getChildNodes();
199                                                                                String dValue = (dstNm.item(0)).getNodeValue().trim();
200                                                                                info.addFilterUrlMapping( pName, pValue, DispatcherType.valueOf( dValue ) );
201                                                                                trace("FilterURL: %s", pName);
202                                                                                trace("FilterURL: %s", pValue);
203                                                                        }
204                                                                }
205                                                        }
206                                                        lstNmElmntLst = fstElmnt.getElementsByTagName("servlet-name");
207                                                        if (lstNmElmntLst != null && lstNmElmntLst.item(0) != null) {
208                                                                Element inNmElmnt = (Element) lstNmElmntLst.item(0);
209                                                                NodeList inNm = inNmElmnt.getChildNodes();
210                                                                String pValue = (inNm.item(0)).getNodeValue().trim();
211                                                                info.addFilterServletNameMapping(pName, pValue, DispatcherType.valueOf("REQUEST"));
212                                                                trace("Filter Servlet: " + pName + " for %s", pValue);
213                                                        }
214                                                } else {
215                                                        log.warnf("No servlet found for %s", pName);
216                                                }
217                                        }
218                                }
219                        }
220                        // do servlet
221                        NodeList listOfServlets = doc.getElementsByTagName("servlet");
222                        totalElements = listOfServlets.getLength();
223                        trace("Total no of servlets: %s", totalElements);
224                        for (int s = 0; s < listOfServlets.getLength(); s++) {
225                                Node fstNode = listOfServlets.item(s);
226                                if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
227                                        Element fstElmnt = (Element) fstNode;
228                                        NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("servlet-name");
229                                        Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
230                                        NodeList fstNm = fstNmElmnt.getChildNodes();
231                                        String pName = (fstNm.item(0)).getNodeValue().trim();
232                                        trace("Adding servlet to undertow: ************* %s *************", pName);
233                                        trace("Param name: %s", pName);
234                                        NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("servlet-class");
235                                        Element lstNmElmnt = (Element) lstNmElmntLst.item(0);
236                                        NodeList lstNm = lstNmElmnt.getChildNodes();
237                                        String pValue = (lstNm.item(0)).getNodeValue().trim();
238                                        trace("Param value: %s", pValue);
239                                        // create the servlet
240                                        ServletInfo servlet = new ServletInfo(pName, (Class<? extends Servlet>) info.getClassLoader()
241                                                        .loadClass(pValue));
242                                        // parse load on startup
243                                        NodeList ldElmntLst = fstElmnt.getElementsByTagName("load-on-startup");
244                                        if (ldElmntLst != null) {
245                                                Element ldElmnt = (Element) ldElmntLst.item(0);
246                                                if(ldElmnt != null) {
247                                                        NodeList ldNm = ldElmnt.getChildNodes();
248                                                        String pLoad = (ldNm.item(0)).getNodeValue().trim();
249                                                        trace("Load on startup: %s", pLoad);
250                                                        servlet.setLoadOnStartup(Integer.valueOf(pLoad));
251                                                }
252                                        }
253                                        servlet.setRequireWelcomeFileMapping(true);
254                                        // do init-param - available in the context of a servlet
255                                        // or filter in the web application
256                                        listOfElements = fstElmnt.getElementsByTagName("init-param");
257                                        totalElements = listOfElements.getLength();
258                                        trace("Total no of init-params: %s", totalElements);
259                                        for (int i = 0; i < totalElements; i++) {
260                                                Node inNode = listOfElements.item(i);
261                                                if (inNode.getNodeType() == Node.ELEMENT_NODE) {
262                                                        Element inElmnt = (Element) inNode;
263                                                        NodeList inNmElmntLst = inElmnt.getElementsByTagName("param-name");
264                                                        Element inNmElmnt = (Element) inNmElmntLst.item(0);
265                                                        NodeList inNm = inNmElmnt.getChildNodes();
266                                                        String inName = (inNm.item(0)).getNodeValue().trim();
267                                                        trace("Param name: %s", inName);
268                                                        NodeList inValElmntLst = inElmnt.getElementsByTagName("param-value");
269                                                        Element inValElmnt = (Element) inValElmntLst.item(0);
270                                                        NodeList inVal = inValElmnt.getChildNodes();
271                                                        String inValue = (inVal.item(0)).getNodeValue().trim();
272                                                        inValue = inValue.replaceAll(".?/WEB-INF", webinfPath);
273                                                        trace("Param value: %s", inValue);
274                                                        // add the param
275                                                        servlet.addInitParam(inName, inValue);
276                                                }
277                                        }
278                                        // add to the map
279                                        servletMap.put(servlet.getName(), servlet);
280                                }
281                        }
282                        // do servlet-mapping
283                        if (!servletMap.isEmpty()) {
284                                listOfElements = doc.getElementsByTagName("servlet-mapping");
285                                totalElements = listOfElements.getLength();
286                                log.warnf("Total no of servlet-mappings: %2", totalElements);
287                                for (int s = 0; s < totalElements; s++) {
288                                        Node fstNode = listOfElements.item(s);
289                                        if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
290                                                Element fstElmnt = (Element) fstNode;
291                                                NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("servlet-name");
292                                                Element fstNmElmnt = (Element) fstNmElmntLst.item(0);
293                                                NodeList fstNm = fstNmElmnt.getChildNodes();
294                                                String pName = (fstNm.item(0)).getNodeValue().trim();
295                                                trace("Param name: %s", pName);
296                                                // lookup the servlet info
297                                                ServletInfo servlet = servletMap.get(pName);
298                                                // add the mapping
299                                                if (servlet != null) {
300                                                        NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("url-pattern");
301                                                        for (int p = 0; p < lstNmElmntLst.getLength(); p++) {
302                                                                Element lstNmElmnt = (Element) lstNmElmntLst.item(p);
303                                                                NodeList lstNm = lstNmElmnt.getChildNodes();
304                                                                String pValue = (lstNm.item(0)).getNodeValue().trim();
305                                                                trace("Param value: %s", pValue);
306                                                                servlet.addMapping(pValue);
307
308                                                        }
309                                                } else {
310                                                        log.warnf("No servlet found for %s", pName);
311                                                }
312                                        }
313                                }
314                                // add servlets to deploy info
315                                info.addServlets(servletMap.values());
316                        }
317                        // do welcome files
318                        listOfElements = doc.getElementsByTagName("welcome-file-list");
319                        totalElements = listOfElements.getLength();
320                        trace("Total no of welcome-files: %s", totalElements);
321                        for (int s = 0; s < totalElements; s++) {
322                                Node fstNode = listOfElements.item(s);
323                                if (fstNode.getNodeType() == Node.ELEMENT_NODE) {
324                                        Element fstElmnt = (Element) fstNode;
325                                        NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("welcome-file");
326                                        int totalWelcomeFiles = fstNmElmntLst.getLength();
327
328                                        log.debug( "Adding welcome pages:" );
329                                        for(int i=0; i < totalWelcomeFiles; i++){
330                                                Element fstNmElmnt = (Element) fstNmElmntLst.item(i);
331                                                NodeList fstNm = fstNmElmnt.getChildNodes();
332                                                String pName = (fstNm.item(0)).getNodeValue().trim();
333                                                trace("Param name: %s", pName);
334                                                log.debug( "welcome page:" + pName);
335                                                // add welcome page
336                                                info.addWelcomePage(pName);
337                                        }
338                                }
339                        }
340                        // do mime types files
341                        listOfElements = doc.getElementsByTagName("mime-mapping");
342                        totalElements = listOfElements.getLength();
343                        log.debugf("Total no of mime-mapping: %s", totalElements);
344                        for (int i = 0; i < totalElements; i++) {
345                                Node inNode = listOfElements.item(i);
346                                if (inNode.getNodeType() == Node.ELEMENT_NODE) {
347                                        Element inElmnt = (Element) inNode;
348                                        NodeList inNmElmntLst = inElmnt.getElementsByTagName("extension");
349                                        Element inNmElmnt = (Element) inNmElmntLst.item(0);
350                                        NodeList inNm = inNmElmnt.getChildNodes();
351                                        String extention = (inNm.item(0)).getNodeValue().trim();
352                                        NodeList inValElmntLst = inElmnt.getElementsByTagName("mime-type");
353                                        Element inValElmnt = (Element) inValElmntLst.item(0);
354                                        NodeList inVal = inValElmnt.getChildNodes();
355                                        String mimeType = (inVal.item(0)).getNodeValue().trim();
356                                        log.debug("extension: " + extention + " mime-type: "+mimeType);
357                                        // add the type
358                                        info.addMimeMapping(new MimeMapping(extention,mimeType));
359                                }
360                        }
361                        // do display name
362                        NodeList dNmElmntLst = doc.getElementsByTagName("display-name");
363                        if (dNmElmntLst.getLength() == 1) {
364                                Node dNmNode = dNmElmntLst.item(0);
365                                if (dNmNode.getNodeType() == Node.TEXT_NODE) {
366                                        String dName = dNmNode.getNodeValue().trim();
367                                        trace("Display name: %s", dName);
368                                        info.setDisplayName(dName);
369                                }
370                        }
371                        // TODO add security stuff
372                } catch (Exception e) {
373                        e.printStackTrace();
374                        log.error("Error reading web.xml", e);
375                }
376        }
377
378        private static void trace(String string, Object elements) {
379                log.tracef(string,elements);
380//              System.out.printf(string,elements);
381//              System.out.println();
382        }
383        
384}