1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package de.pdark.dsmp;
17
18 import java.io.File;
19 import java.io.IOException;
20 import java.net.MalformedURLException;
21 import java.net.URL;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.Iterator;
25 import java.util.List;
26
27 import org.apache.commons.lang.StringUtils;
28 import org.apache.commons.lang.SystemUtils;
29 import org.apache.log4j.Logger;
30 import org.jdom.Document;
31 import org.jdom.Element;
32 import org.jdom.JDOMException;
33 import org.jdom.input.SAXBuilder;
34
35 /**
36 * Read and manage the configuration.
37 *
38 * <p>Unlike the standard config classes, this one allows to reload
39 * the config at any convenient time.
40 *
41 * @author digulla
42 *
43 */
44 public class Config
45 {
46 public static final Logger log = Logger.getLogger(Config.class);
47
48 private static Document config;
49 private static long configLastModified;
50 private static String BASE_DIR = null;
51 private static int serverPort = 8080;
52 private static String proxyHost = "proxy";
53 private static int proxyPort = 80;
54 private static String proxyUser;
55 private static String proxyPassword;
56 private static File cacheDirectory = new File ("cache");
57 private static File patchesDirectory = new File ("patches");
58
59 public static synchronized void reload ()
60 {
61 String fileName = System.getProperty("dsmp.conf", "dsmp.conf");
62 File configFile = new File (fileName);
63 if (!configFile.isAbsolute())
64 configFile = new File (getBaseDirectory(), fileName);
65
66
67 long lastModified = configFile.lastModified();
68 if (config == null || lastModified != configLastModified)
69 {
70 log.info((config == null ? "Loading" : "Reloading")+ " config from "+configFile.getAbsolutePath());
71 configLastModified = lastModified;
72
73 SAXBuilder builder = new SAXBuilder ();
74 Throwable t = null;
75 Document doc = config;
76 List<MirrorEntry> tmpMirrors = mirrors;
77 List<AllowDeny> tmpAllowDeny = allowDeny;
78 String[] tmpNoProxy = noProxy;
79 int tmpPort = serverPort;
80 String tmpProxyHost = proxyHost;
81 int tmpProxyPort = proxyPort;
82 String tmpProxyUser = proxyUser;
83 String tmpProxyPassword = proxyPassword;
84 File tmpCacheDirectory = cacheDirectory;
85 File tmpPatchesDirectory = patchesDirectory;
86
87 try
88 {
89 doc = builder.build(configFile);
90 Element root = doc.getRootElement ();
91 tmpCacheDirectory = getCacheDirectory (root);
92 tmpPatchesDirectory = getPatchesDirectory (root);
93 tmpMirrors = getMirrors (root);
94 tmpAllowDeny = getAllowDeny (root);
95 tmpNoProxy = getNoProxy (root);
96 tmpPort = getPort (root);
97 tmpProxyHost = getProxyHost (root);
98 tmpProxyPort = getProxyPort (root);
99 tmpProxyUser = getProxyUsername (root);
100 tmpProxyPassword = getProxyPassword (root);
101 }
102 catch (JDOMException e)
103 {
104 t = e;
105 }
106 catch (IOException e)
107 {
108 t = e;
109 }
110
111 if (t != null)
112 {
113 String msg = "Error loading config from "+configFile.getAbsolutePath();
114 log.error (msg, t);
115 if (config == null)
116 throw new Error (msg, t);
117 }
118
119
120
121
122 config = doc;
123 cacheDirectory = tmpCacheDirectory;
124 patchesDirectory = tmpPatchesDirectory;
125 mirrors = tmpMirrors;
126 allowDeny = tmpAllowDeny;
127 noProxy = tmpNoProxy;
128 serverPort = tmpPort;
129 proxyHost = tmpProxyHost;
130 proxyPort = tmpProxyPort;
131 proxyUser = tmpProxyUser;
132 proxyPassword = tmpProxyPassword;
133 }
134 }
135
136 public static void setBaseDir (String path)
137 {
138 Config.BASE_DIR = path;
139 }
140
141 private static int getPort (Element root)
142 {
143 int port = getIntProperty (root, "server", "port", 8080);
144 int max = 0xffff;
145 if (port < 1 || port > max)
146 throw new RuntimeException ("Value for proxy.port must be between 1 and "+max);
147 return port;
148 }
149
150 public static int getPort ()
151 {
152 return serverPort;
153 }
154
155 private static File getCacheDirectory (Element root)
156 {
157 String defaultValue = "cache";
158
159 String s = getStringProperty(root, "directories", "cache", defaultValue);
160 File f = new File (s);
161 if (!f.isAbsolute())
162 f = new File (getBaseDirectory (), s);
163
164 IOUtils.mkdirs (f);
165
166 return f;
167 }
168
169 public static File getCacheDirectory ()
170 {
171 return cacheDirectory;
172 }
173
174 private static File getPatchesDirectory (Element root)
175 {
176 String defaultValue = "patches";
177
178 String s = getStringProperty(root, "directories", "patches", defaultValue);
179 File f = new File (s);
180 if (!f.isAbsolute())
181 f = new File (getBaseDirectory (), s);
182
183 IOUtils.mkdirs (f);
184
185 return f;
186 }
187
188 public static File getPatchesDirectory ()
189 {
190 return patchesDirectory;
191 }
192
193 public static File getBaseDirectory ()
194 {
195 String path = BASE_DIR;
196 if (path == null)
197 path = SystemUtils.USER_HOME;
198 return new File (path);
199 }
200
201 private static String getStringProperty (Element root, String element, String attribute, String defaultValue)
202 {
203 Element e = root.getChild(element);
204 if (e == null)
205 return defaultValue;
206
207 String value = e.getAttributeValue(attribute);
208 if (value == null)
209 return defaultValue;
210
211 return value;
212 }
213
214 private static boolean hasProperty (Element root, String element)
215 {
216 Element e = root.getChild(element);
217 if (e == null)
218 return false;
219
220 return true;
221 }
222
223 private static String getStringProperty (Element root, String element, String attribute)
224 {
225 String value = getStringProperty(root, element, attribute, null);
226 if (value == null)
227 throw new RuntimeException ("Property "+element+"@"+attribute+" is not set.");
228
229 return value;
230 }
231
232 private static int getIntProperty (Element root, String element, String attribute, int defaultValue)
233 {
234 String value = getStringProperty(root, element, attribute, null);
235 if (value == null)
236 return defaultValue;
237
238 try
239 {
240 return Integer.parseInt(value);
241 }
242 catch (NumberFormatException e)
243 {
244 throw (NumberFormatException)(new NumberFormatException ("Error convertion value '"+value+"' of property "+element+"@"+attribute+": "+e.getMessage()).initCause(e));
245 }
246 }
247
248 private static boolean hasProxy (Element root)
249 {
250 return hasProperty (root, "proxy");
251 }
252
253 private static String getProxyUsername (Element root)
254 {
255 if (!hasProxy (root))
256 return null;
257
258 return getStringProperty (root, "proxy", "user");
259 }
260
261 public static String getProxyUsername ()
262 {
263 return proxyUser;
264 }
265
266 private static String getProxyPassword (Element root)
267 {
268 if (!hasProxy (root))
269 return null;
270
271 return getStringProperty (root, "proxy", "password");
272 }
273
274 public static String getProxyPassword ()
275 {
276 return proxyPassword;
277 }
278
279 private static String getProxyHost (Element root)
280 {
281 if (!hasProxy (root))
282 return null;
283
284 return getStringProperty (root, "proxy", "host");
285 }
286
287 public static String getProxyHost ()
288 {
289 return proxyHost;
290 }
291
292 private static int getProxyPort (Element root)
293 {
294 int port = getIntProperty (root, "proxy", "port", 80);
295 int max = 0xffff;
296 if (port < 1 || port > max)
297 throw new RuntimeException ("Value for proxy.port must be between 1 and "+max);
298 return port;
299 }
300
301 public static int getProxyPort ()
302 {
303 return proxyPort;
304 }
305
306 private static class MirrorEntry
307 {
308 private String from;
309 private String to;
310
311 public MirrorEntry (String from, String to)
312 {
313 this.from = fix (from);
314 this.to = fix (to);
315 }
316
317 private String fix (String s)
318 {
319 s = s.trim ();
320 if (!s.endsWith("/"))
321 s += "/";
322 return s;
323 }
324
325 public URL getMirrorURL (String s)
326 {
327
328
329
330 if (s.startsWith(from))
331 {
332 s = s.substring(from.length());
333 s = to + s;
334 try
335 {
336 return new URL (s);
337 }
338 catch (MalformedURLException e)
339 {
340 throw new RuntimeException ("Couldn't create URL from "+s, e);
341 }
342 }
343
344 return null;
345 }
346 }
347
348 private static List<MirrorEntry> mirrors = Collections.emptyList ();
349
350 public static List<MirrorEntry> getMirrors (Element root)
351 {
352 List<MirrorEntry> l = new ArrayList<MirrorEntry> ();
353 for (Iterator iter = root.getChildren("redirect").iterator(); iter.hasNext();)
354 {
355 Element element = (Element)iter.next();
356 String from = element.getAttributeValue("from");
357 String to = element.getAttributeValue("to");
358
359 if (StringUtils.isBlank(from))
360 throw new RuntimeException ("from attribute is missing or empty in redirect element");
361 if (StringUtils.isBlank(to))
362 throw new RuntimeException ("to attribute is missing or empty in redirect element");
363
364 l.add (new MirrorEntry (from, to));
365 }
366
367 return l;
368 }
369
370 public static List<MirrorEntry> getMirrors ()
371 {
372 return mirrors;
373 }
374
375 public static URL getMirror (URL url) throws MalformedURLException
376 {
377 String s = url.toString();
378
379 for (MirrorEntry entry: getMirrors())
380 {
381 URL mirror = entry.getMirrorURL(s);
382 if (mirror != null)
383 {
384 log.info ("Redirecting request to mirror "+mirror.toString());
385 return mirror;
386 }
387 }
388
389 return url;
390 }
391
392 private static String[] noProxy = new String[0];
393
394 private static String[] getNoProxy (Element root)
395 {
396 String s = getStringProperty(root, "proxy", "no-proxy", null);
397 if (s == null)
398 return new String[0];
399
400 String[] result = StringUtils.split(s, ",");
401 for (int i=0; i<result.length; i++)
402 {
403 result[i] = result[i].trim ();
404 }
405
406 return result;
407 }
408
409 public static String[] getNoProxy ()
410 {
411 return noProxy;
412 }
413
414 public static boolean useProxy (URL url)
415 {
416 if (!hasProxy (config.getRootElement ()))
417 return false;
418
419 String host = url.getHost();
420 for (String postfix: getNoProxy())
421 {
422 if (host.endsWith(postfix))
423 return false;
424 }
425 return true;
426 }
427
428 private static class AllowDeny
429 {
430 private final String url;
431 private boolean allow;
432
433 public AllowDeny (String url, boolean allow)
434 {
435 this.url = url;
436 this.allow = allow;
437 }
438
439 public boolean matches (String url)
440 {
441 return url.startsWith(this.url);
442 }
443
444 public boolean isAllowed ()
445 {
446 return allow;
447 }
448
449 public String getURL ()
450 {
451 return url;
452 }
453 }
454
455 private static List<AllowDeny> allowDeny = Collections.emptyList ();
456
457 public static List<AllowDeny> getAllowDeny (Element root)
458 {
459 ArrayList<AllowDeny> l = new ArrayList<AllowDeny> ();
460
461 for (Iterator iter = root.getChildren().iterator(); iter.hasNext();)
462 {
463 Element element = (Element)iter.next();
464 if ("allow".equals (element.getName()) || "deny".equals(element.getName()))
465 {
466 boolean allow = "allow".equals (element.getName());
467 String url = element.getAttributeValue("url");
468 if (url == null)
469 throw new RuntimeException ("Missing or empty url attribute in "+element.getName()+" element");
470 l.add (new AllowDeny (url, allow));
471 }
472 }
473
474 return l;
475 }
476
477 public static List<AllowDeny> getAllowDeny ()
478 {
479 return allowDeny;
480 }
481
482 public static boolean isAllowed (URL url)
483 {
484 String s = url.toString();
485 for (AllowDeny rule: getAllowDeny())
486 {
487 if (rule.matches(s))
488 {
489 log.info((rule.isAllowed() ? "Allowing" : "Denying")+" access to "+url+" because of config rule");
490 return rule.isAllowed();
491 }
492 }
493
494 return true;
495 }
496 }