View Javadoc

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                 //replaceAll by default clobbers \ and $
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 }