Repository: cayenne Updated Branches: refs/heads/master 8c3c49a4d -> c4b110043
CAY-2395 cdbimport: add option to create project file Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/c4b11004 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/c4b11004 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/c4b11004 Branch: refs/heads/master Commit: c4b110043e89e3ad74f28d74d272ab3c56e4ba16 Parents: 8c3c49a Author: Nikita Timofeev <[email protected]> Authored: Tue Jan 23 16:18:48 2018 +0300 Committer: Nikita Timofeev <[email protected]> Committed: Tue Jan 23 16:18:48 2018 +0300 ---------------------------------------------------------------------- .../apache/cayenne/tools/DbImporterTask.java | 17 ++ .../cayenne/tools/DbImporterTaskTest.java | 5 + .../apache/cayenne/tools/build-with-project.xml | 36 ++++ .../reverse/configuration/ToolsModule.java | 3 + .../reverse/dbimport/DbImportConfiguration.java | 9 + .../reverse/dbimport/DefaultDbImportAction.java | 65 ++++++- .../dbimport/DefaultDbImportActionTest.java | 171 +++++++++++++++++-- .../org/apache/cayenne/tools/DbImportTask.java | 22 +++ .../org/apache/cayenne/tools/DbImportIT.java | 34 ++++ .../cayenne/tools/dbimport_with_project.gradle | 40 +++++ .../apache/cayenne/tools/test_project_db.sql | 53 ++++++ docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 + .../apache/cayenne/tools/DbImporterMojo.java | 9 + .../dialog/db/load/ModelerDbImportAction.java | 6 +- 14 files changed, 453 insertions(+), 18 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-ant/src/main/java/org/apache/cayenne/tools/DbImporterTask.java ---------------------------------------------------------------------- diff --git a/cayenne-ant/src/main/java/org/apache/cayenne/tools/DbImporterTask.java b/cayenne-ant/src/main/java/org/apache/cayenne/tools/DbImporterTask.java index 5d22173..0d97213 100644 --- a/cayenne-ant/src/main/java/org/apache/cayenne/tools/DbImporterTask.java +++ b/cayenne-ant/src/main/java/org/apache/cayenne/tools/DbImporterTask.java @@ -224,6 +224,9 @@ public class DbImporterTask extends Task { config.setUsePrimitives(flag); } + /** + * @since 4.0 + */ public void setUseJava7Types(boolean flag) { config.setUseJava7Types(flag); } @@ -248,6 +251,20 @@ public class DbImporterTask extends Task { config.setTargetDataMap(map); } + /** + * @since 4.1 + */ + public File getCayenneProject() { + return config.getCayenneProject(); + } + + /** + * @since 4.1 + */ + public void setCayenneProject(File cayenneProject) { + config.setCayenneProject(cayenneProject); + } + public DbImportConfiguration toParameters() { return config; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-ant/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java ---------------------------------------------------------------------- diff --git a/cayenne-ant/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java b/cayenne-ant/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java index 71ae4e3..43745e7 100644 --- a/cayenne-ant/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java +++ b/cayenne-ant/src/test/java/org/apache/cayenne/tools/DbImporterTaskTest.java @@ -95,6 +95,11 @@ public class DbImporterTaskTest { } @Test + public void testBuildWithProject() throws Exception { + assertNotNull(getCdbImport("build-with-project.xml").getCayenneProject()); + } + + @Test public void testIncludeTable() throws Exception { test("build-include-table.xml"); } http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-ant/src/test/resources/org/apache/cayenne/tools/build-with-project.xml ---------------------------------------------------------------------- diff --git a/cayenne-ant/src/test/resources/org/apache/cayenne/tools/build-with-project.xml b/cayenne-ant/src/test/resources/org/apache/cayenne/tools/build-with-project.xml new file mode 100644 index 0000000..223de6a --- /dev/null +++ b/cayenne-ant/src/test/resources/org/apache/cayenne/tools/build-with-project.xml @@ -0,0 +1,36 @@ +<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~ 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. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--> + +<project name="MyProject" default="dist" basedir="."> + + <taskdef name="cdbimport" classname="org.apache.cayenne.tools.DbImporterTask" taskname="cdbimport" + classpath="${basedir}"/> + + <target name="dist"> + <cdbimport map="${context.dir}/WEB-INF/DefaultMap.map.xml" + cayenneProject="${context.dir}/WEB-INF/cayenne-project.xml" + adapter="org.apache.cayenne.dba.hsqldb.HSQLDBAdapter" + driver="org.hsqldb.jdbcDriver" + url="jdbc:hsqldb:hsql://localhost/bookmarker" + username="sa"> + </cdbimport> + </target> + + +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java index 8e3673d..1ab51f0 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/configuration/ToolsModule.java @@ -24,6 +24,7 @@ import org.apache.cayenne.access.translator.batch.DefaultBatchTranslatorFactory; import org.apache.cayenne.access.types.DefaultValueObjectTypeRegistry; import org.apache.cayenne.access.types.ValueObjectTypeRegistry; import org.apache.cayenne.configuration.Constants; +import org.apache.cayenne.configuration.DataChannelDescriptorLoader; import org.apache.cayenne.configuration.DataMapLoader; import org.apache.cayenne.configuration.DefaultRuntimeProperties; import org.apache.cayenne.configuration.RuntimeProperties; @@ -34,6 +35,7 @@ import org.apache.cayenne.configuration.server.ServerModule; import org.apache.cayenne.configuration.xml.DataChannelMetaData; import org.apache.cayenne.configuration.xml.DefaultDataChannelMetaData; import org.apache.cayenne.configuration.xml.HandlerFactory; +import org.apache.cayenne.configuration.xml.XMLDataChannelDescriptorLoader; import org.apache.cayenne.configuration.xml.XMLDataMapLoader; import org.apache.cayenne.configuration.xml.XMLReaderProvider; import org.apache.cayenne.dba.db2.DB2Sniffer; @@ -117,6 +119,7 @@ public class ToolsModule implements Module { binder.bind(DataSourceFactory.class).to(DriverDataSourceFactory.class); binder.bind(DataMapLoader.class).to(XMLDataMapLoader.class); + binder.bind(DataChannelDescriptorLoader.class).to(XMLDataChannelDescriptorLoader.class); binder.bind(HandlerFactory.class).to(ExtensionAwareHandlerFactory.class); binder.bind(DataChannelMetaData.class).to(DefaultDataChannelMetaData.class); binder.bind(XMLReader.class).toProviderInstance(new XMLReaderProvider(true)).withoutScope(); http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DbImportConfiguration.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DbImportConfiguration.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DbImportConfiguration.java index 110a7ff..06b04f0 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DbImportConfiguration.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DbImportConfiguration.java @@ -61,6 +61,7 @@ public class DbImportConfiguration { private String stripFromTableNames; private boolean forceDataMapCatalog; private boolean forceDataMapSchema; + private File cayenneProject; public DbImportConfiguration() { this.dataSourceInfo = new DataSourceInfo(); @@ -154,6 +155,14 @@ public class DbImportConfiguration { this.useJava7Types = useJava7Types; } + public File getCayenneProject() { + return cayenneProject; + } + + public void setCayenneProject(File cayenneProject) { + this.cayenneProject = cayenneProject; + } + public NameFilter createMeaningfulPKFilter() { if (meaningfulPkTables == null) { http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportAction.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportAction.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportAction.java index c31aaab..1e08844 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportAction.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportAction.java @@ -18,7 +18,11 @@ ****************************************************************/ package org.apache.cayenne.dbsync.reverse.dbimport; +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.configuration.ConfigurationNode; import org.apache.cayenne.configuration.ConfigurationTree; +import org.apache.cayenne.configuration.DataChannelDescriptor; +import org.apache.cayenne.configuration.DataChannelDescriptorLoader; import org.apache.cayenne.configuration.DataMapLoader; import org.apache.cayenne.configuration.DataNodeDescriptor; import org.apache.cayenne.configuration.server.DataSourceFactory; @@ -55,8 +59,8 @@ import org.slf4j.Logger; import javax.sql.DataSource; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; +import java.net.MalformedURLException; import java.sql.Connection; import java.util.Collection; import java.util.Collections; @@ -77,19 +81,22 @@ public class DefaultDbImportAction implements DbImportAction { private final DbAdapterFactory adapterFactory; private final DataMapLoader mapLoader; private final MergerTokenFactoryProvider mergerTokenFactoryProvider; + private final DataChannelDescriptorLoader dataChannelDescriptorLoader; public DefaultDbImportAction(@Inject Logger logger, @Inject ProjectSaver projectSaver, @Inject DataSourceFactory dataSourceFactory, @Inject DbAdapterFactory adapterFactory, @Inject DataMapLoader mapLoader, - @Inject MergerTokenFactoryProvider mergerTokenFactoryProvider) { + @Inject MergerTokenFactoryProvider mergerTokenFactoryProvider, + @Inject DataChannelDescriptorLoader dataChannelDescriptorLoader) { this.logger = logger; this.projectSaver = projectSaver; this.dataSourceFactory = dataSourceFactory; this.adapterFactory = adapterFactory; this.mapLoader = mapLoader; this.mergerTokenFactoryProvider = mergerTokenFactoryProvider; + this.dataChannelDescriptorLoader = dataChannelDescriptorLoader; } protected static List<MergerToken> sort(List<MergerToken> reverse) { @@ -172,7 +179,7 @@ public class DefaultDbImportAction implements DbImportAction { hasChanges |= syncProcedures(targetDataMap, sourceDataMap, loaderConfig.getFiltersConfig()); if (hasChanges) { - saveLoaded(targetDataMap); + saveLoaded(targetDataMap, config); } } @@ -383,10 +390,58 @@ public class DefaultDbImportAction implements DbImportAction { return hasChanges; } - protected void saveLoaded(DataMap dataMap) throws FileNotFoundException { - ConfigurationTree<DataMap> projectRoot = new ConfigurationTree<>(dataMap); + /** + * Save imported data. + * This can create DataMap and/or Project files. + */ + protected void saveLoaded(DataMap dataMap, DbImportConfiguration config) throws MalformedURLException { + ConfigurationTree<ConfigurationNode> projectRoot; + if(config.getCayenneProject() == null) { + // Old version of cdbimport, no Cayenne project, need to save only DataMap + projectRoot = new ConfigurationTree<>(dataMap); + } else { + // Cayenne project is present + DataChannelDescriptor dataChannelDescriptor; + if(config.getCayenneProject().exists()) { + // Cayenne project file exists, need to read it and push DataMap inside + URLResource configurationResource = new URLResource(config.getCayenneProject().toURI().toURL()); + ConfigurationTree<DataChannelDescriptor> configurationTree = dataChannelDescriptorLoader.load(configurationResource); + if(!configurationTree.getLoadFailures().isEmpty()) { + throw new CayenneRuntimeException("Unable to load cayenne project %s, %s", config.getCayenneProject(), + configurationTree.getLoadFailures().iterator().next().getDescription()); + } + dataChannelDescriptor = configurationTree.getRootNode(); + // remove old copy of DataMap if it's there + DataMap oldDataMap = dataChannelDescriptor.getDataMap(dataMap.getName()); + if(oldDataMap != null) { + dataChannelDescriptor.getDataMaps().remove(oldDataMap); + } + } else { + // No project file yet, can simply create empty project with resulting DataMap + dataChannelDescriptor = new DataChannelDescriptor(); + dataChannelDescriptor.setName(getProjectNameFromFileName(config.getCayenneProject().getName())); + dataChannelDescriptor.setConfigurationSource(new URLResource(config.getCayenneProject().toURI().toURL())); + logger.info("Project file does not exist. New project will be saved into '" + config.getCayenneProject().getAbsolutePath()); + } + + dataChannelDescriptor.getDataMaps().add(dataMap); + projectRoot = new ConfigurationTree<>(dataChannelDescriptor); + } + Project project = new Project(projectRoot); projectSaver.save(project); + + logger.info(""); + logger.info("All changes saved."); + } + + protected String getProjectNameFromFileName(String fileName) { + int xmlExtPosition = fileName.lastIndexOf(".xml"); + String name = fileName.substring(0, xmlExtPosition == -1 ? fileName.length() : xmlExtPosition); + if(fileName.startsWith("cayenne-")) { + name = name.substring("cayenne-".length()); + } + return name; } protected DataMap load(DbImportConfiguration config, DbAdapter adapter, Connection connection) throws Exception { http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java ---------------------------------------------------------------------- diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java index df8f8b1..364f770 100644 --- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/reverse/dbimport/DefaultDbImportActionTest.java @@ -19,11 +19,11 @@ package org.apache.cayenne.dbsync.reverse.dbimport; import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.configuration.DataChannelDescriptorLoader; import org.apache.cayenne.configuration.DataMapLoader; import org.apache.cayenne.configuration.DataNodeDescriptor; import org.apache.cayenne.configuration.server.DataSourceFactory; import org.apache.cayenne.configuration.server.DbAdapterFactory; -import org.apache.cayenne.configuration.xml.DataChannelMetaData; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dbsync.DbSyncModule; import org.apache.cayenne.dbsync.filter.NamePatternMatcher; @@ -60,6 +60,8 @@ import org.junit.Test; import javax.sql.DataSource; import java.io.File; import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Files; import java.sql.Connection; import java.sql.SQLException; import java.util.Collections; @@ -132,7 +134,7 @@ public class DefaultDbImportActionTest { }; final boolean[] haveWeTriedToSave = {false}; - DefaultDbImportAction action = buildDbImportAction(new FileProjectSaver(Collections.<ProjectExtension>emptyList()) { + DefaultDbImportAction action = buildDbImportAction(new FileProjectSaver(Collections.emptyList()) { @Override public void save(Project project) { haveWeTriedToSave[0] = true; @@ -299,7 +301,9 @@ public class DefaultDbImportActionTest { MergerTokenFactoryProvider mergerTokenFactoryProvider = mock(MergerTokenFactoryProvider.class); when(mergerTokenFactoryProvider.get((DbAdapter)any())).thenReturn(new DefaultMergerTokenFactory()); - return new DefaultDbImportAction(log, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider) { + DataChannelDescriptorLoader dataChannelDescriptorLoader = mock(DataChannelDescriptorLoader.class); + + return new DefaultDbImportAction(log, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider, dataChannelDescriptorLoader) { protected DbLoader createDbLoader(DbAdapter adapter, Connection connection, @@ -309,26 +313,32 @@ public class DefaultDbImportActionTest { }; } + private URL getPackageURL() { + String packagePath = getClass().getPackage().getName().replace('.', '/'); + URL packageUrl = getClass().getClassLoader().getResource(packagePath); + assertNotNull(packageUrl); + return packageUrl; + } + @Test - public void testSaveLoaded() throws Exception { + public void testSaveLoadedNoProject() throws Exception { Logger log = mock(Logger.class); Injector i = DIBootstrap.createInjector(new DbSyncModule(), new ToolsModule(log), new DbImportModule()); + DbImportConfiguration params = mock(DbImportConfiguration.class); + when(params.getCayenneProject()).thenReturn(null); - DefaultDbImportAction action = (DefaultDbImportAction) i.getInstance(DbImportAction.class); + URL outUrl = new URL(getPackageURL(), "dbimport/testSaveLoaded1.map.xml"); - String packagePath = getClass().getPackage().getName().replace('.', '/'); - URL packageUrl = getClass().getClassLoader().getResource(packagePath); - assertNotNull(packageUrl); - URL outUrl = new URL(packageUrl, "dbimport/testSaveLoaded1.map.xml"); + DefaultDbImportAction action = (DefaultDbImportAction) i.getInstance(DbImportAction.class); File out = new File(outUrl.toURI()); out.delete(); - assertFalse(out.isFile()); + assertFalse(out.exists()); DataMap map = new DataMap("testSaveLoaded1"); map.setConfigurationSource(new URLResource(outUrl)); - action.saveLoaded(map); + action.saveLoaded(map, params); assertTrue(out.isFile()); @@ -337,6 +347,145 @@ public class DefaultDbImportActionTest { } @Test + public void testSaveLoadedWithEmptyProject() throws Exception { + Logger log = mock(Logger.class); + Injector i = DIBootstrap.createInjector(new DbSyncModule(), new ToolsModule(log), new DbImportModule()); + DbImportConfiguration params = mock(DbImportConfiguration.class); + + URL projectURL = new URL(getPackageURL(), "dbimport/cayenne-testProject2.map.xml"); + File projectFile = new File(projectURL.toURI()); + projectFile.delete(); + assertFalse(projectFile.exists()); + when(params.getCayenneProject()).thenReturn(projectFile); + + DefaultDbImportAction action = (DefaultDbImportAction) i.getInstance(DbImportAction.class); + + URL dataMapURL = new URL(getPackageURL(), "dbimport/testSaveLoaded2.map.xml"); + + File dataMapFile = new File(dataMapURL.toURI()); + dataMapFile.delete(); + assertFalse(dataMapFile.exists()); + + DataMap map = new DataMap("testSaveLoaded2"); + map.setConfigurationSource(new URLResource(dataMapURL)); + + action.saveLoaded(map, params); + + assertTrue(dataMapFile.isFile()); + assertTrue(projectFile.isFile()); + + String dataMapContents = Util.stringFromFile(dataMapFile); + assertTrue("Has no project version saved", dataMapContents.contains("project-version=\"")); + + String projectContents = Util.stringFromFile(projectFile); + assertTrue("Has no project version saved", projectContents.contains("project-version=\"")); + assertTrue("Has no datamap in project", projectContents.contains("<map name=\"testSaveLoaded2\"/>")); + } + + @Test + public void testSaveLoadedWithNonEmptyProject() throws Exception { + Logger log = mock(Logger.class); + Injector i = DIBootstrap.createInjector(new DbSyncModule(), new ToolsModule(log), new DbImportModule()); + DbImportConfiguration params = mock(DbImportConfiguration.class); + + URL projectURL = new URL(getPackageURL(), "dbimport/cayenne-testProject3.map.xml"); + File projectFile = new File(projectURL.toURI()); + projectFile.delete(); + assertFalse(projectFile.exists()); + + Files.write(projectFile.toPath(), ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<domain xmlns=\"http://cayenne.apache.org/schema/10/domain\"\n" + + "\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + + "\t xsi:schemaLocation=\"http://cayenne.apache.org/schema/10/domain http://cayenne.apache.org/schema/10/domain.xsd\"\n" + + "\t project-version=\"10\">\n" + + "</domain>").getBytes(Charset.forName("UTF-8"))); + assertTrue(projectFile.isFile()); + + when(params.getCayenneProject()).thenReturn(projectFile); + + DefaultDbImportAction action = (DefaultDbImportAction) i.getInstance(DbImportAction.class); + + URL dataMapURL = new URL(getPackageURL(), "dbimport/testSaveLoaded3.map.xml"); + + File dataMapFile = new File(dataMapURL.toURI()); + dataMapFile.delete(); + assertFalse(dataMapFile.exists()); + + DataMap map = new DataMap("testSaveLoaded3"); + map.setConfigurationSource(new URLResource(dataMapURL)); + + action.saveLoaded(map, params); + + assertTrue(dataMapFile.isFile()); + assertTrue(projectFile.isFile()); + + String dataMapContents = Util.stringFromFile(dataMapFile); + assertTrue("Has no project version saved", dataMapContents.contains("project-version=\"")); + + String projectContents = Util.stringFromFile(projectFile); + assertTrue("Has no project version saved", projectContents.contains("project-version=\"")); + assertTrue("Has no datamap in project", projectContents.contains("<map name=\"testSaveLoaded3\"/>")); + } + + @Test + public void testSaveLoadedWithNonEmptyProjectAndNonEmptyDataMap() throws Exception { + Logger log = mock(Logger.class); + Injector i = DIBootstrap.createInjector(new DbSyncModule(), new ToolsModule(log), new DbImportModule()); + DbImportConfiguration params = mock(DbImportConfiguration.class); + + URL projectURL = new URL(getPackageURL(), "dbimport/cayenne-testProject4.map.xml"); + File projectFile = new File(projectURL.toURI()); + projectFile.delete(); + assertFalse(projectFile.exists()); + + Files.write(projectFile.toPath(), ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<domain xmlns=\"http://cayenne.apache.org/schema/10/domain\"\n" + + "\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + + "\t xsi:schemaLocation=\"http://cayenne.apache.org/schema/10/domain http://cayenne.apache.org/schema/10/domain.xsd\"\n" + + "\t project-version=\"10\">\n" + + "\t<map name=\"testSaveLoaded4\"/>\n" + + "</domain>").getBytes(Charset.forName("UTF-8"))); + assertTrue(projectFile.isFile()); + + when(params.getCayenneProject()).thenReturn(projectFile); + + DefaultDbImportAction action = (DefaultDbImportAction) i.getInstance(DbImportAction.class); + + URL dataMapURL = new URL(getPackageURL(), "dbimport/testSaveLoaded4.map.xml"); + + File dataMapFile = new File(dataMapURL.toURI()); + dataMapFile.delete(); + assertFalse(dataMapFile.exists()); + + Files.write(dataMapFile.toPath(), ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + + "<data-map xmlns=\"http://cayenne.apache.org/schema/10/modelMap\"\n" + + "\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + + "\t xsi:schemaLocation=\"http://cayenne.apache.org/schema/10/modelMap http://cayenne.apache.org/schema/10/modelMap.xsd\"\n" + + "\t project-version=\"10\">\n" + + "\t<db-entity name=\"test\">\n" + + "\t\t<db-attribute name=\"test\" type=\"INT\"/>\n" + + "\t</db-entity>\n" + + "</data-map>").getBytes(Charset.forName("UTF-8"))); + assertTrue(dataMapFile.isFile()); + + DataMap map = new DataMap("testSaveLoaded4"); + map.setConfigurationSource(new URLResource(dataMapURL)); + + action.saveLoaded(map, params); + + assertTrue(dataMapFile.isFile()); + assertTrue(projectFile.isFile()); + + String dataMapContents = Util.stringFromFile(dataMapFile); + assertTrue("Has no project version saved", dataMapContents.contains("project-version=\"")); + assertFalse(dataMapContents.contains("<db-entity")); + + String projectContents = Util.stringFromFile(projectFile); + assertTrue("Has no project version saved", projectContents.contains("project-version=\"")); + assertEquals("Has no or too many datamaps in project", 1, Util.countMatches(projectContents, "<map name=\"testSaveLoaded4\"/>")); + } + + @Test public void testMergeTokensSorting() { LinkedList<MergerToken> tokens = new LinkedList<MergerToken>(); tokens.add(new AddColumnToModel(null, null)); http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-gradle-plugin/src/main/java/org/apache/cayenne/tools/DbImportTask.java ---------------------------------------------------------------------- diff --git a/cayenne-gradle-plugin/src/main/java/org/apache/cayenne/tools/DbImportTask.java b/cayenne-gradle-plugin/src/main/java/org/apache/cayenne/tools/DbImportTask.java index c640439..e5a9bd1 100644 --- a/cayenne-gradle-plugin/src/main/java/org/apache/cayenne/tools/DbImportTask.java +++ b/cayenne-gradle-plugin/src/main/java/org/apache/cayenne/tools/DbImportTask.java @@ -62,6 +62,9 @@ public class DbImportTask extends BaseCayenneTask { @Internal private ReverseEngineering reverseEngineering; + @Internal + private File cayenneProject; + public DbImportTask() { // this task should be executed every invocation, so it is never up to date. getOutputs().upToDateWhen(new Spec<Task>() { @@ -127,6 +130,7 @@ public class DbImportTask extends BaseCayenneTask { config.setUsername(dataSource.getUsername()); config.setUsePrimitives(reverseEngineering.isUsePrimitives()); config.setUseJava7Types(reverseEngineering.isUseJava7Types()); + config.setCayenneProject(cayenneProject); return config; } @@ -167,4 +171,22 @@ public class DbImportTask extends BaseCayenneTask { public void adapter(String adapter) { setAdapter(adapter); } + + @OutputFile + @Optional + public File getCayenneProject() { + return cayenneProject; + } + + public void setCayenneProject(File cayenneProject) { + this.cayenneProject = cayenneProject; + } + + public void cayenneProject(File cayenneProject) { + this.cayenneProject = cayenneProject; + } + + public void cayenneProject(String cayenneProjectFileName) { + this.cayenneProject = getProject().file(cayenneProjectFileName); + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-gradle-plugin/src/test/java/org/apache/cayenne/tools/DbImportIT.java ---------------------------------------------------------------------- diff --git a/cayenne-gradle-plugin/src/test/java/org/apache/cayenne/tools/DbImportIT.java b/cayenne-gradle-plugin/src/test/java/org/apache/cayenne/tools/DbImportIT.java index 00ecbe5..1fa16a7 100644 --- a/cayenne-gradle-plugin/src/test/java/org/apache/cayenne/tools/DbImportIT.java +++ b/cayenne-gradle-plugin/src/test/java/org/apache/cayenne/tools/DbImportIT.java @@ -114,9 +114,43 @@ public class DbImportIT extends BaseTaskIT { assertTrue(result.getOutput().contains("Table: SCHEMA_01.TEST2")); } + @Test + public void withProjectTaskSuccess() throws Exception { + String dbUrl = prepareDerbyDatabase("test_project_db"); + File dataMap = new File(projectDir.getAbsolutePath() + "/datamap.map.xml"); + assertFalse(dataMap.exists()); + File project = new File(projectDir.getAbsolutePath() + "/cayenne-project.xml"); + assertFalse(project.exists()); + + GradleRunner runner = createRunner("dbimport_with_project", "cdbimport", "--info", "-PdbUrl=" + dbUrl); + + BuildResult result = runner.build(); + + assertNotNull(result.task(":cdbimport")); + assertEquals(TaskOutcome.SUCCESS, result.task(":cdbimport").getOutcome()); + + assertTrue(dataMap.isFile()); + assertTrue(project.isFile()); + + // Check few lines from reverse engineering output + assertTrue(result.getOutput().contains("Table: APP.PAINTING")); + assertTrue(result.getOutput().contains("Db Relationship : toOne (EXHIBIT.GALLERY_ID, GALLERY.GALLERY_ID)")); + assertTrue(result.getOutput().contains("Db Relationship : toMany (GALLERY.GALLERY_ID, PAINTING.GALLERY_ID)")); + assertTrue(result.getOutput().contains("Create Table ARTIST")); + assertFalse(result.getOutput().contains("Create Table PAINTING1")); + assertTrue(result.getOutput().contains("Skip relation: '.APP.ARTIST.ARTIST_ID <- .APP.PAINTING1.ARTIST_ID # 1'")); + assertTrue(result.getOutput().contains("Migration Complete Successfully.")); + } + private String prepareDerbyDatabase(String sqlFile) throws Exception { URL sqlUrl = Objects.requireNonNull(ResourceUtil.getResource(getClass(), sqlFile + ".sql")); String dbUrl = "jdbc:derby:" + projectDir.getAbsolutePath() + "/build/" + sqlFile; + + // Try to open connection, it may fail at first time, so ignore it + try (Connection unused = DriverManager.getConnection(dbUrl + ";create=true")) { + } catch (SQLException ignore) { + } + try (Connection connection = DriverManager.getConnection(dbUrl + ";create=true")) { try (Statement stmt = connection.createStatement()) { for (String sql : SQLReader.statements(sqlUrl, ";")) { http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-gradle-plugin/src/test/resources/org/apache/cayenne/tools/dbimport_with_project.gradle ---------------------------------------------------------------------- diff --git a/cayenne-gradle-plugin/src/test/resources/org/apache/cayenne/tools/dbimport_with_project.gradle b/cayenne-gradle-plugin/src/test/resources/org/apache/cayenne/tools/dbimport_with_project.gradle new file mode 100644 index 0000000..d99dd49 --- /dev/null +++ b/cayenne-gradle-plugin/src/test/resources/org/apache/cayenne/tools/dbimport_with_project.gradle @@ -0,0 +1,40 @@ +/***************************************************************** + * 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. + ****************************************************************/ + +plugins { + id 'org.apache.cayenne' +} + +cdbimport { + map 'datamap.map.xml' + cayenneProject 'cayenne-project.xml' + + adapter 'org.apache.cayenne.dba.derby.DerbyAdapter' + + dataSource { + username 'sa' + password '' + url dbUrl + driver 'org.apache.derby.jdbc.EmbeddedDriver' + } + + dbImport { + excludeTable 'PAINTING1' + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/cayenne-gradle-plugin/src/test/resources/org/apache/cayenne/tools/test_project_db.sql ---------------------------------------------------------------------- diff --git a/cayenne-gradle-plugin/src/test/resources/org/apache/cayenne/tools/test_project_db.sql b/cayenne-gradle-plugin/src/test/resources/org/apache/cayenne/tools/test_project_db.sql new file mode 100644 index 0000000..82c8d79 --- /dev/null +++ b/cayenne-gradle-plugin/src/test/resources/org/apache/cayenne/tools/test_project_db.sql @@ -0,0 +1,53 @@ +-- 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. + +-- Test Schema for dbimport task test + +CREATE TABLE ARTIST (ARTIST_ID BIGINT NOT NULL, ARTIST_NAME CHAR (254) NOT NULL, DATE_OF_BIRTH DATE , PRIMARY KEY (ARTIST_ID)); +CREATE TABLE NULL_TEST (ID INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR (100), PRIMARY KEY (ID)); +CREATE TABLE ARTIST_CT (ARTIST_ID INTEGER NOT NULL, ARTIST_NAME CHAR (254) NOT NULL, DATE_OF_BIRTH DATE , PRIMARY KEY (ARTIST_ID)); +CREATE TABLE GENERATED_COLUMN (GENERATED_COLUMN INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY, NAME VARCHAR (250), PRIMARY KEY (GENERATED_COLUMN)); +CREATE TABLE GALLERY (GALLERY_ID INTEGER NOT NULL, GALLERY_NAME VARCHAR (100) NOT NULL, PRIMARY KEY (GALLERY_ID)); +CREATE TABLE PAINTING1 (ARTIST_ID BIGINT , ESTIMATED_PRICE DECIMAL (10, 2), GALLERY_ID INTEGER , PAINTING_ID INTEGER NOT NULL, PAINTING_TITLE VARCHAR (255) NOT NULL, PRIMARY KEY (PAINTING_ID)); +CREATE TABLE ARTGROUP (GROUP_ID INTEGER NOT NULL, NAME VARCHAR (100) NOT NULL, PARENT_GROUP_ID INTEGER , PRIMARY KEY (GROUP_ID)); +CREATE TABLE EXHIBIT (CLOSING_DATE TIMESTAMP NOT NULL, EXHIBIT_ID INTEGER NOT NULL, GALLERY_ID INTEGER NOT NULL, OPENING_DATE TIMESTAMP NOT NULL, PRIMARY KEY (EXHIBIT_ID)); +CREATE TABLE ARTIST_GROUP (ARTIST_ID BIGINT NOT NULL, GROUP_ID INTEGER NOT NULL, PRIMARY KEY (ARTIST_ID, GROUP_ID)); +CREATE TABLE PAINTING (ARTIST_ID BIGINT , ESTIMATED_PRICE DECIMAL (10, 2), GALLERY_ID INTEGER , PAINTING_DESCRIPTION VARCHAR (255), PAINTING_ID INTEGER NOT NULL, PAINTING_TITLE VARCHAR (255) NOT NULL, PRIMARY KEY (PAINTING_ID)); +CREATE TABLE ARTIST_EXHIBIT (ARTIST_ID BIGINT NOT NULL, EXHIBIT_ID INTEGER NOT NULL, PRIMARY KEY (ARTIST_ID, EXHIBIT_ID)); +CREATE TABLE PAINTING_INFO (IMAGE_BLOB LONG VARCHAR FOR BIT DATA , PAINTING_ID INTEGER NOT NULL, TEXT_REVIEW LONG VARCHAR , PRIMARY KEY (PAINTING_ID)); + +ALTER TABLE PAINTING1 ADD FOREIGN KEY (ARTIST_ID) REFERENCES ARTIST (ARTIST_ID); +ALTER TABLE ARTGROUP ADD FOREIGN KEY (PARENT_GROUP_ID) REFERENCES ARTGROUP (GROUP_ID); +ALTER TABLE EXHIBIT ADD FOREIGN KEY (GALLERY_ID) REFERENCES GALLERY (GALLERY_ID); +ALTER TABLE ARTIST_GROUP ADD FOREIGN KEY (ARTIST_ID) REFERENCES ARTIST (ARTIST_ID); +ALTER TABLE ARTIST_GROUP ADD FOREIGN KEY (GROUP_ID) REFERENCES ARTGROUP (GROUP_ID); +ALTER TABLE PAINTING ADD FOREIGN KEY (ARTIST_ID) REFERENCES ARTIST (ARTIST_ID); +ALTER TABLE PAINTING ADD FOREIGN KEY (GALLERY_ID) REFERENCES GALLERY (GALLERY_ID); +ALTER TABLE ARTIST_EXHIBIT ADD FOREIGN KEY (ARTIST_ID) REFERENCES ARTIST (ARTIST_ID); +ALTER TABLE ARTIST_EXHIBIT ADD FOREIGN KEY (EXHIBIT_ID) REFERENCES EXHIBIT (EXHIBIT_ID); +ALTER TABLE PAINTING_INFO ADD FOREIGN KEY (PAINTING_ID) REFERENCES PAINTING (PAINTING_ID); + +CREATE SEQUENCE PK_ARTGROUP AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; +CREATE SEQUENCE PK_ARTIST AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; +CREATE SEQUENCE PK_ARTIST_CT AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; +CREATE SEQUENCE PK_ARTIST_GROUP AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; +CREATE SEQUENCE PK_EXHIBIT AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; +CREATE SEQUENCE PK_GALLERY AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; +CREATE SEQUENCE PK_GENERATED_COLUMN AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; +CREATE SEQUENCE PK_NULL_TEST AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; +CREATE SEQUENCE PK_PAINTING AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; +CREATE SEQUENCE PK_PAINTING1 AS BIGINT START WITH 200 INCREMENT BY 20 NO MAXVALUE NO CYCLE; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/docs/doc/src/main/resources/RELEASE-NOTES.txt ---------------------------------------------------------------------- diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt index 5fba36f..5b61a78 100644 --- a/docs/doc/src/main/resources/RELEASE-NOTES.txt +++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt @@ -18,6 +18,7 @@ CAY-2377 Cleanup deprecated code. CAY-2391 cdbimport: add option to skip user-defined relationships CAY-2393 Add sqlserver-docker profile to automate tests on SQLServer CAY-2394 Upgrade to Apache Velocity 2.0 +CAY-2395 cdbimport: add option to create project file CAY-2396 Upgrade maven-assembly-plugin to 3.1.0 Bug Fixes: http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/maven-plugins/cayenne-maven-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java ---------------------------------------------------------------------- diff --git a/maven-plugins/cayenne-maven-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java b/maven-plugins/cayenne-maven-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java index ab04c83..e6422c5 100644 --- a/maven-plugins/cayenne-maven-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java +++ b/maven-plugins/cayenne-maven-plugin/src/main/java/org/apache/cayenne/tools/DbImporterMojo.java @@ -71,6 +71,14 @@ public class DbImporterMojo extends AbstractMojo { private File map; /** + * Project XML file to use. If set cayenneProject will be created or updated after DB importing. + * This is optional parameter. + * @since 4.1 + */ + @Parameter + private File cayenneProject; + + /** * An object that contains reverse engineering rules. */ @Parameter(name = "dbimport", property = "dbimport", alias = "dbImport") @@ -129,6 +137,7 @@ public class DbImporterMojo extends AbstractMojo { config.setStripFromTableNames(dbImportConfig.getStripFromTableNames()); config.setTableTypes(dbImportConfig.getTableTypes()); config.setTargetDataMap(map); + config.setCayenneProject(cayenneProject); config.setUrl(dataSource.getUrl()); config.setUsername(dataSource.getUsername()); config.setUsePrimitives(dbImportConfig.isUsePrimitives()); http://git-wip-us.apache.org/repos/asf/cayenne/blob/c4b11004/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java ---------------------------------------------------------------------- diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java index 5e60bc4..b98313f 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/db/load/ModelerDbImportAction.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.modeler.dialog.db.load; +import org.apache.cayenne.configuration.DataChannelDescriptorLoader; import org.apache.cayenne.configuration.DataMapLoader; import org.apache.cayenne.configuration.server.DataSourceFactory; import org.apache.cayenne.configuration.server.DbAdapterFactory; @@ -41,8 +42,9 @@ public class ModelerDbImportAction extends DefaultDbImportAction { @Inject DataSourceFactory dataSourceFactory, @Inject DbAdapterFactory adapterFactory, @Inject DataMapLoader mapLoader, - @Inject MergerTokenFactoryProvider mergerTokenFactoryProvider) { - super(logger, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider); + @Inject MergerTokenFactoryProvider mergerTokenFactoryProvider, + @Inject DataChannelDescriptorLoader dataChannelDescriptorLoader) { + super(logger, projectSaver, dataSourceFactory, adapterFactory, mapLoader, mergerTokenFactoryProvider, dataChannelDescriptorLoader); } @Override
