1 package org.molwind.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.math.BigDecimal;
26 import java.sql.Timestamp;
27 import java.util.ArrayList;
28 import java.util.Enumeration;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Observable;
32 import java.util.Properties;
33 import java.util.Stack;
34
35 import javax.servlet.ServletContext;
36
37 import org.apache.commons.configuration.Configuration;
38 import org.apache.commons.configuration.ConfigurationException;
39 import org.apache.commons.configuration.XMLConfiguration;
40
41
42 import org.molwind.io.BasePathSupport;
43 import org.molwind.io.WorldLocator;
44 import org.molwind.model.MolwindWorld;
45
46
47
48
49
50
51
52
53
54 public final class MolwindServerConfiguration extends Observable
55 implements WorldLocator, Configurator {
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 private String path;
72 private int moleculesPerLevel;
73 private long lastModified;
74 private double zoom;
75 private boolean intermediateLayer;
76 private ArrayList<String> worldPaths;
77 private ArrayList worldLocators;
78 private Stack configStack;
79 private ServletContext servletContext;
80
81 private static MolwindServerConfiguration config;
82
83
84
85
86 public static final int DEFAULT_MOLECULES_PER_LEVEL = 180;
87
88
89
90
91 public static final double DEFAULT_ZOOM = 0.5d;
92
93
94
95
96 public static final boolean DEFAULT_INTERMEDIATE_LAYER = false;
97
98
99 private MolwindServerConfiguration() {
100 this(null);
101 }
102
103 private MolwindServerConfiguration(final String newPath) {
104 super();
105 path = newPath;
106 moleculesPerLevel = DEFAULT_MOLECULES_PER_LEVEL;
107 zoom = DEFAULT_ZOOM;
108 lastModified = 0L;
109 configStack = new Stack();
110 worldPaths = new ArrayList<String>();
111 worldLocators = new ArrayList();
112 initWorldPaths();
113
114 }
115
116 private void initWorldPaths() {
117 String userPath = System.getProperty("user.dir");
118 if (userPath != null) {
119 addWorldPath(userPath);
120 }
121
122 String userHomePath = System.getProperty("user.home");
123 if (userHomePath != null) {
124 addWorldPath(userHomePath);
125 }
126
127 String javaPath = System.getProperty("java.class.path");
128 String[] javaPathParts = javaPath.split(File.pathSeparator);
129
130 for (int i = 0; i < javaPathParts.length; i++) {
131 File theFile = new File(javaPathParts[i]);
132
133 if (theFile.isDirectory()) {
134 addWorldPath(javaPathParts[i]);
135 } else {
136 theFile = theFile.getParentFile();
137 if (theFile != null) {
138 addWorldPath(theFile.getPath());
139 }
140 }
141 }
142 }
143
144
145
146
147
148
149
150
151
152
153
154 public static MolwindServerConfiguration getInstance(final String path) {
155 if (config == null) {
156 config = new MolwindServerConfiguration(path);
157 }
158 return config;
159 }
160
161
162
163
164
165
166
167
168 public static MolwindServerConfiguration getInstance() {
169 return getInstance(null);
170 }
171
172
173 private void pushConfig(final Configuration cfg, final String startKey)
174 throws ConfigurationException {
175 Iterator pit = cfg.getKeys(startKey);
176 Properties properties = new Properties();
177 configStack.push(properties);
178
179 while (pit.hasNext()) {
180 String kn = (String) pit.next();
181 String an = Stringx.after(kn, startKey + ".");
182 String kv = cfg.getString(kn);
183
184 if (kv != null) {
185 properties.put(an, kv);
186 }
187 }
188 }
189
190 private void load()
191 throws IOException {
192 File theFile = null;
193
194 if (path != null) {
195 theFile = new File(path);
196
197 if (theFile.exists() && (theFile.lastModified() > lastModified)) {
198 MolwindLogger.info("Loading config from " + theFile);
199
200 try {
201 XMLConfiguration xmlConfig = new XMLConfiguration(theFile);
202
203 setMoleculesPerLevel(xmlConfig.getInt("moleculesPerLevel",
204 DEFAULT_MOLECULES_PER_LEVEL));
205 setZoom(xmlConfig.getDouble("zoom", DEFAULT_ZOOM));
206 setIntermediateLayer(xmlConfig.getBoolean(
207 "intermediateLayer", DEFAULT_INTERMEDIATE_LAYER));
208
209 List list = xmlConfig.getList("locators.locator.class");
210
211 Iterator it = list.iterator();
212 clearWorldLocators();
213
214 for (int i = 0; it.hasNext(); i++) {
215 String className = (String) it.next();
216 String pathNames= xmlConfig.getString("locators.locator.params.pathNames");
217
218 try {
219 Class clazz = Class.forName(className);
220 WorldLocator locator =
221 (WorldLocator) clazz.newInstance();
222
223 if (locator instanceof Configurable) {
224 pushConfig(xmlConfig,
225 "locators.locator(" + i + ").params");
226 try {
227 ((Configurable) locator).configure(this);
228 } catch (ConfigException ce) {
229 throw new IOException(ce.getMessage());
230 }
231 }
232 if( locator instanceof BasePathSupport ) {
233 BasePathSupport bps = (BasePathSupport)locator;
234 for(String s: worldPaths ){
235 bps.addBasePath( s );
236 }
237
238 }
239
240
241
242
243
244
245 addWorldLocator(locator);
246 } catch (ClassNotFoundException cnfe) {
247 MolwindLogger.warn("Cannot find locator "
248 + className, cnfe);
249 } catch (InstantiationException iex) {
250 MolwindLogger.warn("Cannot instantiate "
251 + className, iex);
252 } catch (IllegalAccessException iax) {
253 MolwindLogger.warn("Cannot instantiate "
254 + className, iax);
255 }
256 }
257 lastModified = theFile.lastModified();
258 } catch (ConfigurationException ce) {
259 throw new IOException(ce.getMessage());
260 }
261 }
262 }
263 }
264
265 private void ensureLoad() {
266 try {
267 load();
268 } catch (IOException ioe) {
269 MolwindLogger.warn(ioe);
270 }
271 }
272
273
274
275
276
277
278
279
280 public String getPath() {
281 ensureLoad();
282 return path;
283 }
284
285
286
287
288
289
290
291 public void setPath(final String newPath) {
292 this.path = newPath;
293 lastModified = 0L;
294 }
295
296
297
298
299
300
301
302 public int getMoleculesPerLevel() {
303 ensureLoad();
304 return moleculesPerLevel;
305 }
306
307
308
309
310
311
312
313 public void setMoleculesPerLevel(final int newMoleculesPerLevel) {
314 this.moleculesPerLevel = newMoleculesPerLevel;
315 }
316
317
318
319
320
321
322
323 public double getZoom() {
324 ensureLoad();
325 return zoom;
326 }
327
328
329
330
331
332
333
334 public void setZoom(final double newZoom) {
335 this.zoom = newZoom;
336 }
337
338
339
340
341
342
343
344 public boolean isIntermediateLayer() {
345 ensureLoad();
346 return intermediateLayer;
347 }
348
349
350
351
352
353
354
355 public void setIntermediateLayer(final boolean newIntermediateLayer) {
356 this.intermediateLayer = newIntermediateLayer;
357 }
358
359
360
361
362
363
364
365 public void addWorldPath(final String searchPath) {
366 worldPaths.add(searchPath);
367
368 }
369
370
371
372
373
374
375
376 public String[] getWorldPaths() {
377 ensureLoad();
378 String[] paths = new String[worldPaths.size()];
379 return (String[]) worldPaths.toArray(paths);
380 }
381
382
383 private void clearWorldLocators() {
384 worldLocators.clear();
385 }
386
387
388
389
390
391
392
393
394 public void addWorldLocator(final WorldLocator locator) {
395 worldLocators.add(locator);
396 }
397
398
399
400
401
402
403
404 public WorldLocator[] getWorldLocators() {
405 ensureLoad();
406 WorldLocator[] locators = new WorldLocator[worldLocators.size()];
407 return (WorldLocator[]) worldLocators.toArray(locators);
408 }
409
410
411 private String toWorldPath(final String prefix, final String suffix) {
412 File theFile = new File(prefix);
413
414 if (!theFile.exists() && (servletContext != null)) {
415 String scPath = servletContext.getRealPath("/");
416 theFile = new File(scPath, prefix);
417 if (theFile.exists()) {
418 return (new File(theFile, suffix)).getPath();
419 }
420 }
421
422 String worldPath = prefix + (prefix.endsWith("/") ? "" : "/") + suffix;
423
424 return worldPath;
425 }
426
427
428
429
430
431
432
433
434
435
436
437
438 public MolwindWorld locateWorld(final String worldName)
439 throws IOException {
440 ensureLoad();
441 MolwindWorld world = null;
442 WorldLocator[] locators = getWorldLocators();
443 String[] worldPaths = getWorldPaths();
444
445 for (int i = 0; i < locators.length; i++) {
446 for (int j = 0; j < worldPaths.length; j++) {
447 world = locators[i].locateWorld(toWorldPath(worldPaths[j],
448 worldName));
449 if (world != null) {
450 return world;
451 }
452 }
453 }
454
455 return null;
456 }
457
458
459
460
461
462
463
464 public ServletContext getServletContext() {
465 return servletContext;
466 }
467
468
469
470
471
472
473
474 public void setServletContext(final ServletContext newServletContext) {
475 this.servletContext = newServletContext;
476 }
477
478
479 private BigDecimal convertTimestamp(final String pVal) {
480 BigDecimal bDec = null;
481 try {
482 Timestamp timestamp = Timestamp.valueOf(pVal);
483 bDec = BigDecimal.valueOf(timestamp.getTime());
484 } catch (IllegalArgumentException iae) {
485 bDec = null;
486 }
487 return bDec;
488 }
489
490 private BigDecimal numberInit(final String pVal) {
491 if (pVal.length() <= 0) {
492 return BigDecimal.ZERO;
493 }
494
495 BigDecimal bDec = null;
496 try {
497 bDec = new BigDecimal(pVal);
498 } catch (NumberFormatException nfe) {
499
500 bDec = convertTimestamp(pVal);
501 }
502 if (bDec == null) {
503 throw new NumberFormatException();
504 }
505 return bDec;
506 }
507
508 private Object[] createParam(final Class pType, final String pVal) {
509 Object[] param = new Object[1];
510
511 if (pType.equals(Boolean.class) || pType.equals(Boolean.TYPE)) {
512 param[0] = Boolean.valueOf(Stringx.toBoolean(pVal, false));
513 } else if (pType.equals(Character.class)
514 || pType.equals(Character.TYPE)) {
515 param[0] = Character.valueOf(
516 ((pVal.length() > 0) ? pVal.charAt(0) : (char) 0)
517 );
518 } else if (pType.equals(Byte.class) || pType.equals(Byte.TYPE)) {
519 if (pVal.matches("0[01]+") || pVal.matches("[01]{4,8}+")) {
520 param[0] = Byte.valueOf(Byte.parseByte(pVal, 2));
521 } else {
522 param[0] = Byte.valueOf(pVal);
523 }
524 } else if (pType.equals(Short.class) || pType.equals(Short.TYPE)) {
525 param[0] = Short.valueOf((numberInit(pVal)).shortValue());
526 } else if (pType.equals(Integer.class) || pType.equals(Integer.TYPE)) {
527 param[0] = Integer.valueOf((numberInit(pVal)).intValue());
528 } else if (pType.equals(Long.class) || pType.equals(Long.TYPE)) {
529 param[0] = Long.valueOf((numberInit(pVal)).longValue());
530 } else if (pType.equals(Float.class) || pType.equals(Float.TYPE)) {
531 param[0] = Float.valueOf((numberInit(pVal)).floatValue());
532 } else if (pType.equals(Double.class) || pType.equals(Double.TYPE)) {
533 param[0] = Double.valueOf((numberInit(pVal)).doubleValue());
534 } else if (pType.equals(String.class)) {
535 param[0] = pVal;
536 } else if (pType.equals(BigDecimal.class)) {
537 param[0] = numberInit(pVal);
538 } else {
539
540 try {
541 Class clazz = Class.forName(pVal);
542 param[0] = clazz.newInstance();
543 return param;
544 } catch (ClassNotFoundException cnfe) {
545 cnfe.printStackTrace();
546 } catch (InstantiationException iex) {
547 iex.printStackTrace();
548 } catch (IllegalAccessException iax) {
549 iax.printStackTrace();
550 }
551
552
553
554 try {
555 Constructor constructor = pType.getConstructor(pVal.getClass());
556 param[0] = constructor.newInstance(pVal);
557 } catch (Exception ex) {
558 ex.printStackTrace();
559 param = null;
560 }
561 }
562
563 return param;
564 }
565
566 private void invokeSetter(final Object object, final Properties properties)
567 throws ConfigException {
568 Class clazz = object.getClass();
569 Method[] methods = clazz.getMethods();
570 Enumeration en = properties.propertyNames();
571
572 while (en.hasMoreElements()) {
573 String an = (String) en.nextElement();
574 String cont = properties.getProperty(an);
575 String mName = "set" + an.toLowerCase();
576
577 for (int i = 0; i < methods.length; i++) {
578 if (methods[i].getName().equalsIgnoreCase(mName)) {
579 Class[] pTypes = methods[i].getParameterTypes();
580 if (pTypes.length != 1) {
581 continue;
582 }
583 Object[] param = createParam(pTypes[0], cont);
584 if (param == null) {
585 continue;
586 }
587 try {
588 methods[i].invoke(object, param);
589 } catch (IllegalAccessException iae) {
590 throw new ConfigException(iae.getMessage());
591 } catch (InvocationTargetException itex) {
592 throw new ConfigException(itex.getMessage());
593 }
594 }
595 }
596 }
597 }
598
599
600
601
602
603
604
605
606
607
608 public void setup(final Configurable object)
609 throws ConfigException {
610 if (!configStack.isEmpty()) {
611 Properties properties = (Properties) configStack.pop();
612 invokeSetter(object, properties);
613 }
614
615 }
616
617
618
619
620
621
622
623 public String[] getWorldNames() {
624
625 ArrayList<String> result = new ArrayList<String>();
626 for(WorldLocator w:getWorldLocators()){
627 for(String s:w.getWorldNames()){
628 result.add(s);
629 }
630
631 }
632 String[] resultArray = new String[result.size()];
633 int i = 0;
634 for(String h:result){
635 resultArray[i]=h;
636 i++;
637 }
638
639 return resultArray;
640
641
642 }
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861 }