/******************************************************************************* * Copyright (c) 2011 GigaSpaces Technologies Ltd. All rights reserved * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. *******************************************************************************/ package org.cloudifysource.dsl.internal; import groovy.lang.Closure; import groovy.lang.Script; import java.beans.PropertyDescriptor; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.BeanUtilsBean; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.lang.StringUtils; import org.cloudifysource.domain.AppSharedIsolationSLADescriptor; import org.cloudifysource.domain.Application; import org.cloudifysource.domain.BalanceGauge; import org.cloudifysource.domain.BarLineChart; import org.cloudifysource.domain.ComputeDetails; import org.cloudifysource.domain.DSLValidation; import org.cloudifysource.domain.DataGrid; import org.cloudifysource.domain.DedicatedIsolationSLADescriptor; import org.cloudifysource.domain.ExecutableEntriesMap; import org.cloudifysource.domain.GlobalIsolationSLADescriptor; import org.cloudifysource.domain.IsolationSLA; import org.cloudifysource.domain.Memcached; import org.cloudifysource.domain.MetricGroup; import org.cloudifysource.domain.MirrorProcessingUnit; import org.cloudifysource.domain.PluginDescriptor; import org.cloudifysource.domain.Service; import org.cloudifysource.domain.ServiceLifecycle; import org.cloudifysource.domain.ServiceNetwork; import org.cloudifysource.domain.Sla; import org.cloudifysource.domain.StatefulProcessingUnit; import org.cloudifysource.domain.StatelessProcessingUnit; import org.cloudifysource.domain.StorageDetails; import org.cloudifysource.domain.TenantSharedIsolationSLADescriptor; import org.cloudifysource.domain.UserInterface; import org.cloudifysource.domain.WidgetGroup; import org.cloudifysource.domain.cloud.AgentComponent; import org.cloudifysource.domain.cloud.Cloud; import org.cloudifysource.domain.cloud.CloudConfiguration; import org.cloudifysource.domain.cloud.CloudProvider; import org.cloudifysource.domain.cloud.CloudTemplateInstallerConfiguration; import org.cloudifysource.domain.cloud.CloudUser; import org.cloudifysource.domain.cloud.DeployerComponent; import org.cloudifysource.domain.cloud.DiscoveryComponent; import org.cloudifysource.domain.cloud.GridComponents; import org.cloudifysource.domain.cloud.OrchestratorComponent; import org.cloudifysource.domain.cloud.RestComponent; import org.cloudifysource.domain.cloud.UsmComponent; import org.cloudifysource.domain.cloud.WebuiComponent; import org.cloudifysource.domain.cloud.compute.CloudCompute; import org.cloudifysource.domain.cloud.compute.ComputeTemplate; import org.cloudifysource.domain.cloud.compute.ComputeTemplateNetwork; import org.cloudifysource.domain.cloud.network.CloudNetwork; import org.cloudifysource.domain.cloud.network.ManagementNetwork; import org.cloudifysource.domain.cloud.network.NetworkConfiguration; import org.cloudifysource.domain.cloud.network.Subnet; import org.cloudifysource.domain.cloud.storage.CloudStorage; import org.cloudifysource.domain.cloud.storage.StorageTemplate; import org.cloudifysource.domain.entry.ExecutableDSLEntry; import org.cloudifysource.domain.internal.CloudifyDSLEntity; import org.cloudifysource.domain.network.AccessRule; import org.cloudifysource.domain.network.AccessRules; import org.cloudifysource.domain.scalingrules.HighThresholdDetails; import org.cloudifysource.domain.scalingrules.LowThresholdDetails; import org.cloudifysource.domain.scalingrules.ScalingRuleDetails; import org.cloudifysource.domain.statistics.PerInstanceStatisticsDetails; import org.cloudifysource.domain.statistics.ServiceStatisticsDetails; import org.cloudifysource.dsl.entry.ExecutableDSLEntryFactory; import org.cloudifysource.dsl.internal.validators.DSLValidationFactory; import org.cloudifysource.dsl.internal.validators.DSLValidator; import org.cloudifysource.dsl.utils.RecipePathResolver; /************* * Base class for DSL files. * * @author barakme * @since 1.0 * */ public abstract class BaseDslScript extends Script { private static java.util.logging.Logger logger = java.util.logging.Logger.getLogger(BaseDslScript.class.getName()); /******** * DSL property indicating extension of recipe. */ public static final String EXTEND_PROPERTY_NAME = "extend"; private Set<String> processingUnitTypes; private String processingUnitType; protected Object activeObject = null; private Object rootObject; private int propertyCounter; private Set<String> usedProperties = new HashSet<String>(); // used by the 'print' groovy method. Entries are buffered until a println is called. private final StringBuilder printBuilder = new StringBuilder(); // the current active DSL entity - an active object may be a list or a map // but the active entity is always a DSL entity or null protected Object activeEntity; /******** * syntactic sigar for an empty list that process locator implementations can use to specify an empty process IDs * list. */ public static final List<Long> NO_PROCESS_LOCATORS = new LinkedList<Long>(); // DSL Initializer meta data private static Map<String, DSLObjectInitializerData> dslObjectInitializersByName = null; public BaseDslScript() { BeanUtilsBean.getInstance().getConvertUtils().register(true, false, 0); } @Override public void setProperty(final String name, final Object value) { if (this.activeObject == null) { super.setProperty(name, value); return; } if (value.getClass().isArray()) { final Object[] arr = (Object[]) value; if (arr.length > 1) { throw new IllegalArgumentException("Property assignment of field: " + name + " received an array with more then one item: " + Arrays.toString(arr)); } applyPropertyToObject(this.activeObject, name, arr[0]); } else { applyPropertyToObject(this.activeObject, name, value); } } private boolean isDuplicatePropertyAllowed(final Object value) { // Application allows duplicate service values. return this.activeObject instanceof Application && value instanceof Service; } private static boolean isProperyExistsInBean(final Object bean, final String propertyName) { if (bean == null) { throw new NullPointerException("Got a null reference to a bean while checking if a bean has the property: " + propertyName); } try { // first check that the property exists BeanUtils.getProperty(bean, propertyName); return true; } catch (final Exception e) { return false; } } @SuppressWarnings({ "unchecked", "rawtypes" }) private void applyPropertyToObject(final Object object, final String name, final Object value) { if (object instanceof Map<?, ?>) { Map<Object, Object> map = (Map<Object, Object>) object; map.put(name, value); return; } if (!isProperyExistsInBean(object, name)) { throw new IllegalArgumentException("Could not find property: " + name + " on Object: " + object); } // Check for duplicate properties. if (this.usedProperties == null) { throw new IllegalArgumentException("used properties can not be null. Property: " + name + ", Value: " + value.toString() + ", Active object: " + this.activeObject); } if (this.usedProperties.contains(name)) { if (!isDuplicatePropertyAllowed(value)) { throw new IllegalArgumentException("Property duplication was found: Property " + name + " is define" + "d more than once."); } } this.usedProperties.add(name); Object convertedValue = null; try { convertedValue = convertValueToExecutableDSLEntryIfNeeded(getDSLFile().getParentFile(), object, name, value); if (logger.isLoggable(Level.FINEST)) { logger.finest("BeanUtils.setProperty(object=" + object + ",name=" + name + ",value=" + convertedValue + ",value.getClass()=" + convertedValue.getClass()); } // Check if writable if (!PropertyUtils.isWriteable(object, name)) { throw new IllegalArgumentException("Field " + name + " in object of type: " + object.getClass().getName() + " is not writable"); } // If value is a map, merge with existing map final Object currentValue = PropertyUtils.getProperty(object, name); if (currentValue != null && currentValue instanceof Map<?, ?> && convertedValue != null && convertedValue instanceof Map<?, ?>) { final Map<Object, Object> currentMap = (Map<Object, Object>) currentValue; currentMap.putAll((Map<Object, Object>) convertedValue); } else if (PropertyUtils.getPropertyType(object, name).isEnum() && value instanceof String) { final Class enumClass = PropertyUtils.getPropertyType(object, name); final Enum enumValue = Enum.valueOf(enumClass, (String) value); BeanUtils.setProperty(object, name, enumValue); } else { // Then set it BeanUtils.setProperty(object, name, convertedValue); } } catch (final DSLValidationException e) { throw new DSLValidationRuntimeException(e); } catch (final Exception e) { throw new IllegalArgumentException("Failed to set property " + name + " of Object " + object + " to value: " + value + ". Error was: " + e.getMessage(), e); } checkForApplicationServiceBlockNameParameter(name, value); } /** * Convert the value to an ExecutableDSLEntry object if object's property type is ExecutableDSLEntry or * ExecutableEntriesMap. Returns value otherwise. * * @param workDirectory * workDirectory * @param object * object * @param name * property name * @param value * property value * @return The converted object * @throws IllegalAccessException * IllegalAccessException * @throws InvocationTargetException * InvocationTargetException * @throws NoSuchMethodException * NoSuchMethodException * @throws DSLValidationException * DSLValidationException */ public Object convertValueToExecutableDSLEntryIfNeeded(final File workDirectory, final Object object, final String name, final Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, DSLValidationException { final PropertyDescriptor descriptor = PropertyUtils.getPropertyDescriptor(object, name); final Class<?> propertyType = descriptor.getPropertyType(); if (propertyType.equals(ExecutableDSLEntry.class)) { final ExecutableDSLEntry entry = ExecutableDSLEntryFactory.createEntry(value, name, workDirectory); // if (entry.getEntryType() == ExecutableDSLEntryType.STRING) { // // handleDebugEntry(entry); // // } return entry; } else if (propertyType.equals(ExecutableEntriesMap.class)) { return ExecutableDSLEntryFactory.createEntriesMap(value, name, workDirectory); } else { return value; } } private File getDSLFile() { return new File((String) this.getBinding().getVariable(DSLUtils.DSL_FILE_PATH_PROPERTY_NAME)); } private boolean isValidateObjects() { return (Boolean) this.getBinding().getVariable(DSLUtils.DSL_VALIDATE_OBJECTS_PROPERTY_NAME); } @Override public Object invokeMethod(final String name, final Object arg) { beforeHandleInvokeMethod(name, arg); final Object[] arr = (Object[]) arg; final Object param = arr[0]; // check if this is an object declaration if (param instanceof Closure<?>) { @SuppressWarnings("unchecked") final Closure<Object> closure = (Closure<Object>) param; Object retval; try { retval = dslObject(name); } catch (final DSLException e) { throw new IllegalArgumentException("Failed to set: " + name, e); } if(retval == null) { final Object targetMap = getMapOrListProperty(name, retval); if(targetMap != null) { retval = targetMap; } } if (retval != null) { if (this.rootObject == null) { this.rootObject = retval; } swapActiveObject(closure, retval); if (isValidateObjects()) { try { validateObject(retval); } catch (final DSLValidationException e) { throw new DSLValidationRuntimeException(e); } } if (this.activeObject != null) { try { setProperty(name, retval); } catch (final IllegalArgumentException e) { // this will happen every time there is a dsl object // declaration // inside something like a groovy map or list. this.usedProperties.remove(name); } } return retval; } } try { if (handleSpecialProperty(name, arg)) { return null; } } catch (final DSLException e) { throw new IllegalArgumentException("Failed to set " + name + ": " + e.getMessage(), e); } // not an object declaration setProperty(name, arg); return null; } private Object getMapOrListProperty(final String name, Object retval) { // check for a map initializer if (this.activeObject != null) { try { PropertyDescriptor descriptor = PropertyUtils.getPropertyDescriptor(this.activeObject, name); if(descriptor == null) { return null; } if (Map.class.isAssignableFrom(descriptor.getPropertyType()) || List.class.isAssignableFrom(descriptor.getPropertyType())) { final Object target = PropertyUtils.getProperty(this.activeObject, name); if(target == null) { throw new IllegalStateException("The field " + name + " in entity " + this.activeObject.getClass() + " was not initialized. It should be initialized to an empty list/map!"); } return target; } } catch (IllegalAccessException e) { throw new IllegalStateException(e); } catch (InvocationTargetException e) { throw new IllegalStateException(e); } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } } return null; } /************* * Loads an object from an external file. * * @param fileName * the filename, relative to the current file. * @return the object. */ public Object load(final String fileName) { final String dslFilePath = (String) getProperty(ServiceReader.DSL_FILE_PATH_PROPERTY_NAME); if (dslFilePath == null) { throw new IllegalStateException("No dsl file path present in binding context"); } final File activeServiceDirectory = new File(dslFilePath).getParentFile(); final File externalFile = new File(activeServiceDirectory, fileName); if (!externalFile.exists()) { throw new IllegalArgumentException("While processing DSL file, could not find file to load " + fileName); } if (!externalFile.isFile()) { throw new IllegalArgumentException("While processing DSL file, could not load file " + fileName + " as it is not a file"); } Object result; try { result = readExternalDSLFile(externalFile); if (result instanceof Closure<?>) { final Closure<?> closure = (Closure<?>) result; closure.setDelegate(this); closure.setResolveStrategy(Closure.DELEGATE_ONLY); } return result; } catch (final DSLException e) { throw new IllegalArgumentException("Failed to load external DSL file: " + fileName, e); } } private Object readExternalDSLFile(final File externalFile) throws DSLException { @SuppressWarnings("unchecked") final Map<Object, Object> currentVars = this.getBinding().getVariables(); final DSLReader dslReader = new DSLReader(); dslReader.setBindingVariables(currentVars); dslReader.setContext(null); dslReader.setCreateServiceContext(false); dslReader.setDslFile(externalFile); dslReader.setPropertiesFileName(null); final Object result = dslReader.readDslEntity(Object.class); return result; } private void validateObject(final Object entity) throws DSLValidationException { final DSLValidator obj = DSLValidationFactory.getInstance().createValidator(entity); if (obj == null) { return; } final Method[] methods = obj.getClass().getDeclaredMethods(); for (final Method method : methods) { if (method.getAnnotation(DSLValidation.class) != null) { final boolean accessible = method.isAccessible(); try { @SuppressWarnings("unchecked") final Map<Object, Object> currentVars = this.getBinding().getVariables(); final DSLValidationContext validationContext = new DSLValidationContext(); validationContext.setFilePath((String) currentVars.get(DSLUtils.DSL_FILE_PATH_PROPERTY_NAME)); method.setAccessible(true); method.invoke(obj, validationContext); } catch (final InvocationTargetException e) { throw new DSLValidationException(e.getTargetException().getMessage(), e.getTargetException()); } catch (final Exception e) { throw new DSLValidationException("Failed to execute DSL validation: " + e.getMessage(), e); } finally { method.setAccessible(accessible); } } } } private void beforeHandleInvokeMethod(final String name, final Object arg) { if (name.equals("service")) { propertyCounter = 0; } else { propertyCounter++; } } private boolean handleSpecialProperty(final String name, final Object arg) throws DSLException { Object localArg = arg; if (name.equals(EXTEND_PROPERTY_NAME)) { if (propertyCounter > 1) { throw new DSLException(EXTEND_PROPERTY_NAME + " must be first inside the service block"); } if (arg != null && arg.getClass().isArray()) { final Object[] arr = (Object[]) arg; if (arr.length != 1) { throw new DSLException(EXTEND_PROPERTY_NAME + " property must be a single string"); } localArg = ((Object[]) arg)[0]; } if (!(localArg instanceof String)) { throw new DSLException(EXTEND_PROPERTY_NAME + " property must be a string"); } if (!(this.activeObject instanceof Service)) { throw new DSLException(EXTEND_PROPERTY_NAME + " property can only be used on a service"); } final String extendServicePath = (String) localArg; try { File extendedServiceAbsPath = new File(extendServicePath); final RecipePathResolver resolver = new RecipePathResolver(); // Extract the current service directory final String dslFilePath = (String) getProperty(ServiceReader.DSL_FILE_PATH_PROPERTY_NAME); if (dslFilePath == null) { throw new IllegalStateException("No dsl file path present in binding context"); } final File activeServiceDirectory = new File(dslFilePath).getParentFile(); resolver.setCurrentDirectory(activeServiceDirectory); if (resolver.resolveService(extendedServiceAbsPath)) { extendedServiceAbsPath = resolver.getResolved(); } else { throw new DSLException("could not find extended service in paths " + StringUtils.join(resolver.getPathsLooked().toArray(), ", ")); } if (logger.isLoggable(Level.FINER)) { logger.finer("reading extended service file [" + extendedServiceAbsPath + "]"); } // Read the extended service final Service baseService = readServiceToExtend(extendedServiceAbsPath); // ServiceReader.readService(extendedServiceAbsPath); // Populate the current service with the extended service BeanUtils.copyProperties(this.activeObject, baseService); final Service activeService = (Service) activeObject; // Add extended service to the extension list activeService.getExtendedServicesPaths().addFirst(extendedServiceAbsPath.getAbsolutePath()); return true; } catch (final IllegalAccessException e) { throw new DSLException("Failed to parse extended service: " + extendServicePath, e); } catch (final InvocationTargetException e) { throw new DSLException("Failed to parse extended service: " + extendServicePath, e); } } return false; } private Service readServiceToExtend(final File serviceFileToExtend) throws DSLException { @SuppressWarnings("unchecked") final Map<Object, Object> currentVars = this.getBinding().getVariables(); final DSLReader dslReader = new DSLReader(); dslReader.setBindingVariables(currentVars); dslReader.setContext(null); dslReader.setCreateServiceContext(false); if (serviceFileToExtend.isDirectory()) { dslReader.setWorkDir(serviceFileToExtend); } else { dslReader.setDslFile(serviceFileToExtend); } dslReader.setDslFileNameSuffix(DSLUtils.SERVICE_DSL_FILE_NAME_SUFFIX); // dslReader.setLoadUsmLib(true) dslReader.setPropertiesFileName(null); final Service service = dslReader.readDslEntity(Service.class); return service; } /** * * */ public static class DSLObjectInitializerData { private final Class<?> clazz; private final boolean allowRootNode; private final boolean allowInternalNode; private final String name; private final String parentElement; public DSLObjectInitializerData(final String name, final Class<?> clazz, final boolean allowRootNode, final boolean allowInternalNode, final String parentElement) { super(); this.name = name; this.clazz = clazz; this.allowRootNode = allowRootNode; this.allowInternalNode = allowInternalNode; this.parentElement = parentElement; } public String getParentElement() { return parentElement; } public boolean isAllowRootNode() { return allowRootNode; } public boolean isAllowInternalNode() { return allowInternalNode; } public Class<?> getClazz() { return clazz; } public String getName() { return name; } } private static void addObjectInitializerForClass(final Map<String, DSLObjectInitializerData> map, final Class<?> clazz) { final CloudifyDSLEntity entityDetails = clazz.getAnnotation(CloudifyDSLEntity.class); if (entityDetails == null) { throw new IllegalStateException("Incorrect configuration - class " + clazz.getName() + " is not a DSL entity"); } map.put(entityDetails.name(), new DSLObjectInitializerData(entityDetails.name(), entityDetails.clazz(), entityDetails.allowRootNode(), entityDetails.allowInternalNode(), entityDetails.parent())); } /*********** * Returns the DSL Meta-data required to translate DSL elements into POJOs. * * @return DSL meta-data. */ public static synchronized Map<String, DSLObjectInitializerData> getDSLInitializers() { if (dslObjectInitializersByName == null) { dslObjectInitializersByName = new HashMap<String, BaseDslScript.DSLObjectInitializerData>(); addObjectInitializerForClass(dslObjectInitializersByName, Application.class); addObjectInitializerForClass(dslObjectInitializersByName, Service.class); addObjectInitializerForClass(dslObjectInitializersByName, PluginDescriptor.class); addObjectInitializerForClass(dslObjectInitializersByName, ServiceNetwork.class); addObjectInitializerForClass(dslObjectInitializersByName, DataGrid.class); addObjectInitializerForClass(dslObjectInitializersByName, Memcached.class); addObjectInitializerForClass(dslObjectInitializersByName, ServiceLifecycle.class); addObjectInitializerForClass(dslObjectInitializersByName, StatefulProcessingUnit.class); addObjectInitializerForClass(dslObjectInitializersByName, StatelessProcessingUnit.class); addObjectInitializerForClass(dslObjectInitializersByName, MirrorProcessingUnit.class); addObjectInitializerForClass(dslObjectInitializersByName, Cloud.class); addObjectInitializerForClass(dslObjectInitializersByName, CloudProvider.class); addObjectInitializerForClass(dslObjectInitializersByName, CloudUser.class); addObjectInitializerForClass(dslObjectInitializersByName, ComputeTemplate.class); addObjectInitializerForClass(dslObjectInitializersByName, CloudCompute.class); addObjectInitializerForClass(dslObjectInitializersByName, CloudConfiguration.class); addObjectInitializerForClass(dslObjectInitializersByName, ComputeDetails.class); addObjectInitializerForClass(dslObjectInitializersByName, CloudStorage.class); addObjectInitializerForClass(dslObjectInitializersByName, StorageTemplate.class); addObjectInitializerForClass(dslObjectInitializersByName, StorageDetails.class); addObjectInitializerForClass(dslObjectInitializersByName, ComputeDetails.class); addObjectInitializerForClass(dslObjectInitializersByName, Sla.class); addObjectInitializerForClass(dslObjectInitializersByName, AccessRules.class); addObjectInitializerForClass(dslObjectInitializersByName, AccessRule.class); dslObjectInitializersByName.put("userInterface", new DSLObjectInitializerData("userInterface", UserInterface.class, true, true, "service")); dslObjectInitializersByName.put("metricGroup", new DSLObjectInitializerData("metricGroup", MetricGroup.class, false, true, "userInterface")); dslObjectInitializersByName.put("widgetGroup", new DSLObjectInitializerData("widgetGroup", WidgetGroup.class, false, true, "userInterface")); dslObjectInitializersByName.put("balanceGauge", new DSLObjectInitializerData("balanceGauge", BalanceGauge.class, false, true, "widgetGroup")); dslObjectInitializersByName.put("barLineChart", new DSLObjectInitializerData("barLineChart", BarLineChart.class, false, true, "widgetGroup")); addObjectInitializerForClass(dslObjectInitializersByName, ScalingRuleDetails.class); addObjectInitializerForClass(dslObjectInitializersByName, HighThresholdDetails.class); addObjectInitializerForClass(dslObjectInitializersByName, LowThresholdDetails.class); addObjectInitializerForClass(dslObjectInitializersByName, ServiceStatisticsDetails.class); addObjectInitializerForClass(dslObjectInitializersByName, PerInstanceStatisticsDetails.class); addObjectInitializerForClass(dslObjectInitializersByName, IsolationSLA.class); addObjectInitializerForClass(dslObjectInitializersByName, GlobalIsolationSLADescriptor.class); addObjectInitializerForClass(dslObjectInitializersByName, TenantSharedIsolationSLADescriptor.class); addObjectInitializerForClass(dslObjectInitializersByName, AppSharedIsolationSLADescriptor.class); addObjectInitializerForClass(dslObjectInitializersByName, DedicatedIsolationSLADescriptor.class); addObjectInitializerForClass(dslObjectInitializersByName, GridComponents.class); addObjectInitializerForClass(dslObjectInitializersByName, OrchestratorComponent.class); addObjectInitializerForClass(dslObjectInitializersByName, DiscoveryComponent.class); addObjectInitializerForClass(dslObjectInitializersByName, DeployerComponent.class); addObjectInitializerForClass(dslObjectInitializersByName, WebuiComponent.class); addObjectInitializerForClass(dslObjectInitializersByName, UsmComponent.class); addObjectInitializerForClass(dslObjectInitializersByName, RestComponent.class); addObjectInitializerForClass(dslObjectInitializersByName, AgentComponent.class); addObjectInitializerForClass(dslObjectInitializersByName, CloudTemplateInstallerConfiguration.class); addObjectInitializerForClass(dslObjectInitializersByName, CloudNetwork.class); addObjectInitializerForClass(dslObjectInitializersByName, ManagementNetwork.class); addObjectInitializerForClass(dslObjectInitializersByName, NetworkConfiguration.class); addObjectInitializerForClass(dslObjectInitializersByName, Subnet.class); addObjectInitializerForClass(dslObjectInitializersByName, ComputeTemplateNetwork.class); } return dslObjectInitializersByName; } private Object dslObject(final String name) throws DSLException { final DSLObjectInitializerData data = getDSLInitializers().get(name); if (data == null) { return null; } if (isDuplicateProcessingUnit(name)) { throw new DSLException("There may only be one type of processing unit defined. Found more than one: " + "[" + name + ", " + this.processingUnitType + "]"); } if (this.activeObject == null) { // root node if (!data.isAllowRootNode()) { throw new DSLException("Elements of type " + name + " may not be used as the root node of a DSL"); } } else { // internal node if (data.isAllowInternalNode()) { // check that node is nested under allowed element final String parentElement = data.getParentElement(); if (parentElement != null && !parentElement.isEmpty()) { final DSLObjectInitializerData parentType = getDSLInitializers().get(parentElement); if (parentType != null) { // element must be nested under a specific parent type if (!parentType.getClazz().isAssignableFrom(this.activeEntity.getClass())) { throw new DSLException("The type: " + name + " may only be nested under elements of type " + parentType.getName()); } } } } else { throw new DSLException("Elements of type: " + name + " may not be placed in internal nodes"); } } try { // Check if this is in extend mode. The active object should already // contain a value // for this object, simply clone it so we keep its content. if (this.activeObject != null && !(this.activeObject instanceof Application) && isProperyExistsInBean(this.activeObject, name)) { final Object existingPropertyValue = PropertyUtils.getProperty(this.activeObject, name); if (existingPropertyValue != null) { return BeanUtils.cloneBean(existingPropertyValue); } } return data.clazz.newInstance(); } catch (final InstantiationException e) { throw new DSLException("Failed to create new element of type " + data.getName() + " with class: " + data.clazz, e); } catch (final IllegalAccessException e) { throw new DSLException("Failed to create new element of type " + data.getName() + " with class: " + data.clazz, e); } catch (final InvocationTargetException e) { throw new DSLException("Failed to copy existing element of type " + data.getName() + " with class: " + data.clazz, e); } catch (final NoSuchMethodException e) { throw new DSLException("Failed to copy existing element of type " + data.getName() + " with class: " + data.clazz, e); } } // Only one of stateless/stateful/lifecycle may be set private boolean isDuplicateProcessingUnit(final String name) { final Set<String> types = getProcessingUnitTypes(); if (types.contains(name)) { if (StringUtils.isEmpty(this.processingUnitType)) { this.processingUnitType = name; } else { return true; } } return false; } private Set<String> getProcessingUnitTypes() { if (this.processingUnitTypes == null) { this.processingUnitTypes = new HashSet<String>(); this.processingUnitTypes.add("lifecycle"); this.processingUnitTypes.add("statefulProcessingUnit"); this.processingUnitTypes.add("statelessProcessingUnit"); this.processingUnitTypes.add("dataGrid"); this.processingUnitTypes.add("mirrorProcessingUnit"); } return this.processingUnitTypes; } @Override public void println(final Object obj) { if (obj != null) { printBuilder.append(obj.toString()); } logger.info(printBuilder.toString()); // probably performs as well as allocating a new one printBuilder.setLength(0); } @Override public void print(final Object obj) { if (obj != null) { printBuilder.append(obj.toString()); } } private void swapActiveObject(final Closure<Object> closure, final Object obj) { final Object prevObject = this.activeObject; final Set<String> prevSet = this.usedProperties; final Object prevEntity = this.activeEntity; this.activeObject = obj; this.usedProperties = new HashSet<String>(); if( !(obj instanceof List<?>) && !(obj instanceof Map<?, ?>)) { this.activeEntity = obj; } closure.setResolveStrategy(Closure.OWNER_FIRST); closure.call(); activeObject = prevObject; this.usedProperties = prevSet; this.activeEntity = prevEntity; // this is a bit of a hack, but it's the only way to populate dsl entities // into a list inside a closure if (this.activeObject instanceof List<?>) { @SuppressWarnings("unchecked") List<Object> list = (List<Object>) this.activeObject; list.add(obj); } } // ////////////////////////////////////////////////////////////////////////////////// // Special handling for service blocks embedded inside application files // ////////// // ////////////////////////////////////////////////////////////////////////////////// private void checkForApplicationServiceBlockNameParameter(final String propertyName, final Object propertyValue) { // check that we are setting the name property of a service this ia part // of an application if (this.rootObject != null && this.rootObject.getClass().equals(Application.class) && this.activeObject != null && this.activeObject.getClass().equals(Service.class) && propertyName.equals("name")) { final String serviceName = (String) propertyValue; final Service service = loadApplicationService(serviceName); // Override service name with application settings for it service.setName(serviceName); // TODO - must validate that name property was first one to be // applied in this service. try { BeanUtils.copyProperties(this.activeObject, service); } catch (final IllegalAccessException e) { throw new IllegalArgumentException("Failed to load service: " + serviceName, e); } catch (final InvocationTargetException e) { throw new IllegalArgumentException("Failed to load service: " + serviceName, e); } } } @SuppressWarnings("unchecked") private Service loadApplicationService(final String serviceName) { // First find the service dir final String workDirectory = (String) this.getProperty(DSLUtils.APPLICATION_DIR); if (workDirectory == null) { throw new IllegalArgumentException("Work directory was not set while parsing application file"); } final String serviceDirName = workDirectory + File.separator + serviceName; final File serviceDir = new File(serviceDirName); if (!serviceDir.exists() || !serviceDir.isDirectory()) { throw new java.lang.IllegalStateException("Could not find service directory: " + serviceDir + " while loading application"); } // Load the service DSLServiceCompilationResult result; try { final Object applicationProperties = getBinding().getVariables().get(DSLUtils.DSL_PROPERTIES); Map<String, Object> applicationPropertiesMap = null; if (applicationProperties != null) { if (applicationProperties instanceof Map) { applicationPropertiesMap = (Map<String, Object>) applicationProperties; } else { throw new DSLException("applicationProperties must be a map."); } } result = ServiceReader.getApplicationServiceFromDirectory(serviceDir, applicationPropertiesMap); } catch (final DSLException e) { throw new IllegalArgumentException("Failed to load service: " + serviceName + " while loading application: " + e.getMessage(), e); } final Service service = result.getService(); return service; } }