All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgroups.stack.Configurator Maven / Gradle / Ivy

package org.jgroups.stack;


import org.jgroups.Event;
import org.jgroups.Global;
import org.jgroups.PhysicalAddress;
import org.jgroups.annotations.LocalAddress;
import org.jgroups.annotations.Property;
import org.jgroups.annotations.RecommendedForUpgrade;
import org.jgroups.conf.PropertyHelper;
import org.jgroups.conf.ProtocolConfiguration;
import org.jgroups.conf.XmlNode;
import org.jgroups.logging.Log;
import org.jgroups.logging.LogFactory;
import org.jgroups.protocols.TP;
import org.jgroups.util.StackType;
import org.jgroups.util.Util;

import java.lang.reflect.*;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
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); protected static boolean skip_setting_default_values=false; protected final ProtocolStack stack; public Configurator() { stack=null; } public Configurator(ProtocolStack protocolStack) { stack=protocolStack; } public static boolean skipSettingDefaultValues() {return skip_setting_default_values;} public static void skipSettingDefaultValues(boolean f) {skip_setting_default_values=f;} public Protocol setupProtocolStack(List config) throws Exception { return setupProtocolStack(config, stack); } /** * Sets up the protocol stack. Each {@link ProtocolConfiguration} has the protocol name and a map of attribute * names and values (strings). Reflection is used to find the right fields (or setters) based on attribute names, * and set them (by converting the attribute value to the proper object). */ public static Protocol setupProtocolStack(List protocol_configs, ProtocolStack st) throws Exception { List protocols=createProtocolsAndInitializeAttrs(protocol_configs, st); // Fixes NPE with concurrent channel creation when using a shared stack (https://issues.redhat.com/browse/JGRP-1488) Protocol top_protocol=protocols.get(protocols.size() - 1); top_protocol.setUpProtocol(st); return connectProtocols(protocols); } public static Protocol createProtocol(String prot_spec, ProtocolStack stack) throws Exception { return createProtocol(prot_spec, stack, true); } /** * 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 * @throws Exception Will be thrown when the new protocol cannot be created */ public static Protocol createProtocol(String prot_spec, ProtocolStack stack, boolean init_attrs) throws Exception { if(prot_spec == null) throw new Exception("Configurator.createProtocol(): prot_spec is null"); // parse the configuration for this protocol ProtocolConfiguration config=new ProtocolConfiguration(prot_spec); // create an instance of the protocol class and configure it Protocol prot=createLayer(stack, config); if(init_attrs) initializeAttrs(prot, config, Util.getIpStackType()); prot.init(); return prot; } public static List createProtocolsAndInitializeAttrs(List cfgs, ProtocolStack st) throws Exception { List protocols=createProtocols(cfgs, st); if(protocols == null) return null; // Determine how to resolve addresses that are set (e.g.) via symbolic names, by the type of bind_addr in the // transport. The logic below is described in https://issues.redhat.com/browse/JGRP-2343 StackType ip_version=Util.getIpStackType(); Protocol transport=protocols.get(0); if(transport instanceof TP) { ProtocolConfiguration cfg=cfgs.get(0); Field bind_addr_field=Util.getField(transport.getClass(), "bind_addr"); resolveAndAssignField(transport, bind_addr_field, cfg.getProperties(), ip_version); InetAddress resolved_addr=(InetAddress)Util.getField(bind_addr_field, transport); if(resolved_addr != null) ip_version=resolved_addr instanceof Inet6Address? StackType.IPv6 : StackType.IPv4; else if(ip_version == StackType.Dual) ip_version=StackType.IPv4; // prefer IPv4 addresses } for(int i=0; i < cfgs.size(); i++) { ProtocolConfiguration config=cfgs.get(i); Protocol prot=protocols.get(i); initializeAttrs(prot, config, ip_version); } setDefaultAddressValues(protocols, ip_version); ensureValidBindAddresses(protocols); return protocols; } /** * 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 a list of configurations, creates a protocol for each and returns all protocols in a list. * @param protocol_configs A list of ProtocolConfigurations * @param stack The protocol stack * @return List of Protocols */ public static List createProtocols(List protocol_configs, 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(); if(protocol_name == null) return null; Class clazz=config.loadProtocolClass(stack != null? stack.getClass() : null); if(clazz.getAnnotation(Deprecated.class) != null) log.warn("%s has been deprecated; please upgrade to a newer version of the protocol", clazz.getSimpleName()); if(clazz.getAnnotation(RecommendedForUpgrade.class) != null) log.warn("there are more recent versions of %s present; " + "please upgrade to a newer version of the protocol", clazz.getSimpleName()); try { Protocol retval=clazz.getDeclaredConstructor().newInstance(); if(stack != null) retval.setProtocolStack(stack); return retval; } catch(InstantiationException inst_ex) { throw new InstantiationException(String.format(Util.getMessage("ProtocolCreateError"), protocol_name, inst_ex.getLocalizedMessage())); } } /** Sets the attributes in a given protocol from properties */ public static void initializeAttrs(Protocol prot, ProtocolConfiguration config, StackType ip_version) throws Exception { String protocol_name=config.getProtocolName(); if(protocol_name == null) return; Map properties=new HashMap<>(config.getProperties()); initializeAttrs(prot, properties, ip_version); // don't yet initialize attrs of components, but later (after init() has been called); therefore remove all // properties belonging to components Util.forAllComponentTypes(prot.getClass(), (obj, prefix) -> { String key=prefix + "."; properties.keySet().removeIf(s -> s.startsWith(key)); }); 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(XmlNode node : subtrees) prot.parse(node); } } public static void initializeAttrs(Object obj, Map properties, StackType ip_version) throws Exception { // before processing field and method based props, take dependencies specified by @Property.dependsUpon into account AccessibleObject[] dependencyOrderedFieldsAndMethods=computePropertyDependencies(obj, properties); for(AccessibleObject ordered : dependencyOrderedFieldsAndMethods) { if(ordered instanceof Field) resolveAndAssignField(obj, (Field)ordered, properties, ip_version); else if(ordered instanceof Method) resolveAndInvokePropertyMethod(obj, (Method)ordered, properties, ip_version); } } /** * Throws an exception if sanity check fails. Possible sanity check is uniqueness of all protocol names */ public static void sanityCheck(List protocols) throws Exception { Set ids=new HashSet<>(); for(Protocol protocol : protocols) { short id=protocol.getId(); String name=protocol.getName(); if(id > 0 && !ids.add(id)) throw new Exception("Protocol ID " + id + " (name=" + name + ") is duplicate; 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> map) throws Exception { return map.values().stream().flatMap(m -> m.values().stream()) .flatMap(i -> i.getInetAddresses().stream()).filter(Objects::nonNull).collect(Collectors.toSet()); } /* * 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> inetAddressMap=new HashMap<>(); 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=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]); 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, Util.getIpStackType()); } catch(Exception e) { throw new Exception("string 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 Field[] fields=Util.getAllDeclaredFieldsWithAnnotations(protocol.getClass(), Property.class); for(int j=0; j < fields.length; j++) { 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, Util.getIpStackType()); } catch(Exception e) { throw new Exception("string 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); } } } 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=getValueFromObject(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; } /** Returns a map of protocol.attr/InetAddress tuples */ public static Map getInetAddresses2(List protocols) { Map map=new HashMap<>(); if(protocols != null) { for(Protocol p: protocols) { Field[] fields=Util.getAllDeclaredFieldsWithAnnotations(p.getClass(), Property.class); for(Field f: fields) { if(InetAddress.class.isAssignableFrom(f.getType())) { try { map.put(p.getName() + "." + f.getName(), getValueFromObject(p, f)); } catch(IllegalAccessException e) { map.put(p.getName() + "." + f.getName(), null); } } } } } return map; } public static boolean isInetAddressOrCompatibleType(Class c) { return InetAddress.class.isAssignableFrom(c) || SocketAddress.class.isAssignableFrom(c) || PhysicalAddress.class.isAssignableFrom(c); } /** * Processes fields and methods which are inet address related, and assigns them default values defined by * ({@link Property#defaultValueIPv4()} and {@link Property#defaultValueIPv6()}). * This method does the following: * - find all properties which have no value assigned * - generate a value for the field using the property converter for that property and assign it to the field */ public static void setDefaultAddressValues(List protocols, StackType ip_version) throws Exception { if(skip_setting_default_values) { log.trace("skipped setting default address values in protocols as skip_setting_default_values=%b", skip_setting_default_values); return; } for(Protocol prot: protocols) setDefaultAddressValues(prot, ip_version); } public static void setDefaultAddressValues(Object obj, StackType ip_version) throws Exception { InetAddress default_ip_address=Util.getNonLoopbackAddress(ip_version); if(default_ip_address == null) { log.warn(Util.getMessage("OnlyLoopbackFound"), ip_version); default_ip_address=Util.getLoopback(ip_version); } // Process the attributes which are defined via methods first setDefaultAddressValuesMethods(obj, ip_version, default_ip_address); // Next process the fields setDefaultAddressValuesFields(obj, ip_version, default_ip_address); } protected static void setDefaultAddressValuesMethods(Object obj, StackType ip_version, InetAddress default_ip_address) throws Exception { Map properties=new HashMap<>(); // dummy properties Method[] methods=Util.getAllDeclaredMethodsWithAnnotations(obj.getClass(), Property.class); for(Method method: methods) { if(isSetPropertyMethod(method, obj.getClass())) { String propertyName=PropertyHelper.getPropertyName(method); Object existing_value=getValueFromObject(obj, propertyName); if(existing_value != null || !InetAddressInfo.isInetAddressRelated(method)) continue; Property annotation=method.getAnnotation(Property.class); String 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(obj, method, properties, defaultValue, true, ip_version); method.invoke(obj, converted); } catch(Exception e) { throw new Exception("default could not be assigned for method " + propertyName + " in " + obj + " with default " + defaultValue, e); } log.debug("set attribute %s.%s to default value %s", obj, propertyName, converted); } } } } protected static void setDefaultAddressValuesFields(Object obj, StackType ip_version, InetAddress default_ip_address) throws Exception { Map properties=new HashMap<>(); // dummy properties Field[] fields=Util.getAllDeclaredFieldsWithAnnotations(obj.getClass(), Property.class); for(Field field: fields) { String propertyName=PropertyHelper.getPropertyName(field, properties); Object existing_value=getValueFromObject(obj, field); if(existing_value != null || !InetAddressInfo.isInetAddressRelated(field)) continue; Property annotation=field.getAnnotation(Property.class); String defaultValue=ip_version == StackType.IPv6? annotation.defaultValueIPv6() : annotation.defaultValueIPv4(); if(defaultValue != null && !defaultValue.isEmpty()) { // condition for invoking converter if(defaultValue != null || !PropertyHelper.usesDefaultConverter(field)) { Object converted=null; try { if(defaultValue.equalsIgnoreCase(Global.NON_LOOPBACK_ADDRESS)) converted=default_ip_address; else converted=PropertyHelper.getConvertedValue(obj, field, properties, defaultValue, true, ip_version); if(converted != null) Util.setField(field, obj, converted); } catch(Exception e) { throw new Exception("default could not be assigned for field " + propertyName + " in " + obj + " with default value " + defaultValue, e); } log.debug("set property " + obj + "." + propertyName + " 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 */ 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=getValueFromObject(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 T getValueFromObject(Object obj, Field field) throws IllegalAccessException { if(obj == null || field == null) return null; return (T)Util.getField(field, obj); } public static T getValueFromObject(Object obj, String field_name) throws IllegalAccessException { if(obj == null || field_name == null) return null; Field field=Util.getField(obj.getClass(), field_name); return field != null? getValueFromObject(obj, 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 Deque stack=new ArrayDeque<>(); // 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, Deque stack, AccessibleObject obj) { if(orderedList.contains(obj)) return; if(stack.contains(obj)) 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) { for(int i=0; i < objects.size(); i++) { // iterate overall properties marked by @Property // 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); 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, StackType ip_version) throws Exception { Method[] methods=obj.getClass().getMethods(); for(Method method : methods) { resolveAndInvokePropertyMethod(obj, method, props, ip_version); } } public static void resolveAndInvokePropertyMethod(Object obj, Method method, Map props, StackType ip_version) 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, ip_version); 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, StackType ip_version) 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, ip_version); } } public static void resolveAndAssignField(Object obj, Field field, Map props, StackType ip_version) 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, ip_version); 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 boolean isSetPropertyMethod(Method method, Class enclosing_clazz) { return (method.getReturnType() == java.lang.Void.TYPE || method.getReturnType().isAssignableFrom(enclosing_clazz)) && 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); } } /* * 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 && Collection.class.isAssignableFrom((Class)rawType))) { throw new IllegalArgumentException("Invalid parameterized type definition - parameterized type must be a collection"); } // 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())); // 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(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy