package com.temenos.interaction.sdk; /* * #%L * interaction-sdk * %% * Copyright (C) 2012 - 2013 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.log.NullLogSystem; import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader; import org.odata4j.edm.EdmProperty; import org.odata4j.edm.EdmSimpleType; import org.odata4j.edm.EdmType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.temenos.interaction.core.entity.EntityMetadata; import com.temenos.interaction.core.entity.Metadata; import com.temenos.interaction.core.entity.vocabulary.Term; import com.temenos.interaction.core.entity.vocabulary.Vocabulary; import com.temenos.interaction.core.entity.vocabulary.terms.TermComplexGroup; import com.temenos.interaction.core.entity.vocabulary.terms.TermComplexType; import com.temenos.interaction.core.entity.vocabulary.terms.TermIdField; import com.temenos.interaction.core.entity.vocabulary.terms.TermListType; import com.temenos.interaction.core.entity.vocabulary.terms.TermMandatory; import com.temenos.interaction.core.entity.vocabulary.terms.TermValueType; import com.temenos.interaction.odataext.entity.MetadataOData4j; import com.temenos.interaction.sdk.adapter.InteractionAdapter; import com.temenos.interaction.sdk.adapter.edmx.EDMXAdapter; import com.temenos.interaction.sdk.command.Commands; import com.temenos.interaction.sdk.command.Parameter; import com.temenos.interaction.sdk.entity.EMEntity; import com.temenos.interaction.sdk.entity.EMProperty; import com.temenos.interaction.sdk.entity.EMTerm; import com.temenos.interaction.sdk.entity.EntityModel; import com.temenos.interaction.sdk.interaction.IMResourceStateMachine; import com.temenos.interaction.sdk.interaction.InteractionModel; import com.temenos.interaction.sdk.interaction.state.IMState; import com.temenos.interaction.sdk.interaction.transition.IMCollectionStateTransition; import com.temenos.interaction.sdk.interaction.transition.IMEntityStateTransition; import com.temenos.interaction.sdk.interaction.transition.IMTransition; import com.temenos.interaction.sdk.rimdsl.RimDslGenerator; import com.temenos.interaction.sdk.util.IndentationFormatter; /** * This class is the main entry point to the IRIS SDK. It is a simple front end * for generating JPA classes, and associated configuration files. With these * classes and config files in place we can then fill a mock database with * appropriate values and enable IRIS to respond to resource requests from User * Agents. * */ public class JPAResponderGen { private static final Logger LOGGER = LoggerFactory.getLogger(JPAResponderGen.class); // generator properties public static final String PROPERTY_KEY_ROOT = "com.temenos.interaction.sdk"; public static final String STRICT_ODATA_KEY = "strictodata"; public static final String REGENERATE_OUTPUT_KEY = "regenerate"; public static final String JPA_CONFIG_FILE = "jpa-persistence.xml"; public static final String SPRING_CONFIG_FILE = "spring-beans.xml"; public static final String SPRING_RESOURCEMANAGER_FILE = "resourcemanager-context.xml"; public static final String RESPONDER_INSERT_FILE = "responder_insert.sql"; public static final String RESPONDER_SETTINGS_FILE = "responder.properties"; public static final String BEHAVIOUR_CLASS_FILE = "Behaviour.java"; public static final String METADATA_FILE = "metadata.xml"; public static final Parameter COMMAND_SERVICE_DOCUMENT = new Parameter("ServiceDocument", false, ""); public static final Parameter COMMAND_METADATA_ODATA4J = new Parameter("metadataOData4j", true, ""); public static final Parameter COMMAND_METADATA = new Parameter("Metadata", false, ""); public static final Parameter COMMAND_METADATA_SOURCE_ODATAPRODUCER = new Parameter("producer", true, "odataProducer"); public static final Parameter COMMAND_METADATA_SOURCE_MODEL = new Parameter("edmMetadata", true, "edmMetadata"); public static final String FAILED_TO_WRITE = "Failed to write"; public static final String FAILED_TO_CLOSE = "Failed to close"; private boolean strictOData; //Indicates whether it should generate strict odata paths etc. (e.g. Flight(1)/flightschedule rather than FlightSchedule(2051)) private boolean overwriteAllOutput = true; /* * create a new instance of the engine */ VelocityEngine ve = new VelocityEngine(); /** * Construct an instance of this class */ public JPAResponderGen() { this(true); } /** * Construct an instance of this class * @param strictOData indicates whether to generate a strict odata model */ public JPAResponderGen(boolean strictOData) { Properties props = new Properties(); if (strictOData) props.put(PROPERTY_KEY_ROOT + "." + STRICT_ODATA_KEY, "true"); initialise(props); } /** * Construct an instance of this class * @param strictOData indicates whether to generate a strict odata model */ public JPAResponderGen(Properties props) { initialise(props); } @SuppressWarnings("deprecation") protected void initialise(Properties props) { strictOData = isKeyTrue(props.get(PROPERTY_KEY_ROOT + "." + STRICT_ODATA_KEY)); overwriteAllOutput = isKeyTrue(props.get(PROPERTY_KEY_ROOT + "." + REGENERATE_OUTPUT_KEY)); // load .vm templates using classloader ve.setProperty(VelocityEngine.RESOURCE_LOADER, "classpath"); ve.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER + ".class", ClasspathResourceLoader.class.getName()); ve.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new NullLogSystem()); ve.init(); } private boolean isKeyTrue(Object keyValue) { return (keyValue != null && keyValue.toString().equalsIgnoreCase("true")); } /** * Generate project artefacts from conceptual interaction and metadata models. * @param metadata metadata model * @param interactionModel Conceptual interaction model * @param srcOutputPath Path to output directory * @param configOutputPath Path to configuration files directory * @param generateMockResponder Indicates whether to generate artifacts for a mock responder * @return true if successful, false otherwise */ public boolean generateArtifacts(InteractionAdapter adapter, File srcOutputPath, File configOutputPath, boolean generateMockResponder) { return generateArtifacts(adapter.getEntityModel(), adapter.getInteractionModel(), adapter.getCommands(), adapter.getEntitiesInfo(), srcOutputPath, configOutputPath, generateMockResponder); } public boolean generateArtifacts(InteractionAdapter adapter, File srcOutputPath, File configOutputPath) { return generateArtifacts(adapter, srcOutputPath, configOutputPath, true); } public boolean generateArtifacts(Metadata metadata, InteractionModel interactionModel, File srcOutputPath, File configOutputPath, boolean generateMockResponder) { //Create commands Commands commands = getDefaultCommands(); return generateArtifacts(metadata, interactionModel, commands, srcOutputPath, configOutputPath, generateMockResponder); } /** * Generate project artefacts from conceptual interaction and metadata models. * @param metadata metadata model * @param interactionModel Conceptual interaction model * @param commands Commands * @param srcOutputPath Path to output directory * @param configOutputPath Path to configuration files directory * @param generateMockResponder Indicates whether to generate artifacts for a mock responder * @return true if successful, false otherwise */ public boolean generateArtifacts(Metadata metadata, InteractionModel interactionModel, Commands commands, File srcOutputPath, File configOutputPath, boolean generateMockResponder) { //Create the entity model String modelName = metadata.getModelName(); EntityModel entityModel = createEntityModelFromMetadata(modelName, metadata); //Add error entities for(IMState state : interactionModel.getErrorHandlerStates()) { EMEntity emEntity = new EMEntity(state.getName()); EMProperty empErrorMvGroup = new EMProperty("ErrorsMvGroup"); empErrorMvGroup.addVocabularyTerm(new EMTerm(TermIdField.TERM_NAME, "true")); empErrorMvGroup.addVocabularyTerm(new EMTerm(TermListType.TERM_NAME, "true")); empErrorMvGroup.addVocabularyTerm(new EMTerm(TermComplexType.TERM_NAME, "true")); emEntity.addProperty(empErrorMvGroup); EMProperty empCode = new EMProperty("Code"); empCode.addVocabularyTerm(new EMTerm(TermComplexGroup.TERM_NAME, "ErrorsMvGroup")); emEntity.addProperty(empCode); EMProperty empInfo = new EMProperty("Info"); empInfo.addVocabularyTerm(new EMTerm(TermComplexGroup.TERM_NAME, "ErrorsMvGroup")); emEntity.addProperty(empInfo); EMProperty empText = new EMProperty("Text"); empText.addVocabularyTerm(new EMTerm(TermComplexGroup.TERM_NAME, "ErrorsMvGroup")); emEntity.addProperty(empText); EMProperty empType = new EMProperty("Type"); empType.addVocabularyTerm(new EMTerm(TermComplexGroup.TERM_NAME, "ErrorsMvGroup")); emEntity.addProperty(empType); entityModel.addEntity(emEntity); } //Obtain resource information String namespace = modelName + Metadata.MODEL_SUFFIX; List<EntityInfo> entitiesInfo = new ArrayList<EntityInfo>(); for (EntityMetadata entityMetadata: metadata.getEntitiesMetadata().values()) { EntityInfo entityInfo = createEntityInfoFromEntityMetadata(namespace, entityMetadata); addNavPropertiesToEntityInfo(entityInfo, interactionModel); entitiesInfo.add(entityInfo); } return generateArtifacts(entityModel, interactionModel, commands, entitiesInfo, srcOutputPath, configOutputPath, generateMockResponder); } /** * Generate project artefacts from conceptual interaction and metadata models. * @param entityModel the entity metadata * @param interactionModel Conceptual interaction model * @param commands Commands * @param srcOutputPath Path to output directory * @param configOutputPath Path to configuration files directory * @param generateMockResponder Indicates whether to generate artifacts for a mock responder * @return true if successful, false otherwise */ public boolean generateArtifacts(EntityModel entityModel, InteractionModel interactionModel, Commands commands, List<EntityInfo> entitiesInfo, File srcOutputPath, File configOutputPath, boolean generateMockResponder) { boolean ok = true; //Write other artefacts if(!writeArtefacts(entityModel.getModelName(), entitiesInfo, commands, entityModel, interactionModel, srcOutputPath, configOutputPath, generateMockResponder)) { ok = false; } return ok; } /** * see {@link RimDslGenerator#getRIM(InteractionModel, Commands)} */ public InputStream getRIM(InteractionModel interactionModel, Commands commands) throws Exception { RimDslGenerator rimDslGenerator = new RimDslGenerator(ve); return rimDslGenerator.getRIM(interactionModel, commands, strictOData); } /** * see {@link RimDslGenerator#generateRimDslMap(InteractionModel, Commands)} */ public Map<String,String> getRIMsMap(InteractionModel interactionModel, Commands commands) { RimDslGenerator rimDslGenerator = new RimDslGenerator(ve); Map<String,String> rims = rimDslGenerator.generateRimDslMap(interactionModel, commands, strictOData); return rims; } private boolean writeArtefacts(String modelName, List<EntityInfo> entitiesInfo, Commands commands, EntityModel entityModel, InteractionModel interactionModel, File srcOutputPath, File configOutputPath, boolean generateMockResponder) { boolean ok = true; String namespace = modelName + Metadata.MODEL_SUFFIX; interactionModel.setDomain(namespace); //Create the source directory new File(srcOutputPath + "/" + namespace.replace(".", "/")).mkdirs(); // generate metadata.xml if (!writeMetadata(configOutputPath, generateMetadata(entityModel))) { ok = false; } // generate spring configuration files if (!writeSpringConfiguration(configOutputPath, SPRING_CONFIG_FILE, generateSpringConfiguration(namespace, modelName, commands))) { ok = false; } // generate the rim DSL RimDslGenerator rimDslGenerator = new RimDslGenerator(ve); Map<String,String> rims = rimDslGenerator.generateRimDslMap(interactionModel, commands, strictOData); for (String key : rims.keySet()) { String rimDslFilename = key + ".rim"; if (!writeRimDsl(configOutputPath, rimDslFilename, rims.get(key))) { LOGGER.error(FAILED_TO_WRITE +" "+ key); ok = false; break; } } if(generateMockResponder) { //Write JPA classes for(EntityInfo entityInfo : entitiesInfo) { if(!generateJPAEntity(entityInfo, srcOutputPath)) { ok = false; } } // generate persistence.xml if (!writeJPAConfiguration(configOutputPath, generateJPAConfiguration(entitiesInfo))) { ok = false; } // generate spring configuration for JPA database if (!writeSpringConfiguration(configOutputPath, SPRING_RESOURCEMANAGER_FILE, generateSpringResourceManagerContext(modelName))) { ok = false; } // generate responder insert if (!writeResponderDML(configOutputPath, generateResponderDML(entitiesInfo))) { ok = false; } // generate responder settings if (!writeResponderSettings(configOutputPath, generateResponderSettings(modelName))) { ok = false; } } return ok; } private boolean generateJPAEntity(EntityInfo entityInfo, File srcOutputPath) { //Generate JPA class if(entityInfo.isJpaEntity()) { String path = srcOutputPath.getPath() + "/" + entityInfo.getPackageAsPath(); if (!writeClass(path, formClassFilename(path, entityInfo), generateJPAEntityClass(entityInfo))) { return false; } } return true; } /** * Utility method to form class filename. * @param srcTargetDir * @param entityInfo * @return */ public static String formClassFilename(String path, EntityInfo entityInfo) { return path + "/" + entityInfo.getClazz() + ".java"; } protected boolean writeClass(String path, String classFileName, String generatedClass) { FileOutputStream fos = null; try { new File(path).mkdirs(); fos = new FileOutputStream(classFileName); fos.write(generatedClass.getBytes("UTF-8")); } catch (IOException e) { LOGGER.warn(FAILED_TO_WRITE+" class: " + classFileName, e); return false; } finally { try { if (fos != null) fos.close(); } catch (IOException e) { LOGGER.warn(FAILED_TO_CLOSE+" class: " + classFileName, e); } } return true; } public EntityInfo createEntityInfoFromEntityMetadata(String namespace, EntityMetadata entityMetadata) { FieldInfo keyInfo = null; List<FieldInfo> properties = new ArrayList<FieldInfo>(); for(String propertyName : entityMetadata.getPropertyVocabularyKeySet()) { String type = entityMetadata.getTermValue(propertyName, TermValueType.TERM_NAME); if(entityMetadata.getTermValue(propertyName, TermIdField.TERM_NAME).equals("true")) { keyInfo = new FieldInfo(propertyName, javaType(MetadataOData4j.termValueToEdmType(type)), null); } else { List<String> annotations = new ArrayList<String>(); if(type.equals(TermValueType.TIMESTAMP)) { annotations.add("@Temporal(TemporalType.TIMESTAMP)"); } else if(type.equals(TermValueType.DATE)) { annotations.add("@Temporal(TemporalType.DATE)"); } else if(type.equals(TermValueType.TIME)) { annotations.add("@Temporal(TemporalType.TIME)"); } FieldInfo field = new FieldInfo(propertyName, javaType(MetadataOData4j.termValueToEdmType(type)), annotations); properties.add(field); } } //Check if user has specified the name of the JPA entities String jpaNamespace = System.getProperty("jpaNamespace"); boolean isJpaEntity = (jpaNamespace == null || jpaNamespace.equals(namespace)); return new EntityInfo(entityMetadata.getEntityName(), namespace, keyInfo, properties, new ArrayList<JoinInfo>(), isJpaEntity); } /* * Create a EntityModel object from a Metadata container */ private EntityModel createEntityModelFromMetadata(String modelName, Metadata metadata) { //Create the entity model EntityModel entityModel = new EntityModel(modelName); for (EntityMetadata entityMetadata: metadata.getEntitiesMetadata().values()) { EMEntity emEntity = new EMEntity(entityMetadata.getEntityName()); for(String fyllyQualifiedPropertyName : entityMetadata.getPropertyVocabularyKeySet()) { addProperties(entityMetadata, emEntity, fyllyQualifiedPropertyName); } entityModel.addEntity(emEntity); } return entityModel; } // adds the property tree to the entity metadata private void addProperties(EntityMetadata entityMetadata, EMEntity entity, String fyllyQualifiedPropertyName) { List<String> propertyNames = new ArrayList<String>(Arrays.asList(fyllyQualifiedPropertyName.split("\\."))); String rootPropertyName = propertyNames.get(0); EMProperty rootProperty = null; if (entity.contains(rootPropertyName)) { // this property already added to the entity rootProperty = entity.getProperty(rootPropertyName); } else { // new property so create and add to the entity rootProperty = new EMProperty(rootPropertyName); entity.addProperty(rootProperty); Vocabulary vocabulary = entityMetadata.getPropertyVocabulary(rootPropertyName); for (Term term : vocabulary.getTerms()) { rootProperty.addVocabularyTerm(new EMTerm(term.getName(), term.getValue())); } } propertyNames.remove(0); // removes root property as it's already added to the entity addChildProperties(entityMetadata, rootProperty, propertyNames); } // adds the child properties to the top most property private void addChildProperties(EntityMetadata entityMetadata, EMProperty parentProperty, List<String> childPropertyNames) { String fullyQualifiedPropertyName = parentProperty.getName(); for (String childPropertyName : childPropertyNames) { fullyQualifiedPropertyName = fullyQualifiedPropertyName + "." + childPropertyName; EMProperty childProperty = null; if (parentProperty.hasChildProperty(childPropertyName)) { // this property already added to the parent property childProperty = parentProperty.getChildProperty(childPropertyName); } else { // this is a new child property so create and add to the parent property childProperty = new EMProperty(childPropertyName); parentProperty.addChildProperty(childProperty); Vocabulary propertyVocabulary = entityMetadata.getPropertyVocabulary(fullyQualifiedPropertyName); for (Term vocTerm : propertyVocabulary.getTerms()) { if (TermComplexGroup.TERM_NAME.equals(vocTerm.getName())) { // complex group term not added as the properties are built in tree structure continue; } childProperty.addVocabularyTerm(new EMTerm(vocTerm.getName(), vocTerm.getValue())); } } parentProperty = childProperty; } } public static String javaType(EdmType type) { // TODO support complex type keys? assert(type.isSimple()); String javaType = null; if (EdmSimpleType.INT64 == type) { javaType = "Long"; } else if (EdmSimpleType.INT32 == type) { javaType = "Integer"; } else if (EdmSimpleType.INT16 == type) { javaType = "Integer"; } else if (EdmSimpleType.STRING == type) { javaType = "String"; } else if (EdmSimpleType.DATETIME == type) { javaType = "java.util.Date"; } else if (EdmSimpleType.TIME == type) { javaType = "java.util.Date"; } else if (EdmSimpleType.DECIMAL == type) { javaType = "java.math.BigDecimal"; } else if (EdmSimpleType.SINGLE == type) { javaType = "Float"; } else if (EdmSimpleType.DOUBLE == type) { javaType = "Double"; } else if (EdmSimpleType.BOOLEAN == type) { javaType = "Boolean"; } else if (EdmSimpleType.GUID == type) { javaType = "String"; } else if (EdmSimpleType.BINARY == type) { javaType = "String"; } else { // TODO support types other than Long and String throw new RuntimeException("Entity property type " + type.getFullyQualifiedTypeName() + " not supported"); } return javaType; } /* * Create a property with vocabulary term from the Edmx property */ public static EMProperty createEMProperty(EdmProperty property) { EMProperty emProperty = new EMProperty(property.getName()); if(property.isNullable()) { emProperty.addVocabularyTerm(new EMTerm(TermMandatory.TERM_NAME, "true")); } //Set the value type vocabulary term EdmType type = property.getType(); if (type.equals(EdmSimpleType.DATETIME)) { emProperty.addVocabularyTerm(new EMTerm(TermValueType.TERM_NAME, TermValueType.TIMESTAMP)); } else if (type.equals(EdmSimpleType.TIME)) { emProperty.addVocabularyTerm(new EMTerm(TermValueType.TERM_NAME, TermValueType.TIME)); } else if (type.equals(EdmSimpleType.INT64) || type.equals(EdmSimpleType.INT32) || type.equals(EdmSimpleType.INT16)) { emProperty.addVocabularyTerm(new EMTerm(TermValueType.TERM_NAME, TermValueType.INTEGER_NUMBER)); } else if (type.equals(EdmSimpleType.SINGLE) || type.equals(EdmSimpleType.DOUBLE) || type.equals(EdmSimpleType.DECIMAL)) { emProperty.addVocabularyTerm(new EMTerm(TermValueType.TERM_NAME, TermValueType.NUMBER)); } else if (type.equals(EdmSimpleType.BOOLEAN)) { emProperty.addVocabularyTerm(new EMTerm(TermValueType.TERM_NAME, TermValueType.BOOLEAN)); } return emProperty; } protected boolean writeJPAConfiguration(File sourceDir, String generatedPersistenceXML) { FileOutputStream fos = null; try { File metaInfDir = new File(sourceDir.getPath() + "/META-INF"); metaInfDir.mkdirs(); fos = new FileOutputStream(new File(metaInfDir, JPA_CONFIG_FILE)); fos.write(generatedPersistenceXML.getBytes("UTF-8")); } catch (IOException e) { LOGGER.warn(FAILED_TO_WRITE+" : " + JPA_CONFIG_FILE, e); return false; } finally { try { if (fos != null) fos.close(); } catch (IOException e) { LOGGER.warn(FAILED_TO_CLOSE+" : " + JPA_CONFIG_FILE, e); } } return true; } protected boolean writeSpringConfiguration(File sourceDir, String filename, String generatedSpringXML) { FileOutputStream fos = null; try { File metaInfDir = new File(sourceDir.getPath() + "/META-INF"); metaInfDir.mkdirs(); File f = new File(metaInfDir, filename); if(!f.exists()) { fos = new FileOutputStream(f); fos.write(generatedSpringXML.getBytes("UTF-8")); } } catch (IOException e) { LOGGER.warn(FAILED_TO_WRITE+" : " + filename, e); return false; } finally { try { if (fos != null) fos.close(); } catch (IOException e) { LOGGER.warn(FAILED_TO_CLOSE + " : " + filename, e); } } return true; } protected boolean writeResponderDML(File sourceDir, String generateResponderDML) { FileOutputStream fos = null; try { File metaInfDir = new File(sourceDir.getPath() + "/META-INF"); metaInfDir.mkdirs(); fos = new FileOutputStream(new File(metaInfDir, RESPONDER_INSERT_FILE)); fos.write(generateResponderDML.getBytes("UTF-8")); } catch (IOException e) { LOGGER.warn(FAILED_TO_WRITE + " : " + RESPONDER_INSERT_FILE, e); return false; } finally { try { if (fos != null) fos.close(); } catch (IOException e) { LOGGER.warn(FAILED_TO_CLOSE + " : " + RESPONDER_INSERT_FILE, e); } } return true; } protected boolean writeMetadata(File sourceDir, String generatedMetadata) { FileOutputStream fos = null; try { File metaInfDir = new File(sourceDir.getPath()); metaInfDir.mkdirs(); File f = new File(metaInfDir, METADATA_FILE); if(!f.exists()) { fos = new FileOutputStream(f); fos.write(generatedMetadata.getBytes("UTF-8")); } } catch (IOException e) { LOGGER.warn(FAILED_TO_WRITE+" : " + METADATA_FILE, e); return false; } finally { try { if (fos != null) fos.close(); } catch (IOException e) { LOGGER.warn(FAILED_TO_CLOSE+" : " + METADATA_FILE, e); } } return true; } protected boolean writeRimDsl(File sourceDir, String rimDslFilename, String generatedRimDsl) { FileOutputStream fos = null; try { File dir = new File(sourceDir.getPath()); dir.mkdirs(); File f = new File(dir, rimDslFilename); if(overwriteAllOutput || !f.exists()) { fos = new FileOutputStream(f); fos.write(generatedRimDsl.getBytes("UTF-8")); } } catch (IOException e) { LOGGER.warn(FAILED_TO_WRITE + " : " + rimDslFilename, e); return false; } finally { try { if (fos != null) fos.close(); } catch (IOException e) { LOGGER.warn(FAILED_TO_CLOSE + " : " + rimDslFilename, e); } } return true; } protected boolean writeResponderSettings(File sourceDir, String generateResponderSettings) { FileOutputStream fos = null; try { File metaInfDir = new File(sourceDir.getPath()); metaInfDir.mkdirs(); File f = new File(metaInfDir, RESPONDER_SETTINGS_FILE); if(!f.exists()) { fos = new FileOutputStream(f); fos.write(generateResponderSettings.getBytes("UTF-8")); } } catch (IOException e) { LOGGER.warn(FAILED_TO_WRITE + " : " + RESPONDER_SETTINGS_FILE, e); return false; } finally { try { if (fos != null) fos.close(); } catch (IOException e) { LOGGER.warn(FAILED_TO_CLOSE + " : " + RESPONDER_SETTINGS_FILE, e); } } return true; } /** * Generate a JPA Entity from the provided info * @precondition {@link EntityInfo} non null * @precondition {@link EntityInfo} contain a valid package, class name, key, and fields * @postcondition A valid java class, that may be serialised and compiled, or pushed into * the {@link ClassLoader.defineClass} * @param jpaEntityClass * @return */ public String generateJPAEntityClass(EntityInfo jpaEntityClass) { assert(jpaEntityClass != null); VelocityContext context = new VelocityContext(); context.put("jpaentity", jpaEntityClass); Template t = ve.getTemplate("/JPAEntity.vm"); StringWriter sw = new StringWriter(); t.merge(context, sw); return sw.toString(); } /** * Generate the JPA configuration for the provided JPA entities. * @param entitiesInfo * @return */ public String generateJPAConfiguration(List<EntityInfo> entitiesInfo) { VelocityContext context = new VelocityContext(); context.put("entitiesInfo", entitiesInfo); Template t = ve.getTemplate("/jpa-persistence.vm"); StringWriter sw = new StringWriter(); t.merge(context, sw); return sw.toString(); } /** * Generate the Spring configuration for the provided resources. */ public String generateSpringConfiguration(String namespace, String modelName, Commands commands) { VelocityContext context = new VelocityContext(); context.put("behaviourClass", namespace + "." + modelName + "Behaviour"); context.put("commands", commands); Template t = ve.getTemplate("/spring-beans.vm"); StringWriter sw = new StringWriter(); t.merge(context, sw); return sw.toString(); } /** * Generate the Spring configuration for the provided resources. * @param entityContainerNamespace * @return */ public String generateSpringResourceManagerContext(String entityContainerNamespace) { VelocityContext context = new VelocityContext(); context.put("entityContainerNamespace", entityContainerNamespace); Template t = ve.getTemplate("/resourcemanager-context.vm"); StringWriter sw = new StringWriter(); t.merge(context, sw); return sw.toString(); } /** * Generate the responder_insert.sql provided resources. * @return */ public String generateResponderDML(List<EntityInfo> entitiesInfo) { VelocityContext context = new VelocityContext(); context.put("entitiesInfo", entitiesInfo); Template t = ve.getTemplate("/responder_insert.vm"); StringWriter sw = new StringWriter(); t.merge(context, sw); return sw.toString(); } /** * Generate the metadata file * @param entityModel The entity model * @return The generated metadata */ public String generateMetadata(EntityModel entityModel) { VelocityContext context = new VelocityContext(); context.put("entityModel", entityModel); context.put("formatter", IndentationFormatter.getInstance()); Template t = ve.getTemplate("/metadata.vm"); StringWriter sw = new StringWriter(); t.merge(context, sw); return sw.toString(); } /** * Generate responder settings * @return */ public String generateResponderSettings(String responderName) { VelocityContext context = new VelocityContext(); context.put("responderName", responderName); Template t = ve.getTemplate("/responder_settings.vm"); StringWriter sw = new StringWriter(); t.merge(context, sw); return sw.toString(); } public static void main(String[] args) { boolean ok = true; if (args != null && args.length == 2) { String edmxFilePath = args[0]; String targetDirectoryStr = args[1]; File edmxFile = new File(edmxFilePath); File targetDirectory = new File(targetDirectoryStr); // check our configuration if (!edmxFile.exists()) { LOGGER.error("EDMX file not found"); ok = false; } if (!targetDirectory.exists() || !targetDirectory.isDirectory()) { LOGGER.error("Target directory is invalid"); ok = false; } if (ok) { JPAResponderGen rg = new JPAResponderGen(); LOGGER.error("Writing source and configuration to [" + targetDirectory + "]"); ok = rg.generateArtifacts(new EDMXAdapter(edmxFilePath), targetDirectory, targetDirectory, true); } } else { ok = false; } if (!ok) { LOGGER.error(usage()); } } public static String usage() { StringBuffer sb = new StringBuffer(); sb.append("\n") .append("Generates an IRIS JPA responder from an EDMX file.\n") .append("\n") .append("java ").append(JPAResponderGen.class.getName()).append(" [EDMX file] [target directory]\n"); return sb.toString(); } /** * Returns a list of default commands * @return */ public static Commands getDefaultCommands() { Commands commands = new Commands(); //Add RIM events commands.addRimEvent("GET", Commands.HTTP_COMMAND_GET); commands.addRimEvent("POST", Commands.HTTP_COMMAND_POST); commands.addRimEvent("PUT", Commands.HTTP_COMMAND_PUT); commands.addRimEvent("DELETE", Commands.HTTP_COMMAND_DELETE); //Add commands commands.addCommand(Commands.GET_SERVICE_DOCUMENT, "com.temenos.interaction.commands.odata.GETMetadataCommand", COMMAND_SERVICE_DOCUMENT, COMMAND_METADATA_ODATA4J); commands.addCommand(Commands.GET_METADATA, "com.temenos.interaction.commands.odata.GETMetadataCommand", COMMAND_METADATA, COMMAND_METADATA_ODATA4J); commands.addCommand(Commands.GET_EXCEPTION, "com.temenos.interaction.core.command.GETExceptionCommand"); commands.addCommand(Commands.GET_NOOP, "com.temenos.interaction.core.command.NoopGETCommand"); commands.addCommand(Commands.GET_ENTITY, "com.temenos.interaction.commands.odata.GETEntityCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER); commands.addCommand(Commands.GET_ENTITIES, "com.temenos.interaction.commands.odata.GETEntitiesCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER); commands.addCommand(Commands.CREATE_ENTITY, "com.temenos.interaction.commands.odata.CreateEntityCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER); commands.addCommand(Commands.GET_NAV_PROPERTY, "com.temenos.interaction.commands.odata.GETNavPropertyCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER); commands.addCommand(Commands.UPDATE_ENTITY, "com.temenos.interaction.commands.odata.UpdateEntityCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER); commands.addCommand(Commands.DELETE_ENTITY, "com.temenos.interaction.commands.odata.DeleteEntityCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER); return commands; } /* * Add transition properties to entity info */ public static void addNavPropertiesToEntityInfo(EntityInfo entityInfo, InteractionModel interactionModel) { IMResourceStateMachine rsm = interactionModel.findResourceStateMachine(entityInfo.getClazz()); if (rsm != null) { for(IMTransition transition : rsm.getEntityStateTransitions()) { List<FieldInfo> properties = entityInfo.getAllFieldInfos(); List<String> annotations = new ArrayList<String>(); if (transition instanceof IMCollectionStateTransition) { //Transition to collection state //TODO fix reciprocal links //annotations.add("@OneToMany(cascade = CascadeType.ALL, mappedBy = \"" + transition.getTargetStateName() + "\")"); //properties.add(new FieldInfo(transition.getTargetStateName(), "Collection<" + transition.getTargetEntityName() + ">", annotations)); } else if(transition instanceof IMEntityStateTransition){ //Transition to entity state IMEntityStateTransition t = (IMEntityStateTransition) transition; annotations.add("@JoinColumn(name = \"" + t.getLinkProperty() + "\", referencedColumnName = \"" + t.getTargetResourceStateMachine().getMappedEntityProperty() + "\", insertable = false, updatable = false)"); annotations.add("@ManyToOne(optional = false)"); properties.add(new FieldInfo(t.getTargetState().getName(), t.getTargetResourceStateMachine().getEntityName(), annotations)); } } } } }