All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy