/* *************************************************************************************** * 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.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 + "'"); } } } }