/************************************************************************************** * 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; import com.espertech.esper.client.*; import com.espertech.esper.collection.Pair; import com.espertech.esper.core.service.EPRuntimeEventSender; import com.espertech.esper.core.thread.ThreadingService; import com.espertech.esper.epl.core.EngineImportService; import com.espertech.esper.event.arr.ObjectArrayEventBean; import com.espertech.esper.event.arr.ObjectArrayEventType; import com.espertech.esper.event.bean.BeanEventAdapter; import com.espertech.esper.event.bean.BeanEventBean; import com.espertech.esper.event.bean.BeanEventType; import com.espertech.esper.event.bean.BeanEventTypeFactory; import com.espertech.esper.event.map.MapEventBean; import com.espertech.esper.event.map.MapEventType; import com.espertech.esper.event.xml.*; import com.espertech.esper.plugin.*; import com.espertech.esper.util.JavaClassHelper; import com.espertech.esper.util.URIUtil; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import java.io.Serializable; import java.net.URI; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * Implementation for resolving event name to event type. * <p> * The implementation assigned a unique identifier to each event type. * For Class-based event types, only one EventType instance and one event type id exists for the same class. * <p> * Event type names must be unique, that is an name must resolve to a single event type. * <p> * Each event type can have multiple names defined for it. For example, expressions such as * "select * from A" and "select * from B" * in which A and B are names for the same class X the select clauses each fireStatementStopped for events of type X. * In summary, names A and B point to the same underlying event type and therefore event type id. */ public class EventAdapterServiceImpl implements EventAdapterService { private static Log log = LogFactory.getLog(EventAdapterServiceImpl.class); private final ConcurrentHashMap<Class, BeanEventType> typesPerJavaBean; private final Map<String, EventType> nameToTypeMap; private final Map<String, PlugInEventTypeHandler> nameToHandlerMap; private BeanEventAdapter beanEventAdapter; private Map<String, EventType> xmldomRootElementNames; private LinkedHashSet<String> javaPackageNames; private final Map<URI, PlugInEventRepresentation> plugInRepresentations; private final EventTypeIdGenerator eventTypeIdGenerator; /** * Ctor. */ public EventAdapterServiceImpl(EventTypeIdGenerator eventTypeIdGenerator) { this.eventTypeIdGenerator = eventTypeIdGenerator; nameToTypeMap = new HashMap<String, EventType>(); xmldomRootElementNames = new HashMap<String, EventType>(); javaPackageNames = new LinkedHashSet<String>(); nameToHandlerMap = new HashMap<String, PlugInEventTypeHandler>(); // Share the mapping of class to type with the type creation for thread safety typesPerJavaBean = new ConcurrentHashMap<Class, BeanEventType>(); beanEventAdapter = new BeanEventAdapter(typesPerJavaBean, this, eventTypeIdGenerator); plugInRepresentations = new HashMap<URI, PlugInEventRepresentation>(); } public Map<String, EventType> getDeclaredEventTypes() { return new HashMap<String, EventType>(nameToTypeMap); } /** * Set the legacy Java class type information. * @param classToLegacyConfigs is the legacy class configs */ public void setClassLegacyConfigs(Map<String, ConfigurationEventTypeLegacy> classToLegacyConfigs) { beanEventAdapter.setClassToLegacyConfigs(classToLegacyConfigs); } public ConfigurationEventTypeLegacy getClassLegacyConfigs(String className) { return beanEventAdapter.getClassToLegacyConfigs(className); } public Set<WriteablePropertyDescriptor> getWriteableProperties(EventType eventType) { return EventAdapterServiceHelper.getWriteableProperties(eventType); } public EventBeanManufacturer getManufacturer(EventType eventType, WriteablePropertyDescriptor[] properties, EngineImportService engineImportService) throws EventBeanManufactureException { return EventAdapterServiceHelper.getManufacturer(this, eventType, properties, engineImportService); } public EventType[] getAllTypes() { Collection<EventType> types = nameToTypeMap.values(); return types.toArray(new EventType[types.size()]); } public synchronized void addTypeByName(String name, EventType eventType) throws EventAdapterException { if (nameToTypeMap.containsKey(name)) { throw new EventAdapterException("Event type by name '" + name + "' already exists"); } nameToTypeMap.put(name, eventType); } public void addEventRepresentation(URI eventRepURI, PlugInEventRepresentation pluginEventRep) throws EventAdapterException { if (plugInRepresentations.containsKey(eventRepURI)) { throw new EventAdapterException("Plug-in event representation URI by name " + eventRepURI + " already exists"); } plugInRepresentations.put(eventRepURI, pluginEventRep); } public EventType addPlugInEventType(String eventTypeName, URI[] resolutionURIs, Serializable initializer) throws EventAdapterException { if (nameToTypeMap.containsKey(eventTypeName)) { throw new EventAdapterException("Event type named '" + eventTypeName + "' has already been declared"); } PlugInEventRepresentation handlingFactory = null; URI handledEventTypeURI = null; if ((resolutionURIs == null) || (resolutionURIs.length == 0)) { throw new EventAdapterException("Event type named '" + eventTypeName + "' could not be created as" + " no resolution URIs for dynamic resolution of event type names through a plug-in event representation have been defined"); } for (URI eventTypeURI : resolutionURIs) { // Determine a list of event representations that may handle this type Map<URI, Object> allFactories = new HashMap<URI, Object>(plugInRepresentations); Collection<Map.Entry<URI, Object>> factories = URIUtil.filterSort(eventTypeURI, allFactories); if (factories.isEmpty()) { continue; } // Ask each in turn to accept the type (the process of resolving the type) for (Map.Entry<URI, Object> entry : factories) { PlugInEventRepresentation factory = (PlugInEventRepresentation) entry.getValue(); PlugInEventTypeHandlerContext context = new PlugInEventTypeHandlerContext(eventTypeURI, initializer, eventTypeName, eventTypeIdGenerator.getTypeId(eventTypeName)); if (factory.acceptsType(context)) { handlingFactory = factory; handledEventTypeURI = eventTypeURI; break; } } if (handlingFactory != null) { break; } } if (handlingFactory == null) { throw new EventAdapterException("Event type named '" + eventTypeName + "' could not be created as none of the " + "registered plug-in event representations accepts any of the resolution URIs '" + Arrays.toString(resolutionURIs) + "' and initializer"); } PlugInEventTypeHandlerContext context = new PlugInEventTypeHandlerContext(handledEventTypeURI, initializer, eventTypeName, eventTypeIdGenerator.getTypeId(eventTypeName)); PlugInEventTypeHandler handler = handlingFactory.getTypeHandler(context); if (handler == null) { throw new EventAdapterException("Event type named '" + eventTypeName + "' could not be created as no handler was returned"); } EventType eventType = handler.getType(); nameToTypeMap.put(eventTypeName, eventType); nameToHandlerMap.put(eventTypeName, handler); return eventType; } public EventSender getStaticTypeEventSender(EPRuntimeEventSender runtimeEventSender, String eventTypeName, ThreadingService threadingService) throws EventTypeException { EventType eventType = nameToTypeMap.get(eventTypeName); if (eventType == null) { throw new EventTypeException("Event type named '" + eventTypeName + "' could not be found"); } // handle built-in types if (eventType instanceof BeanEventType) { return new EventSenderBean(runtimeEventSender, (BeanEventType) eventType, this, threadingService); } if (eventType instanceof MapEventType) { return new EventSenderMap(runtimeEventSender, (MapEventType) eventType, this, threadingService); } if (eventType instanceof ObjectArrayEventType) { return new EventSenderObjectArray(runtimeEventSender, (ObjectArrayEventType) eventType, this, threadingService); } if (eventType instanceof BaseXMLEventType) { return new EventSenderXMLDOM(runtimeEventSender, (BaseXMLEventType) eventType, this, threadingService); } PlugInEventTypeHandler handlers = nameToHandlerMap.get(eventTypeName); if (handlers != null) { return handlers.getSender(runtimeEventSender); } throw new EventTypeException("An event sender for event type named '" + eventTypeName + "' could not be created as the type is internal"); } public void updateMapEventType(String mapeventTypeName, Map<String, Object> typeMap) throws EventAdapterException { EventType type = nameToTypeMap.get(mapeventTypeName); if (type == null) { throw new EventAdapterException("Event type named '" + mapeventTypeName + "' has not been declared"); } if (!(type instanceof MapEventType)) { throw new EventAdapterException("Event type by name '" + mapeventTypeName + "' is not a Map event type"); } MapEventType mapEventType = (MapEventType) type; mapEventType.addAdditionalProperties(typeMap, this); } public void updateObjectArrayEventType(String objectArrayEventTypeName, Map<String, Object> typeMap) throws EventAdapterException { EventType type = nameToTypeMap.get(objectArrayEventTypeName); if (type == null) { throw new EventAdapterException("Event type named '" + objectArrayEventTypeName + "' has not been declared"); } if (!(type instanceof ObjectArrayEventType)) { throw new EventAdapterException("Event type by name '" + objectArrayEventTypeName + "' is not an Object-array event type"); } ObjectArrayEventType objectArrayEventType = (ObjectArrayEventType) type; objectArrayEventType.addAdditionalProperties(typeMap, this); } public EventSender getDynamicTypeEventSender(EPRuntimeEventSender epRuntime, URI[] uri, ThreadingService threadingService) throws EventTypeException { List<EventSenderURIDesc> handlingFactories = new ArrayList<EventSenderURIDesc>(); for (URI resolutionURI : uri) { // Determine a list of event representations that may handle this type Map<URI, Object> allFactories = new HashMap<URI, Object>(plugInRepresentations); Collection<Map.Entry<URI, Object>> factories = URIUtil.filterSort(resolutionURI, allFactories); if (factories.isEmpty()) { continue; } // Ask each in turn to accept the type (the process of resolving the type) for (Map.Entry<URI, Object> entry : factories) { PlugInEventRepresentation factory = (PlugInEventRepresentation) entry.getValue(); PlugInEventBeanReflectorContext context = new PlugInEventBeanReflectorContext(resolutionURI); if (factory.acceptsEventBeanResolution(context)) { PlugInEventBeanFactory beanFactory = factory.getEventBeanFactory(context); if (beanFactory == null) { log.warn("Plug-in event representation returned a null bean factory, ignoring entry"); continue; } EventSenderURIDesc desc = new EventSenderURIDesc(beanFactory, resolutionURI, entry.getKey()); handlingFactories.add(desc); } } } if (handlingFactories.isEmpty()) { throw new EventTypeException("Event sender for resolution URIs '" + Arrays.toString(uri) + "' did not return at least one event representation's event factory"); } return new EventSenderImpl(handlingFactories, epRuntime, threadingService); } public BeanEventTypeFactory getBeanEventTypeFactory() { return beanEventAdapter; } /** * Sets the default property resolution style. * @param defaultPropertyResolutionStyle is the default style */ public void setDefaultPropertyResolutionStyle(Configuration.PropertyResolutionStyle defaultPropertyResolutionStyle) { beanEventAdapter.setDefaultPropertyResolutionStyle(defaultPropertyResolutionStyle); } public void setDefaultAccessorStyle(ConfigurationEventTypeLegacy.AccessorStyle defaultAccessorStyle) { beanEventAdapter.setDefaultAccessorStyle(defaultAccessorStyle); } public EventType getExistsTypeByName(String eventTypeName) { if (eventTypeName == null) { throw new IllegalStateException("Null event type name parameter"); } return nameToTypeMap.get(eventTypeName); } public synchronized EventType addBeanType(String eventTypeName, Class clazz, boolean isPreconfiguredStatic, boolean isPreconfigured, boolean isConfigured) throws EventAdapterException { if (log.isDebugEnabled()) { log.debug(".addBeanType Adding " + eventTypeName + " for type " + clazz.getName()); } EventType existingType = nameToTypeMap.get(eventTypeName); if (existingType != null) { if (existingType.getUnderlyingType().equals(clazz)) { return existingType; } throw new EventAdapterException("Event type named '" + eventTypeName + "' has already been declared with differing underlying type information:" + existingType.getUnderlyingType().getName() + " versus " + clazz.getName()); } EventType eventType = beanEventAdapter.createBeanType(eventTypeName, clazz, isPreconfiguredStatic, isPreconfigured, isConfigured); nameToTypeMap.put(eventTypeName, eventType); return eventType; } public synchronized EventType addBeanTypeByName(String eventTypeName, Class clazz, boolean isNamedWindow) throws EventAdapterException { if (log.isDebugEnabled()) { log.debug(".addBeanTypeNamedWindow Adding " + eventTypeName + " for type " + clazz.getName()); } EventType existingType = nameToTypeMap.get(eventTypeName); if (existingType != null) { if (existingType instanceof BeanEventType && existingType.getUnderlyingType() == clazz && existingType.getName().equals(eventTypeName)) { EventTypeMetadata.TypeClass typeClass = ((BeanEventType) existingType).getMetadata().getTypeClass(); if (isNamedWindow) { if (typeClass == EventTypeMetadata.TypeClass.NAMED_WINDOW) { return existingType; } } else { if (typeClass == EventTypeMetadata.TypeClass.STREAM) { return existingType; } } } throw new EventAdapterException("An event type named '" + eventTypeName + "' has already been declared"); } EventTypeMetadata.TypeClass typeClass = isNamedWindow ? EventTypeMetadata.TypeClass.NAMED_WINDOW : EventTypeMetadata.TypeClass.STREAM; BeanEventType beanEventType = new BeanEventType(EventTypeMetadata.createBeanType(eventTypeName, clazz, false, false, false, typeClass), eventTypeIdGenerator.getTypeId(eventTypeName), clazz, this, beanEventAdapter.getClassToLegacyConfigs(clazz.getName())); nameToTypeMap.put(eventTypeName, beanEventType); return beanEventType; } /** * Create an event bean given an event of object id. * @param theEvent is the event class * @return event */ public EventBean adapterForBean(Object theEvent) { EventType eventType = typesPerJavaBean.get(theEvent.getClass()); if (eventType == null) { // This will update the typesPerJavaBean mapping eventType = beanEventAdapter.createBeanType(theEvent.getClass().getName(), theEvent.getClass(), false, false, false); } return new BeanEventBean(theEvent, eventType); } /** * Add an event type for the given Java class name. * @param eventTypeName is the name * @param fullyQualClassName is the Java class name * @return event type * @throws EventAdapterException if the Class name cannot resolve or other error occured */ public synchronized EventType addBeanType(String eventTypeName, String fullyQualClassName, boolean considerAutoName, boolean isPreconfiguredStatic, boolean isPreconfigured, boolean isConfigured) throws EventAdapterException { if (log.isDebugEnabled()) { log.debug(".addBeanType Adding " + eventTypeName + " for type " + fullyQualClassName); } EventType existingType = nameToTypeMap.get(eventTypeName); if (existingType != null) { if ((existingType.getUnderlyingType().getName().equals(fullyQualClassName)) || (existingType.getUnderlyingType().getSimpleName().equals(fullyQualClassName))) { if (log.isDebugEnabled()) { log.debug(".addBeanType Returning existing type for " + eventTypeName); } return existingType; } throw new EventAdapterException("Event type named '" + eventTypeName + "' has already been declared with differing underlying type information: Class " + existingType.getUnderlyingType().getName() + " versus " + fullyQualClassName); } // Try to resolve as a fully-qualified class name first Class clazz = null; try { ClassLoader cl = Thread.currentThread().getContextClassLoader(); clazz = Class.forName(fullyQualClassName, true, cl); } catch (ClassNotFoundException ex) { if (!considerAutoName) { throw new EventAdapterException("Event type or class named '" + fullyQualClassName + "' was not found", ex); } // Attempt to resolve from auto-name packages for (String javaPackageName : javaPackageNames) { String generatedClassName = javaPackageName + "." + fullyQualClassName; try { ClassLoader cl = Thread.currentThread().getContextClassLoader(); Class resolvedClass = Class.forName(generatedClassName, true, cl); if (clazz != null) { throw new EventAdapterException("Failed to resolve name '" + eventTypeName + "', the class was ambigously found both in " + "package '" + clazz.getPackage().getName() + "' and in " + "package '" + resolvedClass.getPackage().getName() + "'" , ex); } clazz = resolvedClass; } catch (ClassNotFoundException ex1) { // expected, class may not exists in all packages } } if (clazz == null) { throw new EventAdapterException("Event type or class named '" + fullyQualClassName + "' was not found", ex); } } EventType eventType = beanEventAdapter.createBeanType(eventTypeName, clazz, isPreconfiguredStatic, isPreconfigured, isConfigured); nameToTypeMap.put(eventTypeName, eventType); return eventType; } public synchronized EventType addNestableMapType(String eventTypeName, Map<String, Object> propertyTypes, ConfigurationEventTypeMap optionalConfig, boolean isPreconfiguredStatic, boolean isPreconfigured, boolean isConfigured, boolean namedWindow, boolean insertInto) throws EventAdapterException { Pair<EventType[], Set<EventType>> mapSuperTypes = getSuperTypesDepthFirst(optionalConfig != null ? optionalConfig.getSuperTypes() : null, true); EventTypeMetadata metadata = EventTypeMetadata.createNonPojoApplicationType(EventTypeMetadata.ApplicationType.MAP, eventTypeName, isPreconfiguredStatic, isPreconfigured, isConfigured, namedWindow, insertInto); int typeId = eventTypeIdGenerator.getTypeId(eventTypeName); MapEventType newEventType = new MapEventType(metadata, eventTypeName, typeId, this, propertyTypes, mapSuperTypes.getFirst(), mapSuperTypes.getSecond(), optionalConfig); EventType existingType = nameToTypeMap.get(eventTypeName); if (existingType != null) { // The existing type must be the same as the type createdStatement if (!newEventType.equalsCompareType(existingType)) { String message = newEventType.getEqualsMessage(existingType); throw new EventAdapterException("Event type named '" + eventTypeName + "' has already been declared with differing column name or type information: " + message); } // Since it's the same, return the existing type return existingType; } nameToTypeMap.put(eventTypeName, newEventType); return newEventType; } public synchronized EventType addNestableObjectArrayType(String eventTypeName, Map<String, Object> propertyTypes, ConfigurationEventTypeObjectArray optionalConfig, boolean isPreconfiguredStatic, boolean isPreconfigured, boolean isConfigured, boolean namedWindow, boolean insertInto) throws EventAdapterException { if (optionalConfig != null && optionalConfig.getSuperTypes().size() > 1) { throw new EventAdapterException(ConfigurationEventTypeObjectArray.SINGLE_SUPERTYPE_MSG); } Pair<EventType[], Set<EventType>> mapSuperTypes = getSuperTypesDepthFirst(optionalConfig != null ? optionalConfig.getSuperTypes() : null, false); EventTypeMetadata metadata = EventTypeMetadata.createNonPojoApplicationType(EventTypeMetadata.ApplicationType.OBJECTARR, eventTypeName, isPreconfiguredStatic, isPreconfigured, isConfigured, namedWindow, insertInto); int typeId = eventTypeIdGenerator.getTypeId(eventTypeName); ObjectArrayEventType newEventType = new ObjectArrayEventType(metadata, eventTypeName, typeId, this, propertyTypes, optionalConfig, mapSuperTypes.getFirst(), mapSuperTypes.getSecond()); EventType existingType = nameToTypeMap.get(eventTypeName); if (existingType != null) { // The existing type must be the same as the type createdStatement if (!newEventType.equalsCompareType(existingType)) { String message = newEventType.getEqualsMessage(existingType); throw new EventAdapterException("Event type named '" + eventTypeName + "' has already been declared with differing column name or type information: " + message); } // Since it's the same, return the existing type return existingType; } nameToTypeMap.put(eventTypeName, newEventType); return newEventType; } public EventBean adapterForMap(Map<String, Object> theEvent, String eventTypeName) throws EPException { EventType existingType = nameToTypeMap.get(eventTypeName); if (!(existingType instanceof MapEventType)) { throw new EPException(getMessageExpecting(eventTypeName, existingType, "Map")); } return adapterForTypedMap(theEvent, existingType); } public EventBean adapterForObjectArray(Object[] theEvent, String eventTypeName) throws EPException { EventType existingType = nameToTypeMap.get(eventTypeName); if (!(existingType instanceof ObjectArrayEventType)) { throw new EPException(getMessageExpecting(eventTypeName, existingType, "Object-array")); } return adapterForTypedObjectArray(theEvent, existingType); } public EventBean adapterForDOM(Node node) { Node namedNode; if (node instanceof Document) { namedNode = ((Document) node).getDocumentElement(); } else if (node instanceof Element) { namedNode = node; } else { throw new EPException("Unexpected DOM node of type '" + node.getClass() + "' encountered, please supply a Document or Element node"); } String rootElementName = namedNode.getLocalName(); if (rootElementName == null) { rootElementName = namedNode.getNodeName(); } EventType eventType = xmldomRootElementNames.get(rootElementName); if (eventType == null) { throw new EventAdapterException("DOM event root element name '" + rootElementName + "' has not been configured"); } return new XMLEventBean(namedNode, eventType); } public EventBean adapterForTypedDOM(Node node, EventType eventType) { return new XMLEventBean(node, eventType); } /** * Add a configured XML DOM event type. * @param eventTypeName is the name name of the event type * @param configurationEventTypeXMLDOM configures the event type schema and namespace and XPath * property information. */ public synchronized EventType addXMLDOMType(String eventTypeName, ConfigurationEventTypeXMLDOM configurationEventTypeXMLDOM, SchemaModel optionalSchemaModel, boolean isPreconfiguredStatic) { return addXMLDOMType(eventTypeName, configurationEventTypeXMLDOM, optionalSchemaModel, isPreconfiguredStatic, false); } @Override public EventType replaceXMLEventType(String xmlEventTypeName, ConfigurationEventTypeXMLDOM config, SchemaModel schemaModel) { return addXMLDOMType(xmlEventTypeName, config, schemaModel, false, true); } /** * Add a configured XML DOM event type. * @param eventTypeName is the name name of the event type * @param configurationEventTypeXMLDOM configures the event type schema and namespace and XPath * property information. */ private synchronized EventType addXMLDOMType(String eventTypeName, ConfigurationEventTypeXMLDOM configurationEventTypeXMLDOM, SchemaModel optionalSchemaModel, boolean isPreconfiguredStatic, boolean allowOverrideExisting) { if (configurationEventTypeXMLDOM.getRootElementName() == null) { throw new EventAdapterException("Required root element name has not been supplied"); } if (!allowOverrideExisting) { EventType existingType = nameToTypeMap.get(eventTypeName); if (existingType != null) { String message = "Event type named '" + eventTypeName + "' has already been declared with differing column name or type information"; if (!(existingType instanceof BaseXMLEventType)) { throw new EventAdapterException(message); } ConfigurationEventTypeXMLDOM config = ((BaseXMLEventType) existingType).getConfigurationEventTypeXMLDOM(); if (!config.equals(configurationEventTypeXMLDOM)) { throw new EventAdapterException(message); } return existingType; } } EventTypeMetadata metadata = EventTypeMetadata.createXMLType(eventTypeName, isPreconfiguredStatic, configurationEventTypeXMLDOM.getSchemaResource() == null && configurationEventTypeXMLDOM.getSchemaText() == null); EventType type; if ((configurationEventTypeXMLDOM.getSchemaResource() == null) && (configurationEventTypeXMLDOM.getSchemaText() == null)) { type = new SimpleXMLEventType(metadata, eventTypeIdGenerator.getTypeId(eventTypeName), configurationEventTypeXMLDOM, this); } else { if (optionalSchemaModel == null) { throw new EPException("Schema model has not been provided"); } type = new SchemaXMLEventType(metadata, eventTypeIdGenerator.getTypeId(eventTypeName), configurationEventTypeXMLDOM, optionalSchemaModel, this); } nameToTypeMap.put(eventTypeName, type); xmldomRootElementNames.put(configurationEventTypeXMLDOM.getRootElementName(), type); return type; } public final EventBean adapterForType(Object theEvent, EventType eventType) { return EventAdapterServiceHelper.adapterForType(theEvent, eventType, this); } public final EventBean adapterForTypedMap(Map<String, Object> properties, EventType eventType) { return new MapEventBean(properties, eventType); } public final EventBean adapterForTypedObjectArray(Object[] properties, EventType eventType) { return new ObjectArrayEventBean(properties, eventType); } public synchronized EventType addWrapperType(String eventTypeName, EventType underlyingEventType, Map<String, Object> propertyTypes, boolean isNamedWindow, boolean isInsertInto) throws EventAdapterException { // If we are wrapping an underlying type that is itself a wrapper, then this is a special case if (underlyingEventType instanceof WrapperEventType) { WrapperEventType underlyingWrapperType = (WrapperEventType) underlyingEventType; // the underlying type becomes the type already wrapped // properties are a superset of the wrapped properties and the additional properties underlyingEventType = underlyingWrapperType.getUnderlyingEventType(); Map<String, Object> propertiesSuperset = new HashMap<String, Object>(); propertiesSuperset.putAll(underlyingWrapperType.getUnderlyingMapType().getTypes()); propertiesSuperset.putAll(propertyTypes); propertyTypes = propertiesSuperset; } boolean isPropertyAgnostic = false; if (underlyingEventType instanceof EventTypeSPI) { isPropertyAgnostic = ((EventTypeSPI) underlyingEventType).getMetadata().isPropertyAgnostic(); } EventTypeMetadata metadata = EventTypeMetadata.createWrapper(eventTypeName, isNamedWindow, isInsertInto, isPropertyAgnostic); int typeId = eventTypeIdGenerator.getTypeId(eventTypeName); WrapperEventType newEventType = new WrapperEventType(metadata, eventTypeName, typeId, underlyingEventType, propertyTypes, this); EventType existingType = nameToTypeMap.get(eventTypeName); if (existingType != null) { // The existing type must be the same as the type created if (!newEventType.equalsCompareType(existingType)) { // It is possible that the wrapped event type is compatible: a child type of the desired type String message = isCompatibleWrapper(existingType, underlyingEventType, propertyTypes); if (message == null) { return existingType; } throw new EventAdapterException("Event type named '" + eventTypeName + "' has already been declared with differing column name or type information: " + message); } // Since it's the same, return the existing type return existingType; } nameToTypeMap.put(eventTypeName, newEventType); return newEventType; } /** * Returns true if the wrapper type is compatible with an existing wrapper type, for the reason that * the underlying event is a subtype of the existing underlying wrapper's type. * @param existingType is the existing wrapper type * @param underlyingType is the proposed new wrapper type's underlying type * @param propertyTypes is the additional properties * @return true for compatible, or false if not */ public static String isCompatibleWrapper(EventType existingType, EventType underlyingType, Map<String, Object> propertyTypes) { if (!(existingType instanceof WrapperEventType)) { return "Type '" + existingType.getName() + "' is not compatible"; } WrapperEventType existingWrapper = (WrapperEventType) existingType; String message = MapEventType.isDeepEqualsProperties(existingType.getName(), existingWrapper.getUnderlyingMapType().getTypes(), propertyTypes); if (message != null) { return message; } EventType existingUnderlyingType = existingWrapper.getUnderlyingEventType(); // If one of the supertypes of the underlying type is the existing underlying type, we are compatible if (underlyingType.getSuperTypes() == null) { return "Type '" + existingType.getName() + "' is not compatible"; } for (Iterator<EventType> it = underlyingType.getDeepSuperTypes(); it.hasNext();) { EventType superUnderlying = it.next(); if (superUnderlying == existingUnderlyingType) { return null; } } return "Type '" + existingType.getName() + "' is not compatible"; } public final EventType createAnonymousMapType(String typeName, Map<String, Object> propertyTypes) throws EventAdapterException { String assignedTypeName = EventAdapterService.ANONYMOUS_TYPE_NAME_PREFIX + typeName; EventTypeMetadata metadata = EventTypeMetadata.createAnonymous(assignedTypeName); return new MapEventType(metadata, assignedTypeName, eventTypeIdGenerator.getTypeId(assignedTypeName), this, propertyTypes, null, null, null); } public final EventType createAnonymousObjectArrayType(String typeName, Map<String, Object> propertyTypes) throws EventAdapterException { String assignedTypeName = EventAdapterService.ANONYMOUS_TYPE_NAME_PREFIX + typeName; EventTypeMetadata metadata = EventTypeMetadata.createAnonymous(assignedTypeName); return new ObjectArrayEventType(metadata, assignedTypeName, eventTypeIdGenerator.getTypeId(assignedTypeName), this, propertyTypes, null, null, null); } public EventType createSemiAnonymousMapType(String typeName, Map<String, Pair<EventType, String>> taggedEventTypes, Map<String, Pair<EventType, String>> arrayEventTypes, boolean isUsedByChildViews) { Map<String, Object> mapProperties = new LinkedHashMap<String, Object>(); for (Map.Entry<String, Pair<EventType, String>> entry : taggedEventTypes.entrySet()) { mapProperties.put(entry.getKey(), entry.getValue().getFirst()); } for (Map.Entry<String, Pair<EventType, String>> entry : arrayEventTypes.entrySet()) { mapProperties.put(entry.getKey(), new EventType[] {entry.getValue().getFirst()}); } return createAnonymousMapType(typeName, mapProperties); } public final EventType createAnonymousWrapperType(String typeName, EventType underlyingEventType, Map<String, Object> propertyTypes) throws EventAdapterException { String assignedTypeName = EventAdapterService.ANONYMOUS_TYPE_NAME_PREFIX + typeName; EventTypeMetadata metadata = EventTypeMetadata.createAnonymous(assignedTypeName); // If we are wrapping an underlying type that is itself a wrapper, then this is a special case: unwrap if (underlyingEventType instanceof WrapperEventType) { WrapperEventType underlyingWrapperType = (WrapperEventType) underlyingEventType; // the underlying type becomes the type already wrapped // properties are a superset of the wrapped properties and the additional properties underlyingEventType = underlyingWrapperType.getUnderlyingEventType(); Map<String, Object> propertiesSuperset = new HashMap<String, Object>(); propertiesSuperset.putAll(underlyingWrapperType.getUnderlyingMapType().getTypes()); propertiesSuperset.putAll(propertyTypes); propertyTypes = propertiesSuperset; } return new WrapperEventType(metadata, assignedTypeName, eventTypeIdGenerator.getTypeId(assignedTypeName), underlyingEventType, propertyTypes, this); } public final EventBean adapterForTypedWrapper(EventBean theEvent, Map<String, Object> properties, EventType eventType) { if (theEvent instanceof DecoratingEventBean) { DecoratingEventBean wrapper = (DecoratingEventBean) theEvent; properties.putAll(wrapper.getDecoratingProperties()); return new WrapperEventBean(wrapper.getUnderlyingEvent(), properties, eventType); } else { return new WrapperEventBean(theEvent, properties, eventType); } } public final EventBean adapterForTypedBean(Object bean, EventType eventType) { return new BeanEventBean(bean, eventType); } public void addAutoNamePackage(String javaPackageName) { javaPackageNames.add(javaPackageName); } public EventType createAnonymousBeanType(String eventTypeName, Class clazz) { return new BeanEventType(EventTypeMetadata.createBeanType(eventTypeName, clazz, false, false, false, EventTypeMetadata.TypeClass.ANONYMOUS), -1, clazz, this, beanEventAdapter.getClassToLegacyConfigs(clazz.getName())); } private Pair<EventType[], Set<EventType>> getSuperTypesDepthFirst(Set<String> superTypesSet, boolean expectMapType) throws EventAdapterException { if (superTypesSet == null || superTypesSet.isEmpty()) { return new Pair<EventType[], Set<EventType>>(null,null); } EventType[] superTypes = new EventType[superTypesSet.size()]; Set<EventType> deepSuperTypes = new LinkedHashSet<EventType>(); int count = 0; for (String superName : superTypesSet) { EventType type = this.nameToTypeMap.get(superName); if (type == null) { throw new EventAdapterException("Supertype by name '" + superName + "' could not be found"); } if (expectMapType) { if (!(type instanceof MapEventType)) { throw new EventAdapterException("Supertype by name '" + superName + "' is not a Map, expected a Map event type as a supertype"); } } else { if (!(type instanceof ObjectArrayEventType)) { throw new EventAdapterException("Supertype by name '" + superName + "' is not an Object-array type, expected a Object-array event type as a supertype"); } } superTypes[count++] = type; deepSuperTypes.add(type); addRecursiveSupertypes(deepSuperTypes, type); } List<EventType> superTypesListDepthFirst = new ArrayList<EventType>(deepSuperTypes); Collections.reverse(superTypesListDepthFirst); return new Pair<EventType[], Set<EventType>>(superTypes,new LinkedHashSet<EventType>(superTypesListDepthFirst)); } private static void addRecursiveSupertypes(Set<EventType> superTypes, EventType child) { if (child.getSuperTypes() != null) { for (int i = 0; i < child.getSuperTypes().length; i++) { superTypes.add(child.getSuperTypes()[i]); addRecursiveSupertypes(superTypes, child.getSuperTypes()[i]); } } } public EventBean[] typeCast(List<EventBean> events, EventType targetType) { return EventAdapterServiceHelper.typeCast(events, targetType, this); } public boolean removeType(String name) { EventType eventType = nameToTypeMap.remove(name); if (eventType == null) { return false; } if (eventType instanceof BaseXMLEventType) { BaseXMLEventType baseXML = (BaseXMLEventType) eventType; xmldomRootElementNames.remove(baseXML.getRootElementName()); } nameToHandlerMap.remove(name); return true; } public EventBeanSPI getShellForType(EventType eventType) { return EventAdapterServiceHelper.getShellForType(eventType); } private String getMessageExpecting(String eventTypeName, EventType existingType, String typeOfEventType) { String message = "Event type named '" + eventTypeName + "' has not been defined or is not a " + typeOfEventType + " event type"; if (existingType != null) { message += ", the name '" + eventTypeName + "' refers to a " + JavaClassHelper.getClassNameFullyQualPretty(existingType.getUnderlyingType()) + " event type"; } else { message += ", the name '" + eventTypeName + "' has not been defined as an event type"; } return message; } public EventBeanAdapterFactory getAdapterFactoryForType(EventType eventType) { return EventAdapterServiceHelper.getAdapterFactoryForType(eventType); } }