/** * @(#)JavaCGIBridgeExtension.java 2.00 07/18/98 * * Copyright Info: This class was written by Gunther Birznieks * gunther@clark.net having been inspired by countless other * software developers. * * Version 1.00: 08/10/97 * Version 2.00: 07/19/98 * * The second version of this software was written for * independent study under the guidance of Professor * Marty Hall as part of the Johns Hopkins University * Computer Science Master's Degree program. * * Feel free to copy, cite, reference, sample, borrow, resell * or plagiarize the contents. However, if you don't mind, * please let me know where it goes so that I can at least * watch and take part in the development of the memes. Information * wants to be free; support public domain freeware. * * Donations are appreciated. If you use this software please * consider making a donation to the Electronic Frontier Foundation * http://www.eff.org/ or another organization of your choice * dedicated to defending liberty on the net. * */ package com.extropia.net; import java.util.Hashtable; import java.util.Vector; import java.util.Observable; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.net.MalformedURLException; import java.io.ObjectOutputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.DataOutputStream; import java.io.ByteArrayInputStream; import java.io.ObjectInputStream; import java.io.IOException; import java.io.StreamCorruptedException; import java.io.OptionalDataException; import java.applet.AppletContext; /** * This class extends the core JavaCGIBridge class. It contains * helper methods which may be useful for programmers communicating with * a web server in a browser environment, but are not as likely * to be used by every applet. *

* The main helper method subsets are as follows: *

* [1] Serialization Support. Applets can send serialized Objects to * and from a Web Server. Typically this will be used with CGI * programs written in Java or with Java Servlets. However, serialization * can also be a useful way of storing an applet's state on the server. * In this case, another language such as Perl can easily serve as * a conduit through which the serialized objects are stored as files * on the web server. *

* [2] fetch records support. Typically, you will get all the records * at once with getParsedData() or use the observer interface to * keep track of records as they are parsed on the fly. However, * if the observer interface seems like it is too much overhead * to set up, the fetchNextRecord() method provides an intermediate * alternative to getting all the records at the end of the entire * response and the overhead of setting up an observer interface in * your applet. *

* [3] show document support. Mac Netscape 3.x and below have a * bug in the showDocument method. The show document method here * provides a cross-browser way of displaying other HTML pages. * For more information on Cross-Browser Bugs, view the web site * devoted to these topics at http://gunther.web66.com/crossjava/. *

* In addition, show document also supports passing the JavaCGIBridge * form variables Hashtable data structure. JavaCGIBridgeExtension's * showDocument() method will automatically URL encoded and add the * form variables to the URL so that a CGI program can be called with * the GET method from showDocument(). * * @version 2.00, 18 Jul 1998 * @author Gunther Birznieks * @author Marty Hall *

*/ public class JavaCGIBridgeExtension extends JavaCGIBridge { /** * long which contains a runnning counter of how many * documents have been shown with the showDocument() * method. This is done to resolve a bug in Mac Netscape * 3.0 showDocument() method. *

* @see #showDocument */ private static long _showDocCount = 0; /** * Constructs the object with new separator values */ public JavaCGIBridgeExtension(String field, String record, String startData, String endData) { super(field,record,startData,endData); } /** * Constructs the object with no initialization. This is the default * (empty) constructor. */ public JavaCGIBridgeExtension() { } // Empty constructor /** * Obtains a ByteArrayOutputStream. This is used to store * raw data to send to a CGI Script. A more advanced version of * this method is getObjectOutputStream which is used to * send serialized objects as binary data over a byte array output * stream. You can use this method to bypass posting data with * normal URLEncoded data. This can be useful to post binary data * to a CGI script which will decode it in whatever way you choose * to program the parser. * * @return ByteArrayOutputStream * @see #getObjectOutputStream */ public ByteArrayOutputStream getByteArrayOutputStream() throws IOException { _serialStream = new ByteArrayOutputStream(); return _serialStream; } // end of getByteArrayOutputStream /** * Obtains an ObjectOutputStream. This is used to store the objects * which will be serialized and streamed to the web server. *

* Example Usage: *

*

     * JavaCGIBridgeExtension jcbe = new JavaCGIBridgeExtension();
     * ObjectOutputStream oos = jcbe.getObjectOutputStream();
     * oos.writeObject(myFirstObject);
     * oos.writeObject(mySecondObject);
     * oos.close();
     * ObjectInputStream ois = jcbe.getObjectData(myURL);
     * 
*

* @return ObjectOutputStream * @see #getObjectData */ public ObjectOutputStream getObjectOutputStream() throws IOException { _serialStream = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(_serialStream); return oos; } // end of getObjectOutputStream /** * Obtains an ObjectInputStream that encapsulates the stream * of data being returned from the web server. *

* It follows the same basic syntax of getRawData() and * getParsedData() methods from the core JavaCGIBridge class. *

* @param u URL to get object data from * @return ObjectInputStream data stream to deserialize objects from * @exception JavaCGIBridgeTimeOutException If the retrieval times out * @exception StreamCorruptedException If something is wrong with the stream * of serialized objects. * @exception IOException If there is something wrong with the stream * other than being in the wrong format for retrieving * serialized objects. */ public ObjectInputStream getObjectData(URL u) throws JavaCGIBridgeTimeOutException, StreamCorruptedException, IOException { return getObjectData(u, null); } // end of getObjectData /** * Obtains an ObjectInputStream that encapsulates the stream * of data being returned from the web server. *

* It follows the same basic syntax of getRawData() and * getParsedData() methods from the core JavaCGIBridge class. *

* @param u URL to get object data from * @param ht Hashtable containing form variables to POST * @return ObjectInputStream data stream to deserialize objects from * @exception JavaCGIBridgeTimeOutException If the retrieval times out * @exception StreamCorruptedException If something is wrong with the stream * of serialized objects. * @exception IOException If there is something wrong with the stream * other than being in the wrong format for retrieving * serialized objects. */ public ObjectInputStream getObjectData(URL u, Hashtable ht) throws JavaCGIBridgeTimeOutException, StreamCorruptedException, IOException { ByteArrayInputStream bais = new ByteArrayInputStream(getRawData(u,ht)); ObjectInputStream ois= new ObjectInputStream(bais); return ois; } // end of getObjectData /** * Obtains a Vector containing the fields of the individual record * returned from parsing the records currently being processed by * the JavaCGIBridge object. *

* This method is useful for users who wish to retrieve results before * the entire response has been sent from the Web Server yet without * the programmatic overhead of using the observer interface. For example, * you may want to start displaying the first 100 or so records of data * to the user right away even if there may be a lot more records to * retrieve eventually such as 10,000. *

* Please note that you should be aware that there are multi-threading * concerns when using this method. If you are using JavaCGIBridgePool * to manage your objects, then be aware that the JavaCGIBridge object * delibrately does not set processing to IDLE at the end of processing * when fetchNextRecord is being called. This behavior is activated * by passing "true" to the callOneWay method. *

* Because it is not set to IDLE, JavaCGIBridgePool has no way of * knowing that you are done with the objects. There are two ways to * set the JavaCGIBridge object idle. The first and easiest way * demonstrated below), is to simply iterate over fetchNextRecord() * until it returns null (end of records). When it does this, the * JavaCGIBridge will immediately be set to the IDLE state. The second * way is to manually called setStatus(JavaCGIBridge.IDLE) yourself. *

