1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package net.sf.ehcache.config;
18
19 import net.sf.ehcache.CacheException;
20 import net.sf.ehcache.util.ClassLoaderUtil;
21
22 import javax.xml.parsers.SAXParser;
23 import javax.xml.parsers.SAXParserFactory;
24 import java.io.BufferedInputStream;
25 import java.io.ByteArrayInputStream;
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.Reader;
32 import java.net.URL;
33 import java.util.HashSet;
34 import java.util.Set;
35
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41 import net.sf.ehcache.config.generator.ConfigurationSource;
42
43 /***
44 * A utility class which configures beans from XML, using reflection.
45 *
46 * @author Greg Luck
47 * @version $Id: ConfigurationFactory.html 13146 2011-08-01 17:12:39Z oletizi $
48 */
49 public final class ConfigurationFactory {
50
51 private static final Logger LOG = LoggerFactory.getLogger(ConfigurationFactory.class.getName());
52
53 private static final String DEFAULT_CLASSPATH_CONFIGURATION_FILE = "/ehcache.xml";
54 private static final String FAILSAFE_CLASSPATH_CONFIGURATION_FILE = "/ehcache-failsafe.xml";
55
56 /***
57 * Constructor.
58 */
59 private ConfigurationFactory() {
60
61 }
62
63 /***
64 * Configures a bean from an XML file.
65 */
66 public static Configuration parseConfiguration(final File file) throws CacheException {
67 if (file == null) {
68 throw new CacheException("Attempt to configure ehcache from null file.");
69 }
70
71 LOG.debug("Configuring ehcache from file: {}", file);
72 Configuration configuration = null;
73 InputStream input = null;
74 try {
75 input = new BufferedInputStream(new FileInputStream(file));
76 configuration = parseConfiguration(input);
77 } catch (Exception e) {
78 throw new CacheException("Error configuring from " + file + ". Initial cause was " + e.getMessage(), e);
79 } finally {
80 try {
81 if (input != null) {
82 input.close();
83 }
84 } catch (IOException e) {
85 LOG.error("IOException while closing configuration input stream. Error was " + e.getMessage());
86 }
87 }
88 configuration.setSource(ConfigurationSource.getConfigurationSource(file));
89 return configuration;
90 }
91
92 /***
93 * Configures a bean from an XML file available as an URL.
94 */
95 public static Configuration parseConfiguration(final URL url) throws CacheException {
96 LOG.debug("Configuring ehcache from URL: {}", url);
97 Configuration configuration;
98 InputStream input = null;
99 try {
100 input = url.openStream();
101 configuration = parseConfiguration(input);
102 } catch (Exception e) {
103 throw new CacheException("Error configuring from " + url + ". Initial cause was " + e.getMessage(), e);
104 } finally {
105 try {
106 if (input != null) {
107 input.close();
108 }
109 } catch (IOException e) {
110 LOG.error("IOException while closing configuration input stream. Error was " + e.getMessage());
111 }
112 }
113 configuration.setSource(ConfigurationSource.getConfigurationSource(url));
114 return configuration;
115 }
116
117 /***
118 * Configures a bean from an XML file in the classpath.
119 */
120 public static Configuration parseConfiguration() throws CacheException {
121 ClassLoader standardClassloader = ClassLoaderUtil.getStandardClassLoader();
122 URL url = null;
123 if (standardClassloader != null) {
124 url = standardClassloader.getResource(DEFAULT_CLASSPATH_CONFIGURATION_FILE);
125 }
126 if (url == null) {
127 url = ConfigurationFactory.class.getResource(DEFAULT_CLASSPATH_CONFIGURATION_FILE);
128 }
129 if (url != null) {
130 LOG.debug("Configuring ehcache from ehcache.xml found in the classpath: " + url);
131 } else {
132 url = ConfigurationFactory.class.getResource(FAILSAFE_CLASSPATH_CONFIGURATION_FILE);
133
134 LOG.warn("No configuration found. Configuring ehcache from ehcache-failsafe.xml "
135 + " found in the classpath: {}", url);
136
137 }
138 Configuration configuration = parseConfiguration(url);
139 configuration.setSource(ConfigurationSource.getConfigurationSource());
140 return configuration;
141 }
142
143 /***
144 * Configures a bean from an XML input stream.
145 */
146 public static Configuration parseConfiguration(final InputStream inputStream) throws CacheException {
147
148 LOG.debug("Configuring ehcache from InputStream");
149
150 Configuration configuration = new Configuration();
151 try {
152 InputStream translatedInputStream = translateSystemProperties(inputStream);
153 final SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
154 final BeanHandler handler = new BeanHandler(configuration);
155 parser.parse(translatedInputStream, handler);
156 } catch (Exception e) {
157 throw new CacheException("Error configuring from input stream. Initial cause was " + e.getMessage(), e);
158 }
159 configuration.setSource(ConfigurationSource.getConfigurationSource(inputStream));
160 return configuration;
161 }
162
163 /***
164 * Translates system properties which can be added as tokens to the config file using ${token} syntax.
165 * <p/>
166 * So, if the config file contains a character sequence "multicastGroupAddress=${multicastAddress}", and there is a system property
167 * multicastAddress=230.0.0.12 then the translated sequence becomes "multicastGroupAddress=230.0.0.12"
168 *
169 * @param inputStream
170 * @return a translated stream
171 */
172 private static InputStream translateSystemProperties(InputStream inputStream) throws IOException {
173
174 StringBuilder sb = new StringBuilder();
175 int c;
176 Reader reader = new InputStreamReader(inputStream, "UTF-8");
177 while ((c = reader.read()) != -1) {
178 sb.append((char) c);
179 }
180 String configuration = sb.toString();
181
182 Set tokens = extractPropertyTokens(configuration);
183 for (Object tokenObject : tokens) {
184 String token = (String) tokenObject;
185 String leftTrimmed = token.replaceAll("//$//{", "");
186 String trimmedToken = leftTrimmed.replaceAll("//}", "");
187
188 String property = System.getProperty(trimmedToken);
189 if (property == null) {
190 LOG.debug("Did not find a system property for the " + token +
191 " token specified in the configuration.Replacing with \"\"");
192 } else {
193
194 String propertyWithQuotesProtected = Matcher.quoteReplacement(property);
195 configuration = configuration.replaceAll("//$//{" + trimmedToken + "//}", propertyWithQuotesProtected);
196
197 LOG.debug("Found system property value of " + property + " for the " + token +
198 " token specified in the configuration.");
199 }
200 }
201 return new ByteArrayInputStream(configuration.getBytes("UTF-8"));
202 }
203
204 /***
205 * Extracts properties of the form ${...}
206 *
207 * @param sourceDocument the source document
208 * @return a Set of properties. So, duplicates are only counted once.
209 */
210 static Set extractPropertyTokens(String sourceDocument) {
211 Set propertyTokens = new HashSet();
212 Pattern pattern = Pattern.compile("//$//{.+?//}");
213 Matcher matcher = pattern.matcher(sourceDocument);
214 while (matcher.find()) {
215 String token = matcher.group();
216 propertyTokens.add(token);
217 }
218 return propertyTokens;
219 }
220
221
222 }