Skip to content

Commit

Permalink
Extract http cache as standalone submodule (#667)
Browse files Browse the repository at this point in the history
Extract http cache support as separate artifact

Part of #570
  • Loading branch information
sav007 authored Sep 28, 2017
1 parent 9b124d0 commit 00b9a30
Show file tree
Hide file tree
Showing 45 changed files with 268 additions and 168 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ jdk:
- oraclejdk8

script:
- ./gradlew clean build connectedCheck -x checkstyleTest --stacktrace --max-workers=2 -x apollo-gradle-plugin:test -x apollo-integration:build -x apollo-integration:connectedCheck
- .buildscript/integration_tests_composite.sh
- ./gradlew clean build connectedCheck -x checkstyleTest --stacktrace --max-workers=2 -x apollo-gradle-plugin:test
# -x apollo-integration:build -x apollo-integration:connectedCheck
# - .buildscript/integration_tests_composite.sh

before_script:
- echo no | android create avd --force -n test -t android-15 --abi armeabi-v7a
Expand Down
2 changes: 2 additions & 0 deletions apollo-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ dependencies {
compile dep.jsr305
compile dep.jsr250

compileOnly dep.okHttp

testCompile dep.junit
testCompile dep.truth
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.apollographql.apollo.api.cache.http;

import java.io.IOException;

import javax.annotation.Nonnull;

import okhttp3.Interceptor;
import okhttp3.Response;

/**
* Http GraphQL http request / response cache.
*/
public interface HttpCache {
/**
* Cache key http header
*/
String CACHE_KEY_HEADER = "X-APOLLO-CACHE-KEY";
/**
* Cache fetch strategy http header
*/
String CACHE_FETCH_STRATEGY_HEADER = "X-APOLLO-CACHE-FETCH-STRATEGY";
/**
* Request served Date/time http header
*/
String CACHE_SERVED_DATE_HEADER = "X-APOLLO-SERVED-DATE";
/**
* Prefetch response only flag http header
*/
String CACHE_PREFETCH_HEADER = "X-APOLLO-PREFETCH";
/**
* Cached response expiration timeout http header
*/
String CACHE_EXPIRE_TIMEOUT_HEADER = "X-APOLLO-EXPIRE-TIMEOUT";
/**
* Expire cached response flag http header
*/
String CACHE_EXPIRE_AFTER_READ_HEADER = "X-APOLLO-EXPIRE-AFTER-READ";

/**
* Clear cached http responses
*/
void clear();

/**
* Remove cached http response by key. May throw {@link IOException}
*
* @param cacheKey key of cached response to be removed
*/
void remove(@Nonnull String cacheKey) throws IOException;

/**
* Remove cached http response by key and suppress any exception
*
* @param cacheKey key of cached response to be removed
*/
void removeQuietly(@Nonnull String cacheKey);

/**
* Read cached http response by key
*
* @param cacheKey key of cached response to be read
* @return cached response
*/
Response read(@Nonnull String cacheKey);

/**
* Read and remove cached http response by key if {@code expireAfterRead == true}
*
* @param cacheKey key of cached response to be read
* @param expireAfterRead if {@code true} cached response will be removed after first read
* @return cached response
*/
Response read(@Nonnull String cacheKey, boolean expireAfterRead);

/**
* Provide http cache interceptor to be injected into {@link okhttp3.OkHttpClient#interceptors}. Provided interceptor
* must intercept request and serve cached http response as well as store network response to the http cache store.
*
* @return {@link Interceptor}
*/
Interceptor interceptor();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
package com.apollographql.apollo.cache.http;

import com.apollographql.apollo.internal.cache.http.HttpCacheFetchStrategy;
package com.apollographql.apollo.api.cache.http;

import java.util.concurrent.TimeUnit;

Expand All @@ -16,24 +14,24 @@ public final class HttpCachePolicy {
/**
* Signals the apollo client to fetch the GraphQL query response from the http cache <b>only</b>.
*/
public static final ExpirePolicy CACHE_ONLY = new ExpirePolicy(HttpCacheFetchStrategy.CACHE_ONLY);
public static final ExpirePolicy CACHE_ONLY = new ExpirePolicy(FetchStrategy.CACHE_ONLY);

/**
* Signals the apollo client to fetch the GraphQL query response from the network <b>only</b>.
*/
public static final Policy NETWORK_ONLY = new Policy(HttpCacheFetchStrategy.NETWORK_ONLY, 0, null, false);
public static final Policy NETWORK_ONLY = new Policy(FetchStrategy.NETWORK_ONLY, 0, null, false);

/**
* Signals the apollo client to first fetch the GraphQL query response from the http cache. If it's not present in the
* cache response is fetched from the network.
*/
public static final ExpirePolicy CACHE_FIRST = new ExpirePolicy(HttpCacheFetchStrategy.CACHE_FIRST);
public static final ExpirePolicy CACHE_FIRST = new ExpirePolicy(FetchStrategy.CACHE_FIRST);

/**
* Signals the apollo client to first fetch the GraphQL query response from the network. If it fails then fetch the
* response from the http cache.
*/
public static final ExpirePolicy NETWORK_FIRST = new ExpirePolicy(HttpCacheFetchStrategy.NETWORK_FIRST);
public static final ExpirePolicy NETWORK_FIRST = new ExpirePolicy(FetchStrategy.NETWORK_FIRST);

private HttpCachePolicy() {
}
Expand All @@ -42,13 +40,14 @@ private HttpCachePolicy() {
* Abstraction for http cache policy configurations
*/
public static class Policy {
public final HttpCacheFetchStrategy fetchStrategy;

public final FetchStrategy fetchStrategy;
public final long expireTimeout;
public final TimeUnit expireTimeUnit;
public final boolean expireAfterRead;

Policy(HttpCacheFetchStrategy fetchStrategy, long expireTimeout, TimeUnit expireTimeUnit,
boolean expireAfterRead) {
Policy(FetchStrategy fetchStrategy, long expireTimeout, TimeUnit expireTimeUnit,
boolean expireAfterRead) {
this.fetchStrategy = fetchStrategy;
this.expireTimeout = expireTimeout;
this.expireTimeUnit = expireTimeUnit;
Expand All @@ -67,12 +66,13 @@ public long expireTimeoutMs() {
* Cache policy with provided expiration configuration
*/
public static final class ExpirePolicy extends Policy {
ExpirePolicy(HttpCacheFetchStrategy fetchStrategy) {

ExpirePolicy(FetchStrategy fetchStrategy) {
super(fetchStrategy, 0, null, false);
}

private ExpirePolicy(HttpCacheFetchStrategy fetchStrategy, long expireTimeout, TimeUnit expireTimeUnit,
boolean expireAfterRead) {
private ExpirePolicy(FetchStrategy fetchStrategy, long expireTimeout, TimeUnit expireTimeUnit,
boolean expireAfterRead) {
super(fetchStrategy, expireTimeout, expireTimeUnit, expireAfterRead);
}

Expand All @@ -96,4 +96,28 @@ public ExpirePolicy expireAfterRead() {
return new ExpirePolicy(fetchStrategy, expireTimeout, expireTimeUnit, true);
}
}

/**
* Represents different fetch strategies for http request / response cache
*/
public enum FetchStrategy {
/**
* Signals the apollo client to fetch the GraphQL query response from the http cache <b>only</b>.
*/
CACHE_ONLY,
/**
* Signals the apollo client to fetch the GraphQL query response from the network <b>only</b>.
*/
NETWORK_ONLY,
/**
* Signals the apollo client to first fetch the GraphQL query response from the http cache. If it's not present in
* the cache response is fetched from the network.
*/
CACHE_FIRST,
/**
* Signals the apollo client to first fetch the GraphQL query response from the network. If it fails then fetch the
* response from the http cache.
*/
NETWORK_FIRST
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.apollographql.apollo.cache.http;
package com.apollographql.apollo.api.cache.http;

import javax.annotation.Nonnull;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.apollographql.apollo.cache.http;
package com.apollographql.apollo.api.cache.http;

import java.io.IOException;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.apollographql.apollo.cache.http;
package com.apollographql.apollo.api.cache.http;

import java.io.IOException;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

/**
* ResponseCacheStore is an abstraction for a cache store that is used to read, modify or delete http responses.
* CacheStore is an abstraction for a cache store that is used to read, modify or delete http responses.
*/
public interface HttpCacheStore {
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.apollographql.apollo.internal.util;
package com.apollographql.apollo.internal;

import com.apollographql.apollo.Logger;
import com.apollographql.apollo.api.internal.Optional;
Expand Down
20 changes: 20 additions & 0 deletions apollo-http-cache/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apply plugin: 'java'

targetCompatibility = JavaVersion.VERSION_1_7
sourceCompatibility = JavaVersion.VERSION_1_7

dependencies {
compile dep.jsr305
compile dep.okHttp

compile project(":apollo-api")

testCompile dep.junit
testCompile dep.truth
}

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')

javadoc {
options.encoding = 'UTF-8'
}
4 changes: 4 additions & 0 deletions apollo-http-cache/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
POM_ARTIFACT_ID=apollo-http-cache
POM_NAME=Apollo GraphQL http cache support
POM_DESCRIPTION=Apollo GraphQL http cache support
POM_PACKAGING=jar
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package com.apollographql.apollo.cache.http;

import com.apollographql.apollo.internal.util.ApolloLogger;
import com.apollographql.apollo.Logger;
import com.apollographql.apollo.api.cache.http.HttpCache;
import com.apollographql.apollo.api.cache.http.HttpCacheRecord;
import com.apollographql.apollo.api.cache.http.HttpCacheRecordEditor;
import com.apollographql.apollo.api.cache.http.HttpCacheStore;
import com.apollographql.apollo.api.internal.Optional;
import com.apollographql.apollo.internal.ApolloLogger;

import java.io.IOException;

Expand All @@ -15,47 +21,42 @@
import static com.apollographql.apollo.api.internal.Utils.checkNotNull;
import static com.apollographql.apollo.cache.http.Utils.copyResponseBody;

@SuppressWarnings("WeakerAccess") public final class HttpCache {
public static final String CACHE_KEY_HEADER = "X-APOLLO-CACHE-KEY";
public static final String CACHE_FETCH_STRATEGY_HEADER = "X-APOLLO-CACHE-FETCH-STRATEGY";
public static final String CACHE_SERVED_DATE_HEADER = "X-APOLLO-SERVED-DATE";
public static final String CACHE_PREFETCH_HEADER = "X-APOLLO-PREFETCH";
public static final String CACHE_EXPIRE_TIMEOUT_HEADER = "X-APOLLO-EXPIRE-TIMEOUT";
public static final String CACHE_EXPIRE_AFTER_READ_HEADER = "X-APOLLO-EXPIRE-AFTER-READ";
@SuppressWarnings("WeakerAccess")
public final class ApolloHttpCache implements HttpCache {

private final HttpCacheStore cacheStore;
private final ApolloLogger logger;

public HttpCache(@Nonnull HttpCacheStore cacheStore, @Nonnull ApolloLogger logger) {
this.cacheStore = checkNotNull(cacheStore, "cacheStore can't be null");
this.logger = checkNotNull(logger, "logger can't be null");
public ApolloHttpCache(@Nonnull final HttpCacheStore cacheStore, final Logger logger) {
this.cacheStore = checkNotNull(cacheStore, "cacheStore == null");
this.logger = new ApolloLogger(Optional.fromNullable(logger));
}

public void clear() {
@Override public void clear() {
try {
cacheStore.delete();
} catch (IOException e) {
logger.e(e, "Failed to clear http cache");
}
}

public void remove(@Nonnull String cacheKey) throws IOException {
@Override public void remove(@Nonnull String cacheKey) throws IOException {
cacheStore.remove(cacheKey);
}

public void removeQuietly(@Nonnull String cacheKey) {
@Override public void removeQuietly(@Nonnull String cacheKey) {
try {
remove(cacheKey);
} catch (Exception ignore) {
logger.w(ignore, "Failed to remove cached record for key: %s", cacheKey);
}
}

public Response read(@Nonnull final String cacheKey) {
@Override public Response read(@Nonnull final String cacheKey) {
return read(cacheKey, false);
}

public Response read(@Nonnull final String cacheKey, final boolean expireAfterRead) {
@Override public Response read(@Nonnull final String cacheKey, final boolean expireAfterRead) {
HttpCacheRecord responseCacheRecord = null;
try {
responseCacheRecord = cacheStore.cacheRecord(cacheKey);
Expand All @@ -65,7 +66,8 @@ public Response read(@Nonnull final String cacheKey, final boolean expireAfterRe

final HttpCacheRecord cacheRecord = responseCacheRecord;
Source cacheResponseSource = new ForwardingSource(responseCacheRecord.bodySource()) {
@Override public void close() throws IOException {
@Override
public void close() throws IOException {
super.close();
closeQuietly(cacheRecord);
if (expireAfterRead) {
Expand All @@ -78,16 +80,16 @@ public Response read(@Nonnull final String cacheKey, final boolean expireAfterRe
String contentType = response.header("Content-Type");
String contentLength = response.header("Content-Length");
return response.newBuilder()
.body(new CacheResponseBody(cacheResponseSource, contentType, contentLength))
.build();
.body(new CacheResponseBody(cacheResponseSource, contentType, contentLength))
.build();
} catch (Exception e) {
closeQuietly(responseCacheRecord);
logger.e(e, "Failed to read http cache entry for key: %s", cacheKey);
return null;
}
}

public Interceptor interceptor() {
@Override public Interceptor interceptor() {
return new HttpCacheInterceptor(this, logger);
}

Expand All @@ -104,8 +106,8 @@ Response cacheProxy(@Nonnull Response response, @Nonnull String cacheKey) {
}

return response.newBuilder()
.body(new ResponseBodyProxy(cacheRecordEditor, response, logger))
.build();
.body(new ResponseBodyProxy(cacheRecordEditor, response, logger))
.build();
}
} catch (Exception e) {
abortQuietly(cacheRecordEditor);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.apollographql.apollo.cache.http;

import com.apollographql.apollo.api.cache.http.HttpCacheRecord;
import com.apollographql.apollo.api.cache.http.HttpCacheRecordEditor;
import com.apollographql.apollo.api.cache.http.HttpCacheStore;

import java.io.File;
import java.io.IOException;

Expand Down
Loading

0 comments on commit 00b9a30

Please sign in to comment.