/**************************************************************************************
* 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.event.vaevent;
import com.espertech.esper.client.ConfigurationException;
import com.espertech.esper.client.ConfigurationRevisionEventType;
import com.espertech.esper.client.ConfigurationVariantStream;
import com.espertech.esper.client.EventType;
import com.espertech.esper.event.EventAdapterService;
import com.espertech.esper.event.EventTypeIdGenerator;
import com.espertech.esper.view.StatementStopService;
import java.util.*;
/**
* Service for handling revision event types.
* <p>
* Each named window instance gets a dedicated revision processor.
*/
public class ValueAddEventServiceImpl implements ValueAddEventService
{
/**
* Map of revision event name and revision compiled specification.
*/
protected final Map<String, RevisionSpec> specificationsByRevisionName;
/**
* Map of named window name and processor.
*/
protected final Map<String, ValueAddEventProcessor> processorsByNamedWindow;
/**
* Map of revision event stream and variant stream processor.
*/
protected final Map<String, ValueAddEventProcessor> variantProcessors;
/**
* Ctor.
*/
public ValueAddEventServiceImpl()
{
this.specificationsByRevisionName = new HashMap<String, RevisionSpec>();
this.processorsByNamedWindow = new HashMap<String, ValueAddEventProcessor>();
variantProcessors = new HashMap<String, ValueAddEventProcessor>();
}
public EventType[] getValueAddedTypes()
{
List<EventType> types = new ArrayList<EventType>();
for (Map.Entry<String, ValueAddEventProcessor> revisionNamedWindow : processorsByNamedWindow.entrySet())
{
types.add(revisionNamedWindow.getValue().getValueAddEventType());
}
for (Map.Entry<String, ValueAddEventProcessor> variantProcessor : variantProcessors.entrySet())
{
types.add(variantProcessor.getValue().getValueAddEventType());
}
return types.toArray(new EventType[types.size()]);
}
public void init(Map<String, ConfigurationRevisionEventType> configRevision, Map<String, ConfigurationVariantStream> configVariant, EventAdapterService eventAdapterService, EventTypeIdGenerator eventTypeIdGenerator)
throws ConfigurationException
{
for (Map.Entry<String, ConfigurationRevisionEventType> entry : configRevision.entrySet())
{
addRevisionEventType(entry.getKey(), entry.getValue(), eventAdapterService);
}
for (Map.Entry<String, ConfigurationVariantStream> entry : configVariant.entrySet())
{
addVariantStream(entry.getKey(), entry.getValue(), eventAdapterService, eventTypeIdGenerator);
}
}
public void addRevisionEventType(String revisioneventTypeName, ConfigurationRevisionEventType config, EventAdapterService eventAdapterService)
throws ConfigurationException
{
RevisionSpec specification = validateRevision(revisioneventTypeName, config, eventAdapterService);
specificationsByRevisionName.put(revisioneventTypeName, specification);
}
public void addVariantStream(String variantStreamname, ConfigurationVariantStream variantStreamConfig, EventAdapterService eventAdapterService, EventTypeIdGenerator eventTypeIdGenerator) throws ConfigurationException
{
VariantSpec variantSpec = validateVariantStream(variantStreamname, variantStreamConfig, eventAdapterService);
VAEVariantProcessor processor = new VAEVariantProcessor(variantSpec, eventTypeIdGenerator, variantStreamConfig);
eventAdapterService.addTypeByName(variantStreamname, processor.getValueAddEventType());
variantProcessors.put(variantStreamname, processor);
}
/**
* Validate the variant stream definition.
* @param variantStreamname the stream name
* @param variantStreamConfig the configuration information
* @param eventAdapterService the event adapters
* @return specification for variant streams
*/
public static VariantSpec validateVariantStream(String variantStreamname, ConfigurationVariantStream variantStreamConfig, EventAdapterService eventAdapterService)
{
if (variantStreamConfig.getTypeVariance() == ConfigurationVariantStream.TypeVariance.PREDEFINED)
{
if (variantStreamConfig.getVariantTypeNames().isEmpty())
{
throw new ConfigurationException("Invalid variant stream configuration, no event type name has been added and default type variance requires at least one type, for name '" + variantStreamname + "'");
}
}
Set<EventType> types = new LinkedHashSet<EventType>();
for (String typeName : variantStreamConfig.getVariantTypeNames())
{
EventType type = eventAdapterService.getExistsTypeByName(typeName);
if (type == null)
{
throw new ConfigurationException("Event type by name '" + typeName + "' could not be found for use in variant stream configuration by name '" + variantStreamname + "'");
}
types.add(type);
}
EventType[] eventTypes = types.toArray(new EventType[types.size()]);
return new VariantSpec(variantStreamname, eventTypes, variantStreamConfig.getTypeVariance());
}
public EventType createRevisionType(String namedWindowName, String name, StatementStopService statementStopService, EventAdapterService eventAdapterService, EventTypeIdGenerator eventTypeIdGenerator)
{
RevisionSpec spec = specificationsByRevisionName.get(name);
ValueAddEventProcessor processor;
if (spec.getPropertyRevision() == ConfigurationRevisionEventType.PropertyRevision.OVERLAY_DECLARED)
{
processor = new VAERevisionProcessorDeclared(name, spec, statementStopService, eventAdapterService, eventTypeIdGenerator);
}
else
{
processor = new VAERevisionProcessorMerge(name, spec, statementStopService, eventAdapterService, eventTypeIdGenerator);
}
processorsByNamedWindow.put(namedWindowName, processor);
return processor.getValueAddEventType();
}
public ValueAddEventProcessor getValueAddProcessor(String name)
{
ValueAddEventProcessor proc = processorsByNamedWindow.get(name);
if (proc != null)
{
return proc;
}
return variantProcessors.get(name);
}
public EventType getValueAddUnderlyingType(String name)
{
RevisionSpec spec = specificationsByRevisionName.get(name);
if (spec == null) {
return null;
}
return spec.getBaseEventType();
}
public boolean isRevisionTypeName(String revisionTypeName)
{
return specificationsByRevisionName.containsKey(revisionTypeName);
}
/**
* Valiate the revision configuration.
* @param revisioneventTypeName name of revision types
* @param config configures revision type
* @param eventAdapterService event adapters
* @return revision specification
* @throws ConfigurationException if the configs are invalid
*/
protected static RevisionSpec validateRevision(String revisioneventTypeName, ConfigurationRevisionEventType config, EventAdapterService eventAdapterService)
throws ConfigurationException
{
if ((config.getNameBaseEventTypes() == null) || (config.getNameBaseEventTypes().size() == 0))
{
throw new ConfigurationException("Required base event type name is not set in the configuration for revision event type '" + revisioneventTypeName + "'");
}
if (config.getNameBaseEventTypes().size() > 1)
{
throw new ConfigurationException("Only one base event type name may be added to revision event type '" + revisioneventTypeName + "', multiple base types are not yet supported");
}
// get base types
String baseeventTypeName = config.getNameBaseEventTypes().iterator().next();
EventType baseEventType = eventAdapterService.getExistsTypeByName(baseeventTypeName);
if (baseEventType == null)
{
throw new ConfigurationException("Could not locate event type for name '" + baseeventTypeName + "' in the configuration for revision event type '" + revisioneventTypeName + "'");
}
// get name types
EventType[] deltaTypes = new EventType[config.getNameDeltaEventTypes().size()];
String[] deltaNames = new String[config.getNameDeltaEventTypes().size()];
int count = 0;
for (String deltaName : config.getNameDeltaEventTypes())
{
EventType deltaEventType = eventAdapterService.getExistsTypeByName(deltaName);
if (deltaEventType == null)
{
throw new ConfigurationException("Could not locate event type for name '" + deltaName + "' in the configuration for revision event type '" + revisioneventTypeName + "'");
}
deltaTypes[count] = deltaEventType;
deltaNames[count] = deltaName;
count++;
}
// the key properties must be set
if ((config.getKeyPropertyNames() == null) || (config.getKeyPropertyNames().length == 0))
{
throw new ConfigurationException("Required key properties are not set in the configuration for revision event type '" + revisioneventTypeName + "'");
}
// make sure the key properties exist the base type and all delta types
checkKeysExist(baseEventType, baseeventTypeName, config.getKeyPropertyNames(), revisioneventTypeName);
for (int i = 0; i < deltaTypes.length; i++)
{
checkKeysExist(deltaTypes[i], deltaNames[i], config.getKeyPropertyNames(), revisioneventTypeName);
}
// key property names shared between base and delta must have the same type
String keyPropertyNames[] = PropertyUtility.copyAndSort(config.getKeyPropertyNames());
for (String key : keyPropertyNames)
{
Class typeProperty = baseEventType.getPropertyType(key);
for (EventType dtype : deltaTypes)
{
Class dtypeProperty = dtype.getPropertyType(key);
if ((dtypeProperty != null) && (typeProperty != dtypeProperty))
{
throw new ConfigurationException("Key property named '" + key + "' does not have the same type for base and delta types of revision event type '" + revisioneventTypeName + "'");
}
}
}
// In the "declared" type the change set properties consist of only :
// (base event type properties) minus (key properties) minus (properties only on base event type)
if (config.getPropertyRevision() == ConfigurationRevisionEventType.PropertyRevision.OVERLAY_DECLARED)
{
// determine non-key properties: those overridden by any delta, and those simply only present on the base event type
String nonkeyPropertyNames[] = PropertyUtility.uniqueExclusiveSort(baseEventType.getPropertyNames(), keyPropertyNames);
Set<String> baseEventOnlyProperties = new HashSet<String>();
Set<String> changesetPropertyNames = new HashSet<String>();
for (String nonKey : nonkeyPropertyNames)
{
boolean overriddenProperty = false;
for (EventType type : deltaTypes)
{
if (type.isProperty(nonKey))
{
changesetPropertyNames.add(nonKey);
overriddenProperty = true;
break;
}
}
if (!overriddenProperty)
{
baseEventOnlyProperties.add(nonKey);
}
}
String changesetProperties[] = changesetPropertyNames.toArray(new String[changesetPropertyNames.size()]);
String baseEventOnlyPropertyNames[] = baseEventOnlyProperties.toArray(new String[baseEventOnlyProperties.size()]);
// verify that all changeset properties match event type
for (String changesetProperty : changesetProperties)
{
Class typeProperty = baseEventType.getPropertyType(changesetProperty);
for (EventType dtype : deltaTypes)
{
Class dtypeProperty = dtype.getPropertyType(changesetProperty);
if ((dtypeProperty != null) && (typeProperty != dtypeProperty))
{
throw new ConfigurationException("Property named '" + changesetProperty + "' does not have the same type for base and delta types of revision event type '" + revisioneventTypeName + "'");
}
}
}
return new RevisionSpec(config.getPropertyRevision(), baseEventType, deltaTypes, deltaNames, keyPropertyNames, changesetProperties, baseEventOnlyPropertyNames, false, null);
}
else
{
// In the "exists" type the change set properties consist of all properties: base event properties plus delta types properties
Set<String> allProperties = new HashSet<String>();
allProperties.addAll(Arrays.asList(baseEventType.getPropertyNames()));
for (EventType deltaType : deltaTypes)
{
allProperties.addAll(Arrays.asList(deltaType.getPropertyNames()));
}
String[] allPropertiesArr = allProperties.toArray(new String[allProperties.size()]);
String[] changesetProperties = PropertyUtility.uniqueExclusiveSort(allPropertiesArr, keyPropertyNames);
// All properties must have the same type, if a property exists for any given type
boolean hasContributedByDelta = false;
boolean[] contributedByDelta = new boolean[changesetProperties.length];
count = 0;
for (String property : changesetProperties)
{
Class basePropertyType = baseEventType.getPropertyType(property);
Class typeTemp = null;
if (basePropertyType != null)
{
typeTemp = basePropertyType;
}
else
{
hasContributedByDelta = true;
contributedByDelta[count] = true;
}
for (EventType dtype : deltaTypes)
{
Class dtypeProperty = dtype.getPropertyType(property);
if (dtypeProperty != null)
{
if ((typeTemp != null) && (dtypeProperty != typeTemp))
{
throw new ConfigurationException("Property named '" + property + "' does not have the same type for base and delta types of revision event type '" + revisioneventTypeName + "'");
}
}
typeTemp = dtypeProperty;
}
count++;
}
// Compile changeset
return new RevisionSpec(config.getPropertyRevision(), baseEventType, deltaTypes, deltaNames, keyPropertyNames, changesetProperties, new String[0], hasContributedByDelta, contributedByDelta);
}
}
private static void checkKeysExist(EventType baseEventType, String name, String[] keyProperties, String revisioneventTypeName)
{
String propertyNames[] = baseEventType.getPropertyNames();
for (String keyProperty : keyProperties)
{
boolean exists = false;
for (String propertyName : propertyNames)
{
if (propertyName.equals(keyProperty))
{
exists = true;
break;
}
}
if (!exists)
{
throw new ConfigurationException("Key property '" + keyProperty + "' as defined in the configuration for revision event type '" + revisioneventTypeName + "' does not exists in event type '" + name + "'");
}
}
}
}