1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.http.nio.pool;
28
29 import java.net.ConnectException;
30 import java.util.HashMap;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.LinkedList;
34 import java.util.Map;
35 import java.util.Set;
36
37 import org.apache.http.concurrent.BasicFuture;
38 import org.apache.http.nio.reactor.SessionRequest;
39 import org.apache.http.pool.PoolEntry;
40 import org.apache.http.util.Args;
41 import org.apache.http.util.Asserts;
42
43 abstract class RouteSpecificPool<T, C, E extends PoolEntry<T, C>> {
44
45 private final T route;
46 private final Set<E> leased;
47 private final LinkedList<E> available;
48 private final Map<SessionRequest, BasicFuture<E>> pending;
49
50 RouteSpecificPool(final T route) {
51 super();
52 this.route = route;
53 this.leased = new HashSet<E>();
54 this.available = new LinkedList<E>();
55 this.pending = new HashMap<SessionRequest, BasicFuture<E>>();
56 }
57
58 public T getRoute() {
59 return this.route;
60 }
61
62 protected abstract E createEntry(T route, C conn);
63
64 public int getLeasedCount() {
65 return this.leased.size();
66 }
67
68 public int getPendingCount() {
69 return this.pending.size();
70 }
71
72 public int getAvailableCount() {
73 return this.available.size();
74 }
75
76 public int getAllocatedCount() {
77 return this.available.size() + this.leased.size() + this.pending.size();
78 }
79
80 public E getFree(final Object state) {
81 if (!this.available.isEmpty()) {
82 if (state != null) {
83 final Iterator<E> it = this.available.iterator();
84 while (it.hasNext()) {
85 final E entry = it.next();
86 if (state.equals(entry.getState())) {
87 it.remove();
88 this.leased.add(entry);
89 return entry;
90 }
91 }
92 }
93 final Iterator<E> it = this.available.iterator();
94 while (it.hasNext()) {
95 final E entry = it.next();
96 if (entry.getState() == null) {
97 it.remove();
98 this.leased.add(entry);
99 return entry;
100 }
101 }
102 }
103 return null;
104 }
105
106 public E getLastUsed() {
107 return this.available.isEmpty() ? null : this.available.getLast();
108 }
109
110 public boolean remove(final E entry) {
111 Args.notNull(entry, "Pool entry");
112 if (!this.available.remove(entry)) {
113 if (!this.leased.remove(entry)) {
114 return false;
115 }
116 }
117 return true;
118 }
119
120 public void free(final E entry, final boolean reusable) {
121 Args.notNull(entry, "Pool entry");
122 final boolean found = this.leased.remove(entry);
123 Asserts.check(found, "Entry %s has not been leased from this pool", entry);
124 if (reusable) {
125 this.available.addFirst(entry);
126 }
127 }
128
129 public void addPending(final SessionRequest request, final BasicFuture<E> future) {
130 this.pending.put(request, future);
131 }
132
133 private BasicFuture<E> removeRequest(final SessionRequest request) {
134 return this.pending.remove(request);
135 }
136
137 public E createEntry(final SessionRequest request, final C conn) {
138 final E entry = createEntry(this.route, conn);
139 this.leased.add(entry);
140 return entry;
141 }
142
143 public boolean completed(final SessionRequest request, final E entry) {
144 final BasicFuture<E> future = removeRequest(request);
145 if (future != null) {
146 return future.completed(entry);
147 }
148 request.cancel();
149 return false;
150 }
151
152 public void cancelled(final SessionRequest request) {
153 final BasicFuture<E> future = removeRequest(request);
154 if (future != null) {
155 future.cancel(true);
156 }
157 }
158
159 public void failed(final SessionRequest request, final Exception ex) {
160 final BasicFuture<E> future = removeRequest(request);
161 if (future != null) {
162 future.failed(ex);
163 }
164 }
165
166 public void timeout(final SessionRequest request) {
167 final BasicFuture<E> future = removeRequest(request);
168 if (future != null) {
169 future.failed(new ConnectException("Timeout connecting to [" + request.getRemoteAddress() + "]"));
170 }
171 }
172
173 public void shutdown() {
174 for (final SessionRequest request: this.pending.keySet()) {
175 request.cancel();
176 }
177 this.pending.clear();
178 for (final E entry: this.available) {
179 entry.close();
180 }
181 this.available.clear();
182 for (final E entry: this.leased) {
183 entry.close();
184 }
185 this.leased.clear();
186 }
187
188 @Override
189 public String toString() {
190 final StringBuilder buffer = new StringBuilder();
191 buffer.append("[route: ");
192 buffer.append(this.route);
193 buffer.append("][leased: ");
194 buffer.append(this.leased.size());
195 buffer.append("][available: ");
196 buffer.append(this.available.size());
197 buffer.append("][pending: ");
198 buffer.append(this.pending.size());
199 buffer.append("]");
200 return buffer.toString();
201 }
202
203 }