// QueryTransaction.java // Simple asynchronous transaction class. // Copyright (c) 1996 by Paul Burchard // Distributed under the terms of the GNU Library General Public License //////////////////////////////////////////////////////////////// // Please do not use this code as an example of anything. // // It is a nest of workarounds for bugs in AWT and Netscape, // // for AWT's horrible design for event and layout management, // // for Netscape/Sun's severely broken thread implementation, // // and for Java's OOP-hostile network loading strategy. // //////////////////////////////////////////////////////////////// import java.applet.*; import java.lang.*; import java.io.*; import java.net.*; import java.util.*; class QueryTransaction extends Observable implements Runnable { static int currentSerial = 0; static long timeout = 60000; // one minute static long waitpoll = 4000; //!!! wait() is not reliably notify()-able; check status every few seconds int mySerial = -1; URL scriptURL, queryURL; String queryString; QueryTransaction prev, next; Thread runThread; Applet app; boolean waiting, complete; public QueryTransaction(QueryTransaction chainTrans, URL script) { if((prev = chainTrans) != null) prev = prev.chain(this); scriptURL = script; } public QueryTransaction(QueryTransaction chainTrans, URL script, String query) { if((prev = chainTrans) != null) prev = prev.chain(this); scriptURL = script; setQuery(query); } public QueryTransaction(QueryTransaction chainTrans, URL script, Hashtable query) { if((prev = chainTrans) != null) prev = prev.chain(this); scriptURL = script; setQuery(query); } public final void setQuery(String query) { if(query != null) queryString = urlEncodeText(query); else queryString = null; } public final void setQuery(Hashtable query) { if(query==null || query.isEmpty()) { queryString = null; } else { StringBuffer queryStringBuf = new StringBuffer(); String sep = ""; for(Enumeration i=query.keys(); i.hasMoreElements();) { Object k = i.nextElement(); queryStringBuf.append(sep).append(urlEncodeText(k.toString())).append( "=").append(urlEncodeText(query.get(k).toString())); if(sep.length() == 0) sep = "&"; } queryString = queryStringBuf.toString(); } } public int serial() { return mySerial; } public synchronized QueryTransaction chain(QueryTransaction nextTrans) { if(!complete) { next = nextTrans; return this; } else return null; } public synchronized void unchain() { QueryTransaction nextTrans; synchronized(this) { complete = true; nextTrans = next; prev = next = null; } if(nextTrans!=null && nextTrans.prev==this) nextTrans.notify(); } public synchronized boolean isComplete() { return complete; } public synchronized boolean isWaiting() { return waiting; } public void start(Observer callback) { addObserver(callback); start(); } public void start(Applet callback) { app = callback; start(); } public void start() { mySerial = currentSerial++; if(countObservers() > 0) { // Asynchronous query. if(queryString == null) queryURL = scriptURL; else { try { queryURL = new URL(scriptURL.toExternalForm() + "?" + queryString); } catch(MalformedURLException e) { queryURL = null; } } if(queryURL == null) unchain(); else { runThread = new Thread(this); runThread.setPriority(Thread.NORM_PRIORITY+2); runThread.start(); } } else if(app != null) { // No Observers to receive result; replace designated page instead. unchain(); if(queryString == null) app.getAppletContext().showDocument(scriptURL); else { try { queryURL = new URL(scriptURL.toExternalForm() + "?" + queryString); } catch(MalformedURLException e) { queryURL = null; } if(queryURL != null) app.getAppletContext().showDocument(queryURL); } } else { unchain(); } } public void run() { // Wait for previous transaction (if any) to finish. if(queryURL == null) { unchain(); return; } synchronized(this) { waiting = true; long bailout = System.currentTimeMillis() + timeout; while(waiting && prev!=null && !prev.isComplete()) { try { wait(waitpoll); if(System.currentTimeMillis()>=bailout && prev!=null && !prev.isComplete()) waiting = false; } catch(InterruptedException e) {} } } if(!waiting) { unchain(); setChanged(); notifyObservers(new RuntimeException("Server not responding, giving up on input line "+serial())); return; } synchronized(this) { waiting = false; prev = null; } // Perform transaction via URLConnection. StringBuffer output = new StringBuffer(); try { BufferedInputStream i = new BufferedInputStream(queryURL.openConnection().getInputStream()); setChanged(); for(int b=i.read(); b>=0; b=i.read()) output.append((char)b); //!!! inefficient? i.close(); } catch(Exception e) {} notifyObservers(output.toString()); unchain(); } public static String urlEncodeText(String s) { char[] dec = s.toCharArray(); char[] enc = new char[3*dec.length]; int i, j; for(i=j=0; i=0 && d2>=0) dec[j++] = (char)(16*d1 + d2); } else dec[j++] = c; } catch(IndexOutOfBoundsException e) {} return(new String(dec, 0, j)); } }