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
66 public class ComponentConfigurationReader {
67
68 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ComponentConfigurationReader.class);
69
70 private static final String DTD = "/info/magnolia/module/model/components.dtd";
71
72 private final String dtdUrl;
73 private final BeanReader beanReader;
74
75 public ComponentConfigurationReader() {
76 final URL dtd = getClass().getResource(DTD);
77 if (dtd == null) {
78 throw new ComponentConfigurationException("DTD not found at " + DTD);
79 }
80 dtdUrl = dtd.toString();
81
82 beanReader = new BeanReader();
83 try {
84 beanReader.setValidating(true);
85 beanReader.setErrorHandler(new ErrorHandler());
86 beanReader.registerBeanClass(ComponentsDefinition.class);
87 } catch (IntrospectionException e) {
88 throw new ComponentConfigurationException(e);
89 }
90 }
91
92 public List<ComponentsDefinition> readAll(List<String> resourcePaths) {
93 List<ComponentsDefinition> componentsDefinitions = new ArrayList<ComponentsDefinition>();
94 for (String resourcePath : resourcePaths) {
95 log.debug("Parsing components file {}", resourcePath);
96 ComponentsDefinition componentsDefinition = readFromResource(resourcePath);
97 componentsDefinitions.add(componentsDefinition);
98 }
99 return componentsDefinitions;
100 }
101
102 public ComponentsDefinition read(Reader in) {
103 try {
104 final Reader replacedDtd = replaceDtd(in);
105 return (ComponentsDefinition) beanReader.parse(replacedDtd);
106 } catch (IOException e) {
107 throw new ComponentConfigurationException("Can't read components definition file: " + e.getMessage(), e);
108 } catch (SAXException e) {
109 throw new ComponentConfigurationException(e.getMessage(), e);
110 }
111 }
112
113 public ComponentsDefinition readFromResource(String resourcePath) {
114 final InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream(resourcePath));
115 try {
116 return read(reader);
117 } finally {
118 try {
119 reader.close();
120 } catch (IOException e) {
121 log.error("Can't close input for " + resourcePath);
122 }
123 }
124 }
125
126
127
128
129
130
131
132
133 private Reader replaceDtd(Reader reader) throws IOException {
134 String content = IOUtils.toString(reader);
135
136
137 Pattern pattern = Pattern.compile("<!DOCTYPE .*>");
138 Matcher matcher = pattern.matcher(content);
139 content = matcher.replaceFirst("");
140
141
142 try {
143 Document doc = new SAXBuilder().build(new StringReader(content));
144 doc.setDocType(new DocType("components", dtdUrl));
145
146 XMLOutputter outputter = new XMLOutputter();
147 StringWriter writer = new StringWriter();
148 outputter.output(doc, writer);
149 final String replacedDtd = writer.toString();
150 return new StringReader(replacedDtd);
151 } catch (JDOMException e) {
152 throw new ComponentConfigurationException(e);
153 }
154 }
155
156 private static class ErrorHandler implements org.xml.sax.ErrorHandler {
157
158 @Override
159 public void warning(SAXParseException e) throws SAXException {
160 log.warn("Warning on components definition " + getSaxParseExceptionMessage(e));
161 }
162
163 @Override
164 public void error(SAXParseException e) throws SAXException {
165 throw new SAXException("Invalid components definition file, error " + getSaxParseExceptionMessage(e), e);
166 }
167
168 @Override
169 public void fatalError(SAXParseException e) throws SAXException {
170 throw new SAXException("Invalid components definition file, fatal error " + getSaxParseExceptionMessage(e), e);
171 }
172 }
173
174 private static String getSaxParseExceptionMessage(SAXParseException e) {
175 return "at line " + e.getLineNumber() + " column " + e.getColumnNumber() + ": " + e.getMessage();
176 }
177 }