/*
* 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.environment.se;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javax.enterprise.event.Reception;
import javax.enterprise.event.TransactionPhase;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AfterDeploymentValidation;
import javax.enterprise.inject.spi.AfterTypeDiscovery;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.BeforeShutdown;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ObserverMethod;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
import javax.enterprise.inject.spi.ProcessBeanAttributes;
import javax.enterprise.inject.spi.ProcessInjectionPoint;
import javax.enterprise.inject.spi.ProcessInjectionTarget;
import javax.enterprise.inject.spi.ProcessManagedBean;
import javax.enterprise.inject.spi.ProcessObserverMethod;
import javax.enterprise.inject.spi.ProcessProducer;
import javax.enterprise.inject.spi.ProcessProducerField;
import javax.enterprise.inject.spi.ProcessProducerMethod;
import javax.enterprise.inject.spi.ProcessSessionBean;
import javax.enterprise.inject.spi.ProcessSyntheticAnnotatedType;
import javax.enterprise.inject.spi.ProcessSyntheticBean;
import javax.enterprise.inject.spi.ProcessSyntheticObserverMethod;
import javax.enterprise.inject.spi.WithAnnotations;
import javax.enterprise.util.TypeLiteral;
import org.jboss.weld.bootstrap.SyntheticExtension;
import org.jboss.weld.bootstrap.event.WeldAfterBeanDiscovery;
import org.jboss.weld.bootstrap.events.NotificationListener;
import org.jboss.weld.environment.se.logging.WeldSELogger;
import org.jboss.weld.event.ContainerLifecycleEventObserverMethod;
import org.jboss.weld.util.Observers;
import org.jboss.weld.util.Preconditions;
import org.jboss.weld.util.reflection.Reflections;
/**
* Represents a synthetic container lifecycle event observer.
* <p>
* For parameterized container lifecycele events (such as {@link ProcessAnnotatedType} and {@link ProcessProducerMethod}) it is possible to specify the observed
* container lifecycle event type, e.g. by means of {@link TypeLiteral}. To receive notifications for all observer methods with observed event type of
* {@link String}:
*
* <pre>
* ContainerLifecycleObserver.processObserverMethod(new TypeLiteral<ProcessObserverMethod<String, ?>>() {
* }.getType()).notify((e) -> System.out.println("String observer"));
* </pre>
*
* @author Martin Kouba
* @see Weld#addContainerLifecycleObserver(ContainerLifecycleObserver)
*/
public final class ContainerLifecycleObserver<T> implements ContainerLifecycleEventObserverMethod<T> {
/**
*
* @return a new builder instance
* @see BeforeBeanDiscovery
*/
public static Builder<BeforeBeanDiscovery> beforeBeanDiscovery() {
return ContainerLifecycleObserver.<BeforeBeanDiscovery> of(BeforeBeanDiscovery.class);
}
/**
*
* @param callback
* @return a new container lifecycle observer
* @see BeforeBeanDiscovery
*/
public static ContainerLifecycleObserver<BeforeBeanDiscovery> beforeBeanDiscovery(Consumer<BeforeBeanDiscovery> callback) {
return beforeBeanDiscovery().notify(callback);
}
/**
*
* @return a new builder instance
* @see AfterBeanDiscovery
*/
public static Builder<WeldAfterBeanDiscovery> afterBeanDiscovery() {
return ContainerLifecycleObserver.<WeldAfterBeanDiscovery> of(WeldAfterBeanDiscovery.class);
}
/**
*
* @param callback
* @return a new container lifecycle observer
* @see AfterBeanDiscovery
*/
public static ContainerLifecycleObserver<WeldAfterBeanDiscovery> afterBeanDiscovery(Consumer<WeldAfterBeanDiscovery> callback) {
return afterBeanDiscovery().notify(callback);
}
/**
*
* @return a new builder instance
* @see AfterTypeDiscovery
*/
public static Builder<AfterTypeDiscovery> afterTypeDiscovery() {
return ContainerLifecycleObserver.<AfterTypeDiscovery> of(AfterTypeDiscovery.class);
}
/**
*
* @param callback
* @return a new container lifecycle observer
* @see AfterTypeDiscovery
*/
public static ContainerLifecycleObserver<AfterTypeDiscovery> afterTypeDiscovery(Consumer<AfterTypeDiscovery> callback) {
return afterTypeDiscovery().notify(callback);
}
/**
*
* @param callback
* @return a new builder instance
* @see AfterDeploymentValidation
*/
public static Builder<AfterDeploymentValidation> afterDeploymentValidation() {
return ContainerLifecycleObserver.<AfterDeploymentValidation> of(AfterDeploymentValidation.class);
}
/**
*
* @param callback
* @return a new container lifecycle observer
* @see AfterDeploymentValidation
*/
public static ContainerLifecycleObserver<AfterDeploymentValidation> afterDeploymentValidation(Consumer<AfterDeploymentValidation> callback) {
return afterDeploymentValidation().notify(callback);
}
/**
*
* @param callback
* @return a new builder instance
* @see BeforeShutdown
*/
public static Builder<BeforeShutdown> beforeShutdown() {
return ContainerLifecycleObserver.<BeforeShutdown> of(BeforeShutdown.class);
}
/**
*
* @param callback
* @return a new container lifecycle observer
* @see BeforeShutdown
*/
public static ContainerLifecycleObserver<BeforeShutdown> beforeShutdown(Consumer<BeforeShutdown> callback) {
return beforeShutdown().notify(callback);
}
/**
*
* @return a new builder instance
* @see ProcessAnnotatedType
*/
@SuppressWarnings("serial")
public static Builder<ProcessAnnotatedType<?>> processAnnotatedType() {
return processAnnotatedType(new TypeLiteral<ProcessAnnotatedType<?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessAnnotatedType
*/
public static Builder<ProcessAnnotatedType<?>> processAnnotatedType(Type observedType) {
checkRawType(observedType, ProcessAnnotatedType.class);
return ContainerLifecycleObserver.<ProcessAnnotatedType<?>> of(observedType);
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessSyntheticAnnotatedType
*/
public static Builder<ProcessSyntheticAnnotatedType<?>> processSyntheticAnnotatedType(Type observedType) {
checkRawType(observedType, ProcessSyntheticAnnotatedType.class);
return ContainerLifecycleObserver.<ProcessSyntheticAnnotatedType<?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessInjectionPoint
*/
@SuppressWarnings("serial")
public static Builder<ProcessInjectionPoint<?, ?>> processInjectionPoint() {
return processInjectionPoint(new TypeLiteral<ProcessInjectionPoint<?, ?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessInjectionPoint
*/
public static Builder<ProcessInjectionPoint<?, ?>> processInjectionPoint(Type observedType) {
checkRawType(observedType, ProcessInjectionPoint.class);
return ContainerLifecycleObserver.<ProcessInjectionPoint<?, ?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessInjectionTarget
*
*/
@SuppressWarnings("serial")
public static Builder<ProcessInjectionTarget<?>> processInjectionTarget() {
return processInjectionTarget(new TypeLiteral<ProcessInjectionTarget<?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessInjectionTarget
*/
public static Builder<ProcessInjectionTarget<?>> processInjectionTarget(Type observedType) {
checkRawType(observedType, ProcessInjectionTarget.class);
return ContainerLifecycleObserver.<ProcessInjectionTarget<?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessBeanAttributes
*/
@SuppressWarnings("serial")
public static Builder<ProcessBeanAttributes<?>> processBeanAttributes() {
return processBeanAttributes(new TypeLiteral<ProcessBeanAttributes<?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessBeanAttributes
*/
public static Builder<ProcessBeanAttributes<?>> processBeanAttributes(Type observedType) {
checkRawType(observedType, ProcessBeanAttributes.class);
return ContainerLifecycleObserver.<ProcessBeanAttributes<?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessBean
*/
@SuppressWarnings("serial")
public static Builder<ProcessBean<?>> processBean() {
return processBean(new TypeLiteral<ProcessBean<?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessBean
*/
public static Builder<ProcessBean<?>> processBean(Type observedType) {
checkRawType(observedType, ProcessBean.class);
return ContainerLifecycleObserver.<ProcessBean<?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessManagedBean
*/
@SuppressWarnings("serial")
public static Builder<ProcessManagedBean<?>> processManagedBean() {
return processManagedBean(new TypeLiteral<ProcessManagedBean<?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessManagedBean
*/
public static Builder<ProcessManagedBean<?>> processManagedBean(Type observedType) {
checkRawType(observedType, ProcessManagedBean.class);
return ContainerLifecycleObserver.<ProcessManagedBean<?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessSessionBean
*/
@SuppressWarnings("serial")
public static Builder<ProcessSessionBean<?>> processSessionBean() {
return processSessionBean(new TypeLiteral<ProcessSessionBean<?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessSessionBean
*/
public static Builder<ProcessSessionBean<?>> processSessionBean(Type observedType) {
checkRawType(observedType, ProcessSessionBean.class);
return ContainerLifecycleObserver.<ProcessSessionBean<?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessProducerMethod
*/
@SuppressWarnings("serial")
public static Builder<ProcessProducerMethod<?, ?>> processProducerMethod() {
return processProducerMethod(new TypeLiteral<ProcessProducerMethod<?, ?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessProducerMethod
*/
public static Builder<ProcessProducerMethod<?, ?>> processProducerMethod(Type observedType) {
checkRawType(observedType, ProcessProducerMethod.class);
return ContainerLifecycleObserver.<ProcessProducerMethod<?, ?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessProducerField
*/
@SuppressWarnings("serial")
public static Builder<ProcessProducerField<?, ?>> processProducerField() {
return processProducerField(new TypeLiteral<ProcessProducerField<?, ?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessProducerField
*/
public static Builder<ProcessProducerField<?, ?>> processProducerField(Type observedType) {
checkRawType(observedType, ProcessProducerField.class);
return ContainerLifecycleObserver.<ProcessProducerField<?, ?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessSyntheticBean
*/
@SuppressWarnings("serial")
public static Builder<ProcessSyntheticBean<?>> processSyntheticBean() {
return processSyntheticBean(new TypeLiteral<ProcessSyntheticBean<?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessManagedBean
*/
public static Builder<ProcessSyntheticBean<?>> processSyntheticBean(Type observedType) {
checkRawType(observedType, ProcessSyntheticBean.class);
return ContainerLifecycleObserver.<ProcessSyntheticBean<?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessProducer
*/
@SuppressWarnings("serial")
public static Builder<ProcessProducer<?, ?>> processProducer() {
return processProducer(new TypeLiteral<ProcessProducer<?, ?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessProducer
*/
public static Builder<ProcessProducer<?, ?>> processProducer(Type observedType) {
checkRawType(observedType, ProcessProducer.class);
return ContainerLifecycleObserver.<ProcessProducer<?, ?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessObserverMethod
*/
@SuppressWarnings("serial")
public static Builder<ProcessObserverMethod<?, ?>> processObserverMethod() {
return processObserverMethod(new TypeLiteral<ProcessObserverMethod<?, ?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessObserverMethod
*/
public static Builder<ProcessObserverMethod<?, ?>> processObserverMethod(Type observedType) {
checkRawType(observedType, ProcessObserverMethod.class);
return ContainerLifecycleObserver.<ProcessObserverMethod<?, ?>> of(observedType);
}
/**
*
* @return a new builder instance
* @see ProcessSyntheticObserverMethod
*/
@SuppressWarnings("serial")
public static Builder<ProcessSyntheticObserverMethod<?, ?>> processSyntheticObserverMethod() {
return processSyntheticObserverMethod(new TypeLiteral<ProcessSyntheticObserverMethod<?, ?>>() {
}.getType());
}
/**
*
* @param observedType
* @return a new builder instance
* @see ProcessSyntheticObserverMethod
*/
public static Builder<ProcessSyntheticObserverMethod<?, ?>> processSyntheticObserverMethod(Type observedType) {
checkRawType(observedType, ProcessSyntheticObserverMethod.class);
return ContainerLifecycleObserver.<ProcessSyntheticObserverMethod<?, ?>> of(observedType);
}
// Instance members
private final int priority;
private final Type observedType;
private final BiConsumer<T, BeanManager> callbackWithBeanManager;
private final Consumer<T> callback;
private final Collection<Class<? extends Annotation>> requiredAnnotations;
private volatile BeanManager beanManager;
private volatile SyntheticExtension extension;
private ContainerLifecycleObserver(int priority, Type observedType, BiConsumer<T, BeanManager> callbackWithBeanManager, Consumer<T> callback,
Collection<Class<? extends Annotation>> requiredAnnotations) {
this.priority = priority;
this.observedType = observedType;
this.callbackWithBeanManager = callbackWithBeanManager;
this.callback = callback;
this.requiredAnnotations = requiredAnnotations;
}
@Override
public int getPriority() {
return priority;
}
@Override
public Class<?> getBeanClass() {
return ContainerLifecycleObserver.class;
}
@Override
public Type getObservedType() {
return observedType;
}
@Override
public Set<Annotation> getObservedQualifiers() {
return Collections.emptySet();
}
@Override
public Reception getReception() {
return Reception.ALWAYS;
}
@Override
public TransactionPhase getTransactionPhase() {
return TransactionPhase.IN_PROGRESS;
}
@Override
public void notify(T event) {
if (beanManager == null || extension == null) {
throw WeldSELogger.LOG.containerLifecycleObserverNotInitialized(toString());
}
if (event instanceof NotificationListener) {
NotificationListener.class.cast(event).preNotify(extension);
}
try {
if (callbackWithBeanManager != null) {
callbackWithBeanManager.accept(event, beanManager);
} else {
callback.accept(event);
}
} finally {
if (event instanceof NotificationListener) {
NotificationListener.class.cast(event).postNotify(null);
}
}
}
@Override
public Collection<Class<? extends Annotation>> getRequiredAnnotations() {
return requiredAnnotations;
}
private void setBeanManager(BeanManager beanManager) {
this.beanManager = beanManager;
}
private void setExtension(SyntheticExtension extension) {
this.extension = extension;
}
@Override
public String toString() {
return String.format("ContainerLifecyleObserver [priority=%s, observedType=%s]", priority, observedType);
}
/**
* A synthetic extension is basically a container for synthetic container lifecycle event observers.
*
* @return a builder for a synthetic extension
* @see Weld#addExtension(javax.enterprise.inject.spi.Extension)
*/
public static ExtensionBuilder extensionBuilder() {
return new ExtensionBuilder();
}
private static <T> Builder<T> of(Type observedType) {
if (!Observers.CONTAINER_LIFECYCLE_EVENT_TYPES.contains(Reflections.getRawType(observedType))) {
throw WeldSELogger.LOG.observedTypeNotContonainerLifecycleEventType(observedType);
}
return new Builder<>(observedType);
}
private static void checkRawType(Type observedType, Class<?> rawType) {
if (!rawType.equals(Reflections.getRawType(observedType))) {
throw WeldSELogger.LOG.observedTypeDoesNotMatchContonainerLifecycleEventType(observedType, rawType);
}
}
/**
* This builder is used to create a synthetic container lifecycle event observer.
*
* @author Martin Kouba
*
* @param <T>
*/
public static class Builder<T> {
private static final String OBSERVED_TYPE = "observedType";
private static final String CALLBACK = "callback";
@SuppressWarnings("checkstyle:magicnumber")
private static final int DEFAULT_PRIORITY = javax.interceptor.Interceptor.Priority.APPLICATION + 500;
private int priority = DEFAULT_PRIORITY;
private Type observedType;
private Collection<Class<? extends Annotation>> requiredAnnotations = Collections.emptySet();
private Builder(Type observedType) {
Preconditions.checkArgumentNotNull(observedType, OBSERVED_TYPE);
this.observedType = observedType;
}
/**
* Set the priority.
*
* @param priority
* @return self
*/
public Builder<T> priority(int priority) {
this.priority = priority;
return this;
}
/**
* The annotations are only considered for {@link ProcessAnnotatedType}, i.e. they are ignored for other container lifecycle events.
*
* @param annotations
* @return self
* @see WithAnnotations
*/
@SafeVarargs
public final Builder<T> withAnnotations(Class<? extends Annotation>... annotations) {
this.requiredAnnotations = new HashSet<>();
Collections.addAll(requiredAnnotations, annotations);
return this;
}
/**
* Set a callback used during observer notification. The first callback parameter is an event object and the second parameter is a {@link BeanManager}
* instance.
* <p>
* This is a terminal operation.
*
* @param callback
* @return the built observer
* @see ObserverMethod#notify(Object)
*/
public ContainerLifecycleObserver<T> notify(BiConsumer<T, BeanManager> callback) {
Preconditions.checkArgumentNotNull(callback, CALLBACK);
return new ContainerLifecycleObserver<>(priority, observedType, callback, null, requiredAnnotations);
}
/**
* Set a callback used during observer notification. The callback parameter is an event object.
* <p>
* This is a terminal operation.
*
* @param callback
* @return the built observer
* @see ObserverMethod#notify(Object)
*/
public ContainerLifecycleObserver<T> notify(Consumer<T> callback) {
Preconditions.checkArgumentNotNull(callback, CALLBACK);
return new ContainerLifecycleObserver<>(priority, observedType, null, callback, requiredAnnotations);
}
}
static class ContainerLifecycleObserverExtension implements SyntheticExtension {
private final List<ContainerLifecycleObserver<?>> observers;
ContainerLifecycleObserverExtension(List<ContainerLifecycleObserver<?>> observers) {
this.observers = new ArrayList<>(observers);
}
public void initialize(BeanManager beanManager) {
for (ContainerLifecycleObserver<?> observer : observers) {
observer.setBeanManager(beanManager);
observer.setExtension(this);
}
}
@Override
public Collection<ContainerLifecycleEventObserverMethod<?>> getObservers() {
return Reflections.cast(observers);
}
}
/**
* This builder is used to create a synthetic extension, i.e. a collection of synthetic container lifecycle observers.
*
* @author Martin Kouba
*/
public static class ExtensionBuilder {
private final List<ContainerLifecycleObserver<?>> observers;
private ExtensionBuilder() {
this.observers = new LinkedList<>();
}
/**
*
* @param observer
* @return self
*/
public ExtensionBuilder add(ContainerLifecycleObserver<?> observer) {
observers.add(observer);
return this;
}
public Extension build() {
return new ContainerLifecycleObserverExtension(observers);
}
}
}