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