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.http.nio.pool;
28  
29  import java.io.IOException;
30  import java.net.SocketAddress;
31  import java.util.HashMap;
32  import java.util.HashSet;
33  import java.util.Iterator;
34  import java.util.LinkedList;
35  import java.util.ListIterator;
36  import java.util.Map;
37  import java.util.Set;
38  import java.util.concurrent.ConcurrentLinkedQueue;
39  import java.util.concurrent.Future;
40  import java.util.concurrent.TimeUnit;
41  import java.util.concurrent.TimeoutException;
42  import java.util.concurrent.atomic.AtomicBoolean;
43  import java.util.concurrent.locks.Lock;
44  import java.util.concurrent.locks.ReentrantLock;
45  
46  import org.apache.http.annotation.ThreadSafe;
47  import org.apache.http.concurrent.BasicFuture;
48  import org.apache.http.concurrent.FutureCallback;
49  import org.apache.http.nio.reactor.ConnectingIOReactor;
50  import org.apache.http.nio.reactor.IOSession;
51  import org.apache.http.nio.reactor.SessionRequest;
52  import org.apache.http.nio.reactor.SessionRequestCallback;
53  import org.apache.http.pool.ConnPool;
54  import org.apache.http.pool.ConnPoolControl;
55  import org.apache.http.pool.PoolEntry;
56  import org.apache.http.pool.PoolEntryCallback;
57  import org.apache.http.pool.PoolStats;
58  import org.apache.http.util.Args;
59  import org.apache.http.util.Asserts;
60  
61  /**
62   * Abstract non-blocking connection pool.
63   *
64   * @param <T> route
65   * @param <C> connection object
66   * @param <E> pool entry
67   *
68   * @since 4.2
69   */
70  @ThreadSafe
71  public abstract class AbstractNIOConnPool<T, C, E extends PoolEntry<T, C>>
72                                                    implements ConnPool<T, E>, ConnPoolControl<T> {
73  
74      private final ConnectingIOReactor ioreactor;
75      private final NIOConnFactory<T, C> connFactory;
76      private final SocketAddressResolver<T> addressResolver;
77      private final SessionRequestCallback sessionRequestCallback;
78      private final Map<T, RouteSpecificPool<T, C, E>> routeToPool;
79      private final LinkedList<LeaseRequest<T, C, E>> leasingRequests;
80      private final Set<SessionRequest> pending;
81      private final Set<E> leased;
82      private final LinkedList<E> available;
83      private final ConcurrentLinkedQueue<LeaseRequest<T, C, E>> completedRequests;
84      private final Map<T, Integer> maxPerRoute;
85      private final Lock lock;
86      private final AtomicBoolean isShutDown;
87  
88      private volatile int defaultMaxPerRoute;
89      private volatile int maxTotal;
90  
91      /**
92       * @deprecated use {@link AbstractNIOConnPool#AbstractNIOConnPool(ConnectingIOReactor,
93       *   NIOConnFactory, SocketAddressResolver, int, int)}
94       */
95      @Deprecated
96      public AbstractNIOConnPool(
97              final ConnectingIOReactor ioreactor,
98              final NIOConnFactory<T, C> connFactory,
99              final int defaultMaxPerRoute,
100             final int maxTotal) {
101         super();
102         Args.notNull(ioreactor, "I/O reactor");
103         Args.notNull(connFactory, "Connection factory");
104         Args.positive(defaultMaxPerRoute, "Max per route value");
105         Args.positive(maxTotal, "Max total value");
106         this.ioreactor = ioreactor;
107         this.connFactory = connFactory;
108         this.addressResolver = new SocketAddressResolver<T>() {
109 
110             public SocketAddress resolveLocalAddress(final T route) throws IOException {
111                 return AbstractNIOConnPool.this.resolveLocalAddress(route);
112             }
113 
114             public SocketAddress resolveRemoteAddress(final T route) throws IOException {
115                 return AbstractNIOConnPool.this.resolveRemoteAddress(route);
116             }
117 
118         };
119         this.sessionRequestCallback = new InternalSessionRequestCallback();
120         this.routeToPool = new HashMap<T, RouteSpecificPool<T, C, E>>();
121         this.leasingRequests = new LinkedList<LeaseRequest<T, C, E>>();
122         this.pending = new HashSet<SessionRequest>();
123         this.leased = new HashSet<E>();
124         this.available = new LinkedList<E>();
125         this.maxPerRoute = new HashMap<T, Integer>();
126         this.completedRequests = new ConcurrentLinkedQueue<LeaseRequest<T, C, E>>();
127         this.lock = new ReentrantLock();
128         this.isShutDown = new AtomicBoolean(false);
129         this.defaultMaxPerRoute = defaultMaxPerRoute;
130         this.maxTotal = maxTotal;
131     }
132 
133     /**
134      * @since 4.3
135      */
136     public AbstractNIOConnPool(
137             final ConnectingIOReactor ioreactor,
138             final NIOConnFactory<T, C> connFactory,
139             final SocketAddressResolver<T> addressResolver,
140             final int defaultMaxPerRoute,
141             final int maxTotal) {
142         super();
143         Args.notNull(ioreactor, "I/O reactor");
144         Args.notNull(connFactory, "Connection factory");
145         Args.notNull(addressResolver, "Address resolver");
146         Args.positive(defaultMaxPerRoute, "Max per route value");
147         Args.positive(maxTotal, "Max total value");
148         this.ioreactor = ioreactor;
149         this.connFactory = connFactory;
150         this.addressResolver = addressResolver;
151         this.sessionRequestCallback = new InternalSessionRequestCallback();
152         this.routeToPool = new HashMap<T, RouteSpecificPool<T, C, E>>();
153         this.leasingRequests = new LinkedList<LeaseRequest<T, C, E>>();
154         this.pending = new HashSet<SessionRequest>();
155         this.leased = new HashSet<E>();
156         this.available = new LinkedList<E>();
157         this.completedRequests = new ConcurrentLinkedQueue<LeaseRequest<T, C, E>>();
158         this.maxPerRoute = new HashMap<T, Integer>();
159         this.lock = new ReentrantLock();
160         this.isShutDown = new AtomicBoolean(false);
161         this.defaultMaxPerRoute = defaultMaxPerRoute;
162         this.maxTotal = maxTotal;
163     }
164 
165     /**
166      * @deprecated (4.3) use {@link SocketAddressResolver}
167      */
168     @Deprecated
169     protected SocketAddress resolveRemoteAddress(final T route) {
170         return null;
171     }
172 
173     /**
174      * @deprecated (4.3) use {@link SocketAddressResolver}
175      */
176     @Deprecated
177     protected SocketAddress resolveLocalAddress(final T route) {
178         return null;
179     }
180 
181     protected abstract E createEntry(T route, C conn);
182 
183     /**
184      * @since 4.3
185      */
186     protected void onLease(final E entry) {
187     }
188 
189     /**
190      * @since 4.3
191      */
192     protected void onRelease(final E entry) {
193     }
194 
195     public boolean isShutdown() {
196         return this.isShutDown.get();
197     }
198 
199     public void shutdown(final long waitMs) throws IOException {
200         if (this.isShutDown.compareAndSet(false, true)) {
201             fireCallbacks();
202             this.lock.lock();
203             try {
204                 for (final SessionRequest sessionRequest: this.pending) {
205                     sessionRequest.cancel();
206                 }
207                 for (final E entry: this.available) {
208                     entry.close();
209                 }
210                 for (final E entry: this.leased) {
211                     entry.close();
212                 }
213                 for (final RouteSpecificPool<T, C, E> pool: this.routeToPool.values()) {
214                     pool.shutdown();
215                 }
216                 this.routeToPool.clear();
217                 this.leased.clear();
218                 this.pending.clear();
219                 this.available.clear();
220                 this.leasingRequests.clear();
221                 this.ioreactor.shutdown(waitMs);
222             } finally {
223                 this.lock.unlock();
224             }
225         }
226     }
227 
228     private RouteSpecificPool<T, C, E> getPool(final T route) {
229         RouteSpecificPool<T, C, E> pool = this.routeToPool.get(route);
230         if (pool == null) {
231             pool = new RouteSpecificPool<T, C, E>(route) {
232 
233                 @Override
234                 protected E createEntry(final T route, final C conn) {
235                     return AbstractNIOConnPool.this.createEntry(route, conn);
236                 }
237 
238             };
239             this.routeToPool.put(route, pool);
240         }
241         return pool;
242     }
243 
244     public Future<E> lease(
245             final T route, final Object state,
246             final long connectTimeout, final TimeUnit tunit,
247             final FutureCallback<E> callback) {
248         return this.lease(route, state, connectTimeout, connectTimeout, tunit, callback);
249     }
250 
251     /**
252      * @since 4.3
253      */
254     public Future<E> lease(
255             final T route, final Object state,
256             final long connectTimeout, final long leaseTimeout, final TimeUnit tunit,
257             final FutureCallback<E> callback) {
258         Args.notNull(route, "Route");
259         Args.notNull(tunit, "Time unit");
260         Asserts.check(!this.isShutDown.get(), "Connection pool shut down");
261         final BasicFuture<E> future = new BasicFuture<E>(callback);
262         this.lock.lock();
263         try {
264             final long timeout = connectTimeout > 0 ? tunit.toMillis(connectTimeout) : 0;
265             final LeaseRequest<T, C, E> request = new LeaseRequest<T, C, E>(route, state, timeout, leaseTimeout, future);
266             final boolean completed = processPendingRequest(request);
267             if (!request.isDone() && !completed) {
268                 this.leasingRequests.add(request);
269             }
270             if (request.isDone()) {
271                 this.completedRequests.add(request);
272             }
273         } finally {
274             this.lock.unlock();
275         }
276         fireCallbacks();
277         return future;
278     }
279 
280     public Future<E> lease(final T route, final Object state, final FutureCallback<E> callback) {
281         return lease(route, state, -1, TimeUnit.MICROSECONDS, callback);
282     }
283 
284     public Future<E> lease(final T route, final Object state) {
285         return lease(route, state, -1, TimeUnit.MICROSECONDS, null);
286     }
287 
288     public void release(final E entry, final boolean reusable) {
289         if (entry == null) {
290             return;
291         }
292         if (this.isShutDown.get()) {
293             return;
294         }
295         this.lock.lock();
296         try {
297             if (this.leased.remove(entry)) {
298                 final RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
299                 pool.free(entry, reusable);
300                 if (reusable) {
301                     this.available.addFirst(entry);
302                     onRelease(entry);
303                 } else {
304                     entry.close();
305                 }
306                 processNextPendingRequest();
307             }
308         } finally {
309             this.lock.unlock();
310         }
311         fireCallbacks();
312     }
313 
314     private void processPendingRequests() {
315         final ListIterator<LeaseRequest<T, C, E>> it = this.leasingRequests.listIterator();
316         while (it.hasNext()) {
317             final LeaseRequest<T, C, E> request = it.next();
318             final boolean completed = processPendingRequest(request);
319             if (request.isDone() || completed) {
320                 it.remove();
321             }
322             if (request.isDone()) {
323                 this.completedRequests.add(request);
324             }
325         }
326     }
327 
328     private void processNextPendingRequest() {
329         final ListIterator<LeaseRequest<T, C, E>> it = this.leasingRequests.listIterator();
330         while (it.hasNext()) {
331             final LeaseRequest<T, C, E> request = it.next();
332             final boolean completed = processPendingRequest(request);
333             if (request.isDone() || completed) {
334                 it.remove();
335             }
336             if (request.isDone()) {
337                 this.completedRequests.add(request);
338             }
339             if (completed) {
340                 return;
341             }
342         }
343     }
344 
345     private boolean processPendingRequest(final LeaseRequest<T, C, E> request) {
346         final T route = request.getRoute();
347         final Object state = request.getState();
348         final long deadline = request.getDeadline();
349 
350         final long now = System.currentTimeMillis();
351         if (now > deadline) {
352             request.failed(new TimeoutException());
353             return false;
354         }
355 
356         final RouteSpecificPool<T, C, E> pool = getPool(route);
357         E entry;
358         for (;;) {
359             entry = pool.getFree(state);
360             if (entry == null) {
361                 break;
362             }
363             if (entry.isClosed() || entry.isExpired(System.currentTimeMillis())) {
364                 entry.close();
365                 this.available.remove(entry);
366                 pool.free(entry, false);
367             } else {
368                 break;
369             }
370         }
371         if (entry != null) {
372             this.available.remove(entry);
373             this.leased.add(entry);
374             request.completed(entry);
375             onLease(entry);
376             return true;
377         }
378 
379         // New connection is needed
380         final int maxPerRoute = getMax(route);
381         // Shrink the pool prior to allocating a new connection
382         final int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);
383         if (excess > 0) {
384             for (int i = 0; i < excess; i++) {
385                 final E lastUsed = pool.getLastUsed();
386                 if (lastUsed == null) {
387                     break;
388                 }
389                 lastUsed.close();
390                 this.available.remove(lastUsed);
391                 pool.remove(lastUsed);
392             }
393         }
394 
395         if (pool.getAllocatedCount() < maxPerRoute) {
396             final int totalUsed = this.pending.size() + this.leased.size();
397             final int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);
398             if (freeCapacity == 0) {
399                 return false;
400             }
401             final int totalAvailable = this.available.size();
402             if (totalAvailable > freeCapacity - 1) {
403                 if (!this.available.isEmpty()) {
404                     final E lastUsed = this.available.removeLast();
405                     lastUsed.close();
406                     final RouteSpecificPool<T, C, E> otherpool = getPool(lastUsed.getRoute());
407                     otherpool.remove(lastUsed);
408                 }
409             }
410 
411             final SocketAddress localAddress;
412             final SocketAddress remoteAddress;
413             try {
414                 remoteAddress = this.addressResolver.resolveRemoteAddress(route);
415                 localAddress = this.addressResolver.resolveLocalAddress(route);
416             } catch (final IOException ex) {
417                 request.failed(ex);
418                 return false;
419             }
420 
421             final SessionRequest sessionRequest = this.ioreactor.connect(
422                     remoteAddress, localAddress, route, this.sessionRequestCallback);
423             final int timout = request.getConnectTimeout() < Integer.MAX_VALUE ?
424                     (int) request.getConnectTimeout() : Integer.MAX_VALUE;
425             sessionRequest.setConnectTimeout(timout);
426             this.pending.add(sessionRequest);
427             pool.addPending(sessionRequest, request.getFuture());
428             return true;
429         } else {
430             return false;
431         }
432     }
433 
434     private void fireCallbacks() {
435         LeaseRequest<T, C, E> request;
436         while ((request = this.completedRequests.poll()) != null) {
437             final BasicFuture<E> future = request.getFuture();
438             final Exception ex = request.getException();
439             final E result = request.getResult();
440             if (ex != null) {
441                 future.failed(ex);
442             } else if (result != null) {
443                 future.completed(result);
444             } else {
445                 future.cancel();
446             }
447         }
448     }
449 
450     public void validatePendingRequests() {
451         this.lock.lock();
452         try {
453             final long now = System.currentTimeMillis();
454             final ListIterator<LeaseRequest<T, C, E>> it = this.leasingRequests.listIterator();
455             while (it.hasNext()) {
456                 final LeaseRequest<T, C, E> request = it.next();
457                 final long deadline = request.getDeadline();
458                 if (now > deadline) {
459                     it.remove();
460                     request.failed(new TimeoutException());
461                     this.completedRequests.add(request);
462                 }
463             }
464         } finally {
465             this.lock.unlock();
466         }
467         fireCallbacks();
468     }
469 
470     protected void requestCompleted(final SessionRequest request) {
471         if (this.isShutDown.get()) {
472             return;
473         }
474         @SuppressWarnings("unchecked")
475         final
476         T route = (T) request.getAttachment();
477         this.lock.lock();
478         try {
479             this.pending.remove(request);
480             final RouteSpecificPool<T, C, E> pool = getPool(route);
481             final IOSession session = request.getSession();
482             try {
483                 final C conn = this.connFactory.create(route, session);
484                 final E entry = pool.createEntry(request, conn);
485                 this.leased.add(entry);
486                 pool.completed(request, entry);
487                 onLease(entry);
488             } catch (final IOException ex) {
489                 pool.failed(request, ex);
490             }
491         } finally {
492             this.lock.unlock();
493         }
494         fireCallbacks();
495     }
496 
497     protected void requestCancelled(final SessionRequest request) {
498         if (this.isShutDown.get()) {
499             return;
500         }
501         @SuppressWarnings("unchecked")
502         final
503         T route = (T) request.getAttachment();
504         this.lock.lock();
505         try {
506             this.pending.remove(request);
507             final RouteSpecificPool<T, C, E> pool = getPool(route);
508             pool.cancelled(request);
509             processNextPendingRequest();
510         } finally {
511             this.lock.unlock();
512         }
513         fireCallbacks();
514     }
515 
516     protected void requestFailed(final SessionRequest request) {
517         if (this.isShutDown.get()) {
518             return;
519         }
520         @SuppressWarnings("unchecked")
521         final
522         T route = (T) request.getAttachment();
523         this.lock.lock();
524         try {
525             this.pending.remove(request);
526             final RouteSpecificPool<T, C, E> pool = getPool(route);
527             pool.failed(request, request.getException());
528             processNextPendingRequest();
529         } finally {
530             this.lock.unlock();
531         }
532         fireCallbacks();
533     }
534 
535     protected void requestTimeout(final SessionRequest request) {
536         if (this.isShutDown.get()) {
537             return;
538         }
539         @SuppressWarnings("unchecked")
540         final
541         T route = (T) request.getAttachment();
542         this.lock.lock();
543         try {
544             this.pending.remove(request);
545             final RouteSpecificPool<T, C, E> pool = getPool(route);
546             pool.timeout(request);
547             processNextPendingRequest();
548         } finally {
549             this.lock.unlock();
550         }
551         fireCallbacks();
552     }
553 
554     private int getMax(final T route) {
555         final Integer v = this.maxPerRoute.get(route);
556         if (v != null) {
557             return v.intValue();
558         } else {
559             return this.defaultMaxPerRoute;
560         }
561     }
562 
563     public void setMaxTotal(final int max) {
564         Args.positive(max, "Max value");
565         this.lock.lock();
566         try {
567             this.maxTotal = max;
568         } finally {
569             this.lock.unlock();
570         }
571     }
572 
573     public int getMaxTotal() {
574         this.lock.lock();
575         try {
576             return this.maxTotal;
577         } finally {
578             this.lock.unlock();
579         }
580     }
581 
582     public void setDefaultMaxPerRoute(final int max) {
583         Args.positive(max, "Max value");
584         this.lock.lock();
585         try {
586             this.defaultMaxPerRoute = max;
587         } finally {
588             this.lock.unlock();
589         }
590     }
591 
592     public int getDefaultMaxPerRoute() {
593         this.lock.lock();
594         try {
595             return this.defaultMaxPerRoute;
596         } finally {
597             this.lock.unlock();
598         }
599     }
600 
601     public void setMaxPerRoute(final T route, final int max) {
602         Args.notNull(route, "Route");
603         Args.positive(max, "Max value");
604         this.lock.lock();
605         try {
606             this.maxPerRoute.put(route, Integer.valueOf(max));
607         } finally {
608             this.lock.unlock();
609         }
610     }
611 
612     public int getMaxPerRoute(final T route) {
613         Args.notNull(route, "Route");
614         this.lock.lock();
615         try {
616             return getMax(route);
617         } finally {
618             this.lock.unlock();
619         }
620     }
621 
622     public PoolStats getTotalStats() {
623         this.lock.lock();
624         try {
625             return new PoolStats(
626                     this.leased.size(),
627                     this.pending.size(),
628                     this.available.size(),
629                     this.maxTotal);
630         } finally {
631             this.lock.unlock();
632         }
633     }
634 
635     public PoolStats getStats(final T route) {
636         Args.notNull(route, "Route");
637         this.lock.lock();
638         try {
639             final RouteSpecificPool<T, C, E> pool = getPool(route);
640             return new PoolStats(
641                     pool.getLeasedCount(),
642                     pool.getPendingCount(),
643                     pool.getAvailableCount(),
644                     getMax(route));
645         } finally {
646             this.lock.unlock();
647         }
648     }
649 
650     /**
651      * Enumerates all available connections.
652      *
653      * @since 4.3
654      */
655     protected void enumAvailable(final PoolEntryCallback<T, C> callback) {
656         this.lock.lock();
657         try {
658             final Iterator<E> it = this.available.iterator();
659             while (it.hasNext()) {
660                 final E entry = it.next();
661                 callback.process(entry);
662                 if (entry.isClosed()) {
663                     final RouteSpecificPool<T, C, E> pool = getPool(entry.getRoute());
664                     pool.remove(entry);
665                     it.remove();
666                 }
667             }
668             processPendingRequests();
669             purgePoolMap();
670         } finally {
671             this.lock.unlock();
672         }
673     }
674 
675     /**
676      * Enumerates all leased connections.
677      *
678      * @since 4.3
679      */
680     protected void enumLeased(final PoolEntryCallback<T, C> callback) {
681         this.lock.lock();
682         try {
683             final Iterator<E> it = this.leased.iterator();
684             while (it.hasNext()) {
685                 final E entry = it.next();
686                 callback.process(entry);
687             }
688             processPendingRequests();
689         } finally {
690             this.lock.unlock();
691         }
692     }
693 
694     /**
695      * Use {@link #enumLeased(org.apache.http.pool.PoolEntryCallback)}
696      *  or {@link #enumAvailable(org.apache.http.pool.PoolEntryCallback)} instead.
697      *
698      * @deprecated (4.3.2)
699      */
700     @Deprecated
701     protected void enumEntries(final Iterator<E> it, final PoolEntryCallback<T, C> callback) {
702         while (it.hasNext()) {
703             final E entry = it.next();
704             callback.process(entry);
705         }
706         processPendingRequests();
707     }
708 
709     private void purgePoolMap() {
710         final Iterator<Map.Entry<T, RouteSpecificPool<T, C, E>>> it = this.routeToPool.entrySet().iterator();
711         while (it.hasNext()) {
712             final Map.Entry<T, RouteSpecificPool<T, C, E>> entry = it.next();
713             final RouteSpecificPool<T, C, E> pool = entry.getValue();
714             if (pool.getAllocatedCount() == 0) {
715                 it.remove();
716             }
717         }
718     }
719 
720     public void closeIdle(final long idletime, final TimeUnit tunit) {
721         Args.notNull(tunit, "Time unit");
722         long time = tunit.toMillis(idletime);
723         if (time < 0) {
724             time = 0;
725         }
726         final long deadline = System.currentTimeMillis() - time;
727         enumAvailable(new PoolEntryCallback<T, C>() {
728 
729             public void process(final PoolEntry<T, C> entry) {
730                 if (entry.getUpdated() <= deadline) {
731                     entry.close();
732                 }
733             }
734 
735         });
736     }
737 
738     public void closeExpired() {
739         final long now = System.currentTimeMillis();
740         enumAvailable(new PoolEntryCallback<T, C>() {
741 
742             public void process(final PoolEntry<T, C> entry) {
743                 if (entry.isExpired(now)) {
744                     entry.close();
745                 }
746             }
747 
748         });
749     }
750 
751     @Override
752     public String toString() {
753         final StringBuilder buffer = new StringBuilder();
754         buffer.append("[leased: ");
755         buffer.append(this.leased);
756         buffer.append("][available: ");
757         buffer.append(this.available);
758         buffer.append("][pending: ");
759         buffer.append(this.pending);
760         buffer.append("]");
761         return buffer.toString();
762     }
763 
764     class InternalSessionRequestCallback implements SessionRequestCallback {
765 
766         public void completed(final SessionRequest request) {
767             requestCompleted(request);
768         }
769 
770         public void cancelled(final SessionRequest request) {
771             requestCancelled(request);
772         }
773 
774         public void failed(final SessionRequest request) {
775             requestFailed(request);
776         }
777 
778         public void timeout(final SessionRequest request) {
779             requestTimeout(request);
780         }
781 
782     }
783 
784 }