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