KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > wings > externalizer > AbstractExternalizeManager


1 /*
2  * $Id: AbstractExternalizeManager.java,v 1.6 2005/05/09 18:56:26 neurolabs Exp $
3  * Copyright 2000,2005 wingS development team.
4  *
5  * This file is part of wingS (http://www.j-wings.org).
6  *
7  * wingS is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1
10  * of the License, or (at your option) any later version.
11  *
12  * Please see COPYING for the complete licence.
13  */

14 package org.wings.externalizer;
15
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18 import org.wings.io.Device;
19 import org.wings.util.StringUtil;
20
21 import javax.servlet.http.HttpServletResponse JavaDoc;
22 import java.io.IOException JavaDoc;
23 import java.util.*;
24
25 /**
26  * @author <a HREF="mailto:haaf@mercatis.de">Armin Haaf</a>
27  * @version $Revision: 1.6 $
28  */

29 public abstract class AbstractExternalizeManager {
30     protected final transient static Log log = LogFactory.getLog(AbstractExternalizeManager.class);
31
32     /**
33      * The identifier generated, if the {@link ExternalizeManager} did not find
34      * an apropriate {@link Externalizer}.
35      */

36     public static final String JavaDoc NOT_FOUND_IDENTIFIER = "0";
37
38
39     /*---------------------------------------------------------------
40      * The externalized ID is just a counter start starts with zero. This
41      * happens with each start of the server, and thus generates the same
42      * ID if we restart the application (especially, if we are in the
43      * development phase). Since we externalize the resource with a long
44      * caching timeout, the browser might not refetch a resource externalized
45      * in a fresh instance of the web-application, since the browser has cached
46      * it already.
47      * Thus, we need a unique prefix for each externalized resource, that
48      * changes with each start of the server.
49      * These static variables create a new ID every UNIQUE_TIMESLICE, which
50      * means, that, if we use a 2-character prefix, can offer the browser
51      * the timeframe of FINAL_EXPIRES for this resource to be cached (since
52      * after that time, we have an roll-over of the ID's).
53      *----------------------------------------------------------------*/

54
55     /**
56      * in seconds
57      */

58     public final int UNIQUE_TIMESLICE = 20;
59
60     /**
61      * in seconds; Computed from UNIQUE_TIMESLICE; do not change.
62      */

63     public final long FINAL_EXPIRES =
64             (StringUtil.MAX_RADIX * StringUtil.MAX_RADIX - 1) * UNIQUE_TIMESLICE;
65
66     /**
67      * Prefix for the externalized ID; long. Computed, do not change.
68      */

69     protected final long PREFIX_TIMESLICE =
70             ((System.currentTimeMillis() / 1000) % FINAL_EXPIRES) / UNIQUE_TIMESLICE;
71
72     /**
73      * String prefix for externalized ID as String. Computed, do not change.
74      */

75     protected final String JavaDoc PREFIX_TIMESLICE_STRING =
76             StringUtil.toShortestAlphaNumericString(PREFIX_TIMESLICE, 2);
77
78     // Flags
79

80     /**
81      * for an externalized object with the final flag on the expired date
82      * header is set to a big value. If the final flag is off, the browser
83      * or proxy does not cache the object.
84      */

85     public static final int FINAL = 8;
86
87     /**
88      * for an externalized object with the request flag on, the externalized
89      * object is removed from the {@link ExternalizeManager} after one request
90      * of the object.
91      */

92     public static final int REQUEST = 1;
93
94     /**
95      * for an externalized object with the session flag on, the externalized
96      * object only available to requests within the session which created the
97      * object. The object is not accessible anymore after the session is
98      * destroyed (it is garbage collected after the session is garbage
99      * collected)
100      */

101     public static final int SESSION = 2;
102
103     /**
104      * for an externalized object with the gobal flag on, the externalized
105      * object is available to all requests. Also it is never garbage collected
106      * and available for the lifecycle of the servlet container.
107      */

108     public static final int GLOBAL = 4;
109
110     /**
111      * To generate the identifier for a externalized object.
112      */

113     private long counter = 0;
114
115     /**
116      * To search for an already externalized object. This performs way better
117      * than search in the value set of the
118      * identifier-{@link ExternalizedResource} map.
119      */

120     protected final Map/*<ExternalizedResource,String>*/ reverseExternalized;
121
122     /**
123      * To support Session local externalizing, the {@link ExternalizeManager}
124      * needs to encode the session identifier of the servlet container in the
125      * URL of the externalized object. This is set in the constructor
126      * and should work (I hope so) with all servlet containers.
127      */

128     protected String JavaDoc sessionEncoding = "";
129
130
131     public AbstractExternalizeManager() {
132         log.info("final scope expires in " + FINAL_EXPIRES + " seconds");
133         log.info("use prefix " + PREFIX_TIMESLICE_STRING);
134
135         reverseExternalized = Collections.synchronizedMap(new HashMap());
136     }
137
138     public void setResponse(HttpServletResponse JavaDoc response) {
139         if (response != null) {
140             sessionEncoding = response.encodeURL("foo").substring(3);
141         }
142     }
143
144
145     protected final synchronized long getNextIdentifier() {
146         return ++counter;
147     }
148
149
150     protected String JavaDoc getPrefix() {
151         return PREFIX_TIMESLICE_STRING;
152     }
153
154
155     protected final String JavaDoc createIdentifier() {
156         return getPrefix() + StringUtil.toShortestAlphaNumericString(getNextIdentifier());
157     }
158
159     /**
160      * store the {@link ExternalizedResource} in a map.
161      * The {@link ExternalizedResource} should later on accessible by the
162      * identifier {@link #getExternalizedResource}, {@link #removeExternalizedResource}
163      */

164     protected abstract void storeExternalizedResource(String JavaDoc identifier,
165                                                       ExternalizedResource extInfo);
166
167     /**
168      * get the {@link ExternalizedResource} by identifier.
169      *
170      * @return null, if not found!!
171      */

172     public abstract ExternalizedResource getExternalizedResource(String JavaDoc identifier);
173
174     /**
175      * removes the {@link ExternalizedResource} by identifier.
176      */

177     public abstract void removeExternalizedResource(String JavaDoc identifier);
178
179     /**
180      * externalizes (make a java object available for a browser) an object with
181      * the given {@link Externalizer}. The object is externalized in the
182      * {@link #SESSION} scope.
183      *
184      * @return a URL for accessing the object relative to the base URL.
185      */

186     public String JavaDoc externalize(Object JavaDoc obj, Externalizer externalizer) {
187         return externalize(obj, externalizer, SESSION);
188     }
189
190     /**
191      * externalizes (make a java object available for a browser) an object with
192      * the given {@link Externalizer}. If the given headers are !=null the
193      * headers overwrite the headers from the {@link Externalizer}.
194      * The object is externalized in the
195      * {@link #SESSION} scope.
196      *
197      * @return a URL for accessing the object relative to the base URL.
198      */

199     public String JavaDoc externalize(Object JavaDoc obj, Externalizer externalizer, Collection headers) {
200         return externalize(obj, externalizer, headers, SESSION);
201     }
202
203     /**
204      * externalizes (make a java object available for a browser) an object with
205      * the given {@link Externalizer}. Valid flags are (this may change, look
206      * also in the static variable section)
207      * <ul>
208      * <li>{@link #FINAL}</li>
209      * <li>{@link #REQUEST}</li>
210      * <li>{@link #SESSION}</li>
211      * <li>{@link #GLOBAL}</li>
212      * </ul>
213      *
214      * @return a URL for accessing the object relative to the base URL.
215      */

216     public String JavaDoc externalize(Object JavaDoc obj, Externalizer externalizer, int flags) {
217         if (obj == null || externalizer == null)
218             throw new IllegalStateException JavaDoc("no externalizer");
219
220         return externalize(obj, externalizer, externalizer.getMimeType(obj),
221                 null, flags);
222     }
223
224     /**
225      * externalizes (make a java object available for a browser) an object with
226      * the given {@link Externalizer}. If the given headers are !=null the
227      * headers overwrite the headers from the {@link Externalizer}.
228      * Valid flags are (this may change, look
229      * also in the static variable section)
230      * <ul>
231      * <li>{@link #FINAL}</li>
232      * <li>{@link #REQUEST}</li>
233      * <li>{@link #SESSION}</li>
234      * <li>{@link #GLOBAL}</li>
235      * </ul>
236      *
237      * @return a URL for accessing the object relative to the base URL.
238      */

239     public String JavaDoc externalize(Object JavaDoc obj, Externalizer externalizer,
240                               Collection headers, int flags) {
241         if (obj == null || externalizer == null)
242             throw new IllegalStateException JavaDoc("no externalizer");
243
244         return externalize(obj, externalizer, externalizer.getMimeType(obj),
245                 headers, flags);
246     }
247
248     /**
249      * externalizes (make a java object available for a browser) an object with
250      * the given {@link Externalizer}.
251      * If the mimeType!=null, mimeType overwrites the mimeType of the
252      * {@link Externalizer}.
253      * The object is externalized in the
254      * {@link #SESSION} scope.
255      *
256      * @return a URL for accessing the object relative to the base URL.
257      */

258     public String JavaDoc externalize(Object JavaDoc obj, Externalizer externalizer,
259                               String JavaDoc mimeType) {
260         return externalize(obj, externalizer, mimeType, null, SESSION);
261     }
262
263     /**
264      * externalizes (make a java object available for a browser) an object with
265      * the given {@link Externalizer}.
266      * If the mimeType!=null, mimeType overwrites the mimeType of the
267      * {@link Externalizer}.
268      * If the given headers are !=null the
269      * headers overwrite the headers from the {@link Externalizer}.
270      *
271      * @return a URL for accessing the object relative to the base URL.
272      */

273     public String JavaDoc externalize(Object JavaDoc obj, Externalizer externalizer,
274                               String JavaDoc mimeType, Collection headers) {
275         return externalize(obj, externalizer, mimeType, headers, SESSION);
276     }
277
278     /**
279      * externalizes (make a java object available for a browser) an object with
280      * the given {@link Externalizer}.
281      * If the mimeType!=null, mimeType overwrites the mimeType of the
282      * {@link Externalizer}.
283      * If the given headers are !=null the
284      * headers overwrite the headers from the {@link Externalizer}.
285      * Valid flags are (this may change, look
286      * also in the static variable section)
287      * <ul>
288      * <li>{@link #FINAL}</li>
289      * <li>{@link #REQUEST}</li>
290      * <li>{@link #SESSION}</li>
291      * <li>{@link #GLOBAL}</li>
292      * </ul>
293      *
294      * @return a URL for accessing the object relative to the base URL.
295      */

296     public String JavaDoc externalize(Object JavaDoc obj, Externalizer externalizer,
297                               String JavaDoc mimeType, Collection headers, int flags) {
298         if (externalizer == null) {
299             throw new IllegalStateException JavaDoc("no externalizer");
300         }
301         ExternalizedResource extInfo = new ExternalizedResource(obj, externalizer,
302                 mimeType, headers, flags);
303
304         if ((flags & GLOBAL) > 0) {
305             // session encoding is not necessary here
306
return SystemExternalizeManager.getSharedInstance().externalize(extInfo);
307         } else {
308             return externalize(extInfo);
309         }
310     }
311
312
313     /**
314      * externalizes (make a java object available for a browser) the object in
315      * extInfo.
316      *
317      * @return a URL for accessing the externalized object relative to the base URL.
318      */

319     public String JavaDoc externalize(ExternalizedResource extInfo) {
320         String JavaDoc identifier = (String JavaDoc) reverseExternalized.get(extInfo);
321
322         if (identifier == null) {
323             identifier = createIdentifier();
324             extInfo.setId(identifier);
325
326             storeExternalizedResource(identifier, extInfo);
327             reverseExternalized.put(extInfo, identifier);
328         }
329         String JavaDoc extension = extInfo.getExtension();
330         if (extension != null) {
331             identifier += ("." + extension);
332         }
333
334         return identifier + sessionEncoding;
335     }
336
337     /**
338      * externalizes (make a java object available for a browser) the object in
339      * extInfo.
340      *
341      * @return a URL for accessing the externalized object relative to the base URL.
342      */

343     public String JavaDoc getId(String JavaDoc url) {
344         if (url == null || url.length() == 0) {
345             return url;
346         }
347         String JavaDoc result;
348         if (url.charAt(0) == '-') {
349             result = url;
350         } else {
351             result = url.substring(0, url.length() - sessionEncoding.length());
352         }
353         return result;
354     }
355
356
357     /**
358      * delivers a externalized object identfied with the given identifier to a
359      * client.
360      * It sends an error (404), if the identifier is not registered.
361      */

362     public void deliver(String JavaDoc identifier, HttpServletResponse JavaDoc response,
363                         Device out) throws IOException JavaDoc {
364         ExternalizedResource extInfo = getExternalizedResource(identifier);
365
366         if (extInfo == null) {
367             log.warn("identifier " + identifier + " not found");
368             response.sendError(HttpServletResponse.SC_NOT_FOUND);
369             return;
370         }
371         deliver(extInfo, response, out);
372     }
373
374     public void deliver(ExternalizedResource extInfo, HttpServletResponse JavaDoc response,
375                         Device out)
376             throws IOException JavaDoc {
377         /* FIXME: re-implement.
378         if ( extInfo.deliverOnce() ) {
379             removeExternalizedResource(identifier);
380         }
381         */

382
383         if (extInfo.getMimeType() != null) {
384             response.setContentType(extInfo.getMimeType());
385         }
386
387         // FIXME find out, if this is correct: if the content length
388
// is not size preserving (like a gzip-device), then we must not
389
// send the content size we know..
390
if (out.isSizePreserving()) {
391             int resourceLen = extInfo
392                     .getExternalizer().getLength(extInfo.getObject());
393             if (resourceLen > 0) {
394                 log.debug(extInfo.getMimeType() + ": " + resourceLen);
395                 response.setContentLength(resourceLen);
396             }
397         }
398
399         Collection headers = extInfo.getHeaders();
400         if (headers != null) {
401             for (Iterator it = headers.iterator(); it.hasNext();) {
402                 Map.Entry entry = (Map.Entry) it.next();
403                 if (entry.getValue() instanceof String JavaDoc) {
404                     response.addHeader((String JavaDoc) entry.getKey(),
405                             (String JavaDoc) entry.getValue());
406                 } else if (entry.getValue() instanceof Date) {
407                     response.addDateHeader((String JavaDoc) entry.getKey(),
408                             ((Date) entry.getValue()).getTime());
409
410                 } else if (entry.getValue() instanceof Integer JavaDoc) {
411                     response.addIntHeader((String JavaDoc) entry.getKey(),
412                             ((Integer JavaDoc) entry.getValue()).intValue());
413
414                 } // end of if ()
415
}
416         }
417
418         if (!response.containsHeader("Expires")) {
419             /*
420             * This would be the correct way to do it; alas, that means, that
421             * for static resources, after a day or so, no caching could take
422             * place, since the last modification was at the first time, the
423             * resource was externalized (since it doesn't change).
424             * .. have to think about it.
425             */

426             //response.setDateHeader("Expires",
427
// (1000*FINAL_EXPIRES)
428
// + extInfo.getLastModified());
429
// .. so do this for now, which is the best approximation of what
430
// we want.
431
response.setDateHeader("Expires",
432                     System.currentTimeMillis()
433                     + (1000 * FINAL_EXPIRES));
434         }
435
436         extInfo.getExternalizer().write(extInfo.getObject(), out);
437     }
438
439     public void clear() {
440         reverseExternalized.clear();
441     }
442 }
443
444
445
Popular Tags