Cache no aplicativo / widget GWT com HTML5 localStorage

stou tentando incorporar um cache de dados para um dos meus widgets GW
Tenho uma interface / classe de fonte de dados que recupera alguns dados do meu back-end viaRequestBuilder e JSON. Como eu exibo o widget várias vezes, só quero recuperar os dados uma ve

Então eu tentei vir com um cache de aplicativo. A abordagem ingênua é usar umHashMap em um objeto singleton para armazenar os dados. No entanto, também quero usar o localStorage / sessionStorage do HTML5, se suportad
HTML5 localStorage suporta apenas valores de String. Então eu tenho que converter meu objeto em JSON e armazenar como uma string. No entanto, de alguma forma, não consigo criar uma maneira limpa e agradável de fazer isso. aqui está o que eu tenho até agor

Defino uma interface com duas funções:fetchStatsList() busca a lista de estatísticas que podem ser exibidas no widget efetchStatsData() busca os dados reais.

public interface DataSource {
    public void fetchStatsData(Stat stat,FetchStatsDataCallback callback);
    public void fetchStatsList(FetchStatsListCallback callback);
}

OStat class é uma classe Javascript Overlay simples JavaScriptObject) com alguns getters (getName (), etc) Eu tenho uma implementação normal não-lavávelRequestBuilderDataSource do meu DataSource que se parece com o seguinte:

public class RequestBuilderDataSource implements DataSource {
    @Override
    public void fetchStatsList(final FetchStatsListCallback callback) {
         // create RequestBuilderRequest, retrieve response and parse JSON 
        callback.onFetchStatsList(stats);
    }

    @Override
    public void fetchStatsData(List<Stat> stats,final FetchStatsDataCallback callback) {
        String url = getStatUrl(stats);
        //create RequestBuilderRquest, retrieve response and parse JSON
        callback.onFetchStats(dataTable); //dataTable is of type DataTable
    }
}

Eu deixei de fora a maior parte do código para o RequestBuilder, pois é bastante diret

Isso funciona imediatamente, no entanto, a lista de estatísticas e também os dados são recuperados toda vez que os dados são compartilhados entre cada instância do widge

Para apoiar o cache, adiciono umCache interface e duas implementações de cache (uma para HTML5 localStorage e outra para HashMap):

public interface Cache {
    void put(Object key, Object value);
    Object get(Object key);
    void remove(Object key);
    void clear();
}

Eu adiciono uma nova classeRequestBuilderCacheDataSource que estende oRequestBuilderDataSource e tira umaCache instância em seu construto

public class RequestBuilderCacheDataSource extends RequestBuilderDataSource {

    private final Cache cache;

    publlic RequestBuilderCacheDataSource(final Cache cache) {
        this.cache = cache;
    }

    @Override
    public void fetchStatsList(final FetchStatsListCallback callback) {
       Object value = cache.get("list");
       if (value != null) {
           callback.fetchStatsList((List<Stat>)value);
       }
       else {
           super.fetchStatsList(stats,new FetchStatsListCallback() {
               @Override
               public void onFetchStatsList(List<Stat>stats) {
                   cache.put("list",stats);
                   callback.onFetchStatsList(stats);
               }
           });
           super.fetchStatsList(callback);
       }
    }

    @Override
    public void fetchStatsData(List<Stat> stats,final FetchStatsDataCallback callback) {
        String url = getStatUrl(stats);
        Object value = cache.get(url);
        if (value != null) {
            callback.onFetchStatsData((DataTable)value); 
        }
        else {
            super.fetchStatsData(stats,new FetchStatsDataCallback() {
                @Override
                public void onFetchStatsData(DataTable dataTable) {
                    cache.put(url,dataTable);
                    callback.onFetchStatsData(dataTable);
                }
            });
        }
    }
}

