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

16 package com.zaxxer.hikari.pool;
17
18 import com.zaxxer.hikari.util.ConcurrentBag.IConcurrentBagEntry;
19 import com.zaxxer.hikari.util.FastList;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 import java.sql.Connection;
24 import java.sql.SQLException;
25 import java.sql.Statement;
26 import java.util.concurrent.ScheduledFuture;
27 import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
28
29 import static com.zaxxer.hikari.util.ClockSource.*;
30
31 /**
32  * Entry used in the ConcurrentBag to track Connection instances.
33  *
34  * @author Brett Wooldridge
35  */

36 final class PoolEntry implements IConcurrentBagEntry
37 {
38    private static final Logger LOGGER = LoggerFactory.getLogger(PoolEntry.class);
39    private static final AtomicIntegerFieldUpdater<PoolEntry> stateUpdater;
40
41    Connection connection;
42    long lastAccessed;
43    long lastBorrowed;
44
45    @SuppressWarnings("FieldCanBeLocal")
46    private volatile int state = 0;
47    private volatile boolean evict;
48
49    private volatile ScheduledFuture<?> endOfLife;
50
51    private final FastList<Statement> openStatements;
52    private final HikariPool hikariPool;
53
54    private final boolean isReadOnly;
55    private final boolean isAutoCommit;
56
57    static
58    {
59       stateUpdater = AtomicIntegerFieldUpdater.newUpdater(PoolEntry.class"state");
60    }
61
62    PoolEntry(final Connection connection, final PoolBase pool, final boolean isReadOnly, final boolean isAutoCommit)
63    {
64       this.connection = connection;
65       this.hikariPool = (HikariPool) pool;
66       this.isReadOnly = isReadOnly;
67       this.isAutoCommit = isAutoCommit;
68       this.lastAccessed = currentTime();
69       this.openStatements = new FastList<>(Statement.class, 16);
70    }
71
72    /**
73     * Release this entry back to the pool.
74     *
75     * @param lastAccessed last access time-stamp
76     */

77    void recycle(final long lastAccessed)
78    {
79       if (connection != null) {
80          this.lastAccessed = lastAccessed;
81          hikariPool.recycle(this);
82       }
83    }
84
85    /**
86     * Set the end of life {@link ScheduledFuture}.
87     *
88     * @param endOfLife this PoolEntry/Connection's end of life {@link ScheduledFuture}
89     */

90    void setFutureEol(final ScheduledFuture<?> endOfLife)
91    {
92       this.endOfLife = endOfLife;
93    }
94
95    Connection createProxyConnection(final ProxyLeakTask leakTask, final long now)
96    {
97       return ProxyFactory.getProxyConnection(this, connection, openStatements, leakTask, now, isReadOnly, isAutoCommit);
98    }
99
100    void resetConnectionState(final ProxyConnection proxyConnection, final int dirtyBits) throws SQLException
101    {
102       hikariPool.resetConnectionState(connection, proxyConnection, dirtyBits);
103    }
104
105    String getPoolName()
106    {
107       return hikariPool.toString();
108    }
109
110    boolean isMarkedEvicted()
111    {
112       return evict;
113    }
114
115    void markEvicted()
116    {
117       this.evict = true;
118    }
119
120    void evict(final String closureReason)
121    {
122       hikariPool.closeConnection(this, closureReason);
123    }
124
125    /** Returns millis since lastBorrowed */
126    long getMillisSinceBorrowed()
127    {
128       return elapsedMillis(lastBorrowed);
129    }
130
131    PoolBase getPoolBase()
132    {
133       return hikariPool;
134    }
135
136    /** {@inheritDoc} */
137    @Override
138    public String toString()
139    {
140       final long now = currentTime();
141       return connection
142          + ", accessed " + elapsedDisplayString(lastAccessed, now) + " ago, "
143          + stateToString();
144    }
145
146    // ***********************************************************************
147    //                      IConcurrentBagEntry methods
148    // ***********************************************************************
149
150    /** {@inheritDoc} */
151    @Override
152    public int getState()
153    {
154       return stateUpdater.get(this);
155    }
156
157    /** {@inheritDoc} */
158    @Override
159    public boolean compareAndSet(int expect, int update)
160    {
161       return stateUpdater.compareAndSet(this, expect, update);
162    }
163
164    /** {@inheritDoc} */
165    @Override
166    public void setState(int update)
167    {
168       stateUpdater.set(this, update);
169    }
170
171    Connection close()
172    {
173       ScheduledFuture<?> eol = endOfLife;
174       if (eol != null && !eol.isDone() && !eol.cancel(false)) {
175          LOGGER.warn("{} - maxLifeTime expiration task cancellation unexpectedly returned false for connection {}", getPoolName(), connection);
176       }
177
178       Connection con = connection;
179       connection = null;
180       endOfLife = null;
181       return con;
182    }
183
184    private String stateToString()
185    {
186       switch (state) {
187       case STATE_IN_USE:
188          return "IN_USE";
189       case STATE_NOT_IN_USE:
190          return "NOT_IN_USE";
191       case STATE_REMOVED:
192          return "REMOVED";
193       case STATE_RESERVED:
194          return "RESERVED";
195       default:
196          return "Invalid";
197       }
198    }
199 }
200