Annotation of java/classes/org/w3c/www/protocol/http/HttpManager.java, revision 1.95

1.1       abaird      1: // HttpManager.java
1.95    ! ylafon      2: // $Id: HttpManager.java,v 1.94 2012/06/16 15:48:52 ylafon Exp $
1.1       abaird      3: // (c) COPYRIGHT MIT and INRIA, 1996.
                      4: // Please first read the full copyright statement in file COPYRIGHT.html
                      5: 
1.94      ylafon      6: package org.w3c.www.protocol.http;
1.1       abaird      7: 
1.65      ylafon      8: import java.util.Enumeration;
                      9: import java.util.Hashtable;
                     10: import java.util.Properties;
                     11: 
                     12: import java.net.URL;
                     13: 
                     14: import java.io.InputStream;
                     15: import java.io.PrintStream;
                     16: 
                     17: import org.w3c.www.mime.MimeHeaderHolder;
                     18: import org.w3c.www.mime.MimeParser;
                     19: import org.w3c.www.mime.MimeParserFactory;
                     20: 
                     21: import org.w3c.util.LRUList;
                     22: import org.w3c.util.ObservableProperties;
                     23: import org.w3c.util.PropertyMonitoring;
                     24: import org.w3c.util.SyncLRUList;
1.1       abaird     25: 
1.25      abaird     26: class ManagerDescription {
                     27:     HttpManager manager = null;
1.94      ylafon     28:     Properties properties = null;
1.25      abaird     29: 
                     30:     final HttpManager getManager() {
1.94      ylafon     31:         return manager;
1.25      abaird     32:     }
                     33: 
                     34:     final boolean sameProperties(Properties props) {
1.94      ylafon     35:         if (props.size() != properties.size())
                     36:             return false;
                     37:         Enumeration e = props.propertyNames();
                     38:         while (e.hasMoreElements()) {
                     39:             String name = (String) e.nextElement();
                     40:             String prop = properties.getProperty(name);
                     41:             if ((prop == null) || (!prop.equals(props.getProperty(name))))
                     42:                 return false;
                     43:         }
                     44:         return true;
1.25      abaird     45:     }
                     46: 
                     47:     ManagerDescription(HttpManager manager, Properties props) {
1.94      ylafon     48:         this.manager = manager;
                     49:         this.properties = (Properties) props.clone();
1.25      abaird     50:     }
                     51: }
                     52: 
1.1       abaird     53: class ReplyFactory implements MimeParserFactory {
1.65      ylafon     54: 
1.1       abaird     55:     public MimeHeaderHolder createHeaderHolder(MimeParser parser) {
1.94      ylafon     56:         return new Reply(parser);
1.1       abaird     57:     }
                     58: 
                     59: }
                     60: 
                     61: /**
                     62:  * The client side HTTP request manager.
                     63:  * This class is the user interface (along with the other public classes of
1.94      ylafon     64:  * this package) for the W3C client side library implementing HTTP.
1.89      ylafon     65:  * A typical request is launched though the following sequence:
1.1       abaird     66:  * <pre>
                     67:  * HttpManager     manager = HttpManager.getManager() ;
1.36      ylafon     68:  * Request request = manager.createRequest() ;
1.1       abaird     69:  * request.setMethod(HTTP.GET) ;
                     70:  * request.setURL(new URL("http://www.w3.org/pub/WWW/"));
                     71:  * Reply    reply = manager.runRequest(request) ;
                     72:  * // Get the reply input stream that contains the actual data:
                     73:  * InputStream in = reply.getInputStream() ;
                     74:  * ...
                     75:  * </pre>
                     76:  */
                     77: 
