001: /*
002: * $Id: CheckLinks.java,v 1.37 2007/05/22 11:13:26 agoubard Exp $
003: *
004: * Copyright 2003-2007 Orange Nederland Breedband B.V.
005: * See the COPYRIGHT file for redistribution and use restrictions.
006: */
007: package org.xins.server;
008:
009: import java.io.IOException;
010: import java.io.InterruptedIOException;
011: import java.net.ConnectException;
012: import java.net.UnknownHostException;
013: import java.util.ArrayList;
014: import java.util.Iterator;
015: import java.util.List;
016:
017: import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
018: import org.apache.commons.httpclient.HttpClient;
019: import org.apache.commons.httpclient.HttpMethodBase;
020: import org.apache.commons.httpclient.HttpRecoverableException;
021: import org.apache.commons.httpclient.params.HttpMethodParams;
022: import org.apache.commons.httpclient.methods.OptionsMethod;
023:
024: import org.xins.common.MandatoryArgumentChecker;
025: import org.xins.common.Utils;
026: import org.xins.common.service.Descriptor;
027: import org.xins.common.service.TargetDescriptor;
028: import org.xins.common.xml.ElementBuilder;
029:
030: /**
031: * Checks all the links in the given <code>descriptor</code>s list and builds
032: * a <code>FunctionResult</code>. It connects to each link in
033: * {@link TargetDescriptor}s in {@link Descriptor}s list using a
034: * {@link URLChecker} and calculates the total links count and
035: * total links failures. The returned {@link FunctionResult} contains
036: * information about total links checked, failures and details.
037: *
038: * The following example uses a {@link CheckLinks} object to get the
039: * {@link FunctionResult}.
040: *
041: * <blockquote><pre>
042: * FunctionResult result = CheckLinks.checkLinks(descriptorList);
043: *
044: * // Returns parameters
045: * result.getParameters();
046: * </pre></blockquote>
047: *
048: * @version $Revision: 1.37 $ $Date: 2007/05/22 11:13:26 $
049: * @author <a href="mailto:tauseef.rehman@orange-ftgroup.com">Tauseef Rehman</a>
050: */
051: class CheckLinks {
052:
053: /**
054: * The failure message to be added in the <code>FunctionResult</code> when
055: * the exception is <code>UnknownHostException</code>.
056: */
057: private static final String UNKNOWN_HOST = "UnknownHost";
058:
059: /**
060: * The failure message to be added in the <code>FunctionResult</code> when
061: * the exception is <code>ConnectTimeoutException</code> or the message
062: * of the exception starts with "Connect timed out".
063: */
064: private static final String CONNECTION_TIMEOUT = "ConnectionTimeout";
065:
066: /**
067: * The failure message to be added in the <code>FunctionResult</code> when
068: * the exception is <code>ConnectException</code>.
069: */
070: private static final String CONNECTION_REFUSAL = "ConnectionRefusal";
071:
072: /**
073: * The failure message to be added in the <code>FunctionResult</code> when
074: * the exception is <code>SocketTimeoutException</code>.
075: */
076: private static final String SOCKET_TIMEOUT = "SocketTimeout";
077:
078: /**
079: * The failure message to be added in the <code>FunctionResult</code> when
080: * the exception is <code>IOException</code>.
081: */
082: private static final String OTHER_IO_ERROR = "OtherIOError";
083:
084: /**
085: * The failure message to be added in the <code>FunctionResult</code> when
086: * the exception is an unknown <code>Exception</code>.
087: */
088: private static final String OTHER_FAILURE = "OtherFailure";
089:
090: /**
091: * The success message to be added in the <code>FunctionResult</code>.
092: */
093: private static final String SUCCESS = "Success";
094:
095: /**
096: * HTTP retry handler that does not allow any retries.
097: */
098: private static DefaultHttpMethodRetryHandler NO_RETRIES = new DefaultHttpMethodRetryHandler(
099: 0, false);
100:
101: /**
102: * Checks all the links in <code>TargetDescriptor</code>s inside the
103: * <code>Descriptor</code> list and builds a <code>FunctionResult</code>.
104: * First gets all the {@link TargetDescriptor}s from the
105: * {@link Descriptor}s list then creates {@link URLChecker} threads with
106: * {@link TargetDescriptor}s and runs them. When all the threads have
107: * finished execution, the {@link FunctionResult} is built and returned.
108: * The returned {@link FunctionResult} contains all the links which were
109: * checked with their results.
110: *
111: * @param descriptors
112: * the list of {@link Descriptor}s defined in the runtime properties,
113: * cannot be <code>null</code>.
114: *
115: * @return
116: * the constructed {@link FunctionResult} object, never
117: * <code>null</code>.
118: *
119: * @throws IllegalArgumentException
120: * if <code>descriptors == null</code>.
121: */
122: static FunctionResult checkLinks(List descriptors)
123: throws IllegalArgumentException {
124:
125: // Check preconditions
126: MandatoryArgumentChecker.check("descriptors", descriptors);
127:
128: List threads = new ArrayList();
129: if (descriptors.size() > 0) {
130:
131: // Get all the targets from the descriptor list
132: List targetDescriptors = getTargetDescriptors(descriptors);
133:
134: // Create the thread for each target and run them
135: threads = createAndRunUrlCheckers(targetDescriptors);
136:
137: // Get the biggest time-out from all the targets
138: int timeout = getBiggestTimeout(targetDescriptors);
139:
140: // Wait till all the threads finish their execution or timedout.
141: waitTillThreadsRunning(threads, timeout);
142:
143: // Confirm all threads have finished their execution.
144: confirmThreadsStopped(threads);
145: }
146:
147: // Start building the result
148: FunctionResult builder = new FunctionResult();
149: int errorCount = (descriptors.size() > 0) ? addCheckElements(
150: builder, threads) : 0;
151: builder.param("linkCount", String.valueOf(threads.size()));
152: builder.param("errorCount", String.valueOf(errorCount));
153:
154: return builder;
155: }
156:
157: /**
158: * Creates a list of <code>TargetDescriptor</code>s from the
159: * given <code>Descriptor</code>s list. Each {@link Descriptor} in the
160: * list contains a list of {@link TargetDescriptor}s, which are added to
161: * the returned list.
162: *
163: * @param descriptors
164: * the list of {@link Descriptor}s, cannot be <code>null</code>.
165: *
166: * @return
167: * the constructed {@link TargetDescriptor}s list, never
168: * <code>null</code>.
169: *
170: * @throws IllegalArgumentException
171: * if <code>descriptors == null</code>.
172: */
173: private static List getTargetDescriptors(List descriptors)
174: throws IllegalArgumentException {
175:
176: // Check preconditions
177: MandatoryArgumentChecker.check("descriptors", descriptors);
178:
179: Iterator descriptorIterator = descriptors.iterator();
180: List targetDescriptors = new ArrayList();
181:
182: // Each descriptor in the list contains target descriptors, so
183: // iterate over descriptors and get all the target descriptors, then
184: // iterate over each target descriptor and get the individual
185: // target descriptors.
186: while (descriptorIterator.hasNext()) {
187: Descriptor descriptor = (Descriptor) descriptorIterator
188: .next();
189:
190: // Get the iterator on target descriptor
191: Iterator targetIterator = descriptor.iterateTargets();
192: while (targetIterator.hasNext()) {
193: TargetDescriptor targetDescriptor = (TargetDescriptor) targetIterator
194: .next();
195:
196: // Add all the target descriptors in a list
197: targetDescriptors.add(targetDescriptor);
198: }
199: }
200:
201: return targetDescriptors;
202: }
203:
204: /**
205: * Creates and runs a thread for each <code>TargetDescriptor</code> in the
206: * given list. Each {@link TargetDescriptor} in the list contains a URL. A
207: * {@link URLChecker} thread is created for each {@link TargetDescriptor},
208: * which tries to connect to the URL provided in the
209: * {@link TargetDescriptor}. Each thread is then added to a list which is
210: * returned.
211: *
212: * @param targetDescriptors
213: * the list of {@link TargetDescriptor}s which needs to be checked,
214: * cannot be <code>null</code>.
215: *
216: * @return
217: * the constructed {@link URLChecker}s list, never <code>null</code>.
218: *
219: * @throws IllegalArgumentException
220: * if <code>targetDescriptors == null</code>.
221: */
222: private static List createAndRunUrlCheckers(List targetDescriptors)
223: throws IllegalArgumentException {
224:
225: // Check preconditions
226: MandatoryArgumentChecker.check("targetDescriptors",
227: targetDescriptors);
228:
229: // Iterate over all target descriptors
230: List threads = new ArrayList();
231: Iterator targets = targetDescriptors.iterator();
232: while (targets.hasNext()) {
233: TargetDescriptor target = (TargetDescriptor) targets.next();
234:
235: // Create a thread for the target descriptor
236: URLChecker urlThread = new URLChecker(target);
237:
238: // Start the thread with target descriptor
239: urlThread.start();
240:
241: // Store the thread just started in a list
242: threads.add(urlThread);
243: }
244:
245: return threads;
246: }
247:
248: /**
249: * Returns the biggest time-out of all the URLs defined in
250: * <code>TargetDescriptor</code>s list. Each {@link TargetDescriptor} in
251: * the list has total time-out. The biggest of all of them is returned.
252: * This time-out is then used to setup the time-outs of the
253: * {@link URLChecker} threads.
254: *
255: * @param targetDescriptors
256: * the list of {@link TargetDescriptor}s, cannot be <code>null</code>.
257: *
258: * @return
259: * the biggest time-out from the list, or <code>-1</code> if none of the
260: * target descriptors defines a time-out.
261: *
262: * @throws IllegalArgumentException
263: * if <code>targetDescriptors == null</code>.
264: */
265: private static int getBiggestTimeout(List targetDescriptors)
266: throws IllegalArgumentException {
267:
268: // Check preconditions
269: MandatoryArgumentChecker.check("targetDescriptors",
270: targetDescriptors);
271:
272: Iterator targets = targetDescriptors.iterator();
273: int biggestTimeout = -1;
274:
275: // Iterate over all target descriptors
276: while (targets.hasNext()) {
277: TargetDescriptor target = (TargetDescriptor) targets.next();
278:
279: // Try to get the biggest time out of all the target descriptors
280: if (biggestTimeout < target.getTotalTimeOut()) {
281: biggestTimeout = target.getTotalTimeOut();
282: }
283: }
284:
285: return biggestTimeout;
286: }
287:
288: /**
289: * Sets up the time-out for each thread and waits till each thread finishes
290: * execution. The time-out is the biggest time-out of all the URLs in
291: * {@link TargetDescriptor}s. Timeout for every next thread also considers
292: * the time which is already spent and that time is subtracted from the
293: * time-out for the current thread.
294: *
295: * @param threads
296: * the list of {@link URLChecker} threads, cannot be <code>null</code>.
297: *
298: * @param timeout
299: * the time-out for {@link URLChecker} threads.
300: *
301: * @throws IllegalArgumentException
302: * if <code>threads == null</code>.
303: */
304: private static void waitTillThreadsRunning(List threads, int timeout)
305: throws IllegalArgumentException {
306:
307: // Check preconditions
308: MandatoryArgumentChecker.check("threads", threads);
309:
310: Iterator threadIterator = threads.iterator();
311: long threadTimeout = timeout;
312:
313: // Storing the time approximately when the first thread was started
314: long startTime = System.currentTimeMillis();
315: try {
316:
317: // Iterate over all the threads
318: while (threadIterator.hasNext()) {
319: URLChecker urlThread = (URLChecker) threadIterator
320: .next();
321: urlThread.join(threadTimeout);
322:
323: // If the previous thread was setup with a certain time-out
324: // the next thread should be setup with a time-out subtracted
325: // by the time which is already passed.
326: long endTime = System.currentTimeMillis();
327: long timePassed = endTime - startTime;
328: threadTimeout = timeout - timePassed;
329:
330: // If the time-out becomes negative, it means that the total
331: // time-out interval has passed now we do not need to setup
332: // time-out for threads and they all should have finished
333: // execution by now.
334: if (threadTimeout <= 0) {
335: return;
336: }
337: }
338: } catch (InterruptedException exception) {
339:
340: // The exception is thrown when another thread has interrupted
341: // the current thread. This should never happen so it should log
342: // a programming error and throw a ProgrammingException.
343: throw Utils.logProgrammingError(exception);
344: }
345: }
346:
347: /**
348: * Confimrs that each <code>URLChecker</code> has finished its execution.
349: * If some threads are still running, inforce a connection time-out and let
350: * it run and ignore.
351: *
352: * @param threads
353: * the list of {@link URLChecker} threads, cannot be <code>null</code>.
354: *
355: * @throws IllegalArgumentException
356: * if <code>threads == null</code>.
357: */
358: private static void confirmThreadsStopped(List threads)
359: throws IllegalArgumentException {
360:
361: // Check preconditions
362: MandatoryArgumentChecker.check("threads", threads);
363:
364: Iterator threadIterator = threads.iterator();
365:
366: // Iterate over all the threads
367: while (threadIterator.hasNext()) {
368: URLChecker urlThread = (URLChecker) threadIterator.next();
369:
370: // Check if thread is still alive.
371: if (urlThread.isAlive()) {
372:
373: // Enforce a time-out for the thread and log it.
374: urlThread.enforceTimeout();
375: Log.log_3505(urlThread.getURL());
376: }
377: }
378: }
379:
380: /**
381: * Builds the <code>FunctionResult</code> for all the URLs checked. It
382: * iterates over the list of all {@link URLChecker} threads and gets the
383: * information like the total time each thread took to execute and the
384: * result of the execution. The information is added in an
385: * {@link ElementBuilder} object using which {@link org.xins.common.xml.Element}
386: * is created which then is added to the passed {@link FunctionResult}.
387: *
388: * @param builder
389: * the {@link FunctionResult} where the result is added, cannot be
390: * <code>null</code>.
391: *
392: * @param threads
393: * the list of {@link URLChecker} threads, cannot be <code>null</code>.
394: *
395: * @return
396: * the total number of URLs without success.
397: *
398: * @throws IllegalArgumentException
399: * if <code>builder == null || threads == null</code>.
400: */
401: private static int addCheckElements(FunctionResult builder,
402: List threads) throws IllegalArgumentException {
403:
404: // Check preconditions
405: MandatoryArgumentChecker.check("builder", builder, "threads",
406: threads);
407:
408: Iterator threadIterator = threads.iterator();
409: int errorCount = 0;
410:
411: // Iterate over the threads of target descriptors and create the
412: // check element.
413: while (threadIterator.hasNext()) {
414: URLChecker urlThread = (URLChecker) threadIterator.next();
415: ElementBuilder eb = new ElementBuilder("check");
416: eb.setAttribute("url", urlThread.getURL());
417: eb.setAttribute("duration", Long.toString(urlThread
418: .getDuration()));
419: eb.setAttribute("result", getResult(urlThread));
420: builder.add(eb.createElement());
421:
422: if (!urlThread.getSuccess()) {
423: errorCount++;
424: }
425: }
426:
427: return errorCount;
428: }
429:
430: /**
431: * Returns the value for the result parameter which is added in the
432: * <code>FunctionBuilder</code>. The value of the result depends on the
433: * success or failure of the passed {@link URLChecker} thread. If the
434: * {@link URLChecker} thread gives a success, the status code of the
435: * {@link URLChecker} thread is used to create the value for result
436: * parameter, otherwise the exception in the {@link URLChecker} thread
437: * determines the value for the result parameter.
438: *
439: * @param urlThread
440: * the {@link URLChecker} thread for which the result value is to
441: * detemined, cannot be <code>null</code>.
442: *
443: * @return
444: * the result message, never <code>null</code>.
445: *
446: * @throws IllegalArgumentException
447: * if <code>urlThread == null || urlThread.hasRun() == false</code>.
448: */
449: private static String getResult(URLChecker urlThread)
450: throws IllegalArgumentException {
451:
452: // Check preconditions
453: MandatoryArgumentChecker.check("urlThread", urlThread);
454: if (!urlThread.hasRun()) {
455: throw new IllegalArgumentException(
456: "urlThread().hasRun() == false");
457: }
458:
459: if (urlThread.getSuccess()) {
460: return SUCCESS;
461: } else {
462: return getResult(urlThread.getException(), urlThread
463: .getURL());
464: }
465: }
466:
467: /**
468: * Returns the value for the result parameter which is added in the
469: * <code>FunctionBuilder</code> when the <code>URLChecker</code> thread
470: * failed to connect the URL. The value for the result parameter depends
471: * on the exception occured in the {@link URLChecker} thread. The
472: * exception is passed to this method. Based on the type of exception, an
473: * appropriate value is returned.
474: *
475: * @param exception
476: * the {@link Throwable} exception occured in the {@link URLChecker}
477: * thread, cannot be <code>null</code>.
478: *
479: * @param url
480: * the url which threw the exception, cannot be <code>null</code>.
481: *
482: * @return
483: * the result message, never <code>null</code>.
484: *
485: * @throws IllegalArgumentException
486: * if <code>exception == null</code>.
487: */
488: private static String getResult(Throwable exception, String url)
489: throws IllegalArgumentException {
490:
491: // Check preconditions.
492: MandatoryArgumentChecker.check("exception", exception, "url",
493: url);
494:
495: String exceptionName = exception.getClass().getName();
496: String result;
497:
498: // DNS error, unknown host name
499: if (exception instanceof UnknownHostException) {
500: result = UNKNOWN_HOST;
501:
502: // Connection time-out
503: } else if (exceptionName
504: .equals("org.apache.commons.httpclient.ConnectTimeoutException")
505: || exception.getMessage().startsWith(
506: "Connect timed out")) {
507: result = CONNECTION_TIMEOUT;
508:
509: // Connection refused
510: } else if (exception instanceof ConnectException) {
511: result = CONNECTION_REFUSAL;
512:
513: // SocketTimeoutException is not available in older Java versions,
514: // so we do not refer to the class to avoid a NoClassDefFoundError.
515: } else if (exceptionName
516: .equals("java.net.SocketTimeoutException")) {
517: result = SOCKET_TIMEOUT;
518:
519: // HTTPClient 2.0 socket time out is done using the HttpRecoverableException
520: } else if (exception instanceof HttpRecoverableException
521: && ((HttpRecoverableException) exception).getReason()
522: .indexOf("Read timed out") != -1) {
523: result = SOCKET_TIMEOUT;
524:
525: // Interrupted I/O (this _may_ indicate a socket time-out)
526: } else if (exception instanceof InterruptedIOException) {
527: String exMessage = exception.getMessage();
528:
529: // XXX: Only tested on Sun JVM
530: // TODO: Test on non-Sun JVM
531: if (exMessage.startsWith("Read timed out")) {
532: result = SOCKET_TIMEOUT;
533:
534: // Unspecific I/O error
535: } else {
536: result = OTHER_IO_ERROR;
537: }
538:
539: // Other I/O error
540: } else if (exception instanceof IOException) {
541: result = OTHER_IO_ERROR;
542:
543: // Other error, apparently not an I/O error
544: } else {
545: result = OTHER_FAILURE;
546: }
547:
548: // Log the result and exception.
549: Log.log_3502(exception, url, result);
550:
551: return result;
552: }
553:
554: /**
555: * Creates a new <code>CheckLinks</code> object.
556: */
557: private CheckLinks() {
558: // empty
559: }
560:
561: /**
562: * Tries to connect to a URL provided in the
563: * <code>TargetDescriptor</code>. Runs as a separate thread. The URL is
564: * connected by sending a request associated with an HTTP
565: * <code>OPTIONS</code> method. Also calculates the total time to
566: * connect to the provided URL.
567: *
568: * <p>The following example uses a {@link CheckLinks} object to get the
569: * {@link FunctionResult}.
570: *
571: * <blockquote><pre>TargetDescriptor target = new TargetDescriptor();
572: * target.setURL("www.hotmail.com");
573: *
574: * URLChecker urlThread = new URLChecker(target);
575: * urlThread.start();
576: *
577: * String URL = urlThread.getURL();
578: * int duration = urlThread.getDuration();
579: * boolean success = urlThread.getSuccess();
580: * if (!success) {
581: * exception = urlThread.getException();
582: * }</pre></blockquote>
583: *
584: * @version $Revision: 1.37 $ $Date: 2007/05/22 11:13:26 $
585: * @author <a href="mailto:tauseef.rehman@orange-ftgroup.com">Tauseef Rehman</a>
586: */
587: private static final class URLChecker extends Thread {
588:
589: /**
590: * The target descriptor for which the URL needs to be checked. Never
591: * <code>null</code>.
592: */
593: private final TargetDescriptor _targetDescriptor;
594:
595: /**
596: * The URL to be checked. Never <code>null</code>.
597: */
598: private final String _url;
599:
600: /**
601: * The exception thrown when accessing the URL. Can be
602: * <code>null</code> if the <code>URLChecker</code> has not run yet, or
603: * if there was no error.
604: */
605: private Throwable _exception;
606:
607: /**
608: * The result of the URL check. Is <code>true</code> if the
609: * <code>URLChecker</code> has run and was successful. If either of
610: * these conditions is not met, then <code>false</code>.
611: */
612: private boolean _success;
613:
614: /**
615: * The time taken to check the URL. Initially <code>-1</code>.
616: */
617: private long _duration;
618:
619: /**
620: * The status code returned when the URL was called. Initially
621: * <code>-1</code>, when the <code>URLChecker</code> was not run yet.
622: */
623: private int _statusCode;
624:
625: /**
626: * Constructs a new <code>URLChecker</code> for the specified target
627: * descriptor.
628: *
629: * @param targetDescriptor
630: * the {@link TargetDescriptor}, whose URL needs to be checked,
631: * cannot be <code>null</code>.
632: *
633: * @throws IllegalArgumentException
634: * if <code>targetDescriptor == null</code>.
635: */
636: public URLChecker(TargetDescriptor targetDescriptor)
637: throws IllegalArgumentException {
638:
639: // Check preconditions
640: MandatoryArgumentChecker.check("targetDescriptor",
641: targetDescriptor);
642:
643: // Initialize fields
644: _targetDescriptor = targetDescriptor;
645: _url = targetDescriptor.getURL();
646: _duration = -1;
647: _statusCode = -1;
648:
649: // Check postconditions
650: if (_url == null) {
651: throw Utils.logProgrammingError("_url == null");
652: }
653: }
654:
655: /**
656: * Runs this thread. It tries to connect to the URL provided in the
657: * {@link TargetDescriptor}. The URL is connected by sending a request
658: * associated with an HTTP <code>OPTIONS</code> method. It also
659: * calculates the total time to connect to the provided URL and saves
660: * the exception in case an exception occurs.
661: *
662: * @throws IllegalStateException
663: * if this <code>URLChecker</code> has already run.
664: */
665: public void run() throws IllegalStateException {
666:
667: // Check preconditions
668: if (hasRun()) {
669: throw new IllegalStateException(
670: "This URLChecker for URL: " + _url
671: + "has already run.");
672: }
673:
674: // Logging the start of this thread.
675: Log.log_3503(_url, _targetDescriptor.getTotalTimeOut(),
676: _targetDescriptor.getConnectionTimeOut(),
677: _targetDescriptor.getSocketTimeOut());
678:
679: // Register current time, to compute total duration later
680: long startTime = System.currentTimeMillis();
681:
682: HttpMethodBase optionsMethod = null;
683: try {
684: HttpClient client = new HttpClient();
685:
686: // Set the socket time-out for the URL.
687: client.setTimeout(_targetDescriptor.getSocketTimeOut());
688:
689: // Set the connection time-out for the URL.
690: client
691: .setHttpConnectionFactoryTimeout(_targetDescriptor
692: .getConnectionTimeOut());
693:
694: // Create a new OptionsMethod with the URL, this will represent
695: // a request for information about the communication options
696: // available on the request/response chain identified by the url.
697: // This method allows the client to determine the options and/or
698: // requirements associated with a resource, or the capabilities
699: // of a server, without implying a resource action or initiating
700: // a resource retrieval.
701: optionsMethod = new OptionsMethod(_url);
702: optionsMethod.getParams().setParameter(
703: HttpMethodParams.RETRY_HANDLER, NO_RETRIES);
704:
705: // Execute the OptionsMethod.
706: _statusCode = client.executeMethod(optionsMethod);
707:
708: // Successfully executed, so set the success as true.
709: _success = true;
710: } catch (Throwable exception) {
711:
712: // Save the exception and set the success as false as the
713: // execution was failed.
714: _exception = exception;
715: _success = false;
716: } finally {
717: releaseConnection(optionsMethod);
718: }
719:
720: // Calculate the total time taken to check the URL.
721: _duration = System.currentTimeMillis() - startTime;
722:
723: // Logging the stopping of this thread.
724: Log.log_3504(_url, _duration);
725: }
726:
727: /**
728: * Releases the connection used by the passed
729: * <code>HttpMethodBase</code>. If the connection is not released
730: * successfully and an exception is thrown, then it is just logged and
731: * ignored. If the argument is <code>null</code> then nothing is done.
732: *
733: * @param method
734: * the {@link HttpMethodBase} potentially having an unreleased
735: * connection, can be <code>null</code>.
736: */
737: private void releaseConnection(HttpMethodBase method) {
738:
739: if (method != null) {
740:
741: // Release the connection
742: try {
743: method.releaseConnection();
744:
745: // Just ignore (and log) any exception as we do not want to fail
746: // if the connection is not properly released.
747: } catch (Throwable ignorable) {
748: Utils.logIgnoredException(ignorable);
749: }
750: }
751: }
752:
753: /**
754: * Checks if this <code>URLChecker</code> has already run.
755: *
756: * @return
757: * <code>true</code> if this <code>URLChecker</code> has already run,
758: * or <code>false</code> otherwise.
759: */
760: boolean hasRun() {
761: return (_duration >= 0);
762: }
763:
764: /**
765: * Checks if this <code>URLChecker</code> has already run and if not,
766: * throws an exception.
767: *
768: * @throws IllegalStateException
769: * if this <code>URLChecker</code> has not run yet.
770: */
771: private void assertHasRun() throws IllegalStateException {
772: if (!hasRun()) {
773: String message = "This URLChecker has not run yet. URL: \""
774: + _url + "\".";
775: throw new IllegalStateException(message);
776: }
777: }
778:
779: /**
780: * Returns the total time it took to connect to the URL.
781: *
782: * @return
783: * the total duration in milliseconds, or <code>-1</code> if this
784: * thread has not run.
785: *
786: * @throws IllegalStateException
787: * if this <code>URLChecker</code> has not run yet.
788: */
789: public long getDuration() throws IllegalStateException {
790: assertHasRun();
791: return _duration;
792: }
793:
794: /**
795: * Returns the flag indicating if the URL was connected successfully.
796: *
797: * @return
798: * the success flag, Is <code>true</code> if this thread has run and
799: * was successful. If either of these conditions is not met,
800: * then <code>false</code>.
801: *
802: * @throws IllegalStateException
803: * if this <code>URLChecker</code> has not run yet.
804: */
805: public boolean getSuccess() throws IllegalStateException {
806: assertHasRun();
807: return _success;
808: }
809:
810: /**
811: * Returns the status code of the method execution.
812: *
813: * @return
814: * the status code returned when the URL was called. <code>-1</code>,
815: * when this thread has not run.
816: *
817: * @throws IllegalStateException
818: * if this <code>URLChecker</code> has not run yet.
819: */
820: public int getStatusCode() throws IllegalStateException {
821: assertHasRun();
822: return _statusCode;
823: }
824:
825: /**
826: * Returns the URL which was connected.
827: *
828: * @return
829: * the URL, never <code>null</code>.
830: *
831: * @throws IllegalStateException
832: * if this <code>URLChecker</code> has not run yet.
833: */
834: public String getURL() throws IllegalStateException {
835: assertHasRun();
836: return _url;
837: }
838:
839: /**
840: * Returns the exception thrown while trying to connect to the URL.
841: *
842: * @return
843: * the exception, can be <code>null</code>.
844: *
845: * @throws IllegalStateException
846: * if this <code>URLChecker</code> has not run yet.
847: */
848: public Throwable getException() throws IllegalStateException {
849: assertHasRun();
850: return _exception;
851: }
852:
853: /**
854: * Enforces a time-out on the <code>URLChecker</code> thread. Actualy
855: * the thread is allowed to run and ignored. So set the duration as the
856: * initial connection time-out value and create a new
857: * {@link ConnectException}.
858: */
859: public void enforceTimeout() {
860: if (!hasRun()) {
861:
862: // Set the duration as was defined for connection time-out
863: _duration = _targetDescriptor.getConnectionTimeOut();
864:
865: // Create a new ConnectException.
866: _exception = new ConnectException("Connect timed out");
867:
868: // XXX: Currently it is observed that mostly the URLs which are
869: // expected to throw a ConnectTimeoutException keeps on running
870: // but we need to take care of the situation when because of some
871: // other reason the thread is still active.
872: }
873: }
874:
875: }
876: }
|