1 /****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one *
3 * or more contributor license agreements. See the NOTICE file *
4 * distributed with this work for additional information *
5 * regarding copyright ownership. The ASF licenses this file *
6 * to you under the Apache License, Version 2.0 (the *
7 * "License"); you may not use this file except in compliance *
8 * with the License. You may obtain a copy of the License at *
9 * *
10 * http://www.apache.org/licenses/LICENSE-2.0 *
11 * *
12 * Unless required by applicable law or agreed to in writing, *
13 * software distributed under the License is distributed on an *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
15 * KIND, either express or implied. See the License for the *
16 * specific language governing permissions and limitations *
17 * under the License. *
18 ****************************************************************/
19
20 package org.apache.jsieve;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.Map;
25 import java.util.Properties;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.ConcurrentMap;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.xml.sax.SAXException;
32
33 /**
34 * <p>
35 * <code>ConfigurationManager</code> parses the XML statements
36 * in the Sieve configuration file and translates them to Java objects.
37 * </p>
38 * <p>
39 * The Sieve configuration is read from 3 properties files
40 * </p>
41 * <ul>
42 * <li><code>org/apache/jsieve/commandsmap.properties</code></li>
43 * <li><code>org/apache/jsieve/testsmap.properties</code></li>
44 * <li><code>org/apache/jsieve/comparatorsmap.properties</code></li>
45 * </ul>
46 * <p>They are located by searching the classpath of the current ClassLoader.
47 * </p>
48 * <h4>Thread Safety</h4>
49 * <p>
50 * Each configuration manager instance may be safely accessed by concurrent threads.
51 * </p>
52 * <p>
53 * The managers constructed by
54 * </p>
55 * <ul>
56 * <li>{@link #getCommandManager()}</li>
57 * <li>{@link #getComparatorManager()}</li>
58 * <li>{@link #getTestManager()}</li>
59 * </ul>
60 * <p>
61 * and the {@link SieveFactory} built by
62 * </p>
63 * <ul>
64 * <li>{@link #build()}</li>
65 * </ul>
66 * <p>
67 * may be safely shared by multiple threads.
68 * </p>
69 */
70 public class ConfigurationManager {
71
72 private static final int DEFAULT_INITIAL_CONCURRENCY_LEVEL = 8;
73
74 private static final String COMMANDSMAP_PROPERTIES = "org/apache/jsieve/commandsmap.properties";
75
76 private static final String TESTSMAP_PROPERTIES = "org/apache/jsieve/testsmap.properties";
77
78 private static final String COMPARATORSMAP_PROPERTIES = "org/apache/jsieve/comparatorsmap.properties";
79
80 /**
81 * A Map of the Command names and their associated class names.
82 */
83 private ConcurrentMap<String, String> fieldCommandMap;
84
85 /**
86 * A Map of the Test names and their associated class names.
87 */
88 private ConcurrentMap<String, String> fieldTestMap;
89
90 /**
91 * A Map of the Comparator names and their associated class names.
92 */
93 private ConcurrentMap<String, String> fieldComparatorMap;
94
95 /**
96 * The initial size for the {@link ConcurrentHashMap} concurrency level.
97 */
98 private int initialConcurrencyLevel = DEFAULT_INITIAL_CONCURRENCY_LEVEL;
99
100 private static final Log LOG = LogFactory.getLog("org.apache.jsieve");
101
102 private Log log = LOG;
103
104 /**
105 * Constructor for ConfigurationManager.
106 *
107 * @throws SieveConfigurationException
108 */
109 public ConfigurationManager() throws SieveConfigurationException {
110 super();
111 try {
112 parse();
113 } catch (SAXException e) {
114 if (log.isErrorEnabled())
115 log.error("Exception processing Configuration: ", e);
116 throw new SieveConfigurationException(e);
117 } catch (IOException e) {
118 if (log.isErrorEnabled())
119 log.error("Exception processing Configuration: ", e);
120 throw new SieveConfigurationException(e);
121 }
122 }
123
124 /**
125 * Gets the current initial size for the {@link ConcurrentHashMap} concurrency level.
126 * @return number of concurrent threads estimated for initial sizing
127 */
128 public int getInitialConcurrencyLevel() {
129 return initialConcurrencyLevel;
130 }
131
132 /**
133 * Sets the current initial size for the {@link ConcurrentHashMap} concurrency level.
134 * @param initialConcurrencyLevel number of concurrent threads estimated for initial sizing
135 */
136 public void setInitialConcurrencyLevel(int initialConcurrencyLevel) {
137 this.initialConcurrencyLevel = initialConcurrencyLevel;
138 }
139
140
141
142 /**
143 * <p>
144 * Method getConfigStream answers an InputStream over the Sieve
145 * configuration file. It is located by searching the classpath of the
146 * current ClassLoader.
147 * </p>
148 * <p>
149 * The context classloader is searched first. If a suitably named resource
150 * is found then this is returned. Otherwise, the classloader used to load
151 * this class is searched for the resource.
152 * </p>
153 *
154 * @return InputStream
155 * @throws IOException
156 */
157 private InputStream getConfigStream(String configName) throws IOException {
158 InputStream stream = null;
159 // Context classloader is usually right in a JEE evironment
160 final ClassLoader contextClassLoader = Thread.currentThread()
161 .getContextClassLoader();
162 if (contextClassLoader != null) {
163 stream = contextClassLoader.getResourceAsStream(configName);
164 }
165
166 // Sometimes context classloader will not be set conventionally
167 // So, try class classloader
168 if (null == stream) {
169 stream = ConfigurationManager.class.getClassLoader()
170 .getResourceAsStream(configName);
171 }
172
173 if (null == stream)
174 throw new IOException("Resource \"" + configName + "\" not found");
175 return stream;
176 }
177
178 /**
179 * Method getCommandMap answers a Map of Command names and their associated
180 * class names, lazily initialized if required.
181 *
182 * @return Map not null
183 */
184 public ConcurrentMap<String, String> getCommandMap() {
185 if (null == fieldCommandMap) {
186 fieldCommandMap = new ConcurrentHashMap<String, String>();
187 }
188 return fieldCommandMap;
189 }
190
191 /**
192 * Method getTestMap answers a Map of Test names and their associated class
193 * names, lazily initialized if required.
194 *
195 * @return Map not null
196 */
197 public ConcurrentMap<String, String> getTestMap() {
198 if (null == fieldTestMap) {
199 fieldTestMap = new ConcurrentHashMap<String, String>();
200 }
201 return fieldTestMap;
202 }
203
204 /**
205 * Method getComparatorMap answers a Map of Comparator names and their
206 * associated class names, lazily initialized if required.
207 *
208 * @return Map not null
209 */
210 public ConcurrentMap<String, String> getComparatorMap() {
211 if (null == fieldComparatorMap) {
212 fieldComparatorMap = new ConcurrentHashMap<String, String>();
213 }
214 return fieldComparatorMap;
215 }
216
217 /**
218 * Method parse uses the Digester to parse the XML statements in the Sieve
219 * configuration file into Java objects.
220 *
221 * @throws SAXException
222 * @throws IOException
223 */
224 private void parse() throws SAXException, IOException {
225 setCommandMap(loadConfiguration(COMMANDSMAP_PROPERTIES));
226 setTestMap(loadConfiguration(TESTSMAP_PROPERTIES));
227 setComparatorMap(loadConfiguration(COMPARATORSMAP_PROPERTIES));
228 }
229
230 private ConcurrentMap<String,String> loadConfiguration(final String name) throws IOException {
231 final Properties properties = loadProperties(name);
232 final ConcurrentMap<String, String> result =
233 new ConcurrentHashMap<String, String>(properties.size(), 1.0f, initialConcurrencyLevel);
234 for (final Map.Entry<Object, Object> entry: properties.entrySet()) {
235 result.put(entry.getKey().toString(), entry.getValue().toString());
236 }
237 return result;
238 }
239
240 private Properties loadProperties(final String name) throws IOException {
241 final InputStream is = getConfigStream(name);
242 final Properties p = new Properties();
243 p.load(is);
244 return p;
245 }
246
247 /**
248 * Sets the commandMap.
249 *
250 * @param commandMap
251 * The commandMap to set
252 */
253 private void setCommandMap(ConcurrentMap<String, String> commandMap) {
254 fieldCommandMap = commandMap;
255 }
256
257 /**
258 * Sets the testMap.
259 *
260 * @param testMap
261 * The testMap to set
262 */
263 private void setTestMap(ConcurrentMap<String, String> testMap) {
264 fieldTestMap = testMap;
265 }
266
267 /**
268 * Sets the comparatorMap.
269 *
270 * @param comparatorMap
271 * The comparatorMap to set
272 */
273 private void setComparatorMap(ConcurrentMap<String, String> comparatorMap) {
274 fieldComparatorMap = comparatorMap;
275 }
276
277 public ComparatorManager getComparatorManager() {
278 return new ComparatorManagerImpl(fieldComparatorMap);
279 }
280
281 public CommandManager getCommandManager() {
282 return new CommandManagerImpl(fieldCommandMap);
283 }
284
285 public TestManager getTestManager() {
286 return new TestManagerImpl(fieldTestMap);
287 }
288
289 public Log getLog() {
290 return log;
291 }
292
293 public void setLog(Log log) {
294 this.log = log;
295 }
296
297 public SieveFactory build() {
298 return new SieveFactory(getCommandManager(), getComparatorManager(),
299 getTestManager(), getLog());
300 }
301 }