0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 2001-2004 BBNT Solutions, LLC
0005: * under sponsorship of the Defense Advanced Research Projects
0006: * Agency (DARPA).
0007: *
0008: * You can redistribute this software and/or modify it under the
0009: * terms of the Cougaar Open Source License as published on the
0010: * Cougaar Open Source Website (www.cougaar.org).
0011: *
0012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0023: *
0024: * </copyright>
0025: */
0026: package org.cougaar.lib.vishnu.client;
0027:
0028: import org.apache.xerces.parsers.SAXParser;
0029: import org.cougaar.lib.param.ParamMap;
0030: import org.cougaar.util.log.Logger;
0031: import org.w3c.dom.Document;
0032: import org.xml.sax.helpers.DefaultHandler;
0033:
0034: import java.io.BufferedOutputStream;
0035: import java.io.BufferedReader;
0036: import java.io.File;
0037: import java.io.FileInputStream;
0038: import java.io.FileNotFoundException;
0039: import java.io.FileOutputStream;
0040: import java.io.FileWriter;
0041: import java.io.IOException;
0042: import java.io.InputStream;
0043: import java.io.InputStreamReader;
0044: import java.io.OutputStream;
0045: import java.net.Socket;
0046: import java.net.URL;
0047: import java.net.URLConnection;
0048: import java.net.UnknownHostException;
0049: import java.util.Date;
0050:
0051: /**
0052: * Abstractions of communication for Vishnu <p>
0053: *
0054: * Hides whether we're running internally or externally from callers. <p>
0055: *
0056: * Knows about command-to-URL mapping, e.g. postCancel becomes a URL calling
0057: * the php script postcancel.php. <p>
0058: *
0059: * Uses an internalBuffer to write to when we're running internally. It holds
0060: * the same bytes as are sent to the web server when running externally.
0061: **/
0062: public class VishnuComm {
0063: /**
0064: * Sets the problem name <p>
0065: * calls postCancel and postClear if running externally
0066: */
0067: public VishnuComm(ParamMap myParamTable, String name,
0068: String clusterName, VishnuDomUtil domUtil,
0069: boolean runInternal, Logger logger) {
0070: this .logger = logger;
0071: this .myParamTable = myParamTable;
0072:
0073: localSetup();
0074: this .name = name;
0075: this .clusterName = clusterName;
0076: this .domUtil = domUtil;
0077:
0078: URL = "http://" + hostName + phpPath;
0079:
0080: setProblemName();
0081:
0082: this .runInternal = runInternal;
0083:
0084: // clears any pending jobs for this problem
0085: if (!runInternal) {
0086: postCancel();
0087: postClear();
0088: }
0089: }
0090:
0091: protected ParamMap getMyParams() {
0092: return myParamTable;
0093: }
0094:
0095: protected String getName() {
0096: return name;
0097: }
0098:
0099: protected String getClusterName() {
0100: return clusterName;
0101: }
0102:
0103: public String getProblem() {
0104: return myProblem;
0105: }
0106:
0107: /** sets a variety of parameters */
0108: protected void localSetup() {
0109: try {
0110: if (getMyParams().hasParam("hostName"))
0111: hostName = getMyParams().getStringParam("hostName");
0112: else
0113: hostName = "dante.bbn.com";
0114:
0115: if (getMyParams().hasParam("phpPath"))
0116: phpPath = getMyParams().getStringParam("phpPath");
0117: else
0118: phpPath = "/~dmontana/vishnu/";
0119:
0120: if (getMyParams().hasParam("user"))
0121: myUser = getMyParams().getStringParam("user");
0122: else
0123: myUser = "vishnu";
0124:
0125: if (getMyParams().hasParam("password"))
0126: myPassword = getMyParams().getStringParam("password");
0127: else
0128: myPassword = "vishnu";
0129:
0130: if (getMyParams().hasParam("legalHosts"))
0131: myLegalHosts = getMyParams().getStringParam(
0132: "legalHosts").trim();
0133: else
0134: myLegalHosts = ""; // empty = all schedulers are OK
0135:
0136: if (getMyParams().hasParam("postProblemFile"))
0137: postProblemFile = getMyParams().getStringParam(
0138: "postProblemFile");
0139: else
0140: postProblemFile = "postproblem" + PHP_SUFFIX;
0141:
0142: if (getMyParams().hasParam("postDataFile"))
0143: postDataFile = getMyParams().getStringParam(
0144: "postDataFile");
0145: else
0146: postDataFile = "postdata" + PHP_SUFFIX;
0147:
0148: if (getMyParams().hasParam("kickoffFile"))
0149: kickoffFile = getMyParams().getStringParam(
0150: "kickoffFile");
0151: else
0152: kickoffFile = "postkickoff" + PHP_SUFFIX;
0153:
0154: if (getMyParams().hasParam("readStatusFile"))
0155: readStatusFile = getMyParams().getStringParam(
0156: "readStatusFile");
0157: else
0158: readStatusFile = "readstatus" + PHP_SUFFIX;
0159:
0160: if (getMyParams().hasParam("assignmentsFile"))
0161: assignmentsFile = getMyParams().getStringParam(
0162: "assignmentsFile");
0163: else
0164: assignmentsFile = "assignments" + PHP_SUFFIX;
0165:
0166: if (getMyParams().hasParam("showTiming"))
0167: showTiming = getMyParams()
0168: .getBooleanParam("showTiming");
0169: else
0170: showTiming = true;
0171:
0172: if (getMyParams().hasParam("testing"))
0173: testing = getMyParams().getBooleanParam("testing");
0174: else
0175: testing = false;
0176:
0177: // writes the XML sent to Vishnu web server to a file (human readable)
0178: if (getMyParams().hasParam("writeXMLToFile"))
0179: writeXMLToFile = getMyParams().getBooleanParam(
0180: "writeXMLToFile");
0181: else
0182: writeXMLToFile = false;
0183:
0184: // writes the XML sent to Vishnu web server to a file (machine readable)
0185: if (getMyParams().hasParam("writeEncodedXMLToFile"))
0186: writeEncodedXMLToFile = getMyParams().getBooleanParam(
0187: "writeEncodedXMLToFile");
0188: else
0189: writeEncodedXMLToFile = false;
0190:
0191: // seconds - total wait time is maxWaitCycles * waitTime
0192: if (getMyParams().hasParam("waitTime"))
0193: waitTime = getMyParams().getLongParam("waitTime") * 1000L;
0194: else
0195: waitTime = 5000L;
0196:
0197: // how many times to poll Vishnu before giving up
0198: // total wait time is maxWaitCycles * waitTime
0199: if (getMyParams().hasParam("maxWaitCycles"))
0200: maxWaitCycles = getMyParams().getIntParam(
0201: "maxWaitCycles");
0202: else
0203: maxWaitCycles = 10;
0204:
0205: } catch (Exception e) {
0206: }
0207: }
0208:
0209: /**
0210: * serialize document and post as data to web server or internal buffer
0211: *
0212: * Just calls serializeAndPost with correct arguments.
0213: * @see #serializeAndPost
0214: **/
0215: public void serializeAndPostData(Document doc) {
0216: serializeAndPost(doc, true, runInternal, internalBuffer);
0217: }
0218:
0219: /**
0220: * serialize document and post as problem definition to web server or internal buffer
0221: *
0222: * Just calls serializeAndPost with correct arguments.
0223: * @see #serializeAndPost
0224: */
0225: public void serializeAndPostProblem(Document doc) {
0226: serializeAndPost(doc, false, runInternal, internalBuffer);
0227: }
0228:
0229: /**
0230: * post the Document <code>doc</code> to a URL. <p>
0231: * <br>
0232: * If <code>writeXMLToFile</code> is set, will write a copy of what is
0233: * sent to the URL to a file named ClusterName_type_number, where type is
0234: * problem (the problem definition) or data (the tasks and resources), and
0235: * number is a counter that keeps the file names unique <p>
0236: * <br>
0237: * What's written to the file is human readable, whereas if
0238: * <code>writeEncodedXMLToFile</code> is set, a different file is written,
0239: * named ClusterName_encoded_number. This file contains exactly what is
0240: * sent to the web server, after URL encoding has been performed. <p>
0241: *
0242: * Does the work of serialization here, by calling DomUtil function getDocAsArray,
0243: * and lets the other signature do actual posting.
0244: *
0245: * @see VishnuDomUtil#getDocAsArray
0246: * @param doc - DOM doc to send to URL
0247: * @param postData - true if posting data
0248: */
0249: protected void serializeAndPost(Document doc, boolean postData,
0250: boolean runInternal, StringBuffer internalBuffer) {
0251: serializeAndPost(domUtil.getDocAsArray(doc).toString(),
0252: postData, runInternal, internalBuffer);
0253:
0254: if (writeXMLToFile) {
0255: String suffix = (postData) ? "data" : "problem";
0256: String fileName = getClusterName() + "_" + suffix + "_"
0257: + numFilesWritten++ + ".xml";
0258: logger.info(getName()
0259: + ".serializeAndPost - Writing XML to file "
0260: + fileName);
0261: try {
0262: FileOutputStream temp = new FileOutputStream(fileName);
0263: domUtil.writeDocToStream(doc, temp);
0264: } catch (FileNotFoundException fnfe) { /* never happen */
0265: } catch (IOException ioe) { /* never happen */
0266: }
0267: }
0268: }
0269:
0270: /** Returns an array of Strings with each element containing a line from the given file.
0271: *
0272: * returns null if any error
0273: */
0274: public String readBufferFromFile(String filename) {
0275: // Verify that the file exists.
0276: File inFile = new File(filename);
0277: if (!inFile.exists() || !inFile.isFile()) {
0278: System.out.println("File " + filename
0279: + " is an invalid file");
0280: return null;
0281: }
0282:
0283: StringBuffer sb = null;
0284: try {
0285: int fileLength = (int) inFile.length();
0286: sb = new StringBuffer(fileLength); // for efficient us of string buffer
0287:
0288: // Read in the information from the file into an array of Strings
0289: BufferedReader inReader = new BufferedReader(
0290: new InputStreamReader(new FileInputStream(inFile)));
0291:
0292: String s;
0293: while (true) {
0294: s = inReader.readLine();
0295: if (s == null)
0296: break;
0297: sb.append(s + "\n");
0298: }
0299: } catch (Exception e) {
0300: System.out.println("Error while reading file:" + filename);
0301: return null;
0302: }
0303:
0304: return sb.toString();
0305: }
0306:
0307: public void writeBufferToFile(String suffix, String buffer) {
0308: String fileName = getClusterName() + "_" + suffix + "_"
0309: + numFilesWritten++ + ".xml";
0310: logger.info(getName()
0311: + ".writeBufferToFile - Writing XML to file "
0312: + fileName);
0313: try {
0314: FileWriter writer = new FileWriter(fileName);
0315: writer.write(buffer);
0316: writer.close();
0317: } catch (FileNotFoundException fnfe) { /* never happen */
0318: } catch (IOException ioe) { /* never happen */
0319: }
0320: }
0321:
0322: /** Overloaded: postfix is pre-determined. */
0323: public void writeBufferToFile_withBackup(String suffix,
0324: String postfix, String buffer) {
0325: String baseFileName = getClusterName() + "_" + suffix + postfix;
0326: File baseFile = new File(baseFileName);
0327: if (baseFile.exists()) {
0328: String backupFileName = getClusterName() + "_" + suffix
0329: + "_" + numFilesWritten++ + postfix;
0330: File backupFile = new File(backupFileName);
0331: boolean success;
0332: if (backupFile.exists()) {
0333: success = backupFile.delete();
0334: if (success)
0335: logger
0336: .info(getName()
0337: + ".writeBufferToFile_withBackup - successful delete of file : "
0338: + backupFileName);
0339: else
0340: logger
0341: .info(getName()
0342: + ".writeBufferToFile_withBackup - unsuccessful delete of file : "
0343: + backupFileName);
0344: }
0345:
0346: success = baseFile.renameTo(backupFile);
0347: if (success)
0348: logger
0349: .info(getName()
0350: + ".writeBufferToFile_withBackup - successful rename of file : "
0351: + baseFileName + " to "
0352: + backupFileName);
0353: else
0354: logger
0355: .info(getName()
0356: + ".writeBufferToFile_withBackup - successful rename of file : "
0357: + baseFileName + " to "
0358: + backupFileName);
0359: }
0360:
0361: logger.info(getName()
0362: + ".writeBufferToFile - Writing buffer to file "
0363: + baseFileName);
0364: try {
0365: FileWriter writer = new FileWriter(baseFileName);
0366: writer.write(buffer);
0367: writer.close();
0368: } catch (FileNotFoundException fnfe) { /* never happen */
0369: } catch (IOException ioe) { /* never happen */
0370: }
0371: }
0372:
0373: /**
0374: * <pre>
0375: * Does most of the work of posting, the other signature does the serializing.
0376: *
0377: * If running internally, just appends the data to the buffer. Otherwise tries to
0378: * to either post data or post the problem
0379: *
0380: * Only called by other serializeAndPost
0381: *
0382: * </pre>
0383: * @see #postProblem
0384: * @see #postData
0385: **/
0386: protected void serializeAndPost(String doc, boolean postData,
0387: boolean runInternal, StringBuffer internalBuffer) {
0388: if (runInternal)
0389: appendToInternalBuffer(doc, internalBuffer);
0390: else {
0391: if (postData) {
0392: if (!postData(doc)) {
0393: showPostDataWarning();
0394: }
0395: } else
0396: postProblem(doc);
0397: }
0398: }
0399:
0400: /** adds data to internal buffer */
0401: public void appendToBuffer(String data) {
0402: appendToInternalBuffer(data, internalBuffer);
0403: }
0404:
0405: /** adds data to internal buffer, removing any xml header */
0406: protected void appendToInternalBuffer(String data,
0407: StringBuffer internalBuffer) {
0408: int index;
0409: if ((index = data.indexOf("?>")) != -1) {
0410: String stuff = data.substring(index + 2);
0411: internalBuffer.append(stuff);
0412: }
0413: }
0414:
0415: /**
0416: * Clears internal buffer <p>
0417: *
0418: * Called by various methods in InternalMode, after having asked scheduler to parse data
0419: * in the buffer.
0420: * @see InternalMode#prepareScheduler
0421: **/
0422: public void clearBuffer() {
0423: internalBuffer = new StringBuffer(INITIAL_INTERNAL_BUFFER_SIZE);
0424: }
0425:
0426: /** return string representation of buffer */
0427: public String getBuffer() {
0428: return new String(internalBuffer);
0429: }
0430:
0431: /** dumps warning to stdout when problem definition is out of sync with data format */
0432: protected void showPostDataWarning() {
0433: logger
0434: .info("\n-----------------------------------------------\n"
0435: + getName()
0436: + ".serializeAndPost - got an error posting data.\n"
0437: + "\nThis could be due to one of several causes :\n"
0438: + "1) Connection problems with the web server, if running with a web server OR \n"
0439: + "2) An inconsistency between the object format defined for the problem and\n"
0440: + " the data. You may have to regenerate your object format definition file if you\n"
0441: + " see messages like:\n"
0442: + "<DIV align=left>\n"
0443: + "Context: parsing data<BR>\n"
0444: + "Action: object<BR>\n"
0445: + "Identifier: <BR>\n"
0446: + "Command: insert into obj_Package values ();<BR>\n"
0447: + "Database: vishnu_prob_TRANSCOM_pumpernickle<BR>\n"
0448: + "Error Text: You have an error in your SQL syntax near ');' at line 1<BR><BR>\n"
0449: + "</DIV>\n\n"
0450: + "The problem is that the scheduler is expecting the input tasks and assets \n"
0451: + "to be consistent with the object format, but an unexpected field or object\n"
0452: + "is being sent.\n"
0453: + "For more information, contact Gordon Vidaver, gvidaver@bbn.com, 617 873 3558\n"
0454: + "-----------------------------------------------");
0455: }
0456:
0457: /**
0458: * <pre>
0459: * sets Problem name used by Vishnu
0460: *
0461: * Appends the machine name to divide the name space of problems
0462: * automatically.
0463: *
0464: * For example, if there were an expander in the
0465: * AsmaraTFSP cluster, run on a machine named pumpernickle,
0466: * the problem name would be
0467: * AsmaraTFSP_pumpernickle
0468: *
0469: * </pre>
0470: */
0471: protected void setProblemName() {
0472: try {
0473: if (getMyParams().hasParam("problemName")) {
0474: myProblem = getMyParams().getStringParam("problemName");
0475: } else {
0476: myProblem = getClusterName();
0477:
0478: // mysql doesn't like -'s
0479: myProblem = myProblem.replace('-', '_');
0480: }
0481: } catch (Exception e) {
0482: }
0483:
0484: try {
0485: String machineName = java.net.InetAddress.getLocalHost()
0486: .getHostName().replace('-', '_');
0487: if (machineName.indexOf('.') != -1) {
0488: machineName = machineName.substring(0, machineName
0489: .indexOf('.'));
0490: }
0491: machineName = machineName.replace('.', '_');
0492: myProblem = myProblem + "_" + machineName;
0493: } catch (UnknownHostException uhe) {
0494: logger.error(getName()
0495: + ".localSetup - Huh? Could not find localhost? "
0496: + uhe.getMessage(), uhe);
0497: }
0498: }
0499:
0500: /**
0501: * Posts data to web site or internal buffer, depending on mode <p>
0502: *
0503: * URL encodes the data <p>
0504: *
0505: * Calls postToURL with URL header and data
0506: * @see #postToURL
0507: * @param data to send to URL
0508: */
0509: public boolean postData(String data) {
0510: StringBuffer sb = new StringBuffer();
0511: sb.append("?" + "bogus=ferris&");
0512: sb.append(getProblemPostVar());
0513: sb.append(getInstancePostVar() + "&");
0514: sb.append("user=" + myUser + "&");
0515: sb.append("password=" + myPassword + "&");
0516: sb.append("data=");
0517: try {
0518: sb.append(java.net.URLEncoder.encode(data, "UTF-8"));
0519: } catch (Exception e) {
0520: logger.error("huh?", e);
0521: }
0522:
0523: Date start = new Date();
0524: String reply = postToURL(hostName, postDataFile, sb.toString(),
0525: null, true);
0526: if (showTiming)
0527: domUtil.reportTime(" - did post of data string to URL in ",
0528: start);
0529:
0530: if (!reply.startsWith("SUCCESS")) {
0531: logger.error(getName()
0532: + ".postData - ERROR : Reply to post data was <"
0533: + reply + ">");
0534: return false;
0535: } else if (logger.isInfoEnabled())
0536: logger.info(getName()
0537: + ".postData - Reply to post data was <"
0538: + reply.trim() + ">");
0539:
0540: return true;
0541: }
0542:
0543: /**
0544: * Posts problem to web site/internal buffer <p>
0545: *
0546: * URL encodes the data <p>
0547: *
0548: * Calls postToURL with URL header and data <p>
0549:
0550: * bogus is sent first because user would not arrive
0551: * at php with value if it was sent first. No idea why. <p>
0552: *
0553: * @see #postToURL
0554: * @param data to send to URL
0555: */
0556: public void postProblem(String data) {
0557: StringBuffer sb = new StringBuffer();
0558: sb.append("?" + "bogus=ferris&");
0559: sb.append("user=" + myUser + "&");
0560: sb.append("password=" + myPassword + "&");
0561: sb.append("data=");
0562: try {
0563: sb.append(java.net.URLEncoder.encode(data, "UTF-8"));
0564: } catch (Exception e) {
0565: logger.error("huh?", e);
0566: }
0567:
0568: String reply = postToURL(hostName, postProblemFile, sb
0569: .toString(), null, true);
0570:
0571: if (logger.isInfoEnabled())
0572: logger.info(getName() + ".postProblem - reply was <"
0573: + reply.trim() + ">");
0574: }
0575:
0576: /**
0577: * Cancels any pending jobs. <p>
0578: *
0579: * Should only be done once, when the plugin loads. <p>
0580: *
0581: * This is an insurance policy against the case where someone
0582: * starts a society and runs it, but never starts a scheduler, or otherwise
0583: * gets a problem into the state of "processing" but not "complete."
0584: * Once in the "processing" state, the scheduler will not accept new
0585: * jobs for this problem, effectively blocking it for all time.
0586: *
0587: * bogus is sent first because <code>user</code> would not arrive
0588: * at php with value if it was sent first. No idea why. <p>
0589: *
0590: * Calls postToURL with URL header
0591: * @see #postToURL
0592: */
0593: public void postCancel() {
0594: StringBuffer sb = new StringBuffer();
0595: sb.append("?" + "bogus=ferris&");
0596: sb.append(getProblemPostVar() + "&");
0597: sb.append("username=" + myUser + "&");
0598: sb.append("password=" + myPassword);
0599:
0600: if (logger.isInfoEnabled())
0601: logger
0602: .info(getName()
0603: + ".postCancel - canceling any pending scheduling requests for "
0604: + myProblem);
0605:
0606: String reply = postToURL(hostName, postCancelFile, sb
0607: .toString(), null, true);
0608: if (logger.isInfoEnabled())
0609: logger.info(getName() + ".postCancel - reply was " + reply);
0610: }
0611:
0612: /**
0613: * <pre>
0614: * Tells database or scheduler to forget all data associated with the problem
0615: *
0616: * Calls postToURL with URL header
0617: * </pre>
0618: * @see #postToURL
0619: */
0620: public void postClear() {
0621: StringBuffer sb = new StringBuffer();
0622: sb.append("?" + "bogus=ferris&");
0623: sb.append(getProblemPostVar() + "&");
0624: sb.append("user=" + myUser + "&");
0625: sb.append("password=" + myPassword);
0626:
0627: if (logger.isInfoEnabled())
0628: logger.info(getName()
0629: + ".postClear - clearing data from previous runs "
0630: + myProblem);
0631:
0632: String clearDataMsg = sb + "&<PROBLEM NAME=" + getProblem()
0633: + "/><CLEARDATABASE" + '\\' + ">";
0634:
0635: String reply = postToURL(hostName, postDataFile, clearDataMsg,
0636: null, true);
0637: if (logger.isInfoEnabled())
0638: logger.info(getName() + ".postClear - reply was " + reply);
0639: }
0640:
0641: /**
0642: * <pre>
0643: * Tells the scheduler to start.
0644: *
0645: * bogus is sent first because <code>user</code> would not arrive <br>
0646: * at php with it's value if it was sent first. No idea why. <p>
0647: *
0648: * BOZO - Still a problem???
0649: * Calls postToURL with URL header
0650: * </pre>
0651: * @see #postToURL
0652: */
0653: public void startScheduling() {
0654: StringBuffer sb = new StringBuffer();
0655: sb.append("?" + "bogus=ferris&" + getProblemPostVar());
0656: sb.append(getInstancePostVar() + "&");
0657: sb.append(getUserPostVar() + "&");
0658: sb.append("password=" + myPassword + "&");
0659: sb.append("legalhosts=" + myLegalHosts + "&");
0660: sb.append("ferris=bueller");
0661:
0662: String reply = postToURL(hostName, kickoffFile, sb.toString(),
0663: null, true);
0664: if (logger.isInfoEnabled())
0665: logger.info(getName()
0666: + ".startScheduling - reply to kickoff was "
0667: + reply.trim());
0668: }
0669:
0670: /**
0671: * <pre>
0672: * Polls the scheduler for it's status
0673: *
0674: * Only uses in external mode
0675: *
0676: * Sleeps between polls
0677: *
0678: * Calls postToURL with URL header
0679: * </pre>
0680: * @see ExternalMode#run
0681: * @see #postToURL
0682: */
0683: public boolean waitTillFinished() {
0684: String postVars = getWaitPostVars();
0685:
0686: boolean gotAnswer = false;
0687: for (int i = 0; i < maxWaitCycles; i++) {
0688: String response = postToURL(hostName, readStatusFile,
0689: postVars, null, true);
0690: if (response.indexOf(done) != -1) {
0691: gotAnswer = true;
0692: break;
0693: } else if (logger.isInfoEnabled()) {
0694: logger
0695: .info(getName()
0696: + ".waitTillFinished - Scheduler not done. Reply was <"
0697: + response.trim() + ">");
0698: }
0699:
0700: try {
0701: Thread.sleep(waitTime);
0702: } catch (Exception e) {
0703: }
0704: }
0705:
0706: return gotAnswer;
0707: }
0708:
0709: /**
0710: * <pre>
0711: * Get the answer back from the web site/scheduler
0712: *
0713: * Called by XMLResultHandler
0714: *
0715: * Calls readXML with the assignment handler
0716: *
0717: * </pre>
0718: * @see XMLResultHandler#parseAnswer
0719: * @see #readXML
0720: **/
0721: public void getAnswer(DefaultHandler assignmentHandler) {
0722: try {
0723: String url = "http://" + hostName + phpPath
0724: + assignmentsFile + getWaitPostVars();
0725: URL aURL = new URL(url);
0726:
0727: if (logger.isInfoEnabled())
0728: logger.info(getName() + ".getAnswer - reading from "
0729: + url);
0730:
0731: readXML(aURL, assignmentHandler);
0732: } catch (Exception e) {
0733: logger.error(getName() + ".getAnswer - BAD URL : " + e);
0734: e.printStackTrace();
0735: }
0736: }
0737:
0738: /** create wait URL */
0739: protected String getWaitPostVars() {
0740: StringBuffer sb = new StringBuffer();
0741: sb.append("?" + "bogus=ferris&" + getProblemPostVar());
0742: sb.append(getInstancePostVar() + "&");
0743: sb.append("user=" + myUser + "&");
0744: sb.append("password=" + myPassword + "&");
0745: sb.append("ferris=bueller");
0746: return sb.toString();
0747: }
0748:
0749: /** the problem post variable */
0750: protected String getProblemPostVar() {
0751: return "problem=" + myProblem + "&";
0752: }
0753:
0754: /** the problem instance post variable */
0755: protected String getInstancePostVar() {
0756: return "instance=" + myInstance;
0757: }
0758:
0759: /** the user post variable */
0760: protected String getUserPostVar() {
0761: return "user=" + myUser;
0762: }
0763:
0764: /**
0765: * Posts data to a URL, given the host, the php file, the URL header data, the doc to send
0766: *
0767: * @return response, if asked for one with <code>readResponse</code>
0768: */
0769: public String postToURL(String host, String fileToExec,
0770: String data, Document doc, boolean readResponse) {
0771: try {
0772: String url = "http://" + host + phpPath + fileToExec;
0773: if (testing) {
0774: logger.info("postToURL - (complete) Sending to : "
0775: + url);
0776: try {
0777: logger.info(java.net.URLDecoder.decode(data,
0778: "UTF-8"));
0779: } catch (Exception e) {
0780: logger.error("huh?", e);
0781: }
0782: } else if (logger.isInfoEnabled()) {
0783: logger
0784: .info("postToURL - (partial) Sending to : "
0785: + url);
0786: logger.info(data.substring(0,
0787: (data.length() > 100) ? 100 : data.length()));
0788: }
0789: return postToURL(new URL(url), data, doc, readResponse);
0790: } catch (Exception e) {
0791: logger.error("BAD URL : " + e, e);
0792: return "";
0793: }
0794: }
0795:
0796: /**
0797: * Called from other signature <p>
0798: *
0799: * Post data to the URL <br>
0800: * If readResponse is true, read the response back from the URL and return it.
0801: */
0802: public String postToURL(URL aURL, String data, Document doc,
0803: boolean readResponse) {
0804: try {
0805: URLConnection connection = aURL.openConnection();
0806: connection.setDoOutput(true);
0807: connection.setDoInput(readResponse);
0808:
0809: Date start = new Date();
0810: sendData(connection, data, doc);
0811: if (showTiming)
0812: domUtil.reportTime(" - postToURL sent " + data.length()
0813: + " chars of data in ", start);
0814:
0815: if (readResponse) {
0816: start = new Date();
0817: String response = getResponse(connection);
0818: if (showTiming)
0819: domUtil.reportTime(" - postToURL read response of "
0820: + response.length() + " in ", start);
0821: return response;
0822: }
0823: } catch (Exception e) {
0824: logger.error(
0825: "VishnuPlugin.postToURL -- exception sending data to URL : "
0826: + aURL + "\n" + e.getMessage(), e);
0827: }
0828: return "";
0829: }
0830:
0831: /**
0832: * Sends data on the connection. Writes to buffered stream that wraps the URL output stream. <br>
0833: *
0834: * Calls VishnuDomUtil.writeDocToStream to do most of work. <br>
0835: *
0836: * Optionally writes encoded XML to a file.
0837: * @see VishnuDomUtil#writeDocToStream
0838: */
0839: public void sendData(URLConnection connection, String data,
0840: Document doc) throws IOException {
0841: if (logger.isInfoEnabled()) {
0842: logger.info(name + ".sendData - Sending " + data.length()
0843: + " characters.");
0844: logger.info("\tData="
0845: + data.substring(0, (data.length() > 100) ? 100
0846: : data.length()));
0847: }
0848:
0849: OutputStream os = new BufferedOutputStream(connection
0850: .getOutputStream());
0851: byte[] bytes = data.getBytes();
0852: os.write(bytes);
0853: if (doc != null) {
0854: domUtil.writeDocToStream(doc, os);
0855: /*
0856: if (writeXMLToFile) {
0857: String fileName = getClusterName () + "_" + numFilesWritten++;
0858: debug ("Writing XML to file " + fileName);
0859: FileOutputStream temp = new FileOutputStream (fileName);
0860: writeDocToStream (doc, temp);
0861: }
0862: */
0863: } else if (writeEncodedXMLToFile) {
0864: String fileName = clusterName + "_encoded_"
0865: + numFilesWritten++;
0866: logger.info(name + ".sendData : Writing XML to file "
0867: + fileName);
0868: FileOutputStream temp = new FileOutputStream(fileName);
0869: bytes = data.getBytes();
0870: temp.write(bytes);
0871: temp.flush();
0872: temp.close();
0873: }
0874:
0875: os.flush();
0876: os.close();
0877: }
0878:
0879: /**
0880: * Returns response as string. <br>
0881: *
0882: * If there is an IOException on the input stream, will try two more times.
0883: *
0884: * @param connection the url connection to get data from
0885: * @return String reponse from URL
0886: */
0887: public String getResponse(URLConnection connection)
0888: throws IOException {
0889: StringBuffer sb = new StringBuffer();
0890: int numTries = 3;
0891: boolean madeInputStream = false;
0892: InputStream is = null;
0893:
0894: while (numTries > 0 && !madeInputStream) {
0895: try {
0896: is = connection.getInputStream();
0897: madeInputStream = true;
0898: } catch (IOException ioe) {
0899: logger
0900: .info(name
0901: + ".getResponse - IO Exception on reading from URL, trying again.");
0902: numTries--;
0903: try {
0904: Thread.sleep(5000l);
0905: } catch (Exception e) {
0906: }
0907: }
0908: }
0909: if (!madeInputStream) {
0910: logger.error(name
0911: + ".getResponse - ERROR : could not read from URL "
0912: + connection);
0913: return null;
0914: }
0915:
0916: byte b[] = new byte[1024];
0917: int len;
0918: while ((len = is.read(b)) > -1)
0919: sb.append(new String(b, 0, len));
0920:
0921: return sb.toString();
0922: }
0923:
0924: /** post to URL without using URLConnection -- currently not used */
0925: private static String socketPostToURL(String hostName,
0926: String phpPath, String url, String data,
0927: boolean readResponse, Logger logger) {
0928: try {
0929: Socket socket = new Socket(hostName, 80);
0930: OutputStream os = socket.getOutputStream();
0931: String request = "POST "
0932: + phpPath
0933: + url
0934: + " HTTP/1.0\r\n"
0935: + "Content-Type: application/x-www-form-urlencoded\r\n"
0936: + "Content-Length: " + data.length() + "\r\n\r\n"
0937: + data + "\r\n\r\n";
0938: os.write(request.getBytes());
0939: if (!readResponse)
0940: return "";
0941: InputStream is = socket.getInputStream();
0942: String result = "";
0943: byte[] b = new byte[1];
0944: while ((b[0] = (byte) is.read()) != -1)
0945: result = result + new String(b);
0946: return result;
0947: } catch (Exception e) {
0948: logger.error(e.getMessage(), e);
0949: }
0950: return "";
0951: }
0952:
0953: /**
0954: * Get XML back from URL and give it to a SAX Parser, running the handler
0955: *
0956: * @param aURL the URL to read from
0957: * @param handler will parse the XML coming from URL
0958: **/
0959: protected void readXML(URL aURL, DefaultHandler handler) {
0960: try {
0961: if (logger.isInfoEnabled()) {
0962: URLConnection connection = aURL.openConnection();
0963: connection.setDoOutput(false);
0964: connection.setDoInput(true);
0965:
0966: logger.info(getResponse(connection));
0967: }
0968:
0969: SAXParser parser = new SAXParser();
0970: parser.setContentHandler(handler);
0971: parser.parse(aURL.toString());
0972: } catch (Exception e) {
0973: logger.error(e.getMessage(), e);
0974: }
0975: }
0976:
0977: // necessary configuration parameters - info about the Vishnu web server and mysql user and password
0978: /** web server user */
0979: protected String myUser = "vishnu";
0980: /** web server password */
0981: protected String myPassword = "vishnu";
0982: /** web server host */
0983: protected String hostName = "dante.bbn.com";
0984: /** php path on web server host, relative to document root */
0985: protected String phpPath = "/~dmontana/vishnu/";
0986: /** root of the URL, set above */
0987: protected String URL = "http://" + hostName + phpPath;
0988: /** list of hosts the scheduler can run on */
0989: protected String myLegalHosts = "";
0990:
0991: protected String PHP_SUFFIX = ".php";
0992: /** php file to execute */
0993: protected String postProblemFile = "postproblem" + PHP_SUFFIX;
0994: /** php file to execute */
0995: protected String postDataFile = "postdata" + PHP_SUFFIX;
0996: /** php file to execute */
0997: protected String kickoffFile = "postkickoff" + PHP_SUFFIX;
0998: /** php file to execute */
0999: protected String readStatusFile = "readstatus" + PHP_SUFFIX;
1000: /** php file to execute */
1001: protected String assignmentsFile = "assignments" + PHP_SUFFIX;
1002: /** php file to execute */
1003: protected String postCancelFile = "postcancel" + PHP_SUFFIX;
1004: /** message from web server when problem is done */
1005: protected String done = "percent_complete=100";
1006:
1007: /** max times to wait between polls in waitTillFinished */
1008: protected int maxWaitCycles = 10;
1009:
1010: /** wait between polls in waitTillFinished */
1011: protected long waitTime = 1000;
1012:
1013: /** problem name */
1014: protected String myProblem = "testProblem";
1015: /** not used... */
1016: protected String myInstance = "testInstance";
1017:
1018: /** parameter -- dump timing results to stdout */
1019: protected boolean showTiming;
1020:
1021: /** parameter -- write complete URL to stdout -- little used */
1022: protected boolean testing;
1023: /** parameter -- write encoded xml to a file */
1024: protected boolean writeEncodedXMLToFile;
1025: /** parameter -- write xml to a file */
1026: protected boolean writeXMLToFile = false;
1027: /** name of the cluster+plugin */
1028: protected String name;
1029: /** name of the cluster */
1030: protected String clusterName;
1031: /** dom helper */
1032: protected VishnuDomUtil domUtil;
1033:
1034: /** how many files have been written out via the writeEncodedXMLToFile or writeXMLToFile flag */
1035: protected int numFilesWritten = 0;
1036:
1037: /** param table from plugin */
1038: protected ParamMap myParamTable;
1039: /** parameter -- run internally (when true) or externally */
1040: protected boolean runInternal;
1041: private static final int INITIAL_INTERNAL_BUFFER_SIZE = 16384; //2097152; // 2 M
1042:
1043: /** holds data posted to URLs when running internally */
1044: protected StringBuffer internalBuffer = new StringBuffer();
1045: protected Logger logger;
1046: }
|