blob: ac843f8fa74f4c828d2c36fd684ccc58d3307f6c [file] [log] [blame]
// 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));
}
}
}