com.espertech.esper.util.PopulateUtil Maven / Gradle / Ivy
/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
***************************************************************************************
*/
package com.espertech.esper.util;
import com.espertech.esper.client.dataflow.EPDataFlowOperatorParameterProvider;
import com.espertech.esper.client.dataflow.EPDataFlowOperatorParameterProviderContext;
import com.espertech.esper.dataflow.annotations.DataFlowOpParameter;
import com.espertech.esper.epl.core.EngineImportService;
import com.espertech.esper.epl.expression.core.*;
import com.espertech.esper.event.WriteablePropertyDescriptor;
import com.espertech.esper.event.bean.PropertyHelper;
import com.espertech.esper.event.property.MappedProperty;
import com.espertech.esper.event.property.Property;
import com.espertech.esper.event.property.PropertyParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.*;
public class PopulateUtil {
private final static String CLASS_PROPERTY_NAME = "class";
private final static String SYSTEM_PROPETIES_NAME = "systemProperties".toLowerCase(Locale.ENGLISH);
private static Logger log = LoggerFactory.getLogger(PopulateUtil.class);
public static Object instantiatePopulateObject(Map objectProperties, Class topClass, ExprNodeOrigin exprNodeOrigin, ExprValidationContext exprValidationContext) throws ExprValidationException {
Class applicableClass = topClass;
if (topClass.isInterface()) {
applicableClass = findInterfaceImplementation(objectProperties, topClass, exprValidationContext.getEngineImportService());
}
Object top;
try {
top = applicableClass.newInstance();
} catch (RuntimeException e) {
throw new ExprValidationException("Exception instantiating class " + applicableClass.getName() + ": " + e.getMessage(), e);
} catch (InstantiationException e) {
throw new ExprValidationException(getMessageExceptionInstantiating(applicableClass), e);
} catch (IllegalAccessException e) {
throw new ExprValidationException("Illegal access to construct class " + applicableClass.getName() + ": " + e.getMessage(), e);
}
populateObject(topClass.getSimpleName(), 0, topClass.getSimpleName(), objectProperties, top, exprNodeOrigin, exprValidationContext, null, null);
return top;
}
public static void populateObject(String operatorName, int operatorNum, String dataFlowName, Map objectProperties, Object top, ExprNodeOrigin exprNodeOrigin, ExprValidationContext exprValidationContext, EPDataFlowOperatorParameterProvider optionalParameterProvider, Map optionalParameterURIs)
throws ExprValidationException {
Class applicableClass = top.getClass();
Set writables = PropertyHelper.getWritableProperties(applicableClass);
Set annotatedFields = JavaClassHelper.findAnnotatedFields(top.getClass(), DataFlowOpParameter.class);
Set annotatedMethods = JavaClassHelper.findAnnotatedMethods(top.getClass(), DataFlowOpParameter.class);
// find catch-all methods
Set catchAllMethods = new LinkedHashSet();
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 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(Locale.ENGLISH).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(), exprNodeOrigin, exprValidationContext, false, true);
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(), exprNodeOrigin, exprValidationContext, true, 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);
}
}
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) {
for (Map.Entry entry : optionalParameterURIs.entrySet()) {
String[] elements = URIUtil.parsePathElements(URI.create(entry.getKey()));
if (elements.length < 2) {
throw new ExprValidationException("Failed to parse URI '" + entry.getKey() + "', expected " +
"'operator_name/property_name' format");
}
if (elements[0].equals(operatorName)) {
try {
method.invoke(top, new Object[]{elements[1], entry.getValue()});
} catch (IllegalAccessException e) {
throw new ExprValidationException("Illegal access invoking method for property '" + entry.getKey() + "' for class " + applicableClass.getName() + " method " + method.getName(), e);
} catch (InvocationTargetException e) {
throw new ExprValidationException("Exception invoking method for property '" + entry.getKey() + "' for class " + applicableClass.getName() + " method " + method.getName() + ": " + e.getTargetException().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 Class findInterfaceImplementation(Map 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, engineImportService.getClassForNameProvider());
} catch (ClassNotFoundException e) {
if (!className.contains(".")) {
className = topClass.getPackage().getName() + "." + className;
try {
clazz = JavaClassHelper.getClassForName(className, engineImportService.getClassForNameProvider());
} 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 populateSpecCheckParameters(PopulateFieldWValueDescriptor[] descriptors, Map jsonRaw, Object spec, ExprNodeOrigin exprNodeOrigin, ExprValidationContext exprValidationContext)
throws ExprValidationException {
// lowercase keys
Map lowerCaseJsonRaw = new LinkedHashMap();
for (Map.Entry entry : jsonRaw.entrySet()) {
lowerCaseJsonRaw.put(entry.getKey().toLowerCase(Locale.ENGLISH), entry.getValue());
}
jsonRaw = lowerCaseJsonRaw;
// apply values
for (PopulateFieldWValueDescriptor desc : descriptors) {
Object value = jsonRaw.remove(desc.getPropertyName().toLowerCase(Locale.ENGLISH));
Object coerced = coerceProperty(desc.getPropertyName(), desc.getContainerType(), value, desc.getFieldType(), exprNodeOrigin, exprValidationContext, desc.isForceNumeric(), false);
desc.getSetter().set(coerced);
}
// should not have remaining parameters
if (!jsonRaw.isEmpty()) {
throw new ExprValidationException("Unrecognized parameter '" + jsonRaw.keySet().iterator().next() + "'");
}
}
public static Object coerceProperty(String propertyName, Class containingType, Object value, Class type, ExprNodeOrigin exprNodeOrigin, ExprValidationContext exprValidationContext, boolean forceNumeric, boolean includeClassNameInEx) throws ExprValidationException {
if (value instanceof ExprNode && type != ExprNode.class) {
if (value instanceof ExprIdentNode) {
ExprIdentNode identNode = (ExprIdentNode) value;
Property prop;
try {
prop = PropertyParser.parseAndWalkLaxToSimple(identNode.getFullUnresolvedName());
} 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(Locale.ENGLISH).equals(SYSTEM_PROPETIES_NAME)) {
return System.getProperty(mappedProperty.getKey());
}
} else {
ExprNode exprNode = (ExprNode) value;
ExprNode validated = ExprNodeUtility.getValidatedSubtree(exprNodeOrigin, exprNode, exprValidationContext);
exprValidationContext.getVariableService().setLocalVersion();
ExprEvaluator evaluator = validated.getExprEvaluator();
if (evaluator == null) {
throw new ExprValidationException("Failed to evaluate expression '" + ExprNodeUtility.toExpressionStringMinPrecedenceSafe(exprNode) + "'");
}
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)) {
String detail = "expects an array but receives a value of type " + value.getClass().getName();
throw new ExprValidationException(getExceptionText(propertyName, containingType, includeClassNameInEx, detail));
}
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(), exprNodeOrigin, exprValidationContext, false, includeClassNameInEx);
Array.set(coercedArray, i, coercedValue);
}
return coercedArray;
}
if (!(value instanceof Map)) {
String detail = "expects an " + JavaClassHelper.getClassNameFullyQualPretty(type) + " but receives a value of type " + value.getClass().getName();
throw new ExprValidationException(getExceptionText(propertyName, containingType, includeClassNameInEx, detail));
}
Map props = (Map) value;
return instantiatePopulateObject(props, type, exprNodeOrigin, exprValidationContext);
}
private static String getExceptionText(String propertyName, Class containingType, boolean includeClassNameInEx, String detailText) {
String msg = "Property '" + propertyName + "'";
if (includeClassNameInEx) {
msg += " of class " + JavaClassHelper.getClassNameFullyQualPretty(containingType);
}
msg += " " + detailText;
return msg;
}
private static WriteablePropertyDescriptor findDescriptor(Class clazz, String propertyName, Set writables)
throws ExprValidationException {
for (WriteablePropertyDescriptor desc : writables) {
if (desc.getPropertyName().toLowerCase(Locale.ENGLISH).equals(propertyName.toLowerCase(Locale.ENGLISH))) {
return desc;
}
}
return null;
}
private static String getMessageExceptionInstantiating(Class clazz) {
return "Exception instantiating class " + clazz.getName() + ", please make sure the class has a public no-arg constructor (and for inner classes is declared static)";
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy