Home | History | Annotate | Download | only in dependency
      1 package org.robolectric.internal.dependency;
      2 
      3 import static java.nio.charset.StandardCharsets.UTF_8;
      4 
      5 import java.io.File;
      6 import java.io.FileInputStream;
      7 import java.io.FileOutputStream;
      8 import java.io.IOException;
      9 import java.io.ObjectInputStream;
     10 import java.io.ObjectOutputStream;
     11 import java.io.Serializable;
     12 import java.net.URL;
     13 import java.util.Date;
     14 import java.util.zip.CRC32;
     15 
     16 public class CachedDependencyResolver implements DependencyResolver {
     17   private final static String CACHE_PREFIX = "localArtifactUrl";
     18 
     19   private final DependencyResolver dependencyResolver;
     20   private final CacheNamingStrategy cacheNamingStrategy;
     21   private final CacheValidationStrategy cacheValidationStrategy;
     22   private final Cache cache;
     23 
     24   public CachedDependencyResolver(DependencyResolver dependencyResolver, File cacheDir, long cacheValidTime) {
     25     this(dependencyResolver, new FileCache(cacheDir, cacheValidTime), new DefaultCacheNamingStrategy(), new DefaultCacheValidationStrategy());
     26   }
     27 
     28   public CachedDependencyResolver(DependencyResolver dependencyResolver, Cache cache, CacheNamingStrategy cacheNamingStrategy, CacheValidationStrategy cacheValidationStrategy) {
     29     this.dependencyResolver = dependencyResolver;
     30     this.cache = cache;
     31     this.cacheNamingStrategy = cacheNamingStrategy;
     32     this.cacheValidationStrategy = cacheValidationStrategy;
     33   }
     34 
     35   @Override
     36   public URL getLocalArtifactUrl(DependencyJar dependency) {
     37     final String cacheName = cacheNamingStrategy.getName(CACHE_PREFIX, dependency);
     38     final URL urlFromCache = cache.load(cacheName, URL.class);
     39 
     40     if (urlFromCache != null && cacheValidationStrategy.isValid(urlFromCache)) {
     41       return urlFromCache;
     42     }
     43 
     44     final URL url = dependencyResolver.getLocalArtifactUrl(dependency);
     45     cache.write(cacheName, url);
     46     return url;
     47   }
     48 
     49   interface CacheNamingStrategy {
     50     String getName(String prefix, DependencyJar... dependencies);
     51   }
     52 
     53   interface CacheValidationStrategy {
     54     boolean isValid(URL url);
     55 
     56     boolean isValid(URL[] urls);
     57   }
     58 
     59   static class DefaultCacheValidationStrategy implements CacheValidationStrategy {
     60     @Override
     61     public boolean isValid(URL url) {
     62       return new File(url.getPath()).exists();
     63     }
     64 
     65     @Override
     66     public boolean isValid(URL[] urls) {
     67       for (URL url : urls) {
     68         if (!isValid(url)) {
     69           return false;
     70         }
     71       }
     72       return true;
     73     }
     74   }
     75 
     76   static class DefaultCacheNamingStrategy implements CacheNamingStrategy {
     77     @Override public String getName(String prefix, DependencyJar... dependencies) {
     78       StringBuilder sb = new StringBuilder();
     79 
     80       sb.append(prefix)
     81           .append("#");
     82 
     83       for (DependencyJar dependency : dependencies) {
     84         sb.append(dependency.getGroupId())
     85             .append(":")
     86             .append(dependency.getArtifactId())
     87             .append(":")
     88             .append(dependency.getVersion())
     89             .append(",");
     90       }
     91 
     92       CRC32 crc = new CRC32();
     93       crc.update(sb.toString().getBytes(UTF_8));
     94       return crc.getValue() + "";
     95     }
     96   }
     97 
     98   interface Cache {
     99     <T extends Serializable> T load(String id, Class<T> type);
    100     <T extends Serializable> boolean write(String id, T object);
    101   }
    102 
    103   static class FileCache implements Cache {
    104     private final File dir;
    105     private final long validTime;
    106 
    107     FileCache(File dir, long validTime) {
    108       this.dir = dir;
    109       this.validTime = validTime;
    110     }
    111 
    112     @Override
    113     public <T extends Serializable> T load(String id, Class<T> type) {
    114       try {
    115         File file = new File(dir, id);
    116         if (!file.exists() || (validTime > 0 && file.lastModified() < new Date().getTime() - validTime)) {
    117           return null;
    118         }
    119 
    120         try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) {
    121           Object o = in.readObject();
    122           return o.getClass() == type ? (T) o : null;
    123         }
    124       } catch (IOException | ClassNotFoundException e) {
    125         return null;
    126       }
    127     }
    128 
    129     @Override
    130     public <T extends Serializable> boolean write(String id, T object) {
    131       try {
    132         try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File(dir, id)))) {
    133           out.writeObject(object);
    134           return true;
    135         }
    136       } catch (IOException e) {
    137         return false;
    138       }
    139     }
    140   }
    141 }
    142