001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027package org.apache.http.nio.pool; 028 029import java.net.ConnectException; 030import java.util.HashMap; 031import java.util.HashSet; 032import java.util.Iterator; 033import java.util.LinkedList; 034import java.util.Map; 035import java.util.Set; 036 037import org.apache.http.concurrent.BasicFuture; 038import org.apache.http.nio.reactor.SessionRequest; 039import org.apache.http.pool.PoolEntry; 040import org.apache.http.util.Args; 041import org.apache.http.util.Asserts; 042 043abstract class RouteSpecificPool<T, C, E extends PoolEntry<T, C>> { 044 045 private final T route; 046 private final Set<E> leased; 047 private final LinkedList<E> available; 048 private final Map<SessionRequest, BasicFuture<E>> pending; 049 050 RouteSpecificPool(final T route) { 051 super(); 052 this.route = route; 053 this.leased = new HashSet<E>(); 054 this.available = new LinkedList<E>(); 055 this.pending = new HashMap<SessionRequest, BasicFuture<E>>(); 056 } 057 058 public T getRoute() { 059 return this.route; 060 } 061 062 protected abstract E createEntry(T route, C conn); 063 064 public int getLeasedCount() { 065 return this.leased.size(); 066 } 067 068 public int getPendingCount() { 069 return this.pending.size(); 070 } 071 072 public int getAvailableCount() { 073 return this.available.size(); 074 } 075 076 public int getAllocatedCount() { 077 return this.available.size() + this.leased.size() + this.pending.size(); 078 } 079 080 public E getFree(final Object state) { 081 if (!this.available.isEmpty()) { 082 if (state != null) { 083 final Iterator<E> it = this.available.iterator(); 084 while (it.hasNext()) { 085 final E entry = it.next(); 086 if (state.equals(entry.getState())) { 087 it.remove(); 088 this.leased.add(entry); 089 return entry; 090 } 091 } 092 } 093 final Iterator<E> it = this.available.iterator(); 094 while (it.hasNext()) { 095 final E entry = it.next(); 096 if (entry.getState() == null) { 097 it.remove(); 098 this.leased.add(entry); 099 return entry; 100 } 101 } 102 } 103 return null; 104 } 105 106 public E getLastUsed() { 107 if (!this.available.isEmpty()) { 108 return this.available.getLast(); 109 } else { 110 return null; 111 } 112 } 113 114 public boolean remove(final E entry) { 115 Args.notNull(entry, "Pool entry"); 116 if (!this.available.remove(entry)) { 117 if (!this.leased.remove(entry)) { 118 return false; 119 } 120 } 121 return true; 122 } 123 124 public void free(final E entry, final boolean reusable) { 125 Args.notNull(entry, "Pool entry"); 126 final boolean found = this.leased.remove(entry); 127 Asserts.check(found, "Entry %s has not been leased from this pool", entry); 128 if (reusable) { 129 this.available.addFirst(entry); 130 } 131 } 132 133 public void addPending(final SessionRequest request, final BasicFuture<E> future) { 134 this.pending.put(request, future); 135 } 136 137 private BasicFuture<E> removeRequest(final SessionRequest request) { 138 return this.pending.remove(request); 139 } 140 141 public E createEntry(final SessionRequest request, final C conn) { 142 final E entry = createEntry(this.route, conn); 143 this.leased.add(entry); 144 return entry; 145 } 146 147 public boolean completed(final SessionRequest request, final E entry) { 148 final BasicFuture<E> future = removeRequest(request); 149 if (future != null) { 150 return future.completed(entry); 151 } else { 152 request.cancel(); 153 return false; 154 } 155 } 156 157 public void cancelled(final SessionRequest request) { 158 final BasicFuture<E> future = removeRequest(request); 159 if (future != null) { 160 future.cancel(true); 161 } 162 } 163 164 public void failed(final SessionRequest request, final Exception ex) { 165 final BasicFuture<E> future = removeRequest(request); 166 if (future != null) { 167 future.failed(ex); 168 } 169 } 170 171 public void timeout(final SessionRequest request) { 172 final BasicFuture<E> future = removeRequest(request); 173 if (future != null) { 174 future.failed(new ConnectException()); 175 } 176 } 177 178 public void shutdown() { 179 for (final SessionRequest request: this.pending.keySet()) { 180 request.cancel(); 181 } 182 this.pending.clear(); 183 for (final E entry: this.available) { 184 entry.close(); 185 } 186 this.available.clear(); 187 for (final E entry: this.leased) { 188 entry.close(); 189 } 190 this.leased.clear(); 191 } 192 193 @Override 194 public String toString() { 195 final StringBuilder buffer = new StringBuilder(); 196 buffer.append("[route: "); 197 buffer.append(this.route); 198 buffer.append("][leased: "); 199 buffer.append(this.leased.size()); 200 buffer.append("][available: "); 201 buffer.append(this.available.size()); 202 buffer.append("][pending: "); 203 buffer.append(this.pending.size()); 204 buffer.append("]"); 205 return buffer.toString(); 206 } 207 208}