Java tutorial
/* * Copyright (c) Open Source Strategies, Inc. * * Opentaps 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. * * Opentaps 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Opentaps. If not, see <http://www.gnu.org/licenses/>. */ /******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. *******************************************************************************/ /* This file has been modified by Open Source Strategies, Inc. */ package org.opentaps.domain.container; import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeMap; import freemarker.template.TemplateException; import org.apache.commons.io.FileUtils; import org.ofbiz.base.container.Container; import org.ofbiz.base.container.ContainerConfig; import org.ofbiz.base.container.ContainerException; import org.ofbiz.base.util.Debug; import org.ofbiz.base.util.UtilMisc; import org.ofbiz.base.util.UtilProperties; import org.ofbiz.base.util.UtilValidate; import org.ofbiz.base.util.template.FreeMarkerWorker; import org.ofbiz.entity.Delegator; import org.ofbiz.entity.DelegatorFactory; import org.ofbiz.entity.GenericEntityException; import org.ofbiz.entity.GenericValue; import org.ofbiz.entity.condition.EntityCondition; import org.ofbiz.entity.condition.EntityOperator; import org.ofbiz.service.ServiceDispatcher; /** * Some utility routines for loading seed data. */ public class ConstantsGeneratorContainer implements Container { private static final String MODULE = ConstantsGeneratorContainer.class.getName(); private static final String CONTAINER_NAME = "constants-generation-container"; /** Container config file. */ private String containerConfigFile; /** The path of output. */ private String outputPath; /** Java class Template. */ private String template; /** The properties file containing the configuration of the constants to generate. */ private Properties configProperties; private String delegatorNameToUse; private Delegator delegator; /** Entries in <code>configProperties</code> with this value indicates the entities to query. */ private static final String GENERATE_VALUE = "generate"; /** Prefix constants that starts with a number to make a valid Java constant name. */ private static final String NUMERIC_PREFIX = "_"; /** * Creates a new <code>PojoGeneratorContainer</code> instance. */ public ConstantsGeneratorContainer() { super(); } /** {@inheritDoc} */ public void init(String[] args, String configFile) throws ContainerException { this.containerConfigFile = configFile; // disable job scheduler, JMS listener and startup services ServiceDispatcher.enableJM(false); ServiceDispatcher.enableJMS(false); ServiceDispatcher.enableSvcs(false); } /** {@inheritDoc} */ public boolean start() throws ContainerException { ContainerConfig.Container cfg = ContainerConfig.getContainer(CONTAINER_NAME, containerConfigFile); ContainerConfig.Container.Property delegatorNameProp = cfg.getProperty("delegator-name"); ContainerConfig.Container.Property outputPathProp = cfg.getProperty("outputPath"); ContainerConfig.Container.Property templateProp = cfg.getProperty("template"); ContainerConfig.Container.Property configProp = cfg.getProperty("configProperties"); // get delegator to use from the container configuration if (delegatorNameProp == null || delegatorNameProp.value == null || delegatorNameProp.value.length() == 0) { throw new ContainerException("Invalid delegator-name defined in container configuration"); } else { delegatorNameToUse = delegatorNameProp.value; } // get the delegator delegator = DelegatorFactory.getDelegator(delegatorNameToUse); if (delegator == null) { throw new ContainerException("Invalid delegator name: " + delegatorNameToUse); } // get output path to use from the container configuration if (outputPathProp == null || outputPathProp.value == null || outputPathProp.value.length() == 0) { throw new ContainerException("Invalid output-name defined in container configuration"); } else { outputPath = outputPathProp.value; } // check the output path File outputDir = new File(outputPath); if (!outputDir.canWrite()) { throw new ContainerException("Unable to use output path: [" + outputPath + "], it is not writable"); } // get the template file to use from the container configuration if (templateProp == null || templateProp.value == null || templateProp.value.length() == 0) { throw new ContainerException("Invalid entity template defined in container configuration"); } else { template = templateProp.value; } // get the properties file name containing the configuration if (configProp == null || configProp.value == null || templateProp.value.length() == 0) { throw new ContainerException("Invalid configuration properties defined in container configuration"); } else { configProperties = UtilProperties.getProperties(configProp.value); } return generateConstants(); } private boolean generateConstants() throws ContainerException { // check the template file File templateFile = new File(template); if (!templateFile.canRead()) { throw new ContainerException("Unable to read the template file: [" + template + "]"); } // get the list of entities for which to get constants values // throw an error if the same key is used twice Map<String, ConstantModel> models = readConstantConfiguration(configProperties); // record errors for summary List<String> errorEntities = new LinkedList<String>(); // keep track of generated files Map<String, String> generatedConstants = new HashMap<String, String>(); int totalGenerated = 0; int totalGeneratedClass = 0; if (models != null && models.size() > 0) { Debug.logImportant("=-=-=-=-=-=-= Generating the constants ...", MODULE); for (String key : models.keySet()) { ConstantModel model = models.get(key); if (generatedConstants.containsKey(model.getClassName())) { Debug.logError("Constant file [" + model.getClassName() + "] was already generated and is conflicting with the configuration key [" + key + "]", MODULE); errorEntities.add(key); continue; } List<Map<String, String>> values; try { values = model.getDbValues(delegator); } catch (ConstantException e) { errorEntities.add(key); Debug.logError("Error getting the DB values for [" + key + "] : " + e.getMessage(), MODULE); continue; } totalGenerated += values.size(); // could be an object, but is just a Map for simplicity Map<String, Object> entityInfo = new HashMap<String, Object>(); entityInfo.put("className", model.getClassName()); entityInfo.put("description", model.getDescription()); Boolean useInnerClasses = UtilValidate.isNotEmpty(model.getTypeField()); entityInfo.put("useInnerClasses", useInnerClasses); // group by inner class if needed Map<String, List<Map<String, String>>> byInnerClass = new TreeMap<String, List<Map<String, String>>>(); if (useInnerClasses) { for (Map<String, String> value : values) { String innerClass = value.get("innerClass"); List<Map<String, String>> grouped = byInnerClass.get(innerClass); if (grouped == null) { grouped = new ArrayList<Map<String, String>>(); byInnerClass.put(innerClass, grouped); } grouped.add(value); } } else { byInnerClass.put("", values); } entityInfo.put("valuesByInnerClass", byInnerClass); // render it as FTL Writer writer = new StringWriter(); try { FreeMarkerWorker.renderTemplateAtLocation(template, entityInfo, writer); } catch (MalformedURLException e) { Debug.logError(e, MODULE); errorEntities.add(key); break; } catch (TemplateException e) { Debug.logError(e, MODULE); errorEntities.add(key); break; } catch (IOException e) { Debug.logError(e, MODULE); errorEntities.add(key); break; } catch (IllegalArgumentException e) { Debug.logError(e, MODULE); errorEntities.add(key); break; } // write it as a Java file File file = new File(outputPath + model.getClassName() + ".java"); try { FileUtils.writeStringToFile(file, writer.toString(), "UTF-8"); } catch (IOException e) { Debug.logError(e, "Aborting, error writing file " + outputPath + model.getClassName() + ".java", MODULE); errorEntities.add(key); break; } // increment counter generatedConstants.put(model.getClassName(), key); totalGeneratedClass++; } } else { Debug.logImportant("=-=-=-=-=-=-= No constant to generate.", MODULE); } // error summary if (errorEntities.size() > 0) { Debug.logImportant("The following constants could not be generated:", MODULE); for (String name : errorEntities) { Debug.logImportant(name, MODULE); } } Debug.logImportant("=-=-=-=-=-=-= Finished the constants generation with " + totalGeneratedClass + " classes / " + totalGenerated + " values generated.", MODULE); if (errorEntities.size() > 0) { return false; } return true; } /** {@inheritDoc} */ public void stop() throws ContainerException { } private TreeMap<String, ConstantModel> readConstantConfiguration(Properties config) throws ContainerException { TreeMap<String, ConstantModel> models = new TreeMap<String, ConstantModel>(); // first collect the entity names Enumeration<?> propertyNames = config.propertyNames(); while (propertyNames.hasMoreElements()) { String key = (String) propertyNames.nextElement(); if (GENERATE_VALUE.equals(config.getProperty(key))) { if (models.containsKey(key)) { throw new ContainerException("Entity: [" + key + "] already defined in the configuration."); } models.put(key, new ConstantModel(key)); } } // then for each entity read the configuration for (String key : models.keySet()) { ConstantModel model = models.get(key); model.setClassName(config.getProperty(key + ".className")); model.setDescription(config.getProperty(key + ".description")); model.setTypeField(config.getProperty(key + ".typeField")); model.setNameField(config.getProperty(key + ".nameField")); model.setDescriptionField(config.getProperty(key + ".descriptionField")); model.setConstantField(config.getProperty(key + ".constantField")); model.setWhere(config.getProperty(key + ".where")); } return models; } private static class ConstantComparator implements Comparator<Map<String, String>> { public int compare(Map<String, String> o1, Map<String, String> o2) { String name1 = o1.get("constantName"); String name2 = o2.get("constantName"); if (name1 == null && name2 == null) { return 0; } else if (name1 == null && name2 != null) { return 1; } else { return name1.compareTo(name2); } } } private static class ConstantModel { private String key; private String entityName; private String className; private String description; private String typeField; private String constantField; private String nameField; private String descriptionField; private String where; public ConstantModel(String key) { this.key = key; this.setEntityName(key); } public void setEntityName(String entityName) { int idx = entityName.indexOf("!"); if (idx >= 0) { this.entityName = entityName.substring(0, idx); } else { this.entityName = entityName; } } public void setClassName(String className) { if (className == null) { this.className = entityName + "Constants"; } else { this.className = className; } } public void setDescription(String description) { if (description == null) { this.description = entityName + " constant values"; } else { this.description = description; } } public void setTypeField(String typeField) { this.typeField = typeField; } public void setDescriptionField(String descriptionField) { this.descriptionField = descriptionField; } public void setNameField(String nameField) { this.nameField = nameField; } public void setConstantField(String constantField) { this.constantField = constantField; } public void setWhere(String where) { this.where = where; } public String getEntityName() { return this.entityName; } public String getClassName() { return this.className; } public String getDescription() { return this.description; } public String getConstantField() { return this.constantField; } public String getNameField() { return this.nameField; } public String getDescriptionField() { return this.descriptionField; } public String getTypeField() { return this.typeField; } public EntityCondition getWhereCondition() throws ConstantException { if (where == null) { return null; } // else the where string is field1=value1,field2=value2 ... String[] couples = where.split(","); List<EntityCondition> conds = new ArrayList<EntityCondition>(); for (int i = 0; i < couples.length; i++) { String pair = couples[i]; String[] values = pair.split("="); if (values.length != 2) { throw new ConstantException( "The where string must be formatted as 'field1=value1,field2=value2,...', was: " + where); } conds.add(EntityCondition.makeCondition(values[0], EntityOperator.EQUALS, values[1])); } return EntityCondition.makeCondition(conds); } public List<Map<String, String>> getDbValues(Delegator delegator) throws ConstantException { // this should have been set if (UtilValidate.isEmpty(getConstantField())) { throw new ConstantException("No constantField defined in the configuration for key [" + key + "]"); } Set<String> fields = UtilMisc.toSet(getConstantField()); if (UtilValidate.isNotEmpty(getNameField())) { fields.add(getNameField()); } if (UtilValidate.isNotEmpty(getDescriptionField())) { fields.add(getDescriptionField()); } if (UtilValidate.isNotEmpty(getTypeField())) { fields.add(getTypeField()); } List<String> orderBy = new ArrayList<String>(); if (UtilValidate.isNotEmpty(getTypeField())) { orderBy.add(getTypeField()); } orderBy.add(getConstantField()); List<GenericValue> entities; try { entities = delegator.findList(getEntityName(), getWhereCondition(), fields, orderBy, null, false); } catch (GenericEntityException e) { throw new ConstantException("GenericEntityException: " + e.getMessage()); } List<Map<String, String>> values = new ArrayList<Map<String, String>>(); Set<String> constantNames = new HashSet<String>(); for (GenericValue entity : entities) { Map<String, String> map = new HashMap<String, String>(); String val = entity.getString(getConstantField()); map.put("constantValue", val); String type = ""; if (UtilValidate.isNotEmpty(getTypeField())) { type = entity.getString(getTypeField()); if (UtilValidate.isEmpty(type)) { Debug.logWarning( "The type field is empty in value: " + entity + " using its value as the type.", MODULE); type = makeClassName(val); map.put("innerClassIsSelf", "Y"); } else { type = makeClassName(type); } map.put("innerClass", type); } String name = null; if (UtilValidate.isNotEmpty(getNameField())) { name = entity.getString(getNameField()); } else { name = val; } name = makeConstantName(name); if (constantNames.contains(type + "$" + name)) { throw new ConstantException("Duplicate constant found with name [" + name + "] type [" + type + "], in key [" + key + "] with value: " + entity); } constantNames.add(type + "$" + name); map.put("constantName", name); if (UtilValidate.isNotEmpty(getDescriptionField())) { map.put("constantDescription", entity.getString(getDescriptionField())); } else { map.put("constantDescription", val); } values.add(map); } Collections.sort(values, new ConstantComparator()); return values; } } @SuppressWarnings("serial") private static class ConstantException extends Exception { public ConstantException(String message) { super(message); } } /** * Transform a name String (eg: Some String) into a java static field name (eg: SOME_STRING). * @param name the name to transform * @return a class name */ public static String makeConstantName(String name) { if (UtilValidate.isEmpty(name)) { throw new IllegalArgumentException("name called for null or empty string"); } else { String fieldName = name.toUpperCase(); fieldName = fieldName.replace(' ', '_'); fieldName = fieldName.replace('.', '_'); fieldName = fieldName.replace('-', '_'); fieldName = fieldName.replace(':', '_'); if (fieldName.substring(0, 1).matches("[0-9]")) { fieldName = NUMERIC_PREFIX + fieldName; } return fieldName; } } /** * Transform a constant String (eg: SOME_VALUE) into a class name (eg: SomeValue). * @param name the name to transform * @return a class name */ public static String makeClassName(String name) { if (UtilValidate.isEmpty(name)) { throw new IllegalArgumentException("className called for null or empty string"); } else { String className = name.toLowerCase(); className = Character.toUpperCase(className.charAt(0)) + className.substring(1); className = filterChar(className, "."); className = filterChar(className, "_"); className = filterChar(className, "-"); className = filterChar(className, ":"); return className; } } private static String filterChar(String name, String filter) { int idx = -1; String newName = name; while ((idx = newName.indexOf(filter)) >= 0) { String temp = newName.substring(0, idx); if (newName.length() > (idx + 1)) { temp += Character.toUpperCase(newName.charAt(idx + 1)); } if (newName.length() > (idx + 2)) { temp += newName.substring(idx + 2); } newName = temp; } return newName; } }