1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.objectfactory.configuration;
35
36 import java.beans.IntrospectionException;
37 import java.io.IOException;
38 import java.io.InputStreamReader;
39 import java.io.Reader;
40 import java.io.StringReader;
41 import java.io.StringWriter;
42 import java.net.URL;
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.regex.Matcher;
46 import java.util.regex.Pattern;
47
48 import org.apache.commons.betwixt.io.BeanReader;
49 import org.apache.commons.io.IOUtils;
50 import org.jdom.DocType;
51 import org.jdom.Document;
52 import org.jdom.JDOMException;
53 import org.jdom.input.SAXBuilder;
54 import org.jdom.output.XMLOutputter;
55 import org.xml.sax.SAXException;
56 import org.xml.sax.SAXParseException;
57
58 import info.magnolia.module.model.ComponentsDefinition;
59
60
61
62
63
64
65 public class ComponentConfigurationReader {
66
67 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ComponentConfigurationReader.class);
68
69 private static final String DTD = "/info/magnolia/module/model/components.dtd";
70
71 private final String dtdUrl;
72 private final BeanReader beanReader;
73
74 public ComponentConfigurationReader() {
75 final URL dtd = getClass().getResource(DTD);
76 if (dtd == null) {
77 throw new ComponentConfigurationException("DTD not found at " + DTD);
78 }
79 dtdUrl = dtd.toString();
80
81 beanReader = new BeanReader();
82 try {
83 beanReader.setValidating(true);
84 beanReader.setErrorHandler(new ErrorHandler());
85 beanReader.registerBeanClass(ComponentsDefinition.class);
86 } catch (IntrospectionException e) {
87 throw new ComponentConfigurationException(e);
88 }
89 }
90
91 public List<ComponentsDefinition> readAll(List<String> resourcePaths) {
92 List<ComponentsDefinition> componentsDefinitions = new ArrayList<ComponentsDefinition>();
93 for (String resourcePath : resourcePaths) {
94 log.debug("Parsing components file {}", resourcePath);
95 ComponentsDefinition componentsDefinition = readFromResource(resourcePath);
96 componentsDefinitions.add(componentsDefinition);
97 }
98 return componentsDefinitions;
99 }
100
101 public ComponentsDefinition read(Reader in) {
102 try {
103 final Reader replacedDtd = replaceDtd(in);
104 return (ComponentsDefinition) beanReader.parse(replacedDtd);
105 } catch (IOException e) {
106 throw new ComponentConfigurationException("Can't read components definition file: " + e.getMessage(), e);
107 } catch (SAXException e) {
108 throw new ComponentConfigurationException(e.getMessage(), e);
109 }
110 }
111
112 public ComponentsDefinition readFromResource(String resourcePath) {
113 final InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream(resourcePath));
114 try {
115 return read(reader);
116 } finally {
117 try {
118 reader.close();
119 } catch (IOException e) {
120 log.error("Can't close input for {}", resourcePath);
121 }
122 }
123 }
124
125
126
127
128
129
130
131
132 private Reader replaceDtd(Reader reader) throws IOException {
133 String content = IOUtils.toString(reader);
134
135
136 Pattern pattern = Pattern.compile("<!DOCTYPE .*>");
137 Matcher matcher = pattern.matcher(content);
138 content = matcher.replaceFirst("");
139
140
141 try {
142 Document doc = new SAXBuilder().build(new StringReader(content));
143 doc.setDocType(new DocType("components", dtdUrl));
144
145 XMLOutputter outputter = new XMLOutputter();
146 StringWriter writer = new StringWriter();
147 outputter.output(doc, writer);
148 final String replacedDtd = writer.toString();
149 return new StringReader(replacedDtd);
150 } catch (JDOMException e) {
151 throw new ComponentConfigurationException(e);
152 }
153 }
154
155 private static class ErrorHandler implements org.xml.sax.ErrorHandler {
156
157 @Override
158 public void warning(SAXParseException e) throws SAXException {
159 log.warn("Warning on components definition {}", getSaxParseExceptionMessage(e));
160 }
161
162 @Override
163 public void error(SAXParseException e) throws SAXException {
164 throw new SAXException("Invalid components definition file, error " + getSaxParseExceptionMessage(e), e);
165 }
166
167 @Override
168 public void fatalError(SAXParseException e) throws SAXException {
169 throw new SAXException("Invalid components definition file, fatal error " + getSaxParseExceptionMessage(e), e);
170 }
171 }
172
173 private static String getSaxParseExceptionMessage(SAXParseException e) {
174 return "at line " + e.getLineNumber() + " column " + e.getColumnNumber() + ": " + e.getMessage();
175 }
176 }