Skip to content
This repository has been archived by the owner on Aug 9, 2020. It is now read-only.

Commit

Permalink
Added @Expirable annotation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Victor committed Apr 5, 2016
1 parent d7a0b22 commit 92a91a3
Show file tree
Hide file tree
Showing 28 changed files with 211 additions and 201 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,16 @@ new RxCache.Builder()

This limit ensure that the disk will no grow up limitless in case you have providers with dynamic keys which values changes dynamically, like filters based on gps location or dynamic filters supplied by your back-end solution.

When this limit is reached, RxCache will not be able to persisted in disk new data. That's why RxCache has an automated process which allows to evict 'expirable' records. And by 'expirable' I mean any record which has been saved using a provider annotated with LifeCache.
When this limit is reached, RxCache will not be able to persist in disk new data. That's why RxCache has an automated process to evict any record when the threshold memory assigned to the persistence layer is close to be reached, even if the record life time has not been fulfilled.

So any record persisted with a LifeCache annotation is a candidate to be evicted when new data is requested but there is no more available space, even it its life time has not been fulfilled.
But provider's record annotated with [@Expirable](https://github.com/VictorAlbertos/RxCache/blob/master/rx_cache/src/main/java/io/rx_cache/Expirable.java) annotation and set its value to false will be exclude from the process.

```java
interface Providers {
@Expirable(false)
Observable<List<Mock>> getMocksNotExpirable(Observable<List<Mock>> oMocks);
}
```

### Use expired data if loader not available

Expand Down
36 changes: 36 additions & 0 deletions rx_cache/src/main/java/io/rx_cache/Expirable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2015 Victor Albertos
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.rx_cache;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* RxCache has an automated process to evict any record when the threshold memory assigned to the persistence layer is close to reached, even if its life time has not been fulfilled.
* Provider's record annotated with Expirable annotation and set its value to false will be exclude from the process.
*/
@Target(METHOD)
@Retention(RUNTIME)
public @interface Expirable {
boolean value();
}



72 changes: 0 additions & 72 deletions rx_cache/src/main/java/io/rx_cache/Record.java

This file was deleted.

18 changes: 8 additions & 10 deletions rx_cache/src/main/java/io/rx_cache/internal/Disk.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@

import javax.inject.Inject;

import io.rx_cache.Record;

