blob: db7cd6552d5a78364e84d596853e6400d7eefd91 [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;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Reflection;
using System.Text;
using Apache.NMS.AMQP.Transport;
namespace Apache.NMS.AMQP.Util
{
public class PropertyUtil
{
private const string PROPERTY_TERM_SEPARATOR = ".";
public const string PROPERTY_PREFIX = "NMS" + PROPERTY_TERM_SEPARATOR;
public static string CreateProperty(string name, string subprefix = "", string prefix = PROPERTY_PREFIX)
{
string propertyPrefix = prefix +
(prefix.Length > 0 && !prefix.EndsWith(PROPERTY_TERM_SEPARATOR)
?
PROPERTY_TERM_SEPARATOR
:
""
);
string subPropertyTerm = subprefix +
(subprefix.Length > 0 && !subprefix.EndsWith(PROPERTY_TERM_SEPARATOR)
?
PROPERTY_TERM_SEPARATOR
:
""
);
return propertyPrefix + subPropertyTerm + name;
}
public static void SetProperties(object obj, StringDictionary properties, string propertyPrefix = PROPERTY_PREFIX)
{
Dictionary<string, PropertyInfo> props = GetPropertiesForClass(obj);
foreach (string rawkey in properties.Keys)
{
string key = RemovePrefix(propertyPrefix, rawkey);
Tracer.DebugFormat("Searching for Property: \"{0}\"", key);
if (props.ContainsKey(key))
{
Tracer.DebugFormat(
"Assigning Property {0} to {1}.{2} with value {3}",
key, obj.GetType().Namespace, obj.GetType().Name, properties[rawkey]
);
#if NET40
if (props[key].GetSetMethod() != null)
#else
if(props[key].SetMethod!=null)
#endif
props[key].SetValue(obj, ConvertType(props[key].PropertyType, properties[rawkey]), null);
}
}
}
public static StringDictionary GetProperties(object obj, string propertyPrefix = PROPERTY_PREFIX)
{
StringDictionary result = new StringDictionary();
Dictionary<string, PropertyInfo> props = GetPropertiesForClass(obj);
string propsPrefix = propertyPrefix +
(
propertyPrefix.Length > 0 && !propertyPrefix.EndsWith(PROPERTY_TERM_SEPARATOR)
?
PROPERTY_TERM_SEPARATOR
:
""
);
foreach (string key in props.Keys)
{
object value = props[key].GetValue(obj, null);
if (value != null)
{
result[propertyPrefix + key] = value.ToString();
}
}
return result;
}
public static object ConvertType(Type targetType, string value)
{
if (targetType.IsPrimitive)
{
return Convert.ChangeType(value, targetType);
}
else if (targetType.Equals(typeof(string)))
{
return value;
}
return null;
}
private static string RemovePrefix(string prefix, string propertyName)
{
if (propertyName.StartsWith(prefix, StringComparison.CurrentCultureIgnoreCase) && prefix.Length > 0)
{
return propertyName.Remove(0, prefix.Length);
}
else
{
return propertyName;
}
}
public static Dictionary<string, PropertyInfo> GetPropertiesForClass(object obj)
{
MemberInfo[] members = obj.GetType().GetMembers();
Dictionary<string, PropertyInfo> properties = new Dictionary<string, PropertyInfo>();
foreach (MemberInfo member in members)
{
string memberName = member.Name;
if (member != null && member is PropertyInfo)
{
PropertyInfo prop = member as PropertyInfo;
properties.Add(memberName.ToLower(), prop);
}
}
return properties;
}
public static StringDictionary Clone(StringDictionary original)
{
StringDictionary clone = new StringDictionary();
foreach (string key in original.Keys)
{
clone.Add(key?.Clone() as string, original[key]?.Clone() as string);
}
return clone;
}
/// <summary>
/// See <see cref="Merge(StringDictionary, StringDictionary, out StringDictionary, string, string, string)"/>
/// </summary>
/// <param name="one"></param>
/// <param name="other"></param>
/// <param name="onePrefix"></param>
/// <param name="otherPrefix"></param>
/// <param name="mergePrefix"></param>
/// <returns></returns>
public static StringDictionary Merge(
StringDictionary one,
StringDictionary other,
string onePrefix = PROPERTY_PREFIX,
string otherPrefix = PROPERTY_PREFIX,
string mergePrefix = PROPERTY_PREFIX
)
{
StringDictionary d;
return Merge(one, other, out d, onePrefix, otherPrefix, mergePrefix);
}
/// <summary>
/// Takes all properties from one StringDictionary and merges them with the other StringDictionary.
/// The properties in "one" are prefered over the "other".
///
/// </summary>
/// <param name="one">StringDictionary containing properties.</param>
/// <param name="other">Another StringDictionary containing properties.</param>
/// <param name="cross">Holds all the properties from the "other" StringDictionary that are not used because one has the properties.</param>
/// <param name="onePrefix">Optional string prefix for the properties in "one".</param>
/// <param name="otherPrefix">Optional string prefix for the properties in "other".</param>
/// <param name="mergePrefix">Optional string prefix for the properties in result.</param>
/// <returns>Merged StringDictionary with properties from both "one" and "other".</returns>
public static StringDictionary Merge(
StringDictionary one,
StringDictionary other,
out StringDictionary cross,
string onePrefix = PROPERTY_PREFIX,
string otherPrefix = PROPERTY_PREFIX,
string mergePrefix = PROPERTY_PREFIX
)
{
if (one == null && other != null)
{
cross = null;
return Clone(other);
}
else if (one!=null && other == null)
{
cross = null;
return Clone(one);
}
else if (one == null && other == null)
{
cross = null;
return new StringDictionary();
}
StringDictionary result = new StringDictionary();
StringDictionary dups = new StringDictionary();
Array arr = new string[other.Keys.Count];
other.Keys.CopyTo(arr, 0);
ArrayList otherKeys = new ArrayList(arr);
string mPre = mergePrefix +
(
mergePrefix.Length > 0 && !mergePrefix.EndsWith(PROPERTY_TERM_SEPARATOR)
?
PROPERTY_TERM_SEPARATOR
:
""
);
mergePrefix.ToLower();
string onePre = onePrefix +
(
onePrefix.Length > 0 && !onePrefix.EndsWith(PROPERTY_TERM_SEPARATOR)
?
PROPERTY_TERM_SEPARATOR
:
""
);
onePre.ToLower();
string otherPre = otherPrefix +
(
otherPrefix.Length > 0 && !otherPrefix.EndsWith(PROPERTY_TERM_SEPARATOR)
?
PROPERTY_TERM_SEPARATOR
:
""
);
otherPre.ToLower();
foreach (string rawkey in one.Keys)
{
string key = RemovePrefix(onePre, rawkey);
string otherkey = (otherPre + key).ToLower();
string mergekey = (mPre + key).ToLower();
if (!result.ContainsKey(mergekey))
{
result.Add(mergekey, one[rawkey]);
}
if (other.ContainsKey(otherkey))
{
otherKeys.Remove(otherkey);
dups.Add(mergekey, other[otherkey]);
}
}
foreach (string rawkey in otherKeys)
{
string key = RemovePrefix(otherPre, rawkey);
result.Add(mPre + key, other[rawkey]);
}
cross = dups.Count == 0 ? null : dups;
return result;
}
public static string ToString(IList set)
{
if (set == null)
{
return "null";
}
if (set.Count == 0)
{
return "[]";
}
string result = "[";
foreach (object o in set)
{
result += o.ToString() + ",";
}
return result.Substring(0, result.Length - 1) + "]";
}
private static string ToString(ArrayList set)
{
if(set == null)
{
return "null";
}
if(set.Count == 0)
{
return "[]";
}
string result = "[";
foreach(object o in set)
{
result += o.ToString() + ",";
}
return result.Substring(0,result.Length - 1) + "]";
}
public static string ToString(StringDictionary properties)
{
if(properties == null)
{
return "null";
}
if (properties.Count == 0)
{
return "[]";
}
string result = "[\n";
foreach(string s in properties.Keys)
{
result += string.Format("{0} : {1},\n", s, properties[s]);
}
return result.Substring(0,result.Length-2) + "\n]";
}
public static IDictionary<K,V> Clone<K,V>(IDictionary<K,V> dict)
{
if (dict == null) return null;
Dictionary<K, V> clone = new Dictionary<K, V>(dict.Count);
dict.CopyTo(clone.ToArray(), 0);
return clone;
}
public static StringDictionary FilterProperties(StringDictionary properties, string optionPrefix)
{
if (properties == null)
{
throw new ArgumentNullException(nameof(properties), "The given properties object was null.");
}
StringDictionary filtered = new StringDictionary();
foreach (string entry in properties.Keys)
{
if (entry.StartsWith(optionPrefix))
{
string name = entry.Substring(optionPrefix.Length);
filtered.Add(name, properties[entry]);
}
}
return filtered;
}
}
#region abstract Property Interceptor classes
internal abstract class PropertyInterceptor<T> : StringDictionary where T : class
{
protected delegate void ApplyProperty(T instance, string key, string value);
protected delegate string GetProperty(T instance, string key);
protected delegate void ClearProperty(T instance);
protected delegate bool CheckProperty(T instance);
protected struct Interceptor
{
public ApplyProperty Setter;
public GetProperty Getter;
public ClearProperty Reset;
public CheckProperty Exists;
}
private readonly StringDictionary properties;
private readonly IDictionary<string, Interceptor> interceptors;
private readonly T instance;
protected PropertyInterceptor(T instance, StringDictionary properties, IDictionary<string, Interceptor> interceptors) : base()
{
this.properties = properties;
this.instance = instance;
// initialize interceptor map
this.interceptors = new Dictionary<string, Interceptor>();
foreach (string key in interceptors.Keys)
{
AddInterceptor(key, interceptors[key]);
}
}
protected T Instance { get { return instance; } }
protected StringDictionary Properties { get { return properties; } }
protected void AddInterceptor(string key, Interceptor interceptor)
{
bool updated = false;
// add new interceptor
if (!interceptors.ContainsKey(key))
{
this.interceptors.Add(key, interceptor);
}
else
{
// update interceptor
// this allows subs classes to override base classes.
this.interceptors[key] = interceptor;
updated = true;
}
// Remove intercepted properties from base string dictionary
if (properties.ContainsKey(key) || updated)
{
SetProperty(key, properties[key]);
properties.Remove(key);
}
}
protected void AddProperty(string key, string value)
{
if (interceptors.ContainsKey(key))
{
interceptors[key].Setter(instance, key, value);
}
else
{
properties.Add(key, value);
}
}
protected void SetProperty(string key, string value)
{
if (interceptors.ContainsKey(key))
{
interceptors[key].Setter(instance, key, value);
}
else
{
properties[key] = value;
}
}
protected string GetValue(string key)
{
string value = null;
if (interceptors.ContainsKey(key))
{
if (interceptors[key].Exists(instance))
{
value = interceptors[key].Getter(instance, key);
}
}
else
{
value = properties[key];
}
return value;
}
protected int InterceptorCount
{
get
{
int count = 0;
foreach (string key in interceptors.Keys)
{
Interceptor i = interceptors[key];
if (i.Exists(instance))
{
count++;
}
}
return count;
}
}
#region IDictionary<> Methods
public override int Count => this.properties.Count + InterceptorCount;
public override void Add(string key, string value)
{
AddProperty(key, value);
}
public override string this[string key]
{
get
{
return GetValue(key);
}
set
{
SetProperty(key, value);
}
}
public override void Clear()
{
this.properties.Clear();
Interceptor[] set = interceptors.Values.ToArray();
foreach (Interceptor i in set)
{
i.Reset?.Invoke(Instance);
}
}
public override void Remove(string key)
{
if (this.properties.ContainsKey(key))
{
this.properties.Remove(key);
}
else if (this.interceptors.ContainsKey(key))
{
if (this.interceptors[key].Exists(Instance))
{
this.interceptors[key].Reset(Instance);
}
}
}
public override bool ContainsKey(string key)
{
if (this.properties.ContainsKey(key))
{
return true;
}
else if (this.interceptors.ContainsKey(key))
{
return this.interceptors[key].Exists(Instance);
}
return false;
}
public override ICollection Keys
{
get
{
ISet<string> keys = new HashSet<string>();
foreach (string key in interceptors.Keys)
{
Interceptor i = interceptors[key];
if (i.Exists(instance))
{
keys.Add(key);
}
}
foreach (string key in properties.Keys)
{
keys.Add(key);
}
return keys.ToList();
}
}
public override ICollection Values
{
get
{
ISet<object> values = new HashSet<object>();
foreach (string key in interceptors.Keys)
{
Interceptor i = interceptors[key];
if (i.Exists(instance))
{
values.Add(i.Getter(instance, key));
}
}
foreach (object value in properties.Values)
{
values.Add(value);
}
return values.ToList();
}
}
#endregion
public override string ToString()
{
string result = base.ToString();
foreach (string key in Keys)
{
result += "\n" + key.ToString() + ": " + GetValue(key).ToString();
}
return result;
}
}
#region RelfectionPropertyInterceptor
internal abstract class ReflectionPropertyInterceptor<T> : PropertyInterceptor<T> where T : class
{
#region static methods
private static bool DefaultExists(T instance)
{
return true;
}
private static void SetReflectedValue(PropertyInfo info, object instance, string value)
{
if (info.GetSetMethod() != null)
{
object objValue = ParseReflectedValue(info, value);
info.SetValue(instance, objValue, null);
}
}
private static object GetReflectedValue(PropertyInfo info, object instance)
{
object value = null;
if (info.GetGetMethod() != null)
{
value = info.GetValue(instance);
}
return value;
}
private static object ParseReflectedValue(PropertyInfo info, string value)
{
Type targetType = info.PropertyType;
return PropertyUtil.ConvertType(targetType, value);
}
#endregion
protected struct ReflectedInteceptor
{
public PropertyInfo ReflectedProperty;
public Interceptor BaseInterceptor;
}
private Dictionary<string, ReflectedInteceptor> autoInterceptors = null;
#region Contructor
protected ReflectionPropertyInterceptor(T instance, StringDictionary properties, IDictionary<string, Interceptor> interceptors ) : base(instance, properties, interceptors)
{
this.autoInterceptors = CreateReflectionInterceptors(instance);
if (this.autoInterceptors != null)
{
foreach (string propertyName in this.autoInterceptors.Keys)
{
this.AddInterceptor(propertyName, this.autoInterceptors[propertyName].BaseInterceptor);
}
if (Tracer.IsDebugEnabled)
{
StringBuilder sb = new StringBuilder();
#if TRACE
sb.AppendFormat("Generated {0} reflection properties, Properties = [", this.autoInterceptors.Keys.Count);
foreach (string propertyName in this.autoInterceptors.Keys)
{
sb.AppendFormat("\n\t{0},\n", propertyName);
}
sb.AppendFormat("]");
#else
sb.AppendFormat("Generated {0} reflection properties", this.autoInterceptors.Keys.Count);
#endif
Tracer.DebugFormat(sb.ToString());
}
}
}
#endregion
#region abstract Properties
protected abstract string PropertyPrefix { get; }
#endregion
protected abstract bool CanReflect(PropertyInfo reflectedProperty);
#region Private Methods
private Interceptor? CreateReflectedInterceptor(PropertyInfo reflectedProperty)
{
Interceptor? result = null;
if (reflectedProperty.GetSetMethod() == null)
{
}
else
{
ApplyProperty reflectedSetter = (inst, key, value) =>
{
SetReflectedValue(reflectedProperty, inst, value);
};
GetProperty reflectedGetter = (inst, key) =>
{
return GetReflectedValue(reflectedProperty, inst)?.ToString();
};
ClearProperty reflectedClear = (inst) =>
{
SetReflectedValue(reflectedProperty, inst, null);
};
CheckProperty reflectedExists = DefaultExists;
result = new Interceptor()
{
Getter = reflectedGetter,
Setter = reflectedSetter,
Reset = reflectedClear,
Exists = reflectedExists
};
}
return result;
}
private Dictionary<string, ReflectedInteceptor> CreateReflectionInterceptors(T instance)
{
Dictionary<string, PropertyInfo> objProperties = PropertyUtil.GetPropertiesForClass(instance);
Dictionary<string, ReflectedInteceptor> result = new Dictionary<string, ReflectedInteceptor>();
if (Tracer.IsDebugEnabled)
{
List<string> stringPropertyNames = new List<string>(objProperties.Keys.Count);
foreach (string pName in objProperties.Keys)
{
string propertyName = this.PropertyPrefix + pName;
stringPropertyNames.Add(propertyName);
}
Tracer.DebugFormat("Creating reflection interceptors for Class instance {0}, Generating Properties = {1}", instance.GetType().Name, PropertyUtil.ToString(stringPropertyNames));
}
foreach (string key in objProperties.Keys)
{
string propertyName = this.PropertyPrefix + key;
PropertyInfo info = objProperties[key];
if (!CanReflect(info)) continue;
//MethodInfo propGetter = info.GetGetMethod();
Interceptor? reflectedInterceptor = CreateReflectedInterceptor(info);
if (reflectedInterceptor != null)
{
Interceptor i = (Interceptor)reflectedInterceptor;
ReflectedInteceptor ri = new ReflectedInteceptor()
{
ReflectedProperty = info,
BaseInterceptor = i,
};
result.Add(propertyName, ri);
}
}
return result;
}
#endregion
}
#endregion // end abstract reflection interception
#endregion
#region Transport Property Interceptor class
internal class SecureTransportPropertyInterceptor : TransportPropertyInterceptor
{
internal const string SSL_PROTOCOLS_PROPERTY = "transport." + "SSLProtocol";
private static bool ValidateProtocolString(string protocolString, out string cleanProtocols)
{
const string COMMA = ",";
cleanProtocols = protocolString;
if (protocolString != null)
{
string trim = protocolString.Trim();
if(trim.StartsWith(COMMA) || trim.EndsWith(COMMA))
{
return false;
}
cleanProtocols = trim;
}
return true;
}
protected static Dictionary<string, Interceptor> secureTransportConextInterceptors = new Dictionary<string, Interceptor>()
{
{
SSL_PROTOCOLS_PROPERTY,
new Interceptor()
{
Getter = (context, key) =>
{
return (context as ISecureTransportContext).SSLProtocol;
},
Setter = (context, key, value) =>
{
string cleanValue;
if(!ValidateProtocolString(value, out cleanValue))
{
throw new InvalidPropertyException(key, "Protocol string can not start or end with ','");
}
else
{
(context as ISecureTransportContext).SSLProtocol = cleanValue;
}
},
Exists = (context) =>
{
return true;
},
Reset = (context) =>
{
(context as ISecureTransportContext).SSLProtocol = null;
}
}
}
};
public SecureTransportPropertyInterceptor(ISecureTransportContext context, StringDictionary props ) : base(context, props)
{
foreach(string key in secureTransportConextInterceptors.Keys)
{
this.AddInterceptor(key, secureTransportConextInterceptors[key]);
}
}
protected override bool CanReflect(PropertyInfo reflectedProperty)
{
Type TargetType = reflectedProperty.PropertyType;
return TargetType.IsPrimitive || TargetType.Equals(typeof(string));
}
}
internal class TransportPropertyInterceptor : ReflectionPropertyInterceptor<ITransportContext>
{
protected static Dictionary<string, Interceptor> transportContextInterceptors = new Dictionary<string, Interceptor>()
{
};
public TransportPropertyInterceptor(ITransportContext c, StringDictionary transportProperties) : base(c, transportProperties, transportContextInterceptors)
{
}
protected override string PropertyPrefix => "transport.";
protected override bool CanReflect(PropertyInfo reflectedProperty)
{
return true;
}
}
#endregion
}