* If you are not using JavaCGIBridgePool, then this need to set the object * IDLE is not necesssary if you do not plan on reusing the object itself. *

* Example Usage: *

*

     * Vector v;
     * JavaCGIBridgeExtension jcbe = new JavaCGIBridgeExtension();
     * // true is passed because we are fetching records after the
     * // one-way operation
     * jcbe.callOneWay(myURL, true);
     * while (null != (v = jcbe.fetchNextRecord())) {
     *     // Do something with Vector v
     * }
     * 
*

* @return Vector a vector containing the fields of parsed data * @exception JavaCGIBridgeTimeOutException If the retrieval times out */ public Vector fetchNextRecord() throws JavaCGIBridgeTimeOutException { while(!_threadCompleted && (_vectorParsedData == null || // Note that we will always be behind one record _fetchCount + 2 > _vectorParsedData.size() )) { Thread.yield(); if (_threadTimeOut < (System.currentTimeMillis() - _threadBaseTime)) { this._thread.stop(); this.setStatus(IDLE); throw new JavaCGIBridgeTimeOutException(); } } if (_threadCompleted && (_vectorParsedData == null || _fetchCount == _vectorParsedData.size())) { setStatus(IDLE); return null; } _fetchCount++; _threadBaseTime = System.currentTimeMillis(); return ((Vector)_vectorParsedData.elementAt(_fetchCount - 1)); } // end of fetchNextRecord /** * Tells the browser to show a document. Basically the difference between * this showDocument() and AppletContext's showDocument() method is that * this one fixes some cross platform problems with the Mac Netscape * 3.x implementation of showDocument(). *

* @param ac AppletContext * @param u URL to load * @param target Target to load document in. Same as target in * AppletContext.showDocument() */ public void showDocument(AppletContext ac, URL u, String target) { showDocument(ac,u,target,null,true); } // end of showDocument /** * Tells the browser to show a document. Basically the difference between * this showDocument() and AppletContext's showDocument() method is * that this one fixes some cross platform problems with the Mac Netscape * 3.x implementation of showDocument(). *

* In addition, you can pass a Hashtable with form variables which will be * used to construct a URL encoded query string to append to the URL itself. *

* @param ac AppletContext * @param u URL to load * @param target Target to load document in. Same as target in * AppletContext.showDocument(). * @param formVars Hashtable contains form variables to add to GET method */ public void showDocument(AppletContext ac, URL u, String target, Hashtable formVars) { showDocument(ac,u,target,formVars,true); } // end of showDocument() /** * Tells the browser to show a document. Basically the difference between * this showDocument() and AppletContext's showDocument() method is * that this one fixes some cross platform problems with the Mac Netscape * 3.x implementation of showDocument(). *

* In addition, you can pass a Hashtable with form variables which will be * used to construct a URL encoded query string to append to the URL itself. *

* @param ac AppletContext * @param u URL to load * @param target Target to load document in. Same as target in * AppletContext.showDocument(). * @param formVars Hashtable contains form variables to add to GET method * @param fixMacBug boolean indicating whether or not the code for fixing the * Mac Bug should be activated. There may be intranet * applications where you may choose not to activate the * Mac Bug fix feature. */ public void showDocument(AppletContext ac, URL u, String target, Hashtable formVars, boolean fixMacBug) { URL newURL = u; if (fixMacBug && target == "_blank") { target = "_doc" + _showDocCount; _showDocCount++; } if (formVars != null) { try { newURL = new URL(u.toString() + "?" + getURLEncodedHashtable(formVars)); } catch (MalformedURLException e) { e.printStackTrace(); } } ac.showDocument(newURL, target); if (fixMacBug) { String osName = System.getProperty("os.name").toLowerCase(); String browser = System.getProperty("java.vendor").toLowerCase(); if ((osName.indexOf("mac") != -1 && browser.indexOf("netscape") != -1)) { ac.showDocument(newURL, target); } } } // end of showDocument() } // End of JavaCGIBridgeExtension class