Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jgroups.stack.Configurator Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
package org.jgroups.stack;
import org.jgroups.Event;
import org.jgroups.Global;
import org.jgroups.annotations.DeprecatedProperty;
import org.jgroups.annotations.LocalAddress;
import org.jgroups.annotations.Property;
import org.jgroups.conf.PropertyHelper;
import org.jgroups.conf.ProtocolConfiguration;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.util.StackType;
import org.jgroups.util.Util;
import org.w3c.dom.Node;
import java.lang.reflect.*;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.stream.Collectors;
/**
* The task if this class is to setup and configure the protocol stack. A string describing
* the desired setup, which is both the layering and the configuration of each layer, is
* given to the configurator which creates and configures the protocol stack and returns
* a reference to the top layer (Protocol).
* Future functionality will include the capability to dynamically modify the layering
* of the protocol stack and the properties of each layer.
* @author Bela Ban
* @author Richard Achmatowicz
*/
public class Configurator {
protected static final Log log=LogFactory.getLog(Configurator.class);
private final ProtocolStack stack;
public Configurator() {
stack=null;
}
public Configurator(ProtocolStack protocolStack) {
stack=protocolStack;
}
public Protocol setupProtocolStack(List config) throws Exception {
return setupProtocolStack(config, stack);
}
public Protocol setupProtocolStack(ProtocolStack copySource) throws Exception {
List protocols=copySource.copyProtocols(stack);
Collections.reverse(protocols);
return connectProtocols(protocols);
}
/**
* The configuration string has a number of entries, separated by a ':' (colon).
* Each entry consists of the name of the protocol, followed by an optional configuration
* of that protocol. The configuration is enclosed in parentheses, and contains entries
* which are name/value pairs connected with an assignment sign (=) and separated by
* a semicolon.
* UDP(in_port=5555;out_port=4445):FRAG(frag_size=1024)
* The first entry defines the bottommost layer, the string is parsed
* left to right and the protocol stack constructed bottom up. Example: the string
* "UDP(in_port=5555):FRAG(frag_size=32000):DEBUG" results is the following stack:
*
* -----------------------
* | DEBUG |
* |-----------------------|
* | FRAG frag_size=32000 |
* |-----------------------|
* | UDP in_port=32000 |
* -----------------------
*
*/
public static Protocol setupProtocolStack(List protocol_configs, ProtocolStack st) throws Exception {
List protocols=createProtocols(protocol_configs, st);
if(protocols == null)
return null;
// check InetAddress related features of stack
Map> inetAddressMap = createInetAddressMap(protocol_configs, protocols) ;
Collection addrs=getAddresses(inetAddressMap);
StackType ip_version=Util.getIpStackType();
if(!addrs.isEmpty()) {
// check that all user-supplied InetAddresses have a consistent version:
// 1. If an addr is IPv6 and we have an IPv4 stack *only* --> FAIL
for(InetAddress addr: addrs) {
if(addr instanceof Inet6Address && ip_version == StackType.IPv4 && !Util.isIpv6StackAvailable())
throw new IllegalArgumentException("found IPv6 address " + addr + " in an IPv4-only stack");
// if(addr instanceof Inet4Address && addr.isMulticastAddress() && ip_version == StackType.IPv6)
// throw new Exception("found IPv4 multicast address " + addr + " in an IPv6 stack");
}
}
// process default values
setDefaultValues(protocol_configs, protocols, ip_version);
ensureValidBindAddresses(protocols);
// Fixes NPE with concurrent channel creation when using a shared stack (https://issues.jboss.org/browse/JGRP-1488)
Protocol top_protocol=protocols.get(protocols.size() - 1);
top_protocol.setUpProtocol(st);
return connectProtocols(protocols);
}
public static void setDefaultValues(List protocols) throws Exception {
if(protocols == null)
return;
// check InetAddress related features of stack
Collection addrs=getInetAddresses(protocols);
StackType ip_version=Util.getIpStackType(); // 0 = n/a, 4 = IPv4, 6 = IPv6
if(!addrs.isEmpty()) {
// check that all user-supplied InetAddresses have a consistent version:
// 1. If an addr is IPv6 and we have an IPv4 stack --> FAIL
// 2. If an address is an IPv4 class D (multicast) address and the stack is IPv6: FAIL
// Else pass
for(InetAddress addr : addrs) {
if(addr instanceof Inet6Address && ip_version == StackType.IPv4)
throw new IllegalArgumentException("found IPv6 address " + addr + " in an IPv4 stack");
if(addr instanceof Inet4Address && addr.isMulticastAddress() && ip_version == StackType.IPv6)
throw new Exception("found IPv4 multicast address " + addr + " in an IPv6 stack");
}
}
// process default values
setDefaultValues(protocols, ip_version);
}
/**
* Creates a new protocol given the protocol specification. Initializes the properties and starts the
* up and down handler threads.
* @param prot_spec The specification of the protocol. Same convention as for specifying a protocol stack.
* An exception will be thrown if the class cannot be created. Example:
* "VERIFY_SUSPECT(timeout=1500)" Note that no colons (:) have to be
* specified
* @param stack The protocol stack
* @return Protocol The newly created protocol
* @exception Exception Will be thrown when the new protocol cannot be created
*/
public static Protocol createProtocol(String prot_spec, ProtocolStack stack) throws Exception {
ProtocolConfiguration config;
Protocol prot;
if(prot_spec == null) throw new Exception("Configurator.createProtocol(): prot_spec is null");
// parse the configuration for this protocol
config=new ProtocolConfiguration(prot_spec);
// create an instance of the protocol class and configure it
prot=createLayer(stack, config);
prot.init();
return prot;
}
/* ------------------------------- Private Methods ------------------------------------- */
/**
* Creates a protocol stack by iterating through the protocol list and connecting
* adjacent layers. The list starts with the topmost layer and has the bottommost
* layer at the tail.
* @param protocol_list List of Protocol elements (from top to bottom)
* @return Protocol stack
*/
public static Protocol connectProtocols(List protocol_list) throws Exception {
Protocol current_layer=null, next_layer=null;
for(int i=0; i < protocol_list.size(); i++) {
current_layer=protocol_list.get(i);
if(i + 1 >= protocol_list.size())
break;
next_layer=protocol_list.get(i + 1);
next_layer.setDownProtocol(current_layer);
current_layer.setUpProtocol(next_layer);
}
// basic protocol sanity check
sanityCheck(protocol_list);
return current_layer;
}
/**
* Takes vector of ProtocolConfigurations, iterates through it, creates Protocol for
* each ProtocolConfiguration and returns all Protocols in a list.
* @param protocol_configs List of ProtocolConfigurations
* @param stack The protocol stack
* @return List of Protocols
*/
public static List createProtocols(List protocol_configs, final ProtocolStack stack) throws Exception {
List retval=new LinkedList<>();
for(int i=0; i < protocol_configs.size(); i++) {
ProtocolConfiguration protocol_config=protocol_configs.get(i);
Protocol layer=createLayer(stack, protocol_config);
if(layer == null)
return null;
retval.add(layer);
}
return retval;
}
protected static Protocol createLayer(ProtocolStack stack, ProtocolConfiguration config) throws Exception {
String protocol_name=config.getProtocolName();
Map properties=new HashMap<>(config.getProperties());
Protocol retval=null;
if(protocol_name == null || properties == null)
return null;
String defaultProtocolName=ProtocolConfiguration.protocol_prefix + '.' + protocol_name;
Class> clazz=null;
try {
clazz=Util.loadClass(defaultProtocolName, stack != null? stack.getClass() : null);
}
catch(ClassNotFoundException e) {
}
if(clazz == null) {
try {
clazz=Util.loadClass(protocol_name, config.getClassLoader());
}
catch(ClassNotFoundException e) {
}
if(clazz == null)
throw new Exception(String.format(Util.getMessage("ProtocolLoadError"), protocol_name, defaultProtocolName));
}
try {
retval=(Protocol)clazz.newInstance();
if(stack != null)
retval.setProtocolStack(stack);
removeDeprecatedProperties(retval, properties);
// before processing Field and Method based properties, take dependencies specified
// with @Property.dependsUpon into account
AccessibleObject[] dependencyOrderedFieldsAndMethods = computePropertyDependencies(retval, properties) ;
for(AccessibleObject ordered: dependencyOrderedFieldsAndMethods) {
if (ordered instanceof Field) {
resolveAndAssignField(retval, (Field)ordered, properties) ;
}
else if (ordered instanceof Method) {
resolveAndInvokePropertyMethod(retval, (Method)ordered, properties) ;
}
}
List additional_objects=retval.getConfigurableObjects();
if(additional_objects != null && !additional_objects.isEmpty()) {
for(Object obj: additional_objects) {
resolveAndAssignFields(obj, properties);
resolveAndInvokePropertyMethods(obj, properties);
}
}
if(!properties.isEmpty())
throw new IllegalArgumentException(String.format(Util.getMessage("ConfigurationError"), protocol_name, properties));
// if we have protocol-specific XML configuration, pass it to the protocol
List subtrees=config.getSubtrees();
if(subtrees != null) {
for(Node node: subtrees)
retval.parse(node);
}
}
catch(InstantiationException inst_ex) {
throw new InstantiationException(String.format(Util.getMessage("ProtocolCreateError"), protocol_name, inst_ex.getLocalizedMessage()));
}
return retval;
}
/**
Throws an exception if sanity check fails. Possible sanity check is uniqueness of all protocol names
*/
public static void sanityCheck(List protocols) throws Exception {
// check for unique IDs
Set ids=new HashSet<>();
for(Protocol protocol: protocols) {
short id=protocol.getId();
if(id > 0 && !ids.add(id))
throw new Exception("Protocol ID " + id + " (name=" + protocol.getName() +
") is duplicate; protocol IDs have to be unique");
}
// For each protocol, get its required up and down services and check (if available) if they're satisfied
for(Protocol protocol: protocols) {
List required_down_services=protocol.requiredDownServices();
List required_up_services=protocol.requiredUpServices();
if(required_down_services != null && !required_down_services.isEmpty()) {
// the protocols below 'protocol' have to provide the services listed in required_down_services
List tmp=new ArrayList<>(required_down_services);
removeProvidedUpServices(protocol, tmp);
if(!tmp.isEmpty())
throw new Exception("events " + printEvents(tmp) + " are required by " + protocol.getName() +
", but not provided by any of the protocols below it");
}
if(required_up_services != null && !required_up_services.isEmpty()) {
// the protocols above 'protocol' have to provide the services listed in required_up_services
List tmp=new ArrayList<>(required_up_services);
removeProvidedDownServices(protocol, tmp);
if(!tmp.isEmpty())
throw new Exception("events " + printEvents(tmp) + " are required by " + protocol.getName() +
", but not provided by any of the protocols above it");
}
}
}
protected static String printEvents(List events) {
return events.stream().map(Event::type2String).collect(Collectors.joining(" " , "[", "]"));
}
/**
* Removes all events provided by the protocol below protocol from events
* @param protocol
* @param events
*/
protected static void removeProvidedUpServices(Protocol protocol, List events) {
if(protocol == null || events == null)
return;
for(Protocol prot=protocol.getDownProtocol(); prot != null && !events.isEmpty(); prot=prot.getDownProtocol()) {
List provided_up_services=prot.providedUpServices();
if(provided_up_services != null && !provided_up_services.isEmpty())
events.removeAll(provided_up_services);
}
}
/**
* Removes all events provided by the protocol above protocol from events
* @param protocol
* @param events
*/
protected static void removeProvidedDownServices(Protocol protocol, List events) {
if(protocol == null || events == null)
return;
for(Protocol prot=protocol.getUpProtocol(); prot != null && !events.isEmpty(); prot=prot.getUpProtocol()) {
List provided_down_services=prot.providedDownServices();
if(provided_down_services != null && !provided_down_services.isEmpty())
events.removeAll(provided_down_services);
}
}
/**
* Returns all inet addresses found
*/
public static Collection getAddresses(Map> inetAddressMap) throws Exception {
Set addrs=new HashSet<>();
for(Map.Entry> inetAddressMapEntry : inetAddressMap.entrySet()) {
Map protocolInetAddressMap=inetAddressMapEntry.getValue();
for(Map.Entry protocolInetAddressMapEntry : protocolInetAddressMap.entrySet()) {
InetAddressInfo inetAddressInfo=protocolInetAddressMapEntry.getValue();
// add InetAddressInfo to sets based on IP version
List addresses=inetAddressInfo.getInetAddresses();
for(InetAddress address : addresses) {
if(address == null)
throw new RuntimeException("failed converting address info to IP address: " + inetAddressInfo);
addrs.add(address);
}
}
}
return addrs;
}
/**
* This method takes a set of InetAddresses, represented by an inetAddressmap, and:
* - if the resulting set is non-empty, goes through to see if all InetAddress-related
* user settings have a consistent IP version: v4 or v6, and throws an exception if not
* - if the resulting set is empty, sets the default IP version based on available stacks
* and if a dual stack, stack preferences
* - sets the IP version to be used in the JGroups session
* @return StackType.IPv4 for IPv4, StackType.IPv6 for IPv6, StackType.Unknown if the version cannot be determined
*/
public static StackType determineIpVersionFromAddresses(Collection addrs) throws Exception {
Set ipv4_addrs= new HashSet<>() ;
Set ipv6_addrs= new HashSet<>() ;
for(InetAddress address: addrs) {
if (address instanceof Inet4Address)
ipv4_addrs.add(address) ;
else
ipv6_addrs.add(address) ;
}
log.trace("all addrs=" + addrs + ", IPv4 addrs=" + ipv4_addrs + ", IPv6 addrs=" + ipv6_addrs);
// the user supplied 1 or more IP address inputs. Check if we have a consistent set
if (!addrs.isEmpty()) {
if (!ipv4_addrs.isEmpty() && !ipv6_addrs.isEmpty()) {
throw new RuntimeException("all addresses have to be either IPv4 or IPv6: IPv4 addresses=" +
ipv4_addrs + ", IPv6 addresses=" + ipv6_addrs);
}
return !ipv6_addrs.isEmpty()? StackType.IPv6 : StackType.IPv4;
}
return StackType.Unknown;
}
/*
* A method which does the following:
* - discovers all Fields or Methods within the protocol stack which set
* InetAddress, IpAddress, InetSocketAddress (and Lists of such) for which the user *has*
* specified a default value.
* - stores the resulting set of Fields and Methods in a map of the form:
* Protocol -> Property -> InetAddressInfo
* where InetAddressInfo instances encapsulate the InetAddress related information
* of the Fields and Methods.
*/
public static Map> createInetAddressMap(List protocol_configs,
List protocols) throws Exception {
// Map protocol -> Map, where the latter is protocol specific
Map> inetAddressMap = new HashMap<>() ;
// collect InetAddressInfo
for (int i = 0; i < protocol_configs.size(); i++) {
ProtocolConfiguration protocol_config = protocol_configs.get(i) ;
Protocol protocol = protocols.get(i) ;
String protocolName = protocol.getName();
// regenerate the Properties which were destroyed during basic property processing
Map properties = new HashMap<>(protocol_config.getProperties());
// check which InetAddress-related properties are ***non-null ***, and
// create an InetAddressInfo structure for them
// Method[] methods=protocol.getClass().getMethods();
Method[] methods=Util.getAllDeclaredMethodsWithAnnotations(protocol.getClass(), Property.class);
for(int j = 0; j < methods.length; j++) {
if (methods[j].isAnnotationPresent(Property.class) && isSetPropertyMethod(methods[j], protocol.getClass())) {
String propertyName = PropertyHelper.getPropertyName(methods[j]) ;
String propertyValue = properties.get(propertyName);
// if there is a systemProperty attribute defined in the annotation, set the property value from the system property
String tmp=grabSystemProp(methods[j].getAnnotation(Property.class));
if(tmp != null)
propertyValue=tmp;
if (propertyValue != null && InetAddressInfo.isInetAddressRelated(methods[j])) {
Object converted = null ;
try {
converted=PropertyHelper.getConvertedValue(protocol, methods[j], properties, propertyValue, false);
}
catch(Exception e) {
throw new Exception("String value could not be converted for method " + propertyName + " in "
+ protocolName + " with default value " + propertyValue + ".Exception is " +e, e);
}
InetAddressInfo inetinfo = new InetAddressInfo(protocol, methods[j], properties, propertyValue, converted) ;
Map m=inetAddressMap.computeIfAbsent(protocolName, k -> new HashMap<>());
m.put(propertyName, inetinfo) ;
}
}
}
//traverse class hierarchy and find all annotated fields and add them to the list if annotated
for(Class> clazz=protocol.getClass(); clazz != null; clazz=clazz.getSuperclass()) {
Field[] fields=clazz.getDeclaredFields();
for(int j = 0; j < fields.length; j++ ) {
if (fields[j].isAnnotationPresent(Property.class)) {
String propertyName = PropertyHelper.getPropertyName(fields[j], properties) ;
String propertyValue = properties.get(propertyName) ;
// if there is a systemProperty attribute defined in the annotation, set the property value from the system property
String tmp=grabSystemProp(fields[j].getAnnotation(Property.class));
if(tmp != null)
propertyValue=tmp;
if ((propertyValue != null || !PropertyHelper.usesDefaultConverter(fields[j]))
&& InetAddressInfo.isInetAddressRelated(fields[j])) {
Object converted = null ;
try {
converted=PropertyHelper.getConvertedValue(protocol, fields[j], properties, propertyValue, false);
}
catch(Exception e) {
throw new Exception("String value could not be converted for method " + propertyName + " in "
+ protocolName + " with default value " + propertyValue + ".Exception is " +e, e);
}
InetAddressInfo inetinfo = new InetAddressInfo(protocol, fields[j], properties, propertyValue, converted) ;
Map m=inetAddressMap.computeIfAbsent(protocolName, k -> new HashMap<>());
m.put(propertyName, inetinfo) ;
}// recompute
}
}
}
}
return inetAddressMap ;
}
public static List getInetAddresses(List protocols) throws Exception {
List retval=new LinkedList<>();
// collect InetAddressInfo
for(Protocol protocol : protocols) {
//traverse class hierarchy and find all annotated fields and add them to the list if annotated
for(Class> clazz=protocol.getClass(); clazz != null; clazz=clazz.getSuperclass()) {
Field[] fields=clazz.getDeclaredFields();
for(int j=0; j < fields.length; j++) {
if(fields[j].isAnnotationPresent(Property.class)) {
if(InetAddressInfo.isInetAddressRelated(fields[j])) {
Object value=getValueFromProtocol(protocol, fields[j]);
if(value instanceof InetAddress)
retval.add((InetAddress)value);
else if(value instanceof IpAddress)
retval.add(((IpAddress)value).getIpAddress());
else if(value instanceof InetSocketAddress)
retval.add(((InetSocketAddress)value).getAddress());
}
}
}
}
}
return retval;
}
/*
* Method which processes @Property.default() values, associated with the annotation
* using the defaultValue= attribute. This method does the following:
* - locate all properties which have no user value assigned
* - if the defaultValue attribute is not "", generate a value for the field using the
* property converter for that property and assign it to the field
*/
public static void setDefaultValues(List protocol_configs, List protocols,
StackType ip_version) throws Exception {
InetAddress default_ip_address=Util.getNonLoopbackAddress();
if(default_ip_address == null) {
log.warn(Util.getMessage("OnlyLoopbackFound"), ip_version);
default_ip_address=Util.getLocalhost(ip_version);
}
for(int i=0; i < protocol_configs.size(); i++) {
ProtocolConfiguration protocol_config=protocol_configs.get(i);
Protocol protocol=protocols.get(i);
String protocolName=protocol.getName();
// regenerate the Properties which were destroyed during basic property processing
Map properties=new HashMap<>(protocol_config.getProperties());
Method[] methods=Util.getAllDeclaredMethodsWithAnnotations(protocol.getClass(), Property.class);
for(int j=0; j < methods.length; j++) {
if(isSetPropertyMethod(methods[j], protocol.getClass())) {
String propertyName=PropertyHelper.getPropertyName(methods[j]);
Object propertyValue=getValueFromProtocol(protocol, propertyName);
if(propertyValue == null) { // if propertyValue is null, check if there is a we can use
Property annotation=methods[j].getAnnotation(Property.class);
// get the default value for the method- check for InetAddress types
String defaultValue=null;
if(InetAddressInfo.isInetAddressRelated(methods[j])) {
defaultValue=ip_version == StackType.IPv4? annotation.defaultValueIPv4() : annotation.defaultValueIPv6();
if(defaultValue != null && !defaultValue.isEmpty()) {
Object converted=null;
try {
if(defaultValue.equalsIgnoreCase(Global.NON_LOOPBACK_ADDRESS))
converted=default_ip_address;
else
converted=PropertyHelper.getConvertedValue(protocol, methods[j], properties, defaultValue, true);
methods[j].invoke(protocol, converted);
}
catch(Exception e) {
throw new Exception("default could not be assigned for method " + propertyName + " in "
+ protocolName + " with default " + defaultValue, e);
}
log.debug("set property " + protocolName + "." + propertyName + " to default value " + converted);
}
}
}
}
}
//traverse class hierarchy and find all annotated fields and add them to the list if annotated
Field[] fields=Util.getAllDeclaredFieldsWithAnnotations(protocol.getClass(), Property.class);
for(int j=0; j < fields.length; j++) {
String propertyName=PropertyHelper.getPropertyName(fields[j], properties);
Object propertyValue=getValueFromProtocol(protocol, fields[j]);
if(propertyValue == null) {
// add to collection of @Properties with no user specified value
Property annotation=fields[j].getAnnotation(Property.class);
// get the default value for the field - check for InetAddress types
String defaultValue=null;
if(InetAddressInfo.isInetAddressRelated(fields[j])) {
defaultValue=ip_version == StackType.IPv4? annotation.defaultValueIPv4() : annotation.defaultValueIPv6();
if(defaultValue != null && !defaultValue.isEmpty()) {
// condition for invoking converter
if(defaultValue != null || !PropertyHelper.usesDefaultConverter(fields[j])) {
Object converted=null;
try {
if(defaultValue.equalsIgnoreCase(Global.NON_LOOPBACK_ADDRESS))
converted=default_ip_address;
else
converted=PropertyHelper.getConvertedValue(protocol, fields[j], properties, defaultValue, true);
if(converted != null)
Util.setField(fields[j], protocol, converted);
}
catch(Exception e) {
throw new Exception("default could not be assigned for field " + propertyName + " in "
+ protocolName + " with default value " + defaultValue, e);
}
log.debug("set property " + protocolName + "." + propertyName + " to default value " + converted);
}
}
}
}
}
}
}
public static void setDefaultValues(List protocols, StackType ip_version) throws Exception {
InetAddress default_ip_address=Util.getNonLoopbackAddress();
if(default_ip_address == null) {
log.warn(Util.getMessage("OnlyLoopbackFound"), ip_version);
default_ip_address=Util.getLocalhost(ip_version);
}
for(Protocol protocol : protocols) {
String protocolName=protocol.getName();
//traverse class hierarchy and find all annotated fields and add them to the list if annotated
Field[] fields=Util.getAllDeclaredFieldsWithAnnotations(protocol.getClass(), Property.class);
for(int j=0; j < fields.length; j++) {
// get the default value for the field - check for InetAddress types
if(InetAddressInfo.isInetAddressRelated(fields[j])) {
Object propertyValue=getValueFromProtocol(protocol, fields[j]);
if(propertyValue == null) {
// add to collection of @Properties with no user specified value
Property annotation=fields[j].getAnnotation(Property.class);
String defaultValue=ip_version == StackType.IPv4? annotation.defaultValueIPv4() : annotation.defaultValueIPv6();
if(defaultValue != null && !defaultValue.isEmpty()) {
// condition for invoking converter
Object converted=null;
try {
if(defaultValue.equalsIgnoreCase(Global.NON_LOOPBACK_ADDRESS))
converted=default_ip_address;
else
converted=PropertyHelper.getConvertedValue(protocol, fields[j], defaultValue, true);
if(converted != null)
Util.setField(fields[j], protocol, converted);
}
catch(Exception e) {
throw new Exception("default could not be assigned for field " + fields[j].getName() + " in "
+ protocolName + " with default value " + defaultValue, e);
}
log.debug("set property " + protocolName + "." + fields[j].getName() + " to default value " + converted);
}
}
}
}
}
}
/**
* Makes sure that all fields annotated with @LocalAddress is (1) an InetAddress and (2) a valid address on any
* local network interface
* @param protocols
* @throws Exception
*/
public static void ensureValidBindAddresses(List protocols) throws Exception {
for(Protocol protocol : protocols) {
String protocolName=protocol.getName();
//traverse class hierarchy and find all annotated fields and add them to the list if annotated
Field[] fields=Util.getAllDeclaredFieldsWithAnnotations(protocol.getClass(), LocalAddress.class);
for(int i=0; i < fields.length; i++) {
Object val=getValueFromProtocol(protocol, fields[i]);
if(val == null)
continue;
if(!(val instanceof InetAddress))
throw new Exception("field " + protocolName + "." + fields[i].getName() + " is not an InetAddress");
Util.checkIfValidAddress((InetAddress)val, protocolName);
}
}
}
public static Object getValueFromProtocol(Protocol protocol, Field field) throws IllegalAccessException {
if(protocol == null || field == null) return null;
if(!Modifier.isPublic(field.getModifiers()))
field.setAccessible(true);
return field.get(protocol);
}
public static Object getValueFromProtocol(Protocol protocol, String field_name) throws IllegalAccessException {
if(protocol == null || field_name == null) return null;
Field field=Util.getField(protocol.getClass(), field_name);
return field != null? getValueFromProtocol(protocol, field) : null;
}
/**
* This method creates a list of all properties (Field or Method) in dependency order,
* where dependencies are specified using the dependsUpon specifier of the Property annotation.
* In particular, it does the following:
* (i) creates a master list of properties
* (ii) checks that all dependency references are present
* (iii) creates a copy of the master list in dependency order
*/
static AccessibleObject[] computePropertyDependencies(Object obj, Map properties) {
// List of Fields and Methods of the protocol annotated with @Property
List unorderedFieldsAndMethods = new LinkedList<>() ;
List orderedFieldsAndMethods = new LinkedList<>() ;
// Maps property name to property object
Map propertiesInventory = new HashMap<>() ;
// get the methods for this class and add them to the list if annotated with @Property
Method[] methods=obj.getClass().getMethods();
for(int i = 0; i < methods.length; i++) {
if (methods[i].isAnnotationPresent(Property.class) && isSetPropertyMethod(methods[i], obj.getClass())) {
String propertyName = PropertyHelper.getPropertyName(methods[i]) ;
unorderedFieldsAndMethods.add(methods[i]) ;
propertiesInventory.put(propertyName, methods[i]) ;
}
}
//traverse class hierarchy and find all annotated fields and add them to the list if annotated
for(Class> clazz=obj.getClass(); clazz != null; clazz=clazz.getSuperclass()) {
Field[] fields=clazz.getDeclaredFields();
for(int i = 0; i < fields.length; i++ ) {
if (fields[i].isAnnotationPresent(Property.class)) {
String propertyName = PropertyHelper.getPropertyName(fields[i], properties) ;
unorderedFieldsAndMethods.add(fields[i]) ;
// may need to change this based on name parameter of Property
propertiesInventory.put(propertyName, fields[i]) ;
}
}
}
// at this stage, we have all Fields and Methods annotated with @Property
checkDependencyReferencesPresent(unorderedFieldsAndMethods, propertiesInventory) ;
// order the fields and methods by dependency
orderedFieldsAndMethods = orderFieldsAndMethodsByDependency(unorderedFieldsAndMethods, propertiesInventory) ;
// convert to array of Objects
AccessibleObject[] result = new AccessibleObject[orderedFieldsAndMethods.size()] ;
for(int i = 0; i < orderedFieldsAndMethods.size(); i++) {
result[i] = orderedFieldsAndMethods.get(i) ;
}
return result ;
}
static List orderFieldsAndMethodsByDependency(List unorderedList,
Map propertiesMap) {
// Stack to detect cycle in depends relation
Stack stack = new Stack<>() ;
// the result list
List orderedList = new LinkedList<>() ;
// add the elements from the unordered list to the ordered list
// any dependencies will be checked and added first, in recursive manner
for(int i = 0; i < unorderedList.size(); i++) {
AccessibleObject obj = unorderedList.get(i) ;
addPropertyToDependencyList(orderedList, propertiesMap, stack, obj) ;
}
return orderedList ;
}
/**
* DFS of dependency graph formed by Property annotations and dependsUpon parameter
* This is used to create a list of Properties in dependency order
*/
static void addPropertyToDependencyList(List orderedList, Map props, Stack stack, AccessibleObject obj) {
if (orderedList.contains(obj))
return ;
if (stack.search(obj) > 0) {
throw new RuntimeException("Deadlock in @Property dependency processing") ;
}
// record the fact that we are processing obj
stack.push(obj) ;
// process dependencies for this object before adding it to the list
Property annotation = obj.getAnnotation(Property.class) ;
String dependsClause = annotation.dependsUpon() ;
StringTokenizer st = new StringTokenizer(dependsClause, ",") ;
while (st.hasMoreTokens()) {
String token = st.nextToken().trim();
AccessibleObject dep = props.get(token) ;
// if null, throw exception
addPropertyToDependencyList(orderedList, props, stack, dep) ;
}
// indicate we're done with processing dependencies
stack.pop() ;
// we can now add in dependency order
orderedList.add(obj) ;
}
/*
* Checks that for every dependency referred, there is a matching property
*/
static void checkDependencyReferencesPresent(List objects, Map props) {
// iterate overall properties marked by @Property
for(int i = 0; i < objects.size(); i++) {
// get the Property annotation
AccessibleObject ao = objects.get(i) ;
Property annotation = ao.getAnnotation(Property.class) ;
if (annotation == null) {
throw new IllegalArgumentException("@Property annotation is required for checking dependencies;" +
" annotation is missing for Field/Method " + ao.toString()) ;
}
String dependsClause = annotation.dependsUpon() ;
if (dependsClause.trim().isEmpty())
continue ;
// split dependsUpon specifier into tokens; trim each token; search for token in list
StringTokenizer st = new StringTokenizer(dependsClause, ",") ;
while (st.hasMoreTokens()) {
String token = st.nextToken().trim() ;
// check that the string representing a property name is in the list
boolean found = false ;
Set keyset = props.keySet();
for (Iterator iter = keyset.iterator(); iter.hasNext();) {
if (iter.next().equals(token)) {
found = true ;
break ;
}
}
if (!found) {
throw new IllegalArgumentException("@Property annotation " + annotation.name() +
" has an unresolved dependsUpon property: " + token) ;
}
}
}
}
public static void resolveAndInvokePropertyMethods(Object obj, Map props) throws Exception {
Method[] methods=obj.getClass().getMethods();
for(Method method: methods) {
resolveAndInvokePropertyMethod(obj, method, props) ;
}
}
public static void resolveAndInvokePropertyMethod(Object obj, Method method, Map props) throws Exception {
String methodName=method.getName();
Property annotation=method.getAnnotation(Property.class);
if(annotation != null && isSetPropertyMethod(method, obj.getClass())) {
String propertyName=PropertyHelper.getPropertyName(method) ;
String propertyValue=props.get(propertyName);
// if there is a systemProperty attribute defined in the annotation, set the property value from the system property
String tmp=grabSystemProp(method.getAnnotation(Property.class));
if(tmp != null)
propertyValue=tmp;
if(propertyName != null && propertyValue != null) {
String deprecated_msg=annotation.deprecatedMessage();
if(deprecated_msg != null && !deprecated_msg.isEmpty()) {
log.warn(Util.getMessage("Deprecated"), method.getDeclaringClass().getSimpleName() + "." + methodName,
deprecated_msg);
}
}
if(propertyValue != null) {
Object converted=null;
try {
converted=PropertyHelper.getConvertedValue(obj, method, props, propertyValue, true);
method.invoke(obj, converted);
}
catch(Exception e) {
String name=obj instanceof Protocol? ((Protocol)obj).getName() : obj.getClass().getName();
throw new Exception("Could not assign property " + propertyName + " in "
+ name + ", method is " + methodName + ", converted value is " + converted, e);
}
}
props.remove(propertyName);
}
}
public static void resolveAndAssignFields(Object obj, Map props) throws Exception {
//traverse class hierarchy and find all annotated fields
for(Class> clazz=obj.getClass(); clazz != null; clazz=clazz.getSuperclass()) {
Field[] fields=clazz.getDeclaredFields();
for(Field field: fields)
resolveAndAssignField(obj, field, props) ;
}
}
public static void resolveAndAssignField(Object obj, Field field, Map props) throws Exception {
Property annotation=field.getAnnotation(Property.class);
if(annotation != null) {
String propertyName = PropertyHelper.getPropertyName(field, props) ;
String propertyValue=props.get(propertyName);
// if there is a systemProperty attribute defined in the annotation, set the property value from the system property
// only do this if the property value hasn't yet been set
if(propertyValue == null) {
String tmp=grabSystemProp(field.getAnnotation(Property.class));
if(tmp != null)
propertyValue=tmp;
}
if(propertyName != null && propertyValue != null) {
String deprecated_msg=annotation.deprecatedMessage();
if(deprecated_msg != null && !deprecated_msg.isEmpty()) {
log.warn(Util.getMessage("Deprecated"), field.getDeclaringClass().getSimpleName() + "." + field.getName(),
deprecated_msg);
}
}
if(propertyValue != null || !PropertyHelper.usesDefaultConverter(field)){
Object converted=null;
try {
converted=PropertyHelper.getConvertedValue(obj, field, props, propertyValue, true);
if(converted != null)
Util.setField(field, obj, converted);
}
catch(Exception e) {
String name=obj instanceof Protocol? ((Protocol)obj).getName() : obj.getClass().getName();
throw new Exception("Property assignment of " + propertyName + " in "
+ name + " with original property value " + propertyValue + " and converted to " + converted
+ " could not be assigned", e);
}
}
props.remove(propertyName);
}
}
public static void removeDeprecatedProperties(Object obj, Map props) throws Exception {
//traverse class hierarchy and find all deprecated properties
for(Class> clazz=obj.getClass(); clazz != null; clazz=clazz.getSuperclass()) {
if(clazz.isAnnotationPresent(DeprecatedProperty.class)) {
DeprecatedProperty declaredAnnotation=clazz.getAnnotation(DeprecatedProperty.class);
String[] deprecatedProperties=declaredAnnotation.names();
for(String propertyName : deprecatedProperties) {
String propertyValue=props.get(propertyName);
if(propertyValue != null) {
if(log.isWarnEnabled()) {
String name=obj instanceof Protocol? ((Protocol)obj).getName() : obj.getClass().getName();
log.warn(Util.getMessage("Deprecated"), name + "." + propertyName, "will be ignored");
}
props.remove(propertyName);
}
}
}
}
}
public static boolean isSetPropertyMethod(Method method) {
return (method.getName().startsWith("set")
&& method.getReturnType() == java.lang.Void.TYPE
&& method.getParameterTypes().length == 1);
}
public static boolean isSetPropertyMethod(Method method, Class> enclosing_clazz) {
return (method.getName().startsWith("set")
&& (method.getReturnType() == java.lang.Void.TYPE || enclosing_clazz.isAssignableFrom(method.getReturnType()))
&& method.getParameterTypes().length == 1);
}
private static String grabSystemProp(Property annotation) {
String[] system_property_names=annotation.systemProperty();
String retval=null;
for(String system_property_name: system_property_names) {
if(system_property_name != null && !system_property_name.isEmpty()) {
try {
retval=System.getProperty(system_property_name);
if(retval != null)
return retval;
}
catch(SecurityException ex) {
log.error(Util.getMessage("SyspropFailure"), system_property_name, ex);
}
try {
retval=System.getenv(system_property_name);
if(retval != null)
return retval;
}
catch(SecurityException ex) {
log.error(Util.getMessage("SyspropFailure"), system_property_name, ex);
}
}
}
return retval;
}
/* --------------------------- End of Private Methods ---------------------------------- */
public static class InetAddressInfo {
Protocol protocol ;
AccessibleObject fieldOrMethod ;
Map properties ;
String propertyName ;
String stringValue ;
Object convertedValue ;
boolean isField ;
boolean isParameterized ; // is the associated type parametrized? (e.g. Collection)
Object baseType ; // what is the base type (e.g. Collection)
InetAddressInfo(Protocol protocol, AccessibleObject fieldOrMethod, Map properties, String stringValue,
Object convertedValue) {
// check input values
if (protocol == null) {
throw new IllegalArgumentException("Protocol for Field/Method must be non-null") ;
}
if (fieldOrMethod instanceof Field) {
isField = true ;
} else if (fieldOrMethod instanceof Method) {
isField = false ;
} else
throw new IllegalArgumentException("AccesibleObject is neither Field nor Method") ;
if (properties == null) {
throw new IllegalArgumentException("Properties for Field/Method must be non-null") ;
}
// set the values passed by the user - need to check for null
this.protocol = protocol ;
this.fieldOrMethod = fieldOrMethod ;
this.properties = properties ;
this.stringValue = stringValue ;
this.convertedValue = convertedValue ;
// set the property name
if (isField())
propertyName=PropertyHelper.getPropertyName((Field)fieldOrMethod, properties) ;
else
propertyName=PropertyHelper.getPropertyName((Method)fieldOrMethod) ;
// is variable type parameterized
this.isParameterized = false ;
if (isField())
this.isParameterized = hasParameterizedType((Field)fieldOrMethod) ;
else
this.isParameterized = hasParameterizedType((Method)fieldOrMethod) ;
// if parameterized, what is the base type?
this.baseType = null ;
if (isField() && isParameterized) {
// the Field has a single type
ParameterizedType fpt = (ParameterizedType)((Field)fieldOrMethod).getGenericType() ;
this.baseType = fpt.getActualTypeArguments()[0] ;
}
else if (!isField() && isParameterized) {
// the Method has several parameters (and so types)
Type[] types =((Method)fieldOrMethod).getGenericParameterTypes();
ParameterizedType mpt = (ParameterizedType) types[0] ;
this.baseType = mpt.getActualTypeArguments()[0] ;
}
}
boolean isField() { return isField ; }
String getStringValue() {return stringValue ;}
String getPropertyName() {return propertyName ;}
Object getConvertedValue() {return convertedValue ;}
boolean isParameterized() {return isParameterized ;}
Object getBaseType() { return baseType ;}
static boolean hasParameterizedType(Field f) {
if (f == null) {
throw new IllegalArgumentException("Field argument is null") ;
}
Type type = f.getGenericType();
return (type instanceof ParameterizedType) ;
}
static boolean hasParameterizedType(Method m) throws IllegalArgumentException {
if (m == null) {
throw new IllegalArgumentException("Method argument is null") ;
}
Type[] types = m.getGenericParameterTypes() ;
return (types[0] instanceof ParameterizedType) ;
}
static boolean isInetAddressRelated(Field f) {
if (hasParameterizedType(f)) {
// check for List, List, List
ParameterizedType fieldtype = (ParameterizedType) f.getGenericType() ;
// check that this parameterized type satisfies our constraints
try {
parameterizedTypeSanityCheck(fieldtype) ;
}
catch(IllegalArgumentException e) {
// because this Method's parameter fails the sanity check, its probably not an InetAddress related structure
return false ;
}
Class> listType = (Class>) fieldtype.getActualTypeArguments()[0] ;
return isInetAddressOrCompatibleType(listType) ;
}
else {
// check if the non-parameterized type is InetAddress, InetSocketAddress or IpAddress
Class> fieldtype = f.getType() ;
return isInetAddressOrCompatibleType(fieldtype) ;
}
}
/*
* Checks if this method's single parameter represents of of the following:
* an InetAddress, IpAddress or InetSocketAddress or one of
* List, List or List
*/
static boolean isInetAddressRelated(Method m) {
if (hasParameterizedType(m)) {
Type[] types = m.getGenericParameterTypes();
ParameterizedType methodParamType = (ParameterizedType)types[0] ;
// check that this parameterized type satisfies our constraints
try {
parameterizedTypeSanityCheck(methodParamType) ;
}
catch(IllegalArgumentException e) {
// because this Method's parameter fails the sanity check, its probably not an InetAddress
return false ;
}
Class> listType = (Class>) methodParamType.getActualTypeArguments()[0] ;
return isInetAddressOrCompatibleType(listType) ;
}
else {
Class> methodParamType = m.getParameterTypes()[0] ;
return isInetAddressOrCompatibleType(methodParamType) ;
}
}
static boolean isInetAddressOrCompatibleType(Class> c) {
return c.equals(InetAddress.class) || c.equals(InetSocketAddress.class) || c.equals(IpAddress.class) ;
}
/*
* Check if the parameterized type represents one of:
* List, List, List
*/
static void parameterizedTypeSanityCheck(ParameterizedType pt) throws IllegalArgumentException {
Type rawType = pt.getRawType() ;
Type[] actualTypes = pt.getActualTypeArguments() ;
// constraints on use of parameterized types with @Property
if (!(rawType instanceof Class> && rawType.equals(List.class))) {
throw new IllegalArgumentException("Invalid parameterized type definition - parameterized type must be a List") ;
}
// check for non-parameterized type in List
if (!(actualTypes[0] instanceof Class>)) {
throw new IllegalArgumentException("Invalid parameterized type - List must not contain a parameterized type") ;
}
}
/*
* Converts the computedValue to a list of InetAddresses.
* Because computedValues may be null, we need to return
* a zero length list in some cases.
*/
public List getInetAddresses() {
List addresses = new ArrayList<>() ;
if (getConvertedValue() == null)
return addresses ;
// if we take only an InetAddress argument
if (!isParameterized()) {
addresses.add(getInetAddress(getConvertedValue())) ;
return addresses ;
}
// if we take a List or similar
else {
List> values = (List>) getConvertedValue() ;
if (values.isEmpty())
return addresses ;
for (int i = 0; i < values.size(); i++) {
addresses.add(getInetAddress(values.get(i))) ;
}
return addresses ;
}
}
private static InetAddress getInetAddress(Object obj) throws IllegalArgumentException {
if (obj == null)
throw new IllegalArgumentException("Input argument must represent a non-null IP address") ;
if (obj instanceof InetAddress)
return (InetAddress) obj ;
else if (obj instanceof IpAddress)
return ((IpAddress) obj).getIpAddress() ;
else if (obj instanceof InetSocketAddress)
return ((InetSocketAddress) obj).getAddress() ;
else
throw new IllegalArgumentException("Input argument does not represent one of InetAddress. IpAddress not InetSocketAddress") ;
}
public String toString() {
StringBuilder sb = new StringBuilder() ;
sb.append("InetAddressInfo(")
.append("protocol=" + protocol.getName())
.append(", propertyName=" + getPropertyName())
.append(", string value=" + getStringValue())
.append(", parameterized=" + isParameterized());
if (isParameterized())
sb.append(", baseType=" + getBaseType()) ;
sb.append(")") ;
return sb.toString();
}
}
}