Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
at.spardat.xma.boot.cache.FileCache Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* s IT Solutions AT Spardat GmbH - initial API and implementation
*******************************************************************************/
/*
* Created on : 04.2003
* Created by : s3595
*/
package at.spardat.xma.boot.cache;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import at.spardat.xma.boot.BootRuntime;
import at.spardat.xma.boot.Statics;
import at.spardat.xma.boot.comp.data.XMAResource;
import at.spardat.xma.boot.component.IRtXMASessionClient;
import at.spardat.xma.boot.logger.LogLevel;
import at.spardat.xma.boot.logger.Logger;
import at.spardat.xma.boot.transform.HashChecksum;
import at.spardat.xma.boot.transport.CommunicationException;
import at.spardat.xma.boot.transport.Result;
import at.spardat.xma.boot.transport.Transport;
import at.spardat.xma.boot.transport.XMA_URI;
import at.spardat.xma.boot.util.Util;
import at.spardat.xma.xdelta.JarPatcher;
/**
* file cache for resources
*
* @author s3595 Chris Sch?fer (CGS)
* @version $Id: FileCache.java 2084 2007-11-27 14:53:31Z s3460 $
*/
public class FileCache implements IFileCache {
/** this is a singleton */
private static FileCache instance_;
/** physical file storage */
private FileCacheStore store_;
/** logger */
private Logger log_;
/**
* debug file output
*/
private Boolean debug;
/**
* get the FileCache
*
* @return FileCache Singleton Instance
* @throws IllegalStateException if not initialized
*/
public static IFileCache getInstance(){
if(instance_ == null)
throw new IllegalStateException( "FileCache not initialized"); //$NON-NLS-1$
else
return instance_;
}
/**
* initialize only once before usage
*
* @param brt the BootRuntime to get the configuration from
* @return the new and initialized FileCache
* @throws IllegalArgumentException if file does not exist.
* @throws IOException containing the filename
*/
public static synchronized FileCache initialize( BootRuntime brt ) throws IOException {
if(instance_ == null ) {
instance_ = new FileCache( brt );
}
return instance_ ;
}
/**
* Constructor of the Filecache
* @param brt the BootRuntime to get the configuration from
* @throws IOException containing the filename
*/
private FileCache( BootRuntime brt ) throws IOException {
log_ = Logger.getLogger( "boot.cache" ); //$NON-NLS-1$
debug = brt.getDebug();
store_ = new FileCacheStore(brt);
}
/*
* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#openResource(java.net.URL)
*/
public IFileCacheResource openResource( URL urlRemote ) throws IOException {
// the default is: load resource-file into memory buffer, and do not force a server update check.
return openResource(urlRemote, true, false, null );
}
/*
* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#openResource(at.spardat.xma.boot.component.IRtXMASessionClient, java.net.URL)
*/
public IFileCacheResource openResource(IRtXMASessionClient session, URL urlRemote) throws IOException {
return openResource(urlRemote, true, false, session );
}
/*
* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#openResource(java.net.URL, boolean)
*/
public IFileCacheResource openResource( URL urlRemote, boolean bmode ) throws IOException {
// the default is: load resource-file into memory buffer, and do not force a server update check.
return openResource(urlRemote, bmode, false, null );
}
/*
* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#openResource(java.net.URL, boolean, boolean)
*/
public IFileCacheResource openResource( URL urlRemote, boolean bmode, boolean bforce ) throws IOException {
return openResource(urlRemote, bmode, bforce, null );
}
/*
* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#openResource(java.net.URL, boolean, boolean,at.spardat.xma.boot.component.IRtXMASessionClient)
*/
public IFileCacheResource openResource( URL urlRemote,
boolean bmode, boolean bforce,
IRtXMASessionClient session ) throws IOException {
/* locals ---------------------------------- */
File file = null;
Result result = null;
IFileCacheResource temp = null;
/* locals ---------------------------------- */
if ( urlRemote == null || !isCacheable(urlRemote)) {
throw new IllegalArgumentException( "resource is not cacheable:" + urlRemote); //$NON-NLS-1$
}
file = getFile(urlRemote);
temp = store_.getResource(file,urlRemote);
if(temp!=null && !temp.isExpired() && !bforce) {
try {
if(session!=null && temp.getProperty(Statics.TRANSFORM_HEADER)!=null) {
((FCResource)temp).buffer_ = session.inverseTransform(temp.getProperty(Statics.TRANSFORM_HEADER),((FCResource)temp).getBuffer());
}
return temp; // found in cache, resource is not expired and an update force was not requested
} catch (Exception ex) { // TODO genaue hash-violation-exception erkennen!
temp=null; // cached version corrupted -> unconditional get needed
log_.log(LogLevel.WARNING,"data integrity violated: "+file.getAbsolutePath(),ex);
}
}
// not cached, download it
result = loadResource(urlRemote,
temp!=null ? temp.getLastModified():0L,
temp!=null ? temp.getProperty(Statics.strEtag):null,
session );
// and save it
temp = store_.storeResource(file,result,urlRemote,temp,bmode,bforce);
if(session!=null && temp.getProperty(Statics.TRANSFORM_HEADER)!=null) {
((FCResource)temp).buffer_ = session.inverseTransform(temp.getProperty(Statics.TRANSFORM_HEADER),((FCResource)temp).getBuffer());
}
return temp;
}
/* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#openResource(at.spardat.xma.boot.transport.XMA_URI, at.spardat.xma.boot.comp.data.XMAResource, at.spardat.xma.boot.cache.VersionNumber, boolean)
*/
public IFileCacheResource openResource(XMA_URI appUri, XMAResource resource, VersionNumber serverVers, boolean bforce) throws IOException {
String hash = resource.getVersion_().getVersion();
File file;
URL urlRemote = appUri.getHTTP_AppUri( resource.getHref_() );
if ( urlRemote == null || !isCacheable(urlRemote)) {
throw new IllegalArgumentException( "resource is not cacheable:" + urlRemote); //$NON-NLS-1$
}
if(resource.isShared_()) {
String filename = resource.getHref_();
int last = filename.lastIndexOf('/');
if(last>=0) filename = filename.substring(last);
file = new File(store_.getBaseDir(),Statics.SHARED_DIRNAME + File.separator + VersionNumber.insertHash(filename,hash));
} else {
file = getFile(urlRemote);
}
FCResource temp = store_.getResource(file,urlRemote);
if(temp!=null) {
String currentHash = temp.getProperty(Statics.XMA_DIGEST);
if(hash.equals(currentHash)) {
if(!temp.isExpired() && !bforce) {
return temp; // found in cache, resource is not expired and an update force was not requested
}
} else {
temp=null; // wrong version cached -> unconditional get needed
}
}
// not cached, download it
URL deltaURL = urlRemote;
FCResource latestVersion=null;
if(!bforce) {
latestVersion = store_.findPreviousVersion(file);
if(latestVersion!=null) {
deltaURL = latestVersion.versionNumber.insertAsDelta(urlRemote);
log_.log(LogLevel.INFO,"performing delta download for "+deltaURL.toString());
}
}
URL downloadURL = deltaURL;
try {
if(serverVers!=null && serverVers.compareTo(new VersionNumber(1,6,0))>=0) {
downloadURL = new URL(VersionNumber.insertHash(downloadURL.toExternalForm(),hash));
} else {
if(urlRemote.getQuery()==null) downloadURL = new URL(downloadURL+"?hash="+hash);
else downloadURL = new URL(downloadURL+"&hash="+hash);
}
} catch (MalformedURLException mue) {
MalformedURLException ne = new MalformedURLException("error constructing download URL "+downloadURL.toString());
ne.initCause(mue);
throw ne;
}
Result result;
try {
result = loadResource(downloadURL,
temp!=null ? temp.getLastModified():0L,
temp!=null ? temp.getProperty(Statics.strEtag):null,
null );
} catch (CommunicationException exc) {
if(latestVersion!=null) { // if a delta was not found no the server -> full download
if(exc.getCause() instanceof FileNotFoundException || exc.getReturnCode() == 404) {
log_.log(LogLevel.WARNING,"error downloading "+downloadURL.toString(),exc);
log_.log(LogLevel.WARNING,"performing full download");
return openResource(appUri,resource,serverVers,true);
}
}
throw exc;
}
// apply delta
if(latestVersion!=null) {
try {
File deltaFile = getFile(deltaURL);
try {
deltaFile.getParentFile().mkdirs();
deltaFile.createNewFile();
FileOutputStream deltaOut = new FileOutputStream(deltaFile);
deltaOut.write(result.getBuffer());
deltaOut.close();
} catch (IOException exc) {
IOException ne = new IOException("error storing delta file "+deltaFile.getAbsolutePath());
ne.initCause(exc);
throw ne;
}
ByteArrayOutputStream patched = new ByteArrayOutputStream();
new JarPatcher().applyDelta(new ZipFile(latestVersion.file_),new ZipFile(deltaFile),new ZipOutputStream(patched));
temp = store_.storeResource(file,result,patched.toByteArray(),urlRemote,false);
if(!resource.isJar()) { temp.setProperty(Statics.XMA_DIGEST,hash); }
deltaFile.delete();
} catch(Exception ex) { // if the delta made troubles -> full download
log_.log(LogLevel.WARNING,"error applying delta file "+ex);
return openResource(appUri,resource,serverVers,true);
}
} else {
// and save it
temp = store_.storeResource(file,result,urlRemote,temp,false,bforce);
if(!resource.isJar()) { temp.setProperty(Statics.XMA_DIGEST,hash); }
}
return temp;
}
/**
* Check the integrity of the given resource.
* The given hash value is compared to the stored hash value of the resource. Additionally
* the hash value is calculated from the resource and compared. This does not only detect
* a wrong version of the resource. It detects corrupted and manipulated resources, too.
* @param resource to check
* @param hash expected hash value
* @param isJar if the resource is a jar file or not
* @return true if the hash matches, false otherwise
* @since 1.3.0
* @author s2877
*/
public boolean checkHash(IFileCacheResource resource, String hash, boolean isJar) {
try {
if(hash==null) return false;
if(!hash.equals(resource.getProperty(Statics.XMA_DIGEST))) return false;
if(isJar) return hash.equals(HashChecksum.calcJarCheckSum(((FCResource)resource).file_));
else return hash.equals(HashChecksum.calcCheckSum(((FCResource)resource).getBuffer()));
} catch (Exception exc) {
// if I can't read it, it's corrupted
log_.log(LogLevel.WARNING,"file "+((FCResource)resource).file_.getAbsolutePath()+" corrupted",exc);
return false;
// } catch (Throwable exc) { // java.util.ZipFile wirft auch VirtualMachineErrors !!
// // if I can't read it, it's corrupted
// log_.log(LogLevel.WARNING,"file "+((FCResource)resource).file_.getAbsolutePath()+" corrupted",exc);
// return false;
}
}
/**
* Open a resource from the local cache. No effort is made to download it, if it is not
* cached null
is returned. The resource is not checked for expiration or integrity.
* This method does not find shared resources.
* @param resource to open
* @return the resource from the local cache or null
if not cached.
* @throws IOException containing the filename
* @since 1.3.0
* @author s2877
*/
public IFileCacheResource openLocalResource(XMA_URI resource) {
URL urlRemote = resource.getHTTP_URI();
File file = getFile(urlRemote);
IFileCacheResource localResource = store_.getResource(file,urlRemote);
return localResource;
}
/* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#openResource(at.spardat.xma.boot.transport.XMA_URI, java.lang.String, at.spardat.xma.boot.cache.VersionNumber, boolean)
*/
public IFileCacheResource openResource(XMA_URI resource,String hash,VersionNumber serverVers,boolean bforce) throws IOException {
URL urlRemote = resource.getHTTP_URI();
if ( urlRemote == null || !isCacheable(urlRemote)) {
throw new IllegalArgumentException( "resource is not cacheable:" + urlRemote); //$NON-NLS-1$
}
File file = getFile(urlRemote);
IFileCacheResource temp = store_.getResource(file,urlRemote);
if(temp!=null) {
String currentHash = temp.getProperty(Statics.XMA_DIGEST);
if(hash.equals(currentHash)) {
if(!temp.isExpired() && !bforce) {
return temp; // found in cache, resource is not expired and an update force was not requested
}
} else {
temp=null; // wrong version cached -> unconditional get needed
}
}
// not cached, download it
URL downloadURL = urlRemote;
if(hash!=null) {
try {
if(serverVers!=null && serverVers.compareTo(new VersionNumber(1,6,0))>=0) {
downloadURL = new URL(VersionNumber.insertHash(downloadURL.toExternalForm(),hash));
} else {
if(urlRemote.getQuery()==null) downloadURL = new URL(downloadURL+"?hash="+hash);
else downloadURL = new URL(downloadURL+"&hash="+hash);
}
} catch (MalformedURLException mue) {
MalformedURLException ne = new MalformedURLException("error constructing download URL "+downloadURL.toString());
ne.initCause(mue);
throw ne;
}
}
Result result = loadResource(downloadURL,
temp!=null ? temp.getLastModified():0L,
temp!=null ? temp.getProperty(Statics.strEtag):null,
null );
// and save it
temp = store_.storeResource(file,result,urlRemote,temp,false,bforce);
temp.setProperty(Statics.XMA_DIGEST,hash);
return temp;
}
/**
* load resource from server
*
* @param urlRemote url to load
* @param lastModified info for last modified
* @return Result result got from transport
* @throws CommunicationException by transport
*/
private Result loadResource(URL urlRemote, long lastModified, String etag, IRtXMASessionClient session ) throws MalformedURLException, CommunicationException {
/* locals ---------------------------------- */
Result result = null;
XMA_URI resource = new XMA_URI(urlRemote);
Transport transport = (Transport)Transport.getTransport();
/* end locals ------------------------------ */
result = transport.getResource(session, resource, lastModified, etag);
if( result == null ) {
throw new IllegalStateException( "Transport result object must not be null"); //$NON-NLS-1$
}
return result;
} // loadResource
/*
* (non-Javadoc)
* @see at.spardat.xma.cdb.cache.IFileCache#invalidateResource(java.net.URL)
*/
public void invalidateResource( URL urlRemote ) {
File file = this.getFile(urlRemote);
try {
store_.removeResource(file);
} catch (IOException exc) { // just log and continue
log_.log(LogLevel.WARNING,"error removing "+file.getAbsolutePath(),exc);
}
}
/*
* (non-Javadoc)
* @see at.spardat.xma.cdb.cache.IFileCache#invalidateResource(at.spardat.xma.cdb.cache.IFileCacheResource)
*/
public synchronized void invalidateResource( IFileCacheResource ifcr ) {
try {
store_.removeResource((FCResource)ifcr);
} catch (IOException exc) { // just log and continue
log_.log(LogLevel.WARNING,"error removing "+((FCResource)ifcr).file_.getAbsolutePath(),exc);
}
}
/**
* Returns whether the resource can be cached.
*
* @param source url to check
* @return true if we can cache this protocoll
*/
public static boolean isCacheable(URL source) {
if (source == null)
return false;
if (source.getProtocol().equals( Statics.PROTO_HTTP ))
return true;
if (source.getProtocol().equals( Statics.PROTO_HTTPS))
return true;
return false;
}
/**
* check?s if there is already a local copy of this remote URL
*
* @param urlRemote remote URL to check for a local cached copy
* @return returns true if the resource is in the cache
* @throws IllegalArgumentException if the source is not cacheable
* @see java.net.URL
*/
public boolean isCached(URL urlRemote) {
boolean isCached = false;
FCResource fcr = null;
try {
File file = this.getFile(urlRemote);
fcr = new FCResource(urlRemote,file, false,debug.booleanValue());
if(fcr != null) {
isCached = true;
}
} catch(IOException ex ) {
log_.log(LogLevel.WARNING,"error checking "+urlRemote.toString(),ex);
isCached = false;
}
return isCached;
}
/**
* map?s the remote-url to a file location in the chache directoy.
*
* @param urlRemote url that should be mapped to a filename
* @return File the file representation of this url
*/
private File getFile(URL urlRemote) {
/* locals ---------------------------------- */
StringBuffer path;
File lFile;
/* locals ---------------------------------- */
path = Util.getInstance().urlToPath(urlRemote);
lFile = new File( store_.getBaseDir(), path.toString());
return lFile;
}
/* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#getBaseDir()
*/
public File getBaseDir() {
return store_.getBaseDir();
}
/* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#isCachedAndValid(at.spardat.xma.boot.transport.XMA_URI, java.lang.String)
*/
public boolean isCachedAndValid(XMA_URI uri,String hash) {
return isCachedAndValid(openLocalResource(uri),hash);
}
/* (non-Javadoc)
* @see at.spardat.xma.boot.cache.IFileCache#isCachedAndValid(at.spardat.xma.boot.cache.IFileCacheResource, java.lang.String)
*/
public boolean isCachedAndValid(IFileCacheResource resource,String hash) {
if(resource==null) return false;
if(resource.isExpired()) return false;
if(hash!=null) {
String storedHash = resource.getProperty(Statics.XMA_DIGEST);
return hash.equals(storedHash);
}
return true;
}
} // end FileCache