/**
* Save objects in disk and evict them too. It uses Gson as json parser.
*/
Expand All @@ -49,7 +47,7 @@ public final class Disk implements Persistence {
* @param record the record to be persisted.
* */
@Override public void saveRecord(String key, Record record) {
save(key, new DiskRecord(record));
save(key, record);
}

/**
Expand Down Expand Up @@ -142,12 +140,12 @@ public <T> T retrieve(String key, Class<T> clazz) {

/** Retrieve the Record previously saved.
* @param key the key whereby the object could be retrieved.*/
@Override public <T> DiskRecord<T> retrieveRecord(String key) {
@Override public <T> Record<T> retrieveRecord(String key) {
try {
File file = new File(cacheDirectory, key);

BufferedReader readerTempRecord = new BufferedReader(new FileReader(file.getAbsoluteFile()));
DiskRecord tempDiskRecord = new Gson().fromJson(readerTempRecord, DiskRecord.class);
Record tempDiskRecord = new Gson().fromJson(readerTempRecord, Record.class);
readerTempRecord.close();

BufferedReader reader = new BufferedReader(new FileReader(file.getAbsoluteFile()));
Expand All @@ -158,22 +156,22 @@ public <T> T retrieve(String key, Class<T> clazz) {
boolean isCollection = Collection.class.isAssignableFrom(classCollectionData);
boolean isArray = classCollectionData.isArray();
boolean isMap = Map.class.isAssignableFrom(classCollectionData);
DiskRecord<T> diskRecord;
Record<T> diskRecord;

if (isCollection) {
Type typeCollection = $Gson$Types.newParameterizedTypeWithOwner(null, classCollectionData, classData);
Type typeRecord = $Gson$Types.newParameterizedTypeWithOwner(null, DiskRecord.class, typeCollection, classData);
Type typeRecord = $Gson$Types.newParameterizedTypeWithOwner(null, Record.class, typeCollection, classData);
diskRecord = new Gson().fromJson(reader, typeRecord);
} else if (isArray) {
Type typeRecord = $Gson$Types.newParameterizedTypeWithOwner(null, DiskRecord.class, classCollectionData);
Type typeRecord = $Gson$Types.newParameterizedTypeWithOwner(null, Record.class, classCollectionData);
diskRecord = new Gson().fromJson(reader, typeRecord);
} else if (isMap) {
Class classKeyMap = Class.forName(tempDiskRecord.getDataKeyMapClassName());
Type typeMap = $Gson$Types.newParameterizedTypeWithOwner(null, classCollectionData, classKeyMap, classData);
Type typeRecord = $Gson$Types.newParameterizedTypeWithOwner(null, DiskRecord.class, typeMap, classData);
Type typeRecord = $Gson$Types.newParameterizedTypeWithOwner(null, Record.class, typeMap, classData);
diskRecord = new Gson().fromJson(reader, typeRecord);
} else {
Type type = $Gson$Types.newParameterizedTypeWithOwner(null, DiskRecord.class, classData);
Type type = $Gson$Types.newParameterizedTypeWithOwner(null, Record.class, classData);
diskRecord = new Gson().fromJson(reader, type);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.Set;

import io.rx_cache.PolicyHeapCache;
import io.rx_cache.Record;

public class GuavaMemory implements Memory {
private final Cache<String, Record> records;
Expand Down
2 changes: 0 additions & 2 deletions rx_cache/src/main/java/io/rx_cache/internal/Memory.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

import java.util.Set;

import io.rx_cache.Record;

public interface Memory {
<T> Record<T> getIfPresent(String key);
<T> void put(String key, Record<T> record);
Expand Down
4 changes: 1 addition & 3 deletions rx_cache/src/main/java/io/rx_cache/internal/Persistence.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

import java.util.List;

import io.rx_cache.Record;

/**
* Provides the persistence layer for the cache
* A default implementation which store the objects in disk is supplied:
Expand Down Expand Up @@ -77,5 +75,5 @@ public interface Persistence {
* @param key The key associated with the Record to be retrieved from persistence
* @see Record
*/
<T> DiskRecord<T> retrieveRecord(String key);
<T> Record<T> retrieveRecord(String key);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

import io.rx_cache.EvictDynamicKey;
import io.rx_cache.EvictDynamicKeyGroup;
import io.rx_cache.Record;
import io.rx_cache.Reply;
import io.rx_cache.Source;
import io.rx_cache.internal.cache.EvictExpiredRecordsPersistence;
Expand Down Expand Up @@ -127,7 +126,7 @@ private Observable<Reply> getDataFromLoader(final ProxyTranslator.ConfigProvider
if (data == null)
throw new RxCacheException(Locale.NOT_DATA_RETURN_WHEN_CALLING_OBSERVABLE_LOADER + " " + configProvider.getProviderKey());

twoLayersCache.save(configProvider.getProviderKey(), configProvider.getDynamicKey(), configProvider.getDynamicKeyGroup(), data, configProvider.getLifeTimeMillis());
twoLayersCache.save(configProvider.getProviderKey(), configProvider.getDynamicKey(), configProvider.getDynamicKeyGroup(), data, configProvider.getLifeTimeMillis(), configProvider.isExpirable());
return new Reply(data, Source.CLOUD);
}
}).onErrorReturn(new Func1() {
Expand Down
17 changes: 15 additions & 2 deletions rx_cache/src/main/java/io/rx_cache/internal/ProxyTranslator.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.rx_cache.EvictDynamicKeyGroup;
import io.rx_cache.EvictProvider;
import io.rx_cache.LifeCache;
import io.rx_cache.Expirable;
import io.rx_cache.Reply;
import rx.Observable;

Expand All @@ -40,7 +41,7 @@ ConfigProvider processMethod(Method method, Object[] objectsMethod) {
this.objectsMethod = objectsMethod;

ConfigProvider configProvider = new ConfigProvider(getProviderKey(), getDynamicKey(), getDynamicKeyGroup(), getLoaderObservable(),
getLifeTimeCache(), requiredDetailResponse(), evictProvider());
getLifeTimeCache(), requiredDetailResponse(), evictProvider(), getExpirable());
checkIntegrityConfiguration(configProvider);

return configProvider;
Expand Down Expand Up @@ -79,6 +80,12 @@ protected long getLifeTimeCache() {
return lifeCache.timeUnit().toMillis(lifeCache.duration());
}

protected boolean getExpirable() {
Expirable expirable = method.getAnnotation(Expirable.class);
if (expirable != null) return expirable.value();
return true;
}

protected boolean requiredDetailResponse() {
if (method.getReturnType() != Observable.class) {
String errorMessage = method.getName() + Locale.INVALID_RETURN_TYPE;
Expand Down Expand Up @@ -133,15 +140,17 @@ final static class ConfigProvider {
private final long lifeTime;
private final boolean requiredDetailedResponse;
private final EvictProvider evictProvider;
private final boolean expirable;

ConfigProvider(String providerKey, String dynamicKey, String group, Observable loaderObservable, long lifeTime, boolean requiredDetailedResponse, EvictProvider evictProvider) {
ConfigProvider(String providerKey, String dynamicKey, String group, Observable loaderObservable, long lifeTime, boolean requiredDetailedResponse, EvictProvider evictProvider, boolean expirable) {
this.providerKey = providerKey;
this.dynamicKey = dynamicKey;
this.dynamicKeyGroup = group;
this.loaderObservable = loaderObservable;
this.lifeTime = lifeTime;
this.evictProvider = evictProvider;
this.requiredDetailedResponse = requiredDetailedResponse;
this.expirable = expirable;
}

String getProviderKey() {
Expand Down Expand Up @@ -171,5 +180,9 @@ Observable getLoaderObservable() {
public EvictProvider evictProvider() {
return evictProvider;
}

public boolean isExpirable() {
return expirable;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,36 @@
import java.util.List;
import java.util.Map;

import io.rx_cache.Record;
import io.rx_cache.Source;

public final class DiskRecord<T> extends Record<T> {
/**
* Wrapper around the actual data in order to know if its life time has been expired
* @param <T> The actual data
*/
public class Record<T> {
private Source source;
private final T data;
private final long timeAtWhichWasPersisted;
private final String dataClassName, dataCollectionClassName, dataKeyMapClassName;
private final Boolean isExpirable;

DiskRecord(Record<T> record) {
this(record.getData(), record.getLifeTime());
}
//LifeTime requires to be stored to be evicted by EvictExpiredRecordsTask when no life time is available without a config provider
private long lifeTime;

//Required by EvictExpirableRecordsPersistence task
private transient float sizeOnMb;

@VisibleForTesting DiskRecord(T data) {
this(data, 0);
@VisibleForTesting
Record(T data) {
this(data, true, 0);
}

private DiskRecord(T data, long lifeTime) {
super(data, lifeTime);
public Record(T data, Boolean isExpirable, long lifeTime) {
this.data = data;
this.isExpirable = isExpirable;
this.lifeTime = lifeTime;
this.timeAtWhichWasPersisted = System.currentTimeMillis();
this.source = Source.MEMORY;

boolean isList = Collection.class.isAssignableFrom(data.getClass());
boolean isArray = data.getClass().isArray();
Expand Down Expand Up @@ -81,6 +96,38 @@ private DiskRecord(T data, long lifeTime) {
}
}

public Source getSource() {
return source;
}

public void setSource(Source source) {
this.source = source;
}

public T getData() {
return data;
}

public long getTimeAtWhichWasPersisted() {
return timeAtWhichWasPersisted;
}

public long getLifeTime() {
return lifeTime;
}

public void setLifeTime(long lifeTime) {
this.lifeTime = lifeTime;
}

public float getSizeOnMb() {
return sizeOnMb;
}

public void setSizeOnMb(float sizeOnMb) {
this.sizeOnMb = sizeOnMb;
}

public String getDataClassName() {
return dataClassName;
}
Expand All @@ -92,4 +139,8 @@ String getDataCollectionClassName() {
String getDataKeyMapClassName() {
return dataKeyMapClassName;
}

public Boolean isExpirable() {
return isExpirable == null ? true : isExpirable;
}
}
Loading

0 comments on commit 92a91a3

Please sign in to comment.