cachefilter.java

来自「很棒的web服务器源代码」· Java 代码 · 共 892 行 · 第 1/2 页

JAVA
892
字号
	URL _ru = request.getURL();	String requrl = URLUtils.normalize(_ru).toExternalForm();	// in the pre-cache, wait for full download	// FIXME should be better than this behaviour...	// see EntityCachedResource perform's FIXME ;)	if (precache.containsKey(requrl)) {	    if (debug)		System.out.println("*** Already downloading: "+ requrl);	    try {		CachedResource cr = (CachedResource)precache.get(requrl);		return cr.perform(request);	    } catch (Exception ex) {		// there was a problem with the previous request, 		// it may be better to do it by ourself	    }	}		CachedResource res = null;	try {	    res = store.getCachedResourceReference(requrl);	} catch (InvalidCacheException ex) {	    res = null;	}	// are we disconnected?	if (request.checkOnlyIfCached() || !connected ) {	    // and no entries...	    EntityCachedResource ecr = null;	    if (res != null) {		ecr = (EntityCachedResource) res.lookupResource(request);	    }	    if ((res == null) || (ecr == null)) {		if ( debug )		    trace(request, "unavailable (disconnected).");		Reply reply = request.makeReply(HTTP.GATEWAY_TIMEOUT);		reply.setContent("The cache doesn't have an entry for "				 + "<p><strong>"+request.getURL()+"</strong>"				 + ". <p>And it is disconnected.");		return reply;	    }	    // yeah!	    if (debug) {		trace(request, (connected) ? " hit - only if cached" : 		      " hit while disconneced" );	    }	    if (!validator.isValid(ecr, request)) {		addWarning(request, WARN_STALE);	    }	    addWarning(request, WARN_DISCONNECTED);	    Reply reply = ecr.perform(request);		// Add any warnings collected during processing to the reply:	    setWarnings(request, reply);//FIXME	    request.setState(STATE_HOW, HOW_HIT);	    return reply;	}	// in connected mode, we should now take care of revalidation and such	if (res != null) {	    // if not fully loaded, ask for a revalidation FIXME	    if ((res.getLoadState() == CachedResource.STATE_LOAD_PARTIAL) ||		(res.getLoadState() == CachedResource.STATE_LOAD_ERROR)) {		setRequestRevalidation(res, request);		return null;	    }	    if ( validator.isValid(res, request) ) {		try {		    store.updateResourceGeneration(res);		} catch (InvalidCacheException ex) {		    // should be ok so...		}//FIXME	    request.setState(STATE_HOW, HOW_HIT);		Reply rep = res.perform(request);		return rep;	    } else {		if (debug) {		    System.out.println("*** Revalidation asked for " + requrl);		}		// ask for a revalidation		setRequestRevalidation(res, request);		return null;	    }	}	// lock here while we are waiting for the download	while (uritable.containsKey(requrl)) {	    synchronized (uritable) {		try {		    uritable.wait();		} catch (InterruptedException ex) {}	    }	    if (precache.containsKey(requrl)) {		if (debug)		    System.out.println("*** Already downloading: "+ requrl);		CachedResource cr = (CachedResource)precache.get(requrl);		return cr.perform(request);	    }	    uritable.put(requrl, requrl);	}	return null;    }    /**     * This filter  handle exceptions.     * @param request The request that triggered the exception.     * @param ex The triggered exception.     * @return Always <strong>false</strong>.     */    public boolean exceptionFilter(Request request, HttpException ex) {	URL _ru;	_ru = request.getURL();	String requrl = URLUtils.normalize(_ru).toExternalForm();	synchronized (uritable) {	    uritable.remove(requrl);	    uritable.notifyAll();	}		return false;    }        /**     * The request post-processing hook.     * After each request has been replied to by the target server (be it a      * proxy or the actual origin server), each filter's outgoingFilter     * method is called.     * <p>It gets the original request, and the actual reply as a parameter,     * and should return whatever reply it wants the caller to get.     * @param request The original (handled) request.     * @param reply The reply, as emited by the target server, or constructed     * by some other filter.     * @exception HttpException If the reply emitted by the server is not     * a valid HTTP reply.     */    public Reply outgoingFilter(Request request, Reply reply) 	throws HttpException    {	URL url = URLUtils.normalize(request.getURL());	// Yeah ! We win:	CachedResource c;	c = (CachedResource) request.getState(CacheState.STATE_RESOURCE);	if ( c != null ) {	    if ( debug )		trace(request, "revalidated "+reply.getStatus());//		request.setState(STATE_HOW//				 , ((reply.getStatus() == HTTP.NOT_MODIFIED)//				    ? HOW_REVALIDATION_SUCCESS//				    : HOW_REVALIDATION_FAILURE));//		Reply valrep = c.validate(request, reply);		// Client or Server error, ask for revalidation//		if (valrep.getStatus()/100 >= 4) {//		    c.setWillRevalidate(true);//		}	    if (reply.getStatus() == HTTP.NOT_MODIFIED) {		// FIXME revalidateResource(request, reply)		validator.revalidateResource(c, request, reply);		try {		    store.storeCachedResource(c, c.getCurrentLength());		} catch (InvalidCacheException ex) {		    if (debug) {			ex.printStackTrace();		    }		}		// extract the original request		Request origreq;		origreq = (Request) request.getState(CacheState.STATE_ORIGREQ);		return c.perform(origreq);	    } else {		// delete it		c.delete();		store.getState().notifyResourceDeleted(c);	    }	}	// don't use cache, exit asap	if (!canCache(request, reply)) {	    request.setState(STATE_NOCACHE, Boolean.TRUE);	    if (debug) {		System.out.println("*** Can't cache reply");	    }	    String requrl = url.toExternalForm();	    precache.remove(requrl);	    synchronized (uritable) {		uritable.remove(requrl);		uritable.notifyAll();	    }	    invalidateOnReply(request, reply);	    return null;	}	if (!canStore(request, reply)) {//	    request.setState(STATE_NOSTORE, Boolean.TRUE);	    if (debug) {		System.out.println("*** Can't store reply");	    }	    String requrl = url.toExternalForm();	    precache.remove(requrl);	    synchronized (uritable) {		uritable.remove(requrl);		uritable.notifyAll();	    }	    invalidateOnReply(request, reply);	    return null;	}	pushDocument(request, reply);	return null;    }    private void invalidateOnReply(Request request, Reply reply) {	URL url = request.getURL();	// now invalidate the entities per rfc2616#13.11	if ((request.getMethod() == HTTP.POST) ||	    (request.getMethod() == HTTP.PUT) ||	    (request.getMethod() == HTTP.DELETE)) {	    String rloc = reply.getLocation();	    String rcloc = reply.getContentLocation();	    URL urloc, urcloc;	    // FIXME use a private method instead of duplicating	    if (rloc != null) {		try {		    urloc = new URL(rloc);		    // only if host match		    if (URLUtils.equalsProtocolHostPort(url,urloc)) {			CachedResource res = null;			try {			    res = store.getCachedResourceReference(rloc);			    if (res != null) {				res.setWillRevalidate(true);			    }			} catch (InvalidCacheException ex) {				// weird, but it won't stop us :)			}		    }		} catch (MalformedURLException mule) {		    // nothing to do		}	    }	    if (rcloc != null) {		try {		    urcloc = new URL(url, rcloc);		    // only if host match		    if (URLUtils.equalsProtocolHostPort(url,urcloc)) {			CachedResource res = null;			try {			    String surcloc = urcloc.toExternalForm();			    res = store.getCachedResourceReference(surcloc);			    if (res != null) {				res.setWillRevalidate(true);			    }			} catch (InvalidCacheException ex) {				// weird, but it won't stop us :)			}		    }		} catch (MalformedURLException mule) {		    // nothing to do		}	    }	}    }	        public void sync() {	if (debug) {	    System.out.println("*** Synching the CacheFilter");	}	try {	    store.sync();	} catch (Exception ex) {	    System.err.println(getClass().getName()+": Unable to save cache.");	}    }    /**     * Property monitoring for the CacheFilter.     * The CacheFilter allows you to dynamically (typically through the      * property setter) change the class of the sweeper, the validator,      * the size...     * @param name The name of the property that has changed.     * @return A boolean, <strong>true</strong> if the change was made,      *    <strong>false</strong> otherwise.     */    public boolean propertyChanged(String name) {	if ( name.equals(SERIALIZER_P) ) {	    CacheSerializer cs = null;	    try {		Class c;		c = Class.forName(props.getString(name, null));		cs = (CacheSerializer) c.newInstance();	    } catch (Exception ex) {		return false;	    }	    serializer = cs;	    return true;	} else if ( name.equals(SWEEPER_P) ) {	    CacheSweeper cs = null;	    try {		Class c;		c = Class.forName(props.getString(name, null));		cs = (CacheSweeper) c.newInstance();	    } catch (Exception ex) {		return false;	    }	    // looks good, let's restart with this one!	    sweeper.destroy();	    sweeper = cs;	    sweeper.start();	    return true;	} else if ( name.equals(VALIDATOR_P) ) {	    CacheValidator cv = null;	    try {		Class c;		c = Class.forName(props.getString(name, null));		cv = (CacheValidator) c.newInstance();	    } catch (Exception ex) {		return false;	    }	    validator = cv;	    return true;	} else if ( name.equals(DEBUG_P) ) {	    debug = props.getBoolean(name, debug);	    return true;	} else if ( name.equals(SHARED_P) ) {	    shared = props.getBoolean(name, shared);	    return true;	} else if ( name.equals(CACHE_CONNECTED_P)) {	    connected = props.getBoolean(name, true);	    return true;	}	// ask the store if it can do something	return store.propertyChanged(name);    }    /**     * Push a document in the cache.     * The caller has to forge a a request and a reply before being able     * to make something     * enter the cache.      * The request should provide at least:     * <dl>     * <dt>URL<dl>The URL (key for cache lookups)     * <dt>Method<dl>The method that was "applied" to URL to get forged     * reply.     * </dl>     * <p>It is recommended that the reply provides at least     * the following informations:     * <dl>     * <dt>Status Code</dl>A valid HTTP/1.1 status code (probably <strong>     * 200</code>)     * <dt>InputStream<dl>Containing the entity to be cached,     * <dt>EntityTag<dl>A valid entity tag for the document,     * <dt>CacheControl<dl>Appropriate HTTP/1.1 cache controls for that     *     document,     * <dt>Mime headers<dl>At least a valid content type, and probably a     *     content length (to check consistency with the reply body).     * </dt>     */    public void pushDocument(Request request, Reply reply) {	URL url = URLUtils.normalize(request.getURL());	try {	    synchronized (uritable) {		CachedResource r = null;		String requrl = url.toExternalForm();		r = CachedResourceFactory.createResource(this, request,							 reply);		if (r.uploading)		    precache.put(requrl, r);		uritable.remove(requrl);		uritable.notifyAll();	    }	    if ( debug )		trace(request, "enters cache.");	} catch (Exception ex) {	    ex.printStackTrace();	}    }    /**     * do what is needed when an upload is done!     * ie: remove from the precache and put in the store     * @param the CachedResource to be moved.     */    protected synchronized void cleanUpload(CachedResource cr) {	// FIXME we should check the state!	precache.remove(cr.getIdentifier());	try {	    store.storeCachedResource(cr);	} catch (InvalidCacheException ex) {	    if (debug) {		ex.printStackTrace();	    }	}    }    /**     * @return the store used a CacheStore     */    public CacheStore getStore() {	return store;    }    public void initialize(HttpManager manager) 	throws PropRequestFilterException     {	String validator_c;	String sweeper_c;	String serializer_c;	props = manager.getProperties();	shared    = props.getBoolean(SHARED_P, true);	connected = props.getBoolean(CACHE_CONNECTED_P, true);	debug     = props.getBoolean(DEBUG_P, false);	// now create the add-on classes	validator_c = props.getString(VALIDATOR_P,		       "org.w3c.www.protocol.http.cache.SimpleCacheValidator");	sweeper_c = props.getString(SWEEPER_P,			 "org.w3c.www.protocol.http.cache.SimpleCacheSweeper");	serializer_c = props.getString(SERIALIZER_P,		      "org.w3c.www.protocol.http.cache.SimpleCacheSerializer");	try {	    Class c;	    c = Class.forName(validator_c);	    validator =  (CacheValidator) c.newInstance();	    validator.initialize(this);	    c = Class.forName(sweeper_c);	    sweeper = (CacheSweeper) c.newInstance();	    sweeper.initialize(this);	    c = Class.forName(serializer_c);	    serializer = (CacheSerializer) c.newInstance();	} catch (Exception ex) {	    // a fatal error! The cache won't be loaded...	    ex.printStackTrace();	    throw new PropRequestFilterException("Unable to start cache");	}	// now create the store as we have the basic things here	store = new CacheStore();	try {	    store.initialize(this);	} catch (InvalidCacheException ex) {	    // hum no worky, should do some action there!	    if (debug) {		ex.printStackTrace();	    }	}	// now start the sweeper	sweeper.start();	// Start the ActiveStream handler:	ActiveStream.initialize();	// Register for property changes:	props.registerObserver(this);	// Now, we are ready, register that filter:	manager.setFilter(this);    }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?