Skip to content

Commit

Permalink
Extract http cache as standalone submodule
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 committed Sep 23, 2017
1 parent a48fb43 commit e59914d
Show file tree
Hide file tree
Showing 46 changed files with 269 additions and 163 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 e59914d

Please sign in to comment.