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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 package com.eviware.soapui.impl.wsdl.support.http;
43
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.OutputStream;
47 import java.lang.ref.Reference;
48 import java.lang.ref.ReferenceQueue;
49 import java.lang.ref.WeakReference;
50 import java.net.InetAddress;
51 import java.net.Socket;
52 import java.net.SocketException;
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.Iterator;
56 import java.util.LinkedList;
57 import java.util.Map;
58 import java.util.WeakHashMap;
59
60 import org.apache.commons.httpclient.ConnectionPoolTimeoutException;
61 import org.apache.commons.httpclient.HostConfiguration;
62 import org.apache.commons.httpclient.HttpConnection;
63 import org.apache.commons.httpclient.HttpConnectionManager;
64 import org.apache.commons.httpclient.HttpException;
65 import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
66 import org.apache.commons.httpclient.params.HttpConnectionParams;
67 import org.apache.commons.httpclient.protocol.Protocol;
68 import org.apache.commons.httpclient.util.IdleConnectionHandler;
69 import org.apache.log4j.Logger;
70
71 /***
72 * Manages a set of HttpConnections for various HostConfigurations. Modified to keep different pools for different keystores.
73 *
74 * @author <a href="mailto:becke@u.washington.edu">Michael Becke</a>
75 * @author Eric Johnson
76 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
77 * @author Carl A. Dunham
78 *
79 * @since 2.0
80 */
81 public class SoapUIMultiThreadedHttpConnectionManager implements HttpConnectionManager {
82
83
84
85 /*** Log object for this class. */
86 private static final Logger LOG = Logger.getLogger(SoapUIMultiThreadedHttpConnectionManager.class);
87
88 /*** The default maximum number of connections allowed per host */
89 public static final int DEFAULT_MAX_HOST_CONNECTIONS = 2;
90
91 /*** The default maximum number of connections allowed overall */
92 public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 20;
93
94 /***
95 * A mapping from Reference to ConnectionSource. Used to reclaim resources when connections
96 * are lost to the garbage collector.
97 */
98 private static final Map REFERENCE_TO_CONNECTION_SOURCE = new HashMap();
99
100 /***
101 * The reference queue used to track when HttpConnections are lost to the
102 * garbage collector
103 */
104 private static final ReferenceQueue REFERENCE_QUEUE = new ReferenceQueue();
105
106 /***
107 * The thread responsible for handling lost connections.
108 */
109 private static ReferenceQueueThread REFERENCE_QUEUE_THREAD;
110
111 /***
112 * Holds references to all active instances of this class.
113 */
114 private static WeakHashMap ALL_CONNECTION_MANAGERS = new WeakHashMap();
115
116
117
118
119 /***
120 * Shuts down and cleans up resources used by all instances of
121 * MultiThreadedHttpConnectionManager. All static resources are released, all threads are
122 * stopped, and {@link #shutdown()} is called on all live instances of
123 * MultiThreadedHttpConnectionManager.
124 *
125 * @see #shutdown()
126 */
127 public static void shutdownAll() {
128
129 synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
130
131 synchronized (ALL_CONNECTION_MANAGERS) {
132
133
134 SoapUIMultiThreadedHttpConnectionManager[]
135 connManagers = (SoapUIMultiThreadedHttpConnectionManager[])
136 ALL_CONNECTION_MANAGERS.keySet().toArray(
137 new SoapUIMultiThreadedHttpConnectionManager
138 [ALL_CONNECTION_MANAGERS.size()]
139 );
140
141
142
143 for (int i=0; i<connManagers.length; i++) {
144 if (connManagers[i] != null)
145 connManagers[i].shutdown();
146 }
147 }
148
149
150 if (REFERENCE_QUEUE_THREAD != null) {
151 REFERENCE_QUEUE_THREAD.shutdown();
152 REFERENCE_QUEUE_THREAD = null;
153 }
154 REFERENCE_TO_CONNECTION_SOURCE.clear();
155 }
156 }
157
158 /***
159 * Stores the reference to the given connection along with the host config and connection pool.
160 * These values will be used to reclaim resources if the connection is lost to the garbage
161 * collector. This method should be called before a connection is released from the connection
162 * manager.
163 *
164 * <p>A static reference to the connection manager will also be stored. To ensure that
165 * the connection manager can be GCed {@link #removeReferenceToConnection(HttpConnection)}
166 * should be called for all connections that the connection manager is storing a reference
167 * to.</p>
168 *
169 * @param connection the connection to create a reference for
170 * @param hostConfiguration the connection's host config
171 * @param connectionPool the connection pool that created the connection
172 *
173 * @see #removeReferenceToConnection(HttpConnection)
174 */
175 private static void storeReferenceToConnection(
176 HttpConnectionWithReference connection,
177 HostConfiguration hostConfiguration,
178 ConnectionPool connectionPool
179 ) {
180
181 ConnectionSource source = new ConnectionSource();
182 source.connectionPool = connectionPool;
183 source.hostConfiguration = hostConfiguration;
184
185 synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
186
187
188 if (REFERENCE_QUEUE_THREAD == null) {
189 REFERENCE_QUEUE_THREAD = new ReferenceQueueThread();
190 REFERENCE_QUEUE_THREAD.start();
191 }
192
193 REFERENCE_TO_CONNECTION_SOURCE.put(
194 connection.reference,
195 source
196 );
197 }
198 }
199
200 /***
201 * Closes and releases all connections currently checked out of the given connection pool.
202 * @param connectionPool the connection pool to shutdown the connections for
203 */
204 private static void shutdownCheckedOutConnections(ConnectionPool connectionPool) {
205
206
207 ArrayList<HttpConnection> connectionsToClose = new ArrayList<HttpConnection>();
208
209 synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
210
211 Iterator referenceIter = REFERENCE_TO_CONNECTION_SOURCE.keySet().iterator();
212 while (referenceIter.hasNext()) {
213 Reference ref = (Reference) referenceIter.next();
214 ConnectionSource source =
215 (ConnectionSource) REFERENCE_TO_CONNECTION_SOURCE.get(ref);
216 if (source.connectionPool == connectionPool) {
217 referenceIter.remove();
218 HttpConnection connection = (HttpConnection) ref.get();
219 if (connection != null) {
220 connectionsToClose.add(connection);
221 }
222 }
223 }
224 }
225
226
227
228 for (Iterator i = connectionsToClose.iterator(); i.hasNext();) {
229 HttpConnection connection = (HttpConnection) i.next();
230 connection.close();
231
232
233 connection.setHttpConnectionManager(null);
234 connection.releaseConnection();
235 }
236 }
237
238 /***
239 * Removes the reference being stored for the given connection. This method should be called
240 * when the connection manager again has a direct reference to the connection.
241 *
242 * @param connection the connection to remove the reference for
243 *
244 * @see #storeReferenceToConnection(HttpConnection, HostConfiguration, ConnectionPool)
245 */
246 private static void removeReferenceToConnection(HttpConnectionWithReference connection) {
247
248 synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
249 REFERENCE_TO_CONNECTION_SOURCE.remove(connection.reference);
250 }
251 }
252
253
254
255
256 /***
257 * Collection of parameters associated with this connection manager.
258 */
259 private HttpConnectionManagerParams params = new HttpConnectionManagerParams();
260
261 /*** Connection Pool */
262 private ConnectionPool connectionPool;
263
264 private volatile boolean shutdown = false;
265
266
267
268
269 /***
270 * No-args constructor
271 */
272 public SoapUIMultiThreadedHttpConnectionManager() {
273 this.connectionPool = new ConnectionPool();
274 synchronized(ALL_CONNECTION_MANAGERS) {
275 ALL_CONNECTION_MANAGERS.put(this, null);
276 }
277 }
278
279
280
281
282 /***
283 * Shuts down the connection manager and releases all resources. All connections associated
284 * with this class will be closed and released.
285 *
286 * <p>The connection manager can no longer be used once shut down.
287 *
288 * <p>Calling this method more than once will have no effect.
289 */
290 public synchronized void shutdown() {
291 synchronized (connectionPool) {
292 if (!shutdown) {
293 shutdown = true;
294 connectionPool.shutdown();
295 }
296 }
297 }
298
299 /***
300 * Gets the staleCheckingEnabled value to be set on HttpConnections that are created.
301 *
302 * @return <code>true</code> if stale checking will be enabled on HttpConnections
303 *
304 * @see HttpConnection#isStaleCheckingEnabled()
305 *
306 * @deprecated Use {@link HttpConnectionManagerParams#isStaleCheckingEnabled()},
307 * {@link HttpConnectionManager#getParams()}.
308 */
309 public boolean isConnectionStaleCheckingEnabled() {
310 return this.params.isStaleCheckingEnabled();
311 }
312
313 /***
314 * Sets the staleCheckingEnabled value to be set on HttpConnections that are created.
315 *
316 * @param connectionStaleCheckingEnabled <code>true</code> if stale checking will be enabled
317 * on HttpConnections
318 *
319 * @see HttpConnection#setStaleCheckingEnabled(boolean)
320 *
321 * @deprecated Use {@link HttpConnectionManagerParams#setStaleCheckingEnabled(boolean)},
322 * {@link HttpConnectionManager#getParams()}.
323 */
324 public void setConnectionStaleCheckingEnabled(boolean connectionStaleCheckingEnabled) {
325 this.params.setStaleCheckingEnabled(connectionStaleCheckingEnabled);
326 }
327
328 /***
329 * Sets the maximum number of connections allowed for a given
330 * HostConfiguration. Per RFC 2616 section 8.1.4, this value defaults to 2.
331 *
332 * @param maxHostConnections the number of connections allowed for each
333 * hostConfiguration
334 *
335 * @deprecated Use {@link HttpConnectionManagerParams#setDefaultMaxConnectionsPerHost(int)},
336 * {@link HttpConnectionManager#getParams()}.
337 */
338 public void setMaxConnectionsPerHost(int maxHostConnections) {
339 this.params.setDefaultMaxConnectionsPerHost(maxHostConnections);
340 }
341
342 /***
343 * Gets the maximum number of connections allowed for a given
344 * hostConfiguration.
345 *
346 * @return The maximum number of connections allowed for a given
347 * hostConfiguration.
348 *
349 * @deprecated Use {@link HttpConnectionManagerParams#getDefaultMaxConnectionsPerHost()},
350 * {@link HttpConnectionManager#getParams()}.
351 */
352 public int getMaxConnectionsPerHost() {
353 return this.params.getDefaultMaxConnectionsPerHost();
354 }
355
356 /***
357 * Sets the maximum number of connections allowed for this connection manager.
358 *
359 * @param maxTotalConnections the maximum number of connections allowed
360 *
361 * @deprecated Use {@link HttpConnectionManagerParams#setMaxTotalConnections(int)},
362 * {@link HttpConnectionManager#getParams()}.
363 */
364 public void setMaxTotalConnections(int maxTotalConnections) {
365 this.params.setMaxTotalConnections(maxTotalConnections);
366 }
367
368 /***
369 * Gets the maximum number of connections allowed for this connection manager.
370 *
371 * @return The maximum number of connections allowed
372 *
373 * @deprecated Use {@link HttpConnectionManagerParams#getMaxTotalConnections()},
374 * {@link HttpConnectionManager#getParams()}.
375 */
376 public int getMaxTotalConnections() {
377 return this.params.getMaxTotalConnections();
378 }
379
380 /***
381 * @see HttpConnectionManager#getConnection(HostConfiguration)
382 */
383 public HttpConnection getConnection(HostConfiguration hostConfiguration) {
384
385 while (true) {
386 try {
387 return getConnectionWithTimeout(hostConfiguration, 0);
388 } catch (ConnectionPoolTimeoutException e) {
389
390
391
392 LOG.debug(
393 "Unexpected exception while waiting for connection",
394 e
395 );
396 }
397 }
398 }
399
400 /***
401 * Gets a connection or waits if one is not available. A connection is
402 * available if one exists that is not being used or if fewer than
403 * maxHostConnections have been created in the connectionPool, and fewer
404 * than maxTotalConnections have been created in all connectionPools.
405 *
406 * @param hostConfiguration The host configuration specifying the connection
407 * details.
408 * @param timeout the number of milliseconds to wait for a connection, 0 to
409 * wait indefinitely
410 *
411 * @return HttpConnection an available connection
412 *
413 * @throws HttpException if a connection does not become available in
414 * 'timeout' milliseconds
415 *
416 * @since 3.0
417 */
418 public HttpConnection getConnectionWithTimeout(HostConfiguration hostConfiguration,
419 long timeout) throws ConnectionPoolTimeoutException {
420
421 LOG.trace("enter HttpConnectionManager.getConnectionWithTimeout(HostConfiguration, long)");
422
423 if (hostConfiguration == null) {
424 throw new IllegalArgumentException("hostConfiguration is null");
425 }
426
427 if (LOG.isDebugEnabled()) {
428 LOG.debug("HttpConnectionManager.getConnection: config = "
429 + hostConfiguration + ", timeout = " + timeout);
430 }
431
432 final HttpConnection conn = doGetConnection(hostConfiguration, timeout);
433 conn.getParams().setParameter( SoapUIHostConfiguration.SOAPUI_SSL_CONFIG,
434 hostConfiguration.getParams().getParameter( SoapUIHostConfiguration.SOAPUI_SSL_CONFIG ));
435
436
437
438 return new HttpConnectionAdapter(conn);
439 }
440
441 /***
442 * @see HttpConnectionManager#getConnection(HostConfiguration, long)
443 *
444 * @deprecated Use #getConnectionWithTimeout(HostConfiguration, long)
445 */
446 public HttpConnection getConnection(HostConfiguration hostConfiguration,
447 long timeout) throws HttpException {
448
449 LOG.trace("enter HttpConnectionManager.getConnection(HostConfiguration, long)");
450 try {
451 return getConnectionWithTimeout(hostConfiguration, timeout);
452 } catch(ConnectionPoolTimeoutException e) {
453 throw new HttpException(e.getMessage());
454 }
455 }
456
457 private HttpConnection doGetConnection(HostConfiguration hostConfiguration,
458 long timeout) throws ConnectionPoolTimeoutException {
459
460 HttpConnection connection = null;
461
462 int maxHostConnections = this.params.getMaxConnectionsPerHost(hostConfiguration);
463 int maxTotalConnections = this.params.getMaxTotalConnections();
464
465 synchronized (connectionPool) {
466
467
468
469 hostConfiguration = new SoapUIHostConfiguration(hostConfiguration);
470 HostConnectionPool hostPool = connectionPool.getHostPool(hostConfiguration, true);
471 WaitingThread waitingThread = null;
472
473 boolean useTimeout = (timeout > 0);
474 long timeToWait = timeout;
475 long startWait = 0;
476 long endWait = 0;
477
478 while (connection == null) {
479
480 if (shutdown) {
481 throw new IllegalStateException("Connection factory has been shutdown.");
482 }
483
484
485
486 if (hostPool.freeConnections.size() > 0) {
487 connection = connectionPool.getFreeConnection(hostConfiguration);
488
489
490
491 } else if ((hostPool.numConnections < maxHostConnections)
492 && (connectionPool.numConnections < maxTotalConnections)) {
493
494 connection = connectionPool.createConnection(hostConfiguration);
495
496
497
498
499 } else if ((hostPool.numConnections < maxHostConnections)
500 && (connectionPool.freeConnections.size() > 0)) {
501
502 connectionPool.deleteLeastUsedConnection();
503 connection = connectionPool.createConnection(hostConfiguration);
504
505
506
507
508 } else {
509
510
511
512 try {
513
514 if (useTimeout && timeToWait <= 0) {
515 throw new ConnectionPoolTimeoutException("Timeout waiting for connection");
516 }
517
518 if (LOG.isDebugEnabled()) {
519 LOG.debug("Unable to get a connection, waiting..., hostConfig=" + hostConfiguration);
520 }
521
522 if (waitingThread == null) {
523 waitingThread = new WaitingThread();
524 waitingThread.hostConnectionPool = hostPool;
525 waitingThread.thread = Thread.currentThread();
526 } else {
527 waitingThread.interruptedByConnectionPool = false;
528 }
529
530 if (useTimeout) {
531 startWait = System.currentTimeMillis();
532 }
533
534 hostPool.waitingThreads.addLast(waitingThread);
535 connectionPool.waitingThreads.addLast(waitingThread);
536 connectionPool.wait(timeToWait);
537 } catch (InterruptedException e) {
538 if (!waitingThread.interruptedByConnectionPool) {
539 LOG.debug("Interrupted while waiting for connection", e);
540 throw new IllegalThreadStateException(
541 "Interrupted while waiting in MultiThreadedHttpConnectionManager");
542 }
543
544
545
546 } finally {
547 if (!waitingThread.interruptedByConnectionPool) {
548
549
550
551 hostPool.waitingThreads.remove(waitingThread);
552 connectionPool.waitingThreads.remove(waitingThread);
553 }
554
555 if (useTimeout) {
556 endWait = System.currentTimeMillis();
557 timeToWait -= (endWait - startWait);
558 }
559 }
560 }
561 }
562 }
563 return connection;
564 }
565
566 /***
567 * Gets the total number of pooled connections for the given host configuration. This
568 * is the total number of connections that have been created and are still in use
569 * by this connection manager for the host configuration. This value will
570 * not exceed the {@link #getMaxConnectionsPerHost() maximum number of connections per
571 * host}.
572 *
573 * @param hostConfiguration The host configuration
574 * @return The total number of pooled connections
575 */
576 public int getConnectionsInPool(HostConfiguration hostConfiguration) {
577 synchronized (connectionPool) {
578 HostConnectionPool hostPool = connectionPool.getHostPool(hostConfiguration, false);
579 return (hostPool != null) ? hostPool.numConnections : 0;
580 }
581 }
582
583 /***
584 * Gets the total number of pooled connections. This is the total number of
585 * connections that have been created and are still in use by this connection
586 * manager. This value will not exceed the {@link #getMaxTotalConnections()
587 * maximum number of connections}.
588 *
589 * @return the total number of pooled connections
590 */
591 public int getConnectionsInPool() {
592 synchronized (connectionPool) {
593 return connectionPool.numConnections;
594 }
595 }
596
597 /***
598 * Gets the number of connections in use for this configuration.
599 *
600 * @param hostConfiguration the key that connections are tracked on
601 * @return the number of connections in use
602 *
603 * @deprecated Use {@link #getConnectionsInPool(HostConfiguration)}
604 */
605 public int getConnectionsInUse(HostConfiguration hostConfiguration) {
606 return getConnectionsInPool(hostConfiguration);
607 }
608
609 /***
610 * Gets the total number of connections in use.
611 *
612 * @return the total number of connections in use
613 *
614 * @deprecated Use {@link #getConnectionsInPool()}
615 */
616 public int getConnectionsInUse() {
617 return getConnectionsInPool();
618 }
619
620 /***
621 * Deletes all closed connections. Only connections currently owned by the connection
622 * manager are processed.
623 *
624 * @see HttpConnection#isOpen()
625 *
626 * @since 3.0
627 */
628 public void deleteClosedConnections() {
629 connectionPool.deleteClosedConnections();
630 }
631
632 /***
633 * @since 3.0
634 */
635 public void closeIdleConnections(long idleTimeout) {
636 connectionPool.closeIdleConnections(idleTimeout);
637 deleteClosedConnections();
638 }
639
640 /***
641 * Make the given HttpConnection available for use by other requests.
642 * If another thread is blocked in getConnection() that could use this
643 * connection, it will be woken up.
644 *
645 * @param conn the HttpConnection to make available.
646 */
647 public void releaseConnection(HttpConnection conn) {
648 LOG.trace("enter HttpConnectionManager.releaseConnection(HttpConnection)");
649
650 if (conn instanceof HttpConnectionAdapter) {
651
652 conn = ((HttpConnectionAdapter) conn).getWrappedConnection();
653 } else {
654
655
656 }
657
658
659 finishLastResponse(conn);
660
661 connectionPool.freeConnection(conn);
662 }
663
664 private void finishLastResponse( HttpConnection conn )
665 {
666 InputStream lastResponse = conn.getLastResponseInputStream();
667 if (lastResponse != null) {
668 conn.setLastResponseInputStream(null);
669 try {
670 lastResponse.close();
671 } catch (IOException ioe) {
672 conn.close();
673 }
674 }
675 }
676
677 /***
678 * Gets the host configuration for a connection.
679 * @param conn the connection to get the configuration of
680 * @return a new HostConfiguration
681 */
682 private HostConfiguration configurationForConnection(HttpConnection conn) {
683
684 HostConfiguration connectionConfiguration = new SoapUIHostConfiguration();
685
686 connectionConfiguration.setHost(
687 conn.getHost(),
688 conn.getPort(),
689 conn.getProtocol()
690 );
691 if (conn.getLocalAddress() != null) {
692 connectionConfiguration.setLocalAddress(conn.getLocalAddress());
693 }
694 if (conn.getProxyHost() != null) {
695 connectionConfiguration.setProxy(conn.getProxyHost(), conn.getProxyPort());
696 }
697
698 if( conn.getParams().getParameter( SoapUIHostConfiguration.SOAPUI_SSL_CONFIG ) != null )
699 connectionConfiguration.getParams().setParameter( SoapUIHostConfiguration.SOAPUI_SSL_CONFIG,
700 conn.getParams().getParameter( SoapUIHostConfiguration.SOAPUI_SSL_CONFIG ));
701
702 return connectionConfiguration;
703 }
704
705 /***
706 * Returns {@link HttpConnectionManagerParams parameters} associated
707 * with this connection manager.
708 *
709 * @since 3.0
710 *
711 * @see HttpConnectionManagerParams
712 */
713 public HttpConnectionManagerParams getParams() {
714 return this.params;
715 }
716
717 /***
718 * Assigns {@link HttpConnectionManagerParams parameters} for this
719 * connection manager.
720 *
721 * @since 3.0
722 *
723 * @see HttpConnectionManagerParams
724 */
725 public void setParams(final HttpConnectionManagerParams params) {
726 if (params == null) {
727 throw new IllegalArgumentException("Parameters may not be null");
728 }
729 this.params = params;
730 }
731
732 /***
733 * Global Connection Pool, including per-host pools
734 */
735 private class ConnectionPool {
736
737 /*** The list of free connections */
738 private LinkedList freeConnections = new LinkedList();
739
740 /*** The list of WaitingThreads waiting for a connection */
741 private LinkedList waitingThreads = new LinkedList();
742
743 /***
744 * Map where keys are {@link HostConfiguration}s and values are {@link
745 * HostConnectionPool}s
746 */
747 private final Map mapHosts = new HashMap();
748
749 private IdleConnectionHandler idleConnectionHandler = new IdleConnectionHandler();
750
751 /*** The number of created connections */
752 private int numConnections = 0;
753
754 /***
755 * Cleans up all connection pool resources.
756 */
757 public synchronized void shutdown() {
758
759
760 Iterator iter = freeConnections.iterator();
761 while (iter.hasNext()) {
762 HttpConnection conn = (HttpConnection) iter.next();
763 iter.remove();
764 conn.close();
765 }
766
767
768 shutdownCheckedOutConnections(this);
769
770
771 iter = waitingThreads.iterator();
772 while (iter.hasNext()) {
773 WaitingThread waiter = (WaitingThread) iter.next();
774 iter.remove();
775 waiter.interruptedByConnectionPool = true;
776 waiter.thread.interrupt();
777 }
778
779
780 mapHosts.clear();
781
782
783 idleConnectionHandler.removeAll();
784 }
785
786 /***
787 * Creates a new connection and returns it for use of the calling method.
788 *
789 * @param hostConfiguration the configuration for the connection
790 * @return a new connection or <code>null</code> if none are available
791 */
792 public synchronized HttpConnection createConnection(HostConfiguration hostConfiguration) {
793 HostConnectionPool hostPool = getHostPool(hostConfiguration, true);
794 if (LOG.isDebugEnabled()) {
795 LOG.debug("Allocating new connection, hostConfig=" + hostConfiguration);
796 }
797 HttpConnectionWithReference connection = new HttpConnectionWithReference(
798 hostConfiguration);
799 connection.getParams().setDefaults(SoapUIMultiThreadedHttpConnectionManager.this.params);
800 connection.setHttpConnectionManager(SoapUIMultiThreadedHttpConnectionManager.this);
801 numConnections++;
802 hostPool.numConnections++;
803
804
805
806 storeReferenceToConnection(connection, hostConfiguration, this);
807 return connection;
808 }
809
810 /***
811 * Handles cleaning up for a lost connection with the given config. Decrements any
812 * connection counts and notifies waiting threads, if appropriate.
813 *
814 * @param config the host configuration of the connection that was lost
815 */
816 public synchronized void handleLostConnection(HostConfiguration config) {
817 HostConnectionPool hostPool = getHostPool(config, true);
818 hostPool.numConnections--;
819 if ((hostPool.numConnections == 0) &&
820 hostPool.waitingThreads.isEmpty()) {
821
822 mapHosts.remove(config);
823 }
824
825 numConnections--;
826 notifyWaitingThread(config);
827 }
828
829 /***
830 * Get the pool (list) of connections available for the given hostConfig.
831 *
832 * @param hostConfiguration the configuraton for the connection pool
833 * @param create <code>true</code> to create a pool if not found,
834 * <code>false</code> to return <code>null</code>
835 *
836 * @return a pool (list) of connections available for the given config,
837 * or <code>null</code> if neither found nor created
838 */
839 public synchronized HostConnectionPool getHostPool(HostConfiguration hostConfiguration, boolean create) {
840 LOG.trace("enter HttpConnectionManager.ConnectionPool.getHostPool(HostConfiguration)");
841
842
843 HostConnectionPool listConnections = (HostConnectionPool)
844 mapHosts.get(hostConfiguration);
845 if ((listConnections == null) && create) {
846
847 listConnections = new HostConnectionPool();
848 listConnections.hostConfiguration = hostConfiguration;
849 mapHosts.put(hostConfiguration, listConnections);
850 }
851
852 return listConnections;
853 }
854
855 /***
856 * If available, get a free connection for this host
857 *
858 * @param hostConfiguration the configuraton for the connection pool
859 * @return an available connection for the given config
860 */
861 public synchronized HttpConnection getFreeConnection(HostConfiguration hostConfiguration) {
862
863 HttpConnectionWithReference connection = null;
864
865 HostConnectionPool hostPool = getHostPool(hostConfiguration, false);
866
867 if ((hostPool != null) && (hostPool.freeConnections.size() > 0)) {
868 connection = (HttpConnectionWithReference) hostPool.freeConnections.removeLast();
869 freeConnections.remove(connection);
870
871
872 storeReferenceToConnection(connection, hostConfiguration, this);
873 if (LOG.isDebugEnabled()) {
874 LOG.debug("Getting free connection, hostConfig=" + hostConfiguration);
875 }
876
877
878 idleConnectionHandler.remove(connection);
879 } else if (LOG.isDebugEnabled()) {
880 LOG.debug("There were no free connections to get, hostConfig="
881 + hostConfiguration);
882 }
883 return connection;
884 }
885
886 /***
887 * Deletes all closed connections.
888 */
889 public synchronized void deleteClosedConnections() {
890
891 Iterator iter = freeConnections.iterator();
892
893 while (iter.hasNext()) {
894 HttpConnection conn = (HttpConnection) iter.next();
895 if (!conn.isOpen()) {
896 iter.remove();
897 deleteConnection(conn);
898 }
899 }
900 }
901
902 /***
903 * Closes idle connections.
904 * @param idleTimeout
905 */
906 public synchronized void closeIdleConnections(long idleTimeout) {
907 idleConnectionHandler.closeIdleConnections(idleTimeout);
908 }
909
910 /***
911 * Deletes the given connection. This will remove all reference to the connection
912 * so that it can be GCed.
913 *
914 * <p><b>Note:</b> Does not remove the connection from the freeConnections list. It
915 * is assumed that the caller has already handled this case.</p>
916 *
917 * @param connection The connection to delete
918 */
919 private synchronized void deleteConnection(HttpConnection connection) {
920
921 HostConfiguration connectionConfiguration = configurationForConnection(connection);
922
923 if (LOG.isDebugEnabled()) {
924 LOG.debug("Reclaiming connection, hostConfig=" + connectionConfiguration);
925 }
926
927 connection.close();
928
929 HostConnectionPool hostPool = getHostPool(connectionConfiguration, true);
930
931 hostPool.freeConnections.remove(connection);
932 hostPool.numConnections--;
933 numConnections--;
934 if ((hostPool.numConnections == 0) &&
935 hostPool.waitingThreads.isEmpty()) {
936
937 mapHosts.remove(connectionConfiguration);
938 }
939
940
941 idleConnectionHandler.remove(connection);
942 }
943
944 /***
945 * Close and delete an old, unused connection to make room for a new one.
946 */
947 public synchronized void deleteLeastUsedConnection() {
948
949 HttpConnection connection = (HttpConnection) freeConnections.removeFirst();
950
951 if (connection != null) {
952 deleteConnection(connection);
953 } else if (LOG.isDebugEnabled()) {
954 LOG.debug("Attempted to reclaim an unused connection but there were none.");
955 }
956 }
957
958 /***
959 * Notifies a waiting thread that a connection for the given configuration is
960 * available.
961 * @param configuration the host config to use for notifying
962 * @see #notifyWaitingThread(HostConnectionPool)
963 */
964 public synchronized void notifyWaitingThread(HostConfiguration configuration) {
965 notifyWaitingThread(getHostPool(configuration, true));
966 }
967
968 /***
969 * Notifies a waiting thread that a connection for the given configuration is
970 * available. This will wake a thread waiting in this host pool or if there is not
971 * one a thread in the connection pool will be notified.
972 *
973 * @param hostPool the host pool to use for notifying
974 */
975 public synchronized void notifyWaitingThread(HostConnectionPool hostPool) {
976
977
978
979
980 WaitingThread waitingThread = null;
981
982 if (hostPool.waitingThreads.size() > 0) {
983 if (LOG.isDebugEnabled()) {
984 LOG.debug("Notifying thread waiting on host pool, hostConfig="
985 + hostPool.hostConfiguration);
986 }
987 waitingThread = (WaitingThread) hostPool.waitingThreads.removeFirst();
988 waitingThreads.remove(waitingThread);
989 } else if (waitingThreads.size() > 0) {
990 if (LOG.isDebugEnabled()) {
991 LOG.debug("No-one waiting on host pool, notifying next waiting thread.");
992 }
993 waitingThread = (WaitingThread) waitingThreads.removeFirst();
994 waitingThread.hostConnectionPool.waitingThreads.remove(waitingThread);
995 } else if (LOG.isDebugEnabled()) {
996 LOG.debug("Notifying no-one, there are no waiting threads");
997 }
998
999 if (waitingThread != null) {
1000 waitingThread.interruptedByConnectionPool = true;
1001 waitingThread.thread.interrupt();
1002 }
1003 }
1004
1005 /***
1006 * Marks the given connection as free.
1007 * @param conn a connection that is no longer being used
1008 */
1009 public void freeConnection(HttpConnection conn) {
1010
1011 HostConfiguration connectionConfiguration = configurationForConnection(conn);
1012
1013 if (LOG.isDebugEnabled()) {
1014 LOG.debug("Freeing connection, hostConfig=" + connectionConfiguration);
1015 }
1016
1017 synchronized (this) {
1018
1019 if (shutdown) {
1020
1021
1022 conn.close();
1023 return;
1024 }
1025
1026 HostConnectionPool hostPool = getHostPool(connectionConfiguration, true);
1027
1028
1029 hostPool.freeConnections.add(conn);
1030 if (hostPool.numConnections == 0) {
1031
1032 LOG.error("Host connection pool not found, hostConfig="
1033 + connectionConfiguration);
1034 hostPool.numConnections = 1;
1035 }
1036
1037 freeConnections.add(conn);
1038
1039
1040 removeReferenceToConnection((HttpConnectionWithReference) conn);
1041 if (numConnections == 0) {
1042
1043 LOG.error("Host connection pool not found, hostConfig="
1044 + connectionConfiguration);
1045 numConnections = 1;
1046 }
1047
1048
1049 idleConnectionHandler.add(conn);
1050
1051 notifyWaitingThread(hostPool);
1052 }
1053 }
1054 }
1055
1056 /***
1057 * A simple struct-like class to combine the objects needed to release a connection's
1058 * resources when claimed by the garbage collector.
1059 */
1060 private static class ConnectionSource {
1061
1062 /*** The connection pool that created the connection */
1063 public ConnectionPool connectionPool;
1064
1065 /*** The connection's host configuration */
1066 public HostConfiguration hostConfiguration;
1067 }
1068
1069 /***
1070 * A simple struct-like class to combine the connection list and the count
1071 * of created connections.
1072 */
1073 private static class HostConnectionPool {
1074 /*** The hostConfig this pool is for */
1075 public HostConfiguration hostConfiguration;
1076
1077 /*** The list of free connections */
1078 public LinkedList freeConnections = new LinkedList();
1079
1080 /*** The list of WaitingThreads for this host */
1081 public LinkedList waitingThreads = new LinkedList();
1082
1083 /*** The number of created connections */
1084 public int numConnections = 0;
1085 }
1086
1087 /***
1088 * A simple struct-like class to combine the waiting thread and the connection
1089 * pool it is waiting on.
1090 */
1091 private static class WaitingThread {
1092 /*** The thread that is waiting for a connection */
1093 public Thread thread;
1094
1095 /*** The connection pool the thread is waiting for */
1096 public HostConnectionPool hostConnectionPool;
1097
1098 /*** Flag to indicate if the thread was interrupted by the ConnectionPool. Set
1099 * to true inside {@link ConnectionPool#notifyWaitingThread(HostConnectionPool)}
1100 * before the thread is interrupted. */
1101 public boolean interruptedByConnectionPool = false;
1102 }
1103
1104 /***
1105 * A thread for listening for HttpConnections reclaimed by the garbage
1106 * collector.
1107 */
1108 private static class ReferenceQueueThread extends Thread {
1109
1110 private volatile boolean shutdown = false;
1111
1112 /***
1113 * Create an instance and make this a daemon thread.
1114 */
1115 public ReferenceQueueThread() {
1116 setDaemon(true);
1117 setName("MultiThreadedHttpConnectionManager cleanup");
1118 }
1119
1120 public void shutdown() {
1121 this.shutdown = true;
1122 this.interrupt();
1123 }
1124
1125 /***
1126 * Handles cleaning up for the given connection reference.
1127 *
1128 * @param ref the reference to clean up
1129 */
1130 private void handleReference(Reference ref) {
1131
1132 ConnectionSource source = null;
1133
1134 synchronized (REFERENCE_TO_CONNECTION_SOURCE) {
1135 source = (ConnectionSource) REFERENCE_TO_CONNECTION_SOURCE.remove(ref);
1136 }
1137
1138
1139 if (source != null) {
1140 if (LOG.isDebugEnabled()) {
1141 LOG.debug(
1142 "Connection reclaimed by garbage collector, hostConfig="
1143 + source.hostConfiguration);
1144 }
1145
1146 source.connectionPool.handleLostConnection(source.hostConfiguration);
1147 }
1148 }
1149
1150 /***
1151 * Start execution.
1152 */
1153 public void run() {
1154 while (!shutdown) {
1155 try {
1156
1157 Reference ref = REFERENCE_QUEUE.remove();
1158 if (ref != null) {
1159 handleReference(ref);
1160 }
1161 } catch (InterruptedException e) {
1162 LOG.debug("ReferenceQueueThread interrupted", e);
1163 }
1164 }
1165 }
1166
1167 }
1168
1169 /***
1170 * A connection that keeps a reference to itself.
1171 */
1172 public static class HttpConnectionWithReference extends HttpConnection implements ConnectionWithSocket {
1173
1174 public WeakReference reference = new WeakReference(this, REFERENCE_QUEUE);
1175
1176 /***
1177 * @param hostConfiguration
1178 */
1179 public HttpConnectionWithReference(HostConfiguration hostConfiguration) {
1180 super(hostConfiguration);
1181 }
1182
1183 public Socket getConnectionSocket()
1184 {
1185 return getSocket();
1186 }
1187
1188 }
1189
1190 /***
1191 * An HttpConnection wrapper that ensures a connection cannot be used
1192 * once released.
1193 */
1194 public static class HttpConnectionAdapter extends HttpConnection implements ConnectionWithSocket {
1195
1196
1197 private HttpConnection wrappedConnection;
1198
1199 /***
1200 * Creates a new HttpConnectionAdapter.
1201 * @param connection the connection to be wrapped
1202 */
1203 public HttpConnectionAdapter(HttpConnection connection) {
1204 super(connection.getHost(), connection.getPort(), connection.getProtocol());
1205 this.wrappedConnection = connection;
1206 }
1207
1208 /***
1209 * Tests if the wrapped connection is still available.
1210 * @return boolean
1211 */
1212 protected boolean hasConnection() {
1213 return wrappedConnection != null;
1214 }
1215
1216 /***
1217 * @return HttpConnection
1218 */
1219 HttpConnection getWrappedConnection() {
1220 return wrappedConnection;
1221 }
1222
1223 public void close() {
1224 if (hasConnection()) {
1225 wrappedConnection.close();
1226 } else {
1227
1228 }
1229 }
1230
1231 public InetAddress getLocalAddress() {
1232 if (hasConnection()) {
1233 return wrappedConnection.getLocalAddress();
1234 } else {
1235 return null;
1236 }
1237 }
1238
1239 /***
1240 * @deprecated
1241 */
1242 public boolean isStaleCheckingEnabled() {
1243 if (hasConnection()) {
1244 return wrappedConnection.isStaleCheckingEnabled();
1245 } else {
1246 return false;
1247 }
1248 }
1249
1250 public void setLocalAddress(InetAddress localAddress) {
1251 if (hasConnection()) {
1252 wrappedConnection.setLocalAddress(localAddress);
1253 } else {
1254 throw new IllegalStateException("Connection has been released");
1255 }
1256 }
1257
1258 /***
1259 * @deprecated
1260 */
1261 public void setStaleCheckingEnabled(boolean staleCheckEnabled) {
1262 if (hasConnection()) {
1263 wrappedConnection.setStaleCheckingEnabled(staleCheckEnabled);
1264 } else {
1265 throw new IllegalStateException("Connection has been released");
1266 }
1267 }
1268
1269 public String getHost() {
1270 if (hasConnection()) {
1271 return wrappedConnection.getHost();
1272 } else {
1273 return null;
1274 }
1275 }
1276
1277 public HttpConnectionManager getHttpConnectionManager() {
1278 if (hasConnection()) {
1279 return wrappedConnection.getHttpConnectionManager();
1280 } else {
1281 return null;
1282 }
1283 }
1284
1285 public InputStream getLastResponseInputStream() {
1286 if (hasConnection()) {
1287 return wrappedConnection.getLastResponseInputStream();
1288 } else {
1289 return null;
1290 }
1291 }
1292
1293 public int getPort() {
1294 if (hasConnection()) {
1295 return wrappedConnection.getPort();
1296 } else {
1297 return -1;
1298 }
1299 }
1300
1301 public Protocol getProtocol() {
1302 if (hasConnection()) {
1303 return wrappedConnection.getProtocol();
1304 } else {
1305 return null;
1306 }
1307 }
1308
1309 public String getProxyHost() {
1310 if (hasConnection()) {
1311 return wrappedConnection.getProxyHost();
1312 } else {
1313 return null;
1314 }
1315 }
1316
1317 public int getProxyPort() {
1318 if (hasConnection()) {
1319 return wrappedConnection.getProxyPort();
1320 } else {
1321 return -1;
1322 }
1323 }
1324
1325 public OutputStream getRequestOutputStream()
1326 throws IOException, IllegalStateException {
1327 if (hasConnection()) {
1328 return wrappedConnection.getRequestOutputStream();
1329 } else {
1330 return null;
1331 }
1332 }
1333
1334 public InputStream getResponseInputStream()
1335 throws IOException, IllegalStateException {
1336 if (hasConnection()) {
1337 return wrappedConnection.getResponseInputStream();
1338 } else {
1339 return null;
1340 }
1341 }
1342
1343 public boolean isOpen() {
1344 if (hasConnection()) {
1345 return wrappedConnection.isOpen();
1346 } else {
1347 return false;
1348 }
1349 }
1350
1351 public boolean closeIfStale() throws IOException {
1352 if (hasConnection()) {
1353 return wrappedConnection.closeIfStale();
1354 } else {
1355 return false;
1356 }
1357 }
1358
1359 public boolean isProxied() {
1360 if (hasConnection()) {
1361 return wrappedConnection.isProxied();
1362 } else {
1363 return false;
1364 }
1365 }
1366
1367 public boolean isResponseAvailable() throws IOException {
1368 if (hasConnection()) {
1369 return wrappedConnection.isResponseAvailable();
1370 } else {
1371 return false;
1372 }
1373 }
1374
1375 public boolean isResponseAvailable(int timeout) throws IOException {
1376 if (hasConnection()) {
1377 return wrappedConnection.isResponseAvailable(timeout);
1378 } else {
1379 return false;
1380 }
1381 }
1382
1383 public boolean isSecure() {
1384 if (hasConnection()) {
1385 return wrappedConnection.isSecure();
1386 } else {
1387 return false;
1388 }
1389 }
1390
1391 public boolean isTransparent() {
1392 if (hasConnection()) {
1393 return wrappedConnection.isTransparent();
1394 } else {
1395 return false;
1396 }
1397 }
1398
1399 public void open() throws IOException {
1400 if (hasConnection()) {
1401 wrappedConnection.open();
1402 } else {
1403 throw new IllegalStateException("Connection has been released");
1404 }
1405 }
1406
1407 /***
1408 * @deprecated
1409 */
1410 public void print(String data)
1411 throws IOException, IllegalStateException {
1412 if (hasConnection()) {
1413 wrappedConnection.print(data);
1414 } else {
1415 throw new IllegalStateException("Connection has been released");
1416 }
1417 }
1418
1419 public void printLine()
1420 throws IOException, IllegalStateException {
1421 if (hasConnection()) {
1422 wrappedConnection.printLine();
1423 } else {
1424 throw new IllegalStateException("Connection has been released");
1425 }
1426 }
1427
1428 /***
1429 * @deprecated
1430 */
1431 public void printLine(String data)
1432 throws IOException, IllegalStateException {
1433 if (hasConnection()) {
1434 wrappedConnection.printLine(data);
1435 } else {
1436 throw new IllegalStateException("Connection has been released");
1437 }
1438 }
1439
1440 /***
1441 * @deprecated
1442 */
1443 public String readLine() throws IOException, IllegalStateException {
1444 if (hasConnection()) {
1445 return wrappedConnection.readLine();
1446 } else {
1447 throw new IllegalStateException("Connection has been released");
1448 }
1449 }
1450
1451 public String readLine(String charset) throws IOException, IllegalStateException {
1452 if (hasConnection()) {
1453 return wrappedConnection.readLine(charset);
1454 } else {
1455 throw new IllegalStateException("Connection has been released");
1456 }
1457 }
1458
1459 public void releaseConnection() {
1460 if (!isLocked() && hasConnection()) {
1461 HttpConnection wrappedConnection = this.wrappedConnection;
1462 this.wrappedConnection = null;
1463 wrappedConnection.releaseConnection();
1464 } else {
1465
1466 }
1467 }
1468
1469 /***
1470 * @deprecated
1471 */
1472 public void setConnectionTimeout(int timeout) {
1473 if (hasConnection()) {
1474 wrappedConnection.setConnectionTimeout(timeout);
1475 } else {
1476
1477 }
1478 }
1479
1480 public void setHost(String host) throws IllegalStateException {
1481 if (hasConnection()) {
1482 wrappedConnection.setHost(host);
1483 } else {
1484
1485 }
1486 }
1487
1488 public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) {
1489 if (hasConnection()) {
1490 wrappedConnection.setHttpConnectionManager(httpConnectionManager);
1491 } else {
1492
1493 }
1494 }
1495
1496 public void setLastResponseInputStream(InputStream inStream) {
1497 if (hasConnection()) {
1498 wrappedConnection.setLastResponseInputStream(inStream);
1499 } else {
1500
1501 }
1502 }
1503
1504 public void setPort(int port) throws IllegalStateException {
1505 if (hasConnection()) {
1506 wrappedConnection.setPort(port);
1507 } else {
1508
1509 }
1510 }
1511
1512 public void setProtocol(Protocol protocol) {
1513 if (hasConnection()) {
1514 wrappedConnection.setProtocol(protocol);
1515 } else {
1516
1517 }
1518 }
1519
1520 public void setProxyHost(String host) throws IllegalStateException {
1521 if (hasConnection()) {
1522 wrappedConnection.setProxyHost(host);
1523 } else {
1524
1525 }
1526 }
1527
1528 public void setProxyPort(int port) throws IllegalStateException {
1529 if (hasConnection()) {
1530 wrappedConnection.setProxyPort(port);
1531 } else {
1532
1533 }
1534 }
1535
1536 /***
1537 * @deprecated
1538 */
1539 public void setSoTimeout(int timeout)
1540 throws SocketException, IllegalStateException {
1541 if (hasConnection()) {
1542 wrappedConnection.setSoTimeout(timeout);
1543 } else {
1544
1545 }
1546 }
1547
1548 /***
1549 * @deprecated
1550 */
1551 public void shutdownOutput() {
1552 if (hasConnection()) {
1553 wrappedConnection.shutdownOutput();
1554 } else {
1555
1556 }
1557 }
1558
1559 public void tunnelCreated() throws IllegalStateException, IOException {
1560 if (hasConnection()) {
1561 wrappedConnection.tunnelCreated();
1562 } else {
1563
1564 }
1565 }
1566
1567 public void write(byte[] data, int offset, int length)
1568 throws IOException, IllegalStateException {
1569 if (hasConnection()) {
1570 wrappedConnection.write(data, offset, length);
1571 } else {
1572 throw new IllegalStateException("Connection has been released");
1573 }
1574 }
1575
1576 public void write(byte[] data)
1577 throws IOException, IllegalStateException {
1578 if (hasConnection()) {
1579 wrappedConnection.write(data);
1580 } else {
1581 throw new IllegalStateException("Connection has been released");
1582 }
1583 }
1584
1585 public void writeLine()
1586 throws IOException, IllegalStateException {
1587 if (hasConnection()) {
1588 wrappedConnection.writeLine();
1589 } else {
1590 throw new IllegalStateException("Connection has been released");
1591 }
1592 }
1593
1594 public void writeLine(byte[] data)
1595 throws IOException, IllegalStateException {
1596 if (hasConnection()) {
1597 wrappedConnection.writeLine(data);
1598 } else {
1599 throw new IllegalStateException("Connection has been released");
1600 }
1601 }
1602
1603 public void flushRequestOutputStream() throws IOException {
1604 if (hasConnection()) {
1605 wrappedConnection.flushRequestOutputStream();
1606 } else {
1607 throw new IllegalStateException("Connection has been released");
1608 }
1609 }
1610
1611 /***
1612 * @deprecated
1613 */
1614 public int getSoTimeout() throws SocketException {
1615 if (hasConnection()) {
1616 return wrappedConnection.getSoTimeout();
1617 } else {
1618 throw new IllegalStateException("Connection has been released");
1619 }
1620 }
1621
1622 /***
1623 * @deprecated
1624 */
1625 public String getVirtualHost() {
1626 if (hasConnection()) {
1627 return wrappedConnection.getVirtualHost();
1628 } else {
1629 throw new IllegalStateException("Connection has been released");
1630 }
1631 }
1632
1633 /***
1634 * @deprecated
1635 */
1636 public void setVirtualHost(String host) throws IllegalStateException {
1637 if (hasConnection()) {
1638 wrappedConnection.setVirtualHost(host);
1639 } else {
1640 throw new IllegalStateException("Connection has been released");
1641 }
1642 }
1643
1644 public int getSendBufferSize() throws SocketException {
1645 if (hasConnection()) {
1646 return wrappedConnection.getSendBufferSize();
1647 } else {
1648 throw new IllegalStateException("Connection has been released");
1649 }
1650 }
1651
1652 /***
1653 * @deprecated
1654 */
1655 public void setSendBufferSize(int sendBufferSize) throws SocketException {
1656 if (hasConnection()) {
1657 wrappedConnection.setSendBufferSize(sendBufferSize);
1658 } else {
1659 throw new IllegalStateException("Connection has been released");
1660 }
1661 }
1662
1663 public HttpConnectionParams getParams() {
1664 if (hasConnection()) {
1665 return wrappedConnection.getParams();
1666 } else {
1667 throw new IllegalStateException("Connection has been released");
1668 }
1669 }
1670
1671 public void setParams(final HttpConnectionParams params) {
1672 if (hasConnection()) {
1673 wrappedConnection.setParams(params);
1674 } else {
1675 throw new IllegalStateException("Connection has been released");
1676 }
1677 }
1678
1679
1680
1681
1682 public void print(String data, String charset) throws IOException, IllegalStateException {
1683 if (hasConnection()) {
1684 wrappedConnection.print(data, charset);
1685 } else {
1686 throw new IllegalStateException("Connection has been released");
1687 }
1688 }
1689
1690
1691
1692
1693 public void printLine(String data, String charset)
1694 throws IOException, IllegalStateException {
1695 if (hasConnection()) {
1696 wrappedConnection.printLine(data, charset);
1697 } else {
1698 throw new IllegalStateException("Connection has been released");
1699 }
1700 }
1701
1702
1703
1704
1705 public void setSocketTimeout(int timeout) throws SocketException, IllegalStateException {
1706 if (hasConnection()) {
1707 wrappedConnection.setSocketTimeout(timeout);
1708 } else {
1709 throw new IllegalStateException("Connection has been released");
1710 }
1711 }
1712
1713 public Socket getConnectionSocket()
1714 {
1715 if( wrappedConnection instanceof ConnectionWithSocket )
1716 return ((ConnectionWithSocket)wrappedConnection).getConnectionSocket();
1717
1718 return null;
1719 }
1720
1721 }
1722
1723 }
1724