1.13      abaird     78: public class HttpManager implements PropertyMonitoring {
1.45      ylafon     79: 
                     80:     private static final boolean debug = false;
                     81: 
1.94      ylafon     82:     private static final
1.38      bmahe      83:     String DEFAULT_SERVER_CLASS = "org.w3c.www.protocol.http.HttpBasicServer";
1.17      abaird     84: 
                     85:     /**
                     86:      * The name of the property indicating the class of HttpServer to use.
                     87:      */
                     88:     public static final
1.38      bmahe      89:     String SERVER_CLASS_P = "org.w3c.www.protocol.http.server";
1.17      abaird     90: 
1.2       abaird     91:     /**
                     92:      * The name of the property containing the ProprequestFilter to launch.
                     93:      */
1.94      ylafon     94:     public static final
1.38      bmahe      95:     String FILTERS_PROP_P = "org.w3c.www.protocol.http.filters";
1.5       abaird     96:     /**
1.43      ylafon     97:      * The maximum number of simultaneous connectionlrus.
1.11      abaird     98:      */
                     99:     public static final
1.38      bmahe     100:     String CONN_MAX_P = "org.w3c.www.protocol.http.connections.max";
1.11      abaird    101:     /**
1.35      ylafon    102:      * The SO_TIMEOUT of the client socket.
                    103:      */
                    104:     public static final
1.38      bmahe     105:     String TIMEOUT_P = "org.w3c.www.protocol.http.connections.timeout";
1.35      ylafon    106:     /**
1.82      ylafon    107:      * The connection timeout of the client socket.
                    108:      */
                    109:     public static final
1.94      ylafon    110:     String CONN_TIMEOUT_P = "org.w3c.www.protocol.http.connections.connTimeout";
1.82      ylafon    111:     /**
1.5       abaird    112:      * Header properties - The allowed drift for getting cached resources.
                    113:      */
1.94      ylafon    114:     public static final
1.38      bmahe     115:     String MAX_STALE_P = "org.w3c.www.protocol.http.cacheControl.maxStale";
1.5       abaird    116:     /**
                    117:      * Header properties - The minium freshness required on cached resources.
                    118:      */
                    119:     public static final
1.38      bmahe     120:     String MIN_FRESH_P = "org.w3c.www.protocol.http.cacheControl.minFresh";
1.5       abaird    121:     /**
                    122:      * Header properties - Set the only if cached flag on requests.
                    123:      */
1.94      ylafon    124:     public static final
                    125:     String ONLY_IF_CACHED_P =
                    126:             "org.w3c.www.protocol.http.cacheControl.onlyIfCached";
1.5       abaird    127:     /**
                    128:      * Header properties - Set the user agent.
                    129:      */
1.94      ylafon    130:     public static final
1.38      bmahe     131:     String USER_AGENT_P = "org.w3c.www.protocol.http.userAgent";
1.5       abaird    132:     /**
                    133:      * Header properties - Set the accept header.
                    134:      */
1.94      ylafon    135:     public static final
1.38      bmahe     136:     String ACCEPT_P = "org.w3c.www.protocol.http.accept";
1.5       abaird    137:     /**
                    138:      * Header properties - Set the accept language.
                    139:      */
1.94      ylafon    140:     public static final
1.38      bmahe     141:     String ACCEPT_LANGUAGE_P = "org.w3c.www.protocol.http.acceptLanguage";
1.5       abaird    142:     /**
                    143:      * Header properties - Set the accept encodings.
                    144:      */
1.94      ylafon    145:     public static final
1.38      bmahe     146:     String ACCEPT_ENCODING_P = "org.w3c.www.protocol.http.acceptEncoding";
1.5       abaird    147:     /**
1.62      ylafon    148:      * Header properties - are we parsing answers in a lenient way?
                    149:      */
1.94      ylafon    150:     public static final
1.62      ylafon    151:     String LENIENT_P = "org.w3c.www.protocol.http.lenient";
                    152:     /**
1.79      ylafon    153:      * Header properties - should we reuse a connection for POST?
                    154:      */
1.94      ylafon    155:     public static final
1.79      ylafon    156:     String KEEPBODY_P = "org.w3c.www.protocol.http.keepbody";
                    157:     /**
1.5       abaird    158:      * Header properties - Should we use a proxy ?
                    159:      */
                    160:     public static final
1.12      abaird    161:     String PROXY_SET_P = "proxySet";
1.5       abaird    162:     /**
                    163:      * Header properties - What is the proxy host name.
                    164:      */
                    165:     public static final
1.12      abaird    166:     String PROXY_HOST_P = "proxyHost";
1.5       abaird    167:     /**
                    168:      * Header properties - What is the proxy port number.
                    169:      */
                    170:     public static final
1.12      abaird    171:     String PROXY_PORT_P = "proxyPort";
1.2       abaird    172: 
1.7       abaird    173:     /**
                    174:      * The default value for the <code>Accept</code> header.
                    175:      */
                    176:     public static final
                    177:     String DEFAULT_ACCEPT = "*/*";
                    178:     /**
                    179:      * The default value for the <code>User-Agent</code> header.
                    180:      */
                    181:     public static final
1.93      ylafon    182:     String DEFAULT_USER_AGENT = "Jigsaw/2.3.0-beta2";
1.7       abaird    183: 
1.24      abaird    184:     /**
                    185:      * This array keeps track of all the created managers.
                    186:      * A new manager (kind of HTTP client side context) is created for each
                    187:      * diffferent set of properties.
                    188:      */
1.25      abaird    189:     private static ManagerDescription managers[] = new ManagerDescription[4];
1.65      ylafon    190: 
1.1       abaird    191:     /**
1.17      abaird    192:      * The class to instantiate to create new HttpServer instances.
                    193:      */
                    194:     protected Class serverclass = null;
                    195:     /**
1.13      abaird    196:      * The properties we initialized from.
                    197:      */
                    198:     ObservableProperties props = null;
                    199:     /**
1.1       abaird    200:      * The server this manager knows about, indexed by FQDN of target servers.
                    201:      */
                    202:     protected Hashtable servers = null;
                    203:     /**
                    204:      * The template request (the request we will clone to create new requests)
                    205:      */
1.94      ylafon    206:     protected Request template = null;
1.4       abaird    207:     /**
1.9       abaird    208:      * The LRU list of connections.
1.4       abaird    209:      */
1.9       abaird    210:     protected LRUList connectionsLru = null;
1.1       abaird    211:     /**
                    212:      * The filter engine attached to this manager.
                    213:      */
                    214:     FilterEngine filteng = null;
                    215: 
1.35      ylafon    216:     protected int timeout = 300000;
1.85      ylafon    217:     protected int conn_timeout = 3000;
1.9       abaird    218:     protected int conn_count = 0;
                    219:     protected int conn_max = 5;
1.62      ylafon    220:     protected boolean lenient = true;
1.79      ylafon    221:     protected boolean keepbody = false;
1.9       abaird    222: 
1.83      ylafon    223:     protected Hashtable _tmp_servers = null; // synced during creation
                    224: 
1.1       abaird    225:     /**
1.13      abaird    226:      * Update the proxy configuration to match current properties setting.
1.94      ylafon    227:      *
1.13      abaird    228:      * @return A boolean, <strong>true</strong> if change was done,
1.94      ylafon    229:      *         <strong>false</strong> otherwise.
1.13      abaird    230:      */
                    231: 
                    232:     protected boolean updateProxy() {
1.94      ylafon    233:         boolean set = props.getBoolean(PROXY_SET_P, false);
                    234:         if (set) {
                    235:             // Wow using a proxy now !
                    236:             String host = props.getString(PROXY_HOST_P, null);
                    237:             int port = props.getInteger(PROXY_PORT_P, -1);
                    238:             URL proxy = null;
                    239:             try {
                    240:                 proxy = new URL("http", host, port, "/");
                    241:             } catch (Exception ex) {
                    242:                 return false;
                    243:             }
                    244:             // Now if a proxy...
                    245:             if ((proxy != null) && (proxy.getHost() != null))
                    246:                 template.setProxy(proxy);
                    247:         } else {
                    248:             template.setProxy(null);
                    249:         }
                    250:         return true;
1.13      abaird    251:     }
                    252: 
                    253:     /**
1.24      abaird    254:      * Get this manager properties.
1.94      ylafon    255:      *
1.24      abaird    256:      * @return An ObservableProperties instance.
                    257:      */
                    258: 
                    259:     public final ObservableProperties getProperties() {
1.94      ylafon    260:         return props;
1.24      abaird    261:     }
                    262: 
                    263:     /**
1.13      abaird    264:      * PropertyMonitoring implementation - Update properties on the fly !
1.94      ylafon    265:      *
1.13      abaird    266:      * @param name The name of the property that has changed.
                    267:      * @return A boolean, <strong>true</strong> if change is accepted,
1.94      ylafon    268:      *         <strong>false</strong> otherwise.
1.13      abaird    269:      */
                    270: 
                    271:     public boolean propertyChanged(String name) {
1.94      ylafon    272:         Request tpl = template;
                    273:         if (name.equals(FILTERS_PROP_P)) {
                    274:             // FIXME
                    275:             return true;
                    276:             // return false;
                    277:         } else if (name.equals(TIMEOUT_P)) {
                    278:             setTimeout(props.getInteger(TIMEOUT_P, timeout));
                    279:             return true;
                    280:         } else if (name.equals(CONN_TIMEOUT_P)) {
                    281:             setConnTimeout(props.getInteger(CONN_TIMEOUT_P, conn_timeout));
                    282:             return true;
                    283:         } else if (name.equals(CONN_MAX_P)) {
                    284:             setMaxConnections(props.getInteger(CONN_MAX_P, conn_max));
                    285:             return true;
                    286:         } else if (name.equals(MAX_STALE_P)) {
                    287:             int ival = props.getInteger(MAX_STALE_P, -1);
                    288:             if (ival >= 0)
                    289:                 tpl.setMaxStale(ival);
                    290:             return true;
                    291:         } else if (name.equals(MIN_FRESH_P)) {
                    292:             int ival = props.getInteger(MIN_FRESH_P, -1);
                    293:             if (ival >= 0)
                    294:                 tpl.setMinFresh(ival);
                    295:             return true;
                    296:         } else if (name.equals(LENIENT_P)) {
                    297:             lenient = props.getBoolean(LENIENT_P, lenient);
                    298:             return true;
                    299:         } else if (name.equals(KEEPBODY_P)) {
                    300:             keepbody = props.getBoolean(KEEPBODY_P, keepbody);
                    301:             return true;
                    302:         }
                    303:         if (name.equals(ONLY_IF_CACHED_P)) {
                    304:             tpl.setOnlyIfCached(props.getBoolean(ONLY_IF_CACHED_P, false));
                    305:             return true;
                    306:         } else if (name.equals(USER_AGENT_P)) {
                    307:             tpl.setValue("user-agent"
                    308:                     , props.getString(USER_AGENT_P
                    309:                     , DEFAULT_USER_AGENT));
                    310:             return true;
                    311:         } else if (name.equals(ACCEPT_P)) {
                    312:             tpl.setValue("accept"
                    313:                     , props.getString(ACCEPT_P, DEFAULT_ACCEPT));
                    314:             return true;
                    315:         } else if (name.equals(ACCEPT_LANGUAGE_P)) {
                    316:             String sval = props.getString(ACCEPT_LANGUAGE_P, null);
                    317:             if (sval != null)
                    318:                 tpl.setValue("accept-language", sval);
                    319:             return true;
                    320:         } else if (name.equals(ACCEPT_ENCODING_P)) {
                    321:             String sval = props.getString(ACCEPT_ENCODING_P, null);
                    322:             if (sval != null)
                    323:                 tpl.setValue("accept-encoding", sval);
                    324:             return true;
                    325:         } else if (name.equals(PROXY_SET_P)
                    326:                 || name.equals(PROXY_HOST_P)
                    327:                 || name.equals(PROXY_PORT_P)) {
                    328:             return updateProxy();
                    329:         } else {
                    330:             return true;
                    331:         }
1.13      abaird    332:     }
                    333: 
                    334:     /**
1.4       abaird    335:      * Allow the manager to interact with the user if needed.
                    336:      * This will, for example, allow prompting for paswords, etc.
1.94      ylafon    337:      *
1.4       abaird    338:      * @param onoff Turn interaction on or off.
                    339:      */
                    340: 
                    341:     public void setAllowUserInteraction(boolean onoff) {
1.94      ylafon    342:         template.setAllowUserInteraction(onoff);
1.4       abaird    343:     }
                    344: 
1.94      ylafon    345:     protected static synchronized HttpManager getManager(Class managerclass, Properties p) {
                    346:         // Does such a manager exists already ?
                    347:         for (ManagerDescription descr : managers) {
                    348:             if (descr != null && descr.sameProperties(p)) {
                    349:                 return descr.getManager();
                    350:             }
                    351:         }
                    352:         // Get the props we will initialize from:
                    353:         ObservableProperties props = null;
                    354:         if (p instanceof ObservableProperties)
                    355:             props = (ObservableProperties) p;
                    356:         else
                    357:             props = new ObservableProperties(p);
                    358:         // Create a new manager for this set of properties:
                    359:         HttpManager manager = null;
                    360:         try {
                    361:             Object o = managerclass.newInstance();
                    362:             if (o instanceof HttpManager) {
                    363:                 manager = (HttpManager) o;
                    364:             } else { // default value
                    365:                 manager = new HttpManager();
                    366:             }
                    367:         } catch (Exception ex) {
                    368:             ex.printStackTrace();
                    369:             manager = new HttpManager();
                    370:         }
                    371:         manager.props = props;
                    372:         // Initialize this new manager filters:
                    373:         String filters[] = props.getStringArray(FILTERS_PROP_P, null);
                    374:         if (filters != null) {
                    375:             for (String filter : filters) {
                    376:                 try {
                    377:                     Class c = Class.forName(filter);
                    378:                     PropRequestFilter f = null;
                    379:                     f = (PropRequestFilter) c.newInstance();
                    380:                     f.initialize(manager);
                    381:                 } catch (PropRequestFilterException ex) {
                    382:                     System.out.println("Couldn't initialize filter \""
                    383:                             + filter
                    384:                             + "\" init failed: "
                    385:                             + ex.getMessage());
                    386:                 } catch (Exception ex) {
                    387:                     System.err.println("Error initializing prop filters:");
                    388:                     System.err.println("Coulnd't initialize ["
                    389:                             + filter
                    390:                             + "]: " + ex.getMessage());
                    391:                     ex.printStackTrace();
                    392:                     System.exit(1);
                    393:                 }
                    394:             }
                    395:         }
                    396:         // The factory to create MIME reply holders:
                    397:         manager.factory = manager.getReplyFactory();
                    398:         // The class to create HttpServer instances from
                    399:         String c = props.getString(SERVER_CLASS_P, DEFAULT_SERVER_CLASS);
                    400:         try {
                    401:             manager.serverclass = Class.forName(c);
                    402:         } catch (Exception ex) {
                    403:             System.err.println("Unable to initialize HttpManager: ");
                    404:             System.err.println("Class \"" + c + "\" not found, from property "
                    405:                     + SERVER_CLASS_P);
                    406:             ex.printStackTrace();
                    407:             System.exit(1);
                    408:         }
                    409:         // Setup the template request:
                    410:         Request tpl = manager.template;
                    411:         // Set some default headers value (from props)
                    412:         // Check for a proxy ?
                    413:         manager.updateProxy();
                    414:         // CacheControl, only-if-cached
                    415:         tpl.setOnlyIfCached(props.getBoolean(ONLY_IF_CACHED_P, false));
                    416:         // CacheControl, maxstale
                    417:         int ival = props.getInteger(MAX_STALE_P, -1);
                    418:         if (ival >= 0)
                    419:             tpl.setMaxStale(ival);
                    420:         // CacheControl, minfresh:
                    421:         ival = props.getInteger(MIN_FRESH_P, -1);
                    422:         if (ival >= 0)
                    423:             tpl.setMinFresh(ival);
                    424:         // general, lenient
                    425:         manager.lenient = props.getBoolean(LENIENT_P, true);
                    426:         manager.keepbody = props.getBoolean(KEEPBODY_P, false);
                    427:         // General, User agent
                    428:         String sval;
                    429:         tpl.setValue("user-agent"
                    430:                 , props.getString(USER_AGENT_P
                    431:                 , DEFAULT_USER_AGENT));
                    432:         // General, Accept
                    433:         tpl.setValue("accept"
                    434:                 , props.getString(ACCEPT_P, DEFAULT_ACCEPT));
                    435:         // General, Accept-Language
                    436:         sval = props.getString(ACCEPT_LANGUAGE_P, null);
                    437:         if (sval != null) {
                    438:             if (sval.trim().length() > 0) {
                    439:                 tpl.setValue("accept-language", sval);
                    440:             }
                    441:         }
                    442:         // General, Accept-Encoding
                    443:         sval = props.getString(ACCEPT_ENCODING_P, null);
                    444:         if (sval != null) {
                    445:             if (sval.trim().length() > 0) {
                    446:                 tpl.setValue("accept-encoding", sval);
                    447:             }
                    448:         }
                    449:         // Maximum number of allowed connections:
                    450:         manager.conn_max = props.getInteger(CONN_MAX_P, 5);
                    451:         // timeout value
                    452:         manager.timeout = props.getInteger(TIMEOUT_P, manager.timeout);
                    453:         // connection timeout
                    454:         manager.conn_timeout = props.getInteger(CONN_TIMEOUT_P,
                    455:                 manager.conn_timeout);
                    456:         // Register ourself as a property observer:
                    457:         props.registerObserver(manager);
                    458:         // Register that manager in our knwon managers:
                    459:         for (int i = 0; i < managers.length; i++) {
                    460:             if (managers[i] == null) {
                    461:                 managers[i] = new ManagerDescription(manager, p);
                    462:                 return manager;
                    463:             }
                    464:         }
                    465:         ManagerDescription nm[] = new ManagerDescription[managers.length << 1];
                    466:         System.arraycopy(managers, 0, nm, 0, managers.length);
                    467:         nm[managers.length] = new ManagerDescription(manager, p);
                    468:         managers = nm;
                    469:         return manager;
1.1       abaird    470:     }
1.94      ylafon    471: 
1.66      bmahe     472: 
                    473:     /**
                    474:      * Get an instance of the HTTP manager.
                    475:      * This method returns an actual instance of the HTTP manager. It may
                    476:      * return different managers, if it decides to distribute the load on
                    477:      * different managers (avoid the HttpManager being a bottleneck).
1.94      ylafon    478:      *
1.66      bmahe     479:      * @return An application wide instance of the HTTP manager.
                    480:      */
                    481: 
                    482:     public static synchronized HttpManager getManager(Properties p) {
1.94      ylafon    483:         return getManager(HttpManager.class, p);
1.66      bmahe     484:     }
1.1       abaird    485: 
1.24      abaird    486:     public static HttpManager getManager() {
1.94      ylafon    487:         return getManager(System.getProperties());
1.24      abaird    488:     }
1.1       abaird    489: 
                    490:     /**
1.32      abaird    491:      * Get the String key for the server instance handling that request.
                    492:      * This method takes care of any proxy setting (it will return the key
                    493:      * to the proxy when required.)
1.94      ylafon    494:      *
1.32      abaird    495:      * @return A uniq identifier for the handling server, as a String.
                    496:      */
                    497: 
                    498:     public final String getServerKey(Request request) {
1.94      ylafon    499:         URL proxy = request.getProxy();
                    500:         URL target = request.getURL();
                    501:         String key = null;
                    502:         if (proxy != null) {
                    503:             return ((proxy.getPort() == 80)
                    504:                     ? proxy.getHost().toLowerCase()
                    505:                     : (proxy.getHost().toLowerCase() + ":" + proxy.getPort()));
                    506:         } else {
                    507:             return ((target.getPort() == 80)
                    508:                     ? target.getHost().toLowerCase()
                    509:                     : (target.getHost().toLowerCase() + ":" + target.getPort()));
                    510:         }
1.32      abaird    511:     }
                    512: 
                    513:     /**
1.1       abaird    514:      * Get the appropriate server object for handling request to given target.
1.94      ylafon    515:      *
                    516:      * @param host The server's key, as returned by <code>getServerKey</code>.
                    517:      * @param port The server's port
1.1       abaird    518:      * @return An object complying to the HttpServer interface.
1.94      ylafon    519:      * @throws HttpException If the given host name couldn't be resolved.
1.1       abaird    520:      */
                    521: 
1.76      ylafon    522:     protected HttpServer lookupServer(String host, int port)
1.94      ylafon    523:             throws HttpException {
                    524:         int p = (port == -1) ? 80 : port;
                    525:         String id = ((p == 80)
                    526:                 ? host.toLowerCase()
                    527:                 : (host.toLowerCase() + ":" + p));
                    528:         // Check for an existing server:
                    529:         HttpServer server = (HttpServer) servers.get(id);
                    530:         if (server != null) {
                    531:             return server;
                    532:         }
                    533:         // Create and register a new server:
                    534:         synchronized (_tmp_servers) {
                    535:             if (_tmp_servers.containsKey(id)) {
                    536:                 server = (HttpServer) _tmp_servers.get(id);
                    537:                 synchronized (server) {
                    538:                     while (server.state == null ||
                    539:                             server.state.state != HttpServerState.PREINIT) {
                    540:                         try {
                    541:                             wait(100);
                    542:                         } catch (InterruptedException ex) {
                    543:                         } catch (IllegalMonitorStateException ex) {
                    544:                             break;
                    545:                         }
                    546:                     }
                    547:                 }
                    548:                 if (server.state.state == HttpServerState.OK) {
                    549:                     return server;
                    550:                 } else if (server.state.ex != null) {
                    551:                     throw server.state.ex;
                    552:                 } else {
                    553:                     throw new RuntimeException("Unexpected error " +
                    554:                             "in lookupServer");
                    555:                 }
                    556:             } else {
                    557:                 try {
                    558:                     server = (HttpServer) serverclass.newInstance();
                    559:                 } catch (Exception ex) {
                    560:                     String msg = ("Unable to create an instance of \""
                    561:                             + serverclass.getName()
                    562:                             + "\", invalid config, check the "
                    563:                             + SERVER_CLASS_P + " property.");
                    564:                     throw new HttpException(ex, msg);
                    565:                 }
                    566:             }
                    567:         }
                    568:         try {
                    569:             synchronized (server) {
                    570:                 server.initialize(this, new HttpServerState(server), host, p,
                    571:                         timeout, conn_timeout);
                    572:                 try {
                    573:                     notifyAll();
                    574:                 } catch (IllegalMonitorStateException imse) {
                    575:                 }
                    576:             }
                    577:             // FIXME for long running servers the growing hashtable  is
                    578:             // a potential leak. This is a hard way of taking care of that
1.83      ylafon    579: //         if (servers.size() > conn_max) {
                    580: //             closeAnyConnection();
                    581: //             servers = new Hashtable(conn_max);
                    582: //         }
1.94      ylafon    583:         } finally {
                    584:             synchronized (_tmp_servers) {
                    585:                 if (server.state.state == HttpServerState.OK) {
                    586:                     if (!servers.containsKey(id)) {
                    587:                         servers.put(id, server);
                    588:                     }
                    589:                 } else {
1.83      ylafon    590: //                 System.err.println("ERROR State is "+server.state.state
                    591: //                                    +" for " + server);
1.94      ylafon    592:                 }
                    593:                 _tmp_servers.remove(id);
                    594:             }
                    595:         }
                    596:         return server;
1.1       abaird    597:     }
1.5       abaird    598: 
1.1       abaird    599:     /**
1.60      ylafon    600:      * The given connection is about to be used.
                    601:      * Update our list of available servers.
1.94      ylafon    602:      *
1.60      ylafon    603:      * @param conn The idle connection.
                    604:      */
                    605: 
1.83      ylafon    606:     public synchronized void notifyUse(HttpConnection conn) {
1.94      ylafon    607:         if (debug)
                    608:             System.out.println(conn + "+++ connection used");
                    609:         connectionsLru.remove(conn);
1.60      ylafon    610:     }
                    611: 
                    612:     /**
1.9       abaird    613:      * The given connection can be reused, but is now idle.
1.94      ylafon    614:      *
1.9       abaird    615:      * @param conn The connection that is now idle.
1.4       abaird    616:      */
                    617: 
1.60      ylafon    618:     public synchronized void notifyIdle(HttpConnection conn) {
1.94      ylafon    619:         if (debug)
                    620:             System.out.println(conn + "+++ connection idle");
                    621:         connectionsLru.toHead(conn);
                    622:         notifyAll();
1.4       abaird    623:     }
                    624: 
                    625:     /**
1.9       abaird    626:      * The given connection has just been created.
1.94      ylafon    627:      *
1.9       abaird    628:      * @param conn The newly created connection.
                    629:      */
                    630: 
                    631:     protected synchronized void notifyConnection(HttpConnection conn) {
1.94      ylafon    632:         if (debug)
                    633:             System.out.println(conn + "+++ notify conn_count " + (conn_count + 1)
                    634:                     + " / " + conn_max);
                    635:         if (++conn_count > conn_max)
                    636:             closeAnyConnection();
1.9       abaird    637:     }
                    638: 
                    639:     /**
                    640:      * The given connection has been deleted.
1.94      ylafon    641:      *
1.9       abaird    642:      * @param conn The deleted connection.
                    643:      */
                    644: 
1.83      ylafon    645:     protected synchronized void deleteConnection(HttpConnection conn) {
1.94      ylafon    646:         --conn_count;
                    647:         connectionsLru.remove(conn);
                    648:         if (debug)
                    649:             System.out.println(conn + "+++ delete conn_count: " + conn_count);
                    650:         notifyAll();
1.9       abaird    651:     }
                    652: 
                    653:     protected synchronized boolean tooManyConnections() {
1.94      ylafon    654:         return conn_count >= conn_max;
1.9       abaird    655:     }
                    656: 
                    657:     /**
                    658:      * Try reusing one of the idle connection of that server, if any.
1.94      ylafon    659:      *
1.9       abaird    660:      * @param server The target server.
                    661:      * @return An currently idle connection to the given server.
1.4       abaird    662:      */
                    663: 
1.83      ylafon    664:     protected synchronized HttpConnection getConnection(HttpServer server) {
1.94      ylafon    665:         HttpServerState ss = server.getState();
                    666:         HttpConnection hcn = ss.getConnection();
                    667:         if (hcn != null) {
                    668:             notifyUse(hcn);
                    669:         }
                    670:         return hcn;
1.9       abaird    671:     }
                    672: 
1.47      ylafon    673:     /**
                    674:      * Wait for a connection to come up.
1.94      ylafon    675:      *
1.47      ylafon    676:      * @param server, the target server.
1.94      ylafon    677:      * @throws InterruptedException If interrupted..
1.47      ylafon    678:      */
                    679: 
1.9       abaird    680:     protected synchronized void waitForConnection(HttpServer server)
1.94      ylafon    681:             throws InterruptedException {
                    682:         wait(30000); // FIXME should be tunable, now set to 30s
1.4       abaird    683:     }
                    684: 
                    685:     /**
1.60      ylafon    686:      * Close some connections, but pickling the least recently used ones.
1.94      ylafon    687:      * One third of the max number of connection is cut. This is done to
1.45      ylafon    688:      * eliminate the old connections that should be broken already.
                    689:      * (no Socket.isAlive());
1.94      ylafon    690:      *
1.9       abaird    691:      * @return A boolean, <strong>true</strong> if a connection was closed
1.94      ylafon    692:      *         <strong>false</strong> otherwise.
1.4       abaird    693:      */
                    694: 
1.83      ylafon    695:     protected synchronized boolean closeAnyConnection() {
1.94      ylafon    696:         boolean saved = false;
                    697:         int max = Math.max(conn_max / 3, 1);
                    698:         for (int i = 0; i < max; i++) {
                    699:             HttpConnection conn = (HttpConnection) connectionsLru.removeTail();
                    700:             if (conn != null) {
                    701:                 conn.close();
                    702:                 if (debug)
                    703:                     System.out.println("+++ close request");
                    704:                 saved = true;
                    705:             } else {
                    706:                 break;
                    707:             }
                    708:         }
                    709:         // now purge the server Hashtable
                    710:         synchronized (servers) {
                    711:             Enumeration e = servers.keys();
                    712:             if (debug) {
                    713:                 System.out.println("+++ hashtable purge starting: "
                    714:                         + servers.size() + " entries");
                    715:             }
                    716:             int nbconn = 0;
                    717:             int rnbconn = 0;
                    718:             int nbkept = 0;
                    719:             while (e.hasMoreElements()) {
                    720:                 String id = (String) e.nextElement();
                    721:                 if (id != null) {
                    722:                     HttpServer server = (HttpServer) servers.get(id);
                    723:                     int conn_count = server.state.getConnectionCount();
                    724:                     if (conn_count <= 0) {
                    725:                         if (debug) {
                    726:                             System.out.println("+++ hashtable purge: " + id);
                    727:                         }
                    728:                         servers.remove(id);
                    729:                     } else {
                    730:                         if (debug) {
                    731:                             nbkept++;
                    732:                             nbconn += server.state.getConnectionCount();
                    733:                             if (server.state.conns != null) {
                    734:                                 rnbconn += server.state.conns.size();
                    735:                             }
                    736:                             System.out.println("+++ hashtable keep: " + id
                    737:                                     + " ( "
                    738:                                     + server.state.getConnectionCount()
                    739:                                     + ((server.state.conns != null) ?
                    740:                                     " idle) ( real: " + server.state.conns.size() + ")" :
                    741:                                     " idle)")
                    742:                                     + server.state);
                    743:                         }
                    744:                     }
                    745:                 }
                    746:             }
                    747:             if (debug) {
                    748:                 System.out.println("+++ hashtable purge done, keeping "
                    749:                         + servers.size() + " entries");
                    750:                 System.out.println("+++ hashtable stats, keeping "
                    751:                         + nbconn + " ( " + rnbconn
                    752:                         + " ) connections for " + nbkept
                    753:                         + " servers ( "
                    754:                         + ((float) nbconn / (float) nbkept) + " )");
                    755:             }
                    756:         }
                    757:         return saved;
1.4       abaird    758:     }
                    759: 
                    760:     /**
1.1       abaird    761:      * One of our server handler wants to open a connection.
1.94      ylafon    762:      *
1.1       abaird    763:      * @param block A boolean indicating whether we should block the calling
1.94      ylafon    764:      *              thread until a token is available (otherwise, the method will just
                    765:      *              peek at the connection count, and return the appropriate result).
1.1       abaird    766:      * @return A boolean, <strong>true</strong> if the connection can be
1.94      ylafon    767:      *         opened straight, <strong>false</strong> otherwise.
1.1       abaird    768:      */
                    769: 
1.9       abaird    770:     protected boolean negotiateConnection(HttpServer server) {
1.94      ylafon    771:         HttpServerState ss = server.getState();
                    772:         if (!tooManyConnections()) {
                    773:             return true;
                    774:         } else if (ss.notEnoughConnections()) {
                    775:             return closeAnyConnection();
                    776:         } else if (servers.size() > conn_max) {
                    777:             return closeAnyConnection();
                    778:         }
                    779:         return false;
1.60      ylafon    780:     }
                    781: 
                    782:     /**
                    783:      * A new client connection has been established.
                    784:      * This method will try to maintain a maximum number of established
                    785:      * connections, by closing idle connections when possible.
1.94      ylafon    786:      *
1.60      ylafon    787:      * @param server The server that has established a new connection.
                    788:      */
                    789: 
                    790:     protected final synchronized void incrConnCount(HttpServer server) {
1.94      ylafon    791:         if (++conn_count > conn_max)
                    792:             closeAnyConnection();
                    793:         if (debug)
                    794:             System.out.println("+++ incr conn_count: " + conn_count);
1.60      ylafon    795:     }
                    796: 
                    797:     /**
                    798:      * Decrement the number of established connections.
1.94      ylafon    799:      *
1.60      ylafon    800:      * @param server The server that has closed one connection to its target.
                    801:      */
                    802: 
                    803:     protected final synchronized void decrConnCount(HttpServer server) {
1.94      ylafon    804:         --conn_count;
                    805:         if (debug)
                    806:             System.out.println("+++ decr conn_count: " + conn_count);
                    807:         if (conn_count < 0) {
                    808:             System.err.println(this);
                    809:         }
1.1       abaird    810:     }
                    811: 
                    812:     /**
                    813:      * Run the given request, in synchronous mode.
                    814:      * This method will launch the given request, and block the calling thread
                    815:      * until the response headers are available.
1.94      ylafon    816:      *
1.1       abaird    817:      * @param request The request to run.
1.94      ylafon    818:      * @return An instance of Reply, containing all the reply
                    819:      *         informations.
                    820:      * @throws HttpException If something failed during request processing.
1.1       abaird    821:      */
1.65      ylafon    822: 
1.1       abaird    823:     public Reply runRequest(Request request)
1.94      ylafon    824:             throws HttpException {
                    825:         Reply reply = null;
                    826:         int fcalls = 0;
                    827:         // Now run through the ingoing filters:
                    828:         RequestFilter filters[] = filteng.run(request);
                    829:         if (filters != null) {
                    830:             for (RequestFilter filter : filters) {
                    831:                 if ((reply = filter.ingoingFilter(request)) != null)
                    832:                     break;
                    833:                 fcalls++;
                    834:             }
                    835:         }
                    836:         // Locate the appropriate target server:
                    837:         URL target = request.getURL();
                    838:         if (reply == null) {
                    839:             HttpServer srv = null;
                    840:             boolean rtry;
                    841:             do {
                    842:                 rtry = false;
                    843:                 try {
                    844:                     URL proxy = request.getProxy();
                    845:                     if (proxy != null)
                    846:                         srv = lookupServer(proxy.getHost(), proxy.getPort());
                    847:                     else
                    848:                         srv = lookupServer(target.getHost(), target.getPort());
                    849:                     request.setServer(srv);
                    850:                     reply = srv.runRequest(request);
                    851:                 } catch (HttpException ex) {
                    852:                     for (int i = 0; i < fcalls; i++)
                    853:                         rtry = rtry || filters[i].exceptionFilter(request, ex);
                    854:                     if (!rtry)
                    855:                         throw ex;
                    856: //             } finally {
1.88      ylafon    857: //                 request.unsetServer();
1.94      ylafon    858:                 }
                    859:             } while (rtry);
                    860:         }
                    861:         // Apply the filters on the way back:
                    862:         if (filters != null) {
                    863:             while (--fcalls >= 0) {
                    864:                 Reply frep = filters[fcalls].outgoingFilter(request, reply);
                    865:                 if (frep != null) {
                    866:                     reply = frep;
                    867:                     break;
                    868:                 }
                    869:             }
                    870:         }
                    871:         return reply;
1.1       abaird    872:     }
                    873: 
                    874:     /**
                    875:      * Get this manager's reply factory.
                    876:      * The Reply factory is used when prsing incomming reply from servers, it
1.94      ylafon    877:      * decides what object will be created to hold the actual reply from the
1.1       abaird    878:      * server.
1.94      ylafon    879:      *
1.1       abaird    880:      * @return An object compatible with the MimeParserFactory interface.
                    881:      */
                    882: 
1.94      ylafon    883:     MimeParserFactory factory = null;
1.1       abaird    884: 
1.9       abaird    885:     public MimeParserFactory getReplyFactory() {
1.94      ylafon    886:         if (factory == null) {
                    887:             factory = new ReplyFactory();
                    888:         }
                    889:         return factory;
1.1       abaird    890:     }
                    891: 
                    892:     /**
                    893:      * Add a new request filter.
                    894:      * Request filters are called <em>before</em> a request is launched, and
                    895:      * <em>after</em> the reply headers are available. They allow applications
                    896:      * to setup specific request headers (such as PICS, or PEP stuff) on the
                    897:      * way in, and check the reply on the way out.
                    898:      * <p>Request filters are application wide: if their scope matches
                    899:      * the current request, then they will always be aplied.
                    900:      * <p>Filter scopes are defined inclusively and exclusively
1.94      ylafon    901:      *
                    902:      * @param incs   The URL domains for which the filter should be triggered.
                    903:      * @param exs    The URL domains for which the filter should not be triggered.
1.1       abaird    904:      * @param filter The request filter to add.
                    905:      */
                    906: 
                    907:     public void setFilter(URL incs[], URL exs[], RequestFilter filter) {
1.94      ylafon    908:         if (incs != null) {
                    909:             for (URL inc : incs) {
                    910:                 filteng.setFilter(inc, true, filter);
                    911:             }
                    912:         }
                    913:         if (exs != null) {
                    914:             for (URL ex : exs) {
                    915:                 filteng.setFilter(ex, false, filter);
                    916:             }
                    917:         }
1.1       abaird    918:     }
                    919: 
1.15      abaird    920:     /**
                    921:      * Add a global filter.
                    922:      * The given filter will <em>always</em> be invoked.
1.94      ylafon    923:      *
1.15      abaird    924:      * @param filter The filter to install.
                    925:      */
1.65      ylafon    926: 
1.1       abaird    927:     public void setFilter(RequestFilter filter) {
1.94      ylafon    928:         filteng.setFilter(filter);
1.1       abaird    929:     }
                    930: 
                    931:     /**
1.15      abaird    932:      * Find back an instance of a global filter.
                    933:      * This methods allow external classes to get a pointer to installed
                    934:      * filters of a given class.
1.94      ylafon    935:      *
1.15      abaird    936:      * @param cls The class of the filter to look for.
                    937:      * @return A RequestFilter instance, or <strong>null</strong> if not
1.94      ylafon    938:      *         found.
1.1       abaird    939:      */
                    940: 
1.15      abaird    941:     public RequestFilter getGlobalFilter(Class cls) {
1.94      ylafon    942:         return filteng.getGlobalFilter(cls);
1.1       abaird    943:     }
                    944: 
                    945:     /**
                    946:      * Create a new default outgoing request.
                    947:      * This method should <em>always</em> be used to create outgoing requests.
1.94      ylafon    948:      * It will initialize the request with appropriate default values for
1.1       abaird    949:      * the various headers, and make sure that the request is enhanced by
                    950:      * the registered request filters.
1.94      ylafon    951:      *
1.1       abaird    952:      * @return An instance of Request, suitable to be launched.
                    953:      */
                    954: 
                    955:     public Request createRequest() {
1.94      ylafon    956:         return (Request) template.getDeeperClone();
1.1       abaird    957:     }
                    958: 
                    959:     /**
                    960:      * Global settings - Set the max number of allowed connections.
                    961:      * Set the maximum number of simultaneous connections that can remain
                    962:      * opened. The manager will take care of queuing requests if this number
                    963:      * is reached.
1.94      ylafon    964:      * <p>This value defaults to the value of the
1.82      ylafon    965:      * <code>org.w3c.www.http.connections.max</code> property.
1.94      ylafon    966:      *
1.1       abaird    967:      * @param max_conn The allowed maximum simultaneous open connections.
                    968:      */
                    969: 
1.13      abaird    970:     public synchronized void setMaxConnections(int max_conn) {
1.94      ylafon    971:         this.conn_max = max_conn;
1.35      ylafon    972:     }
                    973: 
                    974:     /**
                    975:      * Global settings - Set the timeout on the socket
1.94      ylafon    976:      * <p/>
                    977:      * <p>This value defaults to the value of the
                    978:      * <code>org.w3c.www.http.connections.timeout</code> property.
1.35      ylafon    979:      *
                    980:      * @param timeout The allowed maximum microsecond before a timeout.
                    981:      */
                    982: 
                    983:     public synchronized void setTimeout(int timeout) {
1.94      ylafon    984:         this.timeout = timeout;
                    985:         Enumeration e = servers.elements();
                    986:         while (e.hasMoreElements()) {
                    987:             ((HttpServer) e.nextElement()).setTimeout(timeout);
                    988:         }
1.62      ylafon    989:     }
                    990: 
                    991:     /**
1.82      ylafon    992:      * Global settings - Set the connection timeout for the socket
1.94      ylafon    993:      * <p/>
                    994:      * <p>This value defaults to the value of the
                    995:      * <code>org.w3c.www.protocol.http.connections.connTimeout</code> property
1.82      ylafon    996:      *
1.94      ylafon    997:      * @param conn_timeout The allowed maximum microsecond before a timeout.
1.82      ylafon    998:      */
                    999: 
                   1000:     public synchronized void setConnTimeout(int conn_timeout) {
1.94      ylafon   1001:         this.conn_timeout = conn_timeout;
                   1002:         Enumeration e = servers.elements();
                   1003:         while (e.hasMoreElements()) {
                   1004:             ((HttpServer) e.nextElement()).setConnTimeout(conn_timeout);
                   1005:         }
1.82      ylafon   1006:     }
                   1007: 
                   1008:     /**
1.62      ylafon   1009:      * Global settings - set the HTTP parsing lenient or not.
1.94      ylafon   1010:      *
1.62      ylafon   1011:      * @param lenient, true by default, false to detect wrong servers
                   1012:      */
                   1013:     public void setLenient(boolean lenient) {
1.94      ylafon   1014:         this.lenient = lenient;
1.62      ylafon   1015:     }
                   1016: 
                   1017:     /**
                   1018:      * Is this manager parsing headers in a lenient way?
1.94      ylafon   1019:      *
1.62      ylafon   1020:      * @return A boolean.
                   1021:      */
                   1022:     public boolean isLenient() {
1.94      ylafon   1023:         return lenient;
1.1       abaird   1024:     }
                   1025: 
                   1026:     /**
                   1027:      * Global settings - Set an optional proxy to use.
                   1028:      * Set the proxy to which all requests should be targeted. If the
1.38      bmahe    1029:      * <code>org.w3c.www.http.proxy</code> property is defined, it will be
1.1       abaird   1030:      * used as the default value.
1.94      ylafon   1031:      *
1.1       abaird   1032:      * @param proxy The URL for the proxy to use.
                   1033:      */
                   1034: 
                   1035:     public void setProxy(URL proxy) {
1.94      ylafon   1036:         template.setProxy(proxy);
1.30      abaird   1037:     }
                   1038: 
                   1039:     /**
                   1040:      * Does this manager uses a proxy to fulfill requests ?
1.94      ylafon   1041:      *
1.30      abaird   1042:      * @return A boolean.
                   1043:      */
                   1044: 
                   1045:     public boolean usingProxy() {
1.94      ylafon   1046:         return template.hasProxy();
1.1       abaird   1047:     }
                   1048: 
                   1049:     /**
                   1050:      * Global settings - Set the request timeout.
1.94      ylafon   1051:      * Once a request has been emited, the HttpManager will sit for this
1.1       abaird   1052:      * given number of milliseconds before the request is declared to have
                   1053:      * timed-out.
                   1054:      * <p>This timeout value defaults to the value of the
1.38      bmahe    1055:      * <code>org.w3c.www.http.requestTimeout</code> property value.
1.94      ylafon   1056:      *
1.1       abaird   1057:      * @param ms The timeout value in milliseconds.
                   1058:      */
                   1059: 
                   1060:     public void setRequestTimeout(int ms) {
                   1061:     }
                   1062: 
                   1063:     /**
                   1064:      * Global settings - Define a global request header.
                   1065:      * Set a default value for some request header. Once defined, the
                   1066:      * header will automatically be defined on <em>all</em> outgoing requests
                   1067:      * created through the <code>createRequest</code> request.
1.94      ylafon   1068:      *
                   1069:      * @param name  The name of the header, case insensitive.
1.1       abaird   1070:      * @param value It's default value.
                   1071:      */
1.65      ylafon   1072: 
1.1       abaird   1073:     public void setGlobalHeader(String name, String value) {
1.94      ylafon   1074:         template.setValue(name, value);
1.1       abaird   1075:     }
                   1076: 
1.18      abaird   1077:     /**
                   1078:      * Global settings - Get a global request header default value.
1.94      ylafon   1079:      *
1.18      abaird   1080:      * @param name The name of the header to get.
                   1081:      * @return The value for that header, as a String, or <strong>
1.94      ylafon   1082:      *         null</strong> if undefined.
1.18      abaird   1083:      */
                   1084: 
1.1       abaird   1085:     public String getGlobalHeader(String name) {
1.94      ylafon   1086:         return template.getValue(name);
1.18      abaird   1087:     }
                   1088: 
1.94      ylafon   1089: 
1.18      abaird   1090:     /**
                   1091:      * Dump all in-memory cached state to persistent storage.
                   1092:      */
                   1093: 
                   1094:     public void sync() {
1.94      ylafon   1095:         filteng.sync();
1.1       abaird   1096:     }
                   1097: 
                   1098:     /**
                   1099:      * Create a new HttpManager.
1.33      abaird   1100:      * FIXME Making this method protected breaks the static method
                   1101:      * to create HttpManager instances (should use a factory here)
1.94      ylafon   1102:      *
                   1103:      * @param props The properties from which the manager should initialize
                   1104:      *              itself, or <strong>null</strong> if none are available.
1.1       abaird   1105:      */
                   1106: 
1.33      abaird   1107:     protected HttpManager() {
1.94      ylafon   1108:         this.template = new Request(this);
                   1109:         this.servers = new Hashtable();
                   1110:         this._tmp_servers = new Hashtable();
                   1111:         this.filteng = new FilterEngine();
                   1112:         this.connectionsLru = new SyncLRUList();
1.1       abaird   1113:     }
                   1114: 
1.83      ylafon   1115: 
1.1       abaird   1116:     /**
                   1117:      * DEBUGGING !
                   1118:      */
                   1119: 
1.83      ylafon   1120:     public synchronized String toString() {
1.94      ylafon   1121:         StringBuilder sb = new StringBuilder();
                   1122:         HttpConnection hcn = (HttpConnection) connectionsLru.getHead();
                   1123:         sb.append("Connections: ");
                   1124:         sb.append(conn_count);
                   1125:         sb.append(" out of ");
                   1126:         sb.append(conn_max);
                   1127:         sb.append("\n\n");
                   1128:         if (hcn != null) {
                   1129:             sb.append("**** Idle Connections list ****\n");
                   1130:             while (hcn != null) {
                   1131:                 sb.append("      ");
                   1132:                 sb.append(hcn.toString());
                   1133:                 sb.append('\n');
                   1134:                 try {
                   1135:                     hcn = (HttpConnection) hcn.getNext();
                   1136:                 } catch (ClassCastException ccex) {
                   1137:                     break;
                   1138:                 }
                   1139:             }
                   1140:         } else {
                   1141:             sb.append("*** NO IDLE CONNECTIONS ***\n");
                   1142:         }
                   1143:         sb.append(servers);
                   1144:         return sb.toString();
1.83      ylafon   1145:     }
                   1146: 
1.1       abaird   1147:     public static void main(String args[]) {
1.94      ylafon   1148:         try {
                   1149:             // Get the manager, and define some global headers:
                   1150:             HttpManager manager = HttpManager.getManager();
                   1151:             manager.setGlobalHeader("User-Agent", DEFAULT_USER_AGENT);
                   1152:             manager.setGlobalHeader("Accept", "*/*;q=1.0");
                   1153:             manager.setGlobalHeader("Accept-Encoding", "gzip");
                   1154:             PropRequestFilter filter =
                   1155:                     new org.w3c.www.protocol.http.cookies.CookieFilter();
                   1156:             filter.initialize(manager);
                   1157:             PropRequestFilter pdebug =
                   1158:                     new org.w3c.www.protocol.http.DebugFilter();
                   1159:             pdebug.initialize(manager);
                   1160:             Request request = manager.createRequest();
                   1161:             request.setURL(new URL(args[0]));
                   1162:             request.setMethod("GET");
                   1163:             Reply reply = manager.runRequest(request);
                   1164:             //Display some infos:
                   1165:             System.out.println("last-modified: " + reply.getLastModified());
                   1166:             System.out.println("length       : " + reply.getContentLength());
                   1167:             // Display the returned body:
                   1168:             InputStream in = reply.getInputStream();
                   1169:             byte buf[] = new byte[4096];
                   1170:             int cnt = 0;
                   1171:             while ((cnt = in.read(buf)) >= 0) {
1.83      ylafon   1172: //           System.out.print(new String(buf, 0, cnt));
1.94      ylafon   1173:             }
                   1174:             System.out.println("-");
                   1175:             in.close();
                   1176:             manager.sync();
                   1177:             System.err.println(manager);
                   1178:         } catch (Exception ex) {
                   1179:             ex.printStackTrace();
                   1180:             if (ex instanceof HttpException) {
                   1181:                 ((HttpException) ex).getException().printStackTrace();
                   1182:             }
                   1183:         }
                   1184:         System.exit(1);
1.1       abaird   1185:     }
                   1186: }
1.34      bmahe    1187: 
                   1188: 

Webmaster