private static Class findInterfaceImplementation( Map<String, Object> properties, Class topClass, EngineImportService engineImportService) throws ExprValidationException { String message = "Failed to find implementation for interface " + topClass.getName(); // Allow to populate the special "class" field if (!properties.containsKey(CLASS_PROPERTY_NAME)) { throw new ExprValidationException( message + ", for interfaces please specified the '" + CLASS_PROPERTY_NAME + "' field that provides the class name either as a simple class name or fully qualified"); } Class clazz = null; String className = (String) properties.get(CLASS_PROPERTY_NAME); try { clazz = JavaClassHelper.getClassForName(className); } catch (ClassNotFoundException e) { if (!className.contains(".")) { className = topClass.getPackage().getName() + "." + className; try { clazz = JavaClassHelper.getClassForName(className); } catch (ClassNotFoundException ex) { } } if (clazz == null) { throw new ExprValidationPropertyException( message + ", could not find class by name '" + className + "'"); } } if (!JavaClassHelper.isSubclassOrImplementsInterface(clazz, topClass)) { throw new ExprValidationException( message + ", class " + JavaClassHelper.getClassNameFullyQualPretty(clazz) + " does not implement the interface"); } return clazz; }
public static void populateObject( String operatorName, int operatorNum, String dataFlowName, Map<String, Object> objectProperties, Object top, EngineImportService engineImportService, EPDataFlowOperatorParameterProvider optionalParameterProvider, Map<String, Object> optionalParameterURIs) throws ExprValidationException { Class applicableClass = top.getClass(); Set<WriteablePropertyDescriptor> writables = PropertyHelper.getWritableProperties(applicableClass); Set<Field> annotatedFields = JavaClassHelper.findAnnotatedFields(top.getClass(), DataFlowOpParameter.class); Set<Method> annotatedMethods = JavaClassHelper.findAnnotatedMethods(top.getClass(), DataFlowOpParameter.class); // find catch-all methods Set<Method> catchAllMethods = new LinkedHashSet<Method>(); if (annotatedMethods != null) { for (Method method : annotatedMethods) { DataFlowOpParameter anno = (DataFlowOpParameter) JavaClassHelper.getAnnotations( DataFlowOpParameter.class, method.getDeclaredAnnotations()) .get(0); if (anno.all()) { if (method.getParameterTypes().length == 2 && method.getParameterTypes()[0] == String.class && method.getParameterTypes()[1] == Object.class) { catchAllMethods.add(method); continue; } throw new ExprValidationException("Invalid annotation for catch-call"); } } } // map provided values for (Map.Entry<String, Object> property : objectProperties.entrySet()) { boolean found = false; String propertyName = property.getKey(); // invoke catch-all setters for (Method method : catchAllMethods) { try { method.invoke(top, new Object[] {propertyName, property.getValue()}); } catch (IllegalAccessException e) { throw new ExprValidationException( "Illegal access invoking method for property '" + propertyName + "' for class " + applicableClass.getName() + " method " + method.getName(), e); } catch (InvocationTargetException e) { throw new ExprValidationException( "Exception invoking method for property '" + propertyName + "' for class " + applicableClass.getName() + " method " + method.getName() + ": " + e.getTargetException().getMessage(), e); } found = true; } if (propertyName.toLowerCase().equals(CLASS_PROPERTY_NAME)) { continue; } // use the writeable property descriptor (appropriate setter method) from writing the property WriteablePropertyDescriptor descriptor = findDescriptor(applicableClass, propertyName, writables); if (descriptor != null) { Object coerceProperty = coerceProperty( propertyName, applicableClass, property.getValue(), descriptor.getType(), engineImportService, false); try { descriptor.getWriteMethod().invoke(top, new Object[] {coerceProperty}); } catch (IllegalArgumentException e) { throw new ExprValidationException( "Illegal argument invoking setter method for property '" + propertyName + "' for class " + applicableClass.getName() + " method " + descriptor.getWriteMethod().getName() + " provided value " + coerceProperty, e); } catch (IllegalAccessException e) { throw new ExprValidationException( "Illegal access invoking setter method for property '" + propertyName + "' for class " + applicableClass.getName() + " method " + descriptor.getWriteMethod().getName(), e); } catch (InvocationTargetException e) { throw new ExprValidationException( "Exception invoking setter method for property '" + propertyName + "' for class " + applicableClass.getName() + " method " + descriptor.getWriteMethod().getName() + ": " + e.getTargetException().getMessage(), e); } continue; } // find the field annotated with {@link @GraphOpProperty} for (Field annotatedField : annotatedFields) { DataFlowOpParameter anno = (DataFlowOpParameter) JavaClassHelper.getAnnotations( DataFlowOpParameter.class, annotatedField.getDeclaredAnnotations()) .get(0); if (anno.name().equals(propertyName) || annotatedField.getName().equals(propertyName)) { Object coerceProperty = coerceProperty( propertyName, applicableClass, property.getValue(), annotatedField.getType(), engineImportService, true); try { annotatedField.setAccessible(true); annotatedField.set(top, coerceProperty); } catch (Exception e) { throw new ExprValidationException( "Failed to set field '" + annotatedField.getName() + "': " + e.getMessage(), e); } found = true; break; } } if (found) { continue; } throw new ExprValidationException( "Failed to find writable property '" + propertyName + "' for class " + applicableClass.getName()); } // second pass: if a parameter URI - value pairs were provided, check that if (optionalParameterURIs != null) { for (Field annotatedField : annotatedFields) { try { annotatedField.setAccessible(true); String uri = operatorName + "/" + annotatedField.getName(); if (optionalParameterURIs.containsKey(uri)) { Object value = optionalParameterURIs.get(uri); annotatedField.set(top, value); if (log.isDebugEnabled()) { log.debug( "Found parameter '" + uri + "' for data flow " + dataFlowName + " setting " + value); } } else { if (log.isDebugEnabled()) { log.debug("Not found parameter '" + uri + "' for data flow " + dataFlowName); } } } catch (Exception e) { throw new ExprValidationException( "Failed to set field '" + annotatedField.getName() + "': " + e.getMessage(), e); } } } // third pass: if a parameter provider is provided, use that if (optionalParameterProvider != null) { for (Field annotatedField : annotatedFields) { try { annotatedField.setAccessible(true); Object provided = annotatedField.get(top); Object value = optionalParameterProvider.provide( new EPDataFlowOperatorParameterProviderContext( operatorName, annotatedField.getName(), top, operatorNum, provided, dataFlowName)); if (value != null) { annotatedField.set(top, value); } } catch (Exception e) { throw new ExprValidationException( "Failed to set field '" + annotatedField.getName() + "': " + e.getMessage(), e); } } } }
private static Object coerceProperty( String propertyName, Class containingType, Object value, Class type, EngineImportService engineImportService, boolean forceNumeric) throws ExprValidationException { if (value instanceof ExprNode && type != ExprNode.class) { if (value instanceof ExprIdentNode) { ExprIdentNode identNode = (ExprIdentNode) value; Property prop; try { prop = PropertyParser.parse(identNode.getFullUnresolvedName(), false); } catch (Exception ex) { throw new ExprValidationException( "Failed to parse property '" + identNode.getFullUnresolvedName() + "'"); } if (!(prop instanceof MappedProperty)) { throw new ExprValidationException( "Unrecognized property '" + identNode.getFullUnresolvedName() + "'"); } MappedProperty mappedProperty = (MappedProperty) prop; if (mappedProperty.getPropertyNameAtomic().toLowerCase().equals(SYSTEM_PROPETIES_NAME)) { return System.getProperty(mappedProperty.getKey()); } } else { ExprNode exprNode = (ExprNode) value; ExprEvaluator evaluator = exprNode.getExprEvaluator(); if (evaluator == null) { throw new ExprValidationException( "Failed to evaluate expression '" + exprNode.toExpressionString() + "'"); } value = evaluator.evaluate(null, true, null); } } if (value == null) { return null; } if (value.getClass() == type) { return value; } if (JavaClassHelper.isAssignmentCompatible(value.getClass(), type)) { if (forceNumeric && JavaClassHelper.getBoxedType(value.getClass()) != JavaClassHelper.getBoxedType(type) && JavaClassHelper.isNumeric(type) && JavaClassHelper.isNumeric(value.getClass())) { value = JavaClassHelper.coerceBoxed((Number) value, JavaClassHelper.getBoxedType(type)); } return value; } if (JavaClassHelper.isSubclassOrImplementsInterface(value.getClass(), type)) { return value; } if (type.isArray()) { if (!(value instanceof Collection)) { throw new ExprValidationException( "Property '" + propertyName + "' of class " + JavaClassHelper.getClassNameFullyQualPretty(containingType) + " expects an array but receives a value of type " + value.getClass().getName()); } Object[] items = ((Collection) value).toArray(); Object coercedArray = Array.newInstance(type.getComponentType(), items.length); for (int i = 0; i < items.length; i++) { Object coercedValue = coerceProperty( propertyName + " (array element)", type, items[i], type.getComponentType(), engineImportService, false); Array.set(coercedArray, i, coercedValue); } return coercedArray; } if (!(value instanceof Map)) { throw new ExprValidationException( "Property '" + propertyName + "' of class " + JavaClassHelper.getClassNameFullyQualPretty(containingType) + " expects an " + JavaClassHelper.getClassNameFullyQualPretty(type) + " but receives a value of type " + value.getClass().getName()); } Map<String, Object> props = (Map<String, Object>) value; return instantiatePopulateObject(props, type, engineImportService); }