/* * JBoss, Home of Professional Open Source * Copyright 2016, Red Hat, Inc., and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.weld.bootstrap.events.configurator; import static org.jboss.weld.util.Preconditions.checkArgumentNotNull; import static org.jboss.weld.util.reflection.Reflections.cast; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; import javax.enterprise.context.Dependent; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.inject.Instance; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanAttributes; import javax.enterprise.inject.spi.InjectionPoint; import javax.enterprise.inject.spi.InjectionTarget; import javax.enterprise.inject.spi.PassivationCapable; import javax.enterprise.inject.spi.configurator.BeanConfigurator; import javax.enterprise.util.TypeLiteral; import org.jboss.weld.bean.BeanIdentifiers; import org.jboss.weld.bootstrap.BeanDeploymentFinder; import org.jboss.weld.inject.WeldInstance; import org.jboss.weld.logging.BeanLogger; import org.jboss.weld.manager.BeanManagerImpl; import org.jboss.weld.util.ForwardingWeldInstance; import org.jboss.weld.util.bean.ForwardingBeanAttributes; import org.jboss.weld.util.collections.ImmutableSet; import org.jboss.weld.util.reflection.Formats; /** * * @author Martin Kouba * * @param <T> */ public class BeanConfiguratorImpl<T> implements BeanConfigurator<T>, Configurator<Bean<T>> { private final BeanManagerImpl beanManager; private Class<?> beanClass; private final Set<InjectionPoint> injectionPoints; private final BeanAttributesConfiguratorImpl<T> attributes; private String id; private CreateCallback<T> createCallback; private DestroyCallback<T> destroyCallback; /** * * @param defaultBeanClass * @param beanDeploymentFinder */ public BeanConfiguratorImpl(Class<?> defaultBeanClass, BeanDeploymentFinder beanDeploymentFinder) { this.beanClass = defaultBeanClass; this.injectionPoints = new HashSet<>(); this.beanManager = beanDeploymentFinder.getOrCreateBeanDeployment(beanClass).getBeanManager(); this.attributes = new BeanAttributesConfiguratorImpl<>(beanManager); } @Override public BeanConfigurator<T> beanClass(Class<?> beanClass) { checkArgumentNotNull(beanClass); this.beanClass = beanClass; return this; } @Override public BeanConfigurator<T> addInjectionPoint(InjectionPoint injectionPoint) { checkArgumentNotNull(injectionPoint); this.injectionPoints.add(injectionPoint); return this; } @Override public BeanConfigurator<T> addInjectionPoints(InjectionPoint... injectionPoints) { checkArgumentNotNull(injectionPoints); Collections.addAll(this.injectionPoints, injectionPoints); return this; } @Override public BeanConfigurator<T> addInjectionPoints(Set<InjectionPoint> injectionPoints) { checkArgumentNotNull(injectionPoints); this.injectionPoints.addAll(injectionPoints); return this; } @Override public BeanConfigurator<T> injectionPoints(InjectionPoint... injectionPoints) { this.injectionPoints.clear(); return addInjectionPoints(injectionPoints); } @Override public BeanConfigurator<T> injectionPoints(Set<InjectionPoint> injectionPoints) { this.injectionPoints.clear(); return addInjectionPoints(injectionPoints); } @Override public BeanConfigurator<T> id(String id) { checkArgumentNotNull(id); this.id = id; return this; } @Override public <U extends T> BeanConfigurator<U> createWith(Function<CreationalContext<U>, U> callback) { checkArgumentNotNull(callback); this.createCallback = cast(CreateCallback.fromCreateWith(callback)); return cast(this); } @Override public <U extends T> BeanConfigurator<U> produceWith(Function<Instance<Object>, U> callback) { checkArgumentNotNull(callback); this.createCallback = cast(CreateCallback.fromProduceWith(callback)); return cast(this); } @Override public BeanConfigurator<T> destroyWith(BiConsumer<T, CreationalContext<T>> callback) { checkArgumentNotNull(callback); this.destroyCallback = DestroyCallback.fromDestroy(callback); return this; } @Override public BeanConfigurator<T> disposeWith(BiConsumer<T, Instance<Object>> callback) { checkArgumentNotNull(callback); this.destroyCallback = DestroyCallback.fromDispose(callback); return this; } @Override public <U extends T> BeanConfigurator<U> read(AnnotatedType<U> type) { checkArgumentNotNull(type); final InjectionTarget<T> injectionTarget = cast(beanManager.getInjectionTargetFactory(type).createInjectionTarget(null)); addInjectionPoints(injectionTarget.getInjectionPoints()); createWith(c -> { T instance = injectionTarget.produce(c); injectionTarget.inject(instance, c); injectionTarget.postConstruct(instance); return instance; }); destroyWith((i, c) -> { injectionTarget.preDestroy(i); c.release(); }); BeanAttributes<U> beanAttributes = beanManager.createBeanAttributes(type); read(beanAttributes); return cast(this); } @Override public BeanConfigurator<T> read(BeanAttributes<?> beanAttributes) { checkArgumentNotNull(beanAttributes); this.attributes.read(beanAttributes); return this; } @Override public BeanConfigurator<T> addType(Type type) { checkArgumentNotNull(type); this.attributes.addType(type); return this; } @Override public BeanConfigurator<T> addType(TypeLiteral<?> typeLiteral) { checkArgumentNotNull(typeLiteral); this.attributes.addType(typeLiteral.getType()); return this; } @Override public BeanConfigurator<T> addTypes(Type... types) { checkArgumentNotNull(types); this.attributes.addTypes(types); return this; } @Override public BeanConfigurator<T> addTypes(Set<Type> types) { checkArgumentNotNull(types); this.attributes.addTypes(types); return this; } @Override public BeanConfigurator<T> addTransitiveTypeClosure(Type type) { checkArgumentNotNull(type); this.attributes.addTransitiveTypeClosure(type); return this; } @Override public BeanConfigurator<T> types(Type... types) { checkArgumentNotNull(types); this.attributes.types(types); return this; } @Override public BeanConfigurator<T> types(Set<Type> types) { checkArgumentNotNull(types); this.attributes.types(types); return this; } @Override public BeanConfigurator<T> scope(Class<? extends Annotation> scope) { checkArgumentNotNull(scope); this.attributes.scope(scope); return this; } @Override public BeanConfigurator<T> addQualifier(Annotation qualifier) { checkArgumentNotNull(qualifier); this.attributes.addQualifier(qualifier); return this; } @Override public BeanConfigurator<T> addQualifiers(Annotation... qualifiers) { checkArgumentNotNull(qualifiers); this.attributes.addQualifiers(qualifiers); return this; } @Override public BeanConfigurator<T> addQualifiers(Set<Annotation> qualifiers) { checkArgumentNotNull(qualifiers); this.attributes.addQualifiers(qualifiers); return this; } @Override public BeanConfigurator<T> qualifiers(Annotation... qualifiers) { checkArgumentNotNull(qualifiers); this.attributes.qualifiers(qualifiers); return this; } @Override public BeanConfigurator<T> qualifiers(Set<Annotation> qualifiers) { checkArgumentNotNull(qualifiers); this.attributes.qualifiers(qualifiers); return this; } @Override public BeanConfigurator<T> addStereotype(Class<? extends Annotation> stereotype) { checkArgumentNotNull(stereotype); this.attributes.addStereotype(stereotype); return this; } @Override public BeanConfigurator<T> addStereotypes(Set<Class<? extends Annotation>> stereotypes) { checkArgumentNotNull(stereotypes); this.attributes.addStereotypes(stereotypes); return this; } @Override public BeanConfigurator<T> stereotypes(Set<Class<? extends Annotation>> stereotypes) { checkArgumentNotNull(stereotypes); this.attributes.stereotypes(stereotypes); return this; } @Override public BeanConfigurator<T> name(String name) { this.attributes.name(name); return this; } @Override public BeanConfigurator<T> alternative(boolean alternative) { this.attributes.alternative(alternative); return this; } public Bean<T> complete() { return new ImmutableBean<>(this); } public BeanManagerImpl getBeanManager() { return beanManager; } static final class CreateCallback<T> { private final Supplier<T> simple; private final Function<CreationalContext<T>, T> create; private final Function<Instance<Object>, T> instance; static <T> CreateCallback<T> fromProduceWith(Function<Instance<Object>, T> callback) { return new CreateCallback<T>(null, null, callback); } static <T> CreateCallback<T> fromProduceWith(Supplier<T> callback) { return new CreateCallback<T>(callback, null, null); } static <T> CreateCallback<T> fromCreateWith(Function<CreationalContext<T>, T> callback) { return new CreateCallback<T>(null, callback, null); } CreateCallback(Supplier<T> simple, Function<CreationalContext<T>, T> create, Function<Instance<Object>, T> instance) { this.simple = simple; this.create = create; this.instance = instance; } private T create(Bean<?> bean, CreationalContext<T> ctx, BeanManagerImpl beanManager) { if (simple != null) { return simple.get(); } else if (instance != null) { return instance.apply(createInstance(bean, ctx, beanManager)); } else { return create.apply(ctx); } } private Instance<Object> createInstance(Bean<?> bean, CreationalContext<T> ctx, BeanManagerImpl beanManager) { WeldInstance<Object> instance = beanManager.getInstance(ctx); if (Dependent.class.equals(bean.getScope())) { return instance; } return new GuardedInstance<>(bean, instance); } } static class GuardedInstance<T> extends ForwardingWeldInstance<T> { private final Bean<?> bean; private final WeldInstance<T> delegate; public GuardedInstance(Bean<?> bean, WeldInstance<T> delegate) { this.bean = bean; this.delegate = delegate; } @Override public WeldInstance<T> delegate() { return delegate; } @Override public <U extends T> WeldInstance<U> select(Class<U> subtype, Annotation... qualifiers) { return wrap(subtype, delegate.select(subtype, qualifiers)); } @Override public <U extends T> WeldInstance<U> select(TypeLiteral<U> subtype, Annotation... qualifiers) { return wrap(subtype.getType(), delegate.select(subtype, qualifiers)); } @Override public WeldInstance<T> select(Annotation... qualifiers) { return wrap(null, delegate.select(qualifiers)); } private <TYPE> WeldInstance<TYPE> wrap(Type subtype, WeldInstance<TYPE> delegate) { if (subtype != null && InjectionPoint.class.equals(subtype)) { throw BeanLogger.LOG.cannotInjectInjectionPointMetadataIntoNonDependent(bean); } return new GuardedInstance<>(bean, delegate); } } static final class DestroyCallback<T> { private final BiConsumer<T, CreationalContext<T>> destroy; private final BiConsumer<T, Instance<Object>> dispose; static <T> DestroyCallback<T> fromDispose(BiConsumer<T, Instance<Object>> callback) { return new DestroyCallback<>(callback, null); } static <T> DestroyCallback<T> fromDestroy(BiConsumer<T, CreationalContext<T>> callback) { return new DestroyCallback<>(null, callback); } public DestroyCallback(BiConsumer<T, Instance<Object>> dispose, BiConsumer<T, CreationalContext<T>> destroy) { this.destroy = destroy; this.dispose = dispose; } void destroy(T instance, CreationalContext<T> ctx, BeanManagerImpl beanManager) { if (dispose != null) { dispose.accept(instance, beanManager.getInstance(ctx)); } else { destroy.accept(instance, ctx); } } } /** * * @author Martin Kouba * * @param <T> the class of the bean instance */ static class ImmutableBean<T> extends ForwardingBeanAttributes<T> implements Bean<T>, PassivationCapable { private final String id; private final BeanManagerImpl beanManager; private final Class<?> beanClass; private final BeanAttributes<T> attributes; private final Set<InjectionPoint> injectionPoints; private final CreateCallback<T> createCallback; private final DestroyCallback<T> destroyCallback; /** * * @param configurator */ ImmutableBean(BeanConfiguratorImpl<T> configurator) { this.beanManager = configurator.getBeanManager(); this.beanClass = configurator.beanClass; this.attributes = configurator.attributes.complete(); this.injectionPoints = ImmutableSet.copyOf(configurator.injectionPoints); this.createCallback = configurator.createCallback; this.destroyCallback = configurator.destroyCallback; if (configurator.id != null) { this.id = configurator.id; } else { this.id = BeanIdentifiers.forBuilderBean(attributes, beanClass); } } @Override public T create(CreationalContext<T> creationalContext) { return createCallback.create(this, creationalContext, beanManager); } @Override public void destroy(T instance, CreationalContext<T> creationalContext) { if (destroyCallback != null) { destroyCallback.destroy(instance, creationalContext, beanManager); } } @Override public Class<?> getBeanClass() { return beanClass; } @Override public Set<InjectionPoint> getInjectionPoints() { return injectionPoints; } @Override public boolean isNullable() { return false; } @Override protected BeanAttributes<T> attributes() { return attributes; } @Override public String getId() { return id; } @Override public String toString() { return "Configurator Bean [" + getBeanClass().toString() + ", types: " + Formats.formatTypes(getTypes()) + ", qualifiers: " + Formats.formatAnnotations(getQualifiers()) + "]"; } } }