001: // PushCacheFilter.java
002: // $Id: PushCacheFilter.java,v 1.1 2001/10/03 15:00:46 ylafon Exp $
003: // (c) COPYRIGHT MIT, INRIA and Keio, 2001.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.www.protocol.http.cache.push;
007:
008: import java.net.URL;
009:
010: import org.w3c.www.http.HTTP;
011:
012: import org.w3c.www.protocol.http.Reply;
013: import org.w3c.www.protocol.http.Request;
014: import org.w3c.www.protocol.http.HttpException;
015: import org.w3c.www.protocol.http.HttpManager;
016: import org.w3c.www.protocol.http.PropRequestFilterException;
017: import org.w3c.www.protocol.http.cache.CacheFilter;
018: import org.w3c.www.protocol.http.cache.CacheSweeper;
019: import org.w3c.www.protocol.http.cache.CachedResource;
020: import org.w3c.www.protocol.http.cache.EntityCachedResource;
021: import org.w3c.www.protocol.http.cache.InvalidCacheException;
022: import org.w3c.www.protocol.http.cache.CacheState;
023: import org.w3c.www.protocol.http.cache.CacheValidator;
024: import org.w3c.www.protocol.http.cache.CacheSerializer;
025: import org.w3c.www.protocol.http.cache.ActiveStream;
026:
027: /**
028: * PushCacheFilter
029: * Based heavily on (much code stolen from) CacheFilter
030: * The important differences are in the initialization where the
031: * PushCacheListener is started, and in ingoingFilter where if
032: * the requested resource is present in the cache and is a PUSH
033: * resource, then the resource is returned immediately without
034: * checking for expiry etc. This allows us to insert pages from
035: * "virtual" web sites such as http://www.push.data/sensor1.html
036: *
037: * @author Paul Henshaw, The Fantastic Corporation, Paul.Henshaw@fantastic.com
038: * @version $Revision: 1.1 $
039: * $Id: PushCacheFilter.java,v 1.1 2001/10/03 15:00:46 ylafon Exp $
040: */
041: public class PushCacheFilter extends CacheFilter {
042: /**
043: * Property name used to acquire port number for {@link PushCacheListener}
044: * value is "org.w3c.www.protocol.http.cache.push.portnumber";
045: */
046: public static final String PORT_NUM_P = "org.w3c.www.protocol.http.cache.push.portnumber";
047:
048: /**
049: * Default port number to use if property value is not supplied
050: * value is 9876
051: */
052: public static final int DEFAULT_PORT_NUM = 9876;
053:
054: /**
055: * Access to PushCacheStore
056: */
057: public PushCacheStore getPushCacheStore() {
058: return ((PushCacheStore) super .getStore());
059: }
060:
061: /**
062: * check if we can use the cache or not for this request
063: * It marks the request as being not cachable if false.
064: * @param a request, the incoming client-side request
065: * @return a boolean, true if we can use the cache
066: */
067: public boolean canUseCache(Request req) {
068: return true;
069: }
070:
071: /**
072: * The request pre-processing hook.
073: * Before each request is launched, all filters will be called back through
074: * this method. They will generally set up additional request header
075: * fields to enhance the request.
076: * @param request The request that is about to be launched.
077: * @return An instance of Reply if the filter could handle the request,
078: * or <strong>null</strong> if processing should continue normally.
079: * @exception HttpException If the filter is supposed to fulfill the
080: * request, but some error happened during that processing.
081: */
082: public Reply ingoingFilter(Request request) throws HttpException {
083: // can we use the cache?
084: if (!canUseCache(request)) {
085: if (debug) {
086: trace(request, "*** Can't use cache");
087: }
088: // we will invalidate this resource, will do that only
089: // on real entity resource, not on negotiated ones
090: if (connected) {
091: CachedResource res = null;
092: EntityCachedResource invalidRes = null;
093: try {
094: String requrl = request.getURL().toExternalForm();
095: res = store.getCachedResourceReference(requrl);
096: if (res != null) {
097: invalidRes = (EntityCachedResource) res
098: .lookupResource(request);
099: }
100: } catch (InvalidCacheException ex) {
101: invalidRes = null;
102: }
103: if (invalidRes != null) {
104: invalidRes.setWillRevalidate(true);
105: }
106: request.setState(STATE_NOCACHE, Boolean.TRUE);
107: return null;
108: } else {
109: // disconnected, abort now!
110: Reply reply = request.makeReply(HTTP.GATEWAY_TIMEOUT);
111: reply.setContent("The cache cannot be use for "
112: + "<p><code>" + request.getMethod()
113: + "</code> " + "<strong>" + request.getURL()
114: + "</strong>" + ". <p>It is disconnected.");
115: return reply;
116: }
117: }
118: // let's try to get the resource!
119:
120: String requrl = request.getURL().toExternalForm();
121: // in the pre-cache, wait for full download
122: // FIXME should be better than this behaviour...
123: // see EntityCachedResource perform's FIXME ;)
124: if (precache.containsKey(requrl)) {
125: if (debug)
126: System.out
127: .println("*** Already downloading: " + requrl);
128: try {
129: CachedResource cr = (CachedResource) precache
130: .get(requrl);
131: return cr.perform(request);
132: } catch (Exception ex) {
133: // there was a problem with the previous request,
134: // it may be better to do it by ourself
135: }
136: }
137:
138: CachedResource res = null;
139: try {
140: res = store.getCachedResourceReference(requrl);
141: } catch (InvalidCacheException ex) {
142: res = null;
143: }
144:
145: // Is this a push resource ?
146: try {
147: if (PushCacheManager.instance().isPushResource(res)) {
148: EntityCachedResource ecr = (EntityCachedResource) res
149: .lookupResource(request);
150:
151: if (ecr != null) {
152: Reply reply = ecr.perform(request);
153: return reply;
154: }
155: }
156: } catch (Exception e) {
157: e.printStackTrace();
158: }
159: // /PSLH
160:
161: // are we disconnected?
162: if (request.checkOnlyIfCached() || !connected) {
163: // and no entries...
164: EntityCachedResource ecr = null;
165: if (res != null) {
166: ecr = (EntityCachedResource) res
167: .lookupResource(request);
168: }
169: if ((res == null) || (ecr == null)) {
170: if (debug)
171: trace(request, "unavailable (disconnected).");
172: Reply reply = request.makeReply(HTTP.GATEWAY_TIMEOUT);
173: reply.setContent("The cache doesn't have an entry for "
174: + "<p><strong>" + request.getURL()
175: + "</strong>" + ". <p>And it is disconnected.");
176: return reply;
177: }
178: // yeah!
179: if (debug) {
180: trace(request, (connected) ? " hit - only if cached"
181: : " hit while disconneced");
182: }
183: if (!validator.isValid(ecr, request)) {
184: addWarning(request, WARN_STALE);
185: }
186: addWarning(request, WARN_DISCONNECTED);
187: Reply reply = ecr.perform(request);
188: // Add any warnings collected during processing to the reply:
189: setWarnings(request, reply);
190: //FIXME request.setState(STATE_HOW, HOW_HIT);
191: return reply;
192: }
193:
194: // in connected mode, we should now take care of revalidation and such
195: if (res != null) {
196: // if not fully loaded, ask for a revalidation FIXME
197: if ((res.getLoadState() == CachedResource.STATE_LOAD_PARTIAL)
198: || (res.getLoadState() == CachedResource.STATE_LOAD_ERROR)) {
199: setRequestRevalidation(res, request);
200: return null;
201: }
202:
203: if (validator.isValid(res, request)) {
204: try {
205: store.updateResourceGeneration(res);
206: } catch (InvalidCacheException ex) {
207: // should be ok so...
208: }
209: //FIXME request.setState(STATE_HOW, HOW_HIT);
210: Reply rep = res.perform(request);
211: return rep;
212: } else {
213: if (debug) {
214: System.out.println("*** Revalidation asked for "
215: + requrl);
216: }
217:
218: // ask for a revalidation
219: setRequestRevalidation(res, request);
220: return null;
221: }
222: }
223:
224: // lock here while we are waiting for the download
225: while (uritable.containsKey(requrl)) {
226: synchronized (uritable) {
227: try {
228: uritable.wait();
229: } catch (InterruptedException ex) {
230: }
231: }
232: if (precache.containsKey(requrl)) {
233: if (debug)
234: System.out.println("*** Already downloading: "
235: + requrl);
236: CachedResource cr = (CachedResource) precache
237: .get(requrl);
238: return cr.perform(request);
239: }
240: uritable.put(requrl, requrl);
241: }
242: return null;
243: }
244:
245: /**
246: * Almost identical to CacheFilter.initialize, but creates a
247: * PushCacheStore instead of a CacheStore and additionaly
248: * starts the PushCacheListener
249: */
250: public void initialize(HttpManager manager)
251: throws PropRequestFilterException {
252: try {
253: String validator_c;
254: String sweeper_c;
255: String serializer_c;
256: props = manager.getProperties();
257:
258: shared = props.getBoolean(SHARED_P, false);
259: connected = props.getBoolean(CACHE_CONNECTED_P, true);
260: debug = props.getBoolean(DEBUG_P, false);
261:
262: // now create the add-on classes
263: validator_c = props
264: .getString(VALIDATOR_P,
265: "org.w3c.www.protocol.http.cache.SimpleCacheValidator");
266: sweeper_c = props
267: .getString(SWEEPER_P,
268: "org.w3c.www.protocol.http.cache.SimpleCacheSweeper");
269: serializer_c = props
270: .getString(SERIALIZER_P,
271: "org.w3c.www.protocol.http.cache.SimpleCacheSerializer");
272: try {
273: Class c;
274: c = Class.forName(validator_c);
275: validator = (CacheValidator) c.newInstance();
276: validator.initialize(this );
277: c = Class.forName(sweeper_c);
278: sweeper = (CacheSweeper) c.newInstance();
279: sweeper.initialize(this );
280: c = Class.forName(serializer_c);
281: serializer = (CacheSerializer) c.newInstance();
282: } catch (Exception ex) {
283: // a fatal error! The cache won't be loaded...
284: ex.printStackTrace();
285: throw new PropRequestFilterException(
286: "Unable to start cache");
287: }
288: // now create the store as we have the basic things here
289: store = new PushCacheStore();
290: try {
291: store.initialize(this );
292: } catch (InvalidCacheException ex) {
293: // hum no worky, should do some action there!
294: if (debug) {
295: ex.printStackTrace();
296: }
297: }
298: // now start the sweeper
299: sweeper.start();
300: // Start the ActiveStream handler:
301: ActiveStream.initialize();
302: // Register for property changes:
303: props.registerObserver(this );
304: // Now, we are ready, register that filter:
305: manager.setFilter(this );
306:
307: //
308: // Create and start a PushCacheListener
309: //
310: int portNum = props
311: .getInteger(PORT_NUM_P, DEFAULT_PORT_NUM);
312: PushCacheListener listener = new PushCacheListener(portNum);
313: listener.start();
314:
315: //
316: // Register this filter with the PushCacheManager
317: //
318: PushCacheManager.instance().registerFilter(this );
319: } catch (Exception e) {
320: e.printStackTrace();
321: }
322: }
323: }
|