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