├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── README_ZH.md ├── android ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── victoralbertos │ │ └── io │ │ └── android │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── victoralbertos │ │ │ └── io │ │ │ └── android │ │ │ ├── MainActivity.java │ │ │ ├── Mock.java │ │ │ ├── RxProviders.java │ │ │ └── RxProvidersActionable.java │ └── res │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ └── strings.xml │ └── test │ └── java │ └── victoralbertos │ └── io │ └── android │ └── ExampleUnitTest.java ├── build.gradle ├── compiler ├── build.gradle └── src │ ├── main │ └── java │ │ └── io │ │ └── rx_cache │ │ ├── ActionsProcessor.java │ │ ├── GenerateActions.java │ │ ├── ParseProviderScheme.java │ │ └── ProviderScheme.java │ └── test │ └── java │ └── io │ └── rx_cache │ ├── ActionsProcessorTest.java │ └── Mock.java ├── core ├── build.gradle └── src │ ├── main │ └── java │ │ └── io │ │ └── rx_cache │ │ ├── Actionable.java │ │ ├── Actions.java │ │ ├── DynamicKey.java │ │ ├── DynamicKeyGroup.java │ │ ├── EvictDynamicKey.java │ │ ├── EvictDynamicKeyGroup.java │ │ ├── EvictProvider.java │ │ ├── Expirable.java │ │ ├── LifeCache.java │ │ ├── Migration.java │ │ ├── Reply.java │ │ ├── RxCacheException.java │ │ ├── SchemeMigration.java │ │ ├── Source.java │ │ └── internal │ │ ├── Disk.java │ │ ├── Locale.java │ │ ├── Memory.java │ │ ├── Persistence.java │ │ ├── ProxyProviders.java │ │ ├── ProxyTranslator.java │ │ ├── Record.java │ │ ├── RxCache.java │ │ ├── RxCacheComponent.java │ │ ├── RxCacheModule.java │ │ ├── cache │ │ ├── Action.java │ │ ├── EvictExpirableRecordsPersistence.java │ │ ├── EvictExpiredRecordsPersistence.java │ │ ├── EvictRecord.java │ │ ├── GetDeepCopy.java │ │ ├── HasRecordExpired.java │ │ ├── RetrieveRecord.java │ │ ├── SaveRecord.java │ │ ├── TwoLayersCache.java │ │ └── memory │ │ │ ├── ReferenceMapMemory.java │ │ │ └── apache │ │ │ ├── AbstractEmptyIterator.java │ │ │ ├── AbstractEmptyMapIterator.java │ │ │ ├── AbstractHashedMap.java │ │ │ ├── AbstractKeyValue.java │ │ │ ├── AbstractMapEntry.java │ │ │ ├── AbstractReferenceMap.java │ │ │ ├── DefaultMapEntry.java │ │ │ ├── EmptyIterator.java │ │ │ ├── EmptyMapIterator.java │ │ │ ├── Get.java │ │ │ ├── IterableGet.java │ │ │ ├── IterableMap.java │ │ │ ├── KeyValue.java │ │ │ ├── MapIterator.java │ │ │ ├── Put.java │ │ │ ├── ReferenceMap.java │ │ │ └── ResettableIterator.java │ │ └── migration │ │ ├── CacheVersion.java │ │ ├── DeleteRecordMatchingClassName.java │ │ ├── DoMigrations.java │ │ ├── GetCacheVersion.java │ │ ├── GetClassesToEvictFromMigrations.java │ │ ├── GetPendingMigrations.java │ │ └── UpgradeCacheVersion.java │ └── test │ └── java │ ├── Providers.java │ ├── Repository.java │ └── io │ └── rx_cache │ └── internal │ ├── ActionsTest.java │ ├── DiskTest.java │ ├── Mock.java │ ├── ProvidersDynamicsKeysRxCacheTest.java │ ├── ProvidersRxCache.java │ ├── ProvidersRxCacheEvictExpirableRecordsTest.java │ ├── ProvidersRxCacheEvictExpiredRecordsTest.java │ ├── ProvidersRxCacheTest.java │ ├── ProxyProvidersTest.java │ ├── ProxyTranslatorTest.java │ ├── cache │ ├── ActionTest.java │ ├── EvictExpirableRecordsPersistenceTest.java │ ├── EvictExpiredRecordsPersistenceTest.java │ ├── SaveRecordTest.java │ └── TwoLayersCacheTest.java │ ├── common │ ├── BaseTest.java │ └── BaseTestEvictingTask.java │ └── migration │ ├── DeleteRecordMatchingClassNameTest.java │ ├── GetCacheVersionTest.java │ ├── GetClassesToEvictFromMigrationsTest.java │ ├── GetPendingMigrationsTest.java │ ├── ProvidersRxCacheMigrations.java │ └── UpgradeCacheVersionTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | **/build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # Android Studio 29 | .navigation/ 30 | .idea/ 31 | **/*.iml 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # .DS_Store files 37 | **/.DS_Store 38 | 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | script: "./gradlew build" 3 | android: 4 | components: 5 | # The BuildTools version used by your project 6 | - build-tools-23.0.1 7 | 8 | # The SDK version used to compile your project 9 | - android-23 10 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'android-apt' 3 | 4 | buildscript { 5 | repositories { 6 | mavenCentral() 7 | jcenter() 8 | } 9 | 10 | dependencies { 11 | classpath "com.neenbedankt.gradle.plugins:android-apt:1.8" 12 | } 13 | } 14 | 15 | android { 16 | compileSdkVersion 23 17 | buildToolsVersion "23.0.1" 18 | 19 | defaultConfig { 20 | applicationId "victoralbertos.io.android" 21 | minSdkVersion 16 22 | targetSdkVersion 23 23 | versionCode 1 24 | versionName "1.0" 25 | } 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | lintOptions { 33 | abortOnError false 34 | } 35 | } 36 | 37 | dependencies { 38 | compile fileTree(dir: 'libs', include: ['*.jar']) 39 | compile project(':core') 40 | apt project(':compiler') 41 | 42 | testCompile 'junit:junit:4.12' 43 | 44 | apt "com.google.dagger:dagger-compiler:2.4" 45 | compile "com.google.dagger:dagger:2.4" 46 | provided "org.glassfish:javax.annotation:10.0-b28" 47 | } 48 | 49 | 50 | /*configurations { 51 | compile.exclude module: 'guava' 52 | }*/ 53 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/victor/Documents/AndroidStudio/android_sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /android/src/androidTest/java/victoralbertos/io/android/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package victoralbertos.io.android; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 10 | 11 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/src/main/java/victoralbertos/io/android/MainActivity.java: -------------------------------------------------------------------------------- 1 | package victoralbertos.io.android; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import io.rx_cache.internal.RxCache; 10 | import rx.Observable; 11 | 12 | /** 13 | * Created by victor on 21/01/16. 14 | */ 15 | public class MainActivity extends Activity { 16 | 17 | @Override protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | 20 | //Create integration test for max mg limit and clearing expired data 21 | 22 | 23 | final RxProviders rxProviders = new RxCache.Builder() 24 | .setMaxMBPersistenceCache(50) 25 | .persistence(getApplicationContext().getFilesDir()) 26 | .using(RxProviders.class); 27 | 28 | /* for (int i = 0; i < 1000; i++) { 29 | String key = System.currentTimeMillis() + i + ""; 30 | rxProviders.getMocksEphemeralPaginate(createObservableMocks(100), new DynamicKey(key)) 31 | .subscribe(); 32 | }*/ 33 | 34 | } 35 | 36 | private Observable> createObservableMocks(int size) { 37 | List mocks = new ArrayList(size); 38 | for (int i = 0; i < size; i++) { 39 | mocks.add(new Mock("Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 40 | "making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, " + 41 | "consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes " + 42 | "from sections 1.10.32 and 1.10.33 of \"de Finibus Bonorum et Malorum\" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the " + 43 | "theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, \"Lorem ipsum dolor sit amet..\", comes from a line in section 1.10.32.")); 44 | } 45 | return Observable.just(mocks); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android/src/main/java/victoralbertos/io/android/Mock.java: -------------------------------------------------------------------------------- 1 | package victoralbertos.io.android; 2 | 3 | /** 4 | * Created by victor on 27/04/16. 5 | */ 6 | public class Mock { 7 | final private String message; 8 | 9 | public Mock(String message) { 10 | this.message = message; 11 | } 12 | 13 | public String getMessage() { 14 | return message; 15 | } 16 | 17 | public class InnerMock { 18 | final private String message; 19 | 20 | public InnerMock(String message) { 21 | this.message = message; 22 | } 23 | 24 | public String getMessage() { 25 | return message; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /android/src/main/java/victoralbertos/io/android/RxProviders.java: -------------------------------------------------------------------------------- 1 | package victoralbertos.io.android; 2 | 3 | /** 4 | * Created by victor on 21/01/16. 5 | */ 6 | public interface RxProviders { 7 | /* @LifeCache(duration = 1, timeUnit = TimeUnit.SECONDS) 8 | Observable getMessage(Observable message, DynamicKey dynamicKey); 9 | Observable> getMocksPaginate(Observable> mocks, DynamicKey page); 10 | 11 | @LifeCache(duration = 1, timeUnit = TimeUnit.MILLISECONDS) 12 | Observable> getMocksEphemeralPaginate(Observable> mocks, DynamicKey page); 13 | 14 | @Actionable 15 | Observable> mocks(Observable> message, EvictProvider evictProvider); 16 | 17 | @Actionable 18 | Observable> mocksDynamicKey(Observable> message, DynamicKey dynamicKey, EvictDynamicKey evictDynamicKey); 19 | 20 | @Actionable 21 | Observable> mocksDynamicKeyGroup(Observable> message, DynamicKeyGroup dynamicKeyGroup, EvictDynamicKeyGroup evictDynamicKey);*/ 22 | } 23 | -------------------------------------------------------------------------------- /android/src/main/java/victoralbertos/io/android/RxProvidersActionable.java: -------------------------------------------------------------------------------- 1 | package victoralbertos.io.android; 2 | 3 | import java.util.List; 4 | import java.util.concurrent.TimeUnit; 5 | 6 | import io.rx_cache.Actionable; 7 | import io.rx_cache.DynamicKey; 8 | import io.rx_cache.DynamicKeyGroup; 9 | import io.rx_cache.EvictDynamicKey; 10 | import io.rx_cache.EvictDynamicKeyGroup; 11 | import io.rx_cache.EvictProvider; 12 | import io.rx_cache.LifeCache; 13 | import rx.Observable; 14 | 15 | /** 16 | * Created by victor on 21/01/16. 17 | */ 18 | public interface RxProvidersActionable { 19 | @LifeCache(duration = 1, timeUnit = TimeUnit.SECONDS) 20 | Observable getMessage(Observable message, DynamicKey dynamicKey); 21 | Observable> getMocksPaginate(Observable> mocks, DynamicKey page); 22 | 23 | @LifeCache(duration = 1, timeUnit = TimeUnit.MILLISECONDS) 24 | Observable> getMocksEphemeralPaginate(Observable> mocks, DynamicKey page); 25 | 26 | @Actionable 27 | Observable> mocks(Observable> message, EvictProvider evictProvider); 28 | 29 | @Actionable 30 | Observable> mocksDynamicKey(Observable> message, DynamicKey dynamicKey, EvictDynamicKey evictDynamicKey); 31 | 32 | @Actionable 33 | Observable> mocksDynamicKeyGroup(Observable> message, DynamicKeyGroup dynamicKeyGroup, EvictDynamicKeyGroup evictDynamicKey); 34 | } 35 | -------------------------------------------------------------------------------- /android/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sungerk/RxCache/97b96550916566204f4b54c88d8434254eaef568/android/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sungerk/RxCache/97b96550916566204f4b54c88d8434254eaef568/android/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sungerk/RxCache/97b96550916566204f4b54c88d8434254eaef568/android/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sungerk/RxCache/97b96550916566204f4b54c88d8434254eaef568/android/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sungerk/RxCache/97b96550916566204f4b54c88d8434254eaef568/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Android 3 | 4 | -------------------------------------------------------------------------------- /android/src/test/java/victoralbertos/io/android/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package victoralbertos.io.android; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.5.0' 9 | // NOTE: Do not place your application dependencies here; they belong 10 | // in the individual module build.gradle files 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | jcenter() 17 | } 18 | } 19 | 20 | task clean(type: Delete) { 21 | delete rootProject.buildDir 22 | } 23 | -------------------------------------------------------------------------------- /compiler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'maven' 3 | group='com.github.VictorAlbertos' 4 | 5 | sourceCompatibility = 1.7 6 | targetCompatibility = 1.7 7 | 8 | dependencies { 9 | compile fileTree(dir: 'libs', include: ['*.jar']) 10 | compile files("${System.properties['java.home']}/../lib/tools.jar") 11 | compile project(':core') 12 | 13 | compile 'com.google.auto.service:auto-service:1.0-rc2' 14 | compile 'com.squareup:javapoet:1.6.1' 15 | 16 | testCompile "junit:junit:4.12" 17 | testCompile "com.google.testing.compile:compile-testing:0.9" 18 | } 19 | 20 | task sourcesJar(type: Jar, dependsOn: classes) { 21 | classifier = 'sources' 22 | from sourceSets.main.allSource 23 | } 24 | 25 | task javadocJar(type: Jar, dependsOn: javadoc) { 26 | classifier = 'javadoc' 27 | from javadoc.destinationDir 28 | } 29 | 30 | artifacts { 31 | archives sourcesJar 32 | archives javadocJar 33 | } -------------------------------------------------------------------------------- /compiler/src/main/java/io/rx_cache/ActionsProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | 20 | import com.google.auto.service.AutoService; 21 | 22 | import java.io.IOException; 23 | import java.util.ArrayList; 24 | import java.util.LinkedHashSet; 25 | import java.util.List; 26 | import java.util.Set; 27 | 28 | import javax.annotation.processing.AbstractProcessor; 29 | import javax.annotation.processing.Filer; 30 | import javax.annotation.processing.Messager; 31 | import javax.annotation.processing.ProcessingEnvironment; 32 | import javax.annotation.processing.Processor; 33 | import javax.annotation.processing.RoundEnvironment; 34 | import javax.lang.model.SourceVersion; 35 | import javax.lang.model.element.Element; 36 | import javax.lang.model.element.TypeElement; 37 | import javax.tools.Diagnostic; 38 | 39 | @AutoService(Processor.class) 40 | public class ActionsProcessor extends AbstractProcessor { 41 | private Messager messager; 42 | private Filer filer; 43 | private List providerSchemes; 44 | 45 | @Override public synchronized void init(ProcessingEnvironment processingEnv) { 46 | super.init(processingEnv); 47 | this.messager = processingEnv.getMessager(); 48 | this.filer = processingEnv.getFiler(); 49 | this.providerSchemes = new ArrayList<>(); 50 | } 51 | 52 | @Override public SourceVersion getSupportedSourceVersion() { 53 | return SourceVersion.latestSupported(); 54 | } 55 | 56 | @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { 57 | providerSchemes.clear(); 58 | 59 | for (Element element : roundEnv.getElementsAnnotatedWith(Actionable.class)) { 60 | try { 61 | ParseProviderScheme parseProviderScheme = new ParseProviderScheme(element); 62 | ProviderScheme providerScheme = parseProviderScheme.getProviderScheme(); 63 | providerSchemes.add(providerScheme); 64 | } catch (ParseProviderScheme.ParseException e) { 65 | messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage(), e.getElement()); 66 | return true; 67 | } 68 | } 69 | 70 | if (providerSchemes.isEmpty()) return false; 71 | 72 | GenerateActions generateActions = new GenerateActions(filer, providerSchemes, ""); 73 | 74 | try { 75 | generateActions.generate(); 76 | } catch (IOException e) { 77 | messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage()); 78 | } 79 | 80 | return false; 81 | } 82 | 83 | @Override public Set getSupportedAnnotationTypes() { 84 | Set annotations = new LinkedHashSet(); 85 | annotations.add(Actionable.class.getCanonicalName()); 86 | return annotations; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /compiler/src/main/java/io/rx_cache/ParseProviderScheme.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | 20 | import com.sun.tools.javac.code.Symbol; 21 | import com.sun.tools.javac.code.Type; 22 | 23 | import java.util.List; 24 | 25 | import javax.lang.model.element.Element; 26 | import javax.lang.model.element.ElementKind; 27 | 28 | class ParseProviderScheme { 29 | private final Symbol.MethodSymbol element; 30 | 31 | ParseProviderScheme(Element element) throws ParseException { 32 | String nameMethod = element.getSimpleName().toString(); 33 | 34 | if (element.getKind() != ElementKind.METHOD) { 35 | throw new ParseException(element, "Error parsing @%s provider. Only methods can be annotated with @%s", 36 | nameMethod, Actionable.class.getSimpleName()); 37 | } 38 | 39 | this.element = (Symbol.MethodSymbol) element; 40 | } 41 | 42 | public ProviderScheme getProviderScheme() throws ParseException { 43 | String classNameOwner = element.owner.toString(); 44 | String nameMethod = element.getSimpleName().toString(); 45 | String signatureMethod = element.toString(); 46 | List symbols = element.getParameters(); 47 | 48 | 49 | String fullQualifiedNameTypeList = fullQualifiedNameTypeList(symbols); 50 | if (fullQualifiedNameTypeList == null) { 51 | throw new ParseException(element, "Error parsing @%s provider. Only list is supported as observable loader", nameMethod); 52 | } 53 | 54 | boolean hasDynamicKey = hasDynamicKey(symbols); 55 | boolean hasDynamicKeyGroup = hasDynamicKeyGroup(symbols); 56 | boolean hasEvictProvider = hasEvictProvider(symbols); 57 | boolean hasEvictDynamicKey = hasEvictDynamicKey(symbols); 58 | boolean hasEvictDynamicKeyGroup = hasEvictDynamicKeyGroup(symbols); 59 | 60 | if (!hasEvictProvider && !hasEvictDynamicKey && !hasEvictDynamicKeyGroup) { 61 | throw new ParseException(element, "Error parsing @%s provider. The provider requires one evicting argument: EvictProvider, EvictDynamicKey or EvictDynamicKeyGroup", nameMethod); 62 | } 63 | 64 | if (hasEvictProvider && hasEvictDynamicKey) { 65 | throw new ParseException(element, "Error parsing @%s provider. The provider requires one evicting argument: EvictProvider, EvictDynamicKey or EvictDynamicKeyGroup", nameMethod); 66 | } 67 | 68 | if (hasEvictProvider && hasEvictDynamicKeyGroup) { 69 | throw new ParseException(element, "Error parsing @%s provider. The provider requires one evicting argument: EvictProvider, EvictDynamicKey or EvictDynamicKeyGroup", nameMethod); 70 | } 71 | 72 | if (hasEvictDynamicKey && hasEvictDynamicKeyGroup) { 73 | throw new ParseException(element, "Error parsing @%s provider. The provider requires one evicting argument: EvictProvider, EvictDynamicKey or EvictDynamicKeyGroup", nameMethod); 74 | } 75 | 76 | ProviderScheme providerScheme = new ProviderScheme(classNameOwner, nameMethod, fullQualifiedNameTypeList, hasDynamicKey, hasDynamicKeyGroup); 77 | return providerScheme; 78 | } 79 | 80 | private String fullQualifiedNameTypeList(List symbols) { 81 | for (Symbol.VarSymbol symbol: symbols) { 82 | String nameSymbol = ((Type.ClassType) (symbol.type)).tsym.toString(); 83 | if (nameSymbol.equals(rx.Observable.class.getName())) { 84 | String fullQualifiedNameTypeList = ((Type.ClassType) (symbol).type).typarams_field.get(0).getTypeArguments().get(0).toString(); 85 | return fullQualifiedNameTypeList; 86 | } 87 | } 88 | return null; 89 | } 90 | 91 | private boolean hasDynamicKey(List symbols) { 92 | return hasSymbol(symbols, DynamicKey.class); 93 | } 94 | 95 | private boolean hasDynamicKeyGroup(List symbols) { 96 | return hasSymbol(symbols, DynamicKeyGroup.class); 97 | } 98 | 99 | private boolean hasEvictProvider(List symbols) { 100 | return hasSymbol(symbols, EvictProvider.class); 101 | } 102 | 103 | private boolean hasEvictDynamicKey(List symbols) { 104 | return hasSymbol(symbols, EvictDynamicKey.class); 105 | } 106 | 107 | private boolean hasEvictDynamicKeyGroup(List symbols) { 108 | return hasSymbol(symbols, EvictDynamicKeyGroup.class); 109 | } 110 | 111 | private boolean hasSymbol(List symbols, Class candidateClass) { 112 | for (Symbol.VarSymbol symbol: symbols) { 113 | String symbolClassName = symbol.type.toString(); 114 | String candidateClassName = candidateClass.getCanonicalName(); 115 | if (symbolClassName.equals(candidateClassName)) return true; 116 | } 117 | 118 | return false; 119 | } 120 | 121 | static class ParseException extends Exception { 122 | private final Element element; 123 | 124 | public ParseException(Element element, String msg, Object... args) { 125 | super(String.format(msg, args)); 126 | this.element = element; 127 | } 128 | 129 | public Element getElement() { 130 | return element; 131 | } 132 | 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /compiler/src/main/java/io/rx_cache/ProviderScheme.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | class ProviderScheme { 20 | private final String fullQualifiedNameOwner, nameMethod, fullQualifiedNameTypeList; 21 | private final boolean hasDynamicKey, hasDynamicKeyGroup; 22 | 23 | public ProviderScheme(String fullQualifiedNameOwner, String nameMethod, String fullQualifiedNameTypeList, boolean hasDynamicKey, boolean hasDynamicKeyGroup) { 24 | this.fullQualifiedNameOwner = fullQualifiedNameOwner; 25 | this.nameMethod = nameMethod; 26 | this.fullQualifiedNameTypeList = fullQualifiedNameTypeList; 27 | this.hasDynamicKey = hasDynamicKey; 28 | this.hasDynamicKeyGroup = hasDynamicKeyGroup; 29 | } 30 | 31 | public String getSimpleNameOwner() { 32 | return fullQualifiedNameOwner.substring(fullQualifiedNameOwner.lastIndexOf(".") + 1); 33 | } 34 | 35 | public String getPackageNameOwner() { 36 | return fullQualifiedNameOwner.substring(0, fullQualifiedNameOwner.lastIndexOf(".")); 37 | } 38 | 39 | public String getNameMethod() { 40 | return nameMethod; 41 | } 42 | 43 | public String getSimpleNameTypeList() { 44 | return fullQualifiedNameTypeList.substring(fullQualifiedNameTypeList.lastIndexOf(".") + 1); 45 | } 46 | 47 | public String getPackageNameTypeList() { 48 | return fullQualifiedNameTypeList.substring(0, fullQualifiedNameTypeList.lastIndexOf(".")); 49 | } 50 | 51 | public boolean hasDynamicKey() { 52 | return hasDynamicKey; 53 | } 54 | 55 | public boolean hasDynamicKeyGroup() { 56 | return hasDynamicKeyGroup; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /compiler/src/test/java/io/rx_cache/ActionsProcessorTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | import com.google.testing.compile.JavaFileObjects; 20 | 21 | import org.junit.Test; 22 | 23 | import javax.tools.JavaFileObject; 24 | 25 | import static com.google.common.truth.Truth.assertAbout; 26 | import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; 27 | 28 | public class ActionsProcessorTest { 29 | 30 | @Test public void Test_Processor() throws Exception { 31 | JavaFileObject source = JavaFileObjects.forSourceString("RxProviders", "" + 32 | "package test;\n" + 33 | "import java.util.List;\n" + 34 | "import io.rx_cache.Actionable;\n" + 35 | "import io.rx_cache.DynamicKey;\n" + 36 | "import io.rx_cache.DynamicKeyGroup;\n" + 37 | "import io.rx_cache.EvictDynamicKey;\n" + 38 | "import io.rx_cache.EvictDynamicKeyGroup;\n" + 39 | "import io.rx_cache.EvictProvider;\n" + 40 | "import rx.Observable;\n" + 41 | 42 | "public interface RxProviders {\n" + 43 | " @Actionable\n" + 44 | " Observable> mocks(Observable> message, EvictProvider evictProvider);\n" + 45 | 46 | " @Actionable\n" + 47 | " Observable> mocksDynamicKey(Observable> message, DynamicKey dynamicKey, EvictDynamicKey evictDynamicKey);\n" + 48 | 49 | " @Actionable\n" + 50 | " Observable> mocksDynamicKeyGroup(Observable> message, DynamicKeyGroup dynamicKeyGroup, EvictDynamicKeyGroup evictDynamicKey);\n" + 51 | "}"); 52 | 53 | JavaFileObject expectedSource = JavaFileObjects.forSourceString("test/ActionsProviders", "" + 54 | "package io.rx_cache;\n" + 55 | "\n" + 56 | "import io.rx_cache.internal.RxCache;\n" + 57 | "import java.lang.String;\n" + 58 | "import java.util.ArrayList;\n" + 59 | "import java.util.List;\n" + 60 | "import rx.Observable;\n" + 61 | "import test.RxProviders;\n" + 62 | "\n" + 63 | "public final class ActionsProviders {\n" + 64 | " public static Actions mocks() {\n" + 65 | " final RxProviders proxy = (RxProviders) RxCache.retainedProxy();\n" + 66 | " Actions.Evict evict = new Actions.Evict() {\n" + 67 | " @Override public Observable> call(Observable> elements) {\n" + 68 | " return proxy.mocks(elements, new EvictProvider(true));\n" + 69 | " }\n" + 70 | " } ;;\n" + 71 | " Observable> oCache = proxy.mocks(Observable.>just(new ArrayList()), new EvictProvider(false));\n" + 72 | " return Actions.with(evict, oCache);\n" + 73 | " }\n" + 74 | "\n" + 75 | " public static Actions mocksDynamicKey(final DynamicKey dynamicKey) {\n" + 76 | " final RxProviders proxy = (RxProviders) RxCache.retainedProxy();\n" + 77 | " Actions.Evict evict = new Actions.Evict() {\n" + 78 | " @Override public Observable> call(Observable> elements) {\n" + 79 | " return proxy.mocksDynamicKey(elements, dynamicKey, new EvictDynamicKey(true));\n" + 80 | " }\n" + 81 | " } ;;\n" + 82 | " Observable> oCache = proxy.mocksDynamicKey(Observable.>just(new ArrayList()), dynamicKey, new EvictDynamicKey(false));\n" + 83 | " return Actions.with(evict, oCache);\n" + 84 | " }\n" + 85 | "\n" + 86 | " public static Actions mocksDynamicKeyGroup(final DynamicKeyGroup dynamicKeyGroup) {\n" + 87 | " final RxProviders proxy = (RxProviders) RxCache.retainedProxy();\n" + 88 | " Actions.Evict evict = new Actions.Evict() {\n" + 89 | " @Override public Observable> call(Observable> elements) {\n" + 90 | " return proxy.mocksDynamicKeyGroup(elements, dynamicKeyGroup, new EvictDynamicKeyGroup(true));\n" + 91 | " }\n" + 92 | " } ;;\n" + 93 | " Observable> oCache = proxy.mocksDynamicKeyGroup(Observable.>just(new ArrayList()), dynamicKeyGroup, new EvictDynamicKeyGroup(false));\n" + 94 | " return Actions.with(evict, oCache);\n" + 95 | " }\n" + 96 | "}"); 97 | 98 | 99 | assertAbout(javaSource()).that(source) 100 | .processedWith(new ActionsProcessor()) 101 | .compilesWithoutError() 102 | .and() 103 | .generatesSources(expectedSource); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /compiler/src/test/java/io/rx_cache/Mock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | public class Mock { 20 | final private String message; 21 | 22 | public Mock(String message) { 23 | this.message = message; 24 | } 25 | 26 | public String getMessage() { 27 | return message; 28 | } 29 | 30 | public class InnerMock { 31 | final private String message; 32 | 33 | public InnerMock(String message) { 34 | this.message = message; 35 | } 36 | 37 | public String getMessage() { 38 | return message; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'base' 3 | 4 | sourceCompatibility = 1.7 5 | targetCompatibility = 1.7 6 | 7 | buildscript { 8 | repositories { 9 | mavenCentral() 10 | jcenter() 11 | maven { 12 | url "https://plugins.gradle.org/m2/" 13 | } 14 | } 15 | 16 | dependencies { 17 | classpath "net.ltgt.gradle:gradle-apt-plugin:0.4" 18 | classpath 'net.sf.proguard:proguard-gradle:4.11' 19 | } 20 | } 21 | 22 | apply plugin: 'idea' 23 | apply plugin: "net.ltgt.apt" 24 | apply plugin: 'maven' 25 | 26 | group = 'com.github.VictorAlbertos' 27 | 28 | configurations { 29 | provided 30 | } 31 | 32 | sourceSets { 33 | main { 34 | compileClasspath += configurations.provided 35 | runtimeClasspath += configurations.provided 36 | } 37 | } 38 | 39 | task sourcesJar(type: Jar, dependsOn: classes) { 40 | classifier = 'sources' 41 | from sourceSets.main.allSource 42 | } 43 | 44 | task javadocJar(type: Jar, dependsOn: javadoc) { 45 | classifier = 'javadoc' 46 | from javadoc.destinationDir 47 | } 48 | 49 | artifacts { 50 | archives sourcesJar 51 | archives javadocJar 52 | } 53 | 54 | dependencies { 55 | compile fileTree(dir: 'libs', include: ['*.jar']) 56 | 57 | apt "com.google.dagger:dagger-compiler:2.4" 58 | compile "com.google.dagger:dagger:2.4" 59 | compile "org.glassfish:javax.annotation:10.0-b28" 60 | 61 | compile "io.reactivex:rxjava:1.1.5" 62 | compile 'com.google.code.gson:gson:2.5' 63 | 64 | testCompile "junit:junit:4.12" 65 | } -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/Actionable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.METHOD; 23 | import static java.lang.annotation.RetentionPolicy.CLASS; 24 | 25 | @Target(METHOD) 26 | @Retention(CLASS) 27 | public @interface Actionable { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/DynamicKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | 20 | /** 21 | * Wrapper around the dynamicKey for those providers which need to handle multiple records, 22 | * so they need to provide multiple keys, such us end points with pagination, 23 | * ordering or filtering requirements 24 | */ 25 | public class DynamicKey { 26 | private final Object dynamicKey; 27 | 28 | public DynamicKey(Object dynamicKey) { 29 | this.dynamicKey = dynamicKey; 30 | } 31 | 32 | public Object getDynamicKey() { 33 | return dynamicKey; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/DynamicKeyGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | 20 | /** 21 | * Wrapper around the key and the group for those providers which need to handle multiple records in sections, 22 | * so they need to provide multiple keys organized in groups, such us end points with filtering AND pagination 23 | * requirements 24 | */ 25 | public class DynamicKeyGroup { 26 | private final Object dynamicKey; 27 | private final Object group; 28 | 29 | public DynamicKeyGroup(Object dynamicKey, Object group) { 30 | this.dynamicKey = dynamicKey; 31 | this.group = group; 32 | } 33 | 34 | public Object getDynamicKey() { 35 | return dynamicKey; 36 | } 37 | 38 | public Object getGroup() { 39 | return group; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/EvictDynamicKey.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | 20 | /** 21 | * For those providers which will need to evict the records of an specific DynamicKey. 22 | * @see DynamicKey 23 | */ 24 | public class EvictDynamicKey extends EvictProvider { 25 | public EvictDynamicKey(boolean evict) { 26 | super(evict); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/EvictDynamicKeyGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | /** 20 | * For those providers which will need to evict the records of an specific DynamicKeyGroup. 21 | * @see DynamicKeyGroup 22 | */ 23 | public class EvictDynamicKeyGroup extends EvictDynamicKey { 24 | public EvictDynamicKeyGroup(boolean evict) { 25 | super(evict); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/EvictProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | /** 20 | * For those providers which will need to evict all the records 21 | * @see EvictDynamicKeyGroup 22 | */ 23 | public class EvictProvider { 24 | private final boolean evict; 25 | 26 | public EvictProvider(boolean evict) { 27 | this.evict = evict; 28 | } 29 | 30 | public boolean evict() { 31 | return evict; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/Expirable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | 22 | import static java.lang.annotation.ElementType.METHOD; 23 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 24 | 25 | /** 26 | * 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. 27 | * Provider's record annotated with Expirable annotation and set its value to false will be exclude from the process. 28 | */ 29 | @Target(METHOD) 30 | @Retention(RUNTIME) 31 | public @interface Expirable { 32 | boolean value(); 33 | } 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/LifeCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import static java.lang.annotation.ElementType.METHOD; 24 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 25 | 26 | /** 27 | * If set, it determines the period of time during which the associated cache will be exists. 28 | * If not set, the cache will not be evicted, as long as it has not been explicitly required 29 | * using an Evict class 30 | * @see EvictProvider 31 | */ 32 | @Target(METHOD) 33 | @Retention(RUNTIME) 34 | public @interface LifeCache { 35 | long duration(); 36 | TimeUnit timeUnit(); 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/Migration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * A migration configuration class. 26 | */ 27 | @Retention(RetentionPolicy.RUNTIME) 28 | @Target(ElementType.METHOD) 29 | public @interface Migration { 30 | /** 31 | * Migration number version. The first migration should start with 1 32 | * @return 33 | */ 34 | int version(); 35 | 36 | /** 37 | * Classes to be evicted due to inconsistency properties regarding prior migrations. 38 | * It means when a new field of a class has been added. 39 | * Deleting classes or deleting fields of classes would be handle automatically by RxCache. 40 | * @return 41 | */ 42 | Class[] evictClasses(); 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/Reply.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | /** 20 | * Wrapper to provide the Source used for retrieving the actual data, plus, the actual data. 21 | * You can set this object as return type for your methods if you want to know which was the source 22 | * for an specific data 23 | * @param The actual data to be retrieved encapsulated inside a Provider object 24 | * @see Source 25 | */ 26 | public final class Reply { 27 | private final T data; 28 | private final Source source; 29 | 30 | public Reply(T data, Source source) { 31 | this.data = data; 32 | this.source = source; 33 | } 34 | 35 | public T getData() { 36 | return data; 37 | } 38 | 39 | public Source getSource() { 40 | return source; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/RxCacheException.java: -------------------------------------------------------------------------------- 1 | package io.rx_cache; 2 | 3 | /** 4 | * Exception thrown by RxCache when some error happens. 5 | */ 6 | public final class RxCacheException extends RuntimeException { 7 | 8 | public RxCacheException(String message) { 9 | super(message); 10 | } 11 | 12 | public RxCacheException(String message, Throwable exception) { 13 | super(message, exception); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/SchemeMigration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | /** 25 | * Set of migrations to be performed in order to guaranty data integrity between releases if data model changes has been performed. 26 | * @see Migration 27 | */ 28 | @Retention(RetentionPolicy.RUNTIME) 29 | @Target(ElementType.TYPE) 30 | public @interface SchemeMigration { 31 | Migration[] value(); 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/Source.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache; 18 | 19 | /** 20 | * Provides information about the source used to retrieved the data at a given time 21 | */ 22 | public enum Source { 23 | MEMORY, PERSISTENCE, CLOUD; 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/Locale.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | public interface Locale { 20 | String INVALID_RETURN_TYPE = " needs to return an Observable or Observable>" ; 21 | String NOT_DATA_RETURN_WHEN_CALLING_OBSERVABLE_LOADER = "The Loader provided did not return any data and there is not data to load from the Cache"; 22 | String REPOSITORY_DISK_ADAPTER_CAN_NOT_BE_NULL = "File cache directory can not be null"; 23 | String PERSISTENCE_CAN_NOT_BE_NULL = "Persistence can not be null"; 24 | String NOT_OBSERVABLE_LOADER_FOUND = " requires an instance of type observable"; 25 | String JUST_ONE_INSTANCE = " requires just one instance of type "; 26 | String EVICT_DYNAMIC_KEY_PROVIDED_BUT_NOT_PROVIDED_ANY_DYNAMIC_KEY = " EvictDynamicKey was provided but not was provided any DynamicKey"; 27 | String EVICT_DYNAMIC_KEY_GROUP_PROVIDED_BUT_NOT_PROVIDED_ANY_DYNAMIC_KEY_GROUP = " EvictDynamicKeyGroup was provided but not was provided any Group"; 28 | String RECORD_CAN_NOT_BE_PERSISTED_BECAUSE_WOULD_EXCEED_THRESHOLD_LIMIT = "RxCache -> Record can not be persisted because it would exceed the max limit megabytes settled down"; 29 | String RECORD_CAN_NOT_BE_EVICTED_BECAUSE_NO_ONE_IS_EXPIRABLE = "Records can not be evicted because no one is expirable"; 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/Memory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import java.util.Set; 20 | 21 | public interface Memory { 22 | Record getIfPresent(String key); 23 | void put(String key, Record record); 24 | Set keySet(); 25 | void evict(String key); 26 | void evictAll(); 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/Persistence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import java.util.List; 20 | 21 | /** 22 | * Provides the persistence layer for the cache 23 | * A default implementation which store the objects in disk is supplied: 24 | * @see io.rx_cache.internal.Disk 25 | */ 26 | public interface Persistence { 27 | 28 | /** 29 | * 基于某种数据持久化保存数据 30 | * @param 数据的键 31 | * @param 需要保存的数据 32 | */ 33 | void save(String key, Object object); 34 | 35 | 36 | /** 37 | * 持久化保存纪录 38 | * @param key The key associated with the record to be persisted 39 | * @param record The record to be persisted 40 | */ 41 | void saveRecord(String key, Record record); 42 | 43 | /** 44 | * 通过指定键删除已保存的持久化数据 45 | * @param 被持久化对象的键 46 | */ 47 | void evict(String key); 48 | 49 | /** 50 | * 删除所有数据 51 | */ 52 | void evictAll(); 53 | 54 | /** 55 | * 检索所有数据 56 | */ 57 | List allKeys(); 58 | 59 | /** 60 | * 持久化数据文件的大小,单位MB 61 | */ 62 | int storedMB(); 63 | 64 | /** 65 | * 根据特定key检索数据 66 | * @param 返回数据的class 67 | * @param key 指定key 68 | * @see Record 69 | */ 70 | T retrieve(String key, Class clazz); 71 | 72 | /** 73 | * 更加特殊key检索纪录 74 | * @param The actual data to be persisted encapsulated inside a Record object 75 | * @param key The key associated with the Record to be retrieved from persistence 76 | * @see Record 77 | */ 78 | Record retrieveRecord(String key); 79 | } 80 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/Record.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import java.util.Collection; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | import io.rx_cache.Source; 24 | 25 | /** 26 | * Wrapper around the actual data in order to know if its life time has been expired 27 | * @param The actual data 28 | */ 29 | public final class Record { 30 | private Source source; 31 | private final T data; 32 | private final long timeAtWhichWasPersisted; 33 | private final String dataClassName, dataCollectionClassName, dataKeyMapClassName; 34 | private final Boolean isExpirable; 35 | 36 | //LifeTime requires to be stored to be evicted by EvictExpiredRecordsTask when no life time is available without a config provider 37 | private long lifeTime; 38 | 39 | //Required by EvictExpirableRecordsPersistence task 40 | private transient float sizeOnMb; 41 | 42 | //VisibleForTesting 43 | Record(T data) { 44 | this(data, true, 0); 45 | } 46 | 47 | public Record(T data, Boolean isExpirable, long lifeTime) { 48 | this.data = data; 49 | this.isExpirable = isExpirable; 50 | this.lifeTime = lifeTime; 51 | this.timeAtWhichWasPersisted = System.currentTimeMillis(); 52 | this.source = Source.MEMORY; 53 | 54 | boolean isList = Collection.class.isAssignableFrom(data.getClass()); 55 | boolean isArray = data.getClass().isArray(); 56 | boolean isMap = Map.class.isAssignableFrom(data.getClass()); 57 | 58 | if (isList) { 59 | dataKeyMapClassName = null; 60 | List list = (List) data; 61 | if (list.size() > 0) { 62 | dataCollectionClassName = data.getClass().getName(); 63 | dataClassName = list.get(0).getClass().getName(); 64 | } else { 65 | dataClassName = null; 66 | dataCollectionClassName = null; 67 | } 68 | } else if (isArray) { 69 | dataKeyMapClassName = null; 70 | Object[] array = (Object[]) data; 71 | if (array.length > 0) { 72 | dataClassName = (array)[0].getClass().getName(); 73 | dataCollectionClassName = data.getClass().getName(); 74 | } else { 75 | dataClassName = null; 76 | dataCollectionClassName = null; 77 | } 78 | } else if (isMap) { 79 | Map map = ((Map) data); 80 | if (map.size() > 0) { 81 | Map.Entry object = (Map.Entry) map.entrySet().iterator().next(); 82 | dataClassName = object.getValue().getClass().getName(); 83 | dataKeyMapClassName = object.getKey().getClass().getName(); 84 | dataCollectionClassName = data.getClass().getName(); 85 | } else { 86 | dataClassName = null; 87 | dataCollectionClassName = null; 88 | dataKeyMapClassName = null; 89 | } 90 | } else { 91 | dataKeyMapClassName = null; 92 | dataClassName = data.getClass().getName(); 93 | dataCollectionClassName = null; 94 | } 95 | } 96 | 97 | public Source getSource() { 98 | return source; 99 | } 100 | 101 | public void setSource(Source source) { 102 | this.source = source; 103 | } 104 | 105 | public T getData() { 106 | return data; 107 | } 108 | 109 | public long getTimeAtWhichWasPersisted() { 110 | return timeAtWhichWasPersisted; 111 | } 112 | 113 | public long getLifeTime() { 114 | return lifeTime; 115 | } 116 | 117 | public void setLifeTime(long lifeTime) { 118 | this.lifeTime = lifeTime; 119 | } 120 | 121 | public float getSizeOnMb() { 122 | return sizeOnMb; 123 | } 124 | 125 | public void setSizeOnMb(float sizeOnMb) { 126 | this.sizeOnMb = sizeOnMb; 127 | } 128 | 129 | public String getDataClassName() { 130 | return dataClassName; 131 | } 132 | 133 | String getDataCollectionClassName() { 134 | return dataCollectionClassName; 135 | } 136 | 137 | String getDataKeyMapClassName() { 138 | return dataKeyMapClassName; 139 | } 140 | 141 | public Boolean isExpirable() { 142 | return isExpirable == null ? true : isExpirable; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/RxCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import java.io.File; 20 | import java.lang.reflect.Proxy; 21 | import java.security.InvalidParameterException; 22 | 23 | import io.rx_cache.internal.cache.TwoLayersCache; 24 | 25 | public final class RxCache { 26 | private final Builder builder; 27 | private static Object retainedProxy; 28 | 29 | private RxCache(Builder builder) { 30 | this.builder = builder; 31 | } 32 | 33 | public T using(final Class classProviders) { 34 | ProxyProviders proxyProviders = DaggerRxCacheComponent.builder() 35 | .rxCacheModule(new RxCacheModule(builder.cacheDirectory, builder.useExpiredDataIfLoaderNotAvailable, builder.maxMBPersistenceCache, classProviders)) 36 | .build().proxyRepository(); 37 | 38 | T proxy = (T) Proxy.newProxyInstance( 39 | classProviders.getClassLoader(), 40 | new Class[]{classProviders}, 41 | proxyProviders); 42 | 43 | retainedProxy = proxy; 44 | 45 | return proxy; 46 | } 47 | 48 | /** 49 | * To be able to access from ActionsProviders auto-generated class. 50 | * @return the current instance of the implemented providers interface. 51 | */ 52 | public static Object retainedProxy() { 53 | return retainedProxy; 54 | } 55 | 56 | /** 57 | * Builder for building an specific RxCache instance 58 | */ 59 | public static class Builder { 60 | private boolean useExpiredDataIfLoaderNotAvailable; 61 | private Integer maxMBPersistenceCache; 62 | private File cacheDirectory; 63 | 64 | /** 65 | * If true RxCache will serve Records already expired, instead of evict them and throw an exception 66 | * If not supplied, false will be the default option 67 | * @return BuilderRxCache The builder of RxCache 68 | */ 69 | public Builder useExpiredDataIfLoaderNotAvailable(boolean useExpiredDataIfLoaderNotAvailable) { 70 | this.useExpiredDataIfLoaderNotAvailable = useExpiredDataIfLoaderNotAvailable; 71 | return this; 72 | } 73 | 74 | /** 75 | * Sets the max memory in megabytes for all stored records on persistence layer 76 | * If not supplied, 100 megabytes will be the default option 77 | * @return BuilderRxCache The builder of RxCache 78 | */ 79 | public Builder setMaxMBPersistenceCache(Integer maxMgPersistenceCache) { 80 | this.maxMBPersistenceCache = maxMgPersistenceCache; 81 | return this; 82 | } 83 | 84 | /** 85 | * Sets the File cache system used by Cache 86 | * @param cacheDirectory The File system used by the persistence implementation of Disk 87 | * @see TwoLayersCache 88 | */ 89 | public RxCache persistence(File cacheDirectory) { 90 | if (cacheDirectory == null) 91 | throw new InvalidParameterException(Locale.REPOSITORY_DISK_ADAPTER_CAN_NOT_BE_NULL); 92 | 93 | this.cacheDirectory = cacheDirectory; 94 | 95 | return new RxCache(this); 96 | } 97 | 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/RxCacheComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import javax.inject.Singleton; 20 | 21 | import dagger.Component; 22 | 23 | @Component(modules = RxCacheModule.class) 24 | @Singleton 25 | interface RxCacheComponent { 26 | ProxyProviders proxyRepository(); 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/RxCacheModule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import java.io.File; 20 | 21 | import javax.inject.Singleton; 22 | 23 | import dagger.Module; 24 | import dagger.Provides; 25 | import io.rx_cache.internal.cache.memory.ReferenceMapMemory; 26 | 27 | @Module 28 | public final class RxCacheModule { 29 | private final File cacheDirectory; 30 | private final boolean useExpiredDataIfLoaderNotAvailable; 31 | private final Integer maxMgPersistenceCache; 32 | private final Class classProviders; 33 | 34 | public RxCacheModule(File cacheDirectory, Boolean useExpiredDataIfLoaderNotAvailable, Integer maxMgPersistenceCache, Class classProviders) { 35 | this.cacheDirectory = cacheDirectory; 36 | this.useExpiredDataIfLoaderNotAvailable = useExpiredDataIfLoaderNotAvailable; 37 | this.maxMgPersistenceCache = maxMgPersistenceCache; 38 | this.classProviders = classProviders; 39 | } 40 | 41 | @Singleton @Provides File provideCacheDirectory() { 42 | return cacheDirectory; 43 | } 44 | 45 | @Singleton @Provides Persistence providePersistence(Disk disk) { 46 | return disk; 47 | } 48 | 49 | @Singleton @Provides Boolean useExpiredDataIfLoaderNotAvailable() { 50 | return useExpiredDataIfLoaderNotAvailable; 51 | } 52 | 53 | @Singleton @Provides Memory provideMemory() { 54 | return new ReferenceMapMemory(); 55 | } 56 | 57 | @Singleton @Provides Integer maxMbPersistenceCache() { 58 | return maxMgPersistenceCache != null ? maxMgPersistenceCache : 100; 59 | } 60 | 61 | @Singleton @Provides Class provideClassProviders() { 62 | return classProviders; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/Action.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import io.rx_cache.internal.Persistence; 23 | import io.rx_cache.internal.Memory; 24 | 25 | abstract class Action { 26 | private static final String PREFIX_DYNAMIC_KEY = "$d$d$d$"; 27 | private static final String PREFIX_DYNAMIC_KEY_GROUP = "$g$g$g$"; 28 | 29 | protected final Memory memory; 30 | protected final Persistence persistence; 31 | 32 | public Action(Memory memory, Persistence persistence) { 33 | this.memory = memory; 34 | this.persistence = persistence; 35 | } 36 | 37 | protected String composeKey(String providerKey, String dynamicKey, String dynamicKeyGroup) { 38 | return providerKey + PREFIX_DYNAMIC_KEY + dynamicKey + PREFIX_DYNAMIC_KEY_GROUP + dynamicKeyGroup; 39 | } 40 | 41 | protected List getKeysOnMemoryMatchingProviderKey(String providerKey) { 42 | List keysMatchingProviderKey = new ArrayList<>(); 43 | 44 | for (String composedKeyMemory : memory.keySet()) { 45 | final String keyPartProviderMemory = composedKeyMemory.substring(0, composedKeyMemory.lastIndexOf(PREFIX_DYNAMIC_KEY)); 46 | 47 | if (providerKey.equals(keyPartProviderMemory)) 48 | keysMatchingProviderKey.add(composedKeyMemory); 49 | } 50 | 51 | return keysMatchingProviderKey; 52 | } 53 | 54 | protected List getKeysOnMemoryMatchingDynamicKey(String providerKey, String dynamicKey) { 55 | List keysMatchingDynamicKey = new ArrayList<>(); 56 | 57 | String composedProviderKeyAndDynamicKey = providerKey + PREFIX_DYNAMIC_KEY + dynamicKey; 58 | 59 | for (String composedKeyMemory : memory.keySet()) { 60 | final String keyPartProviderAndDynamicKeyMemory = composedKeyMemory.substring(0, composedKeyMemory.lastIndexOf(PREFIX_DYNAMIC_KEY_GROUP)); 61 | 62 | if (composedProviderKeyAndDynamicKey.equals(keyPartProviderAndDynamicKeyMemory)) 63 | keysMatchingDynamicKey.add(composedKeyMemory); 64 | 65 | } 66 | 67 | return keysMatchingDynamicKey; 68 | } 69 | 70 | protected String getKeyOnMemoryMatchingDynamicKeyGroup(String providerKey, String dynamicKey, String dynamicKeyGroup) { 71 | return composeKey(providerKey, dynamicKey, dynamicKeyGroup); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/EvictExpirableRecordsPersistence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | import java.util.List; 20 | 21 | import javax.inject.Inject; 22 | import javax.inject.Singleton; 23 | 24 | import io.rx_cache.internal.Locale; 25 | import io.rx_cache.internal.Memory; 26 | import io.rx_cache.internal.Persistence; 27 | import io.rx_cache.internal.Record; 28 | import rx.Observable; 29 | import rx.Subscriber; 30 | import rx.functions.Action1; 31 | import rx.schedulers.Schedulers; 32 | 33 | @Singleton 34 | public class EvictExpirableRecordsPersistence extends Action { 35 | private final Integer maxMgPersistenceCache; 36 | private static final float PERCENTAGE_MEMORY_STORED_TO_START = 0.95f; 37 | //VisibleForTesting 38 | public static final float PERCENTAGE_MEMORY_STORED_TO_STOP = 0.7f; 39 | private final Observable oEvictingTask; 40 | private boolean couldBeExpirableRecords; 41 | 42 | @Inject public EvictExpirableRecordsPersistence(Memory memory, Persistence persistence, Integer maxMgPersistenceCache) { 43 | super(memory, persistence); 44 | this.maxMgPersistenceCache = maxMgPersistenceCache; 45 | this.couldBeExpirableRecords = true; 46 | this.oEvictingTask = oEvictingTask(); 47 | } 48 | 49 | Observable startTaskIfNeeded() { 50 | oEvictingTask.subscribe(); 51 | return oEvictingTask; 52 | } 53 | 54 | private Observable oEvictingTask() { 55 | Observable oEvictingTask = Observable.create(new Observable.OnSubscribe() { 56 | @Override public void call(Subscriber subscriber) { 57 | if (!couldBeExpirableRecords) { 58 | subscriber.onNext(Locale.RECORD_CAN_NOT_BE_EVICTED_BECAUSE_NO_ONE_IS_EXPIRABLE); 59 | subscriber.onCompleted(); 60 | return; 61 | } 62 | 63 | int storedMB = persistence.storedMB(); 64 | 65 | if (!reachedPercentageMemoryToStart(storedMB)) { 66 | subscriber.onCompleted(); 67 | return; 68 | } 69 | 70 | List allKeys = persistence.allKeys(); 71 | 72 | float releasedMBSoFar = 0f; 73 | for (String key : allKeys) { 74 | if (reachedPercentageMemoryToStop(storedMB, releasedMBSoFar)) { 75 | break; 76 | } 77 | 78 | Record record = persistence.retrieveRecord(key); 79 | if (record == null) continue; 80 | if (!record.isExpirable()) continue; 81 | 82 | persistence.evict(key); 83 | subscriber.onNext(key); 84 | 85 | releasedMBSoFar += record.getSizeOnMb(); 86 | } 87 | 88 | couldBeExpirableRecords = reachedPercentageMemoryToStop(storedMB, releasedMBSoFar); 89 | subscriber.onCompleted(); 90 | } 91 | }).subscribeOn((Schedulers.io())) 92 | .observeOn(Schedulers.io()) 93 | .doOnError(new Action1() { 94 | @Override public void call(Throwable throwable) { 95 | throwable.printStackTrace(); 96 | } 97 | }); 98 | return oEvictingTask.share(); 99 | } 100 | 101 | private boolean reachedPercentageMemoryToStop(int storedMBWhenStarted, float releasedMBSoFar) { 102 | float currentStoredMB = storedMBWhenStarted - releasedMBSoFar; 103 | float requiredStoredMBToStop = maxMgPersistenceCache * PERCENTAGE_MEMORY_STORED_TO_STOP; 104 | return currentStoredMB <= requiredStoredMBToStop; 105 | } 106 | 107 | private boolean reachedPercentageMemoryToStart(int storedMB) { 108 | int requiredStoredMBToStart = (int) (maxMgPersistenceCache * PERCENTAGE_MEMORY_STORED_TO_START); 109 | return storedMB >= requiredStoredMBToStart; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/EvictExpiredRecordsPersistence.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | import java.util.List; 20 | 21 | import javax.inject.Inject; 22 | import javax.inject.Singleton; 23 | 24 | import io.rx_cache.internal.Record; 25 | import io.rx_cache.internal.Memory; 26 | import io.rx_cache.internal.Persistence; 27 | import rx.Observable; 28 | import rx.functions.Func0; 29 | 30 | @Singleton 31 | public final class EvictExpiredRecordsPersistence extends Action { 32 | private final HasRecordExpired hasRecordExpired; 33 | 34 | @Inject public EvictExpiredRecordsPersistence(Memory memory, Persistence persistence, HasRecordExpired hasRecordExpired) { 35 | super(memory, persistence); 36 | this.hasRecordExpired = hasRecordExpired; 37 | } 38 | 39 | public Observable startEvictingExpiredRecords() { 40 | return Observable.defer(new Func0>() { 41 | @Override public Observable call() { 42 | List allKeys = persistence.allKeys(); 43 | 44 | for (String key : allKeys) { 45 | Record record = persistence.retrieveRecord(key); 46 | if (record != null && hasRecordExpired.hasRecordExpired(record)) { 47 | persistence.evict(key); 48 | } 49 | } 50 | 51 | return Observable.just(null); 52 | } 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/EvictRecord.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | 20 | import java.util.List; 21 | 22 | import javax.inject.Inject; 23 | 24 | import io.rx_cache.internal.Memory; 25 | import io.rx_cache.internal.Persistence; 26 | 27 | public final class EvictRecord extends Action { 28 | 29 | @Inject public EvictRecord(Memory memory, Persistence persistence) { 30 | super(memory, persistence); 31 | } 32 | 33 | void evictRecordsMatchingProviderKey(String providerKey) { 34 | List keysMatchingKeyProvider = getKeysOnMemoryMatchingProviderKey(providerKey); 35 | 36 | for (String keyMatchingKeyProvider : keysMatchingKeyProvider) { 37 | memory.evict(keyMatchingKeyProvider); 38 | persistence.evict(keyMatchingKeyProvider); 39 | } 40 | } 41 | 42 | void evictRecordsMatchingDynamicKey(String providerKey, String dynamicKey) { 43 | List keysMatchingDynamicKey = getKeysOnMemoryMatchingDynamicKey(providerKey, dynamicKey); 44 | 45 | for (String keyMatchingDynamicKey : keysMatchingDynamicKey) { 46 | memory.evict(keyMatchingDynamicKey); 47 | persistence.evict(keyMatchingDynamicKey); 48 | } 49 | } 50 | 51 | void evictRecordMatchingDynamicKeyGroup(String providerKey, String dynamicKey, String dynamicKeyGroup) { 52 | String composedKey = getKeyOnMemoryMatchingDynamicKeyGroup(providerKey, dynamicKey, dynamicKeyGroup); 53 | 54 | memory.evict(composedKey); 55 | persistence.evict(composedKey); 56 | } 57 | 58 | //VisibleForTesting 59 | void mockMemoryDestroyed() { 60 | memory.evictAll(); 61 | } 62 | 63 | void evictAll() { 64 | memory.evictAll(); 65 | persistence.evictAll(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/GetDeepCopy.java: -------------------------------------------------------------------------------- 1 | package io.rx_cache.internal.cache; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.internal.$Gson$Types; 5 | 6 | import java.lang.reflect.Type; 7 | import java.util.Collection; 8 | import java.util.Map; 9 | 10 | import javax.inject.Inject; 11 | 12 | import io.rx_cache.internal.Memory; 13 | import io.rx_cache.internal.Persistence; 14 | 15 | public class GetDeepCopy extends Action { 16 | 17 | @Inject public GetDeepCopy(Memory memory, Persistence persistence) { 18 | super(memory, persistence); 19 | } 20 | 21 | public T deepCopy(T data) { 22 | try { 23 | Class classData = data.getClass(); 24 | boolean isCollection = Collection.class.isAssignableFrom(classData); 25 | boolean isArray = classData.isArray(); 26 | boolean isMap = Map.class.isAssignableFrom(classData); 27 | 28 | if (isCollection) { 29 | return getDeepCopyCollection(data); 30 | } else if (isArray) { 31 | return getDeepCopyArray(data); 32 | } else if (isMap) { 33 | return getDeepCopyMap(data); 34 | } else { 35 | return getDeepCopyObject(data); 36 | } 37 | } catch (Exception ignore) { 38 | return data; 39 | } 40 | } 41 | 42 | private T getDeepCopyCollection(T data) { 43 | Collection collection = (Collection) data; 44 | if (collection.isEmpty()) return data; 45 | 46 | Class classData = data.getClass(); 47 | Class classItemCollection = collection.toArray()[0].getClass(); 48 | Type typeCollection = $Gson$Types.newParameterizedTypeWithOwner(null, classData, classItemCollection); 49 | String dataString = new Gson().toJson(data); 50 | 51 | return new Gson().fromJson(dataString, typeCollection); 52 | } 53 | 54 | private T getDeepCopyArray(T data) { 55 | T[] array = (T[]) data; 56 | if (array.length == 0) return data; 57 | 58 | Class classItemArray = array[0].getClass(); 59 | Type typeRecord = $Gson$Types.arrayOf(classItemArray); 60 | String dataString = new Gson().toJson(data); 61 | 62 | return new Gson().fromJson(dataString, typeRecord); 63 | } 64 | 65 | private T getDeepCopyMap(T data) { 66 | Map map = (Map) data; 67 | if (map.isEmpty()) return data; 68 | 69 | Class classData = data.getClass(); 70 | Class classValueMap = map.values().toArray()[0].getClass(); 71 | Class classKeyMap = map.keySet().toArray()[0].getClass(); 72 | Type typeMap = $Gson$Types.newParameterizedTypeWithOwner(null, classData, classKeyMap, classValueMap); 73 | String dataString = new Gson().toJson(data); 74 | 75 | return new Gson().fromJson(dataString, typeMap); 76 | } 77 | 78 | private T getDeepCopyObject(T data) { 79 | if (data == null) return data; 80 | 81 | Class classData = data.getClass(); 82 | Type type = $Gson$Types.newParameterizedTypeWithOwner(null, classData); 83 | String dataString = new Gson().toJson(data); 84 | 85 | return new Gson().fromJson(dataString, type); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/HasRecordExpired.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | import javax.inject.Inject; 20 | 21 | import io.rx_cache.internal.Record; 22 | 23 | public class HasRecordExpired { 24 | 25 | public @Inject HasRecordExpired() {} 26 | 27 | public boolean hasRecordExpired(Record record) { 28 | long now = System.currentTimeMillis(); 29 | long expirationDate = record.getTimeAtWhichWasPersisted() + record.getLifeTime(); 30 | return record.getLifeTime() != 0 && now > expirationDate; 31 | } 32 | } -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/RetrieveRecord.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | import javax.inject.Inject; 20 | 21 | import io.rx_cache.internal.Persistence; 22 | import io.rx_cache.internal.Record; 23 | import io.rx_cache.Source; 24 | import io.rx_cache.internal.Memory; 25 | 26 | public final class RetrieveRecord extends Action { 27 | private final EvictRecord evictRecord; 28 | private final HasRecordExpired hasRecordExpired; 29 | 30 | @Inject public RetrieveRecord(Memory memory, Persistence persistence, EvictRecord evictRecord, HasRecordExpired hasRecordExpired) { 31 | super(memory, persistence); 32 | this.evictRecord = evictRecord; 33 | this.hasRecordExpired = hasRecordExpired; 34 | } 35 | 36 | Record retrieveRecord(String providerKey, String dynamicKey, String dynamicKeyGroup, boolean useExpiredDataIfLoaderNotAvailable, long lifeTime) { 37 | String composedKey = composeKey(providerKey, dynamicKey, dynamicKeyGroup); 38 | 39 | Record record = memory.getIfPresent(composedKey); 40 | 41 | if (record != null) { 42 | record.setSource(Source.MEMORY); 43 | } else { 44 | try { 45 | record = persistence.retrieveRecord(composedKey); 46 | record.setSource(Source.PERSISTENCE); 47 | memory.put(composedKey, record); 48 | } catch (Exception ignore) { 49 | return null; 50 | } 51 | } 52 | 53 | record.setLifeTime(lifeTime); 54 | 55 | if (hasRecordExpired.hasRecordExpired(record)) { 56 | if (!dynamicKeyGroup.isEmpty()) evictRecord.evictRecordMatchingDynamicKeyGroup(providerKey, dynamicKey, dynamicKeyGroup); 57 | else if (!dynamicKey.isEmpty()) evictRecord.evictRecordsMatchingDynamicKey(providerKey, dynamicKey); 58 | else evictRecord.evictRecordsMatchingProviderKey(providerKey); 59 | 60 | return useExpiredDataIfLoaderNotAvailable ? record : null; 61 | } 62 | 63 | return record; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/SaveRecord.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | 20 | import javax.inject.Inject; 21 | 22 | import io.rx_cache.internal.Persistence; 23 | import io.rx_cache.internal.Record; 24 | import io.rx_cache.internal.Locale; 25 | import io.rx_cache.internal.Memory; 26 | 27 | public final class SaveRecord extends Action { 28 | private final Integer maxMgPersistenceCache; 29 | private final EvictExpirableRecordsPersistence evictExpirableRecordsPersistence; 30 | 31 | @Inject public SaveRecord(Memory memory, Persistence persistence, Integer maxMgPersistenceCache, EvictExpirableRecordsPersistence evictExpirableRecordsPersistence) { 32 | super(memory, persistence); 33 | this.maxMgPersistenceCache = maxMgPersistenceCache; 34 | this.evictExpirableRecordsPersistence = evictExpirableRecordsPersistence; 35 | } 36 | 37 | void save(String providerKey, String dynamicKey, String dynamicKeyGroup, Object data, long lifeTime, boolean isExpirable) { 38 | String composedKey = composeKey(providerKey, dynamicKey, dynamicKeyGroup); 39 | 40 | Record record = new Record(data, isExpirable, lifeTime); 41 | memory.put(composedKey, record); 42 | 43 | if (persistence.storedMB() >= maxMgPersistenceCache) { 44 | System.out.println(Locale.RECORD_CAN_NOT_BE_PERSISTED_BECAUSE_WOULD_EXCEED_THRESHOLD_LIMIT); 45 | } else { 46 | persistence.saveRecord(composedKey, record); 47 | } 48 | 49 | evictExpirableRecordsPersistence.startTaskIfNeeded(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/TwoLayersCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | 20 | import javax.inject.Inject; 21 | import javax.inject.Singleton; 22 | 23 | import io.rx_cache.internal.Record; 24 | 25 | @Singleton 26 | public final class TwoLayersCache { 27 | private final EvictRecord evictRecord; 28 | private final RetrieveRecord retrieveRecord; 29 | private final SaveRecord saveRecord; 30 | 31 | @Inject public TwoLayersCache(EvictRecord evictRecord, RetrieveRecord retrieveRecord, SaveRecord saveRecord) { 32 | this.evictRecord = evictRecord; 33 | this.retrieveRecord = retrieveRecord; 34 | this.saveRecord = saveRecord; 35 | } 36 | 37 | public Record retrieve(String providerKey, String dynamicKey, String dynamicKeyGroup, boolean useExpiredDataIfLoaderNotAvailable, long lifeTime) { 38 | return retrieveRecord.retrieveRecord(providerKey, dynamicKey, dynamicKeyGroup, useExpiredDataIfLoaderNotAvailable, lifeTime); 39 | } 40 | 41 | public void save(String providerKey, String dynamicKey, String dynamicKeyGroup, Object data, long lifeTime, boolean isExpirable) { 42 | saveRecord.save(providerKey, dynamicKey, dynamicKeyGroup, data, lifeTime, isExpirable); 43 | } 44 | 45 | public void evictProviderKey(final String providerKey) { 46 | evictRecord.evictRecordsMatchingProviderKey(providerKey); 47 | } 48 | 49 | public void evictDynamicKey(String providerKey, String dynamicKey) { 50 | evictRecord.evictRecordsMatchingDynamicKey(providerKey, dynamicKey); 51 | } 52 | 53 | public void evictDynamicKeyGroup(String key, String dynamicKey, String dynamicKeyGroup) { 54 | evictRecord.evictRecordMatchingDynamicKeyGroup(key, dynamicKey, dynamicKeyGroup); 55 | } 56 | 57 | public void evictAll() { 58 | evictRecord.evictAll(); 59 | } 60 | 61 | //Exists for testing purposes 62 | public void mockMemoryDestroyed() { 63 | evictRecord.mockMemoryDestroyed(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/ReferenceMapMemory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache.memory; 18 | 19 | import java.util.Collections; 20 | import java.util.Iterator; 21 | import java.util.Map; 22 | import java.util.Set; 23 | 24 | import io.rx_cache.internal.Memory; 25 | import io.rx_cache.internal.Record; 26 | import io.rx_cache.internal.cache.memory.apache.ReferenceMap; 27 | 28 | public class ReferenceMapMemory implements Memory { 29 | private final Map referenceMap; 30 | 31 | public ReferenceMapMemory() { 32 | referenceMap = Collections.synchronizedMap(new ReferenceMap()); 33 | } 34 | 35 | @Override public Record getIfPresent(String key) { 36 | return referenceMap.get(key); 37 | } 38 | 39 | @Override public void put(String key, Record record) { 40 | referenceMap.put(key, record); 41 | } 42 | 43 | @Override public Set keySet() { 44 | return referenceMap.keySet(); 45 | } 46 | 47 | @Override public void evict(String key) { 48 | referenceMap.remove(key); 49 | } 50 | 51 | @Override public void evictAll() { 52 | Set keys = referenceMap.keySet(); 53 | 54 | synchronized (referenceMap) { 55 | Iterator iterator = keys.iterator(); 56 | while (iterator.hasNext()) { 57 | iterator.next(); 58 | iterator.remove(); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/AbstractEmptyIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | import java.util.NoSuchElementException; 21 | 22 | /** 23 | * Provides an implementation of an empty iterator. 24 | * 25 | * @since 3.1 26 | * @version $Id: AbstractEmptyIterator.java 1477802 2013-04-30 20:01:28Z tn $ 27 | */ 28 | abstract class AbstractEmptyIterator { 29 | 30 | /** 31 | * Constructor. 32 | */ 33 | protected AbstractEmptyIterator() { 34 | super(); 35 | } 36 | 37 | public boolean hasNext() { 38 | return false; 39 | } 40 | 41 | public E next() { 42 | throw new NoSuchElementException("Iterator contains no elements"); 43 | } 44 | 45 | public boolean hasPrevious() { 46 | return false; 47 | } 48 | 49 | public E previous() { 50 | throw new NoSuchElementException("Iterator contains no elements"); 51 | } 52 | 53 | public int nextIndex() { 54 | return 0; 55 | } 56 | 57 | public int previousIndex() { 58 | return -1; 59 | } 60 | 61 | public void add(final E obj) { 62 | throw new UnsupportedOperationException("add() not supported for empty Iterator"); 63 | } 64 | 65 | public void set(final E obj) { 66 | throw new IllegalStateException("Iterator contains no elements"); 67 | } 68 | 69 | public void remove() { 70 | throw new IllegalStateException("Iterator contains no elements"); 71 | } 72 | 73 | public void reset() { 74 | // do nothing 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/AbstractEmptyMapIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package io.rx_cache.internal.cache.memory.apache; 18 | 19 | /** 20 | * Provides an implementation of an empty map iterator. 21 | * 22 | * @since 4.0 23 | * @version $Id: AbstractEmptyMapIterator.java 1477802 2013-04-30 20:01:28Z tn $ 24 | */ 25 | public abstract class AbstractEmptyMapIterator extends AbstractEmptyIterator { 26 | 27 | /** 28 | * Create a new AbstractEmptyMapIterator. 29 | */ 30 | public AbstractEmptyMapIterator() { 31 | super(); 32 | } 33 | 34 | public K getKey() { 35 | throw new IllegalStateException("Iterator contains no elements"); 36 | } 37 | 38 | public V getValue() { 39 | throw new IllegalStateException("Iterator contains no elements"); 40 | } 41 | 42 | public V setValue(final V value) { 43 | throw new IllegalStateException("Iterator contains no elements"); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/AbstractKeyValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | /** 21 | * Abstract pair class to assist with creating KeyValue 22 | * and {@link java.util.Map.Entry Map.Entry} implementations. 23 | * 24 | * @since 3.0 25 | * @version $Id: AbstractKeyValue.java 1477753 2013-04-30 18:24:24Z tn $ 26 | */ 27 | public abstract class AbstractKeyValue implements KeyValue { 28 | 29 | /** The key */ 30 | private K key; 31 | /** The value */ 32 | private V value; 33 | 34 | /** 35 | * Constructs a new pair with the specified key and given value. 36 | * 37 | * @param key the key for the entry, may be null 38 | * @param value the value for the entry, may be null 39 | */ 40 | protected AbstractKeyValue(final K key, final V value) { 41 | super(); 42 | this.key = key; 43 | this.value = value; 44 | } 45 | 46 | /** 47 | * Gets the key from the pair. 48 | * 49 | * @return the key 50 | */ 51 | public K getKey() { 52 | return key; 53 | } 54 | 55 | protected K setKey(K key) { 56 | final K old = this.key; 57 | this.key = key; 58 | return old; 59 | } 60 | 61 | /** 62 | * Gets the value from the pair. 63 | * 64 | * @return the value 65 | */ 66 | public V getValue() { 67 | return value; 68 | } 69 | 70 | protected V setValue(V value) { 71 | final V old = this.value; 72 | this.value = value; 73 | return old; 74 | } 75 | 76 | /** 77 | * Gets a debugging String view of the pair. 78 | * 79 | * @return a String view of the entry 80 | */ 81 | @Override 82 | public String toString() { 83 | return new StringBuilder() 84 | .append(getKey()) 85 | .append('=') 86 | .append(getValue()) 87 | .toString(); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/AbstractMapEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | import java.util.Map; 21 | 22 | /** 23 | * Abstract Pair class to assist with creating correct 24 | * {@link java.util.Map.Entry Map.Entry} implementations. 25 | * 26 | * @since 3.0 27 | * @version $Id: AbstractMapEntry.java 1477753 2013-04-30 18:24:24Z tn $ 28 | */ 29 | public abstract class AbstractMapEntry extends AbstractKeyValue implements Map.Entry { 30 | 31 | /** 32 | * Constructs a new entry with the given key and given value. 33 | * 34 | * @param key the key for the entry, may be null 35 | * @param value the value for the entry, may be null 36 | */ 37 | protected AbstractMapEntry(final K key, final V value) { 38 | super(key, value); 39 | } 40 | 41 | // Map.Entry interface 42 | //------------------------------------------------------------------------- 43 | /** 44 | * Sets the value stored in this Map.Entry. 45 | *

