This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit baee8db689e7118a0acf1c378c31b0458ac64918 Author: Andrus Adamchik <[email protected]> AuthorDate: Sat Aug 8 16:56:58 2026 -0400 ClassGenerationAction cleanup caching Velocity engine and templates --- .../apache/cayenne/gen/ClassGenerationAction.java | 76 ++++++++++------- .../apache/cayenne/gen/TemplateLocationTest.java | 42 +++++----- .../ui/project/editor/datamap/cgen/CgenPanel.java | 94 +++++++++++----------- .../cgen/templateeditor/TemplateEditor.java | 2 +- 4 files changed, 116 insertions(+), 98 deletions(-) diff --git a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java index 263e6c8c6..82a322b54 100644 --- a/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java +++ b/cayenne-cgen/src/main/java/org/apache/cayenne/gen/ClassGenerationAction.java @@ -44,6 +44,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Collection; +import java.util.EnumMap; +import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.stream.Collectors; @@ -58,6 +60,8 @@ public class ClassGenerationAction { protected final Logger logger; private final ToolsUtilsFactory utilsFactory; private final MetadataUtils metadataUtils; + private final VelocityEngine velocityEngine; + private final Map<TemplateType, Template> templates; protected Context context; @@ -71,6 +75,7 @@ public class ClassGenerationAction { * tools.toolbox = application * tools.application.myTool = com.mycompany.MyTool</pre> * Then the methods in the MyTool class will be available for use in the template like ${myTool.myMethod(arg)} + * <p> */ public ClassGenerationAction( CgenConfiguration configuration, @@ -83,6 +88,38 @@ public class ClassGenerationAction { this.metadataUtils = Objects.requireNonNull(metadataUtils); this.logger = Objects.requireNonNull(logger); this.context = createContext(configuration.getExternalToolConfig()); + this.velocityEngine = createVelocityEngine(configuration); + this.templates = new EnumMap<>(TemplateType.class); + } + + private static VelocityEngine createVelocityEngine(CgenConfiguration configuration) { + + Properties props = new Properties(); + props.put(RuntimeConstants.RESOURCE_LOADERS, "string,cayenne"); + + props.put("resource.loader.cayenne.class", ClassGeneratorResourceLoader.class.getName()); + props.put("resource.loader.cayenne.cache", "false"); + if (configuration.getRootPath() != null) { + props.put("resource.loader.cayenne.root", configuration.getRootPath()); + } + + props.put("resource.loader.string.class", StringResourceLoader.class.getName()); + props.put("resource.loader.string.repository.name", CUSTOM_TEMPLATE_REPO); + // keep the repository local to this engine instead of sharing the global static registry + props.put("resource.loader.string.repository.static", "false"); + + StringResourceRepository repository = new StringResourceRepositoryImpl(); + for (TemplateType type : TemplateType.values()) { + CgenTemplate template = configuration.getTemplateByType(type); + if (!template.isFile()) { + repository.putStringResource(template.getName(), template.getData()); + } + } + + VelocityEngine engine = new VelocityEngine(); + engine.setApplicationAttribute(CUSTOM_TEMPLATE_REPO, repository); + engine.init(props); + return engine; } private static Context createContext(String toolConfigFile) { @@ -90,7 +127,7 @@ public class ClassGenerationAction { if (System.getProperty("org.apache.velocity.tools") == null && toolConfigFile == null) { return new VelocityContext(); } - + ToolManager manager = new ToolManager(true, true); if (toolConfigFile != null) { FactoryConfiguration config = ConfigurationUtils.find(toolConfigFile); @@ -148,9 +185,6 @@ public class ClassGenerationAction { /** * Adds entities to the internal entity list. - * - * @param entities collection - * @since 4.0 throws exception */ public void addEntities(Collection<ObjEntity> entities) { if (entities != null) { @@ -169,8 +203,7 @@ public class ClassGenerationAction { } /** - * @param dataMap to add to the list of artifacts to generate - * @since 5.0 replaces removed {@code addQueries()} method + * @since 5.0 */ public void addDataMap(DataMap dataMap) { // data map should be used only in ArtifactsGenerationMode.ALL @@ -232,32 +265,13 @@ public class ClassGenerationAction { } } + /** + * Returns a parsed template for a given type. Templates are parsed on first use and cached, so that all the + * artifacts processed by this action share a single parsed copy. + */ protected Template getTemplate(TemplateType type) { - Properties props = new Properties(); - initVelocityProperties(props, type); - VelocityEngine velocityEngine = new VelocityEngine(); - velocityEngine.init(props); - return velocityEngine.getTemplate(configuration.getTemplateByType(type).getName()); - } - - protected void initVelocityProperties(Properties props, TemplateType type) { - CgenTemplate template = configuration.getTemplateByType(type); - if (template.isFile()) { - props.put(RuntimeConstants.RESOURCE_LOADERS, "cayenne"); - props.put("resource.loader.cayenne.class", ClassGeneratorResourceLoader.class.getName()); - props.put("resource.loader.cayenne.cache", "false"); - if (configuration.getRootPath() != null) { - props.put("resource.loader.cayenne.root", configuration.getRootPath()); - } - } else { - props.put(RuntimeConstants.RESOURCE_LOADERS, "string"); - props.put("resource.loader.string.class", StringResourceLoader.class.getName()); - props.put("resource.loader.string.repository.name", CUSTOM_TEMPLATE_REPO); - - StringResourceRepository repo = new StringResourceRepositoryImpl(); - repo.putStringResource(template.getName(), template.getData()); - StringResourceLoader.setRepository(CUSTOM_TEMPLATE_REPO, repo); - } + return templates.computeIfAbsent(type, + t -> velocityEngine.getTemplate(configuration.getTemplateByType(t).getName())); } /** diff --git a/cayenne-cgen/src/test/java/org/apache/cayenne/gen/TemplateLocationTest.java b/cayenne-cgen/src/test/java/org/apache/cayenne/gen/TemplateLocationTest.java index 8c6b28daa..f9ac5e355 100644 --- a/cayenne-cgen/src/test/java/org/apache/cayenne/gen/TemplateLocationTest.java +++ b/cayenne-cgen/src/test/java/org/apache/cayenne/gen/TemplateLocationTest.java @@ -34,44 +34,48 @@ public class TemplateLocationTest extends CgenCase { @TempDir public File tempFolder; - private CgenConfiguration cgenConfiguration; - private ClassGenerationAction action; - private TemplateType templateType; + private CgenConfiguration configuration; @BeforeEach public void setUp() { - cgenConfiguration = new CgenConfiguration(); - action = new ClassGenerationAction( - cgenConfiguration, - getUnitTestInjector().getInstance(ToolsUtilsFactory.class), - getUnitTestInjector().getInstance(MetadataUtils.class), - NOPLogger.NOP_LOGGER); - templateType = TemplateType.ENTITY_SUBCLASS; + configuration = new CgenConfiguration(); } @Test public void upperLevel() throws Exception { File subFolder = new File(tempFolder, "sub"); subFolder.mkdir(); - cgenConfiguration.setRootPath(subFolder.toPath()); + configuration.setRootPath(subFolder.toPath()); new File(tempFolder, "testTemplate.vm").createNewFile(); - cgenConfiguration.setTemplate(new CgenTemplate("../testTemplate.vm", false, TemplateType.ENTITY_SUBCLASS)); - assertNotNull(action.getTemplate(templateType)); + configuration.setTemplate(new CgenTemplate("../testTemplate.vm", true, TemplateType.ENTITY_SUBCLASS)); + + assertNotNull(createAction().getTemplate(TemplateType.ENTITY_SUBCLASS)); } @Test public void sameLevel() throws Exception { - cgenConfiguration.setRootPath(tempFolder.toPath()); + configuration.setRootPath(tempFolder.toPath()); new File(tempFolder, "testTemplate2.vm").createNewFile(); - cgenConfiguration.setTemplate(new CgenTemplate("testTemplate2.vm", false, TemplateType.ENTITY_SUBCLASS)); - assertNotNull(action.getTemplate(templateType)); + configuration.setTemplate(new CgenTemplate("testTemplate2.vm", true, TemplateType.ENTITY_SUBCLASS)); + + assertNotNull(createAction().getTemplate(TemplateType.ENTITY_SUBCLASS)); } @Test public void aboveLevel() throws Exception { - cgenConfiguration.setRootPath(Paths.get(tempFolder.getParent())); + configuration.setRootPath(Paths.get(tempFolder.getParent())); new File(tempFolder, "testTemplate3.vm").createNewFile(); - cgenConfiguration.setTemplate(new CgenTemplate(tempFolder + "/testTemplate3.vm", false, TemplateType.ENTITY_SUBCLASS)); - assertNotNull(action.getTemplate(templateType)); + configuration.setTemplate( + new CgenTemplate(tempFolder + "/testTemplate3.vm", true, TemplateType.ENTITY_SUBCLASS)); + + assertNotNull(createAction().getTemplate(TemplateType.ENTITY_SUBCLASS)); + } + + private ClassGenerationAction createAction() { + return new ClassGenerationAction( + configuration, + getUnitTestInjector().getInstance(ToolsUtilsFactory.class), + getUnitTestInjector().getInstance(MetadataUtils.class), + NOPLogger.NOP_LOGGER); } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/CgenPanel.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/CgenPanel.java index e57bc1a40..92bddcc75 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/CgenPanel.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/CgenPanel.java @@ -84,7 +84,7 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd private CgenConfigList cgenConfigList; private Object currentClass; - private CgenConfiguration cgenConfiguration; + private CgenConfiguration configuration; private boolean initFromModel; @@ -123,8 +123,8 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd }); } - public CgenConfiguration getCgenConfiguration() { - return cgenConfiguration; + public CgenConfiguration getConfiguration() { + return configuration; } public CgenConfigPanel getStandardModeController() { @@ -212,14 +212,14 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd } public void checkCgenConfigDirty() { - if (initFromModel || cgenConfiguration == null) { + if (initFromModel || configuration == null) { return; } DataMap map = session.getSelectedDataMap(); CgenConfigList existingConfigurations = app.getMetaData().get(map, CgenConfigList.class); if (existingConfigurations == null) { - cgenConfigList.add(cgenConfiguration); + cgenConfigList.add(configuration); app.getMetaData().add(map, cgenConfigList); } @@ -232,7 +232,7 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd initCgenConfigurations(map); initConfigurationsComboBox(); setConfiguration((String) configurationsComboBox.getSelectedItem()); - cgenConfigPanel.initForm(cgenConfiguration); + cgenConfigPanel.initForm(configuration); classesSelector.startup(); initFromModel = false; classesSelector.validate(classes); @@ -242,19 +242,19 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd * Builds a class generator for provided selections. */ private void setConfiguration(String selectedConfig) { - cgenConfiguration = cgenConfigList.getByName(selectedConfig); - if (cgenConfiguration != null) { + configuration = cgenConfigList.getByName(selectedConfig); + if (configuration != null) { // Refresh entityArtifacts from the DataMap so any entities added since the config // was last loaded (e.g. by a dbimport run) are treated as included by default. - cgenConfiguration.resolveExcludedEntities(); - cgenConfiguration.resolveExcludedEmbeddables(); - addToSelectedEntities(cgenConfiguration.getEntities()); - addToSelectedEmbeddables(cgenConfiguration.getEmbeddables()); + configuration.resolveExcludedEntities(); + configuration.resolveExcludedEmbeddables(); + addToSelectedEntities(configuration.getEntities()); + addToSelectedEmbeddables(configuration.getEmbeddables()); return; } DataMap dataMap = session.getSelectedDataMap(); - cgenConfiguration = createDefaultCgenConfiguration(dataMap); + configuration = createDefaultCgenConfiguration(dataMap); addToSelectedEntities(dataMap.getObjEntities() .stream() .map(Entity::getName) @@ -341,7 +341,7 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd } selectionModel.clearAll(); setConfiguration((String) configurationsComboBox.getSelectedItem()); - cgenConfigPanel.initForm(cgenConfiguration); + cgenConfigPanel.initForm(configuration); classesSelector.initBindings(); classesSelector.validate(classes); }); @@ -359,7 +359,7 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd .addModule(binder -> binder.bind(DataChannelMetaData.class).toInstance(app.getMetaData())) .create() .getInstance(ClassGenerationActionFactory.class) - .createAction(cgenConfiguration, NOPLogger.NOP_LOGGER); + .createAction(configuration, NOPLogger.NOP_LOGGER); try { generator.prepareArtifacts(); @@ -400,7 +400,7 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd configurationsComboBox.getSelectedItem()); if (name != null) { if (!cgenConfigList.isExist(name) && !name.isEmpty()) { - cgenConfiguration.setName(name); + configuration.setName(name); configurationsComboBox.removeItem(configurationsComboBox.getSelectedItem()); configurationsComboBox.addItem(name); configurationsComboBox.setSelectedItem(name); @@ -418,7 +418,7 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd JOptionPane.YES_NO_OPTION); if (result == JOptionPane.OK_OPTION) { if (configurationsComboBox.getItemCount() > 1) { - cgenConfigList.removeByName(cgenConfiguration.getName()); + cgenConfigList.removeByName(configuration.getName()); configurationsComboBox.removeItem(configurationsComboBox.getSelectedItem()); configurationsComboBox.setSelectedIndex(0); } else { @@ -443,22 +443,22 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd } private void updateArtifactGenerationMode(boolean selected) { - cgenConfiguration.setArtifactsGenerationMode(selected ? "all" : "entity"); + configuration.setArtifactsGenerationMode(selected ? "all" : "entity"); checkCgenConfigDirty(); } private void updateEntities() { - if (cgenConfiguration != null) { - cgenConfiguration.getEntities().clear(); + if (configuration != null) { + configuration.getEntities().clear(); for (ObjEntity entity : selectionModel.getSelectedEntities(classes)) { - cgenConfiguration.loadEntity(entity); + configuration.loadEntity(entity); } // Keep excludedEntityArtifacts in sync so XML serialization stays correct - Collection<String> excluded = cgenConfiguration.getExcludedEntityArtifacts(); + Collection<String> excluded = configuration.getExcludedEntityArtifacts(); excluded.clear(); - DataMap dataMap = cgenConfiguration.getDataMap(); + DataMap dataMap = configuration.getDataMap(); if (dataMap != null) { - Set<String> selected = cgenConfiguration.getEntities(); + Set<String> selected = configuration.getEntities(); dataMap.getObjEntities().stream() .map(ObjEntity::getName) .filter(name -> !selected.contains(name)) @@ -469,17 +469,17 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd } private void updateEmbeddables() { - if (cgenConfiguration != null) { - cgenConfiguration.getEmbeddables().clear(); + if (configuration != null) { + configuration.getEmbeddables().clear(); for (Embeddable embeddable : selectionModel.getSelectedEmbeddables(classes)) { - cgenConfiguration.loadEmbeddable(embeddable); + configuration.loadEmbeddable(embeddable); } // Keep excludedEmbeddableArtifacts in sync so XML serialization stays correct - Collection<String> excluded = cgenConfiguration.getExcludedEmbeddableArtifacts(); + Collection<String> excluded = configuration.getExcludedEmbeddableArtifacts(); excluded.clear(); - DataMap dataMap = cgenConfiguration.getDataMap(); + DataMap dataMap = configuration.getDataMap(); if (dataMap != null) { - Set<String> selected = cgenConfiguration.getEmbeddables(); + Set<String> selected = configuration.getEmbeddables(); dataMap.getEmbeddables().stream() .map(Embeddable::getClassName) .filter(name -> !selected.contains(name)) @@ -497,8 +497,8 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd private void addEntity(DataMap dataMap, ObjEntity objEntity) { prepareClasses(dataMap); selectionModel.addSelectedEntity(objEntity.getName()); - if (cgenConfiguration != null) { - cgenConfiguration.loadEntity(objEntity); + if (configuration != null) { + configuration.loadEntity(objEntity); } checkCgenConfigDirty(); } @@ -517,9 +517,9 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd return; } selectionModel.renameSelectedEntity(entity.getDataMap(), oldName, newName); - if (cgenConfiguration != null) { - if (cgenConfiguration.getEntities().remove(oldName)) { - cgenConfiguration.getEntities().add(newName); + if (configuration != null) { + if (configuration.getEntities().remove(oldName)) { + configuration.getEntities().add(newName); } } checkCgenConfigDirty(); @@ -533,8 +533,8 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd @Override public void objEntityRemoved(ObjEntityEvent e) { selectionModel.removeFromSelectedEntities(e.getEntity()); - if (cgenConfiguration != null) { - cgenConfiguration.getEntities().remove(e.getEntity().getName()); + if (configuration != null) { + configuration.getEntities().remove(e.getEntity().getName()); } checkCgenConfigDirty(); } @@ -548,9 +548,9 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd return; } selectionModel.renameSelectedEmbeddable(map, oldClassName, newClassName); - if (cgenConfiguration != null) { - if (cgenConfiguration.getEmbeddables().remove(oldClassName)) { - cgenConfiguration.getEmbeddables().add(newClassName); + if (configuration != null) { + if (configuration.getEmbeddables().remove(oldClassName)) { + configuration.getEmbeddables().add(newClassName); } } checkCgenConfigDirty(); @@ -561,8 +561,8 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd prepareClasses(map); Embeddable embeddable = e.getEmbeddable(); selectionModel.addSelectedEmbeddable(embeddable.getClassName()); - if (cgenConfiguration != null) { - cgenConfiguration.loadEmbeddable(embeddable); + if (configuration != null) { + configuration.loadEmbeddable(embeddable); } checkCgenConfigDirty(); } @@ -570,8 +570,8 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd @Override public void embeddableRemoved(EmbeddableEvent e, DataMap map) { selectionModel.removeFromSelectedEmbeddables(e.getEmbeddable()); - if (cgenConfiguration != null) { - cgenConfiguration.getEmbeddables().remove(e.getEmbeddable().getClassName()); + if (configuration != null) { + configuration.getEmbeddables().remove(e.getEmbeddable().getClassName()); } checkCgenConfigDirty(); } @@ -585,7 +585,7 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd // DbImportProjectSaver fires a pair of REMOVE / ADD events after merging new entities into a DataMap. // Re-initialize the panel so newly imported entities appear as included. DataMap map = e.getDataMap(); - if (map != null && cgenConfiguration != null && map == cgenConfiguration.getDataMap()) { + if (map != null && configuration != null && map == configuration.getDataMap()) { initFromModel(map); } } @@ -598,9 +598,9 @@ public class CgenPanel extends ProjectPanel implements ObjEntityListener, Embedd * Update cgen path on project save when no path is already set manually. */ private void onProjectSaved(ProjectAfterSaveEvent e) { - if (cgenConfigPanel != null && cgenConfiguration != null) { + if (cgenConfigPanel != null && configuration != null) { cgenConfigPanel.getOutputFolder() - .setText(cgenConfiguration.buildOutputPath().toString()); + .setText(configuration.buildOutputPath().toString()); } } diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/templateeditor/TemplateEditor.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/templateeditor/TemplateEditor.java index 910f60692..1f0acaebc 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/templateeditor/TemplateEditor.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/ui/project/editor/datamap/cgen/templateeditor/TemplateEditor.java @@ -86,7 +86,7 @@ public class TemplateEditor extends AppFrame { super(app); this.parent = parent; this.templateType = templateType; - this.configuration = parent.getCodeGeneratorController().getCgenConfiguration(); + this.configuration = parent.getCodeGeneratorController().getConfiguration(); this.currentDataMap = configuration.getDataMap(); this.artefactsConfigurator = setupArtefactConfigurator(templateType);
