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 dc096d569f994a408ae469ccc2ae85d4d2f089c1 Author: Andrus Adamchik <[email protected]> AuthorDate: Sat May 23 17:08:59 2026 -0400 CAY-2949 CayenneModeler MCP: dbimport_run tool preliminary refactoring - sharing preferences between MCP and the Modeler * cleanup * tests --- .../apache/cayenne/modeler/pref/PrefsAdapter.java | 8 ++ ...BConnectorPrefs.java => DBConnectorsPrefs.java} | 4 +- .../modeler/pref/dbconnector/DBConnector.java | 5 +- .../modeler/pref/dbconnector/DBConnectors.java | 4 +- .../cayenne/modeler/pref/PrefsLocatorTest.java | 98 ++++++++++++++ .../modeler/pref/adapters/ClasspathPrefsTest.java | 98 ++++++++++++++ .../pref/adapters/DBConnectorsPrefsTest.java | 142 +++++++++++++++++++++ .../modeler/pref/dbconnector/DBConnectorTest.java | 118 +++++++++++++++++ .../modeler/pref/dbconnector/DBConnectorsTest.java | 138 ++++++++++++++++++++ .../org/apache/cayenne/modeler/Application.java | 4 +- .../modeler/pref/adapters/DataMapPrefs.java | 26 +--- 11 files changed, 617 insertions(+), 28 deletions(-) diff --git a/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/PrefsAdapter.java b/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/PrefsAdapter.java index 3fb1d3952..e4f3f55f1 100644 --- a/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/PrefsAdapter.java +++ b/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/PrefsAdapter.java @@ -31,4 +31,12 @@ public abstract class PrefsAdapter { protected PrefsAdapter(Preferences prefs) { this.prefs = prefs; } + + protected void putOrRemove(String key, String value) { + if (value == null) { + prefs.remove(key); + } else { + prefs.put(key, value); + } + } } diff --git a/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/adapters/DBConnectorPrefs.java b/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/adapters/DBConnectorsPrefs.java similarity index 97% rename from modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/adapters/DBConnectorPrefs.java rename to modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/adapters/DBConnectorsPrefs.java index 21c42bb34..d66eb52d5 100644 --- a/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/adapters/DBConnectorPrefs.java +++ b/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/adapters/DBConnectorsPrefs.java @@ -31,7 +31,7 @@ import java.util.prefs.Preferences; * Loads {@link DBConnectors} from the user prefs and binds the registry's * change events to the prefs node, so registry mutations are mirrored to disk. */ -public class DBConnectorPrefs extends PrefsAdapter { +public class DBConnectorsPrefs extends PrefsAdapter { public static final String NODE = "dbConnectors"; @@ -41,7 +41,7 @@ public class DBConnectorPrefs extends PrefsAdapter { private static final String URL_PROPERTY = "url"; private static final String USER_NAME_PROPERTY = "userName"; - public DBConnectorPrefs(Preferences prefs) { + public DBConnectorsPrefs(Preferences prefs) { super(prefs); } diff --git a/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnector.java b/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnector.java index b5db35869..b11b0eab7 100644 --- a/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnector.java +++ b/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnector.java @@ -30,7 +30,6 @@ import org.apache.cayenne.util.Util; */ public class DBConnector { - private static final String EMPTY_STRING = ""; public static final String DB_ADAPTER_PROPERTY = "dbAdapter"; public static final String JDBC_DRIVER_PROPERTY = "jdbcDriver"; public static final String PASSWORD_PROPERTY = "password"; @@ -60,7 +59,7 @@ public class DBConnector { } public String getPassword() { - return password == null ? EMPTY_STRING : password; + return password; } public void setPassword(final String password) { @@ -76,7 +75,7 @@ public class DBConnector { } public String getUserName() { - return userName == null ? EMPTY_STRING : userName; + return userName; } public void setUserName(final String userName) { diff --git a/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectors.java b/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectors.java index b694df69b..68b7bb602 100644 --- a/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectors.java +++ b/modeler/cayenne-modeler-prefs/src/main/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectors.java @@ -19,7 +19,7 @@ package org.apache.cayenne.modeler.pref.dbconnector; -import org.apache.cayenne.modeler.pref.adapters.DBConnectorPrefs; +import org.apache.cayenne.modeler.pref.adapters.DBConnectorsPrefs; import java.util.ArrayList; import java.util.Collections; @@ -29,7 +29,7 @@ import java.util.Map; /** * App-wide registry of named local DB connection profiles. Source of truth at runtime; - * fires events on add/remove so listeners (e.g. {@link DBConnectorPrefs}) can mirror + * fires events on add/remove so listeners (e.g. {@link DBConnectorsPrefs}) can mirror * state to persistent storage. */ public class DBConnectors { diff --git a/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/PrefsLocatorTest.java b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/PrefsLocatorTest.java new file mode 100644 index 000000000..91588bc9e --- /dev/null +++ b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/PrefsLocatorTest.java @@ -0,0 +1,98 @@ +/***************************************************************** + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + +package org.apache.cayenne.modeler.pref; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.UUID; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PrefsLocatorTest { + + private final Preferences root = Preferences.userRoot() + .node("cayenne-test/" + UUID.randomUUID().toString().replace("-", "")); + private final PrefsLocator locator = new PrefsLocator(root); + + @AfterEach + public void cleanup() throws BackingStoreException { + root.removeNode(); + } + + @Test + public void modelerRootIsUnderAllRoot() { + assertTrue(locator.modelerRoot().absolutePath().endsWith("/v5")); + } + + @Test + public void appNodeNullReturnsAppRoot() { + assertTrue(locator.appNode(null).absolutePath().endsWith("/v5/app")); + } + + @Test + public void appNodeEmptyReturnsAppRoot() { + assertTrue(locator.appNode("").absolutePath().endsWith("/v5/app")); + } + + @Test + public void appNodeDescendsIntoSubtree() { + assertTrue(locator.appNode("foo/bar").absolutePath().endsWith("/v5/app/foo/bar")); + } + + @Test + public void projectNodeIncludesId() { + assertTrue(locator.projectNode("abc123").absolutePath().endsWith("/v5/project/abc123")); + } + + @Test + public void dataMapNodeIncludesId() { + assertTrue(locator.dataMapNode("map42").absolutePath().endsWith("/v5/datamap/map42")); + } + + @Test + public void handshakeNodeExistsFalseByDefault() { + assertFalse(locator.handshakeNodeExists("nonce-x")); + } + + @Test + public void handshakeNodeExistsTrueAfterWrite() throws BackingStoreException { + locator.handshakeNode("nonce-y").put("pid", "123"); + root.flush(); + + assertTrue(locator.handshakeNodeExists("nonce-y")); + } + + @Test + public void handshakeRootNodeExistsFalseByDefault() { + assertFalse(locator.handshakeRootNodeExists()); + } + + @Test + public void handshakeRootNodeExistsTrueAfterWrite() throws BackingStoreException { + locator.handshakeNode("any-nonce").put("pid", "1"); + root.flush(); + + assertTrue(locator.handshakeRootNodeExists()); + } +} diff --git a/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/adapters/ClasspathPrefsTest.java b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/adapters/ClasspathPrefsTest.java new file mode 100644 index 000000000..b9a80f15d --- /dev/null +++ b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/adapters/ClasspathPrefsTest.java @@ -0,0 +1,98 @@ +/***************************************************************** + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + +package org.apache.cayenne.modeler.pref.adapters; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ClasspathPrefsTest { + + private final Preferences root = Preferences.userRoot() + .node("cayenne-test/" + UUID.randomUUID().toString().replace("-", "")); + private final ClasspathPrefs prefs = new ClasspathPrefs(root); + + @AfterEach + public void cleanup() throws BackingStoreException { + root.removeNode(); + } + + @Test + public void emptyPrefsReturnsEmptyList() { + assertTrue(prefs.getEntries().isEmpty()); + } + + @Test + public void singleEntryRoundtrip() { + prefs.setEntries(List.of("/lib/foo.jar")); + assertEquals(List.of("/lib/foo.jar"), prefs.getEntries()); + } + + @Test + public void multipleEntriesPreserveOrder() { + List<String> entries = List.of("/a.jar", "/b.jar", "/c.jar"); + prefs.setEntries(entries); + assertEquals(entries, prefs.getEntries()); + } + + @Test + public void setEntriesOverwritesClearsPreviousEntries() { + prefs.setEntries(List.of("/old.jar")); + prefs.setEntries(List.of("/new.jar")); + assertEquals(List.of("/new.jar"), prefs.getEntries()); + } + + @Test + public void numericKeysAreOrderedNumerically() throws BackingStoreException { + // Write keys manually in a non-lexicographic order: 10 before 2 + root.put("1", "/first.jar"); + root.put("10", "/tenth.jar"); + root.put("2", "/second.jar"); + root.flush(); + + // Numeric sort: 1 < 2 < 10, not lexicographic "1" < "10" < "2" + assertEquals(List.of("/first.jar", "/second.jar", "/tenth.jar"), prefs.getEntries()); + } + + @Test + public void nonNumericKeysAreIgnoredOnRead() throws BackingStoreException { + root.put("foo", "/should-be-ignored.jar"); + root.put("1", "/valid.jar"); + root.flush(); + + assertEquals(List.of("/valid.jar"), prefs.getEntries()); + } + + @Test + public void emptyValuesAreSkippedOnRead() throws BackingStoreException { + root.put("1", ""); + root.put("2", "/valid.jar"); + root.flush(); + + assertEquals(List.of("/valid.jar"), prefs.getEntries()); + } +} diff --git a/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/adapters/DBConnectorsPrefsTest.java b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/adapters/DBConnectorsPrefsTest.java new file mode 100644 index 000000000..557d687dd --- /dev/null +++ b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/adapters/DBConnectorsPrefsTest.java @@ -0,0 +1,142 @@ +/***************************************************************** + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + +package org.apache.cayenne.modeler.pref.adapters; + +import org.apache.cayenne.modeler.pref.dbconnector.DBConnector; +import org.apache.cayenne.modeler.pref.dbconnector.DBConnectors; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class DBConnectorsPrefsTest { + + private final Preferences root = Preferences.userRoot() + .node("cayenne-test/" + UUID.randomUUID().toString().replace("-", "")); + private final DBConnectorsPrefs prefs = new DBConnectorsPrefs(root); + + @AfterEach + public void cleanup() throws BackingStoreException { + root.removeNode(); + } + + @Test + public void emptyPrefsReturnsEmptyRegistry() { + assertTrue(prefs.getConnectors().getAll().isEmpty()); + } + + @Test + public void loadsConnectorFromPrefsNode() throws BackingStoreException { + Preferences n = root.node("myconn"); + n.put("url", "jdbc:h2:mem"); + n.put("userName", "sa"); + n.put("password", "secret"); + n.put("jdbcDriver", "org.h2.Driver"); + n.put("dbAdapter", "org.apache.cayenne.dba.h2.H2Adapter"); + root.flush(); + + DBConnectors registry = prefs.getConnectors(); + DBConnector loaded = registry.get("myconn"); + + assertEquals("jdbc:h2:mem", loaded.getUrl()); + assertEquals("sa", loaded.getUserName()); + assertEquals("secret", loaded.getPassword()); + assertEquals("org.h2.Driver", loaded.getJdbcDriver()); + assertEquals("org.apache.cayenne.dba.h2.H2Adapter", loaded.getDbAdapter()); + } + + @Test + public void loadsMultipleConnectors() throws BackingStoreException { + root.node("conn1").put("url", "jdbc:h2:mem:db1"); + root.node("conn2").put("url", "jdbc:h2:mem:db2"); + root.flush(); + + DBConnectors registry = prefs.getConnectors(); + assertEquals(2, registry.getAll().size()); + assertEquals("jdbc:h2:mem:db1", registry.get("conn1").getUrl()); + assertEquals("jdbc:h2:mem:db2", registry.get("conn2").getUrl()); + } + + @Test + public void missingPropertiesLoadAsNull() throws BackingStoreException { + root.node("sparse").put("url", "jdbc:h2:mem"); + root.flush(); + + DBConnector loaded = prefs.getConnectors().get("sparse"); + assertEquals("jdbc:h2:mem", loaded.getUrl()); + assertNull(loaded.getUserName()); + assertNull(loaded.getPassword()); + assertNull(loaded.getJdbcDriver()); + assertNull(loaded.getDbAdapter()); + } + + @Test + public void putMutationWritesToPrefs() throws BackingStoreException { + DBConnectors registry = prefs.getConnectors(); + + DBConnector connector = new DBConnector(); + connector.setUrl("jdbc:pg://localhost/mydb"); + connector.setUserName("admin"); + registry.put("pgconn", connector); + + Preferences stored = root.node("pgconn"); + assertEquals("jdbc:pg://localhost/mydb", stored.get("url", null)); + assertEquals("admin", stored.get("userName", null)); + } + + @Test + public void removeMutationDeletesPrefsNode() throws BackingStoreException { + root.node("toremove").put("url", "jdbc:h2:mem"); + root.flush(); + + DBConnectors registry = prefs.getConnectors(); + registry.remove("toremove"); + + assertFalse(root.nodeExists("toremove")); + } + + @Test + public void initialLoadDoesNotFireWriteback() throws BackingStoreException { + // Write one connector to prefs before loading + root.node("existing").put("url", "jdbc:h2:mem"); + root.flush(); + + List<String> writebackNames = new ArrayList<>(); + DBConnectors registry = prefs.getConnectors(); + + // Attach a spy listener after loading to detect any late writeback + registry.addListener(new DBConnectors.Listener() { + public void connectionUpdated(String name) { writebackNames.add(name); } + public void connectionRemoved(String name) { writebackNames.add(name); } + }); + + // No mutations occurred — writeback list must be empty + assertTrue(writebackNames.isEmpty()); + } +} diff --git a/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectorTest.java b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectorTest.java new file mode 100644 index 000000000..c9211db99 --- /dev/null +++ b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectorTest.java @@ -0,0 +1,118 @@ +/***************************************************************** + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + +package org.apache.cayenne.modeler.pref.dbconnector; + +import org.apache.cayenne.configuration.DataSourceDescriptor; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class DBConnectorTest { + + @Test + public void copyToConnectorReturnsFalseWhenIdentical() { + DBConnector src = connector("jdbc:h2:mem", "sa", "pw", "org.h2.Driver", "org.h2.H2Adapter"); + DBConnector dst = connector("jdbc:h2:mem", "sa", "pw", "org.h2.Driver", "org.h2.H2Adapter"); + + assertFalse(src.copyTo(dst)); + } + + @Test + public void copyToConnectorReturnsTrueOnChange() { + DBConnector src = connector("jdbc:h2:new", "sa", "pw", "org.h2.Driver", "org.h2.H2Adapter"); + DBConnector dst = connector("jdbc:h2:old", "sa", "pw", "org.h2.Driver", "org.h2.H2Adapter"); + + assertTrue(src.copyTo(dst)); + assertEquals("jdbc:h2:new", dst.getUrl()); + } + + @Test + public void copyToConnectorCopiesAllFiveFields() { + DBConnector src = connector("url", "user", "pass", "driver", "adapter"); + DBConnector dst = new DBConnector(); + + src.copyTo(dst); + + assertEquals("url", dst.getUrl()); + assertEquals("user", dst.getUserName()); + assertEquals("pass", dst.getPassword()); + assertEquals("driver", dst.getJdbcDriver()); + assertEquals("adapter", dst.getDbAdapter()); + } + + @Test + public void copyToDataSourceDescriptorCopiesDriverUrlUserPassword() { + DBConnector src = connector("jdbc:pg://db", "admin", "secret", "org.postgresql.Driver", "PgAdapter"); + DataSourceDescriptor dst = new DataSourceDescriptor(); + + src.copyTo(dst); + + assertEquals("jdbc:pg://db", dst.getDataSourceUrl()); + assertEquals("admin", dst.getUserName()); + assertEquals("secret", dst.getPassword()); + assertEquals("org.postgresql.Driver", dst.getJdbcDriver()); + } + + @Test + public void copyToDataSourceDescriptorDoesNotCopyDbAdapter() { + // All 4 copyable fields already match; only dbAdapter differs. + // copyTo must return false — dbAdapter is not part of the DataSourceDescriptor copy. + DBConnector src = connector("url", "user", "pass", "driver", "SomeAdapter"); + DataSourceDescriptor dst = new DataSourceDescriptor(); + dst.setDataSourceUrl("url"); + dst.setUserName("user"); + dst.setPassword("pass"); + dst.setJdbcDriver("driver"); + + assertFalse(src.copyTo(dst)); + } + + @Test + public void copyToConnectorHandlesNullFields() { + DBConnector src = new DBConnector(); + DBConnector dst = connector("url", "user", "pass", "driver", "adapter"); + + assertTrue(src.copyTo(dst)); + assertNull(dst.getUrl()); + assertNull(dst.getUserName()); + assertNull(dst.getPassword()); + assertNull(dst.getJdbcDriver()); + assertNull(dst.getDbAdapter()); + } + + private static DBConnector connector( + String url, + String userName, + String password, + String jdbcDriver, + String dbAdapter) { + + DBConnector c = new DBConnector(); + c.setUrl(url); + c.setUserName(userName); + c.setPassword(password); + c.setJdbcDriver(jdbcDriver); + c.setDbAdapter(dbAdapter); + return c; + } +} diff --git a/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectorsTest.java b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectorsTest.java new file mode 100644 index 000000000..b6c2191bc --- /dev/null +++ b/modeler/cayenne-modeler-prefs/src/test/java/org/apache/cayenne/modeler/pref/dbconnector/DBConnectorsTest.java @@ -0,0 +1,138 @@ +/***************************************************************** + * 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 + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + ****************************************************************/ + +package org.apache.cayenne.modeler.pref.dbconnector; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class DBConnectorsTest { + + @Test + public void putAddsConnectorAndFiresListener() { + DBConnectors registry = new DBConnectors(); + List<String> updated = new ArrayList<>(); + registry.addListener(new DBConnectors.Listener() { + public void connectionUpdated(String name) { updated.add(name); } + public void connectionRemoved(String name) { } + }); + + registry.put("main", new DBConnector()); + + assertEquals(List.of("main"), updated); + assertTrue(registry.getAll().containsKey("main")); + } + + @Test + public void putOnExistingNameFiresUpdateListener() { + DBConnectors registry = new DBConnectors(); + registry.put("main", new DBConnector()); + + List<String> updated = new ArrayList<>(); + registry.addListener(new DBConnectors.Listener() { + public void connectionUpdated(String name) { updated.add(name); } + public void connectionRemoved(String name) { } + }); + + registry.put("main", new DBConnector()); + + assertEquals(List.of("main"), updated); + } + + @Test + public void removeExistingFiresListener() { + DBConnectors registry = new DBConnectors(); + registry.put("main", new DBConnector()); + + List<String> removed = new ArrayList<>(); + registry.addListener(new DBConnectors.Listener() { + public void connectionUpdated(String name) { } + public void connectionRemoved(String name) { removed.add(name); } + }); + + registry.remove("main"); + + assertEquals(List.of("main"), removed); + assertFalse(registry.getAll().containsKey("main")); + } + + @Test + public void removeNonExistentDoesNotFireListener() { + DBConnectors registry = new DBConnectors(); + List<String> removed = new ArrayList<>(); + registry.addListener(new DBConnectors.Listener() { + public void connectionUpdated(String name) { } + public void connectionRemoved(String name) { removed.add(name); } + }); + + registry.remove("nonexistent"); + + assertTrue(removed.isEmpty()); + } + + @Test + public void getAllReturnsUnmodifiableMap() { + DBConnectors registry = new DBConnectors(); + registry.put("main", new DBConnector()); + + Map<String, DBConnector> all = registry.getAll(); + + assertThrows(UnsupportedOperationException.class, () -> all.put("other", new DBConnector())); + } + + @Test + public void getAllPreservesInsertionOrder() { + DBConnectors registry = new DBConnectors(); + registry.put("alpha", new DBConnector()); + registry.put("beta", new DBConnector()); + registry.put("gamma", new DBConnector()); + + List<String> keys = new ArrayList<>(registry.getAll().keySet()); + assertEquals(List.of("alpha", "beta", "gamma"), keys); + } + + @Test + public void multipleListenersAllNotified() { + DBConnectors registry = new DBConnectors(); + List<String> calls1 = new ArrayList<>(); + List<String> calls2 = new ArrayList<>(); + + registry.addListener(new DBConnectors.Listener() { + public void connectionUpdated(String name) { calls1.add(name); } + public void connectionRemoved(String name) { } + }); + registry.addListener(new DBConnectors.Listener() { + public void connectionUpdated(String name) { calls2.add(name); } + public void connectionRemoved(String name) { } + }); + + registry.put("conn", new DBConnector()); + + assertEquals(List.of("conn"), calls1); + assertEquals(List.of("conn"), calls2); + } +} diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/Application.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/Application.java index 8311aee3b..d7542a0d6 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/Application.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/Application.java @@ -33,7 +33,7 @@ import org.apache.cayenne.modeler.log.ModelerLogFactory; import org.apache.cayenne.modeler.platform.UIInitializer; import org.apache.cayenne.modeler.toolkit.filechooser.FileChooserFactory; import org.apache.cayenne.modeler.pref.adapters.ClasspathPrefs; -import org.apache.cayenne.modeler.pref.adapters.DBConnectorPrefs; +import org.apache.cayenne.modeler.pref.adapters.DBConnectorsPrefs; import org.apache.cayenne.modeler.pref.adapters.GeneralPrefs; import org.apache.cayenne.modeler.pref.PrefsLocator; import org.apache.cayenne.modeler.pref.PrefsManager; @@ -190,7 +190,7 @@ public class Application { getPrefsManager().runMigrations(); - this.dbConnectors = new DBConnectorPrefs(prefsLocator.appNode(DBConnectorPrefs.NODE)).getConnectors(); + this.dbConnectors = new DBConnectorsPrefs(prefsLocator.appNode(DBConnectorsPrefs.NODE)).getConnectors(); refreshClassLoader(); diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/adapters/DataMapPrefs.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/adapters/DataMapPrefs.java index 31da2ff49..4c2c87071 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/adapters/DataMapPrefs.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/pref/adapters/DataMapPrefs.java @@ -53,12 +53,7 @@ public final class DataMapPrefs extends PrefsAdapter { } public void setSuperclassPackage(String superclassPackage) { - if (prefs != null) { - if (superclassPackage == null) { - superclassPackage = ""; - } - prefs.put(SUPERCLASS_PACKAGE_PROPERTY, superclassPackage); - } + putOrRemove(SUPERCLASS_PACKAGE_PROPERTY, superclassPackage); } /** @@ -66,7 +61,7 @@ public final class DataMapPrefs extends PrefsAdapter { * connection has been configured (URL not set). */ public DBConnector getConnector() { - if (prefs == null || prefs.get(DBConnector.URL_PROPERTY, null) == null) { + if (prefs.get(DBConnector.URL_PROPERTY, null) == null) { return null; } DBConnector connector = new DBConnector(); @@ -79,18 +74,11 @@ public final class DataMapPrefs extends PrefsAdapter { } public void setConnector(DBConnector connector) { - if (prefs == null) { - return; - } - if (connector.getDbAdapter() != null) { - prefs.put(DBConnector.DB_ADAPTER_PROPERTY, connector.getDbAdapter()); - } else { - prefs.remove(DBConnector.DB_ADAPTER_PROPERTY); - } - prefs.put(DBConnector.URL_PROPERTY, connector.getUrl()); - prefs.put(DBConnector.USER_NAME_PROPERTY, connector.getUserName()); - prefs.put(DBConnector.PASSWORD_PROPERTY, connector.getPassword()); - prefs.put(DBConnector.JDBC_DRIVER_PROPERTY, connector.getJdbcDriver()); + putOrRemove(DBConnector.DB_ADAPTER_PROPERTY, connector.getDbAdapter()); + putOrRemove(DBConnector.URL_PROPERTY, connector.getUrl()); + putOrRemove(DBConnector.USER_NAME_PROPERTY, connector.getUserName()); + putOrRemove(DBConnector.PASSWORD_PROPERTY, connector.getPassword()); + putOrRemove(DBConnector.JDBC_DRIVER_PROPERTY, connector.getJdbcDriver()); } public boolean hasDbAdapter() {
