| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you 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. |
| |
| using System; |
| using System.Collections.Generic; |
| using System.Globalization; |
| using System.Linq; |
| using System.Reflection; |
| using System.Text; |
| using Org.Apache.REEF.Tang.Annotations; |
| using Org.Apache.REEF.Tang.Exceptions; |
| using Org.Apache.REEF.Tang.Implementations.Configuration; |
| using Org.Apache.REEF.Tang.Interface; |
| using Org.Apache.REEF.Tang.Types; |
| using Org.Apache.REEF.Tang.Util; |
| using Org.Apache.REEF.Utilities.Logging; |
| |
| namespace Org.Apache.REEF.Tang.Implementations.InjectionPlan |
| { |
| internal sealed class InjectorImpl : IInjector |
| { |
| private static readonly Logger LOGGER = Logger.GetLogger(typeof(InjectorImpl)); |
| |
| readonly IDictionary<INamedParameterNode, object> namedParameterInstances = new MonotonicTreeMap<INamedParameterNode, object>(); |
| private readonly ICsClassHierarchy classHierarchy; |
| private readonly IConfiguration configuration; |
| readonly IDictionary<IClassNode, object> instances = new MonotonicTreeMap<IClassNode, object>(); |
| private Aspect aspect; |
| private readonly ISet<IInjectionFuture<object>> pendingFutures = new HashSet<IInjectionFuture<object>>(); |
| |
| static readonly InjectionPlan BUILDING = new BuildingInjectionPlan(null); // TODO anonymous class |
| |
| private bool concurrentModificationGuard = false; |
| |
| private void AssertNotConcurrent() |
| { |
| if (concurrentModificationGuard) |
| { |
| // TODO |
| // throw new ConcurrentModificationException("Detected attempt to use Injector from within an injected constructor!"); |
| } |
| } |
| public InjectorImpl(IConfiguration c) |
| { |
| this.configuration = c; |
| this.classHierarchy = (ICsClassHierarchy)c.GetClassHierarchy(); |
| } |
| |
| public object InjectFromPlan(InjectionPlan plan) |
| { |
| if (!plan.IsFeasible()) |
| { |
| var ex = new InjectionException("Cannot inject " + plan.GetNode().GetFullName() + ": " |
| + plan.ToCantInjectString()); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| |
| if (plan.IsAmbiguous()) |
| { |
| var ex = new InjectionException("Cannot inject " + plan.GetNode().GetFullName() + " " |
| + plan.ToCantInjectString()); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| |
| if (plan is InjectionFuturePlan) |
| { |
| InjectionFuturePlan fut = (InjectionFuturePlan)plan; |
| INode node = fut.GetNode(); |
| string key = node.GetFullName(); |
| try |
| { |
| Type t = null; |
| Type nodeType = classHierarchy.ClassForName(node.GetFullName()); |
| |
| if (node is IClassNode) |
| { |
| t = nodeType; |
| } |
| else if (node is INamedParameterNode) |
| { |
| var nn = (INamedParameterNode)node; |
| t = classHierarchy.ClassForName(nn.GetFullArgName()); |
| if (nn.IsSet()) |
| { |
| t = typeof(ISet<>).MakeGenericType(new Type[] { t }); |
| } |
| } |
| else |
| { |
| var ex = new TangApplicationException("Unexpected node type. Wanted ClassNode or NamedParameterNode. Got: " + node); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| |
| // Java - InjectionFuture<?> ret = new InjectionFuture<>(this, javaNamespace.classForName(fut.getNode().getFullName())); |
| // C# - InjectionFuture<object> ret = new InjectionFutureImpl<object>(this, classHierarchy.ClassForName(fut.GetNode().GetFullName())); |
| // We cannot simply create an object from generic with object as <T> |
| // typeof(InjectionFutureImpl<>).MakeGenericType(t) will get the InjectionFutureImpl generic Type with <T> as t |
| // for ClassNode, t is the Type of the class, for NamedParameterNode, t is the Type of the argument |
| // we then use reflection to invoke the constructor |
| // To retain generic argument information?? |
| Type injectionFuture = typeof(InjectionFutureImpl<>).MakeGenericType(t); |
| var constructor = injectionFuture.GetConstructor(new Type[] { typeof(IInjector), typeof(Type) }); |
| IInjectionFuture<object> ret = (IInjectionFuture<object>)constructor.Invoke(new object[] { this, nodeType }); |
| |
| pendingFutures.Add(ret); |
| return ret; |
| } |
| catch (TypeLoadException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new InjectionException("Could not get class for " + key), LOGGER); |
| } |
| } |
| else if (plan.GetNode() is IClassNode && GetCachedInstance((IClassNode)plan.GetNode()) != null) |
| { |
| return GetCachedInstance((IClassNode)plan.GetNode()); |
| } |
| else if (plan is CsInstance) |
| { |
| // TODO: Must be named parameter node. Check. |
| // throw new IllegalStateException("Instance from plan not in Injector's set of instances?!?"); |
| return ((CsInstance)plan).instance; |
| } |
| else if (plan is Constructor) |
| { |
| Constructor constructor = (Constructor)plan; |
| object[] args = new object[constructor.GetArgs().Length]; |
| InjectionPlan[] argPlans = constructor.GetArgs(); |
| |
| for (int i = 0; i < argPlans.Length; i++) |
| { |
| args[i] = InjectFromPlan(argPlans[i]); |
| } |
| |
| try |
| { |
| concurrentModificationGuard = true; |
| object ret = null; |
| try |
| { |
| IConstructorDef def = (IConstructorDef)constructor.GetConstructorDef(); |
| ConstructorInfo c = GetConstructor(def); |
| |
| if (aspect != null) |
| { |
| ret = aspect.Inject(def, c, args); |
| } |
| else |
| { |
| ret = c.Invoke(args); |
| } |
| } |
| catch (ArgumentException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| StringBuilder sb = new StringBuilder("Internal Tang error? Could not call constructor " + constructor.GetConstructorDef() + " with arguments ["); |
| foreach (object o in args) |
| { |
| sb.Append("\n\t" + o); |
| } |
| sb.Append("]"); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException(sb.ToString(), e), LOGGER); |
| } |
| if (ret is IExternalConstructor<object>) |
| { |
| ret = ((IExternalConstructor<object>)ret).NewInstance(); |
| } |
| instances.Add(constructor.GetNode(), ret); |
| return ret; |
| } |
| catch (TargetInvocationException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new InjectionException("Could not invoke constructor: " + plan, e), LOGGER); |
| } |
| finally |
| { |
| concurrentModificationGuard = false; |
| } |
| } |
| else if (plan is Subplan) |
| { |
| Subplan ambiguous = (Subplan)plan; |
| return InjectFromPlan(ambiguous.GetDelegatedPlan()); |
| } |
| else if (plan is SetInjectionPlan) |
| { |
| SetInjectionPlan setPlan = (SetInjectionPlan)plan; |
| INode n = setPlan.GetNode(); |
| string typeOfSet = null; |
| |
| // TODO: This doesn't work for sets of generics (e.g., Set<Foo<int>> |
| // because GetFullName and GetFullArgName strip generic info). |
| if (n is INamedParameterNode) |
| { |
| INamedParameterNode np = (INamedParameterNode)n; |
| typeOfSet = np.GetFullArgName(); |
| } |
| else if (n is IClassNode) |
| { |
| typeOfSet = n.GetFullName(); |
| } |
| else |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new TangApplicationException("Unknown node type :" + n.ToString()), LOGGER); |
| } |
| |
| Type t = classHierarchy.ClassForName(typeOfSet); |
| |
| // MakeGenericType(t = int: MonotonicHashSet<> -> MonotonicHashSet<int> |
| // Get constructor: MonotonicHashSet<int> -> public MonotonicHashSet<int>() { ... } |
| // Invoke: public MonotonicHashSet<int> -> new MonotonicHashSet<int>() |
| object ret = typeof(MonotonicHashSet<>).MakeGenericType(t).GetConstructor(new Type[] { }).Invoke(new object[] { }); // (this, classHierarchy.ClassForName(fut.GetNode().GetFullName())); |
| |
| MethodInfo mf = ret.GetType().GetMethod("Add"); |
| |
| foreach (InjectionPlan subplan in setPlan.GetEntryPlans()) |
| { |
| // ret.Add(InjectFromPlan(subplan)); |
| mf.Invoke(ret, new object[] { InjectFromPlan(subplan) }); |
| } |
| return ret; |
| } |
| else if (plan is ListInjectionPlan) |
| { |
| ListInjectionPlan listPlan = (ListInjectionPlan)plan; |
| INode n = listPlan.GetNode(); |
| string typeOfList = null; |
| |
| if (n is INamedParameterNode) |
| { |
| INamedParameterNode np = (INamedParameterNode)n; |
| typeOfList = np.GetFullArgName(); |
| } |
| else if (n is IClassNode) |
| { |
| typeOfList = n.GetFullName(); |
| } |
| else |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new TangApplicationException("Unknown node type :" + n.ToString()), LOGGER); |
| } |
| |
| Type t = classHierarchy.ClassForName(typeOfList); |
| object ret = typeof(List<>).MakeGenericType(t).GetConstructor(new Type[] { }).Invoke(new object[] { }); |
| |
| MethodInfo mf = ret.GetType().GetMethod("Add"); |
| |
| foreach (InjectionPlan subplan in listPlan.GetEntryPlans()) |
| { |
| mf.Invoke(ret, new object[] { InjectFromPlan(subplan) }); |
| } |
| return ret; |
| } |
| else |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Unknown plan type: " + plan), LOGGER); |
| } |
| return null; // should never reach here |
| } |
| |
| private object GetCachedInstance(IClassNode cn) |
| { |
| //// if (cn.GetFullName().Equals(ReflectionUtilities.NonGenericFullName(typeof(IInjector)))) |
| if (cn.GetFullName().Equals(ReflectionUtilities.GetAssemblyQualifiedName(typeof(IInjector)))) |
| { |
| return this.ForkInjector(); // TODO: We should be insisting on injection futures here! .forkInjector(); |
| } |
| else |
| { |
| object t = null; |
| |
| instances.TryGetValue(cn, out t); |
| |
| if (t != null && t is IInjectionFuture<object>) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Found an injection future in getCachedInstance: " + cn), LOGGER); |
| } |
| return t; |
| } |
| } |
| |
| private ConstructorInfo GetConstructor(IConstructorDef constructor) |
| { |
| Type clazz = (Type)this.classHierarchy.ClassForName(constructor.GetClassName()); |
| IConstructorArg[] args = constructor.GetArgs().ToArray(); |
| Type[] parameterTypes = new Type[args.Length]; |
| for (int i = 0; i < args.Length; i++) |
| { |
| if (args[i].IsInjectionFuture()) |
| { |
| parameterTypes[i] = typeof(IInjectionFuture<>).MakeGenericType(new Type[] { this.classHierarchy.ClassForName(args[i].Gettype()) }); |
| } |
| else |
| { |
| parameterTypes[i] = this.classHierarchy.ClassForName(args[i].Gettype()); |
| } |
| } |
| |
| ConstructorInfo cons = clazz.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes, null); |
| |
| //// TODO |
| //// cons.setAccessible(true); |
| |
| if (cons == null) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new TangApplicationException("Failed to look up constructor: " + constructor.ToString()), LOGGER); |
| } |
| return cons; |
| } |
| |
| private void BuildInjectionPlan(INode n, IDictionary<INode, InjectionPlan> memo) |
| { |
| if (memo.ContainsKey(n)) |
| { |
| InjectionPlan p = null; |
| memo.TryGetValue(n, out p); |
| if (BUILDING == p) |
| { |
| StringBuilder loopyList = new StringBuilder("["); |
| foreach (INode node in memo.Keys) |
| { |
| InjectionPlan p1 = null; |
| memo.TryGetValue(node, out p1); |
| if (p1 == BUILDING) |
| { |
| loopyList.Append(" " + node.GetFullName()); |
| } |
| } |
| loopyList.Append(" ]"); |
| var ex = new ClassHierarchyException("Detected loopy constructor involving " |
| + loopyList.ToString()); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| else |
| { |
| return; |
| } |
| } |
| memo.Add(n, BUILDING); |
| InjectionPlan ip = null; |
| if (n is INamedParameterNode) |
| { |
| INamedParameterNode np = (INamedParameterNode)n; |
| object boundInstance = ParseBoundNamedParameter(np); |
| object defaultInstance = this.classHierarchy.ParseDefaultValue(np); |
| object instance = boundInstance != null ? boundInstance : defaultInstance; |
| |
| if (instance is INode) |
| { |
| BuildInjectionPlan((INode)instance, memo); |
| InjectionPlan sp; |
| memo.TryGetValue((INode)instance, out sp); |
| ip = new Subplan(n, 0, new InjectionPlan[] { sp }); |
| } |
| else if (instance is ISet<object>) |
| { |
| ISet<object> entries = (ISet<object>)instance; |
| ip = CreateSetInjectionPlan(n, memo, entries); |
| } |
| else if (instance is ISet<INode>) |
| { |
| ISet<INode> entries = (ISet<INode>)instance; |
| ip = CreateSetInjectionPlan(n, memo, entries); |
| } |
| else if (instance is IList<object>) |
| { |
| IList<object> entries = (IList<object>)instance; |
| ip = CreateListInjectionPlan(n, memo, entries); |
| } |
| else if (instance is IList<INode>) |
| { |
| IList<INode> entries = (IList<INode>)instance; |
| ip = CreateListInjectionPlan(n, memo, entries); |
| } |
| else |
| { |
| ip = new CsInstance(np, instance); |
| } |
| } |
| else if (n is IClassNode) |
| { |
| IClassNode cn = (IClassNode)n; |
| |
| // Any (or all) of the next four values might be null; that's fine. |
| object cached = GetCachedInstance(cn); |
| IClassNode boundImpl = this.configuration.GetBoundImplementation(cn); |
| IClassNode defaultImpl = ParseDefaultImplementation(cn); |
| IClassNode ec = this.configuration.GetBoundConstructor(cn); |
| |
| ip = BuildClassNodeInjectionPlan(cn, cached, ec, boundImpl, defaultImpl, memo); |
| } |
| else if (n is IPackageNode) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new ArgumentException( |
| "Request to instantiate Java package as object"), LOGGER); |
| } |
| else |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException( |
| "Type hierarchy contained unknown node type!:" + n), LOGGER); |
| } |
| memo[n] = ip; |
| } |
| |
| private InjectionPlan CreateSetInjectionPlan<T>(INode n, IDictionary<INode, InjectionPlan> memo, ISet<T> entries) |
| { |
| ISet<InjectionPlan> plans = new MonotonicHashSet<InjectionPlan>(); |
| CreateInjectionPlanForCollectionElements(n, memo, entries, plans); |
| return new SetInjectionPlan(n, plans); |
| } |
| |
| private void CreateInjectionPlanForCollectionElements<T>(INode n, IDictionary<INode, InjectionPlan> memo, ICollection<T> entries, ICollection<InjectionPlan> plans) |
| { |
| foreach (var entry in entries) |
| { |
| if (entry is IClassNode) |
| { |
| BuildInjectionPlan((IClassNode)entry, memo); |
| InjectionPlan p2 = null; |
| memo.TryGetValue((INode)entry, out p2); |
| if (p2 != null) |
| { |
| plans.Add(p2); |
| } |
| } |
| else |
| { |
| plans.Add(new CsInstance(n, entry)); |
| } |
| } |
| } |
| |
| private InjectionPlan CreateListInjectionPlan<T>(INode n, IDictionary<INode, InjectionPlan> memo, IList<T> entries) |
| { |
| IList<InjectionPlan> plans = new List<InjectionPlan>(); |
| CreateInjectionPlanForCollectionElements(n, memo, entries, plans); |
| return new ListInjectionPlan(n, plans); |
| } |
| |
| private InjectionPlan BuildClassNodeInjectionPlan(IClassNode cn, |
| object cachedInstance, |
| IClassNode externalConstructor, |
| IClassNode boundImpl, |
| IClassNode defaultImpl, |
| IDictionary<INode, InjectionPlan> memo) |
| { |
| if (cachedInstance != null) |
| { |
| return new CsInstance(cn, cachedInstance); |
| } |
| else if (externalConstructor != null) |
| { |
| BuildInjectionPlan(externalConstructor, memo); |
| InjectionPlan ip = null; |
| memo.TryGetValue(externalConstructor, out ip); |
| return new Subplan(cn, 0, new InjectionPlan[] { ip }); |
| } |
| else if (boundImpl != null && !cn.Equals(boundImpl)) |
| { |
| // We need to delegate to boundImpl, so recurse. |
| BuildInjectionPlan(boundImpl, memo); |
| InjectionPlan ip = null; |
| memo.TryGetValue(boundImpl, out ip); |
| return new Subplan(cn, 0, new InjectionPlan[] { ip }); |
| } |
| else if (defaultImpl != null && !cn.Equals(defaultImpl)) |
| { |
| BuildInjectionPlan(defaultImpl, memo); |
| InjectionPlan ip = null; |
| memo.TryGetValue(defaultImpl, out ip); |
| return new Subplan(cn, 0, new InjectionPlan[] { ip }); |
| } |
| else |
| { |
| // if we're here and there is a bound impl or a default impl, |
| // then we're bound / defaulted to ourselves, so don't add |
| // other impls to the list of things to consider. |
| List<IClassNode> candidateImplementations = new List<IClassNode>(); |
| candidateImplementations.Add(cn); |
| List<InjectionPlan> sub_ips = FilterCandidateConstructors(candidateImplementations, memo); |
| if (sub_ips.Count == 1) |
| { |
| return WrapInjectionPlans(cn, sub_ips, false, -1); |
| } |
| return WrapInjectionPlans(cn, sub_ips, true, -1); |
| } |
| } |
| |
| private List<InjectionPlan> FilterCandidateConstructors( |
| List<IClassNode> candidateImplementations, |
| IDictionary<INode, InjectionPlan> memo) |
| { |
| List<InjectionPlan> sub_ips = new List<InjectionPlan>(); |
| |
| // each implementation |
| foreach (IClassNode thisCN in candidateImplementations) |
| { |
| List<InjectionPlan> constructors = new List<InjectionPlan>(); |
| List<IConstructorDef> constructorList = new List<IConstructorDef>(); |
| if (this.configuration.GetLegacyConstructor(thisCN) != null) |
| { |
| constructorList.Add(this.configuration.GetLegacyConstructor(thisCN)); |
| } |
| |
| foreach (var c in thisCN.GetInjectableConstructors()) |
| { |
| constructorList.Add(c); |
| } |
| |
| // each constructor |
| foreach (IConstructorDef def in constructorList) |
| { |
| List<InjectionPlan> args = new List<InjectionPlan>(); |
| IConstructorArg[] defArgs = def.GetArgs().ToArray<IConstructorArg>(); |
| |
| // each argument |
| foreach (IConstructorArg arg in defArgs) |
| { |
| if (!arg.IsInjectionFuture()) |
| { |
| try |
| { |
| INode argNode = this.classHierarchy.GetNode(arg.GetName()); |
| BuildInjectionPlan(argNode, memo); |
| InjectionPlan ip = null; |
| memo.TryGetValue(argNode, out ip); |
| args.Add(ip); |
| } |
| catch (NameResolutionException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| |
| var ex = new IllegalStateException("Detected unresolvable " |
| + "constructor arg while building injection plan. " |
| + "This should have been caught earlier!", e); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| } |
| else |
| { |
| try |
| { |
| args.Add(new InjectionFuturePlan(this.classHierarchy.GetNode(arg.GetName()))); |
| } |
| catch (NameResolutionException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| var ex = new IllegalStateException("Detected unresolvable " |
| + "constructor arg while building injection plan. " |
| + "This should have been caught earlier!", e); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| } |
| } |
| |
| Constructor constructor = new Constructor(thisCN, def, args.ToArray()); |
| constructors.Add(constructor); |
| } |
| |
| // The constructors are embedded in a lattice defined by |
| // isMoreSpecificThan(). We want to see if, amongst the injectable |
| // plans, there is a unique dominant plan, and select it. |
| // First, compute the set of injectable plans. |
| List<int> liveIndices = new List<int>(); |
| for (int i = 0; i < constructors.Count; i++) |
| { |
| if (constructors[i].GetNumAlternatives() > 0) |
| { |
| liveIndices.Add(i); |
| } |
| } |
| |
| // Now, do an all-by-all comparison, removing indices that are dominated |
| // by others. |
| int k = -1; |
| for (int i = 0; i < liveIndices.Count; i++) |
| { |
| for (int j = i + 1; j < liveIndices.Count; j++) |
| { |
| IConstructorDef ci = ((Constructor)constructors[liveIndices[i]]).GetConstructorDef(); |
| IConstructorDef cj = ((Constructor)constructors[liveIndices[j]]).GetConstructorDef(); |
| |
| if (ci.IsMoreSpecificThan(cj)) |
| { |
| // ci's arguments is a superset of cj's |
| k = i; |
| } |
| else if (cj.IsMoreSpecificThan(ci)) |
| { |
| k = j; |
| } |
| } |
| } |
| if (liveIndices.Count == 1) |
| { |
| k = 0; |
| } |
| if (constructors.Count > 0) |
| { |
| sub_ips.Add(WrapInjectionPlans(thisCN, constructors, false, k != -1 ? liveIndices[k] : -1)); |
| } |
| } |
| return sub_ips; |
| } |
| |
| private InjectionPlan WrapInjectionPlans(IClassNode infeasibleNode, |
| List<InjectionPlan> list, bool forceAmbiguous, int selectedIndex) |
| { |
| if (list.Count == 0) |
| { |
| return new Subplan(infeasibleNode, new InjectionPlan[] { }); |
| } |
| else if ((!forceAmbiguous) && list.Count == 1) |
| { |
| return list[0]; |
| } |
| else |
| { |
| return new Subplan(infeasibleNode, selectedIndex, list.ToArray()); |
| } |
| } |
| |
| private IClassNode ParseDefaultImplementation(IClassNode cn) |
| { |
| if (cn.GetDefaultImplementation() != null) |
| { |
| try |
| { |
| return (IClassNode)this.classHierarchy.GetNode(cn.GetDefaultImplementation()); |
| } |
| catch (NameResolutionException e) |
| { |
| var ex = new IllegalStateException("After validation, " + cn + " had a bad default implementation named " + cn.GetDefaultImplementation(), e); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.CaughtAndThrow(ex, Level.Error, LOGGER); |
| } |
| } |
| return null; |
| } |
| |
| private object ParseBoundNamedParameter(INamedParameterNode np) |
| { |
| ISet<object> boundSet = this.configuration.GetBoundSet((INamedParameterNode)np); |
| if (boundSet.Count != 0) |
| { |
| ISet<INode> ret3 = new MonotonicSet<INode>(); |
| ISet<object> ret2 = new MonotonicSet<object>(); |
| return ParseElementsInCollection(np, boundSet, ret3, ret2); |
| } |
| |
| IList<object> boundList = this.configuration.GetBoundList((INamedParameterNode)np); |
| if (boundList != null && boundList.Count != 0) |
| { |
| IList<INode> ret3 = new List<INode>(); |
| IList<object> ret2 = new List<object>(); |
| return ParseElementsInCollection(np, boundList, ret3, ret2); |
| } |
| |
| object ret = null; |
| if (namedParameterInstances.ContainsKey(np)) |
| { |
| namedParameterInstances.TryGetValue(np, out ret); |
| } |
| else |
| { |
| string value = this.configuration.GetNamedParameter(np); |
| if (value == null) |
| { |
| ret = null; |
| } |
| else |
| { |
| try |
| { |
| ret = this.classHierarchy.Parse(np, value); |
| namedParameterInstances.Add(np, ret); |
| } |
| catch (BindException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.CaughtAndThrow(new IllegalStateException( |
| "Could not parse pre-validated value", e), Level.Error, LOGGER); |
| } |
| } |
| } |
| return ret; |
| } |
| |
| private object ParseElementsInCollection(INamedParameterNode np, ICollection<object> boundSet, ICollection<INode> ret3, ICollection<object> ret2) |
| { |
| foreach (object o in boundSet) |
| { |
| if (o is string) |
| { |
| try |
| { |
| var r = this.classHierarchy.Parse(np, (string)o); |
| if (r is INode) |
| { |
| ret3.Add((INode)r); |
| } |
| else |
| { |
| ret2.Add(r); |
| } |
| } |
| catch (ParseException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| var ex = |
| new IllegalStateException("Could not parse " + o + " which was passed into " + np + |
| " FIXME: Parsability is not currently checked by bindSetEntry(Node,String)"); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| } |
| else if (o is INode) |
| { |
| var o2 = o as INode; |
| ret3.Add(o2); |
| } |
| else |
| { |
| var ex = |
| new IllegalStateException("Unexpected object " + o + |
| " in bound set. Should consist of nodes and strings"); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| } |
| if (ret2.Count > 0 && ret3.Count == 0) |
| { |
| return ret2; |
| } |
| if (ret3.Count > 0 && ret2.Count == 0) |
| { |
| return ret3; |
| } |
| if (ret2.Count > 0 && ret3.Count > 0) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new TangApplicationException("Set contains different types of object"), LOGGER); |
| } |
| return ret2; |
| } |
| |
| public object GetInstance(Type iface) |
| { |
| return GetInstance(ReflectionUtilities.GetAssemblyQualifiedName(iface)); |
| } |
| |
| public T GetInstance<T>() where T : class |
| { |
| return (T)GetInstance(ReflectionUtilities.GetAssemblyQualifiedName(typeof(T))); |
| } |
| |
| private void CheckNamedParameter(Type t) |
| { |
| if (ReflectionUtilities.IsAssignableFromIgnoreGeneric(typeof(Name<>), t)) |
| { |
| var ex = new InjectionException("GetInstance() called on Name " |
| + ReflectionUtilities.GetName(t) |
| + " Did you mean to call GetNamedInstance() instead?"); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| } |
| |
| private object GetInstance(INode node) |
| { |
| InjectionPlan plan = (InjectionPlan)GetInjectionPlan(node); |
| object u = InjectFromPlan(plan); |
| |
| while (pendingFutures.Count != 0) |
| { |
| IEnumerator<object> i = pendingFutures.GetEnumerator(); |
| i.MoveNext(); |
| IInjectionFuture<object> f = (IInjectionFuture<object>)i.Current; |
| pendingFutures.Remove(f); |
| f.Get(); |
| } |
| return u; |
| } |
| |
| public object GetInstance(string clazz) |
| { |
| CheckNamedParameter(ReflectionUtilities.GetTypeByName(clazz)); |
| return GetInstance(classHierarchy.GetNode(clazz)); |
| } |
| |
| public U GetNamedInstance<T, U>(GenericType<T> clazz) where T : Name<U> |
| { |
| Type t = typeof(T); |
| return (U)GetInstance(classHierarchy.GetNode(t)); |
| } |
| |
| public U GetNamedInstance<T, U>() where T : Name<U> |
| { |
| Type t = typeof(T); |
| return (U)GetInstance(classHierarchy.GetNode(t)); |
| } |
| |
| public object GetNamedInstance(Type t) |
| { |
| if (!ReflectionUtilities.IsAssignableFromIgnoreGeneric(typeof(Name<>), t)) |
| { |
| var ex = new TangApplicationException(string.Format(CultureInfo.CurrentCulture, "The parameter {0} is not inherit from Name<>", t)); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| return GetInstance(classHierarchy.GetNode(t)); |
| } |
| |
| public InjectionPlan GetInjectionPlan(Type name) |
| { |
| return GetInjectionPlan(this.classHierarchy.GetNode(name)); |
| } |
| |
| public InjectionPlan GetInjectionPlan(INode n) |
| { |
| AssertNotConcurrent(); |
| IDictionary<INode, InjectionPlan> memo = new Dictionary<INode, InjectionPlan>(); |
| BuildInjectionPlan(n, memo); |
| |
| InjectionPlan p = null; |
| memo.TryGetValue(n, out p); |
| |
| if (p != null) |
| { |
| return p; |
| } |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new InjectionException("Fail to get injection plan" + n), LOGGER); |
| return null; // this line should be not reached as Throw throws exception |
| } |
| |
| public bool IsInjectable(string name) |
| { |
| return GetInjectionPlan(this.classHierarchy.GetNode(name)).IsInjectable(); |
| } |
| |
| public bool IsParameterSet(string name) |
| { |
| InjectionPlan p = GetInjectionPlan(classHierarchy.GetNode(name)); |
| return p.IsInjectable(); |
| } |
| |
| public bool IsInjectable(Type clazz) |
| { |
| AssertNotConcurrent(); |
| try |
| { |
| return IsInjectable(ReflectionUtilities.GetAssemblyQualifiedName(clazz)); |
| } |
| catch (NameResolutionException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Could not round trip " + clazz + " through ClassHierarchy", e), LOGGER); |
| return false; |
| } |
| } |
| |
| public bool IsParameterSet(Type name) |
| { |
| return IsParameterSet(ReflectionUtilities.GetAssemblyQualifiedName(name)); |
| } |
| |
| public void BindAspect(Aspect a) |
| { |
| if (aspect != null) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new BindException("Attempt to re-bind aspect! old=" + aspect + " new=" + a), LOGGER); |
| } |
| aspect = a; |
| } |
| |
| public Aspect GetAspect() |
| { |
| return aspect; |
| } |
| |
| public IInjector ForkInjector() |
| { |
| try |
| { |
| return ForkInjector(new ConfigurationImpl[0]); |
| } |
| catch (BindException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Error in forking injector.", e), LOGGER); |
| return null; |
| } |
| } |
| |
| public IInjector ForkInjector(params IConfiguration[] configurations) |
| { |
| InjectorImpl ret; |
| ret = Copy(this, configurations); |
| return ret; |
| } |
| |
| private static InjectorImpl Copy(InjectorImpl old, IConfiguration[] configurations) |
| { |
| InjectorImpl injector = null; |
| try |
| { |
| IConfigurationBuilder cb = old.configuration.newBuilder(); |
| foreach (IConfiguration c in configurations) |
| { |
| cb.AddConfiguration(c); |
| } |
| injector = new InjectorImpl(cb.Build()); |
| } |
| catch (BindException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Unexpected error copying configuration!", e), LOGGER); |
| } |
| |
| foreach (IClassNode cn in old.instances.Keys) |
| { |
| if (cn.GetFullName().Equals(ReflectionUtilities.GetAssemblyQualifiedName(typeof(IInjector))) |
| || cn.GetFullName().Equals(ReflectionUtilities.GetAssemblyQualifiedName(typeof(InjectorImpl)))) |
| { |
| // This would imply that we're treating injector as a singleton somewhere. It should be copied fresh each time. |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException(string.Empty), LOGGER); |
| } |
| try |
| { |
| IClassNode new_cn = (IClassNode)injector.classHierarchy.GetNode(cn.GetFullName()); |
| |
| object o = null; |
| old.instances.TryGetValue(cn, out o); |
| if (o != null) |
| { |
| injector.instances.Add(new_cn, o); |
| } |
| } |
| catch (BindException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Could not resolve name " |
| + cn.GetFullName() + " when copying injector", e), LOGGER); |
| } |
| } |
| |
| foreach (INamedParameterNode np in old.namedParameterInstances.Keys) |
| { |
| // if (!builder.namedParameters.containsKey(np)) { |
| object o = null; |
| old.namedParameterInstances.TryGetValue(np, out o); |
| INamedParameterNode new_np = (INamedParameterNode)injector.classHierarchy.GetNode(np.GetFullName()); |
| injector.namedParameterInstances.Add(new_np, o); |
| } |
| |
| // Fork the aspect (if any) |
| if (old.aspect != null) |
| { |
| injector.BindAspect(old.aspect.CreateChildAspect()); |
| } |
| return injector; |
| } |
| |
| void BindVolatileInstanceNoCopy<T>(GenericType<T> c, T o) |
| { |
| AssertNotConcurrent(); |
| INode n = this.classHierarchy.GetNode(typeof(T)); |
| if (n is IClassNode) |
| { |
| IClassNode cn = (IClassNode)n; |
| object old = GetCachedInstance(cn); |
| if (old != null) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new BindException("Attempt to re-bind instance. Old value was " |
| + old + " new value is " + o), LOGGER); |
| } |
| instances.Add(cn, o); |
| } |
| else |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new ArgumentException("Expected Class but got " + c |
| + " (probably a named parameter)."), LOGGER); |
| } |
| } |
| |
| // void BindVolatileParameterNoCopy(Class<? extends Name<T>> c, T o) |
| void BindVolatileParameterNoCopy<U, T>(GenericType<U> c, T o) |
| where U : Name<T> |
| { |
| INode n = this.classHierarchy.GetNode(typeof(U)); |
| if (n is INamedParameterNode) |
| { |
| INamedParameterNode np = (INamedParameterNode)n; |
| object old = this.configuration.GetNamedParameter(np); |
| if (old != null) |
| { |
| // XXX need to get the binding site here! |
| var ex = new BindException( |
| "Attempt to re-bind named parameter " + ReflectionUtilities.GetAssemblyQualifiedName(typeof(U)) + ". Old value was [" + old |
| + "] new value is [" + o + "]"); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| try |
| { |
| namedParameterInstances.Add(np, o); |
| } |
| catch (ArgumentException e) |
| { |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| var ex = new BindException( |
| "Attempt to re-bind named parameter " + ReflectionUtilities.GetAssemblyQualifiedName(typeof(U)) + ". Old value was [" + old |
| + "] new value is [" + o + "]"); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| } |
| else |
| { |
| var ex = new ArgumentException("Expected Name, got " + typeof(U) + " (probably a class)"); |
| Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| } |
| |
| // public <T> void bindVolatileInstance(Class<T> c, T o) throws BindException { |
| public void BindVolatileInstance<T>(GenericType<T> iface, T inst) |
| { |
| BindVolatileInstanceNoCopy(iface, inst); |
| } |
| |
| /// <summary> |
| /// Binds the volatile instance. |
| /// </summary> |
| /// <typeparam name="T"></typeparam> |
| /// <param name="inst">The inst.</param> |
| public void BindVolatileInstance<T>(T inst) |
| { |
| BindVolatileInstance<T>(GenericType<T>.Class, inst); |
| } |
| |
| /// <summary> |
| /// Binds the volatile parameter. |
| /// </summary> |
| /// <typeparam name="U"></typeparam> |
| /// <typeparam name="T"></typeparam> |
| /// <param name="iface">The iface.</param> |
| /// <param name="inst">The inst.</param> |
| public void BindVolatileParameter<U, T>(GenericType<U> iface, T inst) where U : Name<T> |
| { |
| BindVolatileParameterNoCopy(iface, inst); |
| } |
| |
| /// <summary> |
| /// Binds the volatile parameter. |
| /// </summary> |
| /// <typeparam name="U"></typeparam> |
| /// <typeparam name="T"></typeparam> |
| /// <param name="inst">The inst.</param> |
| public void BindVolatileParameter<U, T>(T inst) where U : Name<T> |
| { |
| BindVolatileParameter(GenericType<U>.Class, inst); |
| } |
| |
| ////public T GetNamedParameter<U, T>(GenericType<U> name) where U : Name<T> |
| ////{ |
| //// return (T)GetNamedInstance(typeof(U)); |
| ////} |
| |
| ////public IInjector CreateChildInjector(IConfiguration[] configurations) |
| ////{ |
| //// return ForkInjector(configurations); |
| ////} |
| |
| /// <summary> |
| /// Gets the injection plan for a given class name |
| /// </summary> |
| /// <param name="name">The name.</param> |
| /// <returns></returns> |
| public InjectionPlan GetInjectionPlan(string name) |
| { |
| return GetInjectionPlan(this.classHierarchy.GetNode(name)); |
| } |
| } |
| } |