001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2020 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 014 * implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.microbean.settings; 018 019import java.beans.IntrospectionException; 020 021import java.lang.annotation.Annotation; 022 023import java.lang.reflect.Executable; 024import java.lang.reflect.GenericArrayType; 025import java.lang.reflect.Member; 026import java.lang.reflect.Parameter; 027import java.lang.reflect.ParameterizedType; 028import java.lang.reflect.Type; 029import java.lang.reflect.TypeVariable; 030import java.lang.reflect.WildcardType; 031 032import java.util.ArrayList; 033import java.util.Collection; 034import java.util.Collections; 035import java.util.HashMap; 036import java.util.HashSet; 037import java.util.Map; 038import java.util.Map.Entry; 039import java.util.Objects; 040import java.util.Set; 041 042import java.util.function.BiFunction; 043import java.util.function.Supplier; 044 045import javax.enterprise.context.Dependent; 046 047import javax.enterprise.context.spi.CreationalContext; 048 049import javax.enterprise.event.Observes; 050 051import javax.enterprise.inject.Any; 052import javax.enterprise.inject.CreationException; 053import javax.enterprise.inject.Default; 054import javax.enterprise.inject.UnsatisfiedResolutionException; 055 056import javax.enterprise.inject.spi.AfterBeanDiscovery; 057import javax.enterprise.inject.spi.AfterDeploymentValidation; 058import javax.enterprise.inject.spi.Annotated; 059import javax.enterprise.inject.spi.AnnotatedField; 060import javax.enterprise.inject.spi.AnnotatedMethod; 061import javax.enterprise.inject.spi.AnnotatedParameter; 062import javax.enterprise.inject.spi.AnnotatedType; 063import javax.enterprise.inject.spi.Bean; 064import javax.enterprise.inject.spi.BeanAttributes; 065import javax.enterprise.inject.spi.BeanManager; 066import javax.enterprise.inject.spi.DefinitionException; 067import javax.enterprise.inject.spi.Extension; 068import javax.enterprise.inject.spi.InjectionPoint; 069import javax.enterprise.inject.spi.ProcessInjectionPoint; 070import javax.enterprise.inject.spi.Producer; 071import javax.enterprise.inject.spi.ProducerFactory; 072 073import javax.enterprise.util.TypeLiteral; 074 075import javax.inject.Singleton; 076 077/** 078 * An {@link Extension} providing support for the {@link Setting} and 079 * {@link Configured} annotations, as well as injection of {@link 080 * Settings} instances. 081 * 082 * @author <a href="https://about.me/lairdnelson" 083 * target="_parent">Laird Nelson</a> 084 */ 085public class SettingsExtension implements Extension { 086 087 088 /* 089 * Instance fields. 090 */ 091 092 093 private final Map<Set<Annotation>, Set<Type>> configuredTypes; 094 095 private final Set<InjectionPoint> settingInjectionPoints; 096 097 private final Set<Set<Annotation>> settingQualifierSets; 098 099 private final Set<Set<Annotation>> settingsQualifierSets; 100 101 private final Set<Type> knownConversionTypes; 102 103 104 /* 105 * Constructors. 106 */ 107 108 109 /** 110 * Creates a new {@link SettingsExtension}. 111 */ 112 public SettingsExtension() { 113 super(); 114 this.configuredTypes = new HashMap<>(); 115 this.settingInjectionPoints = new HashSet<>(); 116 this.settingQualifierSets = new HashSet<>(); 117 this.settingsQualifierSets = new HashSet<>(); 118 this.knownConversionTypes = new HashSet<>(Collections.singleton(String.class)); 119 } 120 121 122 /* 123 * Observer methods. 124 */ 125 126 127 private final <T, X extends Settings> void processSettingsInjectionPoint(@Observes final ProcessInjectionPoint<T, X> event) { 128 final Set<Annotation> qualifiers = new HashSet<>(event.getInjectionPoint().getQualifiers()); 129 qualifiers.add(Any.Literal.INSTANCE); 130 this.settingsQualifierSets.add(qualifiers); 131 } 132 133 private final <T, X> void processInjectionPoint(@Observes final ProcessInjectionPoint<T, X> event, 134 final BeanManager beanManager) { 135 final InjectionPoint injectionPoint = event.getInjectionPoint(); 136 try { 137 if (!this.processSettingInjectionPoint(injectionPoint)) { 138 this.processNonSettingInjectionPoint(injectionPoint, beanManager); 139 } 140 } catch (final Exception definitionException) { 141 event.addDefinitionError(definitionException); 142 } 143 } 144 145 private final boolean processSettingInjectionPoint(final InjectionPoint injectionPoint) { 146 final boolean returnValue; 147 final Type type = injectionPoint.getType(); 148 if (Settings.class.equals(type)) { 149 returnValue = false; 150 } else { 151 final Set<Annotation> injectionPointQualifiers = injectionPoint.getQualifiers(); 152 boolean containsSetting = false; 153 for (final Annotation injectionPointQualifier : injectionPointQualifiers) { 154 if (injectionPointQualifier instanceof Setting) { 155 final Setting setting = (Setting)injectionPointQualifier; 156 if (setting.required()) { 157 final Object defaultValue = setting.defaultValue(); 158 if (defaultValue != null && !defaultValue.equals(Setting.UNSET)) { 159 throw new DefinitionException("While processing the injection point " + injectionPoint + 160 " the Setting annotation named " + setting.name() + 161 " had a defaultValue element specified (" + defaultValue + 162 ") and returned true from its required() element."); 163 } 164 } 165 containsSetting = true; 166 break; 167 } 168 } 169 if (containsSetting) { 170 this.knownConversionTypes.add(type); 171 172 this.settingInjectionPoints.add(injectionPoint); 173 174 final Set<Annotation> settingQualifiers = new HashSet<>(injectionPointQualifiers); 175 settingQualifiers.add(Any.Literal.INSTANCE); 176 this.settingQualifierSets.add(settingQualifiers); 177 178 final Set<Annotation> settingsQualifiers = new HashSet<>(injectionPointQualifiers); 179 settingsQualifiers.removeIf(e -> e instanceof Setting); 180 if (settingsQualifiers.isEmpty()) { 181 settingsQualifiers.add(Default.Literal.INSTANCE); 182 } 183 settingsQualifiers.add(Any.Literal.INSTANCE); 184 this.settingsQualifierSets.add(settingsQualifiers); 185 returnValue = true; 186 } else { 187 returnValue = false; 188 } 189 } 190 return returnValue; 191 } 192 193 private final void processNonSettingInjectionPoint(final InjectionPoint injectionPoint, 194 final BeanManager beanManager) { 195 Objects.requireNonNull(injectionPoint); 196 Objects.requireNonNull(beanManager); 197 final Set<Annotation> qualifiers = injectionPoint.getQualifiers(); 198 if (qualifiers != null && !qualifiers.isEmpty()) { 199 for (final Annotation qualifier : qualifiers) { 200 if (qualifier instanceof Configured) { 201 Set<Type> types = this.configuredTypes.get(qualifiers); 202 if (types == null) { 203 types = new HashSet<>(); 204 this.configuredTypes.put(qualifiers, types); 205 } 206 types.add(injectionPoint.getType()); 207 break; 208 } 209 } 210 } 211 } 212 213 private final void installConverterProviderBeans(@Observes final AfterBeanDiscovery event, 214 final BeanManager beanManager) { 215 final Type type = ConverterProvider.class; 216 addBean(event, 217 beanManager, 218 type, 219 this.settingsQualifierSets, 220 (e, bm, t, nq) -> e.addBean() 221 .addTransitiveTypeClosure(BeanManagerBackedConverterProvider.class) 222 .scope(Singleton.class) 223 .qualifiers(nq) 224 .beanClass(BeanManagerBackedConverterProvider.class) 225 .createWith(cc -> new BeanManagerBackedConverterProvider(bm, nq))); 226 } 227 228 private final void installSourcesSupplierBeans(@Observes final AfterBeanDiscovery event, 229 final BeanManager beanManager) { 230 final Type type = new TypeLiteral<BiFunction<String, Set<Annotation>, Set<Source>>>() { 231 private static final long serialVersionUID = 1L; 232 }.getType(); 233 addBean(event, 234 beanManager, 235 type, 236 this.settingsQualifierSets, 237 (e, bm, t, nq) -> e.addBean() 238 .types(t) 239 .scope(Singleton.class) 240 .qualifiers(nq) 241 .beanClass(BeanManagerBackedSourcesSupplier.class) 242 .createWith(cc -> new BeanManagerBackedSourcesSupplier(bm))); 243 } 244 245 private final void installSettingsBeans(@Observes final AfterBeanDiscovery event, 246 final BeanManager beanManager) { 247 addBean(event, 248 beanManager, 249 Settings.class, 250 this.settingsQualifierSets, 251 (e, bm, t, nq) -> e.addBean() 252 .types(t) 253 .scope(Singleton.class) 254 .qualifiers(nq) 255 .beanClass(Settings.class) 256 .produceWith(instance -> { 257 final Annotation[] qualifiersArray = nq.toArray(new Annotation[nq.size()]); 258 final BiFunction<? super String, ? super Set<Annotation>, ? extends Set<? extends Source>> sourcesSupplier = 259 instance.select(new TypeLiteral<BiFunction<? super String, 260 ? super Set<Annotation>, 261 ? extends Set<? extends Source>>>() { 262 private static final long serialVersionUID = 1L; 263 }, 264 qualifiersArray).get(); 265 final ConverterProvider converterProvider = instance.select(ConverterProvider.class, qualifiersArray).get(); 266 final Iterable<? extends Arbiter> arbiters = instance.select(Arbiter.class, qualifiersArray); 267 return new Settings(nq, sourcesSupplier, converterProvider, arbiters); 268 })); 269 } 270 271 private final void installSettingProducers(@Observes final AfterBeanDiscovery event, 272 final BeanManager beanManager) { 273 if (!this.settingQualifierSets.isEmpty()) { 274 final AnnotatedType<SettingsExtension> annotatedType = beanManager.createAnnotatedType(SettingsExtension.class); 275 final AnnotatedMethod<? super SettingsExtension> producerMethodTemplate = annotatedType.getMethods() 276 .stream() 277 .filter(m -> m.getJavaMember().getName().equals("producerMethodTemplate")) 278 .findFirst() // ...and only 279 .get(); 280 final BeanAttributes<?> delegate = beanManager.createBeanAttributes(producerMethodTemplate); 281 for (final Set<Annotation> settingQualifiers : this.settingQualifierSets) { 282 final Set<Annotation> settingsQualifiers = new HashSet<>(settingQualifiers); 283 settingsQualifiers.removeIf(e -> e instanceof Setting); 284 if (settingsQualifiers.isEmpty()) { 285 settingsQualifiers.add(Default.Literal.INSTANCE); 286 } 287 for (final Type type : this.knownConversionTypes) { 288 if (noBeans(beanManager, type, settingQualifiers)) { 289 // type is the type of, say, an injection point. So it 290 // could be a wildcard or a type variable. Or it could be 291 // a ParameterizedType with recursive wildcards or type 292 // variables. 293 final BeanAttributes<?> beanAttributes = 294 new FlexiblyTypedBeanAttributes<Object>(delegate, settingQualifiers, Collections.singleton(synthesizeLegalBeanType(type, 1))); 295 final ProducerFactory<SettingsExtension> defaultProducerFactory = 296 beanManager.getProducerFactory(producerMethodTemplate, null); 297 final ProducerFactory<SettingsExtension> producerFactory = new ProducerFactory<SettingsExtension>() { 298 @Override 299 public final <T> Producer<T> createProducer(final Bean<T> bean) { 300 final Producer<T> defaultProducer = defaultProducerFactory.createProducer(bean); 301 final Set<InjectionPoint> injectionPoints = 302 qualifyInjectionPoints(defaultProducer.getInjectionPoints(), settingsQualifiers); 303 return new DelegatingProducer<T>(defaultProducer) { 304 @Override 305 public final Set<InjectionPoint> getInjectionPoints() { 306 return injectionPoints; 307 } 308 }; 309 } 310 }; 311 final Bean<?> bean = beanManager.createBean(beanAttributes, SettingsExtension.class, producerFactory); 312 event.addBean(bean); 313 } 314 } 315 } 316 } 317 } 318 319 private final <T> void installConfiguredBeans(@Observes final AfterBeanDiscovery event, 320 final BeanManager beanManager) { 321 final Set<Entry<Set<Annotation>, Set<Type>>> entrySet = this.configuredTypes.entrySet(); 322 for (final Entry<Set<Annotation>, Set<Type>> entry : entrySet) { 323 final Set<Annotation> qualifiers = entry.getKey(); 324 assert qualifiers != null; 325 assert qualifiers.contains(Configured.Literal.INSTANCE); 326 final Set<Type> types = entry.getValue(); 327 assert types != null; 328 for (final Type type : types) { 329 if (noBeans(beanManager, type, qualifiers)) { 330 final Set<Annotation> newQualifiers = new HashSet<>(qualifiers); 331 newQualifiers.removeIf(e -> e instanceof Configured); 332 if (newQualifiers.isEmpty()) { 333 newQualifiers.add(Default.Literal.INSTANCE); 334 } 335 final Annotation[] qualifiersArray = newQualifiers.toArray(new Annotation[newQualifiers.size()]); 336 final Set<Bean<?>> nonConfiguredBeans = beanManager.getBeans(type, qualifiersArray); 337 if (nonConfiguredBeans != null && !nonConfiguredBeans.isEmpty()) { 338 @SuppressWarnings("unchecked") 339 final Bean<T> bean = (Bean<T>)beanManager.resolve(nonConfiguredBeans); 340 assert bean.getTypes().contains(type); 341 event.<T>addBean() 342 .scope(bean.getScope()) 343 .types(type) 344 .beanClass(bean.getBeanClass()) 345 .qualifiers(qualifiers) 346 .createWith(cc -> { 347 Set<Bean<?>> settingsBeans = beanManager.getBeans(Settings.class, qualifiersArray); 348 if (settingsBeans == null || settingsBeans.isEmpty()) { 349 settingsBeans = beanManager.getBeans(Settings.class); 350 } 351 final Bean<?> settingsBean = beanManager.resolve(settingsBeans); 352 final Settings settings = (Settings)beanManager.getReference(settingsBean, Settings.class, cc); 353 354 final T contextualInstance = bean.create(cc); 355 try { 356 settings.configure(contextualInstance, qualifiers); 357 } catch (final IntrospectionException | ReflectiveOperationException exception) { 358 throw new CreationException(exception.getMessage(), exception); 359 } 360 return contextualInstance; 361 }) 362 .destroyWith((contextualInstance, cc) -> bean.destroy(contextualInstance, cc)); 363 } 364 } 365 } 366 } 367 } 368 369 private final void validate(@Observes final AfterDeploymentValidation event, 370 final BeanManager beanManager) { 371 final CreationalContext<?> cc = beanManager.createCreationalContext(null); 372 try { 373 for (final InjectionPoint settingInjectionPoint : this.settingInjectionPoints) { 374 beanManager.validate(settingInjectionPoint); 375 beanManager.getInjectableReference(settingInjectionPoint, cc); 376 } 377 } finally { 378 cc.release(); 379 } 380 this.settingInjectionPoints.clear(); 381 this.settingQualifierSets.clear(); 382 this.settingsQualifierSets.clear(); 383 this.configuredTypes.clear(); 384 } 385 386 387 /* 388 * Utility methods. 389 */ 390 391 392 private final void addBean(final AfterBeanDiscovery event, 393 final BeanManager beanManager, 394 final Type type, 395 final Set<Set<Annotation>> qualifiersSets, 396 final BeanAdder beanAdder) { 397 for (final Set<Annotation> qualifiers : qualifiersSets) { 398 if (noBeans(beanManager, type, qualifiers)) { 399 beanAdder.addBean(event, beanManager, type, qualifiers); 400 } 401 } 402 } 403 404 405 /* 406 * Static methods. 407 */ 408 409 410 /** 411 * A <strong>method template that must not be called 412 * directly</strong> used in building <a 413 * href="https://lairdnelson.wordpress.com/2017/02/11/dynamic-cdi-producer-methods/">dynamic 414 * producer methods</a>. 415 * 416 * @param injectionPoint the injection point for which a setting 417 * value is destined; must not be {@code null} 418 * 419 * @param settings an appropriate {@link Settings} to use to 420 * {@linkplain Settings#get(String, Set, Type, Supplier) acquire a 421 * value}; must not be {@code null} 422 * 423 * @return a value for a setting, or {@code null} 424 * 425 * @nullability This method may return {@code null}. 426 * 427 * @idempotency No guarantees with respect to idempotency are made 428 * of this method. 429 * 430 * @threadsafety This method is safe for concurrent use by mulitple 431 * threads. 432 * 433 * @see <a 434 * href="https://lairdnelson.wordpress.com/2017/02/11/dynamic-cdi-producer-methods/">Dynamic 435 * CDI Producer Methods</a> 436 * 437 * @deprecated This method should be called only by the CDI container. 438 */ 439 @Dependent 440 @Deprecated 441 private static final Object producerMethodTemplate(final InjectionPoint injectionPoint, 442 final BeanManager beanManager, 443 final Settings settings) { 444 Objects.requireNonNull(injectionPoint); 445 Objects.requireNonNull(settings); 446 final Set<Annotation> qualifiers = new HashSet<>(Objects.requireNonNull(injectionPoint.getQualifiers())); 447 qualifiers.removeIf(e -> e instanceof Setting); 448 if (qualifiers.isEmpty()) { 449 qualifiers.add(Default.Literal.INSTANCE); 450 } 451 return settings.get(getName(injectionPoint), 452 qualifiers, 453 injectionPoint.getType(), 454 getDefaultValueFunction(injectionPoint, beanManager)); 455 } 456 457 private static final Setting extractSetting(final InjectionPoint injectionPoint) { 458 Setting returnValue = null; 459 if (injectionPoint != null) { 460 returnValue = extractSetting(injectionPoint.getQualifiers()); 461 } 462 return returnValue; 463 } 464 465 private static final Setting extractSetting(final Set<Annotation> qualifiers) { 466 Setting returnValue = null; 467 if (qualifiers != null && !qualifiers.isEmpty()) { 468 for (final Annotation qualifier : qualifiers) { 469 if (qualifier instanceof Setting) { 470 returnValue = (Setting)qualifier; 471 break; 472 } 473 } 474 } 475 return returnValue; 476 } 477 478 private static final BiFunction<? super String, 479 ? super Set<? extends Annotation>, 480 ? extends String> getDefaultValueFunction(final InjectionPoint injectionPoint, 481 final BeanManager beanManager) { 482 Objects.requireNonNull(injectionPoint); 483 Objects.requireNonNull(beanManager); 484 final Setting setting = Objects.requireNonNull(extractSetting(injectionPoint)); 485 final BiFunction<? super String, ? super Set<? extends Annotation>, ? extends String> returnValue; 486 if (setting.required()) { 487 returnValue = (n, qs) -> { 488 final Set<Annotation> qualifiers = new HashSet<>(Objects.requireNonNull(injectionPoint.getQualifiers())); 489 qualifiers.removeIf(e -> e instanceof Setting); 490 if (qualifiers.isEmpty()) { 491 qualifiers.add(Default.Literal.INSTANCE); 492 } 493 throw new UnsatisfiedResolutionException("No value was found in any source for the setting named " + getName(injectionPoint) + " with qualifiers " + qualifiers); 494 }; 495 } else { 496 final String defaultValue = setting.defaultValue(); 497 if (defaultValue == null || defaultValue.equals(Setting.UNSET)) { 498 returnValue = SettingsExtension::returnNull; 499 } else { 500 returnValue = (n, qs) -> setting.defaultValue(); 501 } 502 } 503 return returnValue; 504 } 505 506 private static final String getName(final InjectionPoint injectionPoint) { 507 return getName(extractSetting(injectionPoint), injectionPoint.getAnnotated()); 508 } 509 510 private static final String getName(final Setting setting, final Annotated annotated) { 511 Objects.requireNonNull(setting); 512 Objects.requireNonNull(annotated); 513 String name = setting.name(); 514 if (name == null || name.isEmpty() || name.equals(Setting.UNSET)) { 515 if (annotated instanceof AnnotatedField) { 516 name = ((AnnotatedField)annotated).getJavaMember().getName(); 517 } else if (annotated instanceof AnnotatedParameter) { 518 final AnnotatedParameter<?> annotatedParameter = (AnnotatedParameter<?>)annotated; 519 final int parameterIndex = annotatedParameter.getPosition(); 520 final Member member = annotatedParameter.getDeclaringCallable().getJavaMember(); 521 final Parameter[] parameters = ((Executable)member).getParameters(); 522 final Parameter parameter = parameters[parameterIndex]; 523 if (parameter.isNamePresent()) { 524 name = parameter.getName(); 525 } else { 526 throw new IllegalStateException("The parameter at index " + 527 parameterIndex + 528 " in " + 529 member + 530 " did not have a name available via reflection. " + 531 "Make sure you compiled its enclosing class, " + 532 member.getDeclaringClass().getName() + 533 ", with the -parameters option supplied to javac, " + 534 " or make use of the name() element of the " + 535 Setting.class.getName() + 536 " annotation."); 537 } 538 } 539 } 540 return name; 541 } 542 543 private static final String returnNull(final String name, 544 final Set<? extends Annotation> qualifiers) { 545 return null; 546 } 547 548 private static final Set<InjectionPoint> qualifyInjectionPoints(final Set<InjectionPoint> injectionPoints, 549 final Set<Annotation> qualifiers) { 550 final Set<InjectionPoint> returnValue = new HashSet<>(); 551 for (final InjectionPoint injectionPoint : injectionPoints) { 552 final Type injectionPointType = injectionPoint.getType(); 553 if (InjectionPoint.class.equals(injectionPointType) || BeanManager.class.equals(injectionPointType)) { 554 returnValue.add(injectionPoint); 555 } else { 556 returnValue.add(qualifyInjectionPoint(injectionPoint, qualifiers)); 557 } 558 } 559 return returnValue; 560 } 561 562 private static final InjectionPoint qualifyInjectionPoint(final InjectionPoint injectionPoint, 563 final Set<Annotation> qualifiers) { 564 final InjectionPoint returnValue; 565 final Set<Annotation> originalQualifiers = injectionPoint.getQualifiers(); 566 if (originalQualifiers == null || originalQualifiers.isEmpty()) { 567 if (qualifiers == null || qualifiers.isEmpty()) { 568 returnValue = injectionPoint; 569 } else { 570 returnValue = new FlexiblyQualifiedInjectionPoint(injectionPoint, qualifiers); 571 } 572 } else if (qualifiers == null || qualifiers.isEmpty() || originalQualifiers.equals(qualifiers)) { 573 returnValue = injectionPoint; // just leave it alone 574 } else { 575 final Set<Annotation> newQualifiers = new HashSet<>(qualifiers); 576 newQualifiers.addAll(originalQualifiers); 577 returnValue = new FlexiblyQualifiedInjectionPoint(injectionPoint, newQualifiers); 578 } 579 return returnValue; 580 } 581 582 private static final boolean noBeans(final BeanManager beanManager, final Type type, final Set<Annotation> qualifiers) { 583 Objects.requireNonNull(beanManager); 584 Objects.requireNonNull(type); 585 final Collection<?> beans; 586 if (qualifiers == null || qualifiers.isEmpty()) { 587 beans = beanManager.getBeans(type); 588 } else { 589 beans = beanManager.getBeans(type, qualifiers.toArray(new Annotation[qualifiers.size()])); 590 } 591 return beans == null || beans.isEmpty(); 592 } 593 594 private static final Type synthesizeLegalBeanType(final Type type) { 595 return synthesizeLegalBeanType(type, -1, 0); 596 } 597 598 static final Type synthesizeLegalBeanType(final Type type, final int depth) { 599 return synthesizeLegalBeanType(type, Math.max(0, depth), 0); 600 } 601 602 private static final Type synthesizeLegalBeanType(final Type type, final int depth, final int currentLevel) { 603 final Type returnValue; 604 if (type instanceof Class || depth == 0 || (depth > 0 && currentLevel > depth)) { 605 returnValue = type; 606 } else if (type instanceof ParameterizedType) { 607 final ParameterizedType ptype = (ParameterizedType)type; 608 final Type rawType = ptype.getRawType(); 609 assert rawType instanceof Class; 610 final Type[] actualTypeArguments = ptype.getActualTypeArguments(); 611 assert actualTypeArguments != null; 612 assert actualTypeArguments.length > 0; 613 final Collection<Type> newTypeArguments = new ArrayList<>(); 614 for (final Type actualTypeArgument : actualTypeArguments) { 615 newTypeArguments.add(synthesizeLegalBeanType(actualTypeArgument, depth, currentLevel + 1)); // XXX recursive 616 } 617 returnValue = new ParameterizedTypeImplementation(ptype.getOwnerType(), 618 (Class<?>)rawType, 619 newTypeArguments.toArray(new Type[newTypeArguments.size()])); 620 } else if (type instanceof WildcardType) { 621 final WildcardType wtype = (WildcardType)type; 622 final Type[] upperBounds = wtype.getUpperBounds(); 623 assert upperBounds != null; 624 assert upperBounds.length > 0; 625 final Type[] lowerBounds = wtype.getLowerBounds(); 626 assert lowerBounds != null; 627 if (lowerBounds.length == 0) { 628 // Upper-bounded wildcard, e.g. ? extends Something 629 if (upperBounds.length == 1) { 630 // Turn ? extends Something into Something 631 returnValue = synthesizeLegalBeanType(upperBounds[0], depth, currentLevel + 1); // XXX recursive 632 } else { 633 // Too complicated/unsupported; just let it fly and CDI will 634 // fail later 635 returnValue = type; 636 } 637 } else { 638 // Lower-bounded wildcard, e.g. ? super Something 639 assert upperBounds.length == 1; 640 assert Object.class.equals(upperBounds[0]); 641 if (lowerBounds.length == 1) { 642 // Turn ? super Something into Something 643 returnValue = synthesizeLegalBeanType(lowerBounds[0], depth, currentLevel + 1); // XXX recursive 644 } else { 645 // Too complicated/unsupported; just let it fly and CDI will 646 // fail later 647 returnValue = type; 648 } 649 } 650 } else if (type instanceof TypeVariable) { 651 final TypeVariable<?> tv = (TypeVariable<?>)type; 652 final Type[] bounds = tv.getBounds(); 653 assert bounds != null; 654 assert bounds.length > 0; 655 if (bounds.length == 1) { 656 returnValue = synthesizeLegalBeanType(bounds[0], depth, currentLevel + 1); // XXX recursive 657 } else { 658 // Too complicated/unsupported; just let it fly and CDI will 659 // fail later 660 returnValue = type; 661 } 662 } else if (type instanceof GenericArrayType) { 663 returnValue = type; 664 } else { 665 throw new IllegalArgumentException("Unsupported Type implementation: " + type); 666 } 667 return returnValue; 668 } 669 670 671 /* 672 * Inner and nested classes. 673 */ 674 675 676 @FunctionalInterface 677 private interface BeanAdder { 678 679 void addBean(final AfterBeanDiscovery event, 680 final BeanManager beanManager, 681 final Type type, 682 final Set<Annotation> qualifiers); 683 684 } 685 686 private static class DelegatingBeanAttributes<T> implements BeanAttributes<T> { 687 688 private final BeanAttributes<?> delegate; 689 690 protected DelegatingBeanAttributes(final BeanAttributes<?> delegate) { 691 super(); 692 this.delegate = Objects.requireNonNull(delegate); 693 } 694 695 @Override 696 public String getName() { 697 return this.delegate.getName(); 698 } 699 700 @Override 701 public Set<Annotation> getQualifiers() { 702 return this.delegate.getQualifiers(); 703 } 704 705 @Override 706 public Class<? extends Annotation> getScope() { 707 return this.delegate.getScope(); 708 } 709 710 @Override 711 public Set<Class<? extends Annotation>> getStereotypes() { 712 return this.delegate.getStereotypes(); 713 } 714 715 @Override 716 public Set<Type> getTypes() { 717 return this.delegate.getTypes(); 718 } 719 720 @Override 721 public boolean isAlternative() { 722 return this.delegate.isAlternative(); 723 } 724 725 @Override 726 public String toString() { 727 return this.delegate.toString(); 728 } 729 730 } 731 732 private static class FlexiblyTypedBeanAttributes<T> extends DelegatingBeanAttributes<T> { 733 734 private final Set<Annotation> qualifiers; 735 736 private final Set<Type> types; 737 738 private FlexiblyTypedBeanAttributes(final BeanAttributes<?> delegate, 739 final Set<Annotation> qualifiers, 740 final Set<Type> types) { 741 super(delegate); 742 if (qualifiers == null) { 743 this.qualifiers = null; 744 } else if (qualifiers.isEmpty()) { 745 this.qualifiers = Collections.emptySet(); 746 } else { 747 this.qualifiers = Collections.unmodifiableSet(qualifiers); 748 } 749 if (types == null) { 750 this.types = null; 751 } else if (types.isEmpty()) { 752 this.types = Collections.emptySet(); 753 } else { 754 this.types = Collections.unmodifiableSet(types); 755 } 756 } 757 758 @Override 759 public Set<Annotation> getQualifiers() { 760 return this.qualifiers; 761 } 762 763 @Override 764 public Set<Type> getTypes() { 765 return this.types; 766 } 767 768 } 769 770 private static class DelegatingInjectionPoint implements InjectionPoint { 771 772 private final InjectionPoint delegate; 773 774 protected DelegatingInjectionPoint(final InjectionPoint delegate) { 775 super(); 776 this.delegate = Objects.requireNonNull(delegate); 777 } 778 779 @Override 780 public Annotated getAnnotated() { 781 return this.delegate.getAnnotated(); 782 } 783 784 @Override 785 public Bean<?> getBean() { 786 return this.delegate.getBean(); 787 } 788 789 @Override 790 public Member getMember() { 791 return this.delegate.getMember(); 792 } 793 794 @Override 795 public Set<Annotation> getQualifiers() { 796 return this.delegate.getQualifiers(); 797 } 798 799 @Override 800 public Type getType() { 801 return this.delegate.getType(); 802 } 803 804 @Override 805 public boolean isDelegate() { 806 return this.delegate.isDelegate(); 807 } 808 809 @Override 810 public boolean isTransient() { 811 return this.delegate.isTransient(); 812 } 813 814 } 815 816 private static class FlexiblyQualifiedInjectionPoint extends DelegatingInjectionPoint { 817 818 private final Set<Annotation> qualifiers; 819 820 private FlexiblyQualifiedInjectionPoint(final InjectionPoint delegate, 821 final Set<Annotation> qualifiers) { 822 super(delegate); 823 if (qualifiers == null) { 824 this.qualifiers = null; 825 } else if (qualifiers.isEmpty()) { 826 this.qualifiers = Collections.emptySet(); 827 } else { 828 this.qualifiers = Collections.unmodifiableSet(qualifiers); 829 } 830 } 831 832 @Override 833 public Set<Annotation> getQualifiers() { 834 return this.qualifiers; 835 } 836 837 } 838 839 private static class DelegatingProducer<T> implements Producer<T> { 840 841 private final Producer<T> delegate; 842 843 protected DelegatingProducer(final Producer<T> producer) { 844 super(); 845 this.delegate = Objects.requireNonNull(producer); 846 } 847 848 @Override 849 public void dispose(final T instance) { 850 this.delegate.dispose(instance); 851 } 852 853 @Override 854 public Set<InjectionPoint> getInjectionPoints() { 855 return this.delegate.getInjectionPoints(); 856 } 857 858 @Override 859 public T produce(final CreationalContext<T> cc) { 860 return this.delegate.produce(cc); 861 } 862 863 } 864 865}