• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / sitemesh2 / 59

22 Mar 2026 02:30AM UTC coverage: 40.347%. Remained the same
59

push

github

hazendaz
[mvn] Update maven wrapper

698 of 1891 branches covered (36.91%)

Branch coverage included in aggregate %.

1555 of 3693 relevant lines covered (42.11%)

0.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.4
/src/main/java/com/opensymphony/module/sitemesh/factory/DefaultFactory.java
1
/*
2
 * SPDX-License-Identifier: Apache-2.0
3
 * Copyright 2011-2026 Hazendaz
4
 */
5
/*
6
 * Title:        DefaultFactory
7
 * Description:
8
 *
9
 * This software is published under the terms of the OpenSymphony Software
10
 * License version 1.1, of which a copy has been included with this
11
 * distribution in the LICENSE.txt file.
12
 */
13

14
package com.opensymphony.module.sitemesh.factory;
15

16
import com.opensymphony.module.sitemesh.Config;
17

18
import java.io.File;
19
import java.io.IOException;
20
import java.io.InputStream;
21
import java.nio.file.Path;
22
import java.util.HashMap;
23
import java.util.Map;
24
import java.util.Properties;
25

26
import javax.xml.parsers.DocumentBuilder;
27
import javax.xml.parsers.DocumentBuilderFactory;
28
import javax.xml.parsers.ParserConfigurationException;
29

30
import org.w3c.dom.Document;
31
import org.w3c.dom.Element;
32
import org.w3c.dom.NodeList;
33
import org.w3c.dom.Text;
34
import org.xml.sax.SAXException;
35

36
/**
37
 * DefaultFactory, reads configuration from the <code>sitemesh.configfile</code> init param, or
38
 * <code>/WEB-INF/sitemesh.xml</code> if not specified, or uses the default configuration if <code>sitemesh.xml</code>
39
 * does not exist.
40
 * <p>
41
 * To use the <code>sitemesh.configfile</code> parameter, add the following to your web.xml:
42
 *
43
 * <pre>
44
 * &lt;context-param&gt;
45
 *      &lt;param-name&gt;sitemesh.configfile&lt;/param-name&gt;
46
 *      &lt;param-value&gt;/WEB-INF/etc/sitemesh.xml&lt;/param-value&gt;
47
 *  &lt;/context-param&gt;
48
 * </pre>
49
 *
50
 * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
51
 * @author <a href="mailto:pathos@pandora.be">Mathias Bogaert</a>
52
 */
