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}