001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs.condition;
020:
021: import org.apache.tools.ant.BuildException;
022: import org.apache.tools.ant.Project;
023: import org.apache.tools.ant.ProjectComponent;
024:
025: import java.lang.reflect.InvocationTargetException;
026: import java.lang.reflect.Method;
027: import java.net.InetAddress;
028: import java.net.MalformedURLException;
029: import java.net.URL;
030: import java.net.UnknownHostException;
031:
032: /**
033: * Test for a host being reachable using ICMP "ping" packets & echo operations.
034: * Ping packets are very reliable for assessing reachability in a LAN or WAN,
035: * but they do not get through any well-configured firewall. Echo (port 7) may.
036: * <p/>
037: * This condition turns unknown host exceptions into false conditions. This is
038: * because on a laptop, DNS is one of the first services lost when the network
039: * goes; you are implicitly offline.
040: * <p/>
041: * If a URL is supplied instead of a host, the hostname is extracted and used in
042: * the test--all other parts of the URL are discarded.
043: * <p/>
044: * The test may not work through firewalls; that is, something may be reachable
045: * using a protocol such as HTTP, while the lower level ICMP packets get dropped
046: * on the floor. Similarly, a host may be detected as reachable with ICMP, but not
047: * reachable on other ports (i.e. port 80), because of firewalls.
048: * <p/>
049: * Requires Java1.5+ to work properly. On Java1.4 and earlier, if a hostname
050: * can be resolved, the destination is assumed to be reachable.
051: *
052: * @since Ant 1.7
053: */
054: public class IsReachable extends ProjectComponent implements Condition {
055:
056: private static final int SECOND = 1000; // millis per second
057: private String host;
058: private String url;
059:
060: /**
061: * The default timeout.
062: */
063: public static final int DEFAULT_TIMEOUT = 30;
064: private int timeout = DEFAULT_TIMEOUT;
065: /**
066: * Error when no hostname is defined
067: */
068: public static final String ERROR_NO_HOSTNAME = "No hostname defined";
069: /**
070: * Error when invalid timeout value is defined
071: */
072: public static final String ERROR_BAD_TIMEOUT = "Invalid timeout value";
073: /**
074: * Unknown host message is seen.
075: */
076: private static final String WARN_UNKNOWN_HOST = "Unknown host: ";
077: /**
078: * Network error message is seen.
079: */
080: public static final String ERROR_ON_NETWORK = "network error to ";
081: /** Error message when url and host are specified. */
082: public static final String ERROR_BOTH_TARGETS = "Both url and host have been specified";
083: /** Error message when no reachably test avail. */
084: public static final String MSG_NO_REACHABLE_TEST = "cannot do a proper reachability test on this Java version";
085: /** Error message when an invalid url is used. */
086: public static final String ERROR_BAD_URL = "Bad URL ";
087: /** Error message when no hostname in url. */
088: public static final String ERROR_NO_HOST_IN_URL = "No hostname in URL ";
089: /** The method name to look for in InetAddress */
090: public static final String METHOD_NAME = "isReachable";
091:
092: /**
093: * Set the host to ping.
094: *
095: * @param host the host to ping.
096: */
097: public void setHost(String host) {
098: this .host = host;
099: }
100:
101: /**
102: * Set the URL from which to extract the hostname.
103: *
104: * @param url a URL object.
105: */
106: public void setUrl(String url) {
107: this .url = url;
108: }
109:
110: /**
111: * Set the timeout for the reachability test in seconds.
112: *
113: * @param timeout the timeout in seconds.
114: */
115: public void setTimeout(int timeout) {
116: this .timeout = timeout;
117: }
118:
119: /**
120: * emptyness test
121: *
122: * @param string param to check
123: *
124: * @return true if it is empty
125: */
126: private boolean empty(String string) {
127: return string == null || string.length() == 0;
128: }
129:
130: private static Class[] parameterTypes = { Integer.TYPE };
131:
132: /**
133: * Evaluate the condition.
134: *
135: * @return true if the condition is true.
136: *
137: * @throws org.apache.tools.ant.BuildException
138: * if an error occurs
139: */
140: public boolean eval() throws BuildException {
141: if (empty(host) && empty(url)) {
142: throw new BuildException(ERROR_NO_HOSTNAME);
143: }
144: if (timeout < 0) {
145: throw new BuildException(ERROR_BAD_TIMEOUT);
146: }
147: String target = host;
148: if (!empty(url)) {
149: if (!empty(host)) {
150: throw new BuildException(ERROR_BOTH_TARGETS);
151: }
152: try {
153: //get the host of a url
154: URL realURL = new URL(url);
155: target = realURL.getHost();
156: if (empty(target)) {
157: throw new BuildException(ERROR_NO_HOST_IN_URL + url);
158: }
159: } catch (MalformedURLException e) {
160: throw new BuildException(ERROR_BAD_URL + url, e);
161: }
162: }
163: log("Probing host " + target, Project.MSG_VERBOSE);
164: InetAddress address;
165: try {
166: address = InetAddress.getByName(target);
167: } catch (UnknownHostException e1) {
168: log(WARN_UNKNOWN_HOST + target);
169: return false;
170: }
171: log("Host address = " + address.getHostAddress(),
172: Project.MSG_VERBOSE);
173: boolean reachable;
174: //Java1.5: reachable = address.isReachable(timeout * 1000);
175: Method reachableMethod = null;
176: try {
177: reachableMethod = InetAddress.class.getMethod(METHOD_NAME,
178: parameterTypes);
179: Object[] params = new Object[1];
180: params[0] = new Integer(timeout * SECOND);
181: try {
182: reachable = ((Boolean) reachableMethod.invoke(address,
183: params)).booleanValue();
184: } catch (IllegalAccessException e) {
185: //utterly implausible, but catered for anyway
186: throw new BuildException("When calling "
187: + reachableMethod);
188: } catch (InvocationTargetException e) {
189: //assume this is an IOexception about un readability
190: Throwable nested = e.getTargetException();
191: log(ERROR_ON_NETWORK + target + ": "
192: + nested.toString());
193: //any kind of fault: not reachable.
194: reachable = false;
195: }
196: } catch (NoSuchMethodException e) {
197: //java1.4 or earlier
198: log("Not found: InetAddress." + METHOD_NAME,
199: Project.MSG_VERBOSE);
200: log(MSG_NO_REACHABLE_TEST);
201: reachable = true;
202:
203: }
204:
205: log("host is" + (reachable ? "" : " not") + " reachable",
206: Project.MSG_VERBOSE);
207: return reachable;
208: }
209: }
|