53
public class DefaultFactory extends BaseFactory {
54

55
    /** The config file name. */
56
    String configFileName;
57

58
    /** The Constant DEFAULT_CONFIG_FILENAME. */
59
    private static final String DEFAULT_CONFIG_FILENAME = "/WEB-INF/sitemesh.xml";
60

61
    /** The config file. */
62
    File configFile;
63

64
    /** The config last modified. */
65
    long configLastModified;
66

67
    /** The config last check. */
68
    private long configLastCheck = 0L;
×
69

70
    /** The config check millis. */
71
    public static long configCheckMillis = 3000L;
1✔
72

73
    /** The config props. */
74
    Map<Object, Object> configProps = new HashMap<>();
×
75

76
    /** The excludes file name. */
77
    String excludesFileName;
78

79
    /** The excludes file. */
80
    File excludesFile;
81

82
    /**
83
     * Instantiates a new default factory.
84
     *
85
     * @param config
86
     *            the config
87
     */
88
    public DefaultFactory(Config config) {
89
        super(config);
×
90

91
        configFileName = config.getServletContext().getInitParameter("sitemesh.configfile");
×
92
        if (configFileName == null) {
×
93
            configFileName = DEFAULT_CONFIG_FILENAME;
×
94
        }
95

96
        // configFilePath is null if loaded from war file
97
        String initParamConfigFile = config.getConfigFile();
×
98
        if (initParamConfigFile != null) {
×
99
            configFileName = initParamConfigFile;
×
100
        }
101

102
        String configFilePath = config.getServletContext().getRealPath(configFileName);
×
103

104
        if (configFilePath != null) { // disable config auto reloading for .war files
×
105
            configFile = Path.of(configFilePath).toFile();
×
106
        }
107

108
        loadConfig();
×
109
    }
×
110

111
    /** Load configuration from file. */
112
    private synchronized void loadConfig() {
113
        try {
114
            // Load and parse the sitemesh.xml file
115
            Element root = loadSitemeshXML();
×
116

117
            NodeList sections = root.getChildNodes();
×
118
            // Loop through child elements of root node
119
            for (int i = 0; i < sections.getLength(); i++) {
×
120
                if (sections.item(i) instanceof Element) {
×
121
                    Element curr = (Element) sections.item(i);
×
122
                    NodeList children = curr.getChildNodes();
×
123

124
                    if ("config-refresh".equalsIgnoreCase(curr.getTagName())) {
×
125
                        String seconds = curr.getAttribute("seconds");
×
126
                        configCheckMillis = Long.parseLong(seconds) * 1000L;
×
127
                    } else if ("property".equalsIgnoreCase(curr.getTagName())) {
×
128
                        String name = curr.getAttribute("name");
×
129
                        String value = curr.getAttribute("value");
×
130
                        if (!"".equals(name) && !"".equals(value)) {
×
131
                            configProps.put("${" + name + "}", value);
×
132
                        }
133
                    } else if ("page-parsers".equalsIgnoreCase(curr.getTagName())) {
×
134
                        // handle <page-parsers>
135
                        loadPageParsers(children);
×
136
                    } else if ("decorator-mappers".equalsIgnoreCase(curr.getTagName())) {
×
137
                        // handle <decorator-mappers>
138
                        loadDecoratorMappers(children);
×
139
                    } else if ("excludes".equalsIgnoreCase(curr.getTagName())) {
×
140
                        // handle <excludes>
141
                        String fileName = replaceProperties(curr.getAttribute("file"));
×
142
                        if (!"".equals(fileName)) {
×
143
                            excludesFileName = fileName;
×
144
                            loadExcludes();
×
145
                        }
146
                    }
147
                }
148
            }
149
        } catch (ParserConfigurationException e) {
×
150
            throw new FactoryException("Could not get XML parser", e);
×
151
        } catch (IOException e) {
×
152
            throw new FactoryException("Could not read config file : " + configFileName, e);
×
153
        } catch (SAXException e) {
×
154
            throw new FactoryException("Could not parse config file : " + configFileName, e);
×
155
        }
×
156
    }
×
157

158
    /**
159
     * Load sitemesh XML.
160
     *
161
     * @return the element
162
     *
163
     * @throws ParserConfigurationException
164
     *             the parser configuration exception
165
     * @throws IOException
166
     *             Signals that an I/O exception has occurred.
167
     * @throws SAXException
168
     *             the SAX exception
169
     */
170
    private Element loadSitemeshXML() throws ParserConfigurationException, IOException, SAXException {
171
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
×
172
        factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
×
173
        DocumentBuilder builder = factory.newDocumentBuilder();
×
174

175
        InputStream is = null;
×
176

177
        if (configFile == null) {
×
178
            is = config.getServletContext().getResourceAsStream(configFileName);
×
179
        } else if (configFile.exists() && configFile.canRead()) {
×
180
            is = configFile.toURI().toURL().openStream();
×
181
        }
182

183
        // load the default sitemesh configuration
184
        if (is == null) {
×
185
            is = getClass().getClassLoader()
×
186
                    .getResourceAsStream("com/opensymphony/module/sitemesh/factory/sitemesh-default.xml");
×
187
        }
188

189
        // load the default sitemesh configuration using another classloader
190
        if (is == null) {
×
191
            is = Thread.currentThread().getContextClassLoader()
×
192
                    .getResourceAsStream("com/opensymphony/module/sitemesh/factory/sitemesh-default.xml");
×
193
        }
194

195
        if (is == null) {
×
196
            throw new IllegalStateException("Cannot load default configuration from jar");
×
197
        }
198

199
        if (configFile != null) {
×
200
            configLastModified = configFile.lastModified();
×
201
        }
202

203
        Document doc = builder.parse(is);
×
204
        Element root = doc.getDocumentElement();
×
205
        // Verify root element
206
        if (!"sitemesh".equalsIgnoreCase(root.getTagName())) {
×
207
            throw new FactoryException("Root element of sitemesh configuration file not <sitemesh>", null);
×
208
        }
209
        return root;
×
210
    }
211

212
    /**
213
     * Load excludes.
214
     *
215
     * @throws ParserConfigurationException
216
     *             the parser configuration exception
217
     * @throws IOException
218
     *             Signals that an I/O exception has occurred.
219
     * @throws SAXException
220
     *             the SAX exception
221
     */
222
    private void loadExcludes() throws ParserConfigurationException, IOException, SAXException {
223
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
×
224
        factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
×
225
        DocumentBuilder builder = factory.newDocumentBuilder();
×
226

227
        InputStream is = null;
×
228

229
        if (excludesFile == null) {
×
230
            is = config.getServletContext().getResourceAsStream(excludesFileName);
×
231
            if (is == null) {
×
232
                String excludesFilePath = config.getServletContext().getRealPath(excludesFileName);// getResourceAsStream
×
233
                                                                                                   // does not work on
234
                                                                                                   // weblogic 10.3.5
235
                if (excludesFilePath != null) {
×
236
                    excludesFile = Path.of(excludesFilePath).toFile();
×
237
                    is = excludesFile.toURI().toURL().openStream();
×
238
                }
239
            }
×
240
        } else if (excludesFile.exists() && excludesFile.canRead()) {
×
241
            is = excludesFile.toURI().toURL().openStream();
×
242
        }
243

244
        if (is == null) {
×
245
            throw new IllegalStateException("Cannot load excludes configuration file \"" + excludesFileName
×
246
                    + "\" as specified in \"sitemesh.xml\" or \"sitemesh-default.xml\"");
247
        }
248

249
        Document document = builder.parse(is);
×
250
        Element root = document.getDocumentElement();
×
251
        NodeList sections = root.getChildNodes();
×
252

253
        // Loop through child elements of root node looking for the <excludes> block
254
        for (int i = 0; i < sections.getLength(); i++) {
×
255
            if (sections.item(i) instanceof Element) {
×
256
                Element curr = (Element) sections.item(i);
×
257
                if ("excludes".equalsIgnoreCase(curr.getTagName())) {
×
258
                    loadExcludeUrls(curr.getChildNodes());
×
259
                }
260
            }
261
        }
262
    }
×
263

264
    /**
265
     * Loop through children of 'page-parsers' element and add all 'parser' mappings.
266
     *
267
     * @param nodes
268
     *            the nodes
269
     */
270
    private void loadPageParsers(NodeList nodes) {
271
        clearParserMappings();
×
272
        for (int i = 0; i < nodes.getLength(); i++) {
×
273
            if (nodes.item(i) instanceof Element) {
×
274
                Element curr = (Element) nodes.item(i);
×
275

276
                if ("parser".equalsIgnoreCase(curr.getTagName())) {
×
277
                    String className = curr.getAttribute("class");
×
278
                    String contentType = curr.getAttribute("content-type");
×
279
                    mapParser(contentType, className);
×
280
                }
281
            }
282
        }
283
    }
×
284

285
    /**
286
     * Load decorator mappers.
287
     *
288
     * @param nodes
289
     *            the nodes
290
     */
291
    private void loadDecoratorMappers(NodeList nodes) {
292
        clearDecoratorMappers();
×
293
        Properties emptyProps = new Properties();
×
294

295
        pushDecoratorMapper("com.opensymphony.module.sitemesh.mapper.NullDecoratorMapper", emptyProps);
×
296

297
        // note, this works from the bottom node up.
298
        for (int i = nodes.getLength() - 1; i > 0; i--) {
×
299
            if (nodes.item(i) instanceof Element) {
×
300
                Element curr = (Element) nodes.item(i);
×
301
                if ("mapper".equalsIgnoreCase(curr.getTagName())) {
×
302
                    String className = curr.getAttribute("class");
×
303
                    Properties props = new Properties();
×
304
                    // build properties from <param> tags.
305
                    NodeList children = curr.getChildNodes();
×
306
                    for (int j = 0; j < children.getLength(); j++) {
×
307
                        if (children.item(j) instanceof Element) {
×
308
                            Element currC = (Element) children.item(j);
×
309
                            if ("param".equalsIgnoreCase(currC.getTagName())) {
×
310
                                String value = currC.getAttribute("value");
×
311
                                props.put(currC.getAttribute("name"), replaceProperties(value));
×
312
                            }
313
                        }
314
                    }
315
                    // add mapper
316
                    pushDecoratorMapper(className, props);
×
317
                }
318
            }
319
        }
320

321
        pushDecoratorMapper("com.opensymphony.module.sitemesh.mapper.InlineDecoratorMapper", emptyProps);
×
322
    }
×
323

324
    /**
325
     * Reads in all the url patterns to exclude from decoration.
326
     *
327
     * @param nodes
328
     *            the nodes
329
     */
330
    private void loadExcludeUrls(NodeList nodes) {
331
        clearExcludeUrls();
×
332
        for (int i = 0; i < nodes.getLength(); i++) {
×
333
            if (nodes.item(i) instanceof Element) {
×
334
                Element p = (Element) nodes.item(i);
×
335
                if ("pattern".equalsIgnoreCase(p.getTagName()) || "url-pattern".equalsIgnoreCase(p.getTagName())) {
×
336
                    Text patternText = (Text) p.getFirstChild();
×
337
                    if (patternText != null) {
×
338
                        String pattern = patternText.getData().trim();
×
339
                        if (pattern != null) {
×
340
                            addExcludeUrl(pattern);
×
341
                        }
342
                    }
343
                }
344
            }
345
        }
346
    }
×
347

348
    /**
349
     * Check if configuration file has been modified, and if so reload it.
350
     */
351
    @Override
352
    public void refresh() {
353
        long time = System.currentTimeMillis();
×
354
        if (time - configLastCheck < configCheckMillis) {
×
355
            return;
×
356
        }
357
        configLastCheck = time;
×
358

359
        if (configFile != null && configLastModified != configFile.lastModified()) {
×
360
            loadConfig();
×
361
        }
362
    }
×
363

364
    /**
365
     * Replaces any properties that appear in the supplied string with their actual values.
366
     *
367
     * @param str
368
     *            the string to replace the properties in
369
     *
370
     * @return the same string but with any properties expanded out to their actual values
371
     */
372
    private String replaceProperties(String str) {
373
        int idx;
374
        for (Map.Entry<Object, Object> entry : configProps.entrySet()) {
×
375
            String key = (String) entry.getKey();
×
376
            while ((idx = str.indexOf(key)) >= 0) {
×
377
                StringBuilder buf = new StringBuilder(100);
×
378
                buf.append(str.substring(0, idx));
×
379
                buf.append(entry.getValue());
×
380
                buf.append(str.substring(idx + key.length()));
×
381
                str = buf.toString();
×
382
            }
×
383
        }
×
384
        return str;
×
385
    }
386
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc