2016-01-18 33 views
11

Nella mia applicazione GWT mi riferisco spesso più volte agli stessi risultati del server. Inoltre non so quale codice venga eseguito per primo. Pertanto, desidero utilizzare la memorizzazione nella cache dei risultati asincroni (lato client).Come memorizzare nella cache i risultati del server in GWT con guava?

Desidero utilizzare una libreria di memorizzazione nella cache esistente; Sto considerando guava-gwt.

Ho trovato questo esempio di Guava sincrona cache (in guava's documentation):

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() 
     .build(
      new CacheLoader<Key, Graph>() { 
      public Graph load(Key key) throws AnyException { 
       return createExpensiveGraph(key); 
      } 
      }); 

Questo è il modo che sto cercando di utilizzare una cache Guava in modo asincrono (non ho idea di come fare questo lavoro):

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() 
     .build(
      new CacheLoader<Key, Graph>() { 
      public Graph load(Key key) throws AnyException { 

       // I want to do something asynchronous here, I cannot use Thread.sleep in the browser/JavaScript environment. 
       service.createExpensiveGraph(key, new AsyncCallback<Graph>() { 

       public void onFailure(Throwable caught) { 
        // how to tell the cache about the failure??? 
       } 

       public void onSuccess(Graph result) { 
        // how to fill the cache with that result??? 
       } 
       }); 

       return // I cannot provide any result yet. What can I return??? 
      } 
      }); 

GWT manca molte classi dal default JRE (soprattutto per quanto riguarda le discussioni e concurrancy).

Come posso utilizzare guava-gwt per memorizzare nella cache risultati asincroni?

risposta

5

Come ho capito che cosa si vuole raggiungere non è solo un cache asincrona ma anche una cache lazy e per crearne una GWT non è il posto migliore in quanto vi è un grosso problema quando si implementa un'app GWT con le esecuzioni asincrone lato client, poiché GWT non ha il client implementazioni laterali dei componenti Future e/o Rx (esistono ancora alcune implementazioni di RxJava per GWT). Così nel consueto java ciò che si vuole creare può essere realizzato da:

LoadingCache<String, Future<String>> graphs = CacheBuilder.newBuilder().build(new CacheLoader<String, Future<String>>() { 
    public Future<String> load(String key) { 
     ExecutorService service = Executors.newSingleThreadExecutor(); 
     return service.submit(()->service.createExpensiveGraph(key)); 
    } 
}); 
Future<String> value = graphs.get("Some Key"); 
if(value.isDone()){ 
    // This will block the execution until data is loaded 
    String success = value.get();   
} 

Ma, come GWT non ha implementazioni per Future s è necessario crearne uno proprio come

public class FutureResult<T> implements AsyncCallback<T> { 
    private enum State { 
     SUCCEEDED, FAILED, INCOMPLETE; 
    } 

    private State state = State.INCOMPLETE; 
    private LinkedHashSet<AsyncCallback<T>> listeners = new LinkedHashSet<AsyncCallback<T>>(); 
    private T value; 
    private Throwable error; 

    public T get() { 
     switch (state) { 
     case INCOMPLETE: 
      // Do not block browser so just throw ex 
      throw new IllegalStateException("The server response did not yet recieved."); 
     case FAILED: { 
      throw new IllegalStateException(error); 
     } 
     case SUCCEEDED: 
      return value; 
     } 
     throw new IllegalStateException("Something very unclear"); 
    } 

    public void addCallback(AsyncCallback<T> callback) { 
     if (callback == null) return; 
     listeners.add(callback); 
    } 

    public boolean isDone() { 
     return state == State.SUCCEEDED; 
    } 

    public void onFailure(Throwable caught) { 
     state = State.FAILED; 
     error = caught; 
     for (AsyncCallback<T> callback : listeners) { 
      callback.onFailure(caught); 
     } 
    } 

    public void onSuccess(T result) { 
     this.value = result; 
     state = State.SUCCEEDED; 
     for (AsyncCallback<T> callback : listeners) { 
      callback.onSuccess(value); 
     } 
    } 

} 

E l'implementazione sarà:

LoadingCache<String, FutureResult<String>> graphs = CacheBuilder.newBuilder().build(new CacheLoader<String, FutureResult<String>>() { 
     public FutureResult<String> load(String key) { 
      FutureResult<String> result = new FutureResult<String>(); 
      return service.createExpensiveGraph(key, result); 
     } 
    }); 

    FutureResult<String> value = graphs.get("Some Key"); 

    // add a custom handler 
    value.addCallback(new AsyncCallback<String>() { 
     public void onSuccess(String result) { 
      // do something 
     } 
     public void onFailure(Throwable caught) { 
      // do something    
     } 
    }); 
    // or see if it is already loaded/do not wait 
    if (value.isDone()) { 
     String success = value.get(); 
    } 

Quando si utilizza il FutureResult non si vogliono solo memorizzare nella cache l'esecuzione, ma anche ottenere una sorta di pigrizia in modo da poter mostrare qualche loading screen mentre i dati vengono caricati nella cache.

+0

Quello che vuoi non è così difficile da implementare, dipende solo da te aggiungere un callback di successo al "FutureResult''. Aggiornerò un esempio di codice sorgente per riflettere la mia idea. Ma dovresti capire che il callback verrà chiamato solo una volta al primo caricamento (che verrà memorizzato nella cache). Quindi, dopo il callback, '' result.isDone'' sarà vero. – Babl

+0

Controlla invece CompletableFuture - si comporta più come una Promessa, anche se dovrai lanciare delle eccezioni se qualcuno usa uno dei metodi Future bloccanti. –

+0

@ColinAlworth supporta GWT 'CompletableFuture'? – Babl

0

Se avete solo bisogno di memorizzare nella cache i risultati delle chiamate asincrone, si può andare per un non Loading Cache, invece di un carico Cache

In questo caso è necessario utilizzare put, getIfPresent metodi per memorizzare e recuperare i record dalla cache.

String v = cache.getIfPresent("one"); 
// returns null 
cache.put("one", "1"); 
v = cache.getIfPresent("one"); 
// returns "1" 

In alternativa un nuovo valore può essere caricato da un Callable su Cache Misses

String v = cache.get(key, 
    new Callable<String>() { 
     public String call() { 
     return key.toLowerCase(); 
    } 
}); 

Per ulteriori riferimenti: https://guava-libraries.googlecode.com/files/JavaCachingwithGuava.pdf

+0

Questo non era quello che stavo cercando. Il primo esempio aiuterà solo a memorizzare i valori per chiave, ma non fornisce alcun aiuto per il caricamento dei dati. Il secondo esempio sarà in grado di gestire solo il caricamento sincrono. – slartidan

+0

Quello che pensavo fosse che si può mettere il risultato nella cache una volta che si ottiene in "onSuccess' della chiamata asincrona. – Hari