1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.tomcat.dbcp.dbcp2;
18
19 import java.io.PrintWriter;
20 import java.sql.Connection;
21 import java.sql.SQLException;
22 import java.sql.SQLFeatureNotSupportedException;
23 import java.util.NoSuchElementException;
24 import java.util.Objects;
25 import java.util.logging.Logger;
26
27 import javax.sql.DataSource;
28
29 import org.apache.juli.logging.Log;
30 import org.apache.juli.logging.LogFactory;
31 import org.apache.tomcat.dbcp.pool2.ObjectPool;
32 import org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool;
33
34 /**
35  * A simple {@link DataSource} implementation that obtains {@link Connection}s from the specified {@link ObjectPool}.
36  *
37  * @param <C>
38  *            The connection type
39  *
40  * @since 2.0
41  */

42 public class PoolingDataSource<C extends Connection> implements DataSource, AutoCloseable {
43
44     private static final Log log = LogFactory.getLog(PoolingDataSource.class);
45
46     /** Controls access to the underlying connection */
47     private boolean accessToUnderlyingConnectionAllowed;
48
49     /**
50      * Constructs a new instance backed by the given connection pool.
51      *
52      * @param pool
53      *            the given connection pool.
54      */

55     public PoolingDataSource(final ObjectPool<C> pool) {
56         Objects.requireNonNull(pool, "Pool must not be null.");
57         this.pool = pool;
58         // Verify that pool's factory refers back to it. If not, log a warning and try to fix.
59         if (this.pool instanceof GenericObjectPool<?>) {
60             final PoolableConnectionFactory pcf = (PoolableConnectionFactory) ((GenericObjectPool<?>) this.pool)
61                     .getFactory();
62             Objects.requireNonNull(pcf, "PoolableConnectionFactory must not be null.");
63             if (pcf.getPool() != this.pool) {
64                 log.warn(Utils.getMessage("poolingDataSource.factoryConfig"));
65                 @SuppressWarnings("unchecked"// PCF must have a pool of PCs
66                 final ObjectPool<PoolableConnection> p = (ObjectPool<PoolableConnection>) this.pool;
67                 pcf.setPool(p);
68             }
69         }
70     }
71
72     /**
73      * Closes and free all {@link Connection}s from the pool.
74      *
75      * @since 2.1
76      */

77     @Override
78     public void close() throws RuntimeException, SQLException {
79         try {
80             pool.close();
81         } catch (final RuntimeException rte) {
82             throw new RuntimeException(Utils.getMessage("pool.close.fail"), rte);
83         } catch (final Exception e) {
84             throw new SQLException(Utils.getMessage("pool.close.fail"), e);
85         }
86     }
87
88     /**
89      * Returns the value of the accessToUnderlyingConnectionAllowed property.
90      *
91      * @return true if access to the underlying {@link Connection} is allowed, false otherwise.
92      */

93     public boolean isAccessToUnderlyingConnectionAllowed() {
94         return this.accessToUnderlyingConnectionAllowed;
95     }
96
97     /**
98      * Sets the value of the accessToUnderlyingConnectionAllowed property. It controls if the PoolGuard allows access to
99      * the underlying connection. (Default: false)
100      *
101      * @param allow
102      *            Access to the underlying connection is granted when true.
103      */

104     public void setAccessToUnderlyingConnectionAllowed(final boolean allow) {
105         this.accessToUnderlyingConnectionAllowed = allow;
106     }
107
108     /* JDBC_4_ANT_KEY_BEGIN */
109     @Override
110     public boolean isWrapperFor(final Class<?> iface) throws SQLException {
111         return false;
112     }
113
114     @Override
115     public <T> T unwrap(final Class<T> iface) throws SQLException {
116         throw new SQLException("PoolingDataSource is not a wrapper.");
117     }
118     /* JDBC_4_ANT_KEY_END */
119
120     @Override
121     public Logger getParentLogger() throws SQLFeatureNotSupportedException {
122         throw new SQLFeatureNotSupportedException();
123     }
124
125     // --- DataSource methods -----------------------------------------
126
127     /**
128      * Returns a {@link java.sql.Connection} from my pool, according to the contract specified by
129      * {@link ObjectPool#borrowObject}.
130      */

131     @Override
132     public Connection getConnection() throws SQLException {
133         try {
134             final C conn = pool.borrowObject();
135             if (conn == null) {
136                 return null;
137             }
138             return new PoolGuardConnectionWrapper<>(conn);
139         } catch (final SQLException e) {
140             throw e;
141         } catch (final NoSuchElementException e) {
142             throw new SQLException("Cannot get a connection, pool error " + e.getMessage(), e);
143         } catch (final RuntimeException e) {
144             throw e;
145         } catch (final InterruptedException e) {
146             // Reset the interrupt status so it is visible to callers
147             Thread.currentThread().interrupt();
148             throw new SQLException("Cannot get a connection, general error", e);
149         } catch (final Exception e) {
150             throw new SQLException("Cannot get a connection, general error", e);
151         }
152     }
153
154     /**
155      * Throws {@link UnsupportedOperationException}
156      *
157      * @throws UnsupportedOperationException
158      *             always thrown
159      */

160     @Override
161     public Connection getConnection(final String uname, final String passwd) throws SQLException {
162         throw new UnsupportedOperationException();
163     }
164
165     /**
166      * Returns my log writer.
167      *
168      * @return my log writer
169      * @see DataSource#getLogWriter
170      */

171     @Override
172     public PrintWriter getLogWriter() {
173         return logWriter;
174     }
175
176     /**
177      * Throws {@link UnsupportedOperationException}.
178      *
179      * @throws UnsupportedOperationException
180      *             As this implementation does not support this feature.
181      */

182     @Override
183     public int getLoginTimeout() {
184         throw new UnsupportedOperationException("Login timeout is not supported.");
185     }
186
187     /**
188      * Throws {@link UnsupportedOperationException}.
189      *
190      * @throws UnsupportedOperationException
191      *             As this implementation does not support this feature.
192      */

193     @Override
194     public void setLoginTimeout(final int seconds) {
195         throw new UnsupportedOperationException("Login timeout is not supported.");
196     }
197
198     /**
199      * Sets my log writer.
200      *
201      * @see DataSource#setLogWriter
202      */

203     @Override
204     public void setLogWriter(final PrintWriter out) {
205         logWriter = out;
206     }
207
208     /** My log writer. */
209     private PrintWriter logWriter = null;
210
211     private final ObjectPool<C> pool;
212
213     protected ObjectPool<C> getPool() {
214         return pool;
215     }
216
217     /**
218      * PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
219      *
220      * @since 2.0
221      */

222     private class PoolGuardConnectionWrapper<D extends Connection> extends DelegatingConnection<D> {
223
224         PoolGuardConnectionWrapper(final D delegate) {
225             super(delegate);
226         }
227
228         /**
229          * @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getDelegate()
230          */

231         @Override
232         public D getDelegate() {
233             return isAccessToUnderlyingConnectionAllowed() ? super.getDelegate() : null;
234         }
235
236         /**
237          * @see org.apache.tomcat.dbcp.dbcp2.DelegatingConnection#getInnermostDelegate()
238          */

239         @Override
240         public Connection getInnermostDelegate() {
241             return isAccessToUnderlyingConnectionAllowed() ? super.getInnermostDelegate() : null;
242         }
243
244         @Override
245         public void close() throws SQLException {
246             if (getDelegateInternal() != null) {
247                 super.close();
248                 super.setDelegate(null);
249             }
250         }
251
252         @Override
253         public boolean isClosed() throws SQLException {
254             return getDelegateInternal() == null ? true : super.isClosed();
255         }
256     }
257 }
258