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.optional.net;
020:
021: import org.apache.commons.net.telnet.TelnetClient;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.OutputStream;
025: import java.util.Calendar;
026: import java.util.Enumeration;
027: import java.util.Vector;
028: import org.apache.tools.ant.BuildException;
029: import org.apache.tools.ant.Project;
030: import org.apache.tools.ant.Task;
031:
032: /**
033: * Automates the telnet protocol.
034: *
035: */
036:
037: public class TelnetTask extends Task {
038: /**
039: * The userid to login with, if automated login is used
040: */
041: private String userid = null;
042:
043: /**
044: * The password to login with, if automated login is used
045: */
046: private String password = null;
047:
048: /**
049: * The server to connect to.
050: */
051: private String server = null;
052:
053: /**
054: * The tcp port to connect to.
055: */
056: private int port = 23;
057:
058: /**
059: * The list of read/write commands for this session
060: */
061: private Vector telnetTasks = new Vector();
062:
063: /**
064: * If true, adds a CR to beginning of login script
065: */
066: private boolean addCarriageReturn = false;
067:
068: /**
069: * Default time allowed for waiting for a valid response
070: * for all child reads. A value of 0 means no limit.
071: */
072: private Integer defaultTimeout = null;
073:
074: /**
075: * Verify that all parameters are included.
076: * Connect and possibly login
077: * Iterate through the list of Reads and writes
078: * @throws BuildException on error
079: */
080: public void execute() throws BuildException {
081: /** A server name is required to continue */
082: if (server == null) {
083: throw new BuildException("No Server Specified");
084: }
085: /** A userid and password must appear together
086: * if they appear. They are not required.
087: */
088: if (userid == null && password != null) {
089: throw new BuildException("No Userid Specified");
090: }
091: if (password == null && userid != null) {
092: throw new BuildException("No Password Specified");
093: }
094:
095: /** Create the telnet client object */
096: AntTelnetClient telnet = null;
097: try {
098: telnet = new AntTelnetClient();
099: try {
100: telnet.connect(server, port);
101: } catch (IOException e) {
102: throw new BuildException("Can't connect to " + server);
103: }
104: /** Login if userid and password were specified */
105: if (userid != null && password != null) {
106: login(telnet);
107: }
108: /** Process each sub command */
109: Enumeration tasksToRun = telnetTasks.elements();
110: while (tasksToRun != null && tasksToRun.hasMoreElements()) {
111: TelnetSubTask task = (TelnetSubTask) tasksToRun
112: .nextElement();
113: if (task instanceof TelnetRead
114: && defaultTimeout != null) {
115: ((TelnetRead) task)
116: .setDefaultTimeout(defaultTimeout);
117: }
118: task.execute(telnet);
119: }
120: } finally {
121: if (telnet != null && telnet.isConnected()) {
122: try {
123: telnet.disconnect();
124: } catch (IOException e) {
125: throw new BuildException(
126: "Error disconnecting from " + server);
127: }
128: }
129: }
130: }
131:
132: /**
133: * Process a 'typical' login. If it differs, use the read
134: * and write tasks explicitely
135: */
136: private void login(AntTelnetClient telnet) {
137: if (addCarriageReturn) {
138: telnet.sendString("\n", true);
139: }
140: telnet.waitForString("ogin:");
141: telnet.sendString(userid, true);
142: telnet.waitForString("assword:");
143: telnet.sendString(password, false);
144: }
145:
146: /**
147: * Set the the login id to use on the server;
148: * required if <tt>password</tt> is set.
149: * @param u a <code>String</code> value
150: */
151: public void setUserid(String u) {
152: this .userid = u;
153: }
154:
155: /**
156: * Set the the login password to use
157: * required if <tt>userid</tt> is set.
158: * @param p a <code>String</code> value
159: */
160: public void setPassword(String p) {
161: this .password = p;
162: }
163:
164: /**
165: * Set the hostname or address of the remote server.
166: * @param m a <code>String</code> value
167: */
168: public void setServer(String m) {
169: this .server = m;
170: }
171:
172: /**
173: * Set the tcp port to connect to; default is 23.
174: * @param p an <code>int</code> value
175: */
176: public void setPort(int p) {
177: this .port = p;
178: }
179:
180: /**
181: * send a carriage return after connecting; optional, defaults to false.
182: * @param b a <code>boolean</code> value
183: */
184: public void setInitialCR(boolean b) {
185: this .addCarriageReturn = b;
186: }
187:
188: /**
189: * set a default timeout in seconds to wait for a response,
190: * zero means forever (the default)
191: * @param i an <code>Integer</code> value
192: */
193: public void setTimeout(Integer i) {
194: this .defaultTimeout = i;
195: }
196:
197: /**
198: * A string to wait for from the server.
199: * A subTask <read> tag was found. Create the object,
200: * Save it in our list, and return it.
201: * @return a read telnet sub task
202: */
203:
204: public TelnetSubTask createRead() {
205: TelnetSubTask task = (TelnetSubTask) new TelnetRead();
206: telnetTasks.addElement(task);
207: return task;
208: }
209:
210: /**
211: * Add text to send to the server
212: * A subTask <write> tag was found. Create the object,
213: * Save it in our list, and return it.
214: * @return a write telnet sub task
215: */
216: public TelnetSubTask createWrite() {
217: TelnetSubTask task = (TelnetSubTask) new TelnetWrite();
218: telnetTasks.addElement(task);
219: return task;
220: }
221:
222: /**
223: * This class is the parent of the Read and Write tasks.
224: * It handles the common attributes for both.
225: */
226: public class TelnetSubTask {
227: // CheckStyle:VisibilityModifier OFF - bc
228: protected String taskString = "";
229:
230: // CheckStyle:VisibilityModifier ON
231: /**
232: * Execute the subtask.
233: * @param telnet the client
234: * @throws BuildException always as it is not allowed to instantiate this object
235: */
236: public void execute(AntTelnetClient telnet)
237: throws BuildException {
238: throw new BuildException(
239: "Shouldn't be able instantiate a SubTask directly");
240: }
241:
242: /**
243: * the message as nested text
244: * @param s the nested text
245: */
246: public void addText(String s) {
247: setString(getProject().replaceProperties(s));
248: }
249:
250: /**
251: * the message as an attribute
252: * @param s a <code>String</code> value
253: */
254: public void setString(String s) {
255: taskString += s;
256: }
257: }
258:
259: /**
260: * Sends text to the connected server
261: */
262: public class TelnetWrite extends TelnetSubTask {
263: private boolean echoString = true;
264:
265: /**
266: * Execute the write task.
267: * @param telnet the task to use
268: * @throws BuildException on error
269: */
270: public void execute(AntTelnetClient telnet)
271: throws BuildException {
272: telnet.sendString(taskString, echoString);
273: }
274:
275: /**
276: * Whether or not the message should be echoed to the log.
277: * Defaults to <code>true</code>.
278: * @param b a <code>boolean</code> value
279: */
280: public void setEcho(boolean b) {
281: echoString = b;
282: }
283: }
284:
285: /**
286: * Reads the output from the connected server
287: * until the required string is found or we time out.
288: */
289: public class TelnetRead extends TelnetSubTask {
290: private Integer timeout = null;
291:
292: /**
293: * Execute the read task.
294: * @param telnet the task to use
295: * @throws BuildException on error
296: */
297: public void execute(AntTelnetClient telnet)
298: throws BuildException {
299: telnet.waitForString(taskString, timeout);
300: }
301:
302: /**
303: * a timeout value that overrides any task wide timeout.
304: * @param i an <code>Integer</code> value
305: */
306: public void setTimeout(Integer i) {
307: this .timeout = i;
308: }
309:
310: /**
311: * Sets the default timeout if none has been set already
312: * @param defaultTimeout an <code>Integer</code> value
313: * @ant.attribute ignore="true"
314: */
315: public void setDefaultTimeout(Integer defaultTimeout) {
316: if (timeout == null) {
317: timeout = defaultTimeout;
318: }
319: }
320: }
321:
322: /**
323: * This class handles the abstraction of the telnet protocol.
324: * Currently it is a wrapper around <a
325: * href="http://jakarta.apache.org/commons/net/index.html">Jakarta
326: * Commons Net</a>.
327: */
328: public class AntTelnetClient extends TelnetClient {
329: /**
330: * Read from the telnet session until the string we are
331: * waiting for is found
332: * @param s The string to wait on
333: */
334: public void waitForString(String s) {
335: waitForString(s, null);
336: }
337:
338: /**
339: * Read from the telnet session until the string we are
340: * waiting for is found or the timeout has been reached
341: * @param s The string to wait on
342: * @param timeout The maximum number of seconds to wait
343: */
344: public void waitForString(String s, Integer timeout) {
345: InputStream is = this .getInputStream();
346: try {
347: StringBuffer sb = new StringBuffer();
348: if (timeout == null || timeout.intValue() == 0) {
349: while (sb.toString().indexOf(s) == -1) {
350: sb.append((char) is.read());
351: }
352: } else {
353: Calendar endTime = Calendar.getInstance();
354: endTime.add(Calendar.SECOND, timeout.intValue());
355: while (sb.toString().indexOf(s) == -1) {
356: while (Calendar.getInstance().before(endTime)
357: && is.available() == 0) {
358: Thread.sleep(250);
359: }
360: if (is.available() == 0) {
361: log("Read before running into timeout: "
362: + sb.toString(), Project.MSG_DEBUG);
363: throw new BuildException(
364: "Response timed-out waiting for \""
365: + s + '\"', getLocation());
366: }
367: sb.append((char) is.read());
368: }
369: }
370: log(sb.toString(), Project.MSG_INFO);
371: } catch (BuildException be) {
372: throw be;
373: } catch (Exception e) {
374: throw new BuildException(e, getLocation());
375: }
376: }
377:
378: /**
379: * Write this string to the telnet session.
380: * @param s the string to write
381: * @param echoString if true log the string sent
382: */
383: public void sendString(String s, boolean echoString) {
384: OutputStream os = this .getOutputStream();
385: try {
386: os.write((s + "\n").getBytes());
387: if (echoString) {
388: log(s, Project.MSG_INFO);
389: }
390: os.flush();
391: } catch (Exception e) {
392: throw new BuildException(e, getLocation());
393: }
394: }
395: }
396: }
|