aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnonymous <no-reply@google.com>2017-08-10 00:23:15 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-08-10 00:23:15 +0000
commit8b42d211a58fc5b434921a51cae2c99a806e00eb (patch)
tree840682988d908a367eadc4d2231bde1e43134333
parentfd51dba5e6a4bb935fb4357a445848aee24eba11 (diff)
parentcc4c86984e7c6d0de90ea4362506fc5ca857bc68 (diff)
downloadvolley-android-o-mr1-preview-2.tar.gz
Import of Volley from GitHub to AOSP. - 27f4207774f7a0b59168f5b3b53d8c0c3841de64 Merge pull request #71 from google/fix-visibility by Jeff Davidson <jpd236@cornell.edu> - e1d75bc49fff2f43b4304688f22ccb57f0c2782c Merge pull request #66 from joebowbeer/dbc_overflow by Jeff Davidson <jpd236@cornell.edu> - d40b75f52f0f8e3467d63294142610a040ff8cd5 Merge pull request #64 from xyhuangjinfu/master by Jeff Davidson <jpd236@cornell.edu> - aafd2e5d1279d68cd2c39b02a915e501974daf2f Merge pull request #52 from joebowbeer/dbc by Jeff Davidson <jpd236@cornell.edu> - baefc4e4f7ad42ce84972716486928ab77b413fc Merge pull request #23 from google/javadoc-fixes by Jeff Davidson <jpd236@cornell.edu> - dea97629925acea8942c06a7ff52938eba5dc78c Merge pull request #39 from google/publish by Jeff Davidson <jpd236@cornell.edu> - f52e1e0d4a18c8e3e7004a3d60c8cbec9a04f3d4 Merge pull request #38 from SamuelYvon/master by Jeff Davidson <jpd236@cornell.edu> - f0ab9a527d3a525325139026a7da0318b506abb4 Merge pull request #41 from LappleApple/patch-1 by Jeff Davidson <jpd236@cornell.edu> - a444f81d099179ab7c5fa5f460262a206125cd37 Merge pull request #18 from Ericliu001/master by Jeff Davidson <jpd236@cornell.edu>android-o-mr1-preview-2android-o-mr1-preview-1
am: cc4c86984e Change-Id: I3c1cbe4af3237ac4bf2f54e4276bbc60151af82e
-rw-r--r--.travis.yml9
-rw-r--r--README.md4
-rw-r--r--bintray-info-template.json21
-rw-r--r--bintray.gradle72
-rw-r--r--build.xml92
-rw-r--r--custom_rules.xml10
-rw-r--r--pom.xml168
-rwxr-xr-xpublish-snapshot-on-commit.sh13
-rw-r--r--rules.gradle14
-rw-r--r--src/main/java/com/android/volley/Cache.java14
-rw-r--r--src/main/java/com/android/volley/CacheDispatcher.java1
-rw-r--r--src/main/java/com/android/volley/Network.java2
-rw-r--r--src/main/java/com/android/volley/RequestQueue.java28
-rw-r--r--src/main/java/com/android/volley/Response.java4
-rw-r--r--src/main/java/com/android/volley/ResponseDelivery.java6
-rw-r--r--src/main/java/com/android/volley/RetryPolicy.java6
-rw-r--r--src/main/java/com/android/volley/VolleyLog.java8
-rw-r--r--src/main/java/com/android/volley/toolbox/Authenticator.java4
-rw-r--r--src/main/java/com/android/volley/toolbox/BasicNetwork.java8
-rw-r--r--src/main/java/com/android/volley/toolbox/ByteArrayPool.java4
-rw-r--r--src/main/java/com/android/volley/toolbox/DiskBasedCache.java225
-rw-r--r--src/main/java/com/android/volley/toolbox/HttpHeaderParser.java2
-rw-r--r--src/main/java/com/android/volley/toolbox/HttpStack.java2
-rw-r--r--src/main/java/com/android/volley/toolbox/HurlStack.java30
-rw-r--r--src/main/java/com/android/volley/toolbox/ImageLoader.java6
-rw-r--r--src/main/java/com/android/volley/toolbox/ImageRequest.java6
-rw-r--r--src/main/java/com/android/volley/toolbox/JsonRequest.java4
-rw-r--r--src/main/java/com/android/volley/toolbox/NetworkImageView.java7
-rw-r--r--src/main/java/com/android/volley/toolbox/StringRequest.java4
-rw-r--r--src/test/java/com/android/volley/mock/TestRequest.java4
-rw-r--r--src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java431
31 files changed, 678 insertions, 531 deletions
diff --git a/.travis.yml b/.travis.yml
index 5f6fa73..4c80cfd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,3 +25,12 @@ cache:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
- $HOME/.android/build-cache
+
+env:
+ global:
+ - secure: pQb1CpD3heCma1831L4rnZGHgn6acFoBMlRrMBWc7nXXM5WwQ4cMSwZ49TVGMMVobA76vKm6dZj4n5ut5/i9amS9Ohlxbpjw5jVp1dqgev6PMupimkYfeTjTVdfGDlian8J7g4kQcVBXghq2WR+rUVh+VlhenE7r7lzEAscWRAKmo0Dzv1E3idI9ik8gkcAdE0ICJmYoq7vxxBhI3XzofHdq0bkzaCzU4P4OSX5HFN8Z4EloTeEeKZj0f02NwS+rrYzhNDs+8619W1gffAjJ9+C0im7iU8axoxMDE9e5dO4PEO03ol/U7F1NNmwFJ+Q78f9qmNw8u4EYnfzUn7nkH+GvYvX9jGlJoNqXPet0eKsJVIShx5eZjpgtUO0ICZKxYuqtKVfj55MPicMLhKFr1VvUTI4noomQVraUAtnI/H0Ia28795WLry4lbMRuYAwCpsmKJKKNQ5sj3dkokpMmn7SH64zNIxKHpAEkrg3UUWh0LUvk7ePn45RAPXS5M0EX0z6LvQEi5dRVfSc2b69CX9vXyqoG/29xwdaYV+BSZjc1zDFN9BXAwDWhfs86ONSmovi5SXoKd/7FWoEAQVX/ZAW93HuScq5pMq7RYbjN4IThqhCC/K6TgwC2PRtPaIK/Rucc9Gvs8Gh5nphabSDzIPoGUEXXPG+lsG10G9qxGKU=
+ - secure: NhFfhYu6WNuFNpJt3GoGMYpWdYv3xfS7FExeEidvIol+GbuglCv/FOVYVFdHnE52+lvmLz1UTdOSvMhbQe6mhL+ZJQYCILH5i3ion92MlH6YWaJisafzBvSrhUjMFzo0LEJwJ9k3FsAARc0QR+GIsyd0un4MrybeZwFX0VzLElxRl5bEXnxXqYjdw7Mzs3fyROnxees1waz6Ksu3v8NPk45ooy633DGl3txVI/05rT89WwtUnvz0ieAAMiOUTOWs6lAkb45hAzdBng+U5YNL/BiK1y+o4PFYCF7nNJ2A2s6qG/hZYiTa3tLR2tgb5sDHQl4KZ2YEVdEGCZ52nY5vNXAWWZIp1thJYOmaVXFHn/YG8+Qpr2+maUHjzlttIOWl41JSN54emyrK5ZbbAKzMOYKZeRcDP41oP9YlqnsPSL2GBbxqG2GAzv5btHXQPTJVpbqwZUOjYM6jetzRifIWisreN8qQ3cWXLe5l/ra0E6TUbOBH2fC0tZwYe6tkmSGnskA81KwmX7qqKdeJvlAYbGS6MylOSlMmrYqmpKCpofatT1/ZYrnZZSPNRZGQt0L+D1LaYMYlEkSqboxb/OCBL+oD6ncsJWT2YYTQSPdqfmUHF8pNi3hCJe04pqVCbaoEYyMrPlZ+PeZnYDuNSC+Moyu+z7Cqikqg2flzH+F0Az0=
+
+# Publish a SNAPSHOT build for all commits to master.
+after_success:
+ - ./publish-snapshot-on-commit.sh \ No newline at end of file
diff --git a/README.md b/README.md
index 3ea6982..78ca41e 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
### Volley
-Volley is an HTTP library that makes networking for Android apps easier and most
+Volley is an HTTP library that makes networking for Android apps easier and, most
importantly, faster.
-For more about Volley and how to use it, visit the [Android developer training
+For more information about Volley and how to use it, visit the [Android developer training
page](https://developer.android.com/training/volley/index.html).
diff --git a/bintray-info-template.json b/bintray-info-template.json
new file mode 100644
index 0000000..4021964
--- /dev/null
+++ b/bintray-info-template.json
@@ -0,0 +1,21 @@
+{
+ "package": {
+ "user_org": "android",
+ "repo": "android-utils",
+ "group": "com.android.volley",
+ "name": "com.android.volley.volley",
+ "desc": "Volley Android library",
+ "licenses": ["Apache-2.0"],
+ "website_url": "https://github.com/google/volley",
+ "issue_tracker_url": "https://github.com/google/volley/issues",
+ "vcs_url": "https://github.com/google/volley.git",
+ "labels": ["android", "volley", "network"],
+ "public_download_numbers": true
+ },
+ "version": {
+ "name": "$VERSION$",
+ "desc": "Volley Android library version $VERSION$",
+ "gpgSign": false
+ },
+ "publish": true
+} \ No newline at end of file
diff --git a/bintray.gradle b/bintray.gradle
index f07693e..8914c1d 100644
--- a/bintray.gradle
+++ b/bintray.gradle
@@ -3,7 +3,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2"
+ classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4.0.0"
}
}
@@ -12,13 +12,12 @@ buildscript {
// is currently not capable of loading plugins by Id if the dependency is anywhere else than
// in the main project build.gradle. This file is "imported" into the project's build.gradle
// through a "apply from:".
-apply plugin: com.jfrog.bintray.gradle.BintrayPlugin
+apply plugin: org.jfrog.gradle.plugin.artifactory.ArtifactoryPlugin
apply plugin: 'maven-publish'
-project.ext.group = 'com.android.volley'
-project.ext.archivesBaseName = 'volley'
-project.ext.version = '1.0.0'
-project.ext.pomDesc = 'Volley Android library'
+def bintrayInfoFilePath = "$buildDir/outputs/bintray-descriptor.bintray-info.json"
+
+project.ext.version = '1.0.1-SNAPSHOT'
task sourcesJar(type: Jar) {
classifier = 'sources'
@@ -35,6 +34,16 @@ task javadocJar(type: Jar, dependsOn: javadoc) {
from javadoc.destinationDir
}
+task bintrayInfoFile {
+ outputs.file(bintrayInfoFilePath)
+ doLast {
+ println 'Creating bintray-info.json'
+ String fileContent = new File("$rootDir/bintray-info-template.json").getText('UTF-8')
+ fileContent = fileContent.replace('$VERSION$', project.ext.version)
+ ((new File(bintrayInfoFilePath))).write(fileContent)
+ }
+}
+
artifacts {
archives javadocJar
archives sourcesJar
@@ -43,45 +52,36 @@ artifacts {
publishing {
publications {
library(MavenPublication) {
- groupId project.ext.group
- artifactId project.ext.archivesBaseName
+ groupId 'com.android.volley'
+ artifactId 'volley'
version project.ext.version
// Release AAR, Sources, and JavaDoc
artifact "$buildDir/outputs/aar/volley-release.aar"
artifact sourcesJar
artifact javadocJar
+ artifact(bintrayInfoFilePath) {
+ builtBy bintrayInfoFile
+ extension "bintray-info.json"
+ }
}
}
}
-bintray {
- user = System.env.BINTRAY_USER
- key = System.env.BINTRAY_USER_KEY
-
- publications = [ 'library' ]
-
- publish = project.hasProperty("release")
- pkg {
- userOrg = 'android'
- repo = 'android-utils'
- group = project.ext.group
- name = project.ext.group + '.' + project.ext.archivesBaseName
- desc = project.ext.pomDesc
- licenses = [ 'Apache-2.0' ]
- websiteUrl = 'https://tools.android.com'
- issueTrackerUrl = 'https://code.google.com/p/android/'
- vcsUrl = 'https://android.googlesource.com/platform/frameworks/volley.git'
- labels = ['android', 'volley', 'network']
- publicDownloadNumbers = true
-
- version {
- name = project.ext.version
- desc = project.ext.pomDesc + ' version ' + project.ext.version
- gpg {
- sign = true
- passphrase = System.env.GPG_PASSPHRASE
- }
+artifactory {
+ contextUrl = "https://oss.jfrog.org"
+ publish {
+ repository {
+ repoKey = 'oss-snapshot-local'
+ username = System.env.CI_DEPLOY_USERNAME
+ password = System.env.CI_DEPLOY_PASSWORD
+ }
+ defaults {
+ publications('library')
+ publishArtifacts = true
}
}
-}
+ resolve {
+ repoKey = 'jcenter'
+ }
+} \ No newline at end of file
diff --git a/build.xml b/build.xml
deleted file mode 100644
index 219c63c..0000000
--- a/build.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="volley" default="help">
-
- <!-- The local.properties file is created and updated by the 'android' tool.
- It contains the path to the SDK. It should *NOT* be checked into
- Version Control Systems. -->
- <property file="local.properties" />
-
- <!-- The ant.properties file can be created by you. It is only edited by the
- 'android' tool to add properties to it.
- This is the place to change some Ant specific build properties.
- Here are some properties you may want to change/update:
-
- source.dir
- The name of the source directory. Default is 'src'.
- out.dir
- The name of the output directory. Default is 'bin'.
-
- For other overridable properties, look at the beginning of the rules
- files in the SDK, at tools/ant/build.xml
-
- Properties related to the SDK location or the project target should
- be updated using the 'android' tool with the 'update' action.
-
- This file is an integral part of the build system for your
- application and should be checked into Version Control Systems.
-
- -->
- <property file="ant.properties" />
-
- <!-- if sdk.dir was not set from one of the property file, then
- get it from the ANDROID_HOME env var.
- This must be done before we load project.properties since
- the proguard config can use sdk.dir -->
- <property environment="env" />
- <condition property="sdk.dir" value="${env.ANDROID_HOME}">
- <isset property="env.ANDROID_HOME" />
- </condition>
-
- <!-- The project.properties file is created and updated by the 'android'
- tool, as well as ADT.
-
- This contains project specific properties such as project target, and library
- dependencies. Lower level build properties are stored in ant.properties
- (or in .classpath for Eclipse projects).
-
- This file is an integral part of the build system for your
- application and should be checked into Version Control Systems. -->
- <loadproperties srcFile="project.properties" />
-
- <!-- quick check on sdk.dir -->
- <fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
- unless="sdk.dir"
- />
-
- <!--
- Import per project custom build rules if present at the root of the project.
- This is the place to put custom intermediary targets such as:
- -pre-build
- -pre-compile
- -post-compile (This is typically used for code obfuscation.
- Compiled code location: ${out.classes.absolute.dir}
- If this is not done in place, override ${out.dex.input.absolute.dir})
- -post-package
- -post-build
- -pre-clean
- -->
- <import file="custom_rules.xml" optional="true" />
-
- <!-- Import the actual build file.
-
- To customize existing targets, there are two options:
- - Customize only one target:
- - copy/paste the target into this file, *before* the
- <import> task.
- - customize it to your needs.
- - Customize the whole content of build.xml
- - copy/paste the content of the rules files (minus the top node)
- into this file, replacing the <import> task.
- - customize to your needs.
-
- ***********************
- ****** IMPORTANT ******
- ***********************
- In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
- in order to avoid having your file be overridden by tools such as "android update project"
- -->
- <!-- version-tag: 1 -->
- <import file="${sdk.dir}/tools/ant/build.xml" />
-
-</project>
diff --git a/custom_rules.xml b/custom_rules.xml
deleted file mode 100644
index 1b94e5d..0000000
--- a/custom_rules.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="volley-rules" default="help">
-
- <!-- A rule to generate the JAR for inclusion in an Android
- application. Output file will be bin/volley.jar -->
- <target name="jar" depends="-compile">
- <jar destfile="bin/volley.jar"
- basedir="bin/classes" />
- </target>
-</project>
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index 7c37e0f..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,168 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <groupId>com.android.volley</groupId>
- <artifactId>volley</artifactId>
- <version>1.0-SNAPSHOT</version>
- <packaging>jar</packaging>
-
- <name>volley</name>
- <url>http://android.com</url>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-
- <java.version>1.6</java.version>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>com.google.android</groupId>
- <artifactId>android</artifactId>
- <version>4.1.1.4</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.10</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.robolectric</groupId>
- <artifactId>robolectric</artifactId>
- <version>3.0</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>1.9.5</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <pluginManagement>
- <plugins>
- <plugin>
- <groupId>com.jayway.maven.plugins.android.generation2</groupId>
- <artifactId>android-maven-plugin</artifactId>
- <version>3.8.1</version>
- <configuration>
- <sdk>
- <platform>19</platform>
- </sdk>
- </configuration>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>3.0</version>
- <configuration>
- <source>${java.version}</source>
- <target>${java.version}</target>
- </configuration>
- </plugin>
- </plugins>
- </pluginManagement>
- </build>
-
- <profiles>
- <profile>
- <id>debug</id>
- <activation>
- <activeByDefault>true</activeByDefault>
- <property>
- <name>performDebugBuild</name>
- <value>true</value>
- </property>
- </activation>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <version>2.18.1</version>
- <executions>
- <execution>
- <id>default-test</id>
- <configuration>
- <argLine>${surefireArgLine}</argLine>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <!-- don't upgrade the version. newer versions generate different results
- see https://github.com/jacoco/jacoco/issues/286 -->
- <version>0.7.2.201409121644</version>
- <executions>
- <execution>
- <id>pre-unit-test</id>
- <goals>
- <goal>prepare-agent</goal>
- </goals>
- <configuration>
- <destFile>${project.build.directory}/surefire-reports/jacoco-ut.exec</destFile>
- <propertyName>surefireArgLine</propertyName>
- </configuration>
- </execution>
- <execution>
- <id>jacoco-report</id>
- <phase>post-integration-test</phase>
- <goals>
- <goal>report</goal>
- <goal>check</goal>
- </goals>
- <configuration>
- <dataFile>${project.build.directory}/surefire-reports/jacoco-ut.exec</dataFile>
- <outputDirectory>${project.build.directory}/jacoco-report</outputDirectory>
- <rules>
- <rule>
- <element>BUNDLE</element>
- <limits>
- <limit>
- <counter>INSTRUCTION</counter>
- <value>COVEREDRATIO</value>
- <minimum>0.40</minimum>
- </limit>
- <!-- enable this if you want that the build breaks if there is a class without a test -->
- <!--
- <limit>
- <counter>CLASS</counter>
- <value>MISSEDCOUNT</value>
- <maximum>0</maximum>
- </limit>
- -->
- </limits>
- </rule>
- <!-- enable this if you want a limit for each java class -->
- <!--
- <rule>
- <element>CLASS</element>
- <excludes>
- <exclude>*Test</exclude>
- </excludes>
- <limits>
- <limit>
- <counter>LINE</counter>
- <value>COVEREDRATIO</value>
- <minimum>0.10</minimum>
- </limit>
- </limits>
- </rule>
- -->
- </rules>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
-</project>
diff --git a/publish-snapshot-on-commit.sh b/publish-snapshot-on-commit.sh
new file mode 100755
index 0000000..0d0e034
--- /dev/null
+++ b/publish-snapshot-on-commit.sh
@@ -0,0 +1,13 @@
+set -eu
+
+if [ "$TRAVIS_REPO_SLUG" == "google/volley" ] && \
+ [ "$TRAVIS_PULL_REQUEST" == "false" ] && \
+ [ "$TRAVIS_BRANCH" == "master" ]; then
+ echo -e "Publishing snapshot build to OJO...\n"
+
+ ./gradlew artifactoryPublish
+
+ echo -e "Published snapshot build to OJO"
+else
+ echo -e "Not publishing snapshot"
+fi \ No newline at end of file
diff --git a/rules.gradle b/rules.gradle
index c7c0f70..af81ac2 100644
--- a/rules.gradle
+++ b/rules.gradle
@@ -14,17 +14,9 @@ android {
// Check if the android plugin version supports unit testing.
if (configurations.findByName("testCompile")) {
dependencies {
- testCompile "junit:junit:4.10"
- testCompile "org.mockito:mockito-core:1.9.5"
+ testCompile "junit:junit:4.12"
+ testCompile "org.hamcrest:hamcrest-library:1.3"
+ testCompile "org.mockito:mockito-core:2.2.29"
testCompile "org.robolectric:robolectric:3.0"
}
}
-
-// TODO(#4): Remove this once Javadoc errors are fixed
-if (JavaVersion.current().isJava8Compatible()) {
- allprojects {
- tasks.withType(Javadoc) {
- options.addStringOption('Xdoclint:none', '-quiet')
- }
- }
-}
diff --git a/src/main/java/com/android/volley/Cache.java b/src/main/java/com/android/volley/Cache.java
index f1ec757..8482c22 100644
--- a/src/main/java/com/android/volley/Cache.java
+++ b/src/main/java/com/android/volley/Cache.java
@@ -28,43 +28,43 @@ public interface Cache {
* @param key Cache key
* @return An {@link Entry} or null in the event of a cache miss
*/
- public Entry get(String key);
+ Entry get(String key);
/**
* Adds or replaces an entry to the cache.
* @param key Cache key
* @param entry Data to store and metadata for cache coherency, TTL, etc.
*/
- public void put(String key, Entry entry);
+ void put(String key, Entry entry);
/**
* Performs any potentially long-running actions needed to initialize the cache;
* will be called from a worker thread.
*/
- public void initialize();
+ void initialize();
/**
* Invalidates an entry in the cache.
* @param key Cache key
* @param fullExpire True to fully expire the entry, false to soft expire
*/
- public void invalidate(String key, boolean fullExpire);
+ void invalidate(String key, boolean fullExpire);
/**
* Removes an entry from the cache.
* @param key Cache key
*/
- public void remove(String key);
+ void remove(String key);
/**
* Empties the cache.
*/
- public void clear();
+ void clear();
/**
* Data and metadata for an entry returned by the cache.
*/
- public static class Entry {
+ class Entry {
/** The data returned from cache. */
public byte[] data;
diff --git a/src/main/java/com/android/volley/CacheDispatcher.java b/src/main/java/com/android/volley/CacheDispatcher.java
index 18d219b..1e7dfc4 100644
--- a/src/main/java/com/android/volley/CacheDispatcher.java
+++ b/src/main/java/com/android/volley/CacheDispatcher.java
@@ -151,7 +151,6 @@ public class CacheDispatcher extends Thread {
if (mQuit) {
return;
}
- continue;
}
}
}
diff --git a/src/main/java/com/android/volley/Network.java b/src/main/java/com/android/volley/Network.java
index ab45830..1e367c8 100644
--- a/src/main/java/com/android/volley/Network.java
+++ b/src/main/java/com/android/volley/Network.java
@@ -26,5 +26,5 @@ public interface Network {
* @return A {@link NetworkResponse} with data and caching metadata; will never be null
* @throws VolleyError on errors
*/
- public NetworkResponse performRequest(Request<?> request) throws VolleyError;
+ NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
diff --git a/src/main/java/com/android/volley/RequestQueue.java b/src/main/java/com/android/volley/RequestQueue.java
index 4324590..0f2e756 100644
--- a/src/main/java/com/android/volley/RequestQueue.java
+++ b/src/main/java/com/android/volley/RequestQueue.java
@@ -40,13 +40,13 @@ import java.util.concurrent.atomic.AtomicInteger;
public class RequestQueue {
/** Callback interface for completed requests. */
- public static interface RequestFinishedListener<T> {
+ public interface RequestFinishedListener<T> {
/** Called when a request has finished processing. */
- public void onRequestFinished(Request<T> request);
+ void onRequestFinished(Request<T> request);
}
/** Used for generating monotonically-increasing sequence numbers for requests. */
- private AtomicInteger mSequenceGenerator = new AtomicInteger();
+ private final AtomicInteger mSequenceGenerator = new AtomicInteger();
/**
* Staging area for requests that already have a duplicate request in flight.
@@ -59,7 +59,7 @@ public class RequestQueue {
* </ul>
*/
private final Map<String, Queue<Request<?>>> mWaitingRequests =
- new HashMap<String, Queue<Request<?>>>();
+ new HashMap<>();
/**
* The set of all requests currently being processed by this RequestQueue. A Request
@@ -70,11 +70,11 @@ public class RequestQueue {
/** The cache triage queue. */
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
- new PriorityBlockingQueue<Request<?>>();
+ new PriorityBlockingQueue<>();
/** The queue of requests that are actually going out to the network. */
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
- new PriorityBlockingQueue<Request<?>>();
+ new PriorityBlockingQueue<>();
/** Number of network request dispatcher threads to start. */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
@@ -89,13 +89,13 @@ public class RequestQueue {
private final ResponseDelivery mDelivery;
/** The network dispatchers. */
- private NetworkDispatcher[] mDispatchers;
+ private final NetworkDispatcher[] mDispatchers;
/** The cache dispatcher. */
private CacheDispatcher mCacheDispatcher;
- private List<RequestFinishedListener> mFinishedListeners =
- new ArrayList<RequestFinishedListener>();
+ private final List<RequestFinishedListener> mFinishedListeners =
+ new ArrayList<>();
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
@@ -160,9 +160,9 @@ public class RequestQueue {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
- for (int i = 0; i < mDispatchers.length; i++) {
- if (mDispatchers[i] != null) {
- mDispatchers[i].quit();
+ for (final NetworkDispatcher mDispatcher : mDispatchers) {
+ if (mDispatcher != null) {
+ mDispatcher.quit();
}
}
}
@@ -186,7 +186,7 @@ public class RequestQueue {
* {@link RequestQueue#cancelAll(RequestFilter)}.
*/
public interface RequestFilter {
- public boolean apply(Request<?> request);
+ boolean apply(Request<?> request);
}
/**
@@ -248,7 +248,7 @@ public class RequestQueue {
// There is already a request in flight. Queue up.
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
- stagedRequests = new LinkedList<Request<?>>();
+ stagedRequests = new LinkedList<>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
diff --git a/src/main/java/com/android/volley/Response.java b/src/main/java/com/android/volley/Response.java
index 1165595..1fe7215 100644
--- a/src/main/java/com/android/volley/Response.java
+++ b/src/main/java/com/android/volley/Response.java
@@ -26,7 +26,7 @@ public class Response<T> {
/** Callback interface for delivering parsed responses. */
public interface Listener<T> {
/** Called when a response is received. */
- public void onResponse(T response);
+ void onResponse(T response);
}
/** Callback interface for delivering error responses. */
@@ -35,7 +35,7 @@ public class Response<T> {
* Callback method that an error has been occurred with the
* provided error code and optional user-readable message.
*/
- public void onErrorResponse(VolleyError error);
+ void onErrorResponse(VolleyError error);
}
/** Returns a successful response containing the parsed result. */
diff --git a/src/main/java/com/android/volley/ResponseDelivery.java b/src/main/java/com/android/volley/ResponseDelivery.java
index 87706af..bef3df5 100644
--- a/src/main/java/com/android/volley/ResponseDelivery.java
+++ b/src/main/java/com/android/volley/ResponseDelivery.java
@@ -20,16 +20,16 @@ public interface ResponseDelivery {
/**
* Parses a response from the network or cache and delivers it.
*/
- public void postResponse(Request<?> request, Response<?> response);
+ void postResponse(Request<?> request, Response<?> response);
/**
* Parses a response from the network or cache and delivers it. The provided
* Runnable will be executed after delivery.
*/
- public void postResponse(Request<?> request, Response<?> response, Runnable runnable);
+ void postResponse(Request<?> request, Response<?> response, Runnable runnable);
/**
* Posts an error for the given request.
*/
- public void postError(Request<?> request, VolleyError error);
+ void postError(Request<?> request, VolleyError error);
}
diff --git a/src/main/java/com/android/volley/RetryPolicy.java b/src/main/java/com/android/volley/RetryPolicy.java
index 0dd198b..f58678d 100644
--- a/src/main/java/com/android/volley/RetryPolicy.java
+++ b/src/main/java/com/android/volley/RetryPolicy.java
@@ -24,12 +24,12 @@ public interface RetryPolicy {
/**
* Returns the current timeout (used for logging).
*/
- public int getCurrentTimeout();
+ int getCurrentTimeout();
/**
* Returns the current retry count (used for logging).
*/
- public int getCurrentRetryCount();
+ int getCurrentRetryCount();
/**
* Prepares for the next retry by applying a backoff to the timeout.
@@ -37,5 +37,5 @@ public interface RetryPolicy {
* @throws VolleyError In the event that the retry could not be performed (for example if we
* ran out of attempts), the passed in error is thrown.
*/
- public void retry(VolleyError error) throws VolleyError;
+ void retry(VolleyError error) throws VolleyError;
}
diff --git a/src/main/java/com/android/volley/VolleyLog.java b/src/main/java/com/android/volley/VolleyLog.java
index ffe9eb8..fc776e5 100644
--- a/src/main/java/com/android/volley/VolleyLog.java
+++ b/src/main/java/com/android/volley/VolleyLog.java
@@ -25,8 +25,8 @@ import java.util.Locale;
/**
* Logging helper class.
- * <p/>
- * to see Volley logs call:<br/>
+ * <p>
+ * to see Volley logs call:<br>
* {@code <android-sdk>/platform-tools/adb shell setprop log.tag.Volley VERBOSE}
*/
public class VolleyLog {
@@ -37,9 +37,9 @@ public class VolleyLog {
/**
* Customize the log tag for your application, so that other apps
* using Volley don't mix their logs with yours.
- * <br />
+ * <br>
* Enable the log property for your tag before starting your app:
- * <br />
+ * <br>
* {@code adb shell setprop log.tag.&lt;tag&gt;}
*/
public static void setTag(String tag) {
diff --git a/src/main/java/com/android/volley/toolbox/Authenticator.java b/src/main/java/com/android/volley/toolbox/Authenticator.java
index d9f5e3c..adfc996 100644
--- a/src/main/java/com/android/volley/toolbox/Authenticator.java
+++ b/src/main/java/com/android/volley/toolbox/Authenticator.java
@@ -27,10 +27,10 @@ public interface Authenticator {
*
* @throws AuthFailureError If authentication did not succeed
*/
- public String getAuthToken() throws AuthFailureError;
+ String getAuthToken() throws AuthFailureError;
/**
* Invalidates the provided auth token.
*/
- public void invalidateAuthToken(String authToken);
+ void invalidateAuthToken(String authToken);
}
diff --git a/src/main/java/com/android/volley/toolbox/BasicNetwork.java b/src/main/java/com/android/volley/toolbox/BasicNetwork.java
index 37c35ec..96fb66e 100644
--- a/src/main/java/com/android/volley/toolbox/BasicNetwork.java
+++ b/src/main/java/com/android/volley/toolbox/BasicNetwork.java
@@ -57,9 +57,9 @@ import java.util.TreeMap;
public class BasicNetwork implements Network {
protected static final boolean DEBUG = VolleyLog.DEBUG;
- private static int SLOW_REQUEST_THRESHOLD_MS = 3000;
+ private static final int SLOW_REQUEST_THRESHOLD_MS = 3000;
- private static int DEFAULT_POOL_SIZE = 4096;
+ private static final int DEFAULT_POOL_SIZE = 4096;
protected final HttpStack mHttpStack;
@@ -257,7 +257,7 @@ public class BasicNetwork implements Network {
} catch (IOException e) {
// This can happen if there was an exception above that left the entity in
// an invalid state.
- VolleyLog.v("Error occured when calling consumingContent");
+ VolleyLog.v("Error occurred when calling consumingContent");
}
mPool.returnBuf(buffer);
bytes.close();
@@ -265,7 +265,7 @@ public class BasicNetwork implements Network {
}
/**
- * Converts Headers[] to Map<String, String>.
+ * Converts Headers[] to Map&lt;String, String&gt;.
*/
protected static Map<String, String> convertHeaders(Header[] headers) {
Map<String, String> result = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
diff --git a/src/main/java/com/android/volley/toolbox/ByteArrayPool.java b/src/main/java/com/android/volley/toolbox/ByteArrayPool.java
index af95076..c8ca2c2 100644
--- a/src/main/java/com/android/volley/toolbox/ByteArrayPool.java
+++ b/src/main/java/com/android/volley/toolbox/ByteArrayPool.java
@@ -53,8 +53,8 @@ import java.util.List;
*/
public class ByteArrayPool {
/** The buffer pool, arranged both by last use and by buffer size */
- private List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();
- private List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);
+ private final List<byte[]> mBuffersByLastUse = new LinkedList<byte[]>();
+ private final List<byte[]> mBuffersBySize = new ArrayList<byte[]>(64);
/** The total size of the buffers in the pool */
private int mCurrentSize = 0;
diff --git a/src/main/java/com/android/volley/toolbox/DiskBasedCache.java b/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
index f724d72..0e65183 100644
--- a/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
+++ b/src/main/java/com/android/volley/toolbox/DiskBasedCache.java
@@ -17,15 +17,18 @@
package com.android.volley.toolbox;
import android.os.SystemClock;
+import android.text.TextUtils;
import com.android.volley.Cache;
import com.android.volley.VolleyLog;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
@@ -110,30 +113,31 @@ public class DiskBasedCache implements Cache {
if (entry == null) {
return null;
}
-
File file = getFileForKey(key);
- CountingInputStream cis = null;
try {
- cis = new CountingInputStream(new BufferedInputStream(new FileInputStream(file)));
- CacheHeader.readHeader(cis); // eat header
- byte[] data = streamToBytes(cis, (int) (file.length() - cis.bytesRead));
- return entry.toCacheEntry(data);
+ CountingInputStream cis = new CountingInputStream(
+ new BufferedInputStream(createInputStream(file)), file.length());
+ try {
+ CacheHeader entryOnDisk = CacheHeader.readHeader(cis);
+ if (!TextUtils.equals(key, entryOnDisk.key)) {
+ // File was shared by two keys and now holds data for a different entry!
+ VolleyLog.d("%s: key=%s, found=%s",
+ file.getAbsolutePath(), key, entryOnDisk.key);
+ // Remove key whose contents on disk have been replaced.
+ removeEntry(key);
+ return null;
+ }
+ byte[] data = streamToBytes(cis, cis.bytesRemaining());
+ return entry.toCacheEntry(data);
+ } finally {
+ // Any IOException thrown here is handled by the below catch block by design.
+ //noinspection ThrowFromFinallyBlock
+ cis.close();
+ }
} catch (IOException e) {
VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
remove(key);
return null;
- } catch (NegativeArraySizeException e) {
- VolleyLog.d("%s: %s", file.getAbsolutePath(), e.toString());
- remove(key);
- return null;
- } finally {
- if (cis != null) {
- try {
- cis.close();
- } catch (IOException ioe) {
- return null;
- }
- }
}
}
@@ -149,28 +153,29 @@ public class DiskBasedCache implements Cache {
}
return;
}
-
File[] files = mRootDirectory.listFiles();
if (files == null) {
return;
}
for (File file : files) {
- BufferedInputStream fis = null;
try {
- fis = new BufferedInputStream(new FileInputStream(file));
- CacheHeader entry = CacheHeader.readHeader(fis);
- entry.size = file.length();
- putEntry(entry.key, entry);
- } catch (IOException e) {
- if (file != null) {
- file.delete();
- }
- } finally {
+ long entrySize = file.length();
+ CountingInputStream cis = new CountingInputStream(
+ new BufferedInputStream(createInputStream(file)), entrySize);
try {
- if (fis != null) {
- fis.close();
- }
- } catch (IOException ignored) { }
+ CacheHeader entry = CacheHeader.readHeader(cis);
+ // NOTE: When this entry was put, its size was recorded as data.length, but
+ // when the entry is initialized below, its size is recorded as file.length()
+ entry.size = entrySize;
+ putEntry(entry.key, entry);
+ } finally {
+ // Any IOException thrown here is handled by the below catch block by design.
+ //noinspection ThrowFromFinallyBlock
+ cis.close();
+ }
+ } catch (IOException e) {
+ //noinspection ResultOfMethodCallIgnored
+ file.delete();
}
}
}
@@ -190,7 +195,6 @@ public class DiskBasedCache implements Cache {
}
put(key, entry);
}
-
}
/**
@@ -201,7 +205,7 @@ public class DiskBasedCache implements Cache {
pruneIfNeeded(entry.data.length);
File file = getFileForKey(key);
try {
- BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(file));
+ BufferedOutputStream fos = new BufferedOutputStream(createOutputStream(file));
CacheHeader e = new CacheHeader(key, entry);
boolean success = e.writeHeader(fos);
if (!success) {
@@ -313,107 +317,118 @@ public class DiskBasedCache implements Cache {
* Removes the entry identified by 'key' from the cache.
*/
private void removeEntry(String key) {
- CacheHeader entry = mEntries.get(key);
- if (entry != null) {
- mTotalSize -= entry.size;
- mEntries.remove(key);
+ CacheHeader removed = mEntries.remove(key);
+ if (removed != null) {
+ mTotalSize -= removed.size;
}
}
/**
- * Reads the contents of an InputStream into a byte[].
- * */
- private static byte[] streamToBytes(InputStream in, int length) throws IOException {
- byte[] bytes = new byte[length];
- int count;
- int pos = 0;
- while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) {
- pos += count;
- }
- if (pos != length) {
- throw new IOException("Expected " + length + " bytes, read " + pos + " bytes");
+ * Reads length bytes from CountingInputStream into byte array.
+ * @param cis input stream
+ * @param length number of bytes to read
+ * @throws IOException if fails to read all bytes
+ */
+ //VisibleForTesting
+ static byte[] streamToBytes(CountingInputStream cis, long length) throws IOException {
+ long maxLength = cis.bytesRemaining();
+ // Length cannot be negative or greater than bytes remaining, and must not overflow int.
+ if (length < 0 || length > maxLength || (int) length != length) {
+ throw new IOException("streamToBytes length=" + length + ", maxLength=" + maxLength);
}
+ byte[] bytes = new byte[(int) length];
+ new DataInputStream(cis).readFully(bytes);
return bytes;
}
+ //VisibleForTesting
+ InputStream createInputStream(File file) throws FileNotFoundException {
+ return new FileInputStream(file);
+ }
+
+ //VisibleForTesting
+ OutputStream createOutputStream(File file) throws FileNotFoundException {
+ return new FileOutputStream(file);
+ }
+
/**
* Handles holding onto the cache headers for an entry.
*/
- // Visible for testing.
+ //VisibleForTesting
static class CacheHeader {
/** The size of the data identified by this CacheHeader. (This is not
* serialized to disk. */
- public long size;
+ long size;
/** The key that identifies the cache entry. */
- public String key;
+ final String key;
/** ETag for cache coherence. */
- public String etag;
+ final String etag;
/** Date of this response as reported by the server. */
- public long serverDate;
+ final long serverDate;
/** The last modified date for the requested object. */
- public long lastModified;
+ final long lastModified;
/** TTL for this record. */
- public long ttl;
+ final long ttl;
/** Soft TTL for this record. */
- public long softTtl;
+ final long softTtl;
/** Headers from the response resulting in this cache entry. */
- public Map<String, String> responseHeaders;
+ final Map<String, String> responseHeaders;
- private CacheHeader() { }
+ private CacheHeader(String key, String etag, long serverDate, long lastModified, long ttl,
+ long softTtl, Map<String, String> responseHeaders) {
+ this.key = key;
+ this.etag = ("".equals(etag)) ? null : etag;
+ this.serverDate = serverDate;
+ this.lastModified = lastModified;
+ this.ttl = ttl;
+ this.softTtl = softTtl;
+ this.responseHeaders = responseHeaders;
+ }
/**
* Instantiates a new CacheHeader object
* @param key The key that identifies the cache entry
* @param entry The cache entry.
*/
- public CacheHeader(String key, Entry entry) {
- this.key = key;
- this.size = entry.data.length;
- this.etag = entry.etag;
- this.serverDate = entry.serverDate;
- this.lastModified = entry.lastModified;
- this.ttl = entry.ttl;
- this.softTtl = entry.softTtl;
- this.responseHeaders = entry.responseHeaders;
+ CacheHeader(String key, Entry entry) {
+ this(key, entry.etag, entry.serverDate, entry.lastModified, entry.ttl, entry.softTtl,
+ entry.responseHeaders);
+ size = entry.data.length;
}
/**
- * Reads the header off of an InputStream and returns a CacheHeader object.
+ * Reads the header from a CountingInputStream and returns a CacheHeader object.
* @param is The InputStream to read from.
- * @throws IOException
+ * @throws IOException if fails to read header
*/
- public static CacheHeader readHeader(InputStream is) throws IOException {
- CacheHeader entry = new CacheHeader();
+ static CacheHeader readHeader(CountingInputStream is) throws IOException {
int magic = readInt(is);
if (magic != CACHE_MAGIC) {
// don't bother deleting, it'll get pruned eventually
throw new IOException();
}
- entry.key = readString(is);
- entry.etag = readString(is);
- if (entry.etag.equals("")) {
- entry.etag = null;
- }
- entry.serverDate = readLong(is);
- entry.lastModified = readLong(is);
- entry.ttl = readLong(is);
- entry.softTtl = readLong(is);
- entry.responseHeaders = readStringStringMap(is);
-
- return entry;
+ String key = readString(is);
+ String etag = readString(is);
+ long serverDate = readLong(is);
+ long lastModified = readLong(is);
+ long ttl = readLong(is);
+ long softTtl = readLong(is);
+ Map<String, String> responseHeaders = readStringStringMap(is);
+ return new CacheHeader(
+ key, etag, serverDate, lastModified, ttl, softTtl, responseHeaders);
}
/**
* Creates a cache entry for the specified data.
*/
- public Entry toCacheEntry(byte[] data) {
+ Entry toCacheEntry(byte[] data) {
Entry e = new Entry();
e.data = data;
e.etag = etag;
@@ -429,7 +444,7 @@ public class DiskBasedCache implements Cache {
/**
* Writes the contents of this CacheHeader to the specified OutputStream.
*/
- public boolean writeHeader(OutputStream os) {
+ boolean writeHeader(OutputStream os) {
try {
writeInt(os, CACHE_MAGIC);
writeString(os, key);
@@ -446,14 +461,16 @@ public class DiskBasedCache implements Cache {
return false;
}
}
-
}
- private static class CountingInputStream extends FilterInputStream {
- private int bytesRead = 0;
+ //VisibleForTesting
+ static class CountingInputStream extends FilterInputStream {
+ private final long length;
+ private long bytesRead;
- private CountingInputStream(InputStream in) {
+ CountingInputStream(InputStream in, long length) {
super(in);
+ this.length = length;
}
@Override
@@ -473,6 +490,15 @@ public class DiskBasedCache implements Cache {
}
return result;
}
+
+ //VisibleForTesting
+ long bytesRead() {
+ return bytesRead;
+ }
+
+ long bytesRemaining() {
+ return length - bytesRead;
+ }
}
/*
@@ -480,6 +506,8 @@ public class DiskBasedCache implements Cache {
* headers on disk. Once upon a time, this used the standard Java
* Object{Input,Output}Stream, but the default implementation relies heavily
* on reflection (even for standard types) and generates a ton of garbage.
+ *
+ * TODO: Replace by standard DataInput and DataOutput in next cache version.
*/
/**
@@ -540,9 +568,9 @@ public class DiskBasedCache implements Cache {
os.write(b, 0, b.length);
}
- static String readString(InputStream is) throws IOException {
- int n = (int) readLong(is);
- byte[] b = streamToBytes(is, n);
+ static String readString(CountingInputStream cis) throws IOException {
+ long n = readLong(cis);
+ byte[] b = streamToBytes(cis, n);
return new String(b, "UTF-8");
}
@@ -558,18 +586,17 @@ public class DiskBasedCache implements Cache {
}
}
- static Map<String, String> readStringStringMap(InputStream is) throws IOException {
- int size = readInt(is);
+ static Map<String, String> readStringStringMap(CountingInputStream cis) throws IOException {
+ int size = readInt(cis);
Map<String, String> result = (size == 0)
? Collections.<String, String>emptyMap()
: new HashMap<String, String>(size);
for (int i = 0; i < size; i++) {
- String key = readString(is).intern();
- String value = readString(is).intern();
+ String key = readString(cis).intern();
+ String value = readString(cis).intern();
result.put(key, value);
}
return result;
}
-
}
diff --git a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
index c3b48d8..f53063c 100644
--- a/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
+++ b/src/main/java/com/android/volley/toolbox/HttpHeaderParser.java
@@ -31,7 +31,7 @@ import java.util.Map;
public class HttpHeaderParser {
/**
- * Extracts a {@link Cache.Entry} from a {@link NetworkResponse}.
+ * Extracts a {@link com.android.volley.Cache.Entry} from a {@link NetworkResponse}.
*
* @param response The network response to parse headers from
* @return a cache entry for the given response, or null if the response is not cacheable.
diff --git a/src/main/java/com/android/volley/toolbox/HttpStack.java b/src/main/java/com/android/volley/toolbox/HttpStack.java
index a52fd06..06f6017 100644
--- a/src/main/java/com/android/volley/toolbox/HttpStack.java
+++ b/src/main/java/com/android/volley/toolbox/HttpStack.java
@@ -39,7 +39,7 @@ public interface HttpStack {
* {@link Request#getHeaders()}
* @return the HTTP response
*/
- public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
+ HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
diff --git a/src/main/java/com/android/volley/toolbox/HurlStack.java b/src/main/java/com/android/volley/toolbox/HurlStack.java
index c53d5e0..66f441d 100644
--- a/src/main/java/com/android/volley/toolbox/HurlStack.java
+++ b/src/main/java/com/android/volley/toolbox/HurlStack.java
@@ -59,7 +59,7 @@ public class HurlStack implements HttpStack {
* Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all.
*/
- public String rewriteUrl(String originalUrl);
+ String rewriteUrl(String originalUrl);
}
private final UrlRewriter mUrlRewriter;
@@ -209,16 +209,8 @@ public class HurlStack implements HttpStack {
// GET. Otherwise, it is assumed that the request is a POST.
byte[] postBody = request.getPostBody();
if (postBody != null) {
- // Prepare output. There is no need to set Content-Length explicitly,
- // since this is handled by HttpURLConnection using the size of the prepared
- // output stream.
- connection.setDoOutput(true);
connection.setRequestMethod("POST");
- connection.addRequestProperty(HEADER_CONTENT_TYPE,
- request.getPostBodyContentType());
- DataOutputStream out = new DataOutputStream(connection.getOutputStream());
- out.write(postBody);
- out.close();
+ addBody(connection, request, postBody);
}
break;
case Method.GET:
@@ -259,11 +251,19 @@ public class HurlStack implements HttpStack {
throws IOException, AuthFailureError {
byte[] body = request.getBody();
if (body != null) {
- connection.setDoOutput(true);
- connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
- DataOutputStream out = new DataOutputStream(connection.getOutputStream());
- out.write(body);
- out.close();
+ addBody(connection, request, body);
}
}
+
+ private static void addBody(HttpURLConnection connection, Request<?> request, byte[] body)
+ throws IOException, AuthFailureError {
+ // Prepare output. There is no need to set Content-Length explicitly,
+ // since this is handled by HttpURLConnection using the size of the prepared
+ // output stream.
+ connection.setDoOutput(true);
+ connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
+ DataOutputStream out = new DataOutputStream(connection.getOutputStream());
+ out.write(body);
+ out.close();
+ }
}
diff --git a/src/main/java/com/android/volley/toolbox/ImageLoader.java b/src/main/java/com/android/volley/toolbox/ImageLoader.java
index d5305e3..33a119b 100644
--- a/src/main/java/com/android/volley/toolbox/ImageLoader.java
+++ b/src/main/java/com/android/volley/toolbox/ImageLoader.java
@@ -72,8 +72,8 @@ public class ImageLoader {
* must not block. Implementation with an LruCache is recommended.
*/
public interface ImageCache {
- public Bitmap getBitmap(String url);
- public void putBitmap(String url, Bitmap bitmap);
+ Bitmap getBitmap(String url);
+ void putBitmap(String url, Bitmap bitmap);
}
/**
@@ -139,7 +139,7 @@ public class ImageLoader {
* image loading in order to, for example, run an animation to fade in network loaded
* images.
*/
- public void onResponse(ImageContainer response, boolean isImmediate);
+ void onResponse(ImageContainer response, boolean isImmediate);
}
/**
diff --git a/src/main/java/com/android/volley/toolbox/ImageRequest.java b/src/main/java/com/android/volley/toolbox/ImageRequest.java
index d663f5f..0f33cd8 100644
--- a/src/main/java/com/android/volley/toolbox/ImageRequest.java
+++ b/src/main/java/com/android/volley/toolbox/ImageRequest.java
@@ -46,7 +46,7 @@ public class ImageRequest extends Request<Bitmap> {
private final Config mDecodeConfig;
private final int mMaxWidth;
private final int mMaxHeight;
- private ScaleType mScaleType;
+ private final ScaleType mScaleType;
/** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
private static final Object sDecodeLock = new Object();
@@ -216,7 +216,9 @@ public class ImageRequest extends Request<Bitmap> {
@Override
protected void deliverResponse(Bitmap response) {
- mListener.onResponse(response);
+ if (mListener != null) {
+ mListener.onResponse(response);
+ }
}
/**
diff --git a/src/main/java/com/android/volley/toolbox/JsonRequest.java b/src/main/java/com/android/volley/toolbox/JsonRequest.java
index 2d58f40..40877b1 100644
--- a/src/main/java/com/android/volley/toolbox/JsonRequest.java
+++ b/src/main/java/com/android/volley/toolbox/JsonRequest.java
@@ -63,7 +63,9 @@ public abstract class JsonRequest<T> extends Request<T> {
@Override
protected void deliverResponse(T response) {
- mListener.onResponse(response);
+ if (mListener != null) {
+ mListener.onResponse(response);
+ }
}
@Override
diff --git a/src/main/java/com/android/volley/toolbox/NetworkImageView.java b/src/main/java/com/android/volley/toolbox/NetworkImageView.java
index 324dbc0..60e4815 100644
--- a/src/main/java/com/android/volley/toolbox/NetworkImageView.java
+++ b/src/main/java/com/android/volley/toolbox/NetworkImageView.java
@@ -147,7 +147,9 @@ public class NetworkImageView extends ImageView {
// The pre-existing content of this view didn't match the current URL. Load the new image
// from the network.
- ImageContainer newContainer = mImageLoader.get(mUrl,
+
+ // update the ImageContainer to be the new bitmap container.
+ mImageContainer = mImageLoader.get(mUrl,
new ImageListener() {
@Override
public void onErrorResponse(VolleyError error) {
@@ -179,9 +181,6 @@ public class NetworkImageView extends ImageView {
}
}
}, maxWidth, maxHeight, scaleType);
-
- // update the ImageContainer to be the new bitmap container.
- mImageContainer = newContainer;
}
private void setDefaultImageOrNull() {
diff --git a/src/main/java/com/android/volley/toolbox/StringRequest.java b/src/main/java/com/android/volley/toolbox/StringRequest.java
index 6b3dfcf..05a62f6 100644
--- a/src/main/java/com/android/volley/toolbox/StringRequest.java
+++ b/src/main/java/com/android/volley/toolbox/StringRequest.java
@@ -57,7 +57,9 @@ public class StringRequest extends Request<String> {
@Override
protected void deliverResponse(String response) {
- mListener.onResponse(response);
+ if (mListener != null) {
+ mListener.onResponse(response);
+ }
}
@Override
diff --git a/src/test/java/com/android/volley/mock/TestRequest.java b/src/test/java/com/android/volley/mock/TestRequest.java
index dfc4dc1..16bf79e 100644
--- a/src/test/java/com/android/volley/mock/TestRequest.java
+++ b/src/test/java/com/android/volley/mock/TestRequest.java
@@ -56,7 +56,7 @@ public class TestRequest {
/** Test example of a POST request in the deprecated style. */
public static class DeprecatedPost extends Base {
- private Map<String, String> mPostParams;
+ private final Map<String, String> mPostParams;
public DeprecatedPost() {
super(TEST_URL, null);
@@ -89,7 +89,7 @@ public class TestRequest {
/** Test example of a POST request in the new style with a body. */
public static class PostWithBody extends Post {
- private Map<String, String> mParams;
+ private final Map<String, String> mParams;
public PostWithBody() {
mParams = new HashMap<String, String>();
diff --git a/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
index 0a8be77..3d8d1f1 100644
--- a/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
+++ b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java
@@ -18,46 +18,371 @@ package com.android.volley.toolbox;
import com.android.volley.Cache;
import com.android.volley.toolbox.DiskBasedCache.CacheHeader;
+import com.android.volley.toolbox.DiskBasedCache.CountingInputStream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
+import java.util.Random;
-import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.arrayWithSize;
+import static org.hamcrest.Matchers.emptyArray;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest="src/main/AndroidManifest.xml", sdk=16)
public class DiskBasedCacheTest {
- // Simple end-to-end serialize/deserialize test.
- @Test public void cacheHeaderSerialization() throws Exception {
- Cache.Entry e = new Cache.Entry();
- e.data = new byte[8];
- e.serverDate = 1234567L;
- e.lastModified = 13572468L;
- e.ttl = 9876543L;
- e.softTtl = 8765432L;
- e.etag = "etag";
- e.responseHeaders = new HashMap<String, String>();
- e.responseHeaders.put("fruit", "banana");
-
- CacheHeader first = new CacheHeader("my-magical-key", e);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- first.writeHeader(baos);
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- CacheHeader second = CacheHeader.readHeader(bais);
+ private static final int MAX_SIZE = 1024 * 1024;
+
+ private Cache cache;
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ @Rule
+ public ExpectedException exception = ExpectedException.none();
+
+ @Before
+ public void setup() throws IOException {
+ // Initialize empty cache
+ cache = new DiskBasedCache(temporaryFolder.getRoot(), MAX_SIZE);
+ cache.initialize();
+ }
+
+ @After
+ public void teardown() {
+ cache = null;
+ }
+
+ @Test
+ public void testEmptyInitialize() {
+ assertThat(cache.get("key"), is(nullValue()));
+ }
+
+ @Test
+ public void testPutGetZeroBytes() {
+ Cache.Entry entry = new Cache.Entry();
+ entry.data = new byte[0];
+ entry.serverDate = 1234567L;
+ entry.lastModified = 13572468L;
+ entry.ttl = 9876543L;
+ entry.softTtl = 8765432L;
+ entry.etag = "etag";
+ entry.responseHeaders = new HashMap<>();
+ entry.responseHeaders.put("fruit", "banana");
+ entry.responseHeaders.put("color", "yellow");
+ cache.put("my-magical-key", entry);
+
+ assertThatEntriesAreEqual(cache.get("my-magical-key"), entry);
+ assertThat(cache.get("unknown-key"), is(nullValue()));
+ }
+
+ @Test
+ public void testPutRemoveGet() {
+ Cache.Entry entry = randomData(511);
+ cache.put("key", entry);
+
+ assertThatEntriesAreEqual(cache.get("key"), entry);
+
+ cache.remove("key");
+ assertThat(cache.get("key"), is(nullValue()));
+ assertThat(listCachedFiles(), is(emptyArray()));
+ }
+
+ @Test
+ public void testPutClearGet() {
+ Cache.Entry entry = randomData(511);
+ cache.put("key", entry);
+
+ assertThatEntriesAreEqual(cache.get("key"), entry);
+
+ cache.clear();
+ assertThat(cache.get("key"), is(nullValue()));
+ assertThat(listCachedFiles(), is(emptyArray()));
+ }
+
+ @Test
+ public void testReinitialize() {
+ Cache.Entry entry = randomData(1023);
+ cache.put("key", entry);
+
+ Cache copy = new DiskBasedCache(temporaryFolder.getRoot(), MAX_SIZE);
+ copy.initialize();
+
+ assertThatEntriesAreEqual(copy.get("key"), entry);
+ }
+
+ @Test
+ public void testInvalidate() {
+ Cache.Entry entry = randomData(32);
+ entry.softTtl = 8765432L;
+ entry.ttl = 9876543L;
+ cache.put("key", entry);
+
+ cache.invalidate("key", false);
+ entry.softTtl = 0; // expired
+ assertThatEntriesAreEqual(cache.get("key"), entry);
+ }
+
+ @Test
+ public void testInvalidateFullExpire() {
+ Cache.Entry entry = randomData(32);
+ entry.softTtl = 8765432L;
+ entry.ttl = 9876543L;
+ cache.put("key", entry);
+
+ cache.invalidate("key", true);
+ entry.softTtl = 0; // expired
+ entry.ttl = 0; // expired
+ assertThatEntriesAreEqual(cache.get("key"), entry);
+ }
+
+ @Test
+ public void testTrim() {
+ Cache.Entry entry = randomData(2 * MAX_SIZE);
+ cache.put("oversize", entry);
+
+ assertThatEntriesAreEqual(cache.get("oversize"), entry);
+
+ entry = randomData(1024);
+ cache.put("kilobyte", entry);
+
+ assertThat(cache.get("oversize"), is(nullValue()));
+ assertThatEntriesAreEqual(cache.get("kilobyte"), entry);
+
+ Cache.Entry entry2 = randomData(1024);
+ cache.put("kilobyte2", entry2);
+ Cache.Entry entry3 = randomData(1024);
+ cache.put("kilobyte3", entry3);
+
+ assertThatEntriesAreEqual(cache.get("kilobyte"), entry);
+ assertThatEntriesAreEqual(cache.get("kilobyte2"), entry2);
+ assertThatEntriesAreEqual(cache.get("kilobyte3"), entry3);
+
+ entry = randomData(MAX_SIZE);
+ cache.put("max", entry);
+
+ assertThat(cache.get("kilobyte"), is(nullValue()));
+ assertThat(cache.get("kilobyte2"), is(nullValue()));
+ assertThat(cache.get("kilobyte3"), is(nullValue()));
+ assertThatEntriesAreEqual(cache.get("max"), entry);
+ }
+
+ @Test
+ @SuppressWarnings("TryFinallyCanBeTryWithResources")
+ public void testGetBadMagic() throws IOException {
+ // Cache something
+ Cache.Entry entry = randomData(1023);
+ cache.put("key", entry);
+ assertThatEntriesAreEqual(cache.get("key"), entry);
+
+ // Overwrite the magic header
+ File cacheFolder = temporaryFolder.getRoot();
+ File file = cacheFolder.listFiles()[0];
+ FileOutputStream fos = new FileOutputStream(file);
+ try {
+ DiskBasedCache.writeInt(fos, 0); // overwrite magic
+ } finally {
+ //noinspection ThrowFromFinallyBlock
+ fos.close();
+ }
+
+ assertThat(cache.get("key"), is(nullValue()));
+ assertThat(listCachedFiles(), is(emptyArray()));
+ }
+
+ @Test
+ @SuppressWarnings("TryFinallyCanBeTryWithResources")
+ public void testGetWrongKey() throws IOException {
+ // Cache something
+ Cache.Entry entry = randomData(1023);
+ cache.put("key", entry);
+ assertThatEntriesAreEqual(cache.get("key"), entry);
+
+ // Access the cached file
+ File cacheFolder = temporaryFolder.getRoot();
+ File file = cacheFolder.listFiles()[0];
+ FileOutputStream fos = new FileOutputStream(file);
+ try {
+ // Overwrite with a different key
+ CacheHeader wrongHeader = new CacheHeader("bad", entry);
+ wrongHeader.writeHeader(fos);
+ } finally {
+ //noinspection ThrowFromFinallyBlock
+ fos.close();
+ }
+
+ // key is gone, but file is still there
+ assertThat(cache.get("key"), is(nullValue()));
+ assertThat(listCachedFiles(), is(arrayWithSize(1)));
+
+ // Note: file is now a zombie because its key does not map to its name
+ }
+
+ @Test
+ public void testStreamToBytesNegativeLength() throws IOException {
+ byte[] data = new byte[1];
+ CountingInputStream cis =
+ new CountingInputStream(new ByteArrayInputStream(data), data.length);
+ exception.expect(IOException.class);
+ DiskBasedCache.streamToBytes(cis, -1);
+ }
- assertEquals(first.key, second.key);
- assertEquals(first.serverDate, second.serverDate);
- assertEquals(first.lastModified, second.lastModified);
- assertEquals(first.ttl, second.ttl);
- assertEquals(first.softTtl, second.softTtl);
- assertEquals(first.etag, second.etag);
- assertEquals(first.responseHeaders, second.responseHeaders);
+ @Test
+ public void testStreamToBytesExcessiveLength() throws IOException {
+ byte[] data = new byte[1];
+ CountingInputStream cis =
+ new CountingInputStream(new ByteArrayInputStream(data), data.length);
+ exception.expect(IOException.class);
+ DiskBasedCache.streamToBytes(cis, 2);
+ }
+
+ @Test
+ public void testStreamToBytesOverflow() throws IOException {
+ byte[] data = new byte[0];
+ CountingInputStream cis =
+ new CountingInputStream(new ByteArrayInputStream(data), 0x100000000L);
+ exception.expect(IOException.class);
+ DiskBasedCache.streamToBytes(cis, 0x100000000L); // int value is 0
}
- @Test public void serializeInt() throws Exception {
+ @Test
+ public void testFileIsDeletedWhenWriteHeaderFails() throws IOException {
+ // Create DataOutputStream that throws IOException
+ OutputStream mockedOutputStream = spy(OutputStream.class);
+ doThrow(IOException.class).when(mockedOutputStream).write(anyInt());
+
+ // Create read-only copy that fails to write anything
+ DiskBasedCache readonly = spy((DiskBasedCache) cache);
+ doReturn(mockedOutputStream).when(readonly).createOutputStream(any(File.class));
+
+ // Attempt to write
+ readonly.put("key", randomData(1111));
+
+ // write is called at least once because each linked stream flushes when closed
+ verify(mockedOutputStream, atLeastOnce()).write(anyInt());
+ assertThat(readonly.get("key"), is(nullValue()));
+ assertThat(listCachedFiles(), is(emptyArray()));
+
+ // Note: original cache will try (without success) to read from file
+ assertThat(cache.get("key"), is(nullValue()));
+ }
+
+ @Test
+ public void testIOExceptionInInitialize() throws IOException {
+ // Cache a few kilobytes
+ cache.put("kilobyte", randomData(1024));
+ cache.put("kilobyte2", randomData(1024));
+ cache.put("kilobyte3", randomData(1024));
+
+ // Create DataInputStream that throws IOException
+ InputStream mockedInputStream = spy(InputStream.class);
+ //noinspection ResultOfMethodCallIgnored
+ doThrow(IOException.class).when(mockedInputStream).read();
+
+ // Create broken cache that fails to read anything
+ DiskBasedCache broken =
+ spy(new DiskBasedCache(temporaryFolder.getRoot()));
+ doReturn(mockedInputStream).when(broken).createInputStream(any(File.class));
+
+ // Attempt to initialize
+ broken.initialize();
+
+ // Everything is gone
+ assertThat(broken.get("kilobyte"), is(nullValue()));
+ assertThat(broken.get("kilobyte2"), is(nullValue()));
+ assertThat(broken.get("kilobyte3"), is(nullValue()));
+ assertThat(listCachedFiles(), is(emptyArray()));
+
+ // Verify that original cache can cope with missing files
+ assertThat(cache.get("kilobyte"), is(nullValue()));
+ assertThat(cache.get("kilobyte2"), is(nullValue()));
+ assertThat(cache.get("kilobyte3"), is(nullValue()));
+ }
+
+ @Test
+ public void testManyResponseHeaders() {
+ Cache.Entry entry = new Cache.Entry();
+ entry.data = new byte[0];
+ entry.responseHeaders = new HashMap<>();
+ for (int i = 0; i < 0xFFFF; i++) {
+ entry.responseHeaders.put(Integer.toString(i), "");
+ }
+ cache.put("key", entry);
+ }
+
+ @Test
+ @SuppressWarnings("TryFinallyCanBeTryWithResources")
+ public void testCountingInputStreamByteCount() throws IOException {
+ // Write some bytes
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ //noinspection ThrowFromFinallyBlock
+ try {
+ DiskBasedCache.writeInt(out, 1);
+ DiskBasedCache.writeLong(out, -1L);
+ DiskBasedCache.writeString(out, "hamburger");
+ } finally {
+ //noinspection ThrowFromFinallyBlock
+ out.close();
+ }
+ long bytesWritten = out.size();
+
+ // Read the bytes and compare the counts
+ CountingInputStream cis =
+ new CountingInputStream(new ByteArrayInputStream(out.toByteArray()), bytesWritten);
+ try {
+ assertThat(cis.bytesRemaining(), is(bytesWritten));
+ assertThat(cis.bytesRead(), is(0L));
+ assertThat(DiskBasedCache.readInt(cis), is(1));
+ assertThat(DiskBasedCache.readLong(cis), is(-1L));
+ assertThat(DiskBasedCache.readString(cis), is("hamburger"));
+ assertThat(cis.bytesRead(), is(bytesWritten));
+ assertThat(cis.bytesRemaining(), is(0L));
+ } finally {
+ //noinspection ThrowFromFinallyBlock
+ cis.close();
+ }
+ }
+
+ /* Serialization tests */
+
+ @Test public void testEmptyReadThrowsEOF() throws IOException {
+ ByteArrayInputStream empty = new ByteArrayInputStream(new byte[]{});
+ exception.expect(EOFException.class);
+ DiskBasedCache.readInt(empty);
+ }
+
+ @Test public void serializeInt() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DiskBasedCache.writeInt(baos, 0);
DiskBasedCache.writeInt(baos, 19791214);
@@ -96,33 +421,35 @@ public class DiskBasedCacheTest {
DiskBasedCache.writeString(baos, "");
DiskBasedCache.writeString(baos, "This is a string.");
DiskBasedCache.writeString(baos, "ファイカス");
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- assertEquals(DiskBasedCache.readString(bais), "");
- assertEquals(DiskBasedCache.readString(bais), "This is a string.");
- assertEquals(DiskBasedCache.readString(bais), "ファイカス");
+ CountingInputStream cis =
+ new CountingInputStream(new ByteArrayInputStream(baos.toByteArray()), baos.size());
+ assertEquals(DiskBasedCache.readString(cis), "");
+ assertEquals(DiskBasedCache.readString(cis), "This is a string.");
+ assertEquals(DiskBasedCache.readString(cis), "ファイカス");
}
@Test public void serializeMap() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- Map<String, String> empty = new HashMap<String, String>();
+ Map<String, String> empty = new HashMap<>();
DiskBasedCache.writeStringStringMap(empty, baos);
DiskBasedCache.writeStringStringMap(null, baos);
- Map<String, String> twoThings = new HashMap<String, String>();
+ Map<String, String> twoThings = new HashMap<>();
twoThings.put("first", "thing");
twoThings.put("second", "item");
DiskBasedCache.writeStringStringMap(twoThings, baos);
- Map<String, String> emptyKey = new HashMap<String, String>();
+ Map<String, String> emptyKey = new HashMap<>();
emptyKey.put("", "value");
DiskBasedCache.writeStringStringMap(emptyKey, baos);
- Map<String, String> emptyValue = new HashMap<String, String>();
+ Map<String, String> emptyValue = new HashMap<>();
emptyValue.put("key", "");
DiskBasedCache.writeStringStringMap(emptyValue, baos);
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- assertEquals(DiskBasedCache.readStringStringMap(bais), empty);
- assertEquals(DiskBasedCache.readStringStringMap(bais), empty); // null reads back empty
- assertEquals(DiskBasedCache.readStringStringMap(bais), twoThings);
- assertEquals(DiskBasedCache.readStringStringMap(bais), emptyKey);
- assertEquals(DiskBasedCache.readStringStringMap(bais), emptyValue);
+ CountingInputStream cis =
+ new CountingInputStream(new ByteArrayInputStream(baos.toByteArray()), baos.size());
+ assertEquals(DiskBasedCache.readStringStringMap(cis), empty);
+ assertEquals(DiskBasedCache.readStringStringMap(cis), empty); // null reads back empty
+ assertEquals(DiskBasedCache.readStringStringMap(cis), twoThings);
+ assertEquals(DiskBasedCache.readStringStringMap(cis), emptyKey);
+ assertEquals(DiskBasedCache.readStringStringMap(cis), emptyValue);
}
@Test
@@ -133,4 +460,28 @@ public class DiskBasedCacheTest {
assertNotNull(DiskBasedCache.class.getMethod("getFileForKey", String.class));
}
+
+ /* Test helpers */
+
+ private void assertThatEntriesAreEqual(Cache.Entry actual, Cache.Entry expected) {
+ assertThat(actual.data, is(equalTo(expected.data)));
+ assertThat(actual.etag, is(equalTo(expected.etag)));
+ assertThat(actual.lastModified, is(equalTo(expected.lastModified)));
+ assertThat(actual.responseHeaders, is(equalTo(expected.responseHeaders)));
+ assertThat(actual.serverDate, is(equalTo(expected.serverDate)));
+ assertThat(actual.softTtl, is(equalTo(expected.softTtl)));
+ assertThat(actual.ttl, is(equalTo(expected.ttl)));
+ }
+
+ private Cache.Entry randomData(int length) {
+ Cache.Entry entry = new Cache.Entry();
+ byte[] data = new byte[length];
+ new Random(42).nextBytes(data); // explicit seed for reproducible results
+ entry.data = data;
+ return entry;
+ }
+
+ private File[] listCachedFiles() {
+ return temporaryFolder.getRoot().listFiles();
+ }
}