| // 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.Reflection; |
| using System.Text; |
| using Org.Apache.REEF.Tang.Annotations; |
| using Org.Apache.REEF.Tang.Exceptions; |
| using Org.Apache.REEF.Tang.Implementations.Tang; |
| using Org.Apache.REEF.Tang.Interface; |
| using Org.Apache.REEF.Tang.Util; |
| using Org.Apache.REEF.Utilities.Logging; |
| |
| namespace Org.Apache.REEF.Tang.Formats |
| { |
| public class ConfigurationModuleBuilder |
| { |
| public readonly ICsConfigurationBuilder B = TangFactory.GetTang().NewConfigurationBuilder(); |
| public readonly MonotonicHashSet<FieldInfo> ReqDecl = new MonotonicHashSet<FieldInfo>(); |
| public readonly MonotonicHashSet<FieldInfo> OptDecl = new MonotonicHashSet<FieldInfo>(); |
| public readonly MonotonicHashSet<object> SetOpts = new MonotonicHashSet<object>(); |
| public readonly MonotonicHashMap<object, FieldInfo> Map = new MonotonicHashMap<object, FieldInfo>(); |
| public readonly MonotonicHashMap<Type, object> FreeImpls = new MonotonicHashMap<Type, object>(); |
| public readonly MonotonicHashMap<Type, object> FreeParams = new MonotonicHashMap<Type, object>(); // Type must extends from Name<> |
| |
| private static readonly Logger LOGGER = Logger.GetLogger(typeof(ConfigurationModuleBuilder)); |
| private static readonly ISet<Type> ParamBlacklist = new MonotonicHashSet<Type>(new Type[] { typeof(IParam<>), typeof(IImpl<>) }); |
| private static readonly ISet<string> ParamTypes = |
| new MonotonicHashSet<string>(new string[] { typeof(RequiredImpl<>).Name, typeof(OptionalImpl<>).Name, typeof(RequiredParameter<>).Name, typeof(OptionalParameter<>).Name }); |
| |
| private readonly MonotonicHashSet<FieldInfo> reqUsed = new MonotonicHashSet<FieldInfo>(); |
| private readonly MonotonicHashSet<FieldInfo> optUsed = new MonotonicHashSet<FieldInfo>(); |
| private readonly MonotonicHashMap<Type, string> lateBindClazz = new MonotonicHashMap<Type, string>(); |
| |
| public ConfigurationModuleBuilder() |
| { |
| foreach (FieldInfo f in GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)) |
| { |
| Type t = f.FieldType; |
| if (ParamBlacklist.Contains(t)) |
| { |
| var e = new ClassHierarchyException( |
| "Found a field of type " + t + " which should be a Required/Optional Parameter/Implementation instead"); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| if (ParamTypes.Contains(t.Name)) |
| { |
| if (!f.IsPublic) |
| { |
| var e = new ClassHierarchyException("Found a non-public configuration option in " + GetType() + ": " + f); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| |
| if (!f.IsStatic) |
| { |
| var e = new ClassHierarchyException("Found a non-static configuration option in " + GetType() + ": " + f); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| if (!f.IsInitOnly) |
| { |
| var e = new ClassHierarchyException("Found a non-readonly configuration option in " + GetType() + ": " + f); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| object o = null; |
| try |
| { |
| o = f.GetValue(null); |
| } |
| catch (ArgumentException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| var ex = new ClassHierarchyException("Could not look up field instance in " + GetType() + " field: " + f, e); |
| Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| catch (FieldAccessException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| var ex = new ClassHierarchyException("Could not look up field instance in " + GetType() + " field: " + f, e); |
| Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| |
| if (Map.ContainsKey(o)) |
| { |
| FieldInfo fi; |
| Map.TryGetValue(o, out fi); |
| var e = new ClassHierarchyException("Detected aliased instances in class " + GetType() + " for fields " + fi + " and " + f); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| if (ReflectionUtilities.IsGenericTypeof(typeof(RequiredImpl<>), t) || ReflectionUtilities.IsGenericTypeof(typeof(RequiredParameter<>), t)) |
| { |
| ReqDecl.Add(f); |
| } |
| else |
| { |
| OptDecl.Add(f); |
| } |
| Map.Add(o, f); |
| } |
| } |
| } |
| |
| private ConfigurationModuleBuilder(ConfigurationModuleBuilder c) |
| { |
| try |
| { |
| B.AddConfiguration(c.B.Build()); |
| } |
| catch (BindException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| var ex = new ClassHierarchyException("Build error in ConfigurationModuleBuilder: " + e); |
| Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| ReqDecl.UnionWith(c.ReqDecl); |
| OptDecl.UnionWith(c.OptDecl); |
| reqUsed.UnionWith(c.reqUsed); |
| optUsed.UnionWith(c.optUsed); |
| SetOpts.UnionWith(c.SetOpts); |
| Map.AddAll(c.Map); |
| FreeImpls.AddAll(c.FreeImpls); |
| FreeParams.AddAll(c.FreeParams); |
| lateBindClazz.AddAll(c.lateBindClazz); |
| } |
| |
| public ConfigurationModuleBuilder Merge(ConfigurationModule d) |
| { |
| if (d == null) |
| { |
| var e = new NullReferenceException("If merge() was passed a static final field that is initialized to non-null, then this is almost certainly caused by a circular class dependency."); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| try |
| { |
| d.AssertStaticClean(); |
| } |
| catch (ClassHierarchyException ex) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(ex, Level.Error, LOGGER); |
| var e = new ClassHierarchyException(ReflectionUtilities.GetAssemblyQualifiedName(GetType()) + ": detected attempt to merge with ConfigurationModule that has had set() called on it", ex); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| ConfigurationModuleBuilder c = DeepCopy(); |
| try |
| { |
| c.B.AddConfiguration(d.Builder.B.Build()); |
| } |
| catch (BindException ex) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(ex, Level.Error, LOGGER); |
| var e = new ClassHierarchyException("Error in AddConfiguration in Merge: " + ex); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| c.ReqDecl.AddAll(d.Builder.ReqDecl); |
| c.OptDecl.AddAll(d.Builder.OptDecl); |
| c.reqUsed.AddAll(d.Builder.reqUsed); |
| c.optUsed.AddAll(d.Builder.optUsed); |
| c.SetOpts.AddAll(d.Builder.SetOpts); |
| //// c.ListOpts.AddAll(d.Builder.ListOpts); |
| c.Map.AddAll(d.Builder.Map); |
| c.FreeImpls.AddAll(d.Builder.FreeImpls); |
| c.FreeParams.AddAll(d.Builder.FreeParams); |
| c.lateBindClazz.AddAll(d.Builder.lateBindClazz); |
| |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindSetEntry<U, T>(GenericType<U> iface, string impl) |
| where U : Name<ISet<T>> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| try |
| { |
| ICsInternalConfigurationBuilder b = (ICsInternalConfigurationBuilder)c.B; |
| b.BindSetEntry(typeof(U), impl); |
| } |
| catch (BindException ex) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(ex, Level.Error, LOGGER); |
| var e = new ClassHierarchyException("Error in BindSetEntry: " + ex); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindSetEntry<U, V, T>(GenericType<U> iface, GenericType<V> impl) |
| where U : Name<ISet<T>> |
| where V : T |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| try |
| { |
| c.B.BindSetEntry<U, V, T>(iface, impl); |
| } |
| catch (BindException ex) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(ex, Level.Error, LOGGER); |
| var e = new ClassHierarchyException("Error in BindSetEntry: " + ex); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindSetEntry<U, T>(GenericType<U> iface, IImpl<T> opt) |
| where U : Name<ISet<T>> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| Type ifaceType = typeof(U); |
| |
| c.ProcessUse(opt); |
| c.FreeImpls.Add(ifaceType, opt); |
| |
| if (!SetOpts.Contains(opt)) |
| { |
| c.SetOpts.Add(opt); |
| } |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindSetEntry<U, T>(GenericType<U> iface, IParam<T> opt) |
| where U : Name<ISet<T>> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| Type ifaceType = typeof(U); |
| c.ProcessUse(opt); |
| |
| c.FreeParams.Add(ifaceType, opt); |
| if (!SetOpts.Contains(opt)) |
| { |
| c.SetOpts.Add(opt); |
| } |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindList<U, T>(GenericType<U> iface, IList<string> impl) |
| where U : Name<IList<T>> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| try |
| { |
| ICsInternalConfigurationBuilder b = (ICsInternalConfigurationBuilder)c.B; |
| b.BindList(typeof(U), impl); |
| } |
| catch (BindException ex) |
| { |
| Utilities.Diagnostics.Exceptions.CaughtAndThrow(new ClassHierarchyException("Error in BindList: " + ex), Level.Error, LOGGER); |
| } |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindList<U, T>(GenericType<U> iface, IImpl<IList<T>> opt) |
| where U : Name<IList<T>> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| Type ifaceType = typeof(U); |
| |
| c.ProcessUse(opt); |
| c.FreeImpls.Add(ifaceType, opt); |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindList<U, T>(GenericType<U> iface, IParam<IList<T>> opt) |
| where U : Name<IList<T>> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| Type ifaceType = typeof(U); |
| c.ProcessUse(opt); |
| |
| c.FreeParams.Add(ifaceType, opt); |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindImplementation<U, T>(GenericType<U> iface, GenericType<T> impl) |
| where T : U |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| try |
| { |
| c.B.BindImplementation(iface, impl); |
| } |
| catch (BindException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Utilities.Diagnostics.Exceptions.Throw(new ClassHierarchyException("Error in BindImplementation: ", e), LOGGER); |
| } |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindImplementation<T>(GenericType<T> iface, string impl) |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| |
| c.lateBindClazz.Add(typeof(T), impl); |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindImplementation<U, T>(GenericType<T> iface, IImpl<U> opt) |
| where U : T |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| c.ProcessUse(opt); |
| Type ifaceType = typeof(T); |
| c.FreeImpls.Add(ifaceType, opt); |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindNamedParameter<U, T>(GenericType<U> name, string value) |
| where U : Name<T> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| try |
| { |
| c.B.BindNamedParameter<U, T>(name, value); |
| } |
| catch (BindException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Utilities.Diagnostics.Exceptions.Throw(new ClassHierarchyException("Error in BindNamedParameter: ", e), LOGGER); |
| } |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindNamedParameter<U, T>(GenericType<U> name, IParam<T> opt) |
| where U : Name<T> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| c.ProcessUse(opt); |
| Type nameType = typeof(U); |
| c.FreeParams.Add(nameType, opt); |
| return c; |
| } |
| |
| // public final <T> ConfigurationModuleBuilder bindNamedParameter(Class<? extends Name<T>> iface, Class<? extends T> impl) |
| // if V is T, you'd better to use public ConfigurationModuleBuilder BindNamedParameter<U, T>(GenericType<U> iface, GenericType<T> impl) defined below |
| public ConfigurationModuleBuilder BindNamedParameter<U, V, T>(GenericType<U> iface, GenericType<V> impl) |
| where U : Name<T> |
| where V : T |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| try |
| { |
| c.B.BindNamedParameter<U, V, T>(iface, impl); |
| } |
| catch (BindException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Utilities.Diagnostics.Exceptions.Throw(new ClassHierarchyException("Error in BindNamedParameter: ", e), LOGGER); |
| } |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindNamedParameter<U, T>(GenericType<U> iface, GenericType<T> impl) |
| where U : Name<T> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| try |
| { |
| c.B.BindNamedParameter<U, T, T>(iface, impl); |
| } |
| catch (BindException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Utilities.Diagnostics.Exceptions.Throw(new ClassHierarchyException("Error in BindNamedParameter: ", e), LOGGER); |
| } |
| return c; |
| } |
| |
| // public final <T> ConfigurationModuleBuilder bindNamedParameter(Class<? extends Name<T>> iface, Impl<? extends T> opt) |
| // if ValueType is T, you would better to use public ConfigurationModuleBuilder BindNamedParameter<U, T>(GenericType<U> iface, IImpl<T> opt) |
| public ConfigurationModuleBuilder BindNamedParameter<U, V, T>(GenericType<U> iface, IImpl<V> opt) |
| where U : Name<T> |
| where V : T |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| c.ProcessUse(opt); |
| Type ifaceType = typeof(U); |
| c.FreeImpls.Add(ifaceType, opt); |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindNamedParameter<U, T>(GenericType<U> iface, IImpl<T> opt) |
| where U : Name<T> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| c.ProcessUse(opt); |
| Type ifaceType = typeof(U); |
| c.FreeImpls.Add(ifaceType, opt); |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindConstructor<T, U>(GenericType<T> clazz, GenericType<U> constructor) |
| where U : IExternalConstructor<T> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| try |
| { |
| c.B.BindConstructor<T, U>(clazz, constructor); |
| } |
| catch (BindException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| Utilities.Diagnostics.Exceptions.Throw(new ClassHierarchyException("Error in BindConstructor: ", e), LOGGER); |
| } |
| return c; |
| } |
| |
| public ConfigurationModuleBuilder BindConstructor<T, U>(GenericType<T> cons, IImpl<U> v) |
| where U : IExternalConstructor<T> |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| c.ProcessUse(v); |
| Type consType = typeof(T); |
| var i = (IImpl<object>)v; |
| c.FreeImpls.Add(consType, i); |
| return c; |
| } |
| |
| public ConfigurationModule Build() |
| { |
| ConfigurationModuleBuilder c = DeepCopy(); |
| |
| if (!(c.reqUsed.ContainsAll(c.ReqDecl) && c.optUsed.ContainsAll(c.OptDecl))) |
| { |
| ISet<FieldInfo> fset = new MonotonicHashSet<FieldInfo>(); |
| foreach (FieldInfo f in c.ReqDecl) |
| { |
| if (!c.reqUsed.Contains(f)) |
| { |
| fset.Add(f); |
| } |
| } |
| foreach (FieldInfo f in c.OptDecl) |
| { |
| if (!c.optUsed.Contains(f)) |
| { |
| fset.Add(f); |
| } |
| } |
| var e = new ClassHierarchyException( |
| "Found declared options that were not used in binds: " |
| + ToString(fset)); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| foreach (Type clz in c.lateBindClazz.Keys) |
| { |
| try |
| { |
| c.B.Bind(ReflectionUtilities.GetAssemblyQualifiedName(clz), c.lateBindClazz.Get(clz)); |
| } |
| catch (NameResolutionException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| var ex = new ClassHierarchyException("ConfigurationModule refers to unknown class: " + c.lateBindClazz.Get(clz), e); |
| Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| catch (BindException e) |
| { |
| Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); |
| var ex = new ClassHierarchyException("bind failed while initializing ConfigurationModuleBuilder", e); |
| Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); |
| } |
| } |
| return new ConfigurationModule(c); |
| } |
| |
| public ConfigurationModuleBuilder DeepCopy() |
| { |
| return new ConfigurationModuleBuilder(this); |
| } |
| |
| public string ToString(ISet<FieldInfo> s) |
| { |
| StringBuilder sb = new StringBuilder("{"); |
| bool first = true; |
| foreach (FieldInfo f in s) |
| { |
| sb.Append((first ? " " : ", ") + f.Name); |
| first = false; |
| } |
| sb.Append(" }"); |
| return sb.ToString(); |
| } |
| |
| private void ProcessUse(object impl) |
| { |
| FieldInfo f; |
| Map.TryGetValue(impl, out f); |
| if (f == null) |
| { |
| var e = new ClassHierarchyException("Unknown Impl/Param when binding " + impl.GetType().Name + ". Did you pass in a field from some other module?"); |
| Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); |
| } |
| if (!reqUsed.Contains(f)) |
| { |
| reqUsed.Add(f); |
| } |
| if (!optUsed.Contains(f)) |
| { |
| optUsed.Add(f); |
| } |
| } |
| } |
| } |