home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1995 November / PCWK1195.iso / inne / win95 / sieciowe / hotja32.lzh / hotjava / classsrc / net / www / html / url.java < prev    next >
Text File  |  1995-08-11  |  22KB  |  735 lines

  1. /*
  2.  * @(#)URL.java    1.61 95/05/21 Jonathan Payne, Chris Warth, James Gosling
  3.  *
  4.  * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, and distribute this software
  7.  * and its documentation for NON-COMMERCIAL purposes and without
  8.  * fee is hereby granted provided that this copyright notice
  9.  * appears in all copies. Please refer to the file "copyright.html"
  10.  * for further important copyright and licensing information.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
  13.  * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  14.  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  15.  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
  16.  * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
  17.  * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  18.  */
  19.  
  20. package net.www.html;
  21.  
  22. import java.util.*;
  23. import java.io.*;
  24. import awt.GifImage;
  25. import awt.XbmImage;
  26. import awt.Xpm2Image;
  27. import net.InetAddress;
  28. import net.UnknownHostException;
  29. import net.www.html.MalformedURLException;
  30. import browser.Observer;
  31. import browser.Observable;
  32.  
  33. /**
  34.  * Class URL represents a Uniform Reference Locator -- a textual reference
  35.  * to an object on the World Wide Web.  The public instance variables contains
  36.  * the broken apart fields of the parsed url.
  37.  */
  38.  
  39. public class URL {
  40.     private static Hashtable content_table = new Hashtable();
  41.     public static String content_unknown = "Unknown";
  42.     public static String content_octet;
  43.     public static String content_oda;
  44.     public static String content_pdf;
  45.     public static String content_postscript;
  46.     public static String content_richtext;
  47.     public static String content_bcpio;
  48.     public static String content_cpio;
  49.     public static String content_dvi;
  50.     public static String content_gtar;
  51.     public static String content_hdf;
  52.     public static String content_latex;
  53.     public static String content_netcdf;
  54.     public static String content_shar;
  55.     public static String content_sv4cpio;
  56.     public static String content_sv4crc;
  57.     public static String content_tar;
  58.     public static String content_tex;
  59.     public static String content_texinfo;
  60.     public static String content_troff;
  61.     public static String content_man;
  62.     public static String content_me;
  63.     public static String content_ms;
  64.     public static String content_ustar;
  65.     public static String content_source;
  66.     public static String content_zip;
  67.     public static String content_basic;
  68.     public static String content_aiff;
  69.     public static String content_wav;
  70.     public static String content_gif;
  71.     public static String content_ief;
  72.     public static String content_jpeg;
  73.     public static String content_tiff;
  74.     public static String content_rast;
  75.     public static String content_anymap;
  76.     public static String content_bitmap;
  77.     public static String content_graymap;
  78.     public static String content_pixmap;
  79.     public static String content_rgb;
  80.     public static String content_xbitmap;
  81.     public static String content_xpixmap;
  82.     public static String content_xwindowdump;
  83.     public static String content_rfc822;
  84.     public static String content_html;
  85.     public static String content_plain;
  86.     public static String content_values;
  87.     public static String content_setext;
  88.     public static String content_mpeg;
  89.     public static String content_quicktime;
  90.     public static String content_msvideo;
  91.     public static String content_movie;
  92.  
  93.     /**
  94.     The static initializer for this class must be completed before 
  95.     the static initializer for URLStreamHandler.  Therefore we create 
  96.     a static method here that is explcitly called by the static initializer of 
  97.         URLStreamHeandler, thus guarrenteeing the correct order of initialization.
  98.     */
  99.     public static void classInit() {
  100.     String ctarray [] = {
  101.         content_octet = "application/octet-stream",
  102.         content_oda = "application/oda",
  103.         content_pdf = "application/pdf",
  104.         content_postscript = "application/postscript",
  105.         content_richtext = "application/rtf",
  106.         content_bcpio = "application/x-bcpio",
  107.         content_cpio = "application/x-cpio",
  108.         content_dvi = "application/x-dvi",
  109.         content_gtar = "application/x-gtar",
  110.         content_hdf = "application/x-hdf",
  111.         content_latex = "application/x-latex",
  112.         content_netcdf = "application/x-netcdf",
  113.         content_shar = "application/x-shar",
  114.         content_sv4cpio = "application/x-sv4cpio",
  115.         content_sv4crc = "application/x-sv4crc",
  116.         content_tar = "application/x-tar",
  117.         content_tex = "application/x-tex",
  118.         content_texinfo = "application/x-texinfo",
  119.         content_troff = "application/x-troff",
  120.         content_man = "application/x-troff-man",
  121.         content_me = "application/x-troff-me",
  122.         content_ms = "application/x-troff-ms",
  123.         content_ustar = "application/x-ustar",
  124.         content_source = "application/x-wais-source",
  125.         content_zip = "application/zip",
  126.         content_basic = "audio/basic",
  127.         content_aiff = "audio/x-aiff",
  128.         content_wav = "audio/x-wav",
  129.         content_gif = "image/gif",
  130.         content_ief = "image/ief",
  131.         content_jpeg = "image/jpeg",
  132.         content_tiff = "image/tiff",
  133.         content_rast = "image/x-cmu-rast",
  134.         content_anymap = "image/x-portable-anymap",
  135.         content_bitmap = "image/x-portable-bitmap",
  136.         content_graymap = "image/x-portable-graymap",
  137.         content_pixmap = "image/x-portable-pixmap",
  138.         content_rgb = "image/x-rgb",
  139.         content_xbitmap = "image/x-xbitmap",
  140.         content_xpixmap = "image/x-xpixmap",
  141.         content_xwindowdump = "image/x-xwindowdump",
  142.         content_rfc822 = "message/rfc822",
  143.         content_html = "text/html",
  144.         content_plain = "text/plain",
  145.         content_values = "text/tab-separated-values",
  146.         content_setext = "text/x-setext",
  147.         content_mpeg = "video/mpeg",
  148.         content_quicktime = "video/quicktime",
  149.         content_msvideo = "video/x-msvideo",
  150.         content_movie = "video/x-sgi-movie"
  151.     };
  152.  
  153.     for (int i = 0; i < ctarray.length; i++) {
  154.         String ct = ctarray[i];
  155.  
  156.         URL.content_table.put(ct, ct);
  157.     }
  158.  
  159.     }
  160.  
  161.     /** only relevant if we're a post url */
  162.     public String postData = null;
  163.  
  164.     /** only relevant if we're a post url */
  165.     public URL    fromUrl;
  166.  
  167.     /** What protocol to use (ftp, http, nntp, ... etc) */
  168.     public String protocol;
  169.  
  170.     /** Host name to which to connect */
  171.     public String host;
  172.  
  173.     /** "File name" on that host */
  174.     public String file;
  175.  
  176.     /** # reference */
  177.     public String ref;
  178.  
  179.     /** Protocol port to connect to */
  180.     public int      port = -1;
  181.  
  182.     /** Format of media, only valid after the stream has been opened */
  183.     public String content_type;
  184.  
  185.     /** Create an absolute URL from the specified protocol,
  186.     host, and file. */
  187.     public URL (String p, String h, String f) {
  188.     protocol = p;
  189.     host = h;
  190.     file = f;
  191.     }
  192.  
  193.     public boolean isPostURL() {
  194.     return postData != null;
  195.     }
  196.  
  197.     /** Create a POST URL */
  198.     public URL(URL url, String post, URL from) {
  199.     this(url.protocol, url.host, url.file);
  200.     postData = post;
  201.     fromUrl = from;
  202.     }
  203.  
  204.     /** Create a URL from the unparsed url in the context of
  205.     the specified context.  If spec is an absolute URL,
  206.     cool, otherwise, parse it in terms of the context.
  207.     Context may be null (indicating no context). */
  208.     public URL (URL context, String spec) {
  209.     String    original = spec;
  210.     int i, limit, c;
  211.     int start = 0;
  212.     String    newProtocol = null;
  213.  
  214.     try {
  215.         limit = spec.length();
  216.         while (limit > 0 && spec.charAt(limit - 1) <= ' ')
  217.         limit--;    /* eliminate trailing whitespace */
  218.         while (start < limit && spec.charAt(start) <= ' ')
  219.         start++;    /* eliminate leading whitespace */
  220.         if (spec.startsWith("url:", start))
  221.         start += 4;
  222.         for (i = start; i < limit && (c = spec.charAt(i)) != '/'; i++)
  223.         if (c == ':') {
  224.             newProtocol = spec.substring(start, i);
  225.             start = i + 1;
  226.             break;
  227.         }
  228.  
  229.         /* Only use our context if the protocols match. */
  230.         if (context != null && (newProtocol == null ||
  231.                     newProtocol.equals(context.protocol))) {
  232.         copyURL(context);
  233.         } else {
  234.         protocol = newProtocol;
  235.         }
  236.  
  237.         if (protocol == null) {
  238.         /* turns into MalformedException below */
  239.         throw new Exception();
  240.         }
  241.  
  242.         if (start <= limit - 2 &&
  243.             spec.charAt(start) == '/' &&
  244.             spec.charAt(start + 1) == '/') {
  245.         start += 2;
  246.         i = spec.indexOf('/', start);
  247.         if (i < 0)
  248.             i = limit;
  249.         int prn = spec.indexOf(':', start);
  250.         port = -1;
  251.         if (prn < i && prn >= 0) {
  252.             try {
  253.             port = Integer.parseInt(spec.substring(prn + 1, i));
  254.             } catch(Exception e) {} /* ignore bogus port numbers */
  255.             if (prn > start)
  256.             host = spec.substring(start, prn);
  257.         } else
  258.             host = spec.substring(start, i);
  259.         start = i;
  260.         file = null;
  261.         } else if (host == null)
  262.         host = "";
  263.         i = spec.indexOf('#', start);
  264.         if (i >= 0) {
  265.         ref = spec.substring(i + 1, limit);
  266.         limit = i;
  267.         }
  268.         else ref = null;
  269.         if (start < limit)
  270.         if (spec.charAt(start) == '/')
  271.             file = spec.substring(start, limit);
  272.         else {
  273.             file = (file != null ?
  274.                 file.substring(0, file.lastIndexOf('/')) : "")
  275.             + "/" + spec.substring(start, limit);
  276.         }
  277.         if (file == null || file.length() == 0)
  278.         file = "/";
  279.         while ((i = file.indexOf("/./")) >= 0) {
  280.         file = file.substring(0, i) + file.substring(i + 2);
  281.         }
  282.         while ((i = file.indexOf("/../")) >= 0) {
  283.         if ((limit = file.lastIndexOf('/', i - 1)) >= 0)
  284.             file = file.substring(0, limit) + file.substring(i + 3);
  285.         else
  286.             file = file.substring(i + 3);
  287.         }
  288.     } catch (Exception e) {
  289.         throw new MalformedURLException(original + ": " + e);
  290.     }
  291.  
  292.     }
  293.  
  294.     /** Create a URL from the unparsed absolute URL */
  295.     public URL(String spec) {
  296.     this(null, spec);
  297.     }
  298.  
  299.     /**
  300.      * Copy another URL's "vital statistics" into us.
  301.      * @param    url The URL to copy.
  302.      */
  303.     protected void copyURL(URL url) {
  304.     protocol = url.protocol;
  305.     host = url.host;
  306.     file = url.file;
  307.     port = url.port;
  308.     }
  309.  
  310.     /**
  311.      * Compare two URLs.
  312.      * @param    o    The URL to compare against.
  313.      * @return    true iff they are equal, false otherwise.
  314.      */
  315.     public boolean equals(Object o) {
  316.     return (o instanceof URL) && sameFile((URL) o);
  317.     }
  318.  
  319.     /** Create an integer suitable for hash table indexing */
  320.     public int hashCode() {
  321.     return protocol.hashCode() ^ host.hashCode() ^ file.hashCode();
  322.     }
  323.  
  324.     /**
  325.      * Compare the host components of two URLs.
  326.      * @param    o    The URL to compare against.
  327.      * @return    true iff they are equal, false otherwise.
  328.      */
  329.     boolean hostsEqual(String h1, String h2) {
  330.     if (h1.equals(h2)) {
  331.         return true;
  332.     }
  333.     try {
  334.         InetAddress a1 = InetAddress.getByName(h1);
  335.         InetAddress a2 = InetAddress.getByName(h2);
  336.         return a1.equals(a2);
  337.     } catch (UnknownHostException e) {
  338.     }
  339.     return false;
  340.     }
  341.  
  342.     /**
  343.      * Compare two URLs, excluding the "ref" field: sameFile is true
  344.      * if the true reference the same remote object, but not necessarily
  345.      * the same subpiece of that object.
  346.      * @param    other    The URL to compare against.
  347.      * @return    true iff they are equal, false otherwise.
  348.      */
  349.     public boolean sameFile(URL other) {
  350.     return (!isPostURL() &&
  351.         protocol.equals(other.protocol)
  352.         && hostsEqual(host, other.host)
  353.         && port == other.port
  354.         && file.equals(other.file));
  355.     }
  356.  
  357.     /**
  358.      * Convert to a human-readable form.
  359.      * @return    The textual representation.
  360.      */
  361.     public String toString() {
  362.     String value = getClass().getName()
  363.         + "[protocol=" + protocol + ", host=" + host
  364.         + ", file=" + file;
  365.  
  366.     if (ref != null) {
  367.         value = value + ", ref=" + ref;
  368.     }
  369.     return value + "]";
  370.     }
  371.  
  372.     /**
  373.      * Reverse the parsing of the URL.
  374.      * @return    The textual representation of the fully qualified URL (ie.
  375.      *        after the context and canonicalization have been applied).
  376.      */
  377.     public String toExternalForm() {
  378.     String    result = protocol + "://" + host;
  379.  
  380.     if (port != -1) {
  381.         result += ":" + port;
  382.     }
  383.     result += file;
  384.     if (ref != null) {
  385.         result += "#" + ref;
  386.     }
  387.  
  388.     return result;
  389.     }
  390.  
  391.     public int getPort() {
  392.     return port == -1 ? 80 : port;
  393.     }
  394.  
  395.     static Hashtable    ht = new Hashtable();
  396.     private URLStreamHandler getHandler() {
  397.     URLStreamHandler sh = (URLStreamHandler) ht.get(protocol);
  398.     if (sh == null) {
  399.         try {
  400.         URL url = new URL("http", host, "/");
  401.         sh = (URLStreamHandler) url.New("net.www.protocol." +
  402.                         protocol +
  403.                         ".Handler");
  404.         } catch(Exception e) {
  405.         e.printStackTrace();
  406.         sh = new unknownHandler(protocol);
  407.         }
  408.         ht.put(protocol, sh);
  409.     }
  410.         return sh;
  411.     }
  412.  
  413.  
  414.     /**
  415.      * Open an input stream to the object references by the URL.  Invokes the
  416.      * appropriate protocol handler.  Failure is indicated by throwing an
  417.      * exception.  The act of opening this stream determines, in a protocol
  418.      * specific way, the content type of the object associated with this URL.
  419.      * @return    The opened input stream.  A value of null indicates that while
  420.      * the open was successful, there is no useful data provided by this
  421.      * protocol, it's done for side-effect only (the usual example is the
  422.      * "mailto" protocol).
  423.      */
  424.     public InputStream openStream() {
  425.     URLStreamHandler h = getHandler();
  426.     InputStream is = h.openStream(this);
  427.  
  428.     if (content_type == URL.content_unknown) {
  429.         is = getContentFromStream(is);
  430.     }
  431.     return is;
  432.     }
  433.  
  434.     /**
  435.      * Similar to openStream except that it allows the stream handler
  436.      * to interact with the user to resolve certain problems.  For
  437.      * example, the http handler will prompt for a user name and
  438.      * password to handle authentication failures.  In these cases,
  439.      * openStream would just toss an exception.
  440.      */
  441.     public InputStream openStreamInteractively() {
  442.     URLStreamHandler h = getHandler();
  443.     InputStream is = h.openStreamInteractively(this);
  444.  
  445.     if (content_type == URL.content_unknown) {
  446.         is = getContentFromStream(is);
  447.     }
  448.     return is;
  449.     }
  450.  
  451.  
  452.     /**
  453.      * This disgusting hack is used to check for files that are
  454.      * actually html but which do not have a MIME header and do not
  455.      * end in .htm or .html.  We need to review all of the http: and ftp: and
  456.      * file: protocol fetching code to see if we can't unify the
  457.      * concepts so that document type is determined in only one
  458.      * place instead of several places like it is today.
  459.      */
  460.     private InputStream getContentFromStream(InputStream is) 
  461.     {
  462.     if (!is.markSupported())
  463.         is = new BufferedInputStream(is);
  464.     
  465.     /*
  466.      * Check for html files that are missing a MIME header.
  467.      * This is sure not perfect but the pages are broken anyway
  468.      * so this is better than nothing.
  469.      */
  470.     is.mark(10);
  471.     byte buf[] = new byte[6];
  472.     int n = is.read(buf);
  473.     String head = null;
  474.     if (n > 0) {
  475.         head = new String(buf, 0, 0, n);
  476.     }
  477.     is.reset();
  478.  
  479.     if (head != null && (head.startsWith("<!") 
  480.         || head.equalsIgnoreCase("<html>")
  481.         || head.equalsIgnoreCase("<body>")
  482.         || head.equalsIgnoreCase("<head>"))) {
  483.         setType(content_html);
  484.     }
  485.  
  486.     return is;
  487.     }
  488.  
  489.  
  490.     /**
  491.      * Force the content type of this URL to a specific value.
  492.      * @param    type    The content type to use.  One of the
  493.      *            content_* static variables in this
  494.      *            class should be used.
  495.      *            eg. setType(URL.content_html);
  496.      */
  497.     public void setType(String type) {
  498.     content_type = type;
  499.     }
  500.  
  501.     /**
  502.      * Given a mime content type String (e.g. image/x-gif) produce the
  503.      * internal URL type atom for that type.
  504.      */
  505.     public String mimeToContent(String type) {
  506.     int semi = type.indexOf(';');
  507.     if (semi >= 0)
  508.         type = type.substring(0, semi);
  509.     String s = (String) content_table.get(type);
  510.     if (s == null) {
  511.         /* never seen this before! */
  512.         content_table.put(type, type);
  513.         s = type;
  514.     }
  515.     return s;
  516.     }
  517.  
  518.     /**
  519.      * Returns true if the data associated with this URL can be cached.
  520.      */
  521.     public boolean canCache() {
  522.     return !isPostURL() && file.indexOf('?') < 0;
  523.     }
  524.  
  525.     private static Hashtable typeht = new Hashtable();
  526.     private static ContentHandler UnknownContentHandlerP = new UnknownContentHandler();
  527.     private static String content_class_prefix = "net.www.content.";
  528.  
  529.     /**    Returns the content handler for the mimeType indicated by
  530.     this URL.  Given a mime-type of the form major/minor we first look
  531.     for a specific implementation class "net.www.content.major.minor",
  532.     if that's missing we try "net.www.content.major.Generic",
  533.     if that's missing we use net.www.html.UnknownContentHandler. */
  534.  
  535.     private ContentHandler contentHandler() {
  536.     ContentHandler ret;
  537.     if (content_type == null)
  538.         return UnknownContentHandlerP;
  539.     ret = (ContentHandler) typeht.get(content_type);
  540.     if (ret == null) {
  541.         try {
  542.         int i = content_class_prefix.length();
  543.         int j = content_type.length();
  544.         int lastdot = 0;
  545.         char nm[] = new char[i + j];
  546.         content_class_prefix.getChars(0, i, nm, 0);
  547.         content_type.getChars(0, j, nm, i);
  548.         while (--j >= 0) {
  549.             char c = nm[i];
  550.             if (c == '/') nm[lastdot = i] = '.';
  551.             else if (!('A' <= c && c <= 'Z' ||
  552.                    'a' <= c && c <= 'z' ||
  553.                    '0' <= c && c <= 9)) nm[i] = '_';
  554.             i++;
  555.         }
  556.         String name = new String(nm);
  557.         try {
  558.             ret = (ContentHandler) New(name);
  559.         } catch(Exception e) {
  560.             /* Skip the search for a generic handler */
  561.             ret = UnknownContentHandlerP;
  562. //            if (lastdot <= 0) throw e;
  563. //            ret = (ContentHandler) New(name.substring(0,lastdot)+".Generic");
  564.         }
  565.         } catch (Exception e) {
  566.         ret = UnknownContentHandlerP;
  567.         }
  568.         typeht.put(content_type, ret);
  569.     }
  570.     return ret;
  571.     }
  572.  
  573.     /**
  574.      * Get the object referred to by this URL.  For example, if it refers to an image
  575.      * the object will be some subclass if DIBitmap.  The instanceof operator should
  576.      * be used to determine what kind of object was returned.
  577.      * @return    the object that was fetched.
  578.      */
  579.     public Object getContent() {
  580.     return getContent(openStream(), null);
  581.     }
  582.  
  583.     static private MimeTable mt = null;
  584.  
  585.     /**
  586.      * Get the object referred to by this URL.  For example, if it refers to an image
  587.      * the object will be some subclass if DIBitmap.  The instanceof operator should
  588.      * be used to determine what kind of object was returned.
  589.      * @param    is    the stream to the Object.  It must have been created by an
  590.      *            earlier call to openStream.
  591.      * @return    the object that was fetched.  If there is no handler for the object,
  592.      *        a stream is returned (logically the unmodified original stream, but
  593.      *        it may have had another stream layered on top of it).  Generally the
  594.      *        caller should prompt to save the file locally if they get a stream back.
  595.      */
  596.     public Object getContent(InputStream is, Observer o) {
  597.     Object ret = null;
  598.     
  599.     if (is == null)
  600.         return null;
  601.     if (mt == null)
  602.         mt = (MimeTable) new("net.www.html." + System.getOSName() + "MimeTable");
  603.     if (!is.markSupported())
  604.         is = new BufferedInputStream(is);
  605.     is.mark(1000);
  606.     int c1 = is.read();
  607.     int c2 = is.read();
  608.     int c3 = is.read();
  609.     int c4 = is.read();
  610.     int c5 = is.read();
  611.     int c6 = is.read();
  612.     is.reset();
  613.  
  614.     /* First we check for a couple of well-known magic types: GIF and xbitmap */
  615.     Checking: {
  616.     if (c1 == 'G' && c2 == 'I' && c3 == 'F' && c4 == '8') {
  617.         /* dont trust the mime type:try reading it as
  618.          * a GIF image first */
  619.         ret = new GifImage(is, o);
  620.         content_type = content_gif;
  621.         return ret;
  622.     } else if (c1 == '#' && c2 == 'd' && c3 == 'e' && c4 == 'f') {
  623.         ret = new XbmImage(is);
  624.         content_type = content_xbitmap;
  625.         break Checking;
  626.     } else if (c1 == '!' && c2 == ' ' && c3 == 'X' && c4 == 'P' && 
  627.            c5 == 'M' && c6 == '2') {
  628.         ret = new Xpm2Image(is);
  629.         content_type = content_xpixmap;
  630.         break Checking;
  631.     }
  632.     /* Check for the Unknown content handler */
  633.     if (content_type == content_unknown) {
  634.         ret = is;    /* dont bother looking for a handler */
  635.         break Checking;
  636.     }
  637.  
  638.     /* Then we look for an exact match in the mime database */
  639.     MimeEntry m = mt.find(content_type);
  640.     if (m != null && !m.starred) {
  641.         ret = m.launch(is, this, mt);
  642.         break Checking;
  643.     }
  644.  
  645.     /* Then we try for an external handler */
  646.     try {
  647.         if (ret == null) {
  648.         ContentHandler ch = contentHandler();
  649.         if (ch != null && ch != UnknownContentHandlerP) {
  650.             ret = ch.getContent(is, this);
  651.             break Checking;
  652.         }
  653.         }
  654.     } catch(Exception e) {
  655.         is.close();
  656.         ret = "Failed to open "+toExternalForm()+": "+e;
  657.         break Checking;
  658.     }
  659.  
  660.     /* Then we check for a generic (starred) mime database entry */
  661.     if (m != null) {
  662.         ret = m.launch(is, this, mt);
  663.         break Checking;
  664.     }
  665.  
  666.     /* If the stream is still intact, return the stream */
  667.     try {
  668.         is.reset();
  669.         ret = is;
  670.     } catch(Exception e) {
  671.         is.close();
  672.         ret = "Failed to open "+toExternalForm()+": "+e;
  673.     }
  674.     }
  675.     if (o != null)
  676.         if (ret instanceof Observable) {
  677.         o.update((Observable) ret);
  678.         }
  679.         else {
  680.         /* observer requested but object not observable */
  681.         is.close();
  682.         throw new FormatException("Observable object expected");
  683.         }
  684.     return ret;
  685.     }
  686.  
  687.  
  688.     /**
  689.      * A simple main program for testing: fetches the object referenced by this URL
  690.      * and prints it.
  691.      */
  692.     public static void main(String args[]) {
  693.     URL url = new URL(null, args[0]);
  694.     InputStream is = url.openStream();
  695.  
  696.     System.out.println("URL type = " + url.content_type);
  697.     int c;
  698.     while ((c = is.read()) != -1) {
  699.         System.out.write(c);
  700.     }
  701.     System.out.flush();
  702.     }
  703.  
  704.     /**
  705.      * Cache classloaders of classloaders
  706.      */
  707.     static Hashtable classloaders = new Hashtable();
  708.  
  709.     /**
  710.      * Create an instance of a named class using a classloader that
  711.      * references this URL.  It is similar to new(clname) except that
  712.      * it specifies a URL to search.
  713.      * @param    clname    The name of the class.
  714.      * @return        The new instance of the class.
  715.      */
  716.     public Object New(String clname) {
  717.     WWWClassLoader loader;
  718.     if ((loader = (WWWClassLoader) classloaders.get(this)) == null) {
  719.         classloaders.put(this, loader = new WWWClassLoader(this));
  720.     }
  721.     return loader.loadClass(clname).newInstance();
  722.     }
  723.  
  724.     /**
  725.      * Flush the classloader cache.  This is only used when reloading a
  726.      * document which went we want to force the system to reload all the
  727.      * applets on that document.  Flushing the cache is not the most
  728.      * efficient mechanism but it is the most expedient.
  729.      */
  730.     static synchronized public void flushClassLoader() {
  731.     classloaders = new Hashtable();
  732.     }
  733.  
  734. }
  735.