View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.hc.core5.pool;
28  
29  import java.util.concurrent.atomic.AtomicReference;
30  
31  import org.apache.hc.core5.function.Supplier;
32  import org.apache.hc.core5.io.CloseMode;
33  import org.apache.hc.core5.io.ModalCloseable;
34  import org.apache.hc.core5.util.Args;
35  import org.apache.hc.core5.util.Deadline;
36  import org.apache.hc.core5.util.TimeValue;
37  
38  /**
39   * Pool entry containing a pool connection object along with its route.
40   * <p>
41   * The connection assigned to this pool entry may have an expiration time and also have an object
42   * representing a connection state (usually a security principal or a unique token identifying
43   * the user whose credentials have been used while establishing the connection).
44   *
45   * @param <T> the route type that represents the opposite endpoint of a pooled
46   *   connection.
47   * @param <C> the connection type.
48   * @since 4.2
49   */
50  public final class PoolEntry<T, C extends ModalCloseable> {
51  
52      private final T route;
53      private final TimeValue timeToLive;
54      private final AtomicReference<C> connRef;
55      private final DisposalCallback<C> disposalCallback;
56      private final Supplier<Long> currentTimeSupplier;
57  
58      private volatile Object state;
59      private volatile long created;
60      private volatile long updated;
61      private volatile Deadline expiryDeadline = Deadline.MIN_VALUE;
62      private volatile Deadline validityDeadline = Deadline.MIN_VALUE;
63  
64      PoolEntry(final T route, final TimeValue timeToLive, final DisposalCallback<C> disposalCallback,
65                final Supplier<Long> currentTimeSupplier) {
66          super();
67          this.route = Args.notNull(route, "Route");
68          this.timeToLive = TimeValue.defaultsToNegativeOneMillisecond(timeToLive);
69          this.connRef = new AtomicReference<>(null);
70          this.disposalCallback = disposalCallback;
71          this.currentTimeSupplier = currentTimeSupplier;
72      }
73  
74      PoolEntry(final T route, final TimeValue timeToLive, final Supplier<Long> currentTimeSupplier) {
75          this(route, timeToLive, null, currentTimeSupplier);
76      }
77  
78      /**
79       * Creates new {@code PoolEntry} instance.
80       *
81       * @param route route to the opposite endpoint.
82       * @param timeToLive maximum time to live. May be zero if the connection
83       *   does not have an expiry deadline.
84       * @param disposalCallback callback invoked before connection disposal.
85       */
86      public PoolEntry(final T route, final TimeValue timeToLive, final DisposalCallback<C> disposalCallback) {
87          this(route, timeToLive, disposalCallback, null);
88      }
89  
90      /**
91       * Creates new {@code PoolEntry} instance.
92       *
93       * @param route route to the opposite endpoint.
94       * @param timeToLive maximum time to live. May be zero if the connection
95       *   does not have an expiry deadline.
96       */
97      public PoolEntry(final T route, final TimeValue timeToLive) {
98          this(route, timeToLive, null, null);
99      }
100 
101     public PoolEntry(final T route) {
102         this(route, null);
103     }
104 
105     long getCurrentTime() {
106         return currentTimeSupplier != null ? currentTimeSupplier.get() : System.currentTimeMillis();
107     }
108 
109     public T getRoute() {
110         return this.route;
111     }
112 
113     public C getConnection() {
114         return this.connRef.get();
115     }
116 
117     /**
118      * @since 5.0
119      */
120     public Deadline getValidityDeadline() {
121         return this.validityDeadline;
122     }
123 
124     public Object getState() {
125         return this.state;
126     }
127 
128     public long getUpdated() {
129         return this.updated;
130     }
131 
132     public Deadline getExpiryDeadline() {
133         return this.expiryDeadline;
134     }
135 
136     /**
137      * @since 5.0
138      */
139     public boolean hasConnection() {
140         return this.connRef.get() != null;
141     }
142 
143     /**
144      * @since 5.0
145      */
146     public void assignConnection(final C conn) {
147         Args.notNull(conn, "connection");
148         if (this.connRef.compareAndSet(null, conn)) {
149             this.created = getCurrentTime();
150             this.updated = this.created;
151             this.validityDeadline = Deadline.calculate(this.created, this.timeToLive);
152             this.expiryDeadline = this.validityDeadline;
153             this.state = null;
154         } else {
155             throw new IllegalStateException("Connection already assigned");
156         }
157     }
158 
159     /**
160      * @since 5.0
161      */
162     public void discardConnection(final CloseMode closeMode) {
163         final C connection = this.connRef.getAndSet(null);
164         if (connection != null) {
165             this.state = null;
166             this.created = 0;
167             this.updated = 0;
168             this.expiryDeadline = Deadline.MIN_VALUE;
169             this.validityDeadline = Deadline.MIN_VALUE;
170             if (this.disposalCallback != null) {
171                 this.disposalCallback.execute(connection, closeMode);
172             } else {
173                 connection.close(closeMode);
174             }
175         }
176     }
177 
178     /**
179      * @since 5.0
180      */
181     public void updateExpiry(final TimeValue expiryTime) {
182         Args.notNull(expiryTime, "Expiry time");
183         final long currentTime = getCurrentTime();
184         final Deadline newExpiry = Deadline.calculate(currentTime, expiryTime);
185         this.expiryDeadline = newExpiry.min(this.validityDeadline);
186         this.updated = currentTime;
187     }
188 
189     /**
190      * @since 5.0
191      */
192     public void updateState(final Object state) {
193         this.state = state;
194         this.updated = getCurrentTime();
195     }
196 
197     @Override
198     public String toString() {
199         final StringBuilder buffer = new StringBuilder();
200         buffer.append("[route:");
201         buffer.append(this.route);
202         buffer.append("][state:");
203         buffer.append(this.state);
204         buffer.append("]");
205         return buffer.toString();
206     }
207 
208 }