/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* 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.errai.ioc.rebind.ioc.graph.api;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.function.Predicate;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Scope;
import org.jboss.errai.codegen.meta.HasAnnotations;
import org.jboss.errai.codegen.meta.MetaClass;
import org.jboss.errai.codegen.meta.MetaClassMember;
import org.jboss.errai.codegen.meta.MetaField;
import org.jboss.errai.codegen.meta.MetaMethod;
import org.jboss.errai.codegen.meta.MetaParameter;
import org.jboss.errai.ioc.client.api.EntryPoint;
import org.jboss.errai.ioc.rebind.ioc.extension.IOCExtensionConfigurator;
import org.jboss.errai.ioc.rebind.ioc.extension.builtin.LoggerFactoryIOCExtension;
import org.jboss.errai.ioc.rebind.ioc.graph.impl.InjectableHandle;
import org.jboss.errai.ioc.rebind.ioc.graph.impl.ResolutionPriority;
import org.jboss.errai.ioc.rebind.ioc.injector.api.InjectableProvider;
import org.jboss.errai.ioc.rebind.ioc.injector.api.WiringElementType;
import jsinterop.annotations.JsType;
/**
* Builds and resolves a dependency graph.
*
* {@link Injectable Injectables} represent a source for a bean (for example an
* explicitly scoped concrete type, or a producer method). {@link Dependency
* Dependencies} are beans that are required for injection points or producer
* parameters in a bean represented by an {@link Injectable}.
*
* The {@link DependencyGraphBuilder} API allows for adding injectables and
* declaring dependencies for those injectables by passing in the types and
* {@link Qualifier qualifiers} of the dependencies.
*
* When all injectables and dependencies are added, calling
* {@link #createGraph()} will return a fully-resolved dependency graph.
*
* @see DependencyGraph
* @author Max Barkley <mbarkley@redhat.com>
*/
public interface DependencyGraphBuilder {
/**
* Add an {@link Injectable} to the graph.
*
* @param injectedType
* The class of the injectable.
* @param qualifier
* The {@link Qualifier} of the injectable.
* @param pathPredicate
* Used to add restrictions on what dependencies are satisfied by the added injectable. This predicate is
* called anytime the created injectable is a candidate for a dependency. The predicate argument is the
* {@link InjectableHandle InjectableHandles} traversed in order to arrive at this injectable. If the
* predicate returns false, the injectable does not satisfy a dependency.
* @param literalScope
* The {@link Scope} of the injectable.
* @param injectableType
* The kind of injectable (i.e. producer, provider, type, etc.).
* @param wiringTypes
* A collection of {@link WiringElementType wiring types} that this injectable has.
*
* @return The newly added {@link Injectable}.
*/
Injectable addInjectable(MetaClass injectedType, Qualifier qualifier, Predicate<List<InjectableHandle>> pathPredicate, Class<? extends Annotation> literalScope,
InjectableType injectableType, WiringElementType... wiringTypes);
/**
* Some {@link IOCExtensionConfigurator IOC extensions} need to generate
* special code per injection point (such as the
* {@link LoggerFactoryIOCExtension}). Adding an extension injectable allows
* for this by adding an object that satisfies a given type and qualifier, but
* is not itself an injectable. For every injection point satisfied by a an
* extension injectable, a {@link ProvidedInjectable} will be created
* containing metadata for the specific injection point.
*
* @param injectedType
* The class of the injectable.
* @param qualifier
* The {@link Qualifier} of the injectable.
* @param pathPredicate
* Used to add restrictions on what dependencies are satisfied by the added injectable. This predicate is
* called anytime the created injectable is a candidate for a dependency. The predicate argument is the
* {@link InjectableHandle InjectableHandles} traversed in order to arrive at this injectable. If the
* predicate returns false, the injectable does not satisfy a dependency.
* @param literalScope
* The {@link Scope} of the injectable.
* @param injectableType
* The kind of injectable (i.e. producer, provider, type, etc.).
* @param wiringTypes
* A collection of {@link WiringElementType wiring types} that this
* injectable has.
*
* @return The newly added extension {@link Injectable}.
*/
Injectable addExtensionInjectable(MetaClass injectedType, Qualifier qualifier,
Predicate<List<InjectableHandle>> pathPredicate, InjectableProvider provider,
WiringElementType... wiringTypes);
/**
* Create a dependency for a field injection point in a bean class.
*
* @param injectable The {@link Injectable} that has the dependency.
* @param type The class of the dependency.
* @param qualifier The qualifier of the dependency.
* @param dependentField The field that the dependency should be injected into.
*/
void addFieldDependency(Injectable injectable, MetaClass type, Qualifier qualifier, MetaField dependentField);
/**
* Create a dependency for a constructor injection point in a bean class.
*
* @param injectable The {@link Injectable} that has the dependency.
* @param type The class of the dependency.
* @param qualifier The qualifier of the dependency.
* @param dependentField The parameter of the constructor that the dependency should be injected into.
*/
void addConstructorDependency(Injectable injectable, MetaClass type, Qualifier qualifier, int paramIndex, MetaParameter param);
/**
* Create a dependency for a producer parameter injection point in a bean class.
*
* @param injectable The {@link Injectable} that has the dependency.
* @param type The class of the dependency.
* @param qualifier The qualifier of the dependency.
* @param dependentField The parameter of the producer method that the dependency should be injected into.
*/
void addProducerParamDependency(Injectable injectable, MetaClass type, Qualifier qualifier, int paramIndex, MetaParameter param);
/**
* Create a dependency for a producer member (field or method) injection point in a bean class.
*
* @param injectable The {@link Injectable} that has the dependency.
* @param type The class of the dependency.
* @param qualifier The qualifier of the dependency.
* @param dependentField The producer member (field or method) that must be invoked to satisfy the dependency.
*/
void addProducerMemberDependency(Injectable injectable, MetaClass type, Qualifier qualifier, MetaClassMember producingMember);
/**
* Create a dependency for a static producer member (field or method) injection point in a bean class.
*
* @param injectable The {@link Injectable} that has the dependency.
* @param type The class of the dependency.
* @param dependentField The producer member (field or method) that must be invoked to satisfy the dependency.
*/
void addProducerMemberDependency(Injectable producedInjectable, MetaClass producerType, MetaClassMember method);
/**
* Create a dependency for a setter method injection point in a bean class.
*
* @param injectable The {@link Injectable} that has the dependency.
* @param type The class of the dependency.
* @param qualifier The qualifier of the dependency.
* @param dependentField The setter method that the dependency should be injected into.
*/
void addSetterMethodDependency(Injectable injectable, MetaClass type, Qualifier qualifier, MetaMethod setter);
/**
* Create a dependency for a disposer method from a producer bean class. This
* kind of dependency must always accomany a producer member dependency to be
* of any use.
*
* @param injectable
* The {@link Injectable} that has the dependency.
* @param type
* The class of the dependency.
* @param qualifier
* The qualifier of the dependency.
* @param dependentField
* The disposer method that must be invoked.
*/
void addDisposesMethodDependency(Injectable injectable, MetaClass type, Qualifier qualifier, MetaMethod disposer);
/**
* Create a dependency for a disposer method parameter. This
* kind of dependency must always accomany a disposer method dependency to be
* of any use.
*
* @param injectable
* The {@link Injectable} that has the dependency.
* @param type
* The class of the dependency.
* @param qualifier
* The qualifier of the dependency.
* @param dependentField
* The parameter that must have an injected value in the disposer method that must be invoked.
*/
void addDisposesParamDependency(Injectable injectable, MetaClass type, Qualifier qualifier, Integer index, MetaParameter param);
/**
* Resolve all dependencies of added injectables. This method throws exceptions if any dependencies are unsastisfied
* or are ambiguously satisfied.
*
* @param strategy
* Defines what strategy is used to determine reachability of beans. Beans considered unreachable will not be
* available at runtime (reducing the size of the generated code).
*
* @return A {@link DependencyGraph} where all contained {@link Injectable injectables} have fully resolved
* dependencies.
*
* @see ResolutionPriority
*/
DependencyGraph createGraph(ReachabilityStrategy strategy);
public static enum ReachabilityStrategy {
/**
* With this strategy all types are considered reachable.
*/
All,
/**
* With this strategy only beans reachable from beans that have explicit annotations relevant to the IoC are
* considered availablbe such as a scope, {@link Inject}, {@link Produces}, {@link JsType}, etc.
*/
Annotated,
/**
* With this strategy only types reachable from a type that is an {@link EntryPoint} or a
* {@link JsType} are kept.
*/
Aggressive
}
/**
* The kinds of {@link Injectable injectables}.
*
* @author Max Barkley <mbarkley@redhat.com>
*/
public static enum InjectableType {
Type, JsType, Producer, Provider, ContextualProvider, Extension, ExtensionProvided, Static, Disabled
}
/**
* The kinds of {@link Dependency dependencies}.
*
* @author Max Barkley <mbarkley@redhat.com>
*/
public static enum DependencyType {
Constructor, Field, ProducerMember, ProducerParameter, SetterParameter, DisposerMethod, DisposerParameter
}
/**
* When a dependency is added via a graph builder method such as
* {@link DependencyGraphBuilder#addFieldDependency(Injectable, MetaClass, Qualifier, MetaField)}
* , a subtype of {@link Dependency} is added to the depending
* {@link Injectable} internally.
*
* Once the graph is resolved, an injectables dependencies can be found using {@link Injectable#getDependencies()}.
*
* @author Max Barkley <mbarkley@redhat.com>
*/
public static interface Dependency extends HasAnnotations {
/**
* This will only return a meaningful value after {@link DependencyGraphBuilder#createGraph()} is called.
*
* @return The injectable that satisfied this dependency.
*/
Injectable getInjectable();
/**
* @return The kind of this dependency.
*/
DependencyType getDependencyType();
}
/**
* A dependency for a parameter in a method or constructor.
*
* @see Dependency
* @author Max Barkley <mbarkley@redhat.com>
*/
public static interface ParamDependency extends Dependency {
/**
* @return The index of this parameter in its enclosing method or constructor.
*/
int getParamIndex();
/**
* @return The parameter of this dependency.
*/
MetaParameter getParameter();
}
/**
* A dependency for a field in a type.
*
* @see Dependency
* @author Max Barkley <mbarkley@redhat.com>
*/
public static interface FieldDependency extends Dependency {
/**
* @return The field of this dependency.
*/
MetaField getField();
}
/**
* A dependency for a setter method parameter.
*
* @see Dependency
* @author Max Barkley <mbarkley@redhat.com>
*/
public static interface SetterParameterDependency extends Dependency {
/**
* @return The setter method of this dependency.
*/
MetaMethod getMethod();
}
/**
* A dependency on a producer instance. If a type has a producer method, two
* injectables should be made for the type. One of the type with the method
* (since it is itself type injectable) and one of the produced type.
*
* The injectable for the produced type will then depend on the producer type
* via a {@link ProducerInstanceDependency}.
*
* @see Dependency
* @author Max Barkley <mbarkley@redhat.com>
*/
public static interface ProducerInstanceDependency extends Dependency {
/**
* @return The producer member of this dependency.
*/
MetaClassMember getProducingMember();
}
/**
* A dependency on a dispoer method. If a producer has a disposer method
* satisfying the qualifiers of one of its producer, then the injectable for
* the produced type should depend on the producer member (via a
* {@link ProducerInstanceDependency}) and the disposer method via this
* dependency.
*
* @see Dependency
* @author Max Barkley <mbarkley@redhat.com>
*/
public static interface DisposerMethodDependency extends Dependency {
/**
* @return The disposer method of this dependency.
*/
MetaMethod getDisposerMethod();
}
}