blob: 7701a1f5706dbb790a1c6ae23755f300d1f0865b [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.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);
}
}
}
}