1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.client;
20
21 import java.io.IOException;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.concurrent.TimeUnit;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27 import org.eclipse.jetty.client.api.Connection;
28 import org.eclipse.jetty.client.api.Destination;
29 import org.eclipse.jetty.util.Callback;
30 import org.eclipse.jetty.util.annotation.ManagedAttribute;
31 import org.eclipse.jetty.util.component.ContainerLifeCycle;
32 import org.eclipse.jetty.util.log.Log;
33 import org.eclipse.jetty.util.log.Logger;
34 import org.eclipse.jetty.util.thread.Scheduler;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class ValidatingConnectionPool extends ConnectionPool
60 {
61 private static final Logger LOG = Log.getLogger(ValidatingConnectionPool.class);
62
63 private final Scheduler scheduler;
64 private final long timeout;
65 private final Map<Connection, Holder> quarantine;
66
67 public ValidatingConnectionPool(Destination destination, int maxConnections, Callback requester, Scheduler scheduler, long timeout)
68 {
69 super(destination, maxConnections, requester);
70 this.scheduler = scheduler;
71 this.timeout = timeout;
72 this.quarantine = new HashMap<>(maxConnections);
73 }
74
75 @ManagedAttribute(value = "The number of validating connections", readonly = true)
76 public int getValidatingConnectionCount()
77 {
78 return quarantine.size();
79 }
80
81 @Override
82 public boolean release(Connection connection)
83 {
84 lock();
85 try
86 {
87 if (!getActiveConnections().remove(connection))
88 return false;
89 Holder holder = new Holder(connection);
90 holder.task = scheduler.schedule(holder, timeout, TimeUnit.MILLISECONDS);
91 quarantine.put(connection, holder);
92 if (LOG.isDebugEnabled())
93 LOG.debug("Validating for {}ms {}", timeout, connection);
94 }
95 finally
96 {
97 unlock();
98 }
99
100 released(connection);
101 return true;
102 }
103
104 @Override
105 public boolean remove(Connection connection)
106 {
107 Holder holder;
108 lock();
109 try
110 {
111 holder = quarantine.remove(connection);
112 }
113 finally
114 {
115 unlock();
116 }
117
118 if (holder == null)
119 return super.remove(connection);
120
121 if (LOG.isDebugEnabled())
122 LOG.debug("Removed while validating {}", connection);
123
124 boolean cancelled = holder.cancel();
125 if (cancelled)
126 return remove(connection, true);
127
128 return super.remove(connection);
129 }
130
131 @Override
132 public void dump(Appendable out, String indent) throws IOException
133 {
134 super.dump(out, indent);
135 ContainerLifeCycle.dump(out, indent, quarantine.values());
136 }
137
138 @Override
139 public String toString()
140 {
141 int size;
142 lock();
143 try
144 {
145 size = quarantine.size();
146 }
147 finally
148 {
149 unlock();
150 }
151 return String.format("%s[v=%d]", super.toString(), size);
152 }
153
154 private class Holder implements Runnable
155 {
156 private final long timestamp = System.nanoTime();
157 private final AtomicBoolean latch = new AtomicBoolean();
158 private final Connection connection;
159 public Scheduler.Task task;
160
161 public Holder(Connection connection)
162 {
163 this.connection = connection;
164 }
165
166 @Override
167 public void run()
168 {
169 if (latch.compareAndSet(false, true))
170 {
171 boolean idle;
172 lock();
173 try
174 {
175 quarantine.remove(connection);
176 idle = offerIdle(connection);
177 if (LOG.isDebugEnabled())
178 LOG.debug("Validated {}", connection);
179 }
180 finally
181 {
182 unlock();
183 }
184
185 if (idle(connection, idle))
186 proceed();
187 }
188 }
189
190 public boolean cancel()
191 {
192 if (latch.compareAndSet(false, true))
193 {
194 task.cancel();
195 return true;
196 }
197 return false;
198 }
199
200 @Override
201 public String toString()
202 {
203 return String.format("%s[validationLeft=%dms]",
204 connection,
205 timeout - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - timestamp)
206 );
207 }
208 }
209 }