1/*2 * Copyright 2000-2004 The Apache Software Foundation.3 * 4 * Licensed under the Apache License, Version 2.0 (the "License");5 * you may not use this file except in compliance with the License.6 * You may obtain a copy of the License at7 * 8 * http://www.apache.org/licenses/LICENSE-2.09 * 10 * Unless required by applicable law or agreed to in writing, software11 * distributed under the License is distributed on an "AS IS" BASIS,12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13 * See the License for the specific language governing permissions and14 * limitations under the License.15 */1617packageorg.apache.jetspeed.cache.disk;
181920//jetspeed stuff21import org.apache.jetspeed.util.URIEncoder;
22import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
23import org.apache.jetspeed.services.logging.JetspeedLogger;
24import org.apache.jetspeed.services.urlmanager.URLFetcher;
25import org.apache.jetspeed.services.resources.JetspeedResources;
2627//standard java stuff28import java.io.*;
29import java.net.*;
3031/***32 *<p>A cache entry represents a data source that can be stored locally for33 *efficiency.34 *35 *<p>It can deliver a string with its contents, but the preferred 36 *way to access to the entry contents is through a Reader 37 *that will get characters from it.38 *39 *<p>There are two kinds of entries:40 *41 *<ul>42 * <li>Local: It is not cached.43 * <li>Remote: It can be cached.44 *</ul>45 *46 *<p>Remote entries can be in the following states:47 *48 *<ul>49 * <li>Invalid: It has no local reference, and the source is transiently or50 * permanently delivering errors51 * <li>Stale: It has no local reference, and it is quiet52 * <li>Loading: It has no local ref yet, and it is loading53 * <li>Refreshing: It has a local ref (current or expired),54 * and it is refreshing it55 * <li>Expired: It has a local ref, but its content is no longer valid56 * <li>Current: It has a valid local ref57 *</ul>58 *59 *<p>TODO: Some data sources need to be written (i. e., are writable). For those,60 * a mechanism need to be provided to write back the resource. We currently think61 * about HTTP PUT as a mechanism.62 *63 *@author <a href="mailto:burton@apache.org">Kevin A. Burton</a>64 *@author <a href="mailto:sgala@hisitech.com">Santiago Gala</a>65 *@version $Id: JetspeedDiskCacheEntry.java,v 1.33 2004/02/23 02:45:29 jford Exp $66 **/67publicclassJetspeedDiskCacheEntry implements DiskCacheEntry {
6869/***70 * <p>Expiration interval that will be used it the remote URL does not71 * specify one. The contract here is:72 * <ul>73 * <li>If we have no hits, we will hit our entry every time DiskCacheDaemon is run to revalidate.</li>74 * <li>No matter how many hits we get, we will reach our entry at most once per defaultExpirationInterval.</li>75 * </ul>76 */77privatestaticlong defaultExpirationInterval = 1000 *
78 JetspeedResources.getInt( JetspeedResources.DEFAULT_DCE_EXPIRATION_TIME_KEY, 15 * 60 );
7980//Used for Local URL writing81static String encoding = JetspeedResources.getString(
82 JetspeedResources.CONTENT_ENCODING_KEY, "iso-8859-1" );
8384private File file = null;
85private String url = null;
86private String sourceURL = null;
8788/***89 Date (ms since epoch) it was last Modified90 */91privatelong lastModified = 0;
92/***93 Date (ms since epoch) it expires94 */95privatelong expires = 0;
9697/***98 * Static initialization of the logger for this class99 */100privatestaticfinalJetspeedLogger logger = JetspeedLogFactoryService.getLogger(JetspeedDiskCacheEntry.class.getName());
101102/***103 *<p>Build a DiskCacheEntry that is based on a cached filesystem entry104 *105 *<p>This is used to reconstruct the entries from the cache at boot time106 *107 *108 */109protectedJetspeedDiskCacheEntry( File file ) {
110this.setFile( file );
111this.setURL( this.getSourceURL() );
112 }
113114/***115 *Build a DiskCacheEntry that is based on a remote URL and may be (or not)116 *backed on disk.117 *118 */119publicJetspeedDiskCacheEntry( String url ) {
120121this.setURL( url );
122this.init();
123 }
124125/***126 * Initialize the file variable, if it is null & the url127 * is not local128 *129 */130publicvoid init() {
131132 URL url = null;
133134// first build the URL object135try {
136 url = new URL(this.getURL());
137 } catch (MalformedURLException e) {
138 logger.error("Error in URL", e);
139return;
140 }
141142//if this is a file:/ based URL then build a file from it.143if ( (this.file == null || this.file.exists() == false )
144 && "file".equals( url.getProtocol() ) )
145 {
146try {
147 File newfile = new File( url.getFile() );
148149this.setFile( newfile );
150if( newfile.exists() == false ) {
151 JetspeedDiskCache.getInstance().add( this.getURL(), true );
152 }
153 } catch ( IOException e ) {
154 logger.error("Error building from file", e);
155return;
156 }
157158 }
159160 }
161162/***163 */164public String getURL() {
165returnthis.url;
166 }
167168/***169 Reconstruct the original URL based on this URL.170 */171public String getSourceURL() {
172//if getFile() is null then this isn't cached173if ( this.getFile() == null ) {
174returnthis.getURL();
175 } else {
176return URIEncoder.decode( this.getFile().getName() );
177 }
178 }
179180/***181 Get the File that this URL obtains within the cache.182 */183public File getFile() {
184returnthis.file;
185 }
186187/***188 Set the file.189 */190publicvoid setFile( File file ) {
191//now make sure it exists.192if ( file.exists() == false ) {
193 String message = "The following file does not exist: " + file.getAbsolutePath();
194 logger.error( message );
195try {
196 JetspeedDiskCache.getInstance().add( this.url, true );
197 } catch (Throwable e) {
198 logger.error("Error setting file", e );
199 }
200 }
201//file should exist after add in the Cache...202this.file = file;
203this.lastModified = file.lastModified();
204this.expires = System.currentTimeMillis() +
205 defaultExpirationInterval;
206 }
207208/***209 Open this URL and read its data, then return it as a string210 */211public String getData() throws IOException {
212213 Reader is = this.getReader();
214 StringWriter bos = new StringWriter();
215216//now process the Reader...217charchars[] = newchar[200];
218219int readCount = 0;
220while( ( readCount = is.read( chars )) > 0 ) {
221 bos.write(chars, 0, readCount);
222 }
223224 is.close();
225226return bos.toString();
227228 }
229230/***231 Get an input stream from this entry 232 */233public InputStream getInputStream() throws IOException {
234 logger.info( "CacheEntry getInputStream() called: " + this.getURL() );
235if(this.getFile() != null)
236 {
237returnnew FileInputStream( this.getFile() );
238 }
239240if(DiskCacheUtils.isLocal( this.getURL() ) )
241 {
242returnnew URL( this.getURL() ).openConnection().getInputStream();
243 }
244245this.lastModified = 0;
246this.expires = 0;
247 URLFetcher.refresh( this.getURL() );
248if(this.getFile() != null)
249returnnew FileInputStream( this.getFile() );
250thrownew IOException( this.getURL() +
251": is not in cache after forcing" );
252 }
253254/***255 Get a Reader from this entry.256 ( Patch for handling character encoding sent by 257 Yoshihiro KANNA <y-kanna@bl.jp.nec.com> )258 For local entries, we assume that the URL coming259 from the WEB server is allright WRT encoding260 For remote entries, we assume that the cache saved them in the local store261 using UTF8 encoding262 */263public Reader getReader() throws IOException {
264265if(DiskCacheUtils.isLocal( this.getURL() ) )
266 {
267 URLConnection conn = new URL( this.getURL() ).openConnection();
268// If the URL has a proper encoding, use it269 String encoding = conn.getContentEncoding();
270if(encoding == null) {
271// else take it from configuration272 encoding = JetspeedResources.getString( JetspeedResources.CONTENT_ENCODING_KEY,
273"iso-8859-1" );
274 }
275//Log.info("Disk Cache Entry: getReader URL -> " +276// this.getURL() +277// " encoding -> " + 278// encoding );279returnnew InputStreamReader(conn.getInputStream(),
280 encoding );
281 }
282283if(this.getFile() != null)
284 {
285 InputStreamReader reader = null;
286try {
287//For cache files, we are assuming UTF8288// instead of local encoding289 reader = new InputStreamReader( new FileInputStream( this.getFile() ), "UTF8" );
290 } catch (UnsupportedEncodingException e) {
291 logger.error("Encoding error", e);
292 reader = new FileReader( this.getFile() );
293 }
294//Log.info("Disk Cache Entry: getReader file -> " + 295// this.getURL() +296// " encoding -> " + 297// reader.getEncoding() );298return reader;
299 }
300301this.lastModified = 0;
302this.expires = 0;
303 URLFetcher.refresh( this.getURL() );
304// If it is in the cache, call recursively...305if(this.getFile() != null)
306returnthis.getReader();
307thrownew IOException( this.getURL() +
308": is not in cache after forcing" );
309310 }
311312/***313 Get a Writer to update this entry.314 For local entries, we assume that the URL coming315 from the WEB server allows PUT316 For remote entries, we throws a IOException317318 */319public Writer getWriter() throws IOException {
320321if( DiskCacheUtils.isRemote( this.getURL() ) ) {
322thrownew IOException("Cannot write to remote URLs!");
323 }
324325if(DiskCacheUtils.isLocal( this.getURL() ) )
326 {
327 URL url = new URL( this.getURL() );
328329if (url.getProtocol().equalsIgnoreCase("http"))
330 {
331 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
332 conn.setDoOutput(true);
333 conn.setRequestMethod("PUT");
334returnnew HttpURLWriter( conn );
335 }
336else337 {
338 File file = new File( url.getFile() );
339 file.getParentFile().mkdirs();
340returnnew FileURLWriter( file );
341 }
342343344 }
345346thrownew IOException( this.getURL() +
347": is not local or remote" );
348349 }
350351/***352 Return the last modified date of this entry.353 */354publiclong getLastModified() {
355if( isLocal() ) {
356try {
357 String localfile = this.getURL().substring(5); //remove "file:"358this.lastModified = new File( localfile ).lastModified();
359 } catch ( Exception e ) {
360if( logger.isDebugEnabled() ) {
361 logger.debug("Error getLastModified ", e);
362 }
363 e.printStackTrace();
364 }
365 }
366returnthis.lastModified;
367368 }
369370/***371 Set the last modified date of this entry.372 */373publicvoid setLastModified(long time) {
374this.lastModified = time;
375376 }
377378/***379 Set the url on which this is based.380 */381publicvoid setURL( String url ) {
382383if ( DiskCacheUtils.isVirtual( url ) ) {
384 url = DiskCacheUtils.getLocalURL( url );
385 }
386387this.url = url;
388 }
389390/***391 Set the expiration date of this entry.392 */393publiclong getExpirationTime() {
394returnthis.expires;
395 }
396397/***398 Set the expiration date of this entry.399 */400publicvoid setExpirationTime(long time) {
401this.expires = time;
402if(this.expires < System.currentTimeMillis())
403 {
404this.expires = System.currentTimeMillis() +
405 defaultExpirationInterval;
406 }
407408 }
409410/***411 */412publicboolean hasExpired() {
413returnthis.expires <= 0 ||
414this.expires < System.currentTimeMillis();
415 }
416417/***418 */419publicboolean isLocal() {
420421return DiskCacheUtils.isLocal(this.getSourceURL());
422 }
423424class HttpURLWriter extends OutputStreamWriter
425 {
426private HttpURLConnection conn;
427428public HttpURLWriter( HttpURLConnection conn )
429 throws UnsupportedEncodingException, IOException
430 {
431super( conn.getOutputStream(), encoding );
432this.conn = conn;
433 logger.info("HttpURLWriter encoding -> " +
434 encoding + " method -> " + this.conn.getRequestMethod() );
435 }
436437publicvoid close() throws IOException
438 {
439//We close the stream440super.close();
441//Required to get the real connection sending PUT data442this.conn.getResponseCode();
443 logger.info("HttpURLWriter close encoding -> " +
444 encoding + " method -> " + this.conn.getRequestMethod() +
445" Status -> " + this.conn.getResponseCode() );
446447 }
448 }
449450class FileURLWriter extends FileWriter
451 {
452private String filename;
453454public FileURLWriter( File file )
455 throws UnsupportedEncodingException, IOException
456 {
457super( file );
458this.filename = file.getPath();
459 logger.info("FileURLWriter opening file -> " + filename );
460 }
461462publicvoid close() throws IOException
463 {
464//We close the stream465super.close();
466 logger.info("FileURLWriter closing file -> " + filename );
467468 }
469 }
470 }