001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.config;
022
023 import java.io.ByteArrayInputStream;
024 import java.io.IOException;
025 import java.io.InputStream;
026 import java.io.ObjectInput;
027 import java.io.ObjectOutput;
028 import java.io.OutputStream;
029 import java.lang.annotation.Annotation;
030 import java.lang.reflect.Constructor;
031 import java.lang.reflect.Modifier;
032 import java.util.ArrayList;
033 import java.util.HashMap;
034 import java.util.List;
035 import java.util.Map;
036 import java.util.Properties;
037 import java.util.Set;
038 import java.util.concurrent.ConcurrentHashMap;
039
040 import org.granite.config.api.Configuration;
041 import org.granite.logging.Logger;
042 import org.granite.messaging.amf.io.AMF3Deserializer;
043 import org.granite.messaging.amf.io.AMF3DeserializerSecurizer;
044 import org.granite.messaging.amf.io.AMF3Serializer;
045 import org.granite.messaging.amf.io.convert.Converter;
046 import org.granite.messaging.amf.io.convert.Converters;
047 import org.granite.messaging.amf.io.util.ActionScriptClassDescriptor;
048 import org.granite.messaging.amf.io.util.ClassGetter;
049 import org.granite.messaging.amf.io.util.DefaultClassGetter;
050 import org.granite.messaging.amf.io.util.JavaClassDescriptor;
051 import org.granite.messaging.amf.io.util.externalizer.Externalizer;
052 import org.granite.messaging.amf.process.AMF3MessageInterceptor;
053 import org.granite.messaging.service.DefaultMethodMatcher;
054 import org.granite.messaging.service.ExceptionConverter;
055 import org.granite.messaging.service.MethodMatcher;
056 import org.granite.messaging.service.ServiceInvocationListener;
057 import org.granite.messaging.service.security.SecurityService;
058 import org.granite.messaging.service.tide.TideComponentMatcher;
059 import org.granite.scan.ScannedItem;
060 import org.granite.scan.ScannedItemHandler;
061 import org.granite.scan.Scanner;
062 import org.granite.scan.ScannerFactory;
063 import org.granite.util.ClassUtil;
064 import org.granite.util.StreamUtil;
065 import org.granite.util.XMap;
066 import org.xml.sax.EntityResolver;
067 import org.xml.sax.InputSource;
068 import org.xml.sax.SAXException;
069
070 /**
071 * @author Franck WOLFF
072 */
073 public class GraniteConfig implements ScannedItemHandler {
074
075 ///////////////////////////////////////////////////////////////////////////
076 // Static fields.
077
078 private static final Logger log = Logger.getLogger(GraniteConfig.class);
079
080 private static final String GRANITE_CONFIG_PUBLIC_ID = "-//Granite Data Services//DTD granite-config internal//EN";
081 private static final String GRANITE_CONFIG_PROPERTIES = "META-INF/granite-config.properties";
082
083 final ExternalizerFactory EXTERNALIZER_FACTORY = new ExternalizerFactory();
084 final ActionScriptClassDescriptorFactory ASC_DESCRIPTOR_FACTORY = new ActionScriptClassDescriptorFactory();
085 final JavaClassDescriptorFactory JC_DESCRIPTOR_FACTORY = new JavaClassDescriptorFactory();
086 final TideComponentMatcherFactory TIDE_COMPONENT_MATCHER_FACTORY = new TideComponentMatcherFactory();
087
088 ///////////////////////////////////////////////////////////////////////////
089 // Instance fields.
090
091 // Should we scan classpath for auto-configured services/externalizers?
092 private boolean scan = false;
093
094 private String MBeanContextName = null;
095
096 // Custom AMF3 (De)Serializer configuration.
097 private Constructor<AMF3Serializer> amf3SerializerConstructor = null;
098 private Constructor<AMF3Deserializer> amf3DeserializerConstructor = null;
099
100 private AMF3DeserializerSecurizer amf3DeserializerSecurizer = null;
101
102 // Custom AMF3 message interceptor configuration.
103 private AMF3MessageInterceptor amf3MessageInterceptor = null;
104
105 // Converters configuration.
106 private List<Class<? extends Converter>> converterClasses = new ArrayList<Class<? extends Converter>>();
107 private Converters converters = null;
108
109 // MethodMatcher configuration.
110 private MethodMatcher methodMatcher = new DefaultMethodMatcher();
111
112 // Invocation listener configuration.
113 private ServiceInvocationListener invocationListener = null;
114
115 // Instantiators configuration.
116 private final Map<String, String> instantiators = new HashMap<String, String>();
117
118 // Class getter configuration.
119 private ClassGetter classGetter = new DefaultClassGetter();
120 private boolean classGetterSet = false;
121
122 // Externalizers configuration.
123 private XMap externalizersConfiguration = null;
124 private final List<Externalizer> scannedExternalizers = new ArrayList<Externalizer>();
125 private final ConcurrentHashMap<String, Externalizer> externalizersByType
126 = new ConcurrentHashMap<String, Externalizer>();
127 private final Map<String, String> externalizersByInstanceOf = new HashMap<String, String>();
128 private final Map<String, String> externalizersByAnnotatedWith = new HashMap<String, String>();
129
130 // Java descriptors configuration.
131 private final ConcurrentHashMap<String, Class<? extends JavaClassDescriptor>> javaDescriptorsByType
132 = new ConcurrentHashMap<String, Class<? extends JavaClassDescriptor>>();
133 private final Map<String, String> javaDescriptorsByInstanceOf = new HashMap<String, String>();
134
135 // AS3 descriptors configuration.
136 private final ConcurrentHashMap<String, Class<? extends ActionScriptClassDescriptor>> as3DescriptorsByType
137 = new ConcurrentHashMap<String, Class<? extends ActionScriptClassDescriptor>>();
138 private final Map<String, String> as3DescriptorsByInstanceOf = new HashMap<String, String>();
139
140 // Exception converters
141 private final List<ExceptionConverter> exceptionConverters = new ArrayList<ExceptionConverter>();
142
143 // Tide-enabled Components configuration.
144 private final ConcurrentHashMap<String, Object[]> enabledTideComponentsByName = new ConcurrentHashMap<String, Object[]>();
145 private final ConcurrentHashMap<String, Object[]> disabledTideComponentsByName = new ConcurrentHashMap<String, Object[]>();
146 private final List<TideComponentMatcher> tideComponentMatchers = new ArrayList<TideComponentMatcher>();
147
148 // Security service configuration.
149 private SecurityService securityService = null;
150
151 // MessageSelector configuration.
152 private Constructor<?> messageSelectorConstructor;
153
154 // Gravity configuration.
155 private XMap gravityConfig;
156
157 ///////////////////////////////////////////////////////////////////////////
158 // Constructor.
159
160 public GraniteConfig(String stdConfig, InputStream customConfigIs, Configuration configuration, String MBeanContextName) throws IOException, SAXException {
161 try {
162 amf3SerializerConstructor = ClassUtil.getConstructor(AMF3Serializer.class, new Class<?>[]{OutputStream.class});
163 amf3DeserializerConstructor = ClassUtil.getConstructor(AMF3Deserializer.class, new Class<?>[]{InputStream.class});
164 } catch (Exception e) {
165 throw new GraniteConfigException("Could not get constructor for AMF3 (de)serializers", e);
166 }
167
168 this.MBeanContextName = MBeanContextName;
169
170 ClassLoader loader = GraniteConfig.class.getClassLoader();
171
172 final ByteArrayInputStream dtd = StreamUtil.getResourceAsStream("org/granite/config/granite-config.dtd", loader);
173 final EntityResolver resolver = new EntityResolver() {
174 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
175 if (GRANITE_CONFIG_PUBLIC_ID.equals(publicId)) {
176 dtd.reset();
177 InputSource source = new InputSource(dtd);
178 source.setPublicId(publicId);
179 return source;
180 }
181 return null;
182 }
183 };
184
185 // Load standard config.
186 InputStream is = null;
187 try {
188 is = StreamUtil.getResourceAsStream("org/granite/config/granite-config.xml", loader);
189 XMap doc = new XMap(is, resolver);
190 forElement(doc, false, null);
191 } finally {
192 if (is != null)
193 is.close();
194 }
195
196 if (stdConfig != null) {
197 try {
198 is = StreamUtil.getResourceAsStream(stdConfig, loader);
199 XMap doc = new XMap(is, resolver);
200 forElement(doc, false, null);
201 } finally {
202 if (is != null)
203 is.close();
204 }
205 }
206
207 // Load custom config (override).
208 if (customConfigIs != null) {
209 XMap doc = new XMap(customConfigIs, resolver);
210 forElement(doc, true, configuration != null ? configuration.getGraniteConfigProperties() : null);
211 }
212
213 if (amf3DeserializerSecurizer == null)
214 log.warn("You should configure a deserializer securizer in your granite-config.xml file in order to prevent potential security exploits!");
215 }
216
217
218 ///////////////////////////////////////////////////////////////////////////
219 // Classpath scan initialization.
220
221 private void scanConfig(String graniteConfigProperties) {
222 //if config overriding exists
223 Scanner scanner = ScannerFactory.createScanner(this, graniteConfigProperties != null ? graniteConfigProperties : GRANITE_CONFIG_PROPERTIES);
224 try {
225 scanner.scan();
226 } catch (Exception e) {
227 log.error(e, "Could not scan classpath for configuration");
228 }
229 }
230
231 public boolean handleMarkerItem(ScannedItem item) {
232 try {
233 return handleProperties(item.loadAsProperties());
234 } catch (Exception e) {
235 log.error(e, "Could not load properties: %s", item);
236 }
237 return true;
238 }
239
240 public void handleScannedItem(ScannedItem item) {
241 if ("class".equals(item.getExtension()) && item.getName().indexOf('$') == -1) {
242 try {
243 handleClass(item.loadAsClass());
244 } catch (NoClassDefFoundError e) {
245 // Ignore errors with Tide classes depending on Gravity
246 } catch (LinkageError e) {
247 // Ignore errors with GraniteDS/Hibernate classes depending on Hibernate 3 when using Hibernate 4
248 } catch (Throwable t) {
249 log.error(t, "Could not load class: %s", item);
250 }
251 }
252 }
253
254 private boolean handleProperties(Properties properties) {
255 if (properties.getProperty("dependsOn") != null) {
256 String dependsOn = properties.getProperty("dependsOn");
257 try {
258 ClassUtil.forName(dependsOn);
259 }
260 catch (ClassNotFoundException e) {
261 // Class not found, skip scan for this package
262 return true;
263 }
264 }
265
266 String classGetterName = properties.getProperty("classGetter");
267 if (!classGetterSet && classGetterName != null) {
268 try {
269 classGetter = ClassUtil.newInstance(classGetterName, ClassGetter.class);
270 } catch (Throwable t) {
271 log.error(t, "Could not create instance of: %s", classGetterName);
272 }
273 }
274
275 String amf3MessageInterceptorName = properties.getProperty("amf3MessageInterceptor");
276 if (amf3MessageInterceptor == null && amf3MessageInterceptorName != null) {
277 try {
278 amf3MessageInterceptor = ClassUtil.newInstance(amf3MessageInterceptorName, AMF3MessageInterceptor.class);
279 } catch (Throwable t) {
280 log.error(t, "Could not create instance of: %s", amf3MessageInterceptorName);
281 }
282 }
283
284 for (Map.Entry<?, ?> me : properties.entrySet()) {
285 if (me.getKey().toString().startsWith("converter.")) {
286 String converterName = me.getValue().toString();
287 try {
288 converterClasses.add(ClassUtil.forName(converterName, Converter.class));
289 } catch (Exception e) {
290 throw new GraniteConfigException("Could not get converter class for: " + converterName, e);
291 }
292 }
293 }
294
295 return false;
296 }
297
298 private void handleClass(Class<?> clazz) {
299 if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
300 if (Externalizer.class.isAssignableFrom(clazz)) {
301 try {
302 scannedExternalizers.add(ClassUtil.newInstance(clazz, Externalizer.class));
303 } catch (Exception e) {
304 log.error(e, "Could not create new instance of: %s", clazz);
305 }
306 }
307
308 if (ExceptionConverter.class.isAssignableFrom(clazz)) {
309 try {
310 exceptionConverters.add(ClassUtil.newInstance(clazz, ExceptionConverter.class));
311 } catch (Exception e) {
312 if (!clazz.getName().equals("org.granite.tide.hibernate.HibernateValidatorExceptionConverter")) // GDS-582
313 log.error(e, "Could not create new instance of: %s", clazz);
314 }
315 }
316 }
317 }
318
319 ///////////////////////////////////////////////////////////////////////////
320 // Property getters.
321
322 public boolean getScan() {
323 return scan;
324 }
325
326 public boolean isRegisterMBeans() {
327 return MBeanContextName != null;
328 }
329
330 public String getMBeanContextName() {
331 return MBeanContextName;
332 }
333
334
335 public ObjectOutput newAMF3Serializer(OutputStream out) {
336 try {
337 return amf3SerializerConstructor.newInstance(new Object[]{out});
338 } catch (Exception e) {
339 throw new GraniteConfigException("Could not create serializer instance with: " + amf3SerializerConstructor, e);
340 }
341 }
342
343 public Constructor<?> getAmf3SerializerConstructor() {
344 return amf3SerializerConstructor;
345 }
346
347 public ObjectInput newAMF3Deserializer(InputStream in) {
348 try {
349 return amf3DeserializerConstructor.newInstance(new Object[]{in});
350 } catch (Exception e) {
351 throw new GraniteConfigException("Could not create deserializer instance with: " + amf3DeserializerConstructor, e);
352 }
353 }
354
355 public Constructor<?> getAmf3DeserializerConstructor() {
356 return amf3DeserializerConstructor;
357 }
358
359 public AMF3DeserializerSecurizer getAmf3DeserializerSecurizer() {
360 return amf3DeserializerSecurizer;
361 }
362 public void setAmf3DeserializerSecurizer(
363 AMF3DeserializerSecurizer amf3DeserializerSecurizer) {
364 this.amf3DeserializerSecurizer = amf3DeserializerSecurizer;
365 }
366
367 public AMF3MessageInterceptor getAmf3MessageInterceptor() {
368 return amf3MessageInterceptor;
369 }
370 public void setAmf3MessageInterceptor(AMF3MessageInterceptor amf3MessageInterceptor) {
371 this.amf3MessageInterceptor = amf3MessageInterceptor;
372 }
373
374 public Map<String, String> getInstantiators() {
375 return instantiators;
376 }
377
378 public Converters getConverters() {
379 return converters;
380 }
381
382 public MethodMatcher getMethodMatcher() {
383 return methodMatcher;
384 }
385
386 public ServiceInvocationListener getInvocationListener() {
387 return invocationListener;
388 }
389
390 public String getInstantiator(String type) {
391 return instantiators.get(type);
392 }
393
394 public ClassGetter getClassGetter() {
395 return classGetter;
396 }
397
398 public XMap getExternalizersConfiguration() {
399 return externalizersConfiguration;
400 }
401
402 public void setExternalizersConfiguration(XMap externalizersConfiguration) {
403 this.externalizersConfiguration = externalizersConfiguration;
404 }
405
406 public Externalizer getExternalizer(String type) {
407 return getElementByType(
408 type,
409 EXTERNALIZER_FACTORY,
410 externalizersByType,
411 externalizersByInstanceOf,
412 externalizersByAnnotatedWith,
413 scannedExternalizers
414 );
415 }
416
417 public Map<String, Externalizer> getExternalizersByType() {
418 return externalizersByType;
419 }
420
421 public Map<String, String> getExternalizersByInstanceOf() {
422 return externalizersByInstanceOf;
423 }
424
425 public Map<String, String> getExternalizersByAnnotatedWith() {
426 return externalizersByAnnotatedWith;
427 }
428
429 public List<Externalizer> getScannedExternalizers() {
430 return scannedExternalizers;
431 }
432
433
434 public Class<? extends ActionScriptClassDescriptor> getActionScriptDescriptor(String type) {
435 return getElementByType(type, ASC_DESCRIPTOR_FACTORY, as3DescriptorsByType, as3DescriptorsByInstanceOf, null, null);
436 }
437
438 public Map<String, Class<? extends ActionScriptClassDescriptor>> getAs3DescriptorsByType() {
439 return as3DescriptorsByType;
440 }
441
442 public Map<String, String> getAs3DescriptorsByInstanceOf() {
443 return as3DescriptorsByInstanceOf;
444 }
445
446
447 public Class<? extends JavaClassDescriptor> getJavaDescriptor(String type) {
448 return getElementByType(type, JC_DESCRIPTOR_FACTORY, javaDescriptorsByType, javaDescriptorsByInstanceOf, null, null);
449 }
450
451 public Map<String, Class<? extends JavaClassDescriptor>> getJavaDescriptorsByType() {
452 return javaDescriptorsByType;
453 }
454
455 public Map<String, String> getJavaDescriptorsByInstanceOf() {
456 return javaDescriptorsByInstanceOf;
457 }
458
459
460 public boolean isComponentTideEnabled(String componentName, Set<Class<?>> componentClasses, Object instance) {
461 return TideComponentMatcherFactory.isComponentTideEnabled(enabledTideComponentsByName, tideComponentMatchers, componentName, componentClasses, instance);
462 }
463
464 public boolean isComponentTideDisabled(String componentName, Set<Class<?>> componentClasses, Object instance) {
465 return TideComponentMatcherFactory.isComponentTideDisabled(disabledTideComponentsByName, tideComponentMatchers, componentName, componentClasses, instance);
466 }
467
468
469 public List<ExceptionConverter> getExceptionConverters() {
470 return exceptionConverters;
471 }
472
473 public void registerExceptionConverter(Class<? extends ExceptionConverter> exceptionConverterClass) {
474 registerExceptionConverter(exceptionConverterClass, false);
475 }
476 public void registerExceptionConverter(Class<? extends ExceptionConverter> exceptionConverterClass, boolean first) {
477 for (ExceptionConverter ec : exceptionConverters) {
478 if (ec.getClass() == exceptionConverterClass)
479 return;
480 }
481 try {
482 ExceptionConverter exceptionConverter = ClassUtil.newInstance(exceptionConverterClass, ExceptionConverter.class);
483 if (first)
484 exceptionConverters.add(0, exceptionConverter);
485 else
486 exceptionConverters.add(exceptionConverter);
487 }
488 catch (Exception e) {
489 log.error(e, "Could not instantiate exception converter: %s", exceptionConverterClass);
490 }
491 }
492
493 public void registerExceptionConverter(ExceptionConverter exceptionConverter, boolean first) {
494 for (ExceptionConverter ec : exceptionConverters) {
495 if (ec.getClass() == exceptionConverter.getClass())
496 return;
497 }
498 if (first)
499 exceptionConverters.add(0, exceptionConverter);
500 else
501 exceptionConverters.add(exceptionConverter);
502 }
503
504 public boolean hasSecurityService() {
505 return securityService != null;
506 }
507
508 public SecurityService getSecurityService() {
509 return securityService;
510 }
511
512 public List<TideComponentMatcher> getTideComponentMatchers() {
513 return tideComponentMatchers;
514 }
515
516 public Map<String, Object[]> getEnabledTideComponentsByName() {
517 return enabledTideComponentsByName;
518 }
519
520 public Map<String, Object[]> getDisabledTideComponentsByName() {
521 return disabledTideComponentsByName;
522 }
523
524
525 public XMap getGravityConfig() {
526 return gravityConfig;
527 }
528
529 public Constructor<?> getMessageSelectorConstructor() {
530 return messageSelectorConstructor;
531 }
532 public Externalizer setExternalizersByType(String type, String externalizerType) {
533 return externalizersByType.put(type, EXTERNALIZER_FACTORY.getInstance(externalizerType, this));
534 }
535
536 public String putExternalizersByInstanceOf(String instanceOf, String externalizerType) {
537 return externalizersByInstanceOf.put(instanceOf, externalizerType);
538 }
539
540 public String putExternalizersByAnnotatedWith(String annotatedWith, String externalizerType) {
541 return externalizersByAnnotatedWith.put(annotatedWith, externalizerType);
542 }
543
544 ///////////////////////////////////////////////////////////////////////////
545 // Static GraniteConfig loading helpers.
546
547 private void forElement(XMap element, boolean custom, String graniteConfigProperties) {
548 String scan = element.get("@scan");
549
550 this.scan = Boolean.TRUE.toString().equals(scan);
551
552 loadCustomAMF3Serializer(element, custom);
553 loadCustomAMF3DeserializerSecurizer(element, custom);
554 loadCustomAMF3MessageInterceptor(element, custom);
555 loadCustomConverters(element, custom);
556 loadCustomMethodMatcher(element, custom);
557 loadCustomInvocationListener(element, custom);
558 loadCustomInstantiators(element, custom);
559 loadCustomClassGetter(element, custom);
560 loadCustomExternalizers(element, custom);
561 loadCustomDescriptors(element, custom);
562 loadCustomExceptionConverters(element, custom);
563 loadCustomTideComponents(element, custom);
564 loadCustomSecurity(element, custom);
565 loadCustomMessageSelector(element, custom);
566 loadCustomGravity(element, custom);
567
568 if (this.scan)
569 scanConfig(graniteConfigProperties);
570
571 finishCustomConverters(custom);
572 }
573
574 private void loadCustomAMF3Serializer(XMap element, boolean custom) {
575 XMap amf3Serializer = element.getOne("amf3-serializer");
576 if (amf3Serializer != null) {
577 String type = amf3Serializer.get("@type");
578 try {
579 Class<AMF3Serializer> amf3SerializerClass = ClassUtil.forName(type, AMF3Serializer.class);
580 amf3SerializerConstructor = ClassUtil.getConstructor(amf3SerializerClass, new Class<?>[]{OutputStream.class});
581 } catch (Exception e) {
582 throw new GraniteConfigException("Could not get constructor for AMF3 serializer: " + type, e);
583 }
584 }
585
586 XMap amf3Deserializer = element.getOne("amf3-deserializer");
587 if (amf3Deserializer != null) {
588 String type = amf3Deserializer.get("@type");
589 try {
590 Class<AMF3Deserializer> amf3DeserializerClass = ClassUtil.forName(type, AMF3Deserializer.class);
591 amf3DeserializerConstructor = ClassUtil.getConstructor(amf3DeserializerClass, new Class<?>[]{InputStream.class});
592 } catch (Exception e) {
593 throw new GraniteConfigException("Could not get constructor for AMF3 deserializer: " + type, e);
594 }
595 }
596 }
597
598 private void loadCustomAMF3DeserializerSecurizer(XMap element, boolean custom) {
599 XMap securizer = element.getOne("amf3-deserializer-securizer");
600 if (securizer != null) {
601 String type = securizer.get("@type");
602 try {
603 amf3DeserializerSecurizer = (AMF3DeserializerSecurizer)ClassUtil.newInstance(type);
604 } catch (Exception e) {
605 throw new GraniteConfigException("Could not construct amf3 deserializer securizer: " + type, e);
606 }
607 String param = securizer.get("@param");
608 try {
609 amf3DeserializerSecurizer.setParam(param);
610 } catch (Exception e) {
611 throw new GraniteConfigException("Could not set param of amf3 deserializer securizer: " + type + ", param: " + param, e);
612 }
613 }
614 }
615
616 private void loadCustomAMF3MessageInterceptor(XMap element, boolean custom) {
617 XMap interceptor = element.getOne("amf3-message-interceptor");
618 if (interceptor != null) {
619 String type = interceptor.get("@type");
620 try {
621 amf3MessageInterceptor = (AMF3MessageInterceptor)ClassUtil.newInstance(type);
622 } catch (Exception e) {
623 throw new GraniteConfigException("Could not construct amf3 message interceptor: " + type, e);
624 }
625 }
626 }
627
628 private void loadCustomConverters(XMap element, boolean custom) {
629 XMap converters = element.getOne("converters");
630 if (converters != null) {
631 // Should we override standard config converters?
632 String override = converters.get("@override");
633 if (Boolean.TRUE.toString().equals(override))
634 converterClasses.clear();
635
636 int i = 0;
637 for (XMap converter : converters.getAll("converter")) {
638 String type = converter.get("@type");
639 try {
640 // For custom config, shifts any standard converters to the end of the list...
641 converterClasses.add(i++, ClassUtil.forName(type, Converter.class));
642 } catch (Exception e) {
643 throw new GraniteConfigException("Could not get converter class for: " + type, e);
644 }
645 }
646 }
647 }
648
649 private void finishCustomConverters(boolean custom) {
650 try {
651 converters = new Converters(converterClasses);
652 } catch (Exception e) {
653 throw new GraniteConfigException("Could not construct new Converters instance", e);
654 }
655
656 // Cleanup...
657 if (custom)
658 converterClasses = null;
659 }
660
661 private void loadCustomMethodMatcher(XMap element, boolean custom) {
662 XMap methodMatcher = element.getOne("method-matcher");
663 if (methodMatcher != null) {
664 String type = methodMatcher.get("@type");
665 try {
666 this.methodMatcher = (MethodMatcher)ClassUtil.newInstance(type);
667 } catch (Exception e) {
668 throw new GraniteConfigException("Could not construct method matcher: " + type, e);
669 }
670 }
671 }
672
673 private void loadCustomInvocationListener(XMap element, boolean custom) {
674 XMap invocationListener = element.getOne("invocation-listener");
675 if (invocationListener != null) {
676 String type = invocationListener.get("@type");
677 try {
678 this.invocationListener = (ServiceInvocationListener)ClassUtil.newInstance(type);
679 } catch (Exception e) {
680 throw new GraniteConfigException("Could not instantiate ServiceInvocationListener: " + type, e);
681 }
682 }
683 }
684
685 private void loadCustomInstantiators(XMap element, boolean custom) {
686 XMap instantiators = element.getOne("instantiators");
687 if (instantiators != null) {
688 for (XMap instantiator : instantiators.getAll("instantiator"))
689 this.instantiators.put(instantiator.get("@type"), instantiator.get("."));
690 }
691 }
692
693 private void loadCustomClassGetter(XMap element, boolean custom) {
694 XMap classGetter = element.getOne("class-getter");
695 if (classGetter != null) {
696 String type = classGetter.get("@type");
697 try {
698 this.classGetter = (ClassGetter)ClassUtil.newInstance(type);
699 classGetterSet = true;
700 } catch (Exception e) {
701 throw new GraniteConfigException("Could not instantiate ClassGetter: " + type, e);
702 }
703 }
704 }
705
706 private void loadCustomExternalizers(XMap element, boolean custom) {
707 externalizersConfiguration = element.getOne("externalizers/configuration");
708
709 for (XMap externalizer : element.getAll("externalizers/externalizer")) {
710 String externalizerType = externalizer.get("@type");
711
712 for (XMap include : externalizer.getAll("include")) {
713 String type = include.get("@type");
714 if (type != null)
715 externalizersByType.put(type, EXTERNALIZER_FACTORY.getInstance(externalizerType, this));
716 else {
717 String instanceOf = include.get("@instance-of");
718 if (instanceOf != null)
719 externalizersByInstanceOf.put(instanceOf, externalizerType);
720 else {
721 String annotatedWith = include.get("@annotated-with");
722 if (annotatedWith == null)
723 throw new GraniteConfigException(
724 "Element 'include' has no attribute 'type', 'instance-of' or 'annotated-with'");
725 externalizersByAnnotatedWith.put(annotatedWith, externalizerType);
726 }
727 }
728 }
729 }
730 }
731
732 /**
733 * Read custom class descriptors.
734 * Descriptor must have 'type' or 'instanceof' attribute
735 * and one of 'java' or 'as3' attributes specified.
736 */
737 private void loadCustomDescriptors(XMap element, boolean custom) {
738 for (XMap descriptor : element.getAll("descriptors/descriptor")) {
739 String type = descriptor.get("@type");
740 if (type != null) {
741 String java = descriptor.get("@java");
742 String as3 = descriptor.get("@as3");
743 if (java == null && as3 == null)
744 throw new GraniteConfigException(
745 "Element 'descriptor' has no attributes 'java' or 'as3'\n" + descriptor
746 );
747 if (java != null)
748 javaDescriptorsByType.put(type, JC_DESCRIPTOR_FACTORY.getInstance(java, this));
749 if (as3 != null)
750 as3DescriptorsByType.put(type, ASC_DESCRIPTOR_FACTORY.getInstance(as3, this));
751 } else {
752 String instanceOf = descriptor.get("@instance-of");
753 if (instanceOf == null)
754 throw new GraniteConfigException(
755 "Element 'descriptor' has no attribute 'type' or 'instance-of'\n" + descriptor
756 );
757 String java = descriptor.get("@java");
758 String as3 = descriptor.get("@as3");
759 if (java == null && as3 == null) {
760 throw new GraniteConfigException(
761 "Element 'descriptor' has no attributes 'java' or 'as3' in:\n" + descriptor
762 );
763 }
764 if (java != null)
765 javaDescriptorsByInstanceOf.put(instanceOf, java);
766 if (as3 != null)
767 as3DescriptorsByInstanceOf.put(instanceOf, as3);
768 }
769 }
770 }
771
772 /**
773 * Read custom class exception converters
774 * Converter must have 'type' attribute
775 */
776 private void loadCustomExceptionConverters(XMap element, boolean custom) {
777 for (XMap exceptionConverter : element.getAll("exception-converters/exception-converter")) {
778 String type = exceptionConverter.get("@type");
779 ExceptionConverter converter = null;
780 try {
781 converter = (ExceptionConverter)ClassUtil.newInstance(type);
782 exceptionConverters.add(converter);
783 } catch (Exception e) {
784 throw new GraniteConfigException("Could not construct exception converter: " + type, e);
785 }
786 }
787 }
788
789 private void loadCustomTideComponents(XMap element, boolean custom) {
790 for (XMap component : element.getAll("tide-components/tide-component")) {
791 boolean disabled = Boolean.TRUE.toString().equals(component.get("@disabled"));
792 String type = component.get("@type");
793 if (type != null)
794 tideComponentMatchers.add(TIDE_COMPONENT_MATCHER_FACTORY.getTypeMatcher(type, disabled));
795 else {
796 String name = component.get("@name");
797 if (name != null)
798 tideComponentMatchers.add(TIDE_COMPONENT_MATCHER_FACTORY.getNameMatcher(name, disabled));
799 else {
800 String instanceOf = component.get("@instance-of");
801 if (instanceOf != null)
802 tideComponentMatchers.add(TIDE_COMPONENT_MATCHER_FACTORY.getInstanceOfMatcher(instanceOf, disabled));
803 else {
804 String annotatedWith = component.get("@annotated-with");
805 if (annotatedWith == null)
806 throw new GraniteConfigException(
807 "Element 'component' has no attribute 'type', 'name', 'instance-of' or 'annotated-with'");
808 tideComponentMatchers.add(TIDE_COMPONENT_MATCHER_FACTORY.getAnnotatedWithMatcher(annotatedWith, disabled));
809 }
810 }
811 }
812 }
813 }
814
815 private void loadCustomSecurity(XMap element, boolean custom) {
816 XMap security = element.getOne("security");
817 if (security != null) {
818 String type = security.get("@type");
819 try {
820 securityService = (SecurityService)ClassUtil.newInstance(type);
821 } catch (Exception e) {
822 throw new GraniteConfigException("Could not instantiate SecurityService: " + type, e);
823 }
824
825 Map<String, String> params = new HashMap<String, String>();
826 for (XMap param : security.getAll("param")) {
827 String name = param.get("@name");
828 String value = param.get("@value");
829 params.put(name, value);
830 }
831 try {
832 securityService.configure(params);
833 } catch (Exception e) {
834 throw new GraniteConfigException("Could not configure SecurityService " + type + " with: " + params, e);
835 }
836 }
837 }
838
839 public void setSecurityService(SecurityService securityService) {
840 this.securityService = securityService;
841 }
842
843 private void loadCustomMessageSelector(XMap element, boolean custom) {
844 XMap selector = element.getOne("message-selector");
845 if (selector != null) {
846 String type = selector.get("@type");
847 try {
848 messageSelectorConstructor = ClassUtil.getConstructor(type, new Class<?>[]{ String.class });
849 } catch (Exception e) {
850 throw new GraniteConfigException("Could not construct message selector: " + type, e);
851 }
852 }
853 }
854
855 private void loadCustomGravity(XMap element, boolean custom) {
856 gravityConfig = element.getOne("gravity");
857 }
858
859 ///////////////////////////////////////////////////////////////////////////
860 // Other helpers.
861
862 private <T> T getElementByType(
863 String type,
864 ConfigurableFactory<T> factory,
865 ConcurrentHashMap<String, T> elementsByType,
866 Map<String, String> elementsByInstanceOf,
867 Map<String, String> elementsByAnnotatedWith,
868 List<T> scannedConfigurables) {
869
870 // This NULL object is a Java null placeholder: ConcurrentHashMap doesn't allow
871 // null values...
872 final T NULL = factory.getNullInstance();
873
874 T element = elementsByType.get(type);
875 if (element != null)
876 return (NULL == element ? null : element);
877 element = NULL;
878
879 Class<?> typeClass = null;
880 try {
881 typeClass = ClassUtil.forName(type);
882 } catch (Exception e) {
883 throw new GraniteConfigException("Could not load class: " + type, e);
884 }
885
886 if (elementsByAnnotatedWith != null && NULL == element) {
887 for (Map.Entry<String, String> entry : elementsByAnnotatedWith.entrySet()) {
888 String annotation = entry.getKey();
889 try {
890 Class<Annotation> annotationClass = ClassUtil.forName(annotation, Annotation.class);
891 if (typeClass.isAnnotationPresent(annotationClass)) {
892 element = factory.getInstance(entry.getValue(), this);
893 break;
894 }
895 } catch (Exception e) {
896 throw new GraniteConfigException("Could not load class: " + annotation, e);
897 }
898 }
899 }
900
901 if (elementsByInstanceOf != null && NULL == element) {
902 for (Map.Entry<String, String> entry : elementsByInstanceOf.entrySet()) {
903 String instanceOf = entry.getKey();
904 try {
905 Class<?> instanceOfClass = ClassUtil.forName(instanceOf);
906 if (instanceOfClass.isAssignableFrom(typeClass)) {
907 element = factory.getInstance(entry.getValue(), this);
908 break;
909 }
910 } catch (Exception e) {
911 throw new GraniteConfigException("Could not load class: " + instanceOf, e);
912 }
913 }
914 }
915
916 if (NULL == element)
917 element = factory.getInstanceForBean(scannedConfigurables, typeClass, this);
918
919 T previous = elementsByType.putIfAbsent(type, element);
920 if (previous != null)
921 element = previous;
922
923 return (NULL == element ? null : element);
924 }
925 }