Repository: cayenne Updated Branches: refs/heads/master c2ce2dfa6 -> e1a3d9788
CAY-2009 Non-blocking connection pool * unit tests * upgrade notes Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/e1a3d978 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/e1a3d978 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/e1a3d978 Branch: refs/heads/master Commit: e1a3d978891dad12a730dd7bc5cb0c284378abce Parents: c2ce2df Author: aadamchik <[email protected]> Authored: Sun May 3 05:14:19 2015 -0400 Committer: aadamchik <[email protected]> Committed: Sun May 3 06:13:21 2015 -0400 ---------------------------------------------------------------------- .../datasource/ManagedPoolingDataSource.java | 17 +++- .../datasource/PoolingDataSourceManager.java | 25 +++-- .../ManagedPoolingDataSourceTest.java | 97 ++++++++++++++++++++ .../datasource/PoolDataSourceManagerTest.java | 80 ++++++++++++++++ docs/doc/src/main/resources/UPGRADE.txt | 10 ++ 5 files changed, 219 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/e1a3d978/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java b/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java index 65083b1..182ad75 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/datasource/ManagedPoolingDataSource.java @@ -42,20 +42,33 @@ public class ManagedPoolingDataSource implements DataSource, ScopeEventListener public ManagedPoolingDataSource(PoolingDataSource dataSource) { this.dataSource = dataSource; - this.dataSourceManager = new PoolingDataSourceManager(dataSource); + + // wake every 2 minutes... + this.dataSourceManager = new PoolingDataSourceManager(dataSource, 120000); dataSourceManager.start(); } + PoolingDataSourceManager getDataSourceManager() { + return dataSourceManager; + } + + /** + * Calls {@link #shutdown()} to drain the underlying pool, close open + * connections and block the DataSource from creating any new connections. + */ @Override public void beforeScopeEnd() { + shutdown(); + } + public void shutdown() { // swap the underlying DataSource to prevent further interaction with // the callers this.dataSource = new StoppedDataSource(dataSource); // shut down the thread.. - dataSourceManager.shutdown(); + this.dataSourceManager.shutdown(); } @Override http://git-wip-us.apache.org/repos/asf/cayenne/blob/e1a3d978/cayenne-server/src/main/java/org/apache/cayenne/datasource/PoolingDataSourceManager.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/datasource/PoolingDataSourceManager.java b/cayenne-server/src/main/java/org/apache/cayenne/datasource/PoolingDataSourceManager.java index 6dacdd6..3d2f93a 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/datasource/PoolingDataSourceManager.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/datasource/PoolingDataSourceManager.java @@ -19,9 +19,9 @@ package org.apache.cayenne.datasource; /** - * Manages the state of a {@link PoolingDataSource} instance, performing - * periodic expansion/contraction of pooled connections, and orchestrating - * shutdown. + * A thread that manages the state of a {@link PoolingDataSource} instance, + * performing periodic expansion/contraction of pooled connections, and + * orchestrating shutdown. * * @since 4.0 */ @@ -29,28 +29,37 @@ class PoolingDataSourceManager extends Thread { private volatile boolean shouldStop; private PoolingDataSource dataSource; + private long managerWakeTime; - PoolingDataSourceManager(PoolingDataSource dataSource) { - setName("PoolManagerCleanup-" + dataSource.hashCode()); + PoolingDataSourceManager(PoolingDataSource dataSource, long managerWakeTime) { + setName("PoolingDataSourceManager-" + dataSource.hashCode()); setDaemon(true); this.dataSource = dataSource; this.shouldStop = false; + this.managerWakeTime = managerWakeTime; } - public void shutdown() { + void shutdown() { shouldStop = true; dataSource.shutdown(); interrupt(); } + PoolingDataSource getDataSource() { + return dataSource; + } + + boolean isStopped() { + return shouldStop; + } + @Override public void run() { while (true) { try { - // don't do it too often - Thread.sleep(600000); + Thread.sleep(managerWakeTime); } catch (InterruptedException iex) { // ignore... } http://git-wip-us.apache.org/repos/asf/cayenne/blob/e1a3d978/cayenne-server/src/test/java/org/apache/cayenne/datasource/ManagedPoolingDataSourceTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/datasource/ManagedPoolingDataSourceTest.java b/cayenne-server/src/test/java/org/apache/cayenne/datasource/ManagedPoolingDataSourceTest.java new file mode 100644 index 0000000..c728956 --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/datasource/ManagedPoolingDataSourceTest.java @@ -0,0 +1,97 @@ +/***************************************************************** + * 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. + ****************************************************************/ +package org.apache.cayenne.datasource; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ManagedPoolingDataSourceTest { + + private Connection[] mockConnections; + private PoolingDataSource mockPoolingDataSource; + private ManagedPoolingDataSource dataSource; + + @Before + public void before() throws SQLException { + + this.mockConnections = new Connection[4]; + for (int i = 0; i < mockConnections.length; i++) { + mockConnections[i] = mock(Connection.class); + } + + this.mockPoolingDataSource = mock(PoolingDataSource.class); + when(mockPoolingDataSource.getConnection()).thenReturn(mockConnections[0], mockConnections[1], + mockConnections[2], mockConnections[3]); + + this.dataSource = new ManagedPoolingDataSource(mockPoolingDataSource); + } + + @After + public void after() { + dataSource.beforeScopeEnd(); + } + + @Test + public void testGetConnection() throws SQLException { + assertSame(mockConnections[0], dataSource.getConnection()); + assertSame(mockConnections[1], dataSource.getConnection()); + assertSame(mockConnections[2], dataSource.getConnection()); + assertSame(mockConnections[3], dataSource.getConnection()); + } + + @Test + public void testShutdown() throws SQLException, InterruptedException { + assertNotNull(dataSource.getConnection()); + + // state before shutdown + verify(mockPoolingDataSource, times(0)).shutdown(); + assertFalse(dataSource.getDataSourceManager().isStopped()); + assertTrue(dataSource.getDataSourceManager().isAlive()); + + dataSource.shutdown(); + + // state after shutdown + verify(mockPoolingDataSource, times(1)).shutdown(); + assertTrue(dataSource.getDataSourceManager().isStopped()); + + // give the thread some time to process interrupt and die + Thread.sleep(200); + assertFalse(dataSource.getDataSourceManager().isAlive()); + + try { + dataSource.getConnection(); + } catch (SQLException e) { + // expected , DataSource should not give out connections any longer + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/e1a3d978/cayenne-server/src/test/java/org/apache/cayenne/datasource/PoolDataSourceManagerTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/datasource/PoolDataSourceManagerTest.java b/cayenne-server/src/test/java/org/apache/cayenne/datasource/PoolDataSourceManagerTest.java new file mode 100644 index 0000000..58b8983 --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/datasource/PoolDataSourceManagerTest.java @@ -0,0 +1,80 @@ +/***************************************************************** + * 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. + ****************************************************************/ +package org.apache.cayenne.datasource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + +import java.sql.SQLException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class PoolDataSourceManagerTest { + + private PoolingDataSource mockPoolingDataSource; + private PoolingDataSourceManager dataSourceManager; + + @Before + public void before() throws SQLException { + this.mockPoolingDataSource = mock(PoolingDataSource.class); + this.dataSourceManager = new PoolingDataSourceManager(mockPoolingDataSource, 100); + } + + @After + public void after() { + dataSourceManager.shutdown(); + } + + @Test + public void testRun_Manage() throws InterruptedException { + + final int[] counter = new int[1]; + + doAnswer(new Answer<Object>() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + counter[0]++; + return null; + } + }).when(mockPoolingDataSource).managePool(); + + dataSourceManager.start(); + + // we can't predict the number of 'managePool' invocations, but it + // should be incrementing as the time goes + + int c0 = counter[0]; + assertEquals(0, c0); + Thread.sleep(300); + + int c1 = counter[0]; + assertTrue(c1 > c0); + + Thread.sleep(300); + + int c2 = counter[0]; + assertTrue(c2 > c1); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/e1a3d978/docs/doc/src/main/resources/UPGRADE.txt ---------------------------------------------------------------------- diff --git a/docs/doc/src/main/resources/UPGRADE.txt b/docs/doc/src/main/resources/UPGRADE.txt index cc5cda1..c29320d 100644 --- a/docs/doc/src/main/resources/UPGRADE.txt +++ b/docs/doc/src/main/resources/UPGRADE.txt @@ -12,6 +12,16 @@ UPGRADING TO 4.0.M3 Note if you have references to @Deprecated String properties and you run cgen without "createPropertyNames" flag, there will be errors. Reference Jira: CAY-1991 +* Per CAY-2010 DataSource building methods were refactored out of ServerRuntimeBuilder and into a standalone + org.apache.cayenne.datasource.DataSourceBuilder. So creating a DataSource in runtime may look like this: + + DataSource ds = DataSourceBuilder.url(dbUrl).driver(dbDriver).build(); + ServerRuntime runtime = ServerRuntimeBuilder.builder().dataSource(ds).build(); + +* Per CAY-2008, CAY-2009 we got rid of org.apache.cayenne.conn.PoolManager and associated classes that made up a + pooling DataSource. A replacement is non-blocking DataSource under org.apache.cayenne.datasource (PoolingDataSource, ManagedPoolingDataSource), + best assembled using org.apache.cayenne.datasource.DataSourceBuilder. + UPGRADING TO 4.0.M2
