/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* 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.epl.core;
import com.espertech.esper.client.ConfigurationDataCache;
import com.espertech.esper.client.ConfigurationMethodRef;
import com.espertech.esper.client.EPException;
import com.espertech.esper.client.EventType;
import com.espertech.esper.core.context.util.EPStatementAgentInstanceHandle;
import com.espertech.esper.epl.db.DataCache;
import com.espertech.esper.epl.db.DataCacheFactory;
import com.espertech.esper.epl.db.PollExecStrategy;
import com.espertech.esper.epl.expression.ExprEvaluatorContext;
import com.espertech.esper.epl.expression.ExprValidationException;
import com.espertech.esper.epl.spec.MethodStreamSpec;
import com.espertech.esper.event.EventAdapterService;
import com.espertech.esper.schedule.ScheduleBucket;
import com.espertech.esper.schedule.SchedulingService;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.view.HistoricalEventViewable;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Factory for method-invocation data provider streams.
*/
public class MethodPollingViewableFactory
{
private static final Log log = LogFactory.getLog(MethodPollingViewableFactory.class);
/**
* Creates a method-invocation polling view for use as a stream that calls a method, or pulls results from cache.
* @param streamNumber the stream number
* @param methodStreamSpec defines the class and method to call
* @param eventAdapterService for creating event types and events
* @param epStatementAgentInstanceHandle for time-based callbacks
* @param methodResolutionService for resolving classes and imports
* @param engineImportService for resolving configurations
* @param schedulingService for scheduling callbacks in expiry-time based caches
* @param scheduleBucket for schedules within the statement
* @param exprEvaluatorContext expression evaluation context
* @return pollable view
* @throws ExprValidationException if the expressions cannot be validated or the method descriptor
* has incorrect class and method names, or parameter number and types don't match
*/
public static HistoricalEventViewable createPollMethodView(int streamNumber,
MethodStreamSpec methodStreamSpec,
EventAdapterService eventAdapterService,
EPStatementAgentInstanceHandle epStatementAgentInstanceHandle,
MethodResolutionService methodResolutionService,
EngineImportService engineImportService,
SchedulingService schedulingService,
ScheduleBucket scheduleBucket,
ExprEvaluatorContext exprEvaluatorContext)
throws ExprValidationException
{
// Try to resolve the method
FastMethod staticMethod;
Class declaringClass;
try
{
Method method = methodResolutionService.resolveMethod(methodStreamSpec.getClassName(), methodStreamSpec.getMethodName());
declaringClass = method.getDeclaringClass();
FastClass declaringFastClass = FastClass.create(Thread.currentThread().getContextClassLoader(), method.getDeclaringClass());
staticMethod = declaringFastClass.getMethod(method);
}
catch(Exception e)
{
throw new ExprValidationException(e.getMessage());
}
// Determine object type returned by method
Class beanClass = staticMethod.getReturnType();
if ((beanClass == void.class) || (beanClass == Void.class) || (JavaClassHelper.isJavaBuiltinDataType(beanClass)))
{
throw new ExprValidationException("Invalid return type for static method '" + staticMethod.getName() + "' of class '" + methodStreamSpec.getClassName() + "', expecting a Java class");
}
if (staticMethod.getReturnType().isArray())
{
beanClass = staticMethod.getReturnType().getComponentType();
}
// If the method returns a Map, look up the map type
Map<String, Object> mapType = null;
String mapTypeName = null;
if ( (JavaClassHelper.isImplementsInterface(staticMethod.getReturnType(), Map.class)) ||
(staticMethod.getReturnType().isArray() && JavaClassHelper.isImplementsInterface(staticMethod.getReturnType().getComponentType(), Map.class)) )
{
MethodMetadataDesc metadata = getCheckMetadata(methodStreamSpec.getMethodName(), methodStreamSpec.getClassName(), methodResolutionService, Map.class);
mapTypeName = metadata.getTypeName();
mapType = (Map<String, Object>) metadata.getTypeMetadata();
}
// If the method returns an Object[] or Object[][], look up the type information
LinkedHashMap<String, Object> oaType = null;
String oaTypeName = null;
if (staticMethod.getReturnType() == Object[].class || staticMethod.getReturnType() == Object[][].class)
{
MethodMetadataDesc metadata = getCheckMetadata(methodStreamSpec.getMethodName(), methodStreamSpec.getClassName(), methodResolutionService, LinkedHashMap.class);
oaTypeName = metadata.getTypeName();
oaType = (LinkedHashMap<String, Object>) metadata.getTypeMetadata();
}
// Determine event type from class and method name
EventType eventType;
if (mapType != null) {
eventType = eventAdapterService.addNestableMapType(mapTypeName, mapType, null, false, true, true, false, false);
}
else if (oaType != null) {
eventType = eventAdapterService.addNestableObjectArrayType(oaTypeName, oaType, null, false, true, true, false, false);
}
else {
eventType = eventAdapterService.addBeanType(beanClass.getName(), beanClass, false, true, true);
}
// Construct polling strategy as a method invocation
ConfigurationMethodRef configCache = engineImportService.getConfigurationMethodRef(declaringClass.getName());
if (configCache == null)
{
configCache = engineImportService.getConfigurationMethodRef(declaringClass.getSimpleName());
}
ConfigurationDataCache dataCacheDesc = (configCache != null) ? configCache.getDataCacheDesc() : null;
DataCache dataCache = DataCacheFactory.getDataCache(dataCacheDesc, epStatementAgentInstanceHandle, schedulingService, scheduleBucket);
PollExecStrategy methodPollStrategy;
if (mapType != null) {
if (staticMethod.getReturnType().isArray()) {
methodPollStrategy = new MethodPollingExecStrategyMapArray(eventAdapterService, staticMethod, eventType);
}
else {
methodPollStrategy = new MethodPollingExecStrategyMapPlain(eventAdapterService, staticMethod, eventType);
}
}
else if (oaType != null) {
if (staticMethod.getReturnType() == Object[][].class) {
methodPollStrategy = new MethodPollingExecStrategyOAArray(eventAdapterService, staticMethod, eventType);
}
else {
methodPollStrategy = new MethodPollingExecStrategyOAPlain(eventAdapterService, staticMethod, eventType);
}
}
else {
if (staticMethod.getReturnType().isArray()) {
methodPollStrategy = new MethodPollingExecStrategyPOJOArray(eventAdapterService, staticMethod, eventType);
}
else {
methodPollStrategy = new MethodPollingExecStrategyPOJOPlain(eventAdapterService, staticMethod, eventType);
}
}
return new MethodPollingViewable(methodStreamSpec, streamNumber, methodStreamSpec.getExpressions(), methodPollStrategy, dataCache, eventType, exprEvaluatorContext);
}
private static MethodMetadataDesc getCheckMetadata(String methodName, String className, MethodResolutionService methodResolutionService, Class metadataClass) {
Method typeGetterMethod;
String getterMethodName = methodName + "Metadata";
try {
typeGetterMethod = methodResolutionService.resolveMethod(className, getterMethodName, new Class[0], new boolean[0], new boolean[0]);
}
catch(Exception e) {
throw new EPException("Could not find getter method for method invocation, expected a method by name '" + getterMethodName + "' accepting no parameters");
}
boolean fail = false;
if (metadataClass.isInterface()) {
fail = !JavaClassHelper.isImplementsInterface(typeGetterMethod.getReturnType(), metadataClass);
}
else {
fail = typeGetterMethod.getReturnType() != metadataClass;
}
if (fail) {
throw new EPException("Getter method '" + getterMethodName + "' does not return " + JavaClassHelper.getClassNameFullyQualPretty(metadataClass));
}
Object resultType;
try {
resultType = typeGetterMethod.invoke(null);
}
catch (Exception e) {
throw new EPException("Error invoking metadata getter method for method invocation, for method by name '" + getterMethodName + "' accepting no parameters: " + e.getMessage(), e);
}
if (resultType == null) {
throw new EPException("Error invoking metadata getter method for method invocation, method returned a null value");
}
return new MethodMetadataDesc(className + "." + typeGetterMethod.getName(), resultType);
}
public static class MethodMetadataDesc {
private final String typeName;
private final Object typeMetadata;
public MethodMetadataDesc(String typeName, Object typeMetadata) {
this.typeName = typeName;
this.typeMetadata = typeMetadata;
}
public String getTypeName() {
return typeName;
}
public Object getTypeMetadata() {
return typeMetadata;
}
}
}