Basicamente, a nova classe pesquisará o valor noCache e, se não for encontrado, chamará a função de busca na classe pai e interceptará o retorno de chamada para colocá-lo no cache e depois o retorno de chamada rea
Então, para suportar o armazenamento local HTML5 e o armazenamento JS HashMap normal, criei duas implementações do meuCache interface:

JS HashMap storage:

public class DefaultcacheImpl implements Cache {
    private HashMap<Object, Object> map;

    public DefaultCacheImpl() {
        this.map = new HashMap<Object, Object>();
    }

    @Override
    public void put(Object key, Object value) {
        if (key == null) {
            throw new NullPointerException("key is null");
        }
        if (value == null) {
            throw new NullPointerException("value is null");
        }
        map.put(key, value);
    }

    @Override
    public Object get(Object key) {
        // Check for null as Cache should not store null values / keys
        if (key == null) {
            throw new NullPointerException("key is null");
        }
        return map.get(key);
    }

    @Override
    public void remove(Object key) {
        map.remove(key);
    }

    @Override
    public void clear() {
       map.clear();
    }
}

HTML5 localStorage:

public class LocalStorageImpl implements Cache{

    public static enum TYPE {LOCAL,SESSION} 
    private TYPE type;
    private Storage cacheStorage = null;

    public LocalStorageImpl(TYPE type) throws Exception {
        this.type = type;
        if (type == TYPE.LOCAL) {
            cacheStorage = Storage.getLocalStorageIfSupported();
        }
        else {
            cacheStorage = Storage.getSessionStorageIfSupported();
        }
        if (cacheStorage == null) {
            throw new Exception("LocalStorage not supported");
        }
    }


    @Override
    public void put(Object key, Object value) {
        //Convert Object (could be any arbitrary object) into JSON
        String jsonData = null;
        if (value instanceof List) {   // in case it is a list of Stat objects
            JSONArray array = new JSONArray();
            int index = 0;
            for (Object val:(List)value) {
                array.set(index,new JSONObject((JavaScriptObject)val));
                index = index +1;
            }
            jsonData = array.toString();
        }
        else  // in case it is a DataTable
        {
            jsonData = new JSONObject((JavaScriptObject) value).toString();
        }
        cacheStorage.setItem(key.toString(), jsonData);
    }

    @Override
    public Object get(Object key) {
        if (key == null) {
            throw new NullPointerException("key is null");
        }
        String jsonDataString = cacheStorage.getItem(key.toString());
        if (jsonDataString == null) {
            return null;
        }
        Object data = null;
        Object jsonData = JsonUtils.safeEval(jsonDataString);
        if (!key.equals("list")) 
            data = DataTable.create((JavaScriptObject)data);
        else if (jsonData instanceof JsArray){
            JsArray<GenomeStat> jsonStats = (JsArray<GenomeStat>)jsonData;
            List<GenomeStat> stats = new ArrayList<GenomeStat>();
            for (int i = 0;i<jsonStats.length();i++) {
                stats.add(jsonStats.get(i));
            }
            data = (Object)stats;
        }
        return data;
    }

    @Override
    public void remove(Object key) {
        cacheStorage.removeItem(key.toString());
    }

    @Override
    public void clear() {
        cacheStorage.clear();
    }

    public TYPE getType() {
        return type;
    }
}

O post ficou um pouco longo, mas espero esclarecer o que tento alcançar. Tudo se resume a duas perguntas:

Feedback sobre o design / arquitetura dessa abordagem (por exemplo, subclassificar RequestBilderDataSource para a função de cache, etc.). Isso pode ser melhorado (provavelmente está mais relacionado ao design geral do que especificamente ao GWTCom oDefaultCacheImpl é realmente fácil armazenar e recuperar objetos arbitrários. Como posso conseguir o mesmo com o localStorage, onde tenho que converter e analisar o JSON? Estou usando umDataTable, que requer chamar oDataTable.create(JavaScriptObject jso) função para trabalhar. Como posso resolver isso sem muitos if / else e instância de cheques?

questionAnswers(2)

yourAnswerToTheQuestion