Annotation of java/classes/org/w3c/www/protocol/http/HttpManager.java, revision 1.24
1.1 abaird 1: // HttpManager.java
1.24 ! abaird 2: // $Id: HttpManager.java,v 1.23 1996/12/26 15:24:55 abaird Exp $
1.1 abaird 3: // (c) COPYRIGHT MIT and INRIA, 1996.
4: // Please first read the full copyright statement in file COPYRIGHT.html
5:
6: package w3c.www.protocol.http ;
7:
8: import java.util.*;
9: import java.net.*;
10: import java.io.*; // FIXME - DEBUG
11:
12: import w3c.www.mime.*;
1.4 abaird 13: import w3c.util.*;
1.1 abaird 14:
15: class ReplyFactory implements MimeParserFactory {
16:
17: public MimeHeaderHolder createHeaderHolder(MimeParser parser) {
18: return new Reply(parser);
19: }
20:
21: }
22:
1.9 abaird 23: class HttpServerState {
24: HttpServer server = null;
25: Vector conns = null;
26:
27: final HttpServer getServer() {
28: return server;
29: }
30:
31: synchronized boolean notEnoughConnections() {
32: return (conns == null) || (conns.size() == 1);
33: }
34:
35: void registerConnection(HttpConnection conn) {
36: if ( conns == null )
37: conns = new Vector(4);
38: conns.addElement(conn);
39: }
40:
41: void unregisterConnection(HttpConnection conn) {
42: if ( conns != null )
43: conns.removeElement(conn);
44: }
45:
46: void deleteConnection(HttpConnection conn) {
47: // conn.close();
48: if ( conns != null )
49: conns.removeElement(conn);
50: }
51:
52: synchronized HttpConnection getConnection() {
53: if ((conns != null) && (conns.size() > 0)) {
54: HttpConnection conn = (HttpConnection) conns.elementAt(0);
55: conns.removeElementAt(0);
56: return conn;
57: }
58: return null;
59: }
60:
61: HttpServerState(HttpServer server) {
62: this.server = server;
63: }
64: }
1.1 abaird 65:
66: /**
67: * The client side HTTP request manager.
68: * This class is the user interface (along with the other public classes of
69: * this package) for the W3C client side library implementing HTTP.
70: * A typicall request is launched though the following sequence:
71: * <pre>
72: * HttpManager manager = HttpManager.getManager() ;
73: * Request request = manager.makeRequest() ;
74: * request.setMethod(HTTP.GET) ;
75: * request.setURL(new URL("http://www.w3.org/pub/WWW/"));
76: * Reply reply = manager.runRequest(request) ;
77: * // Get the reply input stream that contains the actual data:
78: * InputStream in = reply.getInputStream() ;
79: * ...
80: * </pre>
81: */
82:
1.13 abaird 83: public class HttpManager implements PropertyMonitoring {
1.17 abaird 84: private static final
85: String DEFAULT_SERVER_CLASS = "w3c.www.protocol.http.HttpBasicServer";
86:
87: /**
88: * The name of the property indicating the class of HttpServer to use.
89: */
90: public static final
91: String SERVER_CLASS_P = "w3c.www.protocol.http.server";
92:
1.2 abaird 93: /**
94: * The name of the property containing the ProprequestFilter to launch.
95: */
1.12 abaird 96: public static final
97: String FILTERS_PROP_P = "w3c.www.protocol.http.filters";
1.5 abaird 98: /**
1.11 abaird 99: * The maximum number of simultaneous connections.
100: */
101: public static final
1.12 abaird 102: String CONN_MAX_P = "w3c.www.protocol.http.connections.max";
1.11 abaird 103: /**
1.5 abaird 104: * Header properties - The allowed drift for getting cached resources.
105: */
106: public static final
1.12 abaird 107: String MAX_STALE_P = "w3c.www.protocol.http.cacheControl.maxStale";
1.5 abaird 108: /**
109: * Header properties - The minium freshness required on cached resources.
110: */
111: public static final
1.12 abaird 112: String MIN_FRESH_P = "w3c.www.protocol.http.cacheControl.minFresh";
1.5 abaird 113: /**
114: * Header properties - Set the only if cached flag on requests.
115: */
116: public static final
1.12 abaird 117: String ONLY_IF_CACHED_P="w3c.www.protocol.http.cacheControl.onlyIfCached";
1.5 abaird 118: /**
119: * Header properties - Set the user agent.
120: */
121: public static final
1.12 abaird 122: String USER_AGENT_P = "w3c.www.protocol.http.userAgent";
1.5 abaird 123: /**
124: * Header properties - Set the accept header.
125: */
126: public static final
1.12 abaird 127: String ACCEPT_P = "w3c.www.protocol.http.accept";
1.5 abaird 128: /**
129: * Header properties - Set the accept language.
130: */
131: public static final
1.12 abaird 132: String ACCEPT_LANGUAGE_P = "w3c.www.protocol.http.acceptLanguage";
1.5 abaird 133: /**
134: * Header properties - Set the accept encodings.
135: */
136: public static final
1.12 abaird 137: String ACCEPT_ENCODING_P = "w3c.www.protocol.http.acceptEncoding";
1.5 abaird 138: /**
139: * Header properties - Should we use a proxy ?
140: */
141: public static final
1.12 abaird 142: String PROXY_SET_P = "proxySet";
1.5 abaird 143: /**
144: * Header properties - What is the proxy host name.
145: */
146: public static final
1.12 abaird 147: String PROXY_HOST_P = "proxyHost";
1.5 abaird 148: /**
149: * Header properties - What is the proxy port number.
150: */
151: public static final
1.12 abaird 152: String PROXY_PORT_P = "proxyPort";
1.2 abaird 153:
1.7 abaird 154: /**
155: * The default value for the <code>Accept</code> header.
156: */
157: public static final
158: String DEFAULT_ACCEPT = "*/*";
159: /**
160: * The default value for the <code>User-Agent</code> header.
161: */
162: public static final
163: String DEFAULT_USER_AGENT = "Jigsaw/1.0a2";
164:
1.24 ! abaird 165: /**
! 166: * This array keeps track of all the created managers.
! 167: * A new manager (kind of HTTP client side context) is created for each
! 168: * diffferent set of properties.
! 169: */
! 170: private static HttpManager managers[] = new HttpManager[4];
1.1 abaird 171:
172: /**
1.17 abaird 173: * The class to instantiate to create new HttpServer instances.
174: */
175: protected Class serverclass = null;
176: /**
1.13 abaird 177: * The properties we initialized from.
178: */
179: ObservableProperties props = null;
180: /**
1.1 abaird 181: * The server this manager knows about, indexed by FQDN of target servers.
182: */
183: protected Hashtable servers = null;
184: /**
185: * The template request (the request we will clone to create new requests)
186: */
1.4 abaird 187: protected Request template = null ;
188: /**
1.9 abaird 189: * The LRU list of connections.
1.4 abaird 190: */
1.9 abaird 191: protected LRUList connectionsLru = null;
1.1 abaird 192: /**
193: * The filter engine attached to this manager.
194: */
195: FilterEngine filteng = null;
196:
1.9 abaird 197: protected int conn_count = 0;
198: protected int conn_max = 5;
199:
1.1 abaird 200: /**
1.13 abaird 201: * Update the proxy configuration to match current properties setting.
202: * @return A boolean, <strong>true</strong> if change was done,
203: * <strong>false</strong> otherwise.
204: */
205:
206: protected boolean updateProxy() {
207: boolean set = props.getBoolean(PROXY_SET_P, false);
208: if ( set ) {
209: // Wow using a proxy now !
210: String host = props.getString(PROXY_HOST_P, null);
211: int port = props.getInteger(PROXY_PORT_P, -1);
212: URL proxy = null;
213: try {
214: proxy = new URL("http", host, port, "/");
215: } catch (Exception ex) {
216: return false;
217: }
218: // Now if a proxy...
219: if ( proxy != null )
1.24 ! abaird 220: template.setProxy(proxy);
1.13 abaird 221: } else {
1.24 ! abaird 222: template.setProxy(null);
1.13 abaird 223: }
224: return true;
225: }
226:
227: /**
1.24 ! abaird 228: * Get this manager properties.
! 229: * @return An ObservableProperties instance.
! 230: */
! 231:
! 232: public final ObservableProperties getProperties() {
! 233: return props;
! 234: }
! 235:
! 236: /**
1.13 abaird 237: * PropertyMonitoring implementation - Update properties on the fly !
238: * @param name The name of the property that has changed.
239: * @return A boolean, <strong>true</strong> if change is accepted,
240: * <strong>false</strong> otherwise.
241: */
242:
243: public boolean propertyChanged(String name) {
1.24 ! abaird 244: Request tpl = template;
1.13 abaird 245: if ( name.equals(FILTERS_PROP_P) ) {
246: return false;
247: } else if ( name.equals(CONN_MAX_P) ) {
248: setMaxConnections(props.getInteger(CONN_MAX_P, conn_max));
249: return true;
250: } else if ( name.equals(MAX_STALE_P) ) {
251: int ival = props.getInteger(MAX_STALE_P, -1);
252: if ( ival >= 0 )
253: tpl.setMaxStale(ival);
254: return true;
255: } else if ( name.equals(MIN_FRESH_P) ) {
256: int ival = props.getInteger(MIN_FRESH_P, -1);
257: if ( ival >= 0 )
258: tpl.setMinFresh(ival);
259: return true;
260: } else if ( name.equals(ONLY_IF_CACHED_P) ) {
261: tpl.setOnlyIfCached(props.getBoolean(ONLY_IF_CACHED_P, false));
262: return true;
263: } else if ( name.equals(USER_AGENT_P) ) {
264: tpl.setValue("user-agent"
265: , props.getString(USER_AGENT_P
266: , DEFAULT_USER_AGENT));
267: return true;
268: } else if ( name.equals(ACCEPT_P) ) {
269: tpl.setValue("accept"
270: , props.getString(ACCEPT_P, DEFAULT_ACCEPT));
271: return true;
272: } else if ( name.equals(ACCEPT_LANGUAGE_P) ) {
273: String sval = props.getString(ACCEPT_LANGUAGE_P, null);
274: if ( sval != null )
275: tpl.setValue("accept-language", sval);
276: return true;
277: } else if ( name.equals(ACCEPT_ENCODING_P) ) {
278: String sval = props.getString(ACCEPT_ENCODING_P, null);
279: if ( sval != null )
280: tpl.setValue("accept-encoding", sval);
281: return true;
282: } else if ( name.equals(PROXY_SET_P)
283: || name.equals(PROXY_HOST_P)
284: || name.equals(PROXY_PORT_P) ) {
285: return updateProxy();
286: } else {
287: return true;
288: }
289: }
290:
291: /**
1.4 abaird 292: * Allow the manager to interact with the user if needed.
293: * This will, for example, allow prompting for paswords, etc.
294: * @param onoff Turn interaction on or off.
295: */
296:
297: public void setAllowUserInteraction(boolean onoff) {
298: template.setAllowUserInteraction(onoff);
299: }
300:
301: /**
1.1 abaird 302: * Get an instance of the HTTP manager.
303: * This method returns an actual instance of the HTTP manager. It may
304: * return different managers, if it decides to distribute the load on
305: * different managers (avoid the HttpManager being a bottleneck).
306: * @return An application wide instance of the HTTP manager.
307: */
308:
1.24 ! abaird 309: public static synchronized HttpManager getManager(Properties p) {
! 310: // Does such a manager exists already ?
! 311: for (int i = 0 ; i < managers.length ; i++) {
! 312: if ( managers[i] == null )
! 313: continue;
! 314: if ( managers[i].getProperties() == p ) {
! 315: return managers[i];
! 316: }
! 317: }
! 318: // Create a new manager for this set of properties:
! 319: HttpManager manager = new HttpManager(p) ;
! 320: // Get the props we will initialize from:
! 321: if ( p instanceof ObservableProperties )
! 322: manager.props = (ObservableProperties) p;
! 323: else
! 324: manager.props = new ObservableProperties(p);
! 325: ObservableProperties props = manager.props;
! 326: // Initialize this new manager filters:
! 327: String filters[] = props.getStringArray(FILTERS_PROP_P, null);
! 328: if ( filters != null ) {
! 329: for (int i = 0 ; i < filters.length ; i++) {
! 330: try {
! 331: Class c = Class.forName(filters[i]);
! 332: PropRequestFilter f = null;
! 333: f = (PropRequestFilter) c.newInstance();
! 334: f.initialize(manager);
! 335: } catch (PropRequestFilterException ex) {
! 336: System.out.println("Couldn't initialize filter \""
! 337: + filters[i]
! 338: + "\" init failed: "
! 339: + ex.getMessage());
! 340: } catch (Exception ex) {
! 341: System.err.println("Error initializing prop filters:");
! 342: System.err.println("Coulnd't initialize ["
! 343: + filters[i]
! 344: + "]: " + ex.getMessage());
! 345: ex.printStackTrace();
! 346: System.exit(1);
1.2 abaird 347: }
348: }
1.24 ! abaird 349: }
! 350: // The factory to create MIME reply holders:
! 351: manager.factory = new ReplyFactory();
! 352: // The class to create HttpServer instances from
! 353: String c = props.getString(SERVER_CLASS_P, DEFAULT_SERVER_CLASS);
! 354: try {
! 355: manager.serverclass = Class.forName(c);
! 356: } catch (Exception ex) {
! 357: System.err.println("Unable to initialize HttpManager: ");
! 358: System.err.println("Class \""+c+"\" not found, from property "
! 359: + SERVER_CLASS_P);
! 360: ex.printStackTrace();
! 361: System.exit(1);
! 362: }
! 363: // Setup the template request:
! 364: Request tpl = manager.template;
! 365: // Set some default headers value (from props)
! 366: // Check for a proxy ?
! 367: manager.updateProxy();
! 368: // CacheControl, only-if-cached
! 369: tpl.setOnlyIfCached(props.getBoolean(ONLY_IF_CACHED_P, false));
! 370: // CacheControl, maxstale
! 371: int ival = props.getInteger(MAX_STALE_P, -1);
! 372: if ( ival >= 0 )
! 373: tpl.setMaxStale(ival);
! 374: // CacheControl, minfresh:
! 375: ival = props.getInteger(MIN_FRESH_P, -1);
! 376: if ( ival >= 0 )
! 377: tpl.setMinFresh(ival);
! 378: // General, User agent
! 379: tpl.setValue("user-agent"
! 380: , props.getString(USER_AGENT_P
! 381: , DEFAULT_USER_AGENT));
! 382: // General, Accept
! 383: tpl.setValue("accept"
! 384: , props.getString(ACCEPT_P, DEFAULT_ACCEPT));
! 385: // General, Accept-Language
! 386: String sval = props.getString(ACCEPT_LANGUAGE_P, null);
! 387: if ( sval != null )
! 388: tpl.setValue("accept-language", sval);
! 389: // General, Accept-Encoding
! 390: sval = props.getString(ACCEPT_ENCODING_P, null);
! 391: if ( sval != null )
! 392: tpl.setValue("accept-encoding", sval);
! 393: // Maximum number of allowed connections:
! 394: manager.conn_max = props.getInteger(CONN_MAX_P, 5);
! 395: // Register ourself as a property observer:
! 396: props.registerObserver(manager);
! 397: // Register that manager in our knwon managers:
! 398: for (int i = 0 ; i < managers.length ; i++) {
! 399: if ( managers[i] == null ) {
! 400: managers[i] = manager;
! 401: return manager;
1.17 abaird 402: }
1.1 abaird 403: }
1.24 ! abaird 404: HttpManager newman[] = new HttpManager[managers.length << 1];
! 405: System.arraycopy(managers, 0, newman, 0, managers.length);
! 406: newman[managers.length] = manager;
! 407: managers = newman;
1.1 abaird 408: return manager;
409: }
410:
1.24 ! abaird 411: public static HttpManager getManager() {
! 412: return getManager(System.getProperties());
! 413: }
1.1 abaird 414:
415: /**
416: * Get the appropriate server object for handling request to given target.
417: * @param key The server's identifier encoded as a <code>host:port</code>
418: * String.
419: * @return An object complying to the HttpServer interface.
420: * @exception HttpException If the given host name couldn't be resolved.
421: */
422:
1.5 abaird 423: protected synchronized HttpServer lookupServer(String host, int port)
1.1 abaird 424: throws HttpException
425: {
1.5 abaird 426: int p = (port == -1) ? 80 : port;
427: String id = (p == 80) ? host : (host +":"+p);
1.1 abaird 428: // Check for an existing server:
429: HttpServer server = (HttpServer) servers.get(id);
430: if ( server != null )
431: return server;
432: // Create and register a new server:
1.17 abaird 433: try {
434: server = (HttpServer) serverclass.newInstance();
435: } catch (Exception ex) {
436: String msg = ("Unable to create an instance of \""
437: + serverclass.getName()
438: + "\", invalid config, check the "
439: + SERVER_CLASS_P + " property.");
1.21 abaird 440: throw new HttpException(ex, msg);
1.17 abaird 441: }
1.9 abaird 442: server.initialize(this, new HttpServerState(server), host, p);
1.1 abaird 443: servers.put(id, server);
444: return server;
445: }
1.5 abaird 446:
1.1 abaird 447: /**
1.9 abaird 448: * The given connection is about to be used.
1.4 abaird 449: * Update our list of available servers.
1.9 abaird 450: * @param conn The idle connection.
1.4 abaird 451: */
452:
1.9 abaird 453: public void notifyUse(HttpConnection conn) {
454: connectionsLru.remove(conn);
1.4 abaird 455: }
456:
457: /**
1.9 abaird 458: * The given connection can be reused, but is now idle.
459: * @param conn The connection that is now idle.
1.4 abaird 460: */
461:
1.9 abaird 462: public void notifyIdle(HttpConnection conn) {
463: connectionsLru.toHead(conn);
1.4 abaird 464: }
465:
466: /**
1.9 abaird 467: * The given connection has just been created.
468: * @param conn The newly created connection.
469: */
470:
471: protected synchronized void notifyConnection(HttpConnection conn) {
472: if ( ++conn_count > conn_max )
473: closeAnyConnection();
474: }
475:
476: /**
477: * The given connection has been deleted.
478: * @param conn The deleted connection.
479: */
480:
481: protected void deleteConnection(HttpConnection conn) {
482: HttpServerState ss = conn.getServer().getState();
483: ss.deleteConnection(conn);
484: synchronized(this) {
485: --conn_count;
486: notifyAll();
487: }
488: }
489:
490: protected synchronized boolean tooManyConnections() {
491: return conn_count > conn_max;
492: }
493:
494: /**
495: * Try reusing one of the idle connection of that server, if any.
496: * @param server The target server.
497: * @return An currently idle connection to the given server.
1.4 abaird 498: */
499:
1.9 abaird 500: protected HttpConnection getConnection(HttpServer server) {
501: HttpServerState ss = server.getState();
502: return ss.getConnection();
503: }
504:
505: protected synchronized void waitForConnection(HttpServer server)
506: throws InterruptedException
507: {
508: wait();
1.4 abaird 509: }
510:
511: /**
1.9 abaird 512: * Close one connection, but pickling the least recently used one.
513: * @return A boolean, <strong>true</strong> if a connection was closed
514: * <strong>false</strong> otherwise.
1.4 abaird 515: */
516:
1.9 abaird 517: protected boolean closeAnyConnection() {
518: HttpConnection conn = (HttpConnection) connectionsLru.removeTail();
519: if ( conn != null ) {
520: conn.close();
521: deleteConnection(conn);
522: return true;
523: } else {
524: return false;
525: }
1.4 abaird 526: }
527:
528: /**
1.1 abaird 529: * One of our server handler wants to open a connection.
530: * @param block A boolean indicating whether we should block the calling
531: * thread until a token is available (otherwise, the method will just
532: * peek at the connection count, and return the appropriate result).
533: * @return A boolean, <strong>true</strong> if the connection can be
534: * opened straight, <strong>false</strong> otherwise.
535: */
536:
1.9 abaird 537: protected boolean negotiateConnection(HttpServer server) {
538: HttpServerState ss = server.getState();
1.10 abaird 539: if ( ! tooManyConnections() ) {
1.4 abaird 540: return true;
1.10 abaird 541: } else if ( ss.notEnoughConnections() ) {
542: return closeAnyConnection();
1.9 abaird 543: } else if ( servers.size() > conn_max ) {
544: return closeAnyConnection();
1.4 abaird 545: }
1.9 abaird 546: return false;
547: }
548:
549: /**
550: * A new client connection has been established.
551: * This method will try to maintain a maximum number of established
552: * connections, by closing idle connections when possible.
553: * @param server The server that has established a new connection.
554: */
555:
556: protected final synchronized void incrConnCount(HttpServer server) {
557: if ( ++conn_count > conn_max )
558: closeAnyConnection();
559: }
560:
561: /**
562: * Decrement the number of established connections.
563: * @param server The server that has closed one connection to its target.
564: */
565:
566: protected final synchronized void decrConnCount(HttpServer server) {
567: --conn_count;
1.1 abaird 568: }
569:
570: /**
571: * Run the given request, in synchronous mode.
572: * This method will launch the given request, and block the calling thread
573: * until the response headers are available.
574: * @param request The request to run.
575: * @return An instance of Reply, containing all the reply
576: * informations.
577: * @exception HTTPException If something failed during request processing.
578: */
579:
580: public Reply runRequest(Request request)
581: throws HttpException
582: {
1.19 abaird 583: URL target = request.getURL();
584: Reply reply = null;
585: int fcalls = 0;
1.1 abaird 586: // Now run through the ingoing filters:
587: RequestFilter filters[] = filteng.run(request);
588: if ( filters != null ) {
589: for (int i = 0 ; i < filters.length ; i++) {
1.19 abaird 590: if ((reply = filters[fcalls].ingoingFilter(request)) != null)
591: break;
592: fcalls++;
1.1 abaird 593: }
594: }
1.16 abaird 595: // Locate the appropriate target server:
1.19 abaird 596: if ( reply == null ) {
1.21 abaird 597: HttpServer srv = null;
1.22 ylafon 598: URL proxy;
599: boolean rtry ;
1.21 abaird 600: do {
1.22 ylafon 601: rtry = false;
1.21 abaird 602: try {
1.22 ylafon 603: proxy = request.getProxy();
1.21 abaird 604: srv = ((proxy != null)
605: ? lookupServer(proxy.getHost(), proxy.getPort())
606: : lookupServer(target.getHost(), target.getPort()));
607: reply = srv.runRequest(request);
608: } catch (HttpException ex) {
609: for (int i = 0; i < fcalls; i++)
610: rtry = rtry || filters[i].exceptionFilter(request, ex);
1.23 abaird 611: if ( ! rtry )
1.22 ylafon 612: throw ex;
1.21 abaird 613: }
614: } while (rtry);
1.16 abaird 615: }
1.1 abaird 616: // Apply the filters on the way back:
617: if ( filters != null ) {
1.19 abaird 618: while (--fcalls >= 0) {
619: Reply frep = filters[fcalls].outgoingFilter(request, reply);
620: if ( frep != null ) {
621: reply = frep;
622: break;
623: }
1.3 abaird 624: }
1.1 abaird 625: }
626: return reply;
627: }
628:
629: /**
630: * Get this manager's reply factory.
631: * The Reply factory is used when prsing incomming reply from servers, it
632: * decides what object will be created to hold the actual reply from the
633: * server.
634: * @return An object compatible with the MimeParserFactory interface.
635: */
636:
637: MimeParserFactory factory = null ;
638:
1.9 abaird 639: public MimeParserFactory getReplyFactory() {
1.1 abaird 640: return factory;
641: }
642:
643: /**
644: * Add a new request filter.
645: * Request filters are called <em>before</em> a request is launched, and
646: * <em>after</em> the reply headers are available. They allow applications
647: * to setup specific request headers (such as PICS, or PEP stuff) on the
648: * way in, and check the reply on the way out.
649: * <p>Request filters are application wide: if their scope matches
650: * the current request, then they will always be aplied.
651: * <p>Filter scopes are defined inclusively and exclusively
1.15 abaird 652: * @param incs The URL domains for which the filter should be triggered.
653: * @param exs The URL domains for which the filter should not be triggered.
1.1 abaird 654: * @param filter The request filter to add.
655: */
656:
657: public void setFilter(URL incs[], URL exs[], RequestFilter filter) {
658: if ( incs != null ) {
659: for (int i = 0 ; i < incs.length ; i++)
660: filteng.setFilter(incs[i], true, filter);
661: }
662: if ( exs != null ) {
663: for (int i = 0 ; i < exs.length ; i++)
664: filteng.setFilter(exs[i], false, filter);
665: }
666: return;
667: }
668:
1.15 abaird 669: /**
670: * Add a global filter.
671: * The given filter will <em>always</em> be invoked.
672: * @param filter The filter to install.
673: */
674:
1.1 abaird 675: public void setFilter(RequestFilter filter) {
676: filteng.setFilter(filter);
677: }
678:
679: /**
1.15 abaird 680: * Find back an instance of a global filter.
681: * This methods allow external classes to get a pointer to installed
682: * filters of a given class.
683: * @param cls The class of the filter to look for.
684: * @return A RequestFilter instance, or <strong>null</strong> if not
685: * found.
1.1 abaird 686: */
687:
1.15 abaird 688: public RequestFilter getGlobalFilter(Class cls) {
689: return filteng.getGlobalFilter(cls);
1.1 abaird 690: }
691:
692: /**
693: * Create a new default outgoing request.
694: * This method should <em>always</em> be used to create outgoing requests.
695: * It will initialize the request with appropriate default values for
696: * the various headers, and make sure that the request is enhanced by
697: * the registered request filters.
698: * @return An instance of Request, suitable to be launched.
699: */
700:
701: public Request createRequest() {
702: return (Request) template.getClone() ;
703: }
704:
705: /**
706: * Global settings - Set the max number of allowed connections.
707: * Set the maximum number of simultaneous connections that can remain
708: * opened. The manager will take care of queuing requests if this number
709: * is reached.
710: * <p>This value defaults to the value of the
711: * <code>w3c.www.http.maxConnections</code> property.
712: * @param max_conn The allowed maximum simultaneous open connections.
713: */
714:
1.13 abaird 715: public synchronized void setMaxConnections(int max_conn) {
716: this.conn_max = max_conn;
1.1 abaird 717: }
718:
719: /**
720: * Global settings - Set an optional proxy to use.
721: * Set the proxy to which all requests should be targeted. If the
722: * <code>w3c.www.http.proxy</code> property is defined, it will be
723: * used as the default value.
724: * @param proxy The URL for the proxy to use.
725: */
726:
727: public void setProxy(URL proxy) {
1.5 abaird 728: template.setProxy(proxy);
1.1 abaird 729: }
730:
731: /**
732: * Global settings - Set the request timeout.
733: * Once a request has been emited, the HttpManager will sit for this
734: * given number of milliseconds before the request is declared to have
735: * timed-out.
736: * <p>This timeout value defaults to the value of the
737: * <code>w3c.www.http.requestTimeout</code> property value.
738: * @param ms The timeout value in milliseconds.
739: */
740:
741: public void setRequestTimeout(int ms) {
742: }
743:
744: /**
745: * Global settings - Define a global request header.
746: * Set a default value for some request header. Once defined, the
747: * header will automatically be defined on <em>all</em> outgoing requests
748: * created through the <code>createRequest</code> request.
749: * @param name The name of the header, case insensitive.
750: * @param value It's default value.
751: */
752:
753: public void setGlobalHeader(String name, String value) {
754: template.setValue(name, value);
755: }
756:
1.18 abaird 757: /**
758: * Global settings - Get a global request header default value.
759: * @param name The name of the header to get.
760: * @return The value for that header, as a String, or <strong>
761: * null</strong> if undefined.
762: */
763:
1.1 abaird 764: public String getGlobalHeader(String name) {
765: return template.getValue(name);
1.18 abaird 766: }
767:
768:
769: /**
770: * Dump all in-memory cached state to persistent storage.
771: */
772:
773: public void sync() {
774: filteng.sync();
1.1 abaird 775: }
776:
777: /**
778: * Create a new HttpManager.
779: * This can only be called from this package. The caller must rather
780: * use the <code>getManager</code> method.
781: * @param props The properties from which the manager should initialize
782: * itself, or <strong>null</strong> if none are available.
783: */
784:
785: HttpManager(Properties props) {
1.9 abaird 786: this.template = new Request(this);
787: this.servers = new Hashtable();
788: this.filteng = new FilterEngine();
789: this.connectionsLru = new SyncLRUList();
1.1 abaird 790: }
791:
792: HttpManager() {
793: this(System.getProperties());
794: }
795:
796: /**
797: * DEBUGGING !
798: */
799:
800: public static void main(String args[]) {
801: try {
802: // Get the manager, and define some global headers:
803: HttpManager manager = HttpManager.getManager();
804: manager.setGlobalHeader("User-Agent", "Jigsaw/1.0a");
805: manager.setGlobalHeader("Accept", "*/*;q=1.0");
806: manager.setGlobalHeader("Accept-Encoding", "gzip");
807: Request request = manager.createRequest();
808: request.setURL(new URL(args[0]));
809: request.setMethod("GET");
810: Reply reply = manager.runRequest(request);
811: // Display some infos:
812: System.out.println("last-modified: "+reply.getLastModified());
813: System.out.println("length : "+reply.getContentLength());
814: // Display the returned body:
815: InputStream in = reply.getInputStream();
816: byte buf[] = new byte[4096];
817: int cnt = 0;
818: while ((cnt = in.read(buf)) > 0)
819: System.out.print(new String(buf, 0, 0, cnt));
820: System.out.println("-");
821: in.close();
822: } catch (Exception ex) {
823: ex.printStackTrace();
824: }
825: System.exit(1);
826: }
827: }
Webmaster