46 | * This Map.Entry is not connected to a Map, so only the 47 | * local data is changed. 48 | * 49 | * @param value the new value 50 | * @return the previous value 51 | */ 52 | @Override 53 | public V setValue(final V value) { 54 | return super.setValue(value); 55 | } 56 | 57 | /** 58 | * Compares this Map.Entry with another Map.Entry. 59 | *

60 | * Implemented per API documentation of {@link java.util.Map.Entry#equals(Object)} 61 | * 62 | * @param obj the object to compare to 63 | * @return true if equal key and value 64 | */ 65 | @Override 66 | public boolean equals(final Object obj) { 67 | if (obj == this) { 68 | return true; 69 | } 70 | if (obj instanceof Map.Entry == false) { 71 | return false; 72 | } 73 | final Map.Entry other = (Map.Entry) obj; 74 | return 75 | (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) && 76 | (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())); 77 | } 78 | 79 | /** 80 | * Gets a hashCode compatible with the equals method. 81 | *

82 | * Implemented per API documentation of {@link java.util.Map.Entry#hashCode()} 83 | * 84 | * @return a suitable hash code 85 | */ 86 | @Override 87 | public int hashCode() { 88 | return (getKey() == null ? 0 : getKey().hashCode()) ^ 89 | (getValue() == null ? 0 : getValue().hashCode()); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/DefaultMapEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | import java.util.Map; 21 | 22 | 23 | /** 24 | * A restricted implementation of {@link java.util.Map.Entry Map.Entry} that prevents 25 | * the {@link java.util.Map.Entry Map.Entry} contract from being broken. 26 | * 27 | * @since 3.0 28 | * @version $Id: DefaultMapEntry.java 1533984 2013-10-20 21:12:51Z tn $ 29 | */ 30 | public final class DefaultMapEntry extends AbstractMapEntry { 31 | 32 | /** 33 | * Constructs a new entry with the specified key and given value. 34 | * 35 | * @param key the key for the entry, may be null 36 | * @param value the value for the entry, may be null 37 | */ 38 | public DefaultMapEntry(final K key, final V value) { 39 | super(key, value); 40 | } 41 | 42 | /** 43 | * Constructs a new entry from the specified KeyValue. 44 | * 45 | * @param pair the pair to copy, must not be null 46 | * @throws NullPointerException if the entry is null 47 | */ 48 | public DefaultMapEntry(final KeyValue pair) { 49 | super(pair.getKey(), pair.getValue()); 50 | } 51 | 52 | /** 53 | * Constructs a new entry from the specified Map.Entry. 54 | * 55 | * @param entry the entry to copy, must not be null 56 | * @throws NullPointerException if the entry is null 57 | */ 58 | public DefaultMapEntry(final Map.Entry entry) { 59 | super(entry.getKey(), entry.getValue()); 60 | } 61 | 62 | } 63 | 64 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/EmptyIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | import java.util.Iterator; 21 | 22 | /** 23 | * Provides an implementation of an empty iterator. 24 | *

25 | * This class provides an implementation of an empty iterator. 26 | * This class provides for binary compatibility between Commons Collections 27 | * 2.1.1 and 3.1 due to issues with IteratorUtils. 28 | * 29 | * @since 2.1.1 and 3.1 30 | * @version $Id: EmptyIterator.java 1543955 2013-11-20 21:23:53Z tn $ 31 | */ 32 | public class EmptyIterator extends AbstractEmptyIterator implements ResettableIterator { 33 | 34 | /** 35 | * Singleton instance of the iterator. 36 | * @since 3.1 37 | */ 38 | @SuppressWarnings("rawtypes") 39 | public static final ResettableIterator RESETTABLE_INSTANCE = new EmptyIterator(); 40 | 41 | /** 42 | * Singleton instance of the iterator. 43 | * @since 2.1.1 and 3.1 44 | */ 45 | @SuppressWarnings("rawtypes") 46 | public static final Iterator INSTANCE = RESETTABLE_INSTANCE; 47 | 48 | /** 49 | * Get a typed resettable empty iterator instance. 50 | * @param the element type 51 | * @return ResettableIterator 52 | */ 53 | @SuppressWarnings("unchecked") 54 | public static ResettableIterator resettableEmptyIterator() { 55 | return (ResettableIterator) RESETTABLE_INSTANCE; 56 | } 57 | 58 | /** 59 | * Get a typed empty iterator instance. 60 | * @param the element type 61 | * @return Iterator 62 | */ 63 | @SuppressWarnings("unchecked") 64 | public static Iterator emptyIterator() { 65 | return (Iterator) INSTANCE; 66 | } 67 | 68 | /** 69 | * Constructor. 70 | */ 71 | protected EmptyIterator() { 72 | super(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/EmptyMapIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | /** 21 | * Provides an implementation of an empty map iterator. 22 | * 23 | * @since 3.1 24 | * @version $Id: EmptyMapIterator.java 1543955 2013-11-20 21:23:53Z tn $ 25 | */ 26 | public class EmptyMapIterator extends AbstractEmptyMapIterator implements 27 | MapIterator, ResettableIterator { 28 | 29 | /** 30 | * Singleton instance of the iterator. 31 | * @since 3.1 32 | */ 33 | @SuppressWarnings("rawtypes") 34 | public static final MapIterator INSTANCE = new EmptyMapIterator(); 35 | 36 | /** 37 | * Get a typed instance of the iterator. 38 | * @param the key type 39 | * @param the value type 40 | * @return {@link MapIterator} 41 | */ 42 | @SuppressWarnings("unchecked") 43 | public static MapIterator emptyMapIterator() { 44 | return (MapIterator) INSTANCE; 45 | } 46 | 47 | /** 48 | * Constructor. 49 | */ 50 | protected EmptyMapIterator() { 51 | super(); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/Get.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | import java.util.Collection; 21 | import java.util.Map; 22 | import java.util.Set; 23 | 24 | /** 25 | * The "read" subset of the {@link java.util.Map} interface. 26 | * 27 | * @since 4.0 28 | * @version $Id: Get.java 1543265 2013-11-19 00:48:44Z ggregory $ 29 | * 30 | * @see Put 31 | */ 32 | public interface Get { 33 | 34 | /** 35 | * @see java.util.Map#containsKey(Object) 36 | */ 37 | boolean containsKey(Object key); 38 | 39 | /** 40 | * @see java.util.Map#containsValue(Object) 41 | */ 42 | boolean containsValue(Object value); 43 | 44 | /** 45 | * @see java.util.Map#entrySet() 46 | */ 47 | Set> entrySet(); 48 | 49 | /** 50 | * @see java.util.Map#get(Object) 51 | */ 52 | V get(Object key); 53 | 54 | /** 55 | * @see java.util.Map#remove(Object) 56 | */ 57 | V remove(Object key); 58 | 59 | /** 60 | * @see java.util.Map#isEmpty() 61 | */ 62 | boolean isEmpty(); 63 | 64 | /** 65 | * @see java.util.Map#keySet() 66 | */ 67 | Set keySet(); 68 | 69 | /** 70 | * @see java.util.Map#size() 71 | */ 72 | int size(); 73 | 74 | /** 75 | * @see java.util.Map#values() 76 | */ 77 | Collection values(); 78 | 79 | } 80 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/IterableGet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | /** 21 | * The "read" subset of the {@link java.util.Map} interface. 22 | * 23 | * @since 4.0 24 | * @version $Id: IterableGet.java 1477779 2013-04-30 18:55:24Z tn $ 25 | * 26 | * @see Put 27 | */ 28 | public interface IterableGet extends Get { 29 | /** 30 | * Obtains a MapIterator over the map. 31 | *

32 | * A map iterator is an efficient way of iterating over maps. 33 | * There is no need to access the entry set or use Map Entry objects. 34 | *

35 |      * IterableMap map = new HashedMap();
36 |      * MapIterator it = map.mapIterator();
37 |      * while (it.hasNext()) {
38 |      *   String key = it.next();
39 |      *   Integer value = it.getValue();
40 |      *   it.setValue(value + 1);
41 |      * }
42 |      * 
43 | * 44 | * @return a map iterator 45 | */ 46 | MapIterator mapIterator(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/IterableMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | import java.util.Map; 21 | 22 | /** 23 | * Defines a map that can be iterated directly without needing to create an entry set. 24 | *

25 | * A map iterator is an efficient way of iterating over maps. 26 | * There is no need to access the entry set or use Map Entry objects. 27 | *

28 |  * IterableMap map = new HashedMap();
29 |  * MapIterator it = map.mapIterator();
30 |  * while (it.hasNext()) {
31 |  *   String key = it.next();
32 |  *   Integer value = it.getValue();
33 |  *   it.setValue(value + 1);
34 |  * }
35 |  * 
36 | * 37 | * @param the type of the keys in the map 38 | * @param the type of the values in the map 39 | * 40 | * @since 3.0 41 | * @version $Id: IterableMap.java 1469004 2013-04-17 17:37:03Z tn $ 42 | */ 43 | public interface IterableMap extends Map, Put, IterableGet { 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/KeyValue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | /** 21 | * Defines a simple key value pair. 22 | *

23 | * A Map Entry has considerable additional semantics over and above a simple 24 | * key-value pair. This interface defines the minimum key value, with just the 25 | * two get methods. 26 | * 27 | * @param the type of the key 28 | * @param the type of the value 29 | * @since 3.0 30 | * @version $Id: KeyValue.java 1477779 2013-04-30 18:55:24Z tn $ 31 | */ 32 | public interface KeyValue { 33 | 34 | /** 35 | * Gets the key from the pair. 36 | * 37 | * @return the key 38 | */ 39 | K getKey(); 40 | 41 | /** 42 | * Gets the value from the pair. 43 | * 44 | * @return the value 45 | */ 46 | V getValue(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/MapIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | import java.util.Iterator; 21 | 22 | /** 23 | * Defines an iterator that operates over a Map. 24 | *

25 | * This iterator is a special version designed for maps. It can be more 26 | * efficient to use this rather than an entry set iterator where the option 27 | * is available, and it is certainly more convenient. 28 | *

29 | * A map that provides this interface may not hold the data internally using 30 | * Map Entry objects, thus this interface can avoid lots of object creation. 31 | *

32 | * In use, this iterator iterates through the keys in the map. After each call 33 | * to next(), the getValue() method provides direct 34 | * access to the value. The value can also be set using setValue(). 35 | *

 36 |  * MapIterator it = map.mapIterator();
 37 |  * while (it.hasNext()) {
 38 |  *   String key = it.next();
 39 |  *   Integer value = it.getValue();
 40 |  *   it.setValue(value + 1);
 41 |  * }
 42 |  * 
43 | * 44 | * @param the type of the keys in the map 45 | * @param the type of the values in the map 46 | * @since 3.0 47 | * @version $Id: MapIterator.java 1469004 2013-04-17 17:37:03Z tn $ 48 | */ 49 | public interface MapIterator extends Iterator { 50 | 51 | /** 52 | * Checks to see if there are more entries still to be iterated. 53 | * 54 | * @return true if the iterator has more elements 55 | */ 56 | boolean hasNext(); 57 | 58 | /** 59 | * Gets the next key from the Map. 60 | * 61 | * @return the next key in the iteration 62 | * @throws java.util.NoSuchElementException if the iteration is finished 63 | */ 64 | K next(); 65 | 66 | //----------------------------------------------------------------------- 67 | /** 68 | * Gets the current key, which is the key returned by the last call 69 | * to next(). 70 | * 71 | * @return the current key 72 | * @throws IllegalStateException if next() has not yet been called 73 | */ 74 | K getKey(); 75 | 76 | /** 77 | * Gets the current value, which is the value associated with the last key 78 | * returned by next(). 79 | * 80 | * @return the current value 81 | * @throws IllegalStateException if next() has not yet been called 82 | */ 83 | V getValue(); 84 | 85 | //----------------------------------------------------------------------- 86 | /** 87 | * Removes the last returned key from the underlying Map (optional operation). 88 | *

89 | * This method can be called once per call to next(). 90 | * 91 | * @throws UnsupportedOperationException if remove is not supported by the map 92 | * @throws IllegalStateException if next() has not yet been called 93 | * @throws IllegalStateException if remove() has already been called 94 | * since the last call to next() 95 | */ 96 | void remove(); 97 | 98 | /** 99 | * Sets the value associated with the current key (optional operation). 100 | * 101 | * @param value the new value 102 | * @return the previous value 103 | * @throws UnsupportedOperationException if setValue is not supported by the map 104 | * @throws IllegalStateException if next() has not yet been called 105 | * @throws IllegalStateException if remove() has been called since the 106 | * last call to next() 107 | */ 108 | V setValue(V value); 109 | 110 | } 111 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/Put.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | import java.util.Map; 21 | 22 | /** 23 | * The "write" subset of the {@link Map} interface. 24 | *

25 | * NOTE: in the original {@link Map} interface, {@link Map#put(Object, Object)} is known 26 | * to have the same return type as {@link Map#get(Object)}, namely {@code V}. {@link Put} 27 | * makes no assumptions in this regard (there is no association with, nor even knowledge 28 | * of, a "reading" interface) and thus defines {@link #put(Object, Object)} as returning 29 | * {@link Object}. 30 | * 31 | * @since 4.0 32 | * @version $Id: Put.java 1543257 2013-11-19 00:45:55Z ggregory $ 33 | * 34 | * @see Get 35 | */ 36 | public interface Put { 37 | 38 | /** 39 | * @see Map#clear() 40 | */ 41 | void clear(); 42 | 43 | /** 44 | * Note that the return type is Object, rather than V as in the Map interface. 45 | * See the class Javadoc for further info. 46 | * 47 | * @see Map#put(Object, Object) 48 | */ 49 | Object put(K key, V value); 50 | 51 | /** 52 | * @see Map#putAll(Map) 53 | */ 54 | void putAll(Map t); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/cache/memory/apache/ResettableIterator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package io.rx_cache.internal.cache.memory.apache; 19 | 20 | import java.util.Iterator; 21 | 22 | /** 23 | * Defines an iterator that can be reset back to an initial state. 24 | *

25 | * This interface allows an iterator to be repeatedly reused. 26 | * 27 | * @param the type to iterate over 28 | * @since 3.0 29 | * @version $Id: ResettableIterator.java 1543263 2013-11-19 00:47:55Z ggregory $ 30 | */ 31 | public interface ResettableIterator extends Iterator { 32 | 33 | /** 34 | * Resets the iterator back to the position at which the iterator 35 | * was created. 36 | */ 37 | void reset(); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/migration/CacheVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | 20 | import io.rx_cache.internal.Persistence; 21 | 22 | abstract class CacheVersion { 23 | protected final Persistence persistence; 24 | protected final static String KEY_CACHE_VERSION = "key_cache_version"; 25 | 26 | public CacheVersion(Persistence persistence) { 27 | this.persistence = persistence; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/migration/DeleteRecordMatchingClassName.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | 20 | import java.util.List; 21 | 22 | import javax.inject.Inject; 23 | 24 | import io.rx_cache.internal.Persistence; 25 | import io.rx_cache.internal.Record; 26 | import rx.Observable; 27 | 28 | final class DeleteRecordMatchingClassName { 29 | private final Persistence persistence; 30 | private List classes; 31 | 32 | @Inject public DeleteRecordMatchingClassName(Persistence persistence) { 33 | this.persistence = persistence; 34 | } 35 | 36 | DeleteRecordMatchingClassName with(List classes) { 37 | this.classes = classes; 38 | return this; 39 | } 40 | 41 | Observable react() { 42 | if (classes.isEmpty()) return Observable.just(null); 43 | 44 | List allKeys = persistence.allKeys(); 45 | 46 | for (String key : allKeys) { 47 | Record record = persistence.retrieveRecord(key); 48 | if (evictRecord(record)) persistence.evict(key); 49 | } 50 | 51 | return Observable.just(null); 52 | } 53 | 54 | private boolean evictRecord(Record record) { 55 | String candidate = record.getDataClassName(); 56 | 57 | for (Class aClass : classes) { 58 | String className = aClass.getName(); 59 | if (className.equals(candidate)) { 60 | return true; 61 | } 62 | } 63 | 64 | return false; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/migration/DoMigrations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | import java.util.List; 20 | 21 | import javax.inject.Inject; 22 | 23 | import io.rx_cache.Migration; 24 | import io.rx_cache.internal.Persistence; 25 | import rx.Observable; 26 | import rx.functions.Func1; 27 | 28 | public final class DoMigrations { 29 | private final GetCacheVersion getCacheVersion; 30 | private final GetPendingMigrations getPendingMigrations; 31 | private final GetClassesToEvictFromMigrations getClassesToEvictFromMigrations; 32 | private final DeleteRecordMatchingClassName deleteRecordMatchingClassName; 33 | private final UpgradeCacheVersion upgradeCacheVersion; 34 | 35 | @Inject public DoMigrations(Persistence persistence, Class providerClass) { 36 | this.getCacheVersion = new GetCacheVersion(persistence); 37 | this.getPendingMigrations = new GetPendingMigrations(providerClass); 38 | this.getClassesToEvictFromMigrations = new GetClassesToEvictFromMigrations(); 39 | this.deleteRecordMatchingClassName = new DeleteRecordMatchingClassName(persistence); 40 | this.upgradeCacheVersion = new UpgradeCacheVersion(persistence); 41 | } 42 | 43 | private List migrations; 44 | 45 | public Observable react() { 46 | return getCacheVersion.react().flatMap(new Func1>>() { 47 | @Override public Observable> call(Integer currentCacheVersion) { 48 | return getPendingMigrations.with(currentCacheVersion).react(); 49 | } 50 | }).flatMap(new Func1, Observable>>() { 51 | @Override public Observable> call(List migrations) { 52 | DoMigrations.this.migrations = migrations; 53 | return getClassesToEvictFromMigrations.with(migrations).react(); 54 | } 55 | }).flatMap(new Func1, Observable>() { 56 | @Override public Observable call(List classes) { 57 | return deleteRecordMatchingClassName.with(classes).react(); 58 | } 59 | }).flatMap(new Func1>() { 60 | @Override public Observable call(Object o) { 61 | return upgradeCacheVersion.with(migrations).react(); 62 | } 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/migration/GetCacheVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | 20 | import javax.inject.Inject; 21 | 22 | import io.rx_cache.internal.Persistence; 23 | import rx.Observable; 24 | 25 | final class GetCacheVersion extends CacheVersion { 26 | 27 | @Inject public GetCacheVersion(Persistence persistence) { 28 | super(persistence); 29 | } 30 | 31 | Observable react() { 32 | Integer currentVersion = persistence.retrieve(KEY_CACHE_VERSION, Integer.class); 33 | currentVersion = currentVersion == null ? 0 : currentVersion; 34 | return Observable.just(currentVersion); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/migration/GetClassesToEvictFromMigrations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import javax.inject.Inject; 23 | 24 | import io.rx_cache.Migration; 25 | import rx.Observable; 26 | 27 | final class GetClassesToEvictFromMigrations { 28 | private List migrations; 29 | 30 | @Inject public GetClassesToEvictFromMigrations() {} 31 | 32 | GetClassesToEvictFromMigrations with(List migrations) { 33 | this.migrations = migrations; 34 | return this; 35 | } 36 | 37 | Observable> react() { 38 | List classesToEvict = new ArrayList<>(); 39 | 40 | for (Migration migration : migrations) { 41 | for (Class candidate : migration.evictClasses()) { 42 | if (!isAlreadyAdded(classesToEvict, candidate)) classesToEvict.add(candidate); 43 | } 44 | } 45 | 46 | return Observable.just(classesToEvict); 47 | } 48 | 49 | private boolean isAlreadyAdded(List classesToEvict, Class candidate) { 50 | for (Class aClass : classesToEvict) { 51 | String className = aClass.getName(); 52 | String classNameCandidate = candidate.getName(); 53 | if (className.equals(classNameCandidate)) { 54 | return true; 55 | } 56 | } 57 | 58 | return false; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/migration/GetPendingMigrations.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | import java.lang.annotation.Annotation; 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.Collections; 23 | import java.util.Comparator; 24 | import java.util.List; 25 | 26 | import javax.inject.Inject; 27 | 28 | import io.rx_cache.Migration; 29 | import io.rx_cache.SchemeMigration; 30 | import rx.Observable; 31 | 32 | final class GetPendingMigrations { 33 | private final Class providersClass; 34 | private int cacheVersion; 35 | 36 | @Inject GetPendingMigrations(Class providersClass) { 37 | this.providersClass = providersClass; 38 | } 39 | 40 | GetPendingMigrations with(int currentCacheVersion) { 41 | this.cacheVersion = currentCacheVersion; 42 | return this; 43 | } 44 | 45 | public Observable> react() { 46 | List migrations = new ArrayList<>(); 47 | Annotation annotation = providersClass.getAnnotation(SchemeMigration.class); 48 | 49 | if (annotation == null) return Observable.just(migrations); 50 | 51 | SchemeMigration schemeMigration = (SchemeMigration) annotation; 52 | migrations = Arrays.asList(schemeMigration.value()); 53 | 54 | Collections.sort(migrations, new Comparator() { 55 | @Override public int compare(Migration migration1, Migration migration2) { 56 | return migration1.version() - migration2.version(); 57 | } 58 | }); 59 | 60 | List pendingMigrations = new ArrayList<>(); 61 | 62 | for (Migration migration : migrations) { 63 | if (cacheVersion < migration.version()) 64 | pendingMigrations.add(migration); 65 | } 66 | 67 | return Observable.just(pendingMigrations); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /core/src/main/java/io/rx_cache/internal/migration/UpgradeCacheVersion.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | 20 | import java.util.List; 21 | 22 | import javax.inject.Inject; 23 | 24 | import io.rx_cache.Migration; 25 | import io.rx_cache.internal.Persistence; 26 | import rx.Observable; 27 | 28 | final class UpgradeCacheVersion extends CacheVersion { 29 | private List migrations; 30 | 31 | @Inject public UpgradeCacheVersion(Persistence persistence) { 32 | super(persistence); 33 | } 34 | 35 | UpgradeCacheVersion with(List migrations) { 36 | this.migrations = migrations; 37 | return this; 38 | } 39 | 40 | Observable react() { 41 | if (migrations.isEmpty()) return Observable.just(null); 42 | 43 | Migration migration = migrations.get(migrations.size() - 1); 44 | persistence.save(KEY_CACHE_VERSION, migration.version()); 45 | 46 | return Observable.just(null); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/test/java/Providers.java: -------------------------------------------------------------------------------- 1 | import java.util.List; 2 | import java.util.concurrent.TimeUnit; 3 | 4 | import io.rx_cache.DynamicKey; 5 | import io.rx_cache.DynamicKeyGroup; 6 | import io.rx_cache.EvictDynamicKey; 7 | import io.rx_cache.EvictProvider; 8 | import io.rx_cache.LifeCache; 9 | import io.rx_cache.Migration; 10 | import io.rx_cache.SchemeMigration; 11 | import io.rx_cache.internal.Mock; 12 | import rx.Observable; 13 | 14 | /** 15 | * Created by victor on 27/02/16. 16 | */ 17 | @SchemeMigration({ 18 | @Migration(version = 1, evictClasses = { 19 | Mock.class 20 | }) 21 | }) 22 | interface Providers { 23 | Observable> getMocks(Observable> oMocks); 24 | 25 | @LifeCache(duration = 5, timeUnit = TimeUnit.MINUTES) 26 | Observable> getMocksWith5MinutesLifeTime(Observable> oMocks); 27 | 28 | Observable> getMocksEvictProvider(Observable> oMocks, EvictProvider evictProvider); 29 | 30 | Observable> getMocksPaginate(Observable> oMocks, DynamicKey page); 31 | 32 | Observable> getMocksPaginateEvictPerPage(Observable> oMocks, DynamicKey page, EvictDynamicKey evictPage); 33 | 34 | Observable> getMocksPaginateWithFiltersEvictingPerFilter(Observable> oMocks, DynamicKeyGroup filterPage, EvictDynamicKey evictFilter); 35 | } 36 | -------------------------------------------------------------------------------- /core/src/test/java/Repository.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | import java.util.Arrays; 3 | import java.util.List; 4 | 5 | import io.rx_cache.DynamicKey; 6 | import io.rx_cache.DynamicKeyGroup; 7 | import io.rx_cache.EvictDynamicKey; 8 | import io.rx_cache.EvictProvider; 9 | import io.rx_cache.internal.Mock; 10 | import io.rx_cache.internal.RxCache; 11 | import rx.Observable; 12 | 13 | /** 14 | * Created by victor on 27/02/16. 15 | */ 16 | public class Repository { 17 | private final Providers providers; 18 | 19 | public Repository(File cacheDir) { 20 | providers = new RxCache.Builder() 21 | .persistence(cacheDir) 22 | .using(Providers.class); 23 | } 24 | 25 | public Observable> getMocks(final boolean update) { 26 | return providers.getMocksEvictProvider(getExpensiveMocks(), new EvictProvider(update)); 27 | } 28 | 29 | public Observable> getMocksPaginate(final int page, final boolean update) { 30 | return providers.getMocksPaginateEvictPerPage(getExpensiveMocks(), new DynamicKey(page), new EvictDynamicKey(update)); 31 | } 32 | 33 | public Observable> getMocksWithFiltersPaginate(final String filter, final int page, final boolean updateFilter) { 34 | return providers.getMocksPaginateWithFiltersEvictingPerFilter(getExpensiveMocks(), new DynamicKeyGroup(filter, page), new EvictDynamicKey(updateFilter)); 35 | } 36 | 37 | //In a real use case, here is when you build your observable with the expensive operation. 38 | //Or if you are making http calls you can use Retrofit to get it out of the box. 39 | private Observable> getExpensiveMocks() { 40 | return Observable.just(Arrays.asList(new Mock(""))); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/DiskTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Arrays; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.TreeMap; 27 | import java.util.Vector; 28 | 29 | import io.rx_cache.internal.common.BaseTest; 30 | 31 | import static org.hamcrest.MatcherAssert.assertThat; 32 | import static org.hamcrest.core.Is.is; 33 | 34 | public class DiskTest extends BaseTest { 35 | private final static String KEY = "store"; 36 | private final static String VALUE = "dummy"; 37 | 38 | @Test public void When_A_Record_Is_Supplied_Retrieve_It() { 39 | disk.save(KEY, new Record(new Mock(VALUE))); 40 | 41 | Record diskRecord = disk.retrieveRecord(KEY); 42 | assertThat(diskRecord.getData().getMessage(), is(VALUE)); 43 | } 44 | 45 | @Test public void When_A_Record_Collection_Is_Supplied_Retrieve_It() { 46 | List mocks = Arrays.asList(new Mock(VALUE), new Mock(VALUE + 1)); 47 | disk.save(KEY, new Record(mocks)); 48 | 49 | Record> diskRecord = disk.retrieveRecord(KEY); 50 | assertThat(diskRecord.getData().get(0).getMessage(), is(VALUE)); 51 | assertThat(diskRecord.getData().get(1).getMessage(), is(VALUE+1)); 52 | } 53 | 54 | @Test public void When_A_Record_Array_Is_Supplied_Retrieve_It() { 55 | Mock[] mocks = {new Mock(VALUE), new Mock(VALUE+1)}; 56 | disk.save(KEY, new Record(mocks)); 57 | 58 | Record diskRecord = disk.retrieveRecord(KEY); 59 | assertThat(diskRecord.getData()[0].getMessage(), is(VALUE)); 60 | assertThat(diskRecord.getData()[1].getMessage(), is(VALUE+1)); 61 | } 62 | 63 | @Test public void When_A_Record_Map_Is_Supplied_Retrieve_It() { 64 | Map mocks = new HashMap(); 65 | mocks.put(1, new Mock(VALUE)); 66 | mocks.put(2, new Mock(VALUE + 1)); 67 | 68 | disk.save(KEY, new Record(mocks)); 69 | 70 | Record> diskRecord = disk.retrieveRecord(KEY); 71 | assertThat(diskRecord.getData().get(1).getMessage(), is(VALUE)); 72 | assertThat(diskRecord.getData().get(2).getMessage(), is(VALUE+1)); 73 | } 74 | 75 | @Test public void When_A_Collection_Is_Supplied_Retrieve_It() { 76 | List mockArrayList = new ArrayList(); 77 | mockArrayList.add(new Mock(VALUE)); 78 | mockArrayList.add(new Mock(VALUE + 1)); 79 | 80 | disk.save(KEY, mockArrayList); 81 | mockArrayList = disk.retrieveCollection(KEY, ArrayList.class, Mock.class); 82 | 83 | assertThat(mockArrayList.get(0).getMessage(), is(VALUE)); 84 | assertThat(mockArrayList.get(1).getMessage(), is(VALUE + 1)); 85 | 86 | Vector mocksVector = new Vector(3, 2); 87 | mocksVector.add(new Mock(VALUE)); 88 | mocksVector.add(new Mock(VALUE + 1)); 89 | 90 | disk.save(KEY, mocksVector); 91 | mocksVector = disk.retrieveCollection(KEY, Vector.class, Mock.class); 92 | 93 | assertThat(mocksVector.get(0).getMessage(), is(VALUE)); 94 | assertThat(mocksVector.get(1).getMessage(), is(VALUE+1)); 95 | } 96 | 97 | @Test public void When_A_Map_Is_Supplied_Retrieve_It() { 98 | Map mocksHashMap = new HashMap(); 99 | mocksHashMap.put(1, new Mock(VALUE)); 100 | mocksHashMap.put(2, new Mock(VALUE + 1)); 101 | 102 | disk.save(KEY, mocksHashMap); 103 | 104 | mocksHashMap = disk.retrieveMap(KEY, TreeMap.class, Integer.class, Mock.class); 105 | assertThat(mocksHashMap.get(1).getMessage(), is(VALUE)); 106 | assertThat(mocksHashMap.get(2).getMessage(), is(VALUE+1)); 107 | } 108 | 109 | @Test public void When_An_Array_Is_Supplied_Retrieve_It() { 110 | Mock[] mocksArray = {new Mock(VALUE), new Mock(VALUE+1)}; 111 | disk.save(KEY, mocksArray); 112 | 113 | mocksArray = disk.retrieveArray(KEY, Mock.class); 114 | assertThat(mocksArray[0].getMessage(), is(VALUE)); 115 | assertThat(mocksArray[1].getMessage(), is(VALUE+1)); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/Mock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | /** 20 | * Created by victor on 28/12/15. 21 | */ 22 | public class Mock { 23 | private String message; 24 | 25 | public Mock(String message) { 26 | this.message = message; 27 | } 28 | 29 | public String getMessage() { 30 | return message; 31 | } 32 | 33 | public void setMessage(String message) { 34 | this.message = message; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/ProvidersRxCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | import io.rx_cache.DynamicKey; 24 | import io.rx_cache.DynamicKeyGroup; 25 | import io.rx_cache.EvictDynamicKey; 26 | import io.rx_cache.EvictDynamicKeyGroup; 27 | import io.rx_cache.EvictProvider; 28 | import io.rx_cache.Expirable; 29 | import io.rx_cache.LifeCache; 30 | import io.rx_cache.Reply; 31 | import rx.Observable; 32 | 33 | /** 34 | * Provided to test as an integration test the library RxCache 35 | */ 36 | interface ProvidersRxCache { 37 | Observable> getMocks(Observable> mocks); 38 | 39 | Observable>> getMocksWithDetailResponse(Observable> mocks); 40 | 41 | @LifeCache(duration = 1, timeUnit = TimeUnit.SECONDS) 42 | Observable>> getMocksListResponseOneSecond(Observable> mocks); 43 | 44 | Observable>> getMocksMapResponse(Observable> mocks); 45 | 46 | Observable> getMocksArrayResponse(Observable mocks); 47 | 48 | @LifeCache(duration = 1, timeUnit = TimeUnit.MINUTES) 49 | Observable> getMocksLifeTimeMinutes(Observable> mocks); 50 | 51 | @LifeCache(duration = 1, timeUnit = TimeUnit.SECONDS) 52 | Observable> getMocksLifeTimeSeconds(Observable> mocks); 53 | 54 | @LifeCache(duration = 65000, timeUnit = TimeUnit.MILLISECONDS) 55 | Observable> getMocksLifeTimeMillis(Observable> mocks); 56 | 57 | Observable> getMocksPaginate(Observable> mocks, DynamicKey page); 58 | 59 | @Expirable(false) 60 | @LifeCache(duration = 1, timeUnit = TimeUnit.DAYS) 61 | Observable> getMocksPaginateNotExpirable(Observable> mocks, DynamicKey page); 62 | 63 | Observable> getMocksPaginateEvictProvider(Observable> mocks, DynamicKey page, EvictProvider evictProvider); 64 | 65 | Reply> getMocksBadReturnType(Observable> mocks); 66 | 67 | Observable>> getMocksEvictProvider(Observable> mocks, EvictProvider evictProvider); 68 | 69 | Observable>> getMocksDynamicKeyEvictPage(Observable> mocks, DynamicKey page, EvictDynamicKey evictPage); 70 | 71 | Observable getLoggedMock(Observable mock, EvictProvider evictProvider); 72 | 73 | Observable> getMocksFilteredPaginateEvict(Observable> oMocks, DynamicKeyGroup dynamicKeyGroup, EvictProvider evictDynamicKey); 74 | 75 | @LifeCache(duration = 1, timeUnit = TimeUnit.MILLISECONDS) 76 | Observable> getEphemeralMocksPaginate(Observable> mocks, DynamicKey page); 77 | 78 | Observable getMockWithoutLoaderObservable(); 79 | 80 | int getMockWithoutReturnObservable(); 81 | 82 | Observable getMockMultipleObservables(Observable mock, Observable mock2); 83 | 84 | Observable getMockMultipleEvicts(Observable mock, EvictProvider evictProvider, EvictProvider evictProvider2); 85 | 86 | Observable getMockMultipleDynamicKeys(Observable mock, DynamicKey dynamicKey, DynamicKey dynamicKey2); 87 | 88 | Observable getMockEvictDynamicKeyProvidingDynamicKey(Observable mock, DynamicKey dynamicKey, EvictDynamicKey evictDynamicKey); 89 | Observable getMockEvictDynamicKeyWithoutProvidingDynamicKey(Observable mock, EvictDynamicKey evictDynamicKey); 90 | 91 | Observable getMockEvictDynamicKeyGroupProvidingDynamicKeyGroup(Observable mock, DynamicKeyGroup dynamicKeyGroup, EvictDynamicKeyGroup evictDynamicKeyGroup); 92 | Observable getMockEvictDynamicKeyGroupWithoutProvidingDynamicKeyGroup(Observable mock, EvictDynamicKeyGroup evictDynamicKeyGroup); 93 | } 94 | 95 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/ProvidersRxCacheEvictExpirableRecordsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import org.junit.Before; 20 | import org.junit.FixMethodOrder; 21 | import org.junit.Rule; 22 | import org.junit.Test; 23 | import org.junit.rules.TemporaryFolder; 24 | import org.junit.runners.MethodSorters; 25 | 26 | import java.util.List; 27 | 28 | import io.rx_cache.DynamicKey; 29 | import io.rx_cache.internal.cache.EvictExpirableRecordsPersistence; 30 | import io.rx_cache.internal.common.BaseTestEvictingTask; 31 | import rx.observers.TestSubscriber; 32 | 33 | import static org.hamcrest.CoreMatchers.is; 34 | import static org.junit.Assert.assertThat; 35 | 36 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 37 | public class ProvidersRxCacheEvictExpirableRecordsTest extends BaseTestEvictingTask { 38 | @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); 39 | 40 | private ProvidersRxCache providersRxCache; 41 | private int maxMgPersistenceCache = 7; 42 | 43 | @Before public void setUp() { 44 | providersRxCache = new RxCache.Builder() 45 | .setMaxMBPersistenceCache(maxMgPersistenceCache) 46 | .persistence(temporaryFolder.getRoot()) 47 | .using(ProvidersRxCache.class); 48 | } 49 | 50 | @Test public void When_Expirable_Records_Evict() { 51 | assertThat(getSizeMB(temporaryFolder.getRoot()), is(0)); 52 | 53 | for (int i = 0; i < 50; i++) { 54 | waitTime(50); 55 | TestSubscriber> subscriber = new TestSubscriber<>(); 56 | String key = i + ""; 57 | providersRxCache.getMocksPaginate(createObservableMocks(), new DynamicKey(key)).subscribe(subscriber); 58 | subscriber.awaitTerminalEvent(); 59 | } 60 | 61 | waitTime(1000); 62 | int expectedStoredMB = (int) (maxMgPersistenceCache * EvictExpirableRecordsPersistence.PERCENTAGE_MEMORY_STORED_TO_STOP); 63 | assertThat(getSizeMB(temporaryFolder.getRoot()), is(expectedStoredMB)); 64 | } 65 | 66 | @Test public void When_No_Expirable_Records_Do_Not_Evict() { 67 | assertThat(getSizeMB(temporaryFolder.getRoot()), is(0)); 68 | 69 | for (int i = 0; i < 50; i++) { 70 | waitTime(50); 71 | TestSubscriber> subscriber = new TestSubscriber<>(); 72 | String key = i + ""; 73 | providersRxCache.getMocksPaginateNotExpirable(createObservableMocks(), new DynamicKey(key)).subscribe(subscriber); 74 | subscriber.awaitTerminalEvent(); 75 | } 76 | 77 | waitTime(1000); 78 | assertThat(getSizeMB(temporaryFolder.getRoot()), is(maxMgPersistenceCache)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/ProvidersRxCacheEvictExpiredRecordsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal; 18 | 19 | import org.junit.Before; 20 | import org.junit.ClassRule; 21 | import org.junit.FixMethodOrder; 22 | import org.junit.Test; 23 | import org.junit.rules.TemporaryFolder; 24 | import org.junit.runners.MethodSorters; 25 | 26 | import java.util.List; 27 | 28 | import io.rx_cache.DynamicKey; 29 | import io.rx_cache.internal.common.BaseTestEvictingTask; 30 | import rx.observers.TestSubscriber; 31 | 32 | import static org.hamcrest.CoreMatchers.is; 33 | import static org.junit.Assert.assertThat; 34 | 35 | /** 36 | * Created by victor on 03/03/16. 37 | */ 38 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 39 | public class ProvidersRxCacheEvictExpiredRecordsTest extends BaseTestEvictingTask { 40 | @ClassRule public static TemporaryFolder temporaryFolder = new TemporaryFolder(); 41 | private ProvidersRxCache providersRxCache; 42 | 43 | @Before public void setUp() { 44 | providersRxCache = new RxCache.Builder() 45 | .persistence(temporaryFolder.getRoot()) 46 | .using(ProvidersRxCache.class); 47 | } 48 | 49 | @Test public void _1_Populate_Disk_With_Expired_Records_And_No_Retrievable_Keys() { 50 | assert getSizeMB(temporaryFolder.getRoot()) == 0; 51 | 52 | for (int i = 0; i < 50; i++) { 53 | waitTime(50); 54 | TestSubscriber> subscriber = new TestSubscriber<>(); 55 | String key = System.currentTimeMillis() + i + ""; 56 | providersRxCache.getEphemeralMocksPaginate(createObservableMocks(), new DynamicKey(key)).subscribe(subscriber); 57 | subscriber.awaitTerminalEvent(); 58 | } 59 | 60 | assert getSizeMB(temporaryFolder.getRoot()) > 0; 61 | } 62 | 63 | @Test public void _2_Perform_Evicting_Task_And_Check_Results() { 64 | waitTime(1000); 65 | assertThat(getSizeMB(temporaryFolder.getRoot()), is(0)); 66 | } 67 | 68 | @Test public void _3_Populate_Disk_With_No_Expired_Records_And_No_Retrievable_Keys() { 69 | assertThat(getSizeMB(temporaryFolder.getRoot()), is(0)); 70 | 71 | for (int i = 0; i < 50; i++) { 72 | waitTime(50); 73 | TestSubscriber> subscriber = new TestSubscriber<>(); 74 | String key = System.currentTimeMillis() + i + ""; 75 | providersRxCache.getMocksPaginate(createObservableMocks(), new DynamicKey(key)).subscribe(subscriber); 76 | subscriber.awaitTerminalEvent(); 77 | } 78 | 79 | assert getSizeMB(temporaryFolder.getRoot()) > 0; 80 | } 81 | 82 | @Test public void _4_Perform_Evicting_Task_And_Check_Results() { 83 | waitTime(1000); 84 | assert getSizeMB(temporaryFolder.getRoot()) > 0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/cache/ActionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | import org.junit.Test; 20 | 21 | import java.util.LinkedHashMap; 22 | import java.util.List; 23 | import java.util.Set; 24 | 25 | import io.rx_cache.internal.Record; 26 | import io.rx_cache.internal.Memory; 27 | import io.rx_cache.internal.common.BaseTest; 28 | 29 | import static org.hamcrest.MatcherAssert.assertThat; 30 | import static org.hamcrest.core.Is.is; 31 | 32 | public class ActionTest extends BaseTest { 33 | private Memory memory; 34 | private Action actionUT; 35 | 36 | private static final String PROVIDER_KEY = "get_mocks"; 37 | private static final String DYNAMIC_KEY_1 = "filter_1", DYNAMIC_KEY_2 = "filter_2"; 38 | private static final String DYNAMIC_KEY_GROUP_1 = "page_1", DYNAMIC_KEY_GROUP_2 = "page_2"; 39 | 40 | @Override public void setUp() { 41 | super.setUp(); 42 | 43 | memory = new MockMemory(); 44 | actionUT = new Action(memory, disk) {}; 45 | } 46 | 47 | @Test public void Check_Keys_Matching_Provider_Key() { 48 | List keysMatchingProviderKey = actionUT.getKeysOnMemoryMatchingProviderKey(PROVIDER_KEY); 49 | assertThat(keysMatchingProviderKey.get(0), is(filter1Page1)); 50 | assertThat(keysMatchingProviderKey.get(1), is(filter1Page2)); 51 | assertThat(keysMatchingProviderKey.get(2), is(filter2Page1)); 52 | assertThat(keysMatchingProviderKey.get(3), is(filter2Page2)); 53 | assertThat(keysMatchingProviderKey.size(), is(4)); 54 | } 55 | 56 | @Test public void Check_Keys_Matching_Dynamic_Key() { 57 | List keysMatchingDynamicKey1 = actionUT.getKeysOnMemoryMatchingDynamicKey(PROVIDER_KEY, DYNAMIC_KEY_1); 58 | assertThat(keysMatchingDynamicKey1.get(0), is(filter1Page1)); 59 | assertThat(keysMatchingDynamicKey1.get(1), is(filter1Page2)); 60 | assertThat(keysMatchingDynamicKey1.size(), is(2)); 61 | 62 | List keysMatchingDynamicKey2 = actionUT.getKeysOnMemoryMatchingDynamicKey(PROVIDER_KEY, DYNAMIC_KEY_2); 63 | assertThat(keysMatchingDynamicKey2.get(0), is(filter2Page1)); 64 | assertThat(keysMatchingDynamicKey2.get(1), is(filter2Page2)); 65 | assertThat(keysMatchingDynamicKey2.size(), is(2)); 66 | } 67 | 68 | @Test public void Check_Keys_Matching_Dynamic_Key_Group() { 69 | String keyMatchingDynamicKey1DynamicKeyGroup1 = actionUT.getKeyOnMemoryMatchingDynamicKeyGroup(PROVIDER_KEY, DYNAMIC_KEY_1, DYNAMIC_KEY_GROUP_1); 70 | assertThat(keyMatchingDynamicKey1DynamicKeyGroup1, is(filter1Page1)); 71 | 72 | String keyMatchingDynamicKey1DynamicKeyGroup2 = actionUT.getKeyOnMemoryMatchingDynamicKeyGroup(PROVIDER_KEY, DYNAMIC_KEY_1, DYNAMIC_KEY_GROUP_2); 73 | assertThat(keyMatchingDynamicKey1DynamicKeyGroup2, is(filter1Page2)); 74 | 75 | String keyMatchingDynamicKey2DynamicKeyGroup1 = actionUT.getKeyOnMemoryMatchingDynamicKeyGroup(PROVIDER_KEY, DYNAMIC_KEY_2, DYNAMIC_KEY_GROUP_1); 76 | assertThat(keyMatchingDynamicKey2DynamicKeyGroup1, is(filter2Page1)); 77 | 78 | String keyMatchingDynamicKey2DynamicKeyGroup2 = actionUT.getKeyOnMemoryMatchingDynamicKeyGroup(PROVIDER_KEY, DYNAMIC_KEY_2, DYNAMIC_KEY_GROUP_2); 79 | assertThat(keyMatchingDynamicKey2DynamicKeyGroup2, is(filter2Page2)); 80 | } 81 | 82 | private static String filter1Page1, filter1Page2, filter2Page1, filter2Page2; 83 | 84 | private static class MockMemory implements Memory { 85 | private final LinkedHashMap mockCache; 86 | 87 | public MockMemory() { 88 | mockCache = new LinkedHashMap(); 89 | 90 | Action action = new Action(null, null) {}; 91 | filter1Page1 = action.composeKey(PROVIDER_KEY, DYNAMIC_KEY_1, DYNAMIC_KEY_GROUP_1); 92 | mockCache.put(filter1Page1, mock(filter1Page1)); 93 | 94 | filter1Page2 = action.composeKey(PROVIDER_KEY, DYNAMIC_KEY_1, DYNAMIC_KEY_GROUP_2); 95 | mockCache.put(filter1Page2, mock(filter1Page2)); 96 | 97 | filter2Page1 = action.composeKey(PROVIDER_KEY, DYNAMIC_KEY_2, DYNAMIC_KEY_GROUP_1); 98 | mockCache.put(filter2Page1, mock(filter2Page1)); 99 | 100 | filter2Page2 = action.composeKey(PROVIDER_KEY, DYNAMIC_KEY_2, DYNAMIC_KEY_GROUP_2); 101 | mockCache.put(filter2Page2, mock(filter2Page2)); 102 | } 103 | 104 | @Override public Record getIfPresent(String key) { 105 | return null; 106 | } 107 | 108 | @Override public void put(String key, Record record) {} 109 | 110 | @Override public Set keySet() { 111 | return mockCache.keySet(); 112 | } 113 | 114 | @Override public void evict(String key) {} 115 | 116 | @Override public void evictAll() {} 117 | 118 | private Record mock(String value) { 119 | return new Record(value, true, 0); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/cache/EvictExpirableRecordsPersistenceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | import org.junit.Test; 20 | import org.junit.experimental.theories.DataPoint; 21 | import org.junit.experimental.theories.Theories; 22 | import org.junit.experimental.theories.Theory; 23 | import org.junit.runner.RunWith; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import io.rx_cache.internal.Record; 29 | import io.rx_cache.internal.Locale; 30 | import io.rx_cache.internal.Memory; 31 | import io.rx_cache.internal.Mock; 32 | import io.rx_cache.internal.cache.memory.ReferenceMapMemory; 33 | import io.rx_cache.internal.common.BaseTest; 34 | import rx.observers.TestSubscriber; 35 | 36 | import static org.hamcrest.CoreMatchers.is; 37 | import static org.junit.Assert.assertThat; 38 | 39 | @RunWith(Theories.class) 40 | public class EvictExpirableRecordsPersistenceTest extends BaseTest { 41 | private EvictExpirableRecordsPersistence evictExpirableRecordsPersistenceUT; 42 | private final Memory memory = new ReferenceMapMemory(); 43 | 44 | @Override public void setUp() { 45 | super.setUp(); 46 | } 47 | 48 | /* @Test public void When_Task_Is_Running_Do_Not_Start_Again() { 49 | evictExpirableRecordsPersistenceUT = new EvictExpirableRecordsPersistence(memory, disk, 10); 50 | 51 | for (int i = 0; i < 10; i++) { 52 | evictExpirableRecordsPersistenceUT.startTaskIfNeeded(); 53 | } 54 | 55 | assert(evictExpirableRecordsPersistenceUT.runningTasks() < 5); 56 | } 57 | 58 | @Test public void When_Task_Is_Not_Running_Start_Again() { 59 | evictExpirableRecordsPersistenceUT = new EvictExpirableRecordsPersistence(memory, disk, 10); 60 | 61 | for (int i = 0; i < 10; i++) { 62 | waitTime(100); 63 | evictExpirableRecordsPersistenceUT.startTaskIfNeeded(); 64 | } 65 | 66 | assertThat(evictExpirableRecordsPersistenceUT.runningTasks(), is(10)); 67 | }*/ 68 | 69 | @Test public void When_Not_Reached_Memory_Threshold_Not_Emit() { 70 | evictExpirableRecordsPersistenceUT = new EvictExpirableRecordsPersistence(memory, disk, 10); 71 | 72 | populate(true); 73 | assertThat(disk.allKeys().size(), is(100)); 74 | 75 | TestSubscriber testSubscriber = new TestSubscriber(); 76 | evictExpirableRecordsPersistenceUT.startTaskIfNeeded().subscribe(testSubscriber); 77 | testSubscriber.awaitTerminalEvent(); 78 | testSubscriber.assertNoErrors(); 79 | testSubscriber.assertNoValues(); 80 | } 81 | 82 | @DataPoint public static Integer _3_MB = 3; 83 | @DataPoint public static Integer _5_MB = 5; 84 | @DataPoint public static Integer _7_MB = 7; 85 | @Theory @Test public void When_Reached_Memory_Threshold_Perform_Task(int maxMgPersistenceCache) { 86 | evictExpirableRecordsPersistenceUT = new EvictExpirableRecordsPersistence(memory, disk, maxMgPersistenceCache); 87 | 88 | populate(true); 89 | assertThat(disk.allKeys().size(), is(mocksCount())); 90 | 91 | TestSubscriber testSubscriber = new TestSubscriber(); 92 | evictExpirableRecordsPersistenceUT.startTaskIfNeeded().subscribe(testSubscriber); 93 | testSubscriber.awaitTerminalEvent(); 94 | testSubscriber.assertNoErrors(); 95 | 96 | int expectedStoredMB = (int) (maxMgPersistenceCache * EvictExpirableRecordsPersistence.PERCENTAGE_MEMORY_STORED_TO_STOP); 97 | assertThat(expectedStoredMB, is(disk.storedMB())); 98 | } 99 | 100 | @Test public void When_Reached_Memory_Threshold_But_Not_Expirable_Records_Do_Not_Evict() { 101 | int maxMgPersistenceCache = 5; 102 | 103 | evictExpirableRecordsPersistenceUT = new EvictExpirableRecordsPersistence(memory, disk, maxMgPersistenceCache); 104 | 105 | populate(false); 106 | assertThat(disk.allKeys().size(), is(mocksCount())); 107 | 108 | TestSubscriber testSubscriber = new TestSubscriber(); 109 | evictExpirableRecordsPersistenceUT.startTaskIfNeeded().subscribe(testSubscriber); 110 | testSubscriber.awaitTerminalEvent(); 111 | testSubscriber.assertNoErrors(); 112 | testSubscriber.assertNoValues(); 113 | 114 | assertThat(sizeMbDataPopulated(), is(disk.storedMB())); 115 | 116 | //after first time does not start process again, just return warning message 117 | testSubscriber = new TestSubscriber(); 118 | evictExpirableRecordsPersistenceUT.startTaskIfNeeded().subscribe(testSubscriber); 119 | testSubscriber.awaitTerminalEvent(); 120 | testSubscriber.assertNoErrors(); 121 | testSubscriber.assertValue(Locale.RECORD_CAN_NOT_BE_EVICTED_BECAUSE_NO_ONE_IS_EXPIRABLE); 122 | } 123 | 124 | //7 mb 125 | private void populate(boolean expirable) { 126 | for (int i = 0; i < mocksCount(); i++) { 127 | List mocks = new ArrayList(mocksCount()); 128 | 129 | for (int z = 0; z < mocksCount(); z++) { 130 | Mock mock = new Mock("Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC," + 131 | "making it over 2000 years old.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 132 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 133 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC."); 134 | mocks.add(mock); 135 | } 136 | 137 | Record> record = new Record<>(mocks, expirable, 1); 138 | disk.saveRecord(String.valueOf(i), record); 139 | } 140 | } 141 | 142 | private int mocksCount() { 143 | return 100; 144 | } 145 | 146 | private int sizeMbDataPopulated() { 147 | return 7; 148 | } 149 | } -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/cache/EvictExpiredRecordsPersistenceTest.java: -------------------------------------------------------------------------------- 1 | package io.rx_cache.internal.cache; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | 7 | import io.rx_cache.internal.Record; 8 | import io.rx_cache.internal.Memory; 9 | import io.rx_cache.internal.Mock; 10 | import io.rx_cache.internal.cache.memory.ReferenceMapMemory; 11 | import io.rx_cache.internal.common.BaseTest; 12 | import rx.observers.TestSubscriber; 13 | 14 | import static org.hamcrest.MatcherAssert.assertThat; 15 | import static org.hamcrest.core.Is.is; 16 | 17 | /** 18 | * Created by victor on 03/03/16. 19 | */ 20 | public class EvictExpiredRecordsPersistenceTest extends BaseTest { 21 | private EvictExpiredRecordsPersistence evictExpiredRecordsPersistenceUT; 22 | private HasRecordExpired hasRecordExpired; 23 | private TwoLayersCache twoLayersCache; 24 | private final Memory memory = new ReferenceMapMemory(); 25 | private static final long ONE_SECOND_LIFE = 1000, THIRTY_SECOND_LIFE = 30000, MORE_THAN_ONE_SECOND_LIFE = 1250; 26 | 27 | @Override public void setUp() { 28 | super.setUp(); 29 | 30 | twoLayersCache = new TwoLayersCache(evictRecord(memory), retrieveRecord(memory), saveRecord(memory)); 31 | hasRecordExpired = new HasRecordExpired(); 32 | evictExpiredRecordsPersistenceUT = new EvictExpiredRecordsPersistence(memory, disk, hasRecordExpired); 33 | } 34 | 35 | @Test public void Evict_Just_Expired_Records() { 36 | int recordsCount = 100; 37 | 38 | for (int i = 0; i < recordsCount/2; i++) { 39 | twoLayersCache.save(i+"_expired", "", "", new Mock(i+"_expired"), ONE_SECOND_LIFE, true); 40 | twoLayersCache.save(i+"_live", "", "", new Mock(i+"_live"), THIRTY_SECOND_LIFE, true); 41 | } 42 | 43 | waitTime(MORE_THAN_ONE_SECOND_LIFE); 44 | 45 | assertThat(disk.allKeys().size(), is(recordsCount)); 46 | 47 | TestSubscriber testSubscriber = new TestSubscriber(); 48 | evictExpiredRecordsPersistenceUT.startEvictingExpiredRecords().subscribe(testSubscriber); 49 | testSubscriber.awaitTerminalEvent(); 50 | testSubscriber.assertNoErrors(); 51 | 52 | List allKeys = disk.allKeys(); 53 | assertThat(allKeys.size(), is(recordsCount / 2)); 54 | 55 | for (String key : allKeys) { 56 | key = key.substring(0, key.indexOf("$")); 57 | Record record = twoLayersCache.retrieve(key, "", "", false, THIRTY_SECOND_LIFE); 58 | assert(record.getData().getMessage().contains("live")); 59 | assert(!record.getData().getMessage().contains("expired")); 60 | } 61 | } 62 | 63 | private SaveRecord saveRecord(Memory memory) { 64 | return new SaveRecord(memory, disk, 100, new EvictExpirableRecordsPersistence(memory, disk, 100)); 65 | } 66 | 67 | private EvictRecord evictRecord(Memory memory) { 68 | return new EvictRecord(memory, disk); 69 | } 70 | 71 | private RetrieveRecord retrieveRecord(Memory memory) { 72 | return new RetrieveRecord(memory, disk, new EvictRecord(memory, disk), new HasRecordExpired()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/cache/SaveRecordTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.cache; 18 | 19 | import org.junit.Test; 20 | import org.junit.experimental.theories.DataPoint; 21 | import org.junit.experimental.theories.Theories; 22 | import org.junit.experimental.theories.Theory; 23 | import org.junit.runner.RunWith; 24 | 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import io.rx_cache.internal.Memory; 29 | import io.rx_cache.internal.Mock; 30 | import io.rx_cache.internal.cache.memory.ReferenceMapMemory; 31 | import io.rx_cache.internal.common.BaseTest; 32 | 33 | import static org.hamcrest.core.Is.is; 34 | import static org.junit.Assert.assertThat; 35 | import static org.junit.Assert.assertTrue; 36 | 37 | @RunWith(Theories.class) 38 | public class SaveRecordTest extends BaseTest { 39 | private Memory memory; 40 | private SaveRecord saveRecordUT; 41 | 42 | @DataPoint public static Integer _10_MB = 10; 43 | @DataPoint public static Integer _20_MB = 20; 44 | @DataPoint public static Integer _30_MB = 30; 45 | 46 | @Override public void setUp() { 47 | super.setUp(); 48 | memory = new ReferenceMapMemory(); 49 | } 50 | 51 | @Test @Theory public void When_Max_Persistence_Exceed_Do_Not_Persists_Data(Integer maxMB) { 52 | saveRecordUT = new SaveRecord(memory, disk, maxMB, new EvictExpirableRecordsPersistence(memory, disk, 100)); 53 | 54 | int records = 250; 55 | 56 | //39 megabytes of memory 57 | for (int i = 0; i < records; i++) { 58 | saveRecordUT.save(i+"", "", "", createMocks(records), 0, true); 59 | } 60 | 61 | assertTrue("storedMB minor or equal than " + maxMB, disk.storedMB() <= maxMB); 62 | assertThat(memory.keySet().size(), is(records)); 63 | } 64 | 65 | private List createMocks(int size) { 66 | List mocks = new ArrayList(size); 67 | 68 | for (int i = 0; i < size; i++) { 69 | mocks.add(new Mock("Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC," + 70 | "making it over 2000 years old.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 71 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 72 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC.")); 73 | } 74 | 75 | return mocks; 76 | } 77 | } -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/common/BaseTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.common; 18 | 19 | import org.junit.Before; 20 | import org.junit.Rule; 21 | import org.junit.rules.TemporaryFolder; 22 | 23 | import io.rx_cache.internal.Disk; 24 | 25 | 26 | public class BaseTest { 27 | protected Disk disk; 28 | @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); 29 | 30 | @Before public void setUp() { 31 | disk = new Disk(temporaryFolder.getRoot()); 32 | } 33 | 34 | protected void waitTime(long millis) { 35 | try { 36 | Thread.sleep(millis); 37 | } catch (InterruptedException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/common/BaseTestEvictingTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.common; 18 | 19 | import java.io.File; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | import io.rx_cache.internal.Mock; 24 | import rx.Observable; 25 | 26 | public class BaseTestEvictingTask { 27 | 28 | protected void waitTime(long millis) { 29 | try { 30 | Thread.sleep(millis); 31 | } catch (InterruptedException e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | 36 | protected int getSizeMB(File dir) { 37 | long bytes = 0; 38 | 39 | File[] files = dir.listFiles(); 40 | for (File file: files) { 41 | bytes += file.length(); 42 | } 43 | 44 | return (int) Math.ceil(bytes/1024.0/1024.0); 45 | } 46 | 47 | protected Observable> createObservableMocks() { 48 | List mocks = new ArrayList(); 49 | 50 | for (int i = 0; i < 100; i++) { 51 | Mock mock = new Mock("Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC," + 52 | "making it over 2000 years old.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 53 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 54 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC."+ 55 | "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC," + 56 | "making it over 2000 years old.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 57 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 58 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC." + 59 | "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC," + 60 | "making it over 2000 years old.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 61 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 62 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC."+ 63 | "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC," + 64 | "making it over 2000 years old.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 65 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 66 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC."+ 67 | "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC," + 68 | "making it over 2000 years old.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 69 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 70 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC."+ 71 | "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC," + 72 | "making it over 2000 years old.Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 73 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, " + 74 | "making it over 2000 years old. Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC."); 75 | mocks.add(mock); 76 | } 77 | 78 | return Observable.just(mocks); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/migration/DeleteRecordMatchingClassNameTest.java: -------------------------------------------------------------------------------- 1 | package io.rx_cache.internal.migration; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Arrays; 6 | 7 | import io.rx_cache.internal.Record; 8 | import io.rx_cache.internal.common.BaseTest; 9 | import rx.observers.TestSubscriber; 10 | 11 | import static org.hamcrest.MatcherAssert.assertThat; 12 | import static org.hamcrest.core.Is.is; 13 | 14 | public class DeleteRecordMatchingClassNameTest extends BaseTest { 15 | private DeleteRecordMatchingClassName deleteRecordMatchingClassNameUT; 16 | private TestSubscriber testSubscriber; 17 | 18 | @Override public void setUp() { 19 | super.setUp(); 20 | deleteRecordMatchingClassNameUT = new DeleteRecordMatchingClassName(disk); 21 | } 22 | 23 | @Test public void When_Class_Matches_Delete_Record_1() { 24 | disk.saveRecord(Mock1.KEY, new Record(new Mock1(), true, 0l)); 25 | disk.saveRecord(Mock2.KEY, new Record(new Mock2(), true, 0l)); 26 | 27 | assertThat(disk.allKeys().size(), is(2)); 28 | 29 | testSubscriber = new TestSubscriber<>(); 30 | deleteRecordMatchingClassNameUT.with(Arrays.asList(Mock1.class)).react().subscribe(testSubscriber); 31 | testSubscriber.awaitTerminalEvent(); 32 | assertThat(disk.allKeys().size(), is(1)); 33 | } 34 | 35 | @Test public void When_Class_Matches_Delete_Record_2() { 36 | disk.saveRecord(Mock1.KEY, new Record(new Mock1(), true, 0l)); 37 | disk.saveRecord(Mock2.KEY, new Record(new Mock2(), true, 0l)); 38 | 39 | assertThat(disk.allKeys().size(), is(2)); 40 | 41 | testSubscriber = new TestSubscriber<>(); 42 | deleteRecordMatchingClassNameUT.with(Arrays.asList(Mock1.class, Mock2.class)).react().subscribe(testSubscriber); 43 | testSubscriber.awaitTerminalEvent(); 44 | assertThat(disk.allKeys().size(), is(0)); 45 | } 46 | 47 | @Test public void When_Class_Matches_Delete_Record_1_List() { 48 | disk.saveRecord(Mock1.KEY, new Record(Arrays.asList(new Mock1()), true, 0l)); 49 | disk.saveRecord(Mock2.KEY, new Record(Arrays.asList(new Mock2()), true, 0l)); 50 | 51 | assertThat(disk.allKeys().size(), is(2)); 52 | 53 | testSubscriber = new TestSubscriber<>(); 54 | deleteRecordMatchingClassNameUT.with(Arrays.asList(Mock1.class)).react().subscribe(testSubscriber); 55 | testSubscriber.awaitTerminalEvent(); 56 | assertThat(disk.allKeys().size(), is(1)); 57 | } 58 | 59 | @Test public void When_Class_Matches_Delete_Record_2_List() { 60 | disk.saveRecord(Mock1.KEY, new Record(Arrays.asList(new Mock1()), true, 0l)); 61 | disk.saveRecord(Mock2.KEY, new Record(Arrays.asList(new Mock2()), true, 0l)); 62 | 63 | assertThat(disk.allKeys().size(), is(2)); 64 | 65 | testSubscriber = new TestSubscriber<>(); 66 | deleteRecordMatchingClassNameUT.with(Arrays.asList(Mock1.class, Mock2.class)).react().subscribe(testSubscriber); 67 | testSubscriber.awaitTerminalEvent(); 68 | assertThat(disk.allKeys().size(), is(0)); 69 | } 70 | 71 | private class Mock1 { 72 | private static final String KEY = "Mock1"; 73 | } 74 | 75 | private class Mock2 { 76 | private static final String KEY = "Mock2"; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/migration/GetCacheVersionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | 20 | import org.junit.Test; 21 | 22 | import io.rx_cache.internal.common.BaseTest; 23 | import rx.observers.TestSubscriber; 24 | 25 | import static org.hamcrest.core.Is.is; 26 | import static org.junit.Assert.assertThat; 27 | 28 | public class GetCacheVersionTest extends BaseTest { 29 | private GetCacheVersion getCacheVersionUT; 30 | private TestSubscriber versionTestSubscriber; 31 | 32 | @Override public void setUp() { 33 | super.setUp(); 34 | getCacheVersionUT = new GetCacheVersion(disk); 35 | versionTestSubscriber = new TestSubscriber<>(); 36 | } 37 | 38 | @Test public void When_No_Version_Specified_Then_Return_0() { 39 | getCacheVersionUT.react().subscribe(versionTestSubscriber); 40 | versionTestSubscriber.awaitTerminalEvent(); 41 | int currentVersion = versionTestSubscriber.getOnNextEvents().get(0); 42 | 43 | assertThat(currentVersion, is(0)); 44 | } 45 | 46 | @Test public void When_Version_Specified_Then_Get_It() { 47 | disk.save(GetCacheVersion.KEY_CACHE_VERSION, 5); 48 | 49 | getCacheVersionUT.react().subscribe(versionTestSubscriber); 50 | versionTestSubscriber.awaitTerminalEvent(); 51 | int currentVersion = versionTestSubscriber.getOnNextEvents().get(0); 52 | 53 | assertThat(currentVersion, is(5)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/migration/GetClassesToEvictFromMigrationsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | 23 | import java.lang.annotation.Annotation; 24 | import java.util.Arrays; 25 | import java.util.List; 26 | 27 | import io.rx_cache.Migration; 28 | import io.rx_cache.SchemeMigration; 29 | import rx.observers.TestSubscriber; 30 | 31 | import static org.hamcrest.core.Is.is; 32 | import static org.junit.Assert.assertThat; 33 | 34 | public class GetClassesToEvictFromMigrationsTest { 35 | private GetClassesToEvictFromMigrations getClassesToEvictFromMigrationsUT; 36 | private TestSubscriber> testSubscriber; 37 | 38 | @Before public void setUp() { 39 | getClassesToEvictFromMigrationsUT = new GetClassesToEvictFromMigrations(); 40 | testSubscriber = new TestSubscriber<>(); 41 | } 42 | 43 | @Test public void When_Migration_Contain_One_Class_To_Evict_Get_It() { 44 | Annotation annotation = OneMigrationProviders.class.getAnnotation(SchemeMigration.class); 45 | SchemeMigration schemeMigration = (SchemeMigration) annotation; 46 | List migrations = Arrays.asList(schemeMigration.value()); 47 | 48 | getClassesToEvictFromMigrationsUT.with(migrations).react().subscribe(testSubscriber); 49 | testSubscriber.awaitTerminalEvent(); 50 | 51 | List classes = testSubscriber.getOnNextEvents().get(0); 52 | assertThat(classes.size(), is(1)); 53 | } 54 | 55 | @Test public void When_Migrations_Contains_Classes_To_Evict_Get_Them() { 56 | Annotation annotation = MigrationsProviders.class.getAnnotation(SchemeMigration.class); 57 | SchemeMigration schemeMigration = (SchemeMigration) annotation; 58 | List migrations = Arrays.asList(schemeMigration.value()); 59 | 60 | getClassesToEvictFromMigrationsUT.with(migrations).react().subscribe(testSubscriber); 61 | testSubscriber.awaitTerminalEvent(); 62 | 63 | List classes = testSubscriber.getOnNextEvents().get(0); 64 | assertThat(classes.size(), is(2)); 65 | } 66 | 67 | @Test public void When_Several_Classes_To_Evict_With_Same_Type_Only_Keep_One() { 68 | Annotation annotation = MigrationsRepeatedProviders.class.getAnnotation(SchemeMigration.class); 69 | SchemeMigration schemeMigration = (SchemeMigration) annotation; 70 | List migrations = Arrays.asList(schemeMigration.value()); 71 | 72 | getClassesToEvictFromMigrationsUT.with(migrations).react().subscribe(testSubscriber); 73 | testSubscriber.awaitTerminalEvent(); 74 | 75 | List classes = testSubscriber.getOnNextEvents().get(0); 76 | assertThat(classes.size(), is(3)); 77 | } 78 | 79 | @SchemeMigration(@Migration(version = 1, evictClasses = {Mock1.class})) 80 | private interface OneMigrationProviders {} 81 | 82 | 83 | @SchemeMigration({ 84 | @Migration(version = 1, evictClasses = {Mock1.class}), 85 | @Migration(version = 2, evictClasses = {Mock2.class}), 86 | }) 87 | private interface MigrationsProviders {} 88 | 89 | 90 | @SchemeMigration({ 91 | @Migration(version = 1, evictClasses = {Mock1.class}), 92 | @Migration(version = 2, evictClasses = {Mock2.class}), 93 | @Migration(version = 3, evictClasses = {Mock1.class}), 94 | @Migration(version = 4, evictClasses = {Mock2.class}), 95 | @Migration(version = 5, evictClasses = {Mock3.class}) 96 | }) 97 | private interface MigrationsRepeatedProviders {} 98 | 99 | private class Mock1 {} 100 | private class Mock2 {} 101 | private class Mock3 {} 102 | } 103 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/migration/GetPendingMigrationsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | package io.rx_cache.internal.migration; 19 | 20 | import org.junit.Before; 21 | import org.junit.Test; 22 | 23 | import java.util.List; 24 | 25 | import io.rx_cache.Migration; 26 | import io.rx_cache.SchemeMigration; 27 | import io.rx_cache.internal.Mock; 28 | import rx.observers.TestSubscriber; 29 | 30 | import static org.hamcrest.CoreMatchers.is; 31 | import static org.junit.Assert.assertThat; 32 | 33 | public class GetPendingMigrationsTest { 34 | private GetPendingMigrations getPendingMigrationsUT; 35 | private TestSubscriber> testSubscriber; 36 | 37 | @Before public void setUp() { 38 | testSubscriber = new TestSubscriber<>(); 39 | } 40 | 41 | @Test public void When_No_Scheme_Migration_Supplied_Then_Retrieve_Empty() { 42 | getPendingMigrationsUT = new GetPendingMigrations(NoSchemeMigration.class); 43 | getPendingMigrationsUT.react().subscribe(testSubscriber); 44 | testSubscriber.awaitTerminalEvent(); 45 | 46 | List migrations = testSubscriber.getOnNextEvents().get(0); 47 | assertThat(migrations.size(), is(0)); 48 | } 49 | 50 | @Test public void When_Scheme_Migration_With_No_Migrations_Supplied_Then_Retrieve_Empty() { 51 | getPendingMigrationsUT = new GetPendingMigrations(SchemeMigrationWithNoMigrations.class); 52 | getPendingMigrationsUT.react().subscribe(testSubscriber); 53 | testSubscriber.awaitTerminalEvent(); 54 | 55 | List migrations = testSubscriber.getOnNextEvents().get(0); 56 | assertThat(migrations.size(), is(0)); 57 | } 58 | 59 | @Test public void When_Migrations_Supplied_Then_Retrieve_Them() { 60 | getPendingMigrationsUT = new GetPendingMigrations(Migrations.class); 61 | getPendingMigrationsUT.react().subscribe(testSubscriber); 62 | testSubscriber.awaitTerminalEvent(); 63 | 64 | List migrations = testSubscriber.getOnNextEvents().get(0); 65 | assertThat(migrations.size(), is(1)); 66 | } 67 | 68 | @Test public void When_Migrations_Supplied_Are_Sorted_Then_Retrieve_Them_Sorted_By_Version() { 69 | getPendingMigrationsUT = new GetPendingMigrations(MigrationsSorted.class); 70 | getPendingMigrationsUT.react().subscribe(testSubscriber); 71 | testSubscriber.awaitTerminalEvent(); 72 | 73 | List migrations = testSubscriber.getOnNextEvents().get(0); 74 | assertThat(migrations.get(0).version(), is(1)); 75 | assertThat(migrations.get(1).version(), is(2)); 76 | assertThat(migrations.get(2).version(), is(3)); 77 | assertThat(migrations.get(3).version(), is(4)); 78 | } 79 | 80 | @Test public void When_Migrations_Supplied_Are_Not_Sorted_Then_Retrieve_Them_Sorted_By_Version() { 81 | getPendingMigrationsUT = new GetPendingMigrations(MigrationsNoSorted.class); 82 | getPendingMigrationsUT.react().subscribe(testSubscriber); 83 | testSubscriber.awaitTerminalEvent(); 84 | 85 | List migrations = testSubscriber.getOnNextEvents().get(0); 86 | assertThat(migrations.get(0).version(), is(1)); 87 | assertThat(migrations.get(1).version(), is(2)); 88 | assertThat(migrations.get(2).version(), is(3)); 89 | assertThat(migrations.get(3).version(), is(4)); 90 | } 91 | 92 | 93 | @Test public void When_Migrations_Supplied_And_Version_Cache_Then_Get_Only_Pending_Migrations() { 94 | getPendingMigrationsUT = new GetPendingMigrations(MigrationsSorted.class); 95 | getPendingMigrationsUT.with(2).react().subscribe(testSubscriber); 96 | testSubscriber.awaitTerminalEvent(); 97 | 98 | List migrations = testSubscriber.getOnNextEvents().get(0); 99 | assertThat(migrations.get(0).version(), is(3)); 100 | assertThat(migrations.get(1).version(), is(4)); 101 | 102 | testSubscriber = new TestSubscriber<>(); 103 | getPendingMigrationsUT = new GetPendingMigrations(MigrationsSorted.class); 104 | getPendingMigrationsUT.with(0).react().subscribe(testSubscriber); 105 | testSubscriber.awaitTerminalEvent(); 106 | 107 | migrations = testSubscriber.getOnNextEvents().get(0); 108 | assertThat(migrations.get(0).version(), is(1)); 109 | assertThat(migrations.get(1).version(), is(2)); 110 | assertThat(migrations.get(2).version(), is(3)); 111 | assertThat(migrations.get(3).version(), is(4)); 112 | 113 | testSubscriber = new TestSubscriber<>(); 114 | getPendingMigrationsUT = new GetPendingMigrations(MigrationsSorted.class); 115 | getPendingMigrationsUT.with(4).react().subscribe(testSubscriber); 116 | testSubscriber.awaitTerminalEvent(); 117 | migrations = testSubscriber.getOnNextEvents().get(0); 118 | assertThat(migrations.size(), is(0)); 119 | } 120 | 121 | private class NoSchemeMigration {} 122 | 123 | @SchemeMigration(value = {}) 124 | private class SchemeMigrationWithNoMigrations {} 125 | 126 | @SchemeMigration(@Migration(version = 1, evictClasses = {Mock.class})) 127 | private class Migrations {} 128 | 129 | @SchemeMigration({ 130 | @Migration(version = 1, evictClasses = {Mock.class}), 131 | @Migration(version = 2, evictClasses = {Mock.class}), 132 | @Migration(version = 3, evictClasses = {Mock.class}), 133 | @Migration(version = 4, evictClasses = {Mock.class}) 134 | }) 135 | private class MigrationsSorted {} 136 | 137 | @SchemeMigration({ 138 | @Migration(version = 4, evictClasses = {Mock.class}), 139 | @Migration(version = 2, evictClasses = {Mock.class}), 140 | @Migration(version = 1, evictClasses = {Mock.class}), 141 | @Migration(version = 3, evictClasses = {Mock.class}) 142 | }) 143 | private class MigrationsNoSorted {} 144 | } 145 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/migration/ProvidersRxCacheMigrations.java: -------------------------------------------------------------------------------- 1 | package io.rx_cache.internal.migration; 2 | 3 | import org.junit.ClassRule; 4 | import org.junit.Test; 5 | import org.junit.rules.TemporaryFolder; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import io.rx_cache.Migration; 11 | import io.rx_cache.SchemeMigration; 12 | import io.rx_cache.internal.ProxyProviders; 13 | import io.rx_cache.internal.RxCache; 14 | import rx.Observable; 15 | import rx.observers.TestSubscriber; 16 | 17 | import static org.hamcrest.MatcherAssert.assertThat; 18 | import static org.hamcrest.core.Is.is; 19 | 20 | public class ProvidersRxCacheMigrations { 21 | @ClassRule static public TemporaryFolder temporaryFolder = new TemporaryFolder(); 22 | 23 | @Test public void _1_Populate_Mocks() { 24 | populateMocks(); 25 | } 26 | 27 | @Test public void _2_When_Migrations_Working_Request_Will_Be_Hold_Until_Finish() { 28 | int countFiles = temporaryFolder.getRoot().listFiles().length; 29 | assert countFiles > 0; 30 | 31 | ProvidersMigrations providersMigrations = new RxCache.Builder() 32 | .persistence(temporaryFolder.getRoot()) 33 | .using(ProvidersMigrations.class); 34 | 35 | TestSubscriber> testSubscriber = new TestSubscriber<>(); 36 | providersMigrations.getMocks(Observable.>just(null)).subscribe(testSubscriber); 37 | testSubscriber.awaitTerminalEvent(); 38 | testSubscriber.assertNoValues(); 39 | testSubscriber.assertError(ProxyProviders.RxCacheException.class); 40 | } 41 | 42 | private void populateMocks() { 43 | Providers providers = new RxCache.Builder() 44 | .persistence(temporaryFolder.getRoot()) 45 | .using(Providers.class); 46 | 47 | TestSubscriber> testSubscriber = new TestSubscriber<>(); 48 | providers.getMocks(getMocks()).subscribe(testSubscriber); 49 | testSubscriber.awaitTerminalEvent(); 50 | 51 | assertThat(testSubscriber.getOnNextEvents().get(0).size(), is(SIZE_MOCKS)); 52 | } 53 | 54 | private static int SIZE_MOCKS = 1000; 55 | private Observable> getMocks() { 56 | List mocks = new ArrayList<>(); 57 | 58 | for (int i = 0; i < SIZE_MOCKS; i++) { 59 | mocks.add(new Mock1()); 60 | } 61 | 62 | return Observable.just(mocks); 63 | } 64 | 65 | private interface Providers { 66 | Observable> getMocks(Observable> mocks); 67 | } 68 | 69 | @SchemeMigration({@Migration(version = 1, evictClasses = Mock1.class)}) 70 | private interface ProvidersMigrations { 71 | Observable> getMocks(Observable> mocks); 72 | } 73 | 74 | private class Mock1 { 75 | private final String payload = "Lorem Ipsum is simply dummy text of the printing and " + 76 | "typesetting industry. Lorem Ipsum has been the industry's standard dummy text " + 77 | "ever since the 1500s, when an unknown printer took a galley of type and scrambled " + 78 | "it to make a type specimen book. It has survived not only five centuries"; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/src/test/java/io/rx_cache/internal/migration/UpgradeCacheVersionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Victor Albertos 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.rx_cache.internal.migration; 18 | 19 | import org.junit.Test; 20 | 21 | import java.lang.annotation.Annotation; 22 | import java.util.Arrays; 23 | import java.util.List; 24 | 25 | import io.rx_cache.Migration; 26 | import io.rx_cache.SchemeMigration; 27 | import io.rx_cache.internal.common.BaseTest; 28 | import rx.observers.TestSubscriber; 29 | 30 | import static org.hamcrest.core.Is.is; 31 | import static org.junit.Assert.assertThat; 32 | 33 | public class UpgradeCacheVersionTest extends BaseTest { 34 | private UpgradeCacheVersion upgradeCacheVersionUT; 35 | private GetCacheVersion getCacheVersion; 36 | private TestSubscriber upgradeTestSubscriber; 37 | private TestSubscriber versionTestSubscriber; 38 | 39 | @Override public void setUp() { 40 | super.setUp(); 41 | upgradeCacheVersionUT = new UpgradeCacheVersion(disk); 42 | getCacheVersion = new GetCacheVersion(disk); 43 | 44 | upgradeTestSubscriber = new TestSubscriber<>(); 45 | versionTestSubscriber = new TestSubscriber<>(); 46 | } 47 | 48 | @Test public void When_Upgrade_Version_Upgrade_It() { 49 | Annotation annotation = Migrations.class.getAnnotation(SchemeMigration.class); 50 | SchemeMigration schemeMigration = (SchemeMigration) annotation; 51 | List migrations = Arrays.asList(schemeMigration.value()); 52 | 53 | upgradeCacheVersionUT.with(migrations).react().subscribe(upgradeTestSubscriber); 54 | upgradeTestSubscriber.awaitTerminalEvent(); 55 | upgradeTestSubscriber.assertNoErrors(); 56 | upgradeTestSubscriber.assertCompleted(); 57 | 58 | getCacheVersion.react().subscribe(versionTestSubscriber); 59 | versionTestSubscriber.awaitTerminalEvent(); 60 | int currentVersion = versionTestSubscriber.getOnNextEvents().get(0); 61 | 62 | assertThat(currentVersion, is(5)); 63 | } 64 | 65 | 66 | @SchemeMigration({ 67 | @Migration(version = 1, evictClasses = {Mock1.class}), 68 | @Migration(version = 3, evictClasses = {Mock1.class}), 69 | @Migration(version = 4, evictClasses = {Mock1.class}), 70 | @Migration(version = 5, evictClasses = {Mock1.class}) 71 | }) 72 | private interface Migrations {} 73 | 74 | private class Mock1 { 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sungerk/RxCache/97b96550916566204f4b54c88d8434254eaef568/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Apr 23 15:37:18 CEST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':core', ':android', ':compiler' 2 | --------------------------------------------------------------------------------