├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ ├── menu
│ │ │ │ └── menu_main.xml
│ │ │ └── layout
│ │ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── raizlabs
│ │ │ └── webservicemanager
│ │ │ └── MainActivity.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── raizlabs
│ │ └── webservicemanager
│ │ └── ApplicationTest.java
├── build.gradle
└── proguard-rules.pro
├── WebServiceManager
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── raizlabs
│ │ │ │ └── webservicemanager
│ │ │ │ ├── Constants.java
│ │ │ │ ├── ssl
│ │ │ │ ├── TLS.java
│ │ │ │ ├── TrustManager.java
│ │ │ │ ├── TrustDelegate.java
│ │ │ │ ├── TrustManagerFactory.java
│ │ │ │ ├── TrustAllX509TrustManager.java
│ │ │ │ ├── DefaultX509TrustManager.java
│ │ │ │ ├── CachedTrustDelegate.java
│ │ │ │ ├── SimpleSSLSocketFactory.java
│ │ │ │ └── DelegatedX509TrustManager.java
│ │ │ │ ├── requests
│ │ │ │ ├── HttpUriRequestable.java
│ │ │ │ ├── UrlConnectionRequestable.java
│ │ │ │ ├── BaseWebServiceProgress.java
│ │ │ │ ├── StringRequest.java
│ │ │ │ ├── JSONRequest.java
│ │ │ │ ├── JSONArrayRequest.java
│ │ │ │ ├── BitmapRequest.java
│ │ │ │ ├── UploadFileRequest.java
│ │ │ │ ├── DownloadFileRequest.java
│ │ │ │ ├── WebServiceRequest.java
│ │ │ │ ├── BaseWebServiceRequest.java
│ │ │ │ └── RequestBuilder.java
│ │ │ │ ├── webservicemanager
│ │ │ │ ├── WebServiceProgress.java
│ │ │ │ ├── RequestMode.java
│ │ │ │ ├── WebServiceRequestListener.java
│ │ │ │ ├── ResultInfo.java
│ │ │ │ ├── FailedResultInfo.java
│ │ │ │ ├── BasicResultInfo.java
│ │ │ │ └── WebServiceManager.java
│ │ │ │ ├── ProgressInputStreamEntity.java
│ │ │ │ ├── HttpUtils.java
│ │ │ │ ├── HttpMethod.java
│ │ │ │ ├── HttpClientProvider.java
│ │ │ │ ├── responses
│ │ │ │ ├── HttpURLConnectionResponse.java
│ │ │ │ ├── HttpClientResponse.java
│ │ │ │ ├── Response.java
│ │ │ │ └── BaseResponse.java
│ │ │ │ ├── caching
│ │ │ │ ├── SimpleWebFileCache.java
│ │ │ │ └── WebFileCache.java
│ │ │ │ ├── RequestExecutionPool.java
│ │ │ │ ├── json
│ │ │ │ └── JSONNameValuesMap.java
│ │ │ │ └── BasicHttpClientProvider.java
│ │ └── AndroidManifest.xml
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── raizlabs
│ │ └── webservicemanager
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .gitignore
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/WebServiceManager/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':WebServiceManager'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | .DS_Store
4 | /build
5 | /captures
6 | *.iml
7 | .idea
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/AndroidWebServiceManager/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/WebServiceManager/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WebServiceManager
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/AndroidWebServiceManager/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/AndroidWebServiceManager/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/AndroidWebServiceManager/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Rightpoint/AndroidWebServiceManager/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | VERSION_NAME=1.0.3
2 | ARTIFACT_NAME = WebServiceManager
3 | GROUP_NAME = com.raizlabs
4 | GIT_URL = https://github.com/Raizlabs/AndroidWebServiceManager.git
5 | SITE_URL = https://github.com/Raizlabs/AndroidWebServiceManager
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | WebServiceManager
3 |
4 | Hello world!
5 | Settings
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 08 09:59:21 PDT 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/Constants.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | public class Constants {
4 | public static class Defaults {
5 | public static final int ConnectionTimeoutMillis = 30 * 1000;
6 | public static final int ReadTimeoutMillis = 30 * 1000;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ssl/TLS.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.ssl;
2 |
3 | public enum TLS {
4 |
5 | VERSION_1("TLS"),VERSION_1_1("TLSv1.1"),VERSION_1_2("TLSv1.2");
6 |
7 | TLS(String version) {
8 | this.version = version;
9 | }
10 |
11 | private String version;
12 |
13 | public String getVersion() {
14 | return version;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ssl/TrustManager.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.ssl;
2 |
3 | import javax.net.ssl.HostnameVerifier;
4 | import javax.net.ssl.X509TrustManager;
5 |
6 | /**
7 | * Interface for a class which handles trusting certificates and hostnames.
8 | * @author Dylan James
9 | *
10 | */
11 | public interface TrustManager extends X509TrustManager, HostnameVerifier{
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/raizlabs/webservicemanager/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/WebServiceManager/src/androidTest/java/com/raizlabs/webservicemanager/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/HttpUriRequestable.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import org.apache.http.client.methods.HttpUriRequest;
4 |
5 | /**
6 | * Interface for a class which can provide an {@link HttpUriRequest}.
7 | * @author Dylan James
8 | *
9 | */
10 | public interface HttpUriRequestable {
11 | /**
12 | * @return The {@link HttpUriRequest}.
13 | */
14 | public HttpUriRequest getHttpUriRequest();
15 | }
16 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/UrlConnectionRequestable.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import java.net.HttpURLConnection;
4 |
5 | /**
6 | * Interface for a class which can provide an {@link HttpURLConnection}.
7 | * @author Dylan James
8 | *
9 | */
10 | public interface UrlConnectionRequestable {
11 | /**
12 | * @return The {@link HttpURLConnection}.
13 | */
14 | public HttpURLConnection getUrlConnection();
15 | }
16 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/webservicemanager/WebServiceProgress.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.webservicemanager;
2 |
3 | /**
4 | * Interface which provides the progress of a web service request.
5 | *
6 | * @author Dylan James
7 | */
8 | public interface WebServiceProgress {
9 | /**
10 | * @return The current progress value, or -1 if unknown.
11 | */
12 | long getCurrentProgress();
13 | /**
14 | * @return The maximum progress value, or -1 if unknown.
15 | */
16 | long getMaximumProgress();
17 | }
18 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/webservicemanager/RequestMode.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.webservicemanager;
2 |
3 | /**
4 | * Enum which represents the valid request modes for the {@link WebServiceManager}.
5 | *
6 | * @author Dylan James
7 | *
8 | */
9 | public enum RequestMode {
10 | /**
11 | * Indicates that requests are handled via {@link org.apache.http.client.HttpClient}s.
12 | */
13 | HttpClient,
14 | /**
15 | * Indicates that requests are handled via {@link java.net.HttpURLConnection}s.
16 | */
17 | HttpURLConnection
18 | }
19 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/webservicemanager/WebServiceRequestListener.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.webservicemanager;
2 |
3 | /**
4 | * Listener interface for a background web request.
5 | * @param The type of the result.
6 | */
7 | public interface WebServiceRequestListener {
8 | /**
9 | * Called when the web request completes.
10 | * @param manager The manager which executed the request.
11 | * @param result The result of the request.
12 | */
13 | public void onRequestComplete(WebServiceManager manager, ResultInfo result);
14 | }
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | buildToolsVersion '27.0.3'
6 |
7 | defaultConfig {
8 | applicationId "com.raizlabs.webservicemanager"
9 | minSdkVersion 15
10 | targetSdkVersion 27
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 | implementation 'com.android.support:appcompat-v7:27.1.1'
25 | }
26 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/mervynanthony/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/WebServiceManager/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/mervynanthony/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/BaseWebServiceProgress.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import com.raizlabs.webservicemanager.webservicemanager.WebServiceProgress;
4 |
5 | /**
6 | * Base implementation of a {@link WebServiceProgress}
7 | *
8 | * @author Dylan James
9 | *
10 | */
11 | public class BaseWebServiceProgress implements WebServiceProgress {
12 |
13 | long currentProgress;
14 | long maxProgress;
15 |
16 | public BaseWebServiceProgress(long current, long max) {
17 | this.currentProgress = current;
18 | this.maxProgress = max;
19 | }
20 |
21 | @Override
22 | public long getCurrentProgress() {
23 | return currentProgress;
24 | }
25 |
26 | public void setCurrentProgress(long currentProgress) {
27 | this.currentProgress = currentProgress;
28 | }
29 |
30 | @Override
31 | public long getMaximumProgress() {
32 | return maxProgress;
33 | }
34 |
35 | public void setMaximumProgress(long maxProgress) {
36 | this.maxProgress = maxProgress;
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ssl/TrustDelegate.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.ssl;
2 |
3 | import java.security.cert.X509Certificate;
4 |
5 | import javax.net.ssl.SSLSession;
6 |
7 | /**
8 | * Interface which is used as a delegate to check whether certificates
9 | * and hostnames should be trusted.
10 | * @author Dylan James
11 | */
12 | public interface TrustDelegate {
13 | /**
14 | * Called to determine whether the given certificate chain should be
15 | * trusted.
16 | * @param chain the certificate chain to validate.
17 | * @param authType the authentication type used.
18 | * @param isServer true if the certificate is coming from a server,
19 | * false if it is coming from a client.
20 | * @return true to trust the certificate.
21 | */
22 | public boolean checkCertificateTrusted(X509Certificate[] chain, String authType, boolean isServer);
23 |
24 | /**
25 | * Verifies that the specified hostname is allowed within the specified SSL session.
26 | * @param hostname the hostname.
27 | * @param session the SSL session of the connection.
28 | * @return true to trust the hostname for this session.
29 | */
30 | public boolean checkHostnameTrusted(String hostname, SSLSession session);
31 | }
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/webservicemanager/ResultInfo.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.webservicemanager;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * Interface which provides information about the result of a web service request.
7 | * @author Dylan James
8 | *
9 | * @param The type of the result which will be returned.
10 | */
11 | public interface ResultInfo {
12 | /**
13 | * @return The date when the result was requested.
14 | */
15 | Date getRequestDate();
16 |
17 | /**
18 | * @return The result of the request, or null if it failed.
19 | */
20 | ResultType getResult();
21 |
22 | /**
23 | * @return The response code from the request, or -1 if it failed.
24 | */
25 | int getResponseCode();
26 |
27 | /**
28 | * @return The response message from the request, or null if it failed.
29 | */
30 | String getResponseMessage();
31 |
32 | /**
33 | * @return True if the response code of this request is a standard OK response.
34 | */
35 | boolean isStatusOK();
36 |
37 | /**
38 | * Sets whether the request was cancelled.
39 | * @param cancelled
40 | */
41 | void setCancelled(boolean cancelled);
42 | /**
43 | * @return True if the result was cancelled.
44 | */
45 | boolean wasCancelled();
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/raizlabs/webservicemanager/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.Menu;
6 | import android.view.MenuItem;
7 |
8 | public class MainActivity extends AppCompatActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_main);
14 | }
15 |
16 | @Override
17 | public boolean onCreateOptionsMenu(Menu menu) {
18 | // Inflate the menu; this adds items to the action bar if it is present.
19 | getMenuInflater().inflate(R.menu.menu_main, menu);
20 | return true;
21 | }
22 |
23 | @Override
24 | public boolean onOptionsItemSelected(MenuItem item) {
25 | // Handle action bar item clicks here. The action bar will
26 | // automatically handle clicks on the Home/Up button, so long
27 | // as you specify a parent activity in AndroidManifest.xml.
28 | int id = item.getItemId();
29 |
30 | //noinspection SimplifiableIfStatement
31 | if(id == R.id.action_settings) {
32 | return true;
33 | }
34 |
35 | return super.onOptionsItemSelected(item);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/StringRequest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import com.raizlabs.webservicemanager.HttpMethod;
4 | import com.raizlabs.webservicemanager.responses.Response;
5 |
6 | /**
7 | * A request which return the content of a response as a String.
8 | * @author Dylan James
9 | *
10 | */
11 | public class StringRequest extends BaseWebServiceRequest{
12 |
13 | private RequestBuilder builder;
14 |
15 | /**
16 | * Creates a {@link StringRequest} which will execute the given
17 | * {@link RequestBuilder} and return the content of the response as a
18 | * String.
19 | * @param request The {@link RequestBuilder} to execute.
20 | */
21 | public StringRequest(RequestBuilder request) {
22 | this.builder = request;
23 | }
24 |
25 | /**
26 | * Creates a {@link StringRequest} which will perform an HTTP GET at the
27 | * given URL and return the content of the response as a String.
28 | * @param url The URL to perform the request to.
29 | */
30 | public StringRequest(String url) {
31 | this.builder = new RequestBuilder(HttpMethod.Get, url);
32 | }
33 |
34 | @Override
35 | protected RequestBuilder getRequestBuilder() {
36 | return builder;
37 | }
38 |
39 | @Override
40 | protected String translate(Response response) {
41 | return response.getContentAsString();
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/webservicemanager/FailedResultInfo.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.webservicemanager;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * {@link ResultInfo} implementation for a request that totally failed and doesn't
7 | * have any sort of response. Useful for connection failures, invalid parameters, etc.
8 | * @author Dylan James
9 | *
10 | * @param
11 | */
12 | public class FailedResultInfo implements ResultInfo{
13 |
14 | private Date requestDate;
15 | private boolean cancelled;
16 |
17 | public FailedResultInfo() {
18 | cancelled = false;
19 | }
20 |
21 | public FailedResultInfo(Date requestDate) {
22 | this.requestDate = requestDate;
23 | }
24 |
25 | @Override
26 | public Date getRequestDate() {
27 | return requestDate;
28 | }
29 |
30 | @Override
31 | public ResultType getResult() {
32 | return null;
33 | }
34 |
35 | @Override
36 | public int getResponseCode() {
37 | return -1;
38 | }
39 |
40 | @Override
41 | public String getResponseMessage() {
42 | return null;
43 | }
44 |
45 | @Override
46 | public boolean isStatusOK() {
47 | return false;
48 | }
49 |
50 | @Override
51 | public void setCancelled(boolean cancelled) {
52 | this.cancelled = cancelled;
53 | }
54 |
55 | @Override
56 | public boolean wasCancelled() {
57 | return cancelled;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/JSONRequest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import com.raizlabs.webservicemanager.HttpMethod;
4 | import com.raizlabs.webservicemanager.responses.Response;
5 |
6 | import org.json.JSONObject;
7 |
8 | /**
9 | * A request which parses the result as an {@link JSONObject}.
10 | * @author Dylan James
11 | *
12 | */
13 | public class JSONRequest extends BaseWebServiceRequest{
14 |
15 | private RequestBuilder builder;
16 |
17 | /**
18 | * Constructs a {@link JSONRequest} from the given {@link RequestBuilder}
19 | * which will execute the request and return the content of the response
20 | * as a {@link JSONObject}.
21 | * @param request The {@link RequestBuilder} to execute.
22 | */
23 | public JSONRequest(RequestBuilder request) {
24 | this.builder = request;
25 | }
26 |
27 | /**
28 | * Constructs a {@link JSONRequest} which will do an HTTP GET at the
29 | * given URL and return the content of the response as a {@link JSONObject}
30 | * @param url
31 | */
32 | public JSONRequest(String url) {
33 | this.builder = new RequestBuilder(HttpMethod.Get, url);
34 | }
35 |
36 | @Override
37 | protected RequestBuilder getRequestBuilder() {
38 | return builder;
39 | }
40 |
41 | @Override
42 | protected JSONObject translate(Response response) {
43 | return response.getContentAsJSON();
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/JSONArrayRequest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import com.raizlabs.webservicemanager.HttpMethod;
4 | import com.raizlabs.webservicemanager.responses.Response;
5 |
6 | import org.json.JSONArray;
7 |
8 | /**
9 | * A request which parses the result as an {@link JSONArray}.
10 | * @author Alex Wang
11 | *
12 | */
13 | public class JSONArrayRequest extends BaseWebServiceRequest{
14 |
15 | private RequestBuilder builder;
16 |
17 | /**
18 | * Constructs a {@link JSONArrayRequest} from the given {@link RequestBuilder}
19 | * which will execute the request and return the content of the response
20 | * as a {@link JSONArray}.
21 | * @param request The {@link RequestBuilder} to execute.
22 | */
23 | public JSONArrayRequest(RequestBuilder request) {
24 | this.builder = request;
25 | }
26 |
27 | /**
28 | * Constructs a {@link JSONArrayRequest} which will do an HTTP GET at the
29 | * given URL and return the content of the response as a {@link JSONArray}
30 | * @param url
31 | */
32 | public JSONArrayRequest(String url) {
33 | this.builder = new RequestBuilder(HttpMethod.Get, url);
34 | }
35 |
36 | @Override
37 | protected RequestBuilder getRequestBuilder() {
38 | return builder;
39 | }
40 |
41 | @Override
42 | protected JSONArray translate(Response response) {
43 | return response.getContentAsJSONArray();
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ProgressInputStreamEntity.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | import com.raizlabs.coreutils.listeners.ProgressListener;
4 |
5 | import org.apache.http.entity.InputStreamEntity;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.OutputStream;
10 |
11 | /**
12 | * An {@link InputStreamEntity} which updates a given {@link ProgressListener}
13 | * of its progress.
14 | *
15 | * @author Dylan James
16 | */
17 | public class ProgressInputStreamEntity extends InputStreamEntity {
18 |
19 | private ProgressListener listener;
20 | private int updateInterval;
21 | private long length;
22 |
23 | /**
24 | * Creates a {@link ProgressInputStreamEntity} with the given parameters.
25 | * @param instream The {@link InputStream} to write.
26 | * @param length The length of the {@link InputStream} in bytes.
27 | * @param listener The {@link ProgressListener} to call when updates occur.
28 | * @param updateInterval How frequently (in bytes) updates should be called.
29 | */
30 | public ProgressInputStreamEntity(InputStream instream, long length, ProgressListener listener, int updateInterval) {
31 | super(instream, length);
32 | this.length = length;
33 | this.listener = listener;
34 | this.updateInterval = updateInterval;
35 | }
36 |
37 | @Override
38 | public void writeTo(final OutputStream outstream) throws IOException {
39 | super.writeTo(new OutputStream() {
40 | private long totalWritten = 0;
41 | private long lastUpdate = 0;
42 |
43 | @Override
44 | public void write(int oneByte) throws IOException {
45 | outstream.write(oneByte);
46 | if (listener != null && ++totalWritten - lastUpdate >= updateInterval) {
47 | listener.onProgressUpdate(totalWritten, length);
48 | lastUpdate = totalWritten;
49 | }
50 | }
51 | });
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/HttpUtils.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | import com.raizlabs.webservicemanager.responses.Response;
4 |
5 | import org.apache.http.HttpResponse;
6 |
7 | import java.io.IOException;
8 | import java.net.HttpURLConnection;
9 |
10 | /**
11 | * Class which provides some utilities for HTTP.
12 | * @author Dylan James
13 | *
14 | */
15 | public class HttpUtils {
16 | /**
17 | * Returns true if the given {@link HttpURLConnection} has a standard
18 | * successful response (200-299)
19 | * @param conn The {@link HttpURLConnection} to check.
20 | * @return True if the response is between 200 and 299.
21 | */
22 | public static boolean isResponseOK(HttpURLConnection conn) {
23 | try {
24 | return conn != null && isResponseOK(conn.getResponseCode());
25 | } catch (IOException e) {
26 | return false;
27 | }
28 | }
29 |
30 | /**
31 | * Returns true if the given {@link Response} has a standard
32 | * successful response (200-299)
33 | * @param response The {@link Response} to check.
34 | * @return True if the response is between 200 and 299.
35 | */
36 | public static boolean isResponseOK(Response response) {
37 | return isResponseOK(response.getResponseCode());
38 | }
39 |
40 | /**
41 | * Returns true if the given {@link HttpResponse} has a standard
42 | * successful response (200-299).
43 | * @param response The {@link HttpResponse} to check.
44 | * @return True if the response is between 200 and 299.
45 | */
46 | public static boolean isResponseOK(HttpResponse response) {
47 | return response != null &&
48 | response.getStatusLine() != null &&
49 | isResponseOK(response.getStatusLine().getStatusCode());
50 | }
51 |
52 | /**
53 | * Returns true if the given status code is a standard
54 | * successful response (200 - 299).
55 | * @param statusCode The status code to check.
56 | * @return True if the status code is between 200 and 299.
57 | */
58 | public static boolean isResponseOK(int statusCode) {
59 | return statusCode / 100 == 2;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/HttpMethod.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | import org.apache.http.client.methods.HttpDelete;
4 | import org.apache.http.client.methods.HttpGet;
5 | import org.apache.http.client.methods.HttpHead;
6 | import org.apache.http.client.methods.HttpPost;
7 | import org.apache.http.client.methods.HttpPut;
8 | import org.apache.http.client.methods.HttpRequestBase;
9 |
10 | /**
11 | * Enum which represents each of the Http Methods.
12 | * Contains some helpers for getting the method
13 | * name and vice versa.
14 | *
15 | * @author Dylan James
16 | *
17 | */
18 | public enum HttpMethod {
19 | Get
20 | {
21 | @Override
22 | public HttpRequestBase createRequest() {
23 | return new HttpGet();
24 | }
25 |
26 | @Override
27 | public String getMethodName() {
28 | return "GET";
29 | }
30 | },
31 | Post
32 | {
33 | @Override
34 | public HttpRequestBase createRequest() {
35 | return new HttpPost();
36 | }
37 |
38 | @Override
39 | public String getMethodName() {
40 | return "POST";
41 | }
42 | },
43 | Put {
44 | @Override
45 | public HttpRequestBase createRequest() {
46 | return new HttpPut();
47 | }
48 |
49 | @Override
50 | public String getMethodName() {
51 | return "PUT";
52 | }
53 | },
54 | Delete {
55 | @Override
56 | public HttpRequestBase createRequest() {
57 | return new HttpDelete();
58 | }
59 |
60 | @Override
61 | public String getMethodName() {
62 | return "DELETE";
63 | }
64 | },
65 | Head {
66 | @Override
67 | public HttpRequestBase createRequest() {
68 | return new HttpHead();
69 | }
70 |
71 | @Override
72 | public String getMethodName() {
73 | return "HEAD";
74 | }
75 | };
76 |
77 | public abstract HttpRequestBase createRequest();
78 | public abstract String getMethodName();
79 |
80 | public static HttpMethod fromName(String name) {
81 | for (HttpMethod method : values()) {
82 | if (method.getMethodName().equalsIgnoreCase(name)) {
83 | return method;
84 | }
85 | }
86 |
87 | return null;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ssl/TrustManagerFactory.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.ssl;
2 |
3 | import java.security.KeyStore;
4 |
5 | /**
6 | * Factory class which helps with the creation of {@link TrustManager}s.
7 | * @author Dylan James
8 | */
9 | public class TrustManagerFactory {
10 |
11 | /**
12 | * Gets a {@link TrustManager} which acts like the default trust
13 | * of the system.
14 | * @param keystore Optional: The {@link KeyStore} to create
15 | * the trust manager with.
16 | * @return The created {@link TrustManager}.
17 | */
18 | public static TrustManager getDefaultTrustManager(KeyStore keystore) {
19 | if (keystore == null) keystore = getDefaultKeyStore();
20 | return new DefaultX509TrustManager(keystore);
21 | }
22 |
23 | /**
24 | * Gets a {@link TrustManager} which accepts every certificate and host.
25 | * @param keystore Optional: The {@link KeyStore} to create
26 | * the trust manager with.
27 | * @return The created {@link TrustManager}.
28 | */
29 | public static TrustManager getTrustAllManager(KeyStore keystore) {
30 | if (keystore == null) keystore = getDefaultKeyStore();
31 | return new TrustAllX509TrustManager(keystore);
32 | }
33 |
34 | /**
35 | * Gets a {@link TrustManager} which tries the default trust manager and
36 | * queries the delegate if the default manager rejects it.
37 | * @param delegate The {@link TrustDelegate} to call when the default
38 | * trust manager rejects a certificate or host.
39 | * @param keystore Optional: The {@link KeyStore} to create
40 | * the trust manager with.
41 | * @return The created {@link TrustManager}.
42 | */
43 | public static TrustManager getDelegatedTrustManager(TrustDelegate delegate,
44 | KeyStore keystore) {
45 | if (keystore == null) keystore = getDefaultKeyStore();
46 | return new DelegatedX509TrustManager(keystore, delegate);
47 | }
48 |
49 | /**
50 | * @return The default {@link KeyStore} that is used if nulls are supplied.
51 | */
52 | public static KeyStore getDefaultKeyStore() {
53 | return null;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/HttpClientProvider.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | import org.apache.http.client.HttpClient;
4 | import org.apache.http.conn.scheme.SocketFactory;
5 |
6 | /**
7 | * Interface which provides {@link HttpClient}s.
8 | *
9 | * @author Dylan James
10 | *
11 | */
12 | public interface HttpClientProvider {
13 | /**
14 | * Sets the maximum number of connections that this {@link HttpClientProvider}
15 | * will provide.
16 | * @param maxConnections The maximum number of connections.
17 | */
18 | public void setMaxConnections(int maxConnections);
19 | /**
20 | * Gets an {@link HttpClient} from this {@link HttpClientProvider}.
21 | * @return The obtained {@linkd HttpClient}.
22 | */
23 | public HttpClient getClient();
24 |
25 | /**
26 | * @return The {@link SocketFactory} that will be used to create HTTP Sockets.
27 | */
28 | public SocketFactory getHttpSocketFactory();
29 | /**
30 | * Sets the {@link SocketFactory} to use to create HTTP Sockets.
31 | * @param factory The factory to use.
32 | */
33 | public void setHttpSocketFactory(SocketFactory factory);
34 | /**
35 | * @return The {@link SocketFactory} that will be used to create HTTPS Sockets.
36 | */
37 | public SocketFactory getHttpsSocketFactory();
38 | /**
39 | * Sets the {@link SocketFactory} to use to create HTTPS Sockets.
40 | * @param factory The factory to use.
41 | */
42 | public void setHttpsSocketFactory(SocketFactory factory);
43 |
44 |
45 |
46 | /**
47 | * @return The timeout for establishing a connection (in milliseconds)
48 | * @see #setConnectionTimeout(int)
49 | */
50 | public int getConnectionTimeout();
51 | /**
52 | * Sets the timeout for establishing a connection. Setting this to zero
53 | * means a timeout is not used.
54 | * @param timeoutMillis The timeout value in milliseconds
55 | */
56 | public void setConnectionTimeout(int timeoutMillis);
57 |
58 |
59 | /**
60 | * @return The timeout for reading data from the connection (in milliseconds)
61 | */
62 | public int getReadTimeout();
63 | /**
64 | * Sets the timeout for establishing a connection. Setting this to zero
65 | * means a timeout is not used.
66 | * @param timeoutMillis The timeout value in milliseconds.
67 | */
68 | public void setReadTimeout(int timeoutMillis);
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/BitmapRequest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.BitmapFactory.Options;
6 | import android.graphics.Rect;
7 |
8 | import com.raizlabs.webservicemanager.HttpMethod;
9 | import com.raizlabs.webservicemanager.responses.Response;
10 |
11 | /**
12 | * A request which obtains a {@link Bitmap} from a request. Keep in mind that
13 | * it is easy to download a {@link Bitmap} which exceeds the heap size and
14 | * will crash, so populate the options accordingly. This should only be used
15 | * when the size is known ahead of time, otherwise use a different
16 | * {@link WebServiceRequest} which will allow getting the bounds first.
17 | *
18 | * @author Dylan James
19 | *
20 | */
21 | public class BitmapRequest extends BaseWebServiceRequest{
22 |
23 | private RequestBuilder builder;
24 | private Rect outPadding;
25 | private Options options;
26 |
27 | /**
28 | * Creates a {@link BitmapRequest} which executes an HTTP GET at the given
29 | * URL and returns the response content decoded as a {@link Bitmap}.
30 | * @param url The URL to request.
31 | * @param outPadding @see {@link BitmapFactory#decodeStream(java.io.InputStream, Rect, Options)}
32 | * @param options The decoding {@link Options}.
33 | */
34 | public BitmapRequest(String url, Rect outPadding, Options options) {
35 | this(new RequestBuilder(HttpMethod.Get, url), outPadding, options);
36 | }
37 |
38 | /**
39 | * Creates a {@link BitmapRequest} which executes the given
40 | * {@link RequestBuilder} and returns the response content
41 | * decoded as a {@link Bitmap}.
42 | * @param request The {@link RequestBuilder} to execute.
43 | * @param outPadding @see {@link BitmapFactory#decodeStream(java.io.InputStream, Rect, Options)}
44 | * @param options The decoding {@link Options}.
45 | */
46 | public BitmapRequest(RequestBuilder request, Rect outPadding, Options options) {
47 | this.builder = request;
48 | this.outPadding = outPadding;
49 | this.options = options;
50 | }
51 |
52 | @Override
53 | protected RequestBuilder getRequestBuilder() {
54 | return builder;
55 | }
56 |
57 | @Override
58 | protected Bitmap translate(Response response) {
59 | return response.getContentAsBitmap(outPadding, options);
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/responses/HttpURLConnectionResponse.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.responses;
2 |
3 | import android.text.TextUtils;
4 |
5 | import com.raizlabs.webservicemanager.HttpMethod;
6 |
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.net.HttpURLConnection;
10 |
11 | /**
12 | * {@link Response} implementation which wraps an {@link HttpURLConnection}.
13 | * @author Dylan James
14 | *
15 | */
16 | public class HttpURLConnectionResponse extends BaseResponse {
17 | private HttpURLConnection connection;
18 |
19 | /**
20 | * Creates an {@link HttpURLConnectionResponse} from the given
21 | * {@link HttpURLConnection}.
22 | * @param connection The actual connection.
23 | */
24 | public HttpURLConnectionResponse(HttpURLConnection connection) {
25 | this.connection = connection;
26 | }
27 |
28 | @Override
29 | public boolean containsHeader(String name) {
30 | return connection != null && !TextUtils.isEmpty(connection.getHeaderField(name));
31 | }
32 |
33 | @Override
34 | public String getHeaderValue(String name) {
35 | return connection == null ? null : connection.getHeaderField(name);
36 | }
37 |
38 | @Override
39 | public int getResponseCode() {
40 | try {
41 | return connection.getResponseCode();
42 | } catch (Exception e) {
43 | return -1;
44 | }
45 | }
46 |
47 | @Override
48 | public String getResponseMessage() {
49 | try {
50 | return connection.getResponseMessage();
51 | } catch (Exception e) {
52 | return null;
53 | }
54 | }
55 |
56 | @Override
57 | public String getContentEncoding() {
58 | return connection == null ? null : connection.getContentEncoding();
59 | }
60 |
61 | @Override
62 | public long getContentLength() {
63 | return connection == null ? null : connection.getContentLength();
64 | }
65 |
66 | @Override
67 | public String getContentType() {
68 | return connection == null ? null : connection.getContentType();
69 | }
70 |
71 | @Override
72 | public InputStream getContentStream() throws IOException {
73 | return connection == null ? null : connection.getInputStream();
74 | }
75 |
76 | @Override
77 | public HttpMethod getRequestMethod() {
78 | return connection == null ? null : HttpMethod.fromName(connection.getRequestMethod());
79 | }
80 |
81 | @Override
82 | public void close() {
83 | if (connection != null) {
84 | connection.disconnect();
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/WebServiceManager/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.jfrog.bintray'
3 | apply plugin: 'com.github.dcendents.android-maven'
4 |
5 | android {
6 | compileSdkVersion 27
7 | buildToolsVersion '27.0.3'
8 |
9 | defaultConfig {
10 | minSdkVersion 8
11 | targetSdkVersion 27
12 | versionCode 1
13 | versionName "1.0"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | useLibrary 'org.apache.http.legacy'
23 | }
24 |
25 | dependencies {
26 | implementation 'com.android.support:appcompat-v7:27.1.1'
27 | implementation 'com.raizlabs:CoreUtils:1.1.7'
28 | }
29 |
30 | //Generate POM files needed to publish the artifacts to JCenter public Repo
31 | install {
32 | repositories.mavenInstaller.pom.project {
33 | packaging 'aar'
34 | groupId GROUP_NAME
35 | artifactId ARTIFACT_NAME
36 |
37 | // Set your license
38 | licenses {
39 | license {
40 | name 'The Apache Software License, Version 2.0'
41 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
42 | }
43 | }
44 |
45 | scm {
46 | connection GIT_URL
47 | developerConnection GIT_URL
48 | url SITE_URL
49 | }
50 | }
51 | }
52 |
53 | task androidJavadocs(type: Javadoc) {
54 | source = android.sourceSets.main.java.srcDirs
55 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
56 | }
57 |
58 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
59 | classifier = 'javadoc'
60 | from androidJavadocs.destinationDir
61 | }
62 |
63 | task androidSourcesJar(type: Jar) {
64 | classifier = 'sources'
65 | from android.sourceSets.main.java.srcDirs
66 | }
67 |
68 | artifacts {
69 | archives androidSourcesJar
70 | archives androidJavadocsJar
71 | }
72 |
73 | //Bintray Upload
74 | Properties properties = new Properties()
75 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
76 |
77 | bintray {
78 | user = properties.getProperty("bintray_user")
79 | key = properties.getProperty("bintray_key")
80 |
81 | configurations = ['archives']
82 |
83 | pkg {
84 | repo = 'Libraries'
85 | name = ARTIFACT_NAME
86 | userOrg = 'raizlabs'
87 | publish = true
88 | version.name = VERSION_NAME
89 | }
90 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ssl/TrustAllX509TrustManager.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.ssl;
2 |
3 | import java.security.KeyStore;
4 | import java.security.KeyStoreException;
5 | import java.security.NoSuchAlgorithmException;
6 | import java.security.cert.CertificateException;
7 | import java.security.cert.X509Certificate;
8 | import java.util.HashSet;
9 |
10 | import javax.net.ssl.SSLSession;
11 | import javax.net.ssl.TrustManager;
12 | import javax.net.ssl.TrustManagerFactory;
13 | import javax.net.ssl.X509TrustManager;
14 |
15 | /**
16 | * {@link com.raizlabs.webservicemanager.ssl.TrustManager} which accepts all certificates
17 | * and hosts.
18 | * @author Dylan James
19 | */
20 | public class TrustAllX509TrustManager implements com.raizlabs.webservicemanager.ssl.TrustManager {
21 | TrustManager[] trustManagers;
22 |
23 | public TrustAllX509TrustManager(KeyStore keystore) {
24 | super();
25 | try {
26 | TrustManagerFactory factory =
27 | TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
28 | factory.init(keystore);
29 | trustManagers = factory.getTrustManagers();
30 | } catch (NoSuchAlgorithmException e) {
31 | } catch (KeyStoreException e) { }
32 | }
33 |
34 | @Override
35 | public void checkClientTrusted(X509Certificate[] chain, String authType)
36 | throws CertificateException {
37 | // Don't do anything - we would throw an exception to indicate
38 | // that the client is not trusted
39 | }
40 |
41 | @Override
42 | public void checkServerTrusted(X509Certificate[] chain, String authType)
43 | throws CertificateException {
44 | // Don't do anything - we would throw an exception to indicate
45 | // that the server is not trusted
46 | }
47 |
48 | @Override
49 | public X509Certificate[] getAcceptedIssuers() {
50 | // Return all the accepted issuers of all the trust managers we found.
51 | if (trustManagers == null || trustManagers.length == 0) {
52 | return new X509Certificate[0];
53 | } else {
54 | HashSet allCerts = new HashSet();
55 | // Loop through all our trust managers
56 | for (TrustManager manager : trustManagers) {
57 | // Get the accepted issuers if we can
58 | if (manager instanceof X509TrustManager) {
59 | X509Certificate[] managerCerts = ((X509TrustManager) manager).getAcceptedIssuers();
60 | // Add all the certificates we found
61 | if (managerCerts != null) {
62 | for (X509Certificate cert : managerCerts) {
63 | allCerts.add(cert);
64 | }
65 | }
66 | }
67 | }
68 | // Return all the certificates we found
69 | return allCerts.toArray(new X509Certificate[0]);
70 | }
71 | }
72 |
73 | @Override
74 | public boolean verify(String hostname, SSLSession session) {
75 | return true;
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/caching/SimpleWebFileCache.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.caching;
2 |
3 | import android.content.Context;
4 |
5 | import com.raizlabs.webservicemanager.requests.DownloadFileRequest;
6 | import com.raizlabs.webservicemanager.requests.RequestBuilder;
7 | import com.raizlabs.webservicemanager.requests.WebServiceRequest;
8 | import com.raizlabs.webservicemanager.webservicemanager.WebServiceManager;
9 |
10 | import java.io.File;
11 |
12 | /**
13 | * {@link WebFileCache} implementation which simply keys requests by their URL and
14 | * does simple file downloads to store the results.
15 | * @author Dylan James
16 | *
17 | */
18 | public class SimpleWebFileCache extends WebFileCache {
19 | private File cacheDir;
20 | private long maxAge;
21 | /**
22 | * Creates a {@link SimpleWebFileCache} whose data never expires.
23 | * @param name The name of the {@link WebFileCache}. This should be unique
24 | * across the application to avoid collisions.
25 | * @param webManager The {@link WebServiceManager} to use to perform web requests.
26 | * @param context A {@link Context} to use to access resources. This is only used
27 | * for initialization and will not be stored.
28 | */
29 | public SimpleWebFileCache(String name, WebServiceManager webManager, Context context) {
30 | this(name, webManager, context, Long.MIN_VALUE);
31 | }
32 |
33 | /**
34 | * Creates a {@link SimpleWebFileCache} whose data expires after the given age.
35 | * @param name The name of the {@link WebFileCache}. This should be unique
36 | * across the application to avoid collisions.
37 | * @param webManager The {@link WebServiceManager} to use to perform web requests.
38 | * @param context A {@link Context} to use to access resources. This is only used
39 | * for initialization and will not be stored.
40 | * @param maxAge The maximum allowed age of data in milliseconds. A negative value
41 | * indicates that the data is always valid.
42 | */
43 | public SimpleWebFileCache(String name, WebServiceManager webManager, Context context, long maxAge) {
44 | super(name, webManager, context);
45 | cacheDir = getCacheDir(name, context);
46 | this.maxAge = maxAge;
47 | }
48 |
49 | protected File getCacheDir(String name, Context context) {
50 | return new File(context.getCacheDir(), String.format("URLWebFileCaches/%s", name));
51 | }
52 |
53 | @Override
54 | protected WebServiceRequest getRequest(RequestBuilder builder, File targetFile) {
55 | return new DownloadFileRequest(targetFile, builder, null);
56 | }
57 |
58 | @Override
59 | protected String getKeyForRequest(RequestBuilder request) {
60 | return request.getRequest().getURI().getSchemeSpecificPart();
61 | }
62 |
63 | @Override
64 | protected File getFileForKey(String key) {
65 | return new File(cacheDir, key);
66 | }
67 |
68 | @Override
69 | protected boolean isFresh(RequestBuilder request, long age) {
70 | if (maxAge > 0) {
71 | return age < maxAge;
72 | } else {
73 | return true;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ssl/DefaultX509TrustManager.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.ssl;
2 |
3 | import java.security.KeyStore;
4 | import java.security.KeyStoreException;
5 | import java.security.NoSuchAlgorithmException;
6 | import java.security.cert.CertificateException;
7 | import java.security.cert.X509Certificate;
8 |
9 | import javax.net.ssl.HostnameVerifier;
10 | import javax.net.ssl.HttpsURLConnection;
11 | import javax.net.ssl.SSLSession;
12 | import javax.net.ssl.TrustManager;
13 | import javax.net.ssl.TrustManagerFactory;
14 | import javax.net.ssl.X509TrustManager;
15 |
16 | /**
17 | * {@link com.raizlabs.webservicemanager.ssl.TrustManager} which wraps the default trust
18 | * manager and verifier.
19 | * @author Dylan James
20 | */
21 | public class DefaultX509TrustManager implements com.raizlabs.webservicemanager.ssl.TrustManager {
22 | X509TrustManager defaultManager;
23 | HostnameVerifier defaultVerifier;
24 |
25 | public DefaultX509TrustManager(KeyStore keystore) {
26 | super();
27 | try {
28 | TrustManagerFactory factory =
29 | TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
30 | factory.init(keystore);
31 | TrustManager[] trustManagers = factory.getTrustManagers();
32 | if (trustManagers != null) {
33 | // Find the first X509TrustManager
34 | for (TrustManager trustManager : trustManagers) {
35 | if (trustManager instanceof X509TrustManager) {
36 | defaultManager = (X509TrustManager) trustManager;
37 | break;
38 | }
39 | }
40 | }
41 | } catch (NoSuchAlgorithmException e) {
42 | } catch (KeyStoreException e) { }
43 |
44 | defaultVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
45 | }
46 |
47 | @Override
48 | public void checkClientTrusted(X509Certificate[] chain, String authType)
49 | throws CertificateException {
50 | checkCertifacteTrusted(chain, authType);
51 | }
52 |
53 | @Override
54 | public void checkServerTrusted(X509Certificate[] chain, String authType)
55 | throws CertificateException {
56 | checkCertifacteTrusted(chain, authType);
57 | }
58 |
59 | protected void checkCertifacteTrusted(X509Certificate[] chain, String authType)
60 | throws CertificateException {
61 | // Fail if we don't have a default manager
62 | if (defaultManager == null) {
63 | final String msg = String.format(
64 | "No trust manager in %s. Can't trust any certificates.",
65 | getClass().getName());
66 | throw new CertificateException(msg);
67 | } else {
68 | // Otherwise, check the certificate chain with the default manager
69 | defaultManager.checkClientTrusted(chain, authType);
70 | }
71 | }
72 |
73 | @Override
74 | public X509Certificate[] getAcceptedIssuers() {
75 | if (defaultManager == null) {
76 | return new X509Certificate[0];
77 | } else {
78 | return defaultManager.getAcceptedIssuers();
79 | }
80 | }
81 |
82 | @Override
83 | public boolean verify(String hostname, SSLSession session) {
84 | if (defaultVerifier != null) {
85 | return defaultVerifier.verify(hostname, session);
86 | }
87 | return false;
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ssl/CachedTrustDelegate.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.ssl;
2 |
3 | import java.security.cert.X509Certificate;
4 | import java.util.Arrays;
5 | import java.util.HashMap;
6 |
7 | import javax.net.ssl.SSLSession;
8 |
9 | /**
10 | * Abstract base implementation of a {@link TrustDelegate} which will
11 | * only call the certificate check once per unique chain of certificates
12 | * and will only call the hostname check once per hostname.
13 | * @author Dylan James
14 | *
15 | */
16 | public abstract class CachedTrustDelegate implements TrustDelegate {
17 | private static class CertificateChain {
18 | private X509Certificate[] chain;
19 | public CertificateChain(X509Certificate[] chain) {
20 | this.chain = chain;
21 | }
22 |
23 | @Override
24 | public boolean equals(Object o) {
25 | if (o instanceof CertificateChain) {
26 | return Arrays.equals(chain, ((CertificateChain) o).chain);
27 | }
28 | return false;
29 | }
30 |
31 | @Override
32 | public int hashCode() {
33 | StringBuilder nameBuilder = new StringBuilder();
34 | for (X509Certificate cert : chain) {
35 | nameBuilder.append(":" + cert.toString());
36 | }
37 | return nameBuilder.toString().hashCode();
38 | }
39 | }
40 |
41 | private HashMap cachedCertificates;
42 | private HashMap cachedHosts;
43 |
44 | public CachedTrustDelegate() {
45 | cachedCertificates = new HashMap();
46 | cachedHosts = new HashMap();
47 | }
48 |
49 | @Override
50 | public boolean checkCertificateTrusted(X509Certificate[] chain,
51 | String authType, boolean isServer) {
52 | synchronized (cachedCertificates) {
53 | CertificateChain certChain = new CertificateChain(chain);
54 | Boolean result = cachedCertificates.get(certChain);
55 | if (result == null) {
56 | result = isCertificateTrusted(chain, authType, isServer);
57 | cachedCertificates.put(certChain, result);
58 | }
59 | return result;
60 | }
61 | }
62 |
63 | @Override
64 | public boolean checkHostnameTrusted(String hostname, SSLSession session) {
65 | synchronized (cachedHosts) {
66 | Boolean result = cachedHosts.get(hostname);
67 | if (result == null) {
68 | result = isHostNameTrusted(hostname, session);
69 | cachedHosts.put(hostname, result);
70 | }
71 | return result;
72 | }
73 | }
74 |
75 | /**
76 | * Called to determine whether the given certificate chain should be
77 | * trusted when it hasn't been queried before.
78 | * @param chain the certificate chain to validate.
79 | * @param authType the authentication type used.
80 | * @param isServer true if the certificate is coming from a server,
81 | * false if it is coming from a client.
82 | * @return true to trust the certificate.
83 | */
84 | public abstract boolean isCertificateTrusted(X509Certificate[] chain,
85 | String authType, boolean isServer);
86 | /**
87 | * Verifies that the specified hostname is allowed within the specified SSL session.
88 | *
89 | * NOTE: This will cache the result for the hostname and not the
90 | * hostname/session pair.
91 | * @param hostname the hostname.
92 | * @param session the SSL session of the connection.
93 | * @return true to trust the hostname for this session.
94 | */
95 | public abstract boolean isHostNameTrusted(String hostname, SSLSession session);
96 | }
97 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/UploadFileRequest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import com.raizlabs.coreutils.listeners.ProgressListener;
4 | import com.raizlabs.webservicemanager.HttpMethod;
5 | import com.raizlabs.webservicemanager.HttpUtils;
6 | import com.raizlabs.webservicemanager.responses.Response;
7 |
8 | import java.io.File;
9 |
10 | /**
11 | * Request class which allows the uploading of a local file and returns
12 | * whether the response is a standard OK response.
13 | * @author Dylan James
14 | *
15 | */
16 | public class UploadFileRequest extends BaseWebServiceRequest{
17 |
18 | private RequestBuilder builder;
19 |
20 | /**
21 | * Creates an {@link UploadFileRequest} from a pre-populated {@link RequestBuilder}.
22 | * The file and progress listener should already be set, else use
23 | * {@link #UploadFileRequest(RequestBuilder, File, ProgressListener)}
24 | * @param request The pre-populated {@link RequestBuilder}
25 | */
26 | public UploadFileRequest(RequestBuilder request) {
27 | this.builder = request;
28 | }
29 |
30 | /**
31 | * Creates an {@link UploadFileRequest} from the given data.
32 | * @param request The {@link RequestBuilder} to be used as a base for the request.
33 | * @param localFile The local {@link File} to upload.
34 | * @param listener The {@link ProgressListener} which will be called during the upload.
35 | * (May be null).
36 | */
37 | public UploadFileRequest(RequestBuilder request, File localFile, final ProgressListener listener) {
38 | // "Middle man" listener which will publish progress and call any external
39 | // progress listener
40 | ProgressListener mListener = new ProgressListener() {
41 | @Override
42 | public void onProgressUpdate(long currentProgress, long maxProgress) {
43 | publishProgress(currentProgress, maxProgress);
44 | if (listener != null) {
45 | listener.onProgressUpdate(currentProgress, maxProgress);
46 | }
47 | }
48 | };
49 | request.setFileInput(localFile, mListener);
50 | this.builder = request;
51 | }
52 |
53 | /**
54 | * Creates an {@link UploadFileRequest} which uploads the given {@link File} to the
55 | * given URL using an HTTP PUT.
56 | * @param url The remote location to put the file.
57 | * @param localFile The local {@link File} to upload.
58 | * @param listener The {@link ProgressListener} which will be called during the upload.
59 | * (May be null).
60 | */
61 | public UploadFileRequest(String url, File localFile, ProgressListener listener) {
62 | this(HttpMethod.Put, url, localFile, listener);
63 | }
64 |
65 | /**
66 | * Creates an {@link UploadFileRequest} which uploads the given {@link File} to the
67 | * given URL using the given {@link HttpMethod}.
68 | * @param method The {@link HttpMethod} to use.
69 | * @param url The remote location to put the file.
70 | * @param localFile The local {@link File} to upload.
71 | * @param listener The {@link ProgressListener} which will be called during the upload.
72 | * (May be null).
73 | */
74 | public UploadFileRequest(HttpMethod method, String url, File localFile, ProgressListener listener) {
75 | this(new RequestBuilder(method, url), localFile, listener);
76 | }
77 |
78 | @Override
79 | protected RequestBuilder getRequestBuilder() {
80 | return builder;
81 | }
82 |
83 | @Override
84 | protected Boolean translate(Response response) {
85 | return HttpUtils.isResponseOK(response);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/webservicemanager/BasicResultInfo.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.webservicemanager;
2 |
3 | import android.util.Log;
4 |
5 | import com.raizlabs.webservicemanager.BuildConfig;
6 | import com.raizlabs.webservicemanager.HttpUtils;
7 |
8 | import org.apache.http.HttpResponse;
9 | import org.apache.http.StatusLine;
10 |
11 | import java.io.IOException;
12 | import java.net.HttpURLConnection;
13 | import java.util.Date;
14 |
15 | /**
16 | * Class which implements {@link ResultInfo} with basic functionality to
17 | * allow easy wrapping of {@link HttpURLConnection}s and
18 | * {@link HttpResponse}s.
19 | * @author Dylan James
20 | *
21 | * @param The type of the result which will be returned
22 | */
23 | public class BasicResultInfo implements ResultInfo{
24 | Date RequestDate;
25 | public Date getRequestDate() { return RequestDate; }
26 | ResultType Result;
27 | public ResultType getResult() { return Result; }
28 |
29 | int ResponseCode = -1;
30 | public int getResponseCode() { return ResponseCode; }
31 | String ResponseMessage;
32 | public String getResponseMessage() { return ResponseMessage; }
33 |
34 | public boolean isStatusOK() { return HttpUtils.isResponseOK(ResponseCode); }
35 |
36 | boolean cancelled;
37 | /**
38 | * @return True if the request was cancelled.
39 | */
40 | public boolean wasCancelled() { return cancelled; }
41 | public void setCancelled(boolean cancelled) { this.cancelled = cancelled; }
42 |
43 | /**
44 | * Creates a {@link BasicResultInfo} by wrapping the given result and
45 | * {@link HttpURLConnection}.
46 | * @param result The result of the request.
47 | * @param requestDate The {@link Date} the request was completed.
48 | * @param connection The {@link HttpURLConnection} resulting from the request.
49 | * @throws IOException If there was an exception with the connection.
50 | */
51 | public BasicResultInfo(ResultType result, Date requestDate, HttpURLConnection connection) throws IOException {
52 | this(result, requestDate);
53 | wrap(connection);
54 | }
55 |
56 | /**
57 | * Creates a {@link BasicResultInfo} by wrapping the given result and
58 | * {@link HttpResponse}.
59 | * @param result The result of the request.
60 | * @param requestDate The {@link Date} the request was completed.
61 | * @param response The {@link HttpResponse} resulting from the request.
62 | */
63 | public BasicResultInfo(ResultType result, Date requestDate, HttpResponse response) {
64 | this(result, requestDate);
65 | wrap(response);
66 | }
67 |
68 |
69 | private BasicResultInfo(ResultType result, Date requestDate) {
70 | this.Result = result;
71 | this.RequestDate = requestDate;
72 | this.cancelled = false;
73 | }
74 |
75 |
76 | private void wrap(HttpURLConnection conn) throws IOException {
77 | if (conn != null) {
78 | try {
79 | this.ResponseCode = conn.getResponseCode();
80 | this.ResponseMessage = conn.getResponseMessage();
81 | } catch (IOException e) {
82 | if (BuildConfig.DEBUG) {
83 | Log.w(getClass().getName(), "IO Exception when wrapping URLConnection: " + e.getMessage());
84 | }
85 | throw e;
86 | }
87 | }
88 | }
89 |
90 | private void wrap(HttpResponse response) {
91 | if (response != null && response.getStatusLine() != null) {
92 | StatusLine status = response.getStatusLine();
93 | if (status != null) {
94 | this.ResponseCode = status.getStatusCode();
95 | this.ResponseMessage = status.getReasonPhrase();
96 | }
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/responses/HttpClientResponse.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.responses;
2 |
3 | import com.raizlabs.webservicemanager.HttpMethod;
4 |
5 | import org.apache.http.Header;
6 | import org.apache.http.HttpEntity;
7 | import org.apache.http.HttpResponse;
8 |
9 | import java.io.IOException;
10 | import java.io.InputStream;
11 |
12 | /**
13 | * {@link Response} implementation which wraps an {@link HttpResponse} which
14 | * is usually obtained via an {@link HttpClient}.
15 | * @author Dylan James
16 | *
17 | */
18 | public class HttpClientResponse extends BaseResponse {
19 | private HttpResponse response;
20 | private HttpMethod requestMethod;
21 |
22 | /**
23 | * Creates an {@link HttpClientResponse} from the given response and method.
24 | * @param response The actual {@link HttpResponse}.
25 | * @param requestMethod The {@link HttpMethod} which was used to obtain the
26 | * response.
27 | */
28 | public HttpClientResponse(HttpResponse response, HttpMethod requestMethod) {
29 | this.response = response;
30 | this.requestMethod = requestMethod;
31 | }
32 |
33 | @Override
34 | public boolean containsHeader(String name) {
35 | return response != null && response.containsHeader(name);
36 | }
37 |
38 | @Override
39 | public String getHeaderValue(String name) {
40 | if (response != null) {
41 | Header header = response.getFirstHeader(name);
42 | if (header != null) {
43 | return header.getValue();
44 | }
45 | }
46 |
47 | return null;
48 | }
49 |
50 | @Override
51 | public int getResponseCode() {
52 | if (response != null && response.getStatusLine() != null) {
53 | return response.getStatusLine().getStatusCode();
54 | }
55 | return -1;
56 | }
57 |
58 | @Override
59 | public String getResponseMessage() {
60 | if (response != null && response.getStatusLine() != null) {
61 | return response.getStatusLine().getReasonPhrase();
62 | }
63 | return null;
64 | }
65 |
66 | @Override
67 | public String getContentEncoding() {
68 | if (response != null && response.getEntity() != null &&
69 | response.getEntity().getContentEncoding() != null) {
70 | return response.getEntity().getContentEncoding().getValue();
71 | }
72 | return null;
73 | }
74 |
75 | @Override
76 | public long getContentLength() {
77 | if (response != null && response.getEntity() != null) {
78 | return response.getEntity().getContentLength();
79 | }
80 | return -1;
81 | }
82 |
83 | @Override
84 | public String getContentType() {
85 | if (response != null && response.getEntity() != null &&
86 | response.getEntity().getContentType() != null) {
87 | return response.getEntity().getContentType().getValue();
88 | }
89 | return null;
90 | }
91 |
92 | @Override
93 | public InputStream getContentStream() throws IOException {
94 | try {
95 | return response.getEntity().getContent();
96 | } catch (Exception e) {
97 | return null;
98 | }
99 | }
100 |
101 | @Override
102 | public HttpMethod getRequestMethod() {
103 | return requestMethod;
104 | }
105 |
106 | @Override
107 | public void close() {
108 | // Try to consume the content handled by the given entity
109 | // Not doing this may leave the connection open and can be dangerous
110 | // if reusing the client.
111 | // See: https://groups.google.com/forum/?fromgroups=#!topic/android-developers/uL8ah41voW4
112 | if (response != null) {
113 | HttpEntity entity = response.getEntity();
114 | if (entity != null) {
115 | try {
116 | entity.consumeContent();
117 | } catch (IOException e) { }
118 | }
119 | }
120 | }
121 |
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/DownloadFileRequest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import com.raizlabs.coreutils.listeners.ProgressListener;
4 | import com.raizlabs.webservicemanager.HttpMethod;
5 | import com.raizlabs.webservicemanager.responses.Response;
6 |
7 | import java.io.File;
8 |
9 | /**
10 | * A request which downloads a remote file and returns true if the download
11 | * was successful.
12 | * @author Dylan James
13 | *
14 | */
15 | public class DownloadFileRequest extends BaseWebServiceRequest {
16 |
17 | private RequestBuilder builder;
18 | private File localFile;
19 | private ProgressListener progressListener;
20 | /**
21 | * Sets the {@link ProgressListener} which will be called during the download.
22 | * @param listener
23 | */
24 | public void setProgressListener(ProgressListener listener) {
25 | this.progressListener = listener;
26 | }
27 |
28 | /**
29 | * Creates a {@link DownloadFileRequest} which downloads the content at the
30 | * given URL to a {@link File} at the given path.
31 | * @param localPath The path to download the content to.
32 | * @param url The URL to get the content at (using an HTTP GET).
33 | * @param listener The {@link ProgressListener} which will be called during
34 | * the download. (May be null)
35 | */
36 | public DownloadFileRequest(String localPath, String url, ProgressListener listener) {
37 | this(new File(localPath), url, listener);
38 | }
39 |
40 | /**
41 | * Creates a {@link DownloadFileRequest} which downloads the content at the
42 | * given URL to the given local {@link File}.
43 | * @param localFile The {@link File} to download the content to.
44 | * @param url The URL to get the content at (using an HTTP GET).
45 | * @param listener The {@link ProgressListener} which will be called during
46 | * the download. (May be null)
47 | */
48 | public DownloadFileRequest(File localFile, String url, ProgressListener listener) {
49 | this (localFile, new RequestBuilder(HttpMethod.Get, url), listener);
50 | }
51 |
52 | /**
53 | * Creates a {@link DownloadFileRequest} which downloads the content of the
54 | * response to a {@link File} at the given path.
55 | * @param localPath The path to download the content to.
56 | * @param request The {@link RequestBuilder} to execute.
57 | * @param listener The {@link ProgressListener} which will be called during
58 | * the download. (May be null)
59 | */
60 | public DownloadFileRequest(String localPath, RequestBuilder request, ProgressListener listener) {
61 | this(new File(localPath), request, listener);
62 | }
63 |
64 | /**
65 | * Creates a {@link DownloadFileRequest} which downloads the content of the
66 | * response to the given local {@link File}.
67 | * @param localFile The {@link File} to download the content to.
68 | * @param request The {@link RequestBuilder} to execute.
69 | * @param listener The {@link ProgressListener} which will be called during
70 | * the download. (May be null)
71 | */
72 | public DownloadFileRequest(File localFile, RequestBuilder request, ProgressListener listener) {
73 | if (localFile == null) {
74 | throw new NullPointerException("Local File cannot be null");
75 | }
76 | this.localFile = localFile;
77 | this.builder = request;
78 | this.progressListener = listener;
79 | }
80 |
81 | @Override
82 | protected RequestBuilder getRequestBuilder() {
83 | return builder;
84 | }
85 |
86 | @Override
87 | protected Boolean translate(Response response) {
88 | // "Middle man" listener which will publish progress and call any external
89 | // progress listener
90 | ProgressListener listener = new ProgressListener() {
91 | @Override
92 | public void onProgressUpdate(long currentProgress, long maxProgress) {
93 | publishProgress(currentProgress, maxProgress);
94 | if (progressListener != null) {
95 | progressListener.onProgressUpdate(currentProgress, maxProgress);
96 | }
97 | }
98 | };
99 | return response.readContentToFile(localFile, listener);
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/responses/Response.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.responses;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.Rect;
6 |
7 | import com.raizlabs.coreutils.listeners.ProgressListener;
8 | import com.raizlabs.webservicemanager.HttpMethod;
9 |
10 | import org.json.JSONArray;
11 | import org.json.JSONObject;
12 |
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.io.InputStream;
16 |
17 | /**
18 | * Interface for a web response which abstracts the underlying implementation.
19 | *
20 | * @author Dylan James
21 | *
22 | */
23 | public interface Response {
24 | /**
25 | * Returns true if the response contains a header with he given name.
26 | * @param name The name of the header to look for.
27 | * @return True if the header exists
28 | */
29 | public boolean containsHeader(String name);
30 | /**
31 | * Gets the value for the header with the given name, or null if no
32 | * header exists with that name.
33 | * @param name The name of the header to look up.
34 | * @return The value of the header or null if none exists.
35 | */
36 | public String getHeaderValue(String name);
37 |
38 | /**
39 | * Gets the response code for this {@link Response}
40 | * @return
41 | */
42 | public int getResponseCode();
43 | /**
44 | * Gets the response message for this {@link Response}.
45 | * @return
46 | */
47 | public String getResponseMessage();
48 |
49 | /**
50 | * Gets the content encoding for this {@link Response}.
51 | * @return The content encoding, or null if it wasn't defined.
52 | */
53 | public String getContentEncoding();
54 | /**
55 | * Gets the content length for this {@link Response}.
56 | * @return The content length, or -1 if it was not defined.
57 | */
58 | public long getContentLength();
59 | /**
60 | * Gets the content type for this {@link Response}
61 | * @return The content type, or null if it was not defined.
62 | */
63 | public String getContentType();
64 | /**
65 | * Gets the {@link InputStream} to the content of this
66 | * {@link Response} or null if one does not exist.
67 | * @return The {@link InputStream} to the content or null
68 | * if it does not exist.
69 | * @throws IOException
70 | */
71 | public InputStream getContentStream() throws IOException;
72 |
73 | /**
74 | * @return The {@link HttpMethod} that was used to get this
75 | * {@link Response}.
76 | */
77 | public HttpMethod getRequestMethod();
78 |
79 | /**
80 | * Gets the content of this {@link Response} by parsing it
81 | * into a string.
82 | * @return The content, or null if there was none.
83 | */
84 | public String getContentAsString();
85 |
86 | /**
87 | * Gets the content of this {@link Response} by decoding
88 | * the stream as a {@link Bitmap}.
89 | * @see BitmapFactory#decodeStream(InputStream, Rect, BitmapFactory.Options)
90 | * @param outPadding
91 | * @param options
92 | * @return The decoded {@link Bitmap}.
93 | */
94 | public Bitmap getContentAsBitmap(Rect outPadding, BitmapFactory.Options options);
95 |
96 | /**
97 | * Gets the content of this {@link Response} by parsing the
98 | * stream as an {@link JSONArray}.
99 | * @return The {@link JSONArray} parsed from the stream, or null if it
100 | * couldn't be parsed.
101 | */
102 | public JSONArray getContentAsJSONArray();
103 |
104 | /**
105 | * Gets the content of this {@link Response} by parsing the
106 | * stream as an {@link JSONObject}.
107 | * @return The {@link JSONObject} parsed from the stream, or null if it
108 | * couldn't be parsed.
109 | */
110 | public JSONObject getContentAsJSON();
111 |
112 | /**
113 | * Reads the contents of this {@link Response} into the given
114 | * file, updating the given {@link ProgressListener} as data
115 | * is read.
116 | * @param file The {@link File} to read the content into. This
117 | * file will be overwritten if it exists.
118 | * @param progressListener The {@link ProgressListener} which will
119 | * be called as data is read.
120 | * @return True if all the data was read successfully, or false if
121 | * there was an error, the content length wasn't defined, or the
122 | * content length didn't match. The {@link File} is not deleted if
123 | * the lengths didn't match or weren't defined.
124 | */
125 | public boolean readContentToFile(File file, ProgressListener progressListener);
126 |
127 | /**
128 | * Closes any connections or resources connected to this {@link Response}.
129 | */
130 | public void close();
131 | }
132 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/responses/BaseResponse.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.responses;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.BitmapFactory.Options;
6 | import android.graphics.Rect;
7 | import android.text.TextUtils;
8 |
9 | import com.raizlabs.coreutils.io.IOUtils;
10 | import com.raizlabs.coreutils.listeners.ProgressListener;
11 | import com.raizlabs.coreutils.logging.Logger;
12 |
13 | import org.json.JSONArray;
14 | import org.json.JSONException;
15 | import org.json.JSONObject;
16 |
17 | import java.io.File;
18 | import java.io.FileOutputStream;
19 | import java.io.IOException;
20 | import java.io.InputStream;
21 |
22 | /**
23 | * Abstract class which does some of the generic work for a response.
24 | * @author Dylan James
25 | *
26 | */
27 | public abstract class BaseResponse implements Response {
28 |
29 | @Override
30 | public String getContentAsString() {
31 | InputStream content = null;
32 | try {
33 | content = getContentStream();
34 | if (content != null) {
35 | return IOUtils.readStream(content);
36 | }
37 | } catch (IOException e) {
38 | Logger.w(getClass().getName(), "IOException in getContentAsString: " + e.getMessage());
39 | } finally {
40 | IOUtils.safeClose(content);
41 | }
42 | return null;
43 | }
44 |
45 | @Override
46 | public Bitmap getContentAsBitmap(Rect outPadding, Options options) {
47 | InputStream content = null;
48 | try {
49 | content = getContentStream();
50 | if (content != null) {
51 | return BitmapFactory.decodeStream(content, outPadding, options);
52 | }
53 | } catch (IOException e) {
54 | Logger.w(getClass().getName(), "IOException in getContentAsBitmap: " + e.getMessage());
55 | } finally {
56 | IOUtils.safeClose(content);
57 | }
58 |
59 | return null;
60 | }
61 |
62 | @Override
63 | public JSONArray getContentAsJSONArray() {
64 | String content = getContentAsString();
65 | if (!TextUtils.isEmpty(content)) {
66 | try {
67 | return new JSONArray(content);
68 | } catch (JSONException e) {
69 | Logger.w(getClass().getName(), "JSONException in getContentAsJSONArray: " + e.getMessage());
70 | }
71 | }
72 |
73 | return null;
74 | }
75 |
76 | @Override
77 | public JSONObject getContentAsJSON() {
78 | String content = getContentAsString();
79 | if (!TextUtils.isEmpty(content)) {
80 | try {
81 | return new JSONObject(content);
82 | } catch (JSONException e) {
83 | Logger.w(getClass().getName(), "JSONException in getContentAsJSON: " + e.getMessage());
84 | }
85 | }
86 |
87 | return null;
88 | }
89 |
90 | @Override
91 | public boolean readContentToFile(File file, ProgressListener progressListener) {
92 | InputStream input;
93 | try {
94 | // Get the input stream from the content
95 | input = getContentStream();
96 | // If there was no content stream, fail
97 | if (input == null) {
98 | return false;
99 | }
100 | } catch (IOException e) {
101 | Logger.w(getClass().getName(), "IOException in getContentToFile", e);
102 | return false;
103 | }
104 |
105 | // Delete the file if it exists
106 | if (file.exists()) {
107 | file.delete();
108 | }
109 | // Create the directory for the file
110 | file.getParentFile().mkdirs();
111 |
112 | FileOutputStream out = null;
113 | try {
114 | // Get an output stream to the file
115 | out = new FileOutputStream(file);
116 | } catch (IOException e) {
117 | Logger.w(getClass().getName(), "IOException in readContentToFile", e);
118 | IOUtils.safeClose(input);
119 | return false;
120 | }
121 |
122 | long expectedSize = getContentLength();
123 |
124 | try {
125 | byte[] buffer = new byte[1024];
126 | long totalRead = 0;
127 | int read;
128 | // Pump all the data
129 | while ((read = input.read(buffer)) != -1) {
130 | out.write(buffer, 0, read);
131 | out.flush();
132 | totalRead += read;
133 | // Update the progress listener if we have one
134 | if (progressListener != null) {
135 | progressListener.onProgressUpdate(totalRead, expectedSize);
136 | }
137 | }
138 | // If the expected size matches, we succeeded.
139 | // If the expected size was not defined, we return true as we don't know if it failed.
140 | return expectedSize == -1 || totalRead == expectedSize;
141 | } catch (IOException ex) {
142 | Logger.w(getClass().getName(), "IOException in readContentToFile", ex);
143 | return false;
144 | } finally {
145 | // Close both our streams
146 | IOUtils.safeClose(out);
147 | IOUtils.safeClose(input);
148 | }
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/RequestExecutionPool.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | import android.util.Log;
4 |
5 | import org.apache.http.HttpResponse;
6 | import org.apache.http.client.ClientProtocolException;
7 | import org.apache.http.client.methods.HttpUriRequest;
8 | import org.apache.http.protocol.BasicHttpContext;
9 | import org.apache.http.protocol.HttpContext;
10 |
11 | import java.io.IOException;
12 | import java.util.HashSet;
13 |
14 | /**
15 | * A class which maintains a set of executing requests.
16 | * @author Dylan James
17 | *
18 | */
19 | public class RequestExecutionPool {
20 |
21 | private HttpClientProvider clientProvider;
22 | /**
23 | * @return The {@link HttpClientProvider} which is being used to obtain
24 | * clients.
25 | */
26 | public HttpClientProvider getClientProvider() { return clientProvider; }
27 | /**
28 | * Sets the {@link HttpClientProvider} to use to obtain clients to
29 | * execute requests.
30 | * @param clientProvider
31 | */
32 | public void setClientProvider(HttpClientProvider clientProvider) { this.clientProvider = clientProvider; }
33 |
34 | /**
35 | * The set of the currently executing requests
36 | */
37 | private HashSet pendingRequests;
38 |
39 | /**
40 | * Constructs a new {@link RequestExecutionPool} with default parameters.
41 | */
42 | public RequestExecutionPool() {
43 | this.clientProvider = new BasicHttpClientProvider();
44 | init();
45 | }
46 |
47 | /**
48 | * Constructs a new {@link RequestExecutionPool} with the given number of
49 | * maximum connections.
50 | * @param maxConnections
51 | */
52 | public RequestExecutionPool(int maxConnections) {
53 | this.clientProvider = new BasicHttpClientProvider(maxConnections);
54 | init();
55 | }
56 |
57 | /**
58 | * Constructs a new {@link RequestExecutionPool} which uses the given
59 | * {@link HttpClientProvider} to obtain it's clients.
60 | * @param clientProvider The {@link HttpClientProvider} to obtain
61 | * clients from.
62 | */
63 | public RequestExecutionPool(HttpClientProvider clientProvider) {
64 | this.clientProvider = clientProvider;
65 | init();
66 | }
67 |
68 | private void init() {
69 | pendingRequests = new HashSet();
70 | }
71 |
72 | /**
73 | * Executes the given {@link HttpUriRequest} utilizing the current
74 | * {@link HttpClientProvider}.
75 | * @param request The {@link HttpUriRequest} to execute.
76 | * @return The {@link HttpResponse} resulting from the execution, or
77 | * null if something goes wrong.
78 | * @see #doRequestOrThrow(HttpUriRequest)
79 | */
80 | public HttpResponse doRequest(HttpUriRequest request) {
81 | addRequest(request);
82 | HttpContext context = new BasicHttpContext();
83 | try {
84 | return getClientProvider().getClient().execute(request, context);
85 | } catch (ClientProtocolException e) {
86 | if (BuildConfig.DEBUG) {
87 | Log.e(getClass().getName(), e.getMessage(), e);
88 | }
89 | } catch (IOException e) {
90 | if (BuildConfig.DEBUG) {
91 | Log.e(getClass().getName(), e.getMessage(), e);
92 | }
93 | } finally {
94 | removeRequest(request);
95 | }
96 | return null;
97 | }
98 |
99 | /**
100 | * Executes the given {@link HttpUriRequest} utilizing the current
101 | * {@link HttpClientProvider}, throwing any encountered exception.
102 | * @param request The {@link HttpUriRequest} to execute.
103 | * @return The {@link HttpResponse} resulting from the execution.
104 | * @throws ClientProtocolException
105 | * @throws IOException
106 | */
107 | public HttpResponse doRequestOrThrow(HttpUriRequest request) throws ClientProtocolException, IOException {
108 | addRequest(request);
109 | HttpResponse response = clientProvider.getClient().execute(request);
110 | removeRequest(request);
111 | return response;
112 | }
113 |
114 | private void addRequest(HttpUriRequest request) {
115 | synchronized (pendingRequests) {
116 | pendingRequests.add(request);
117 | }
118 | }
119 |
120 | private boolean removeRequest(HttpUriRequest request) {
121 | synchronized (pendingRequests) {
122 | return pendingRequests.remove(request);
123 | }
124 | }
125 |
126 | /**
127 | * Aborts the given {@link HttpUriRequest}.
128 | * @param request
129 | */
130 | public void abortRequest(final HttpUriRequest request) {
131 | new Thread(new Runnable() {
132 | @Override
133 | public void run() {
134 | request.abort();
135 | removeRequest(request);
136 | }
137 | }).start();
138 | }
139 |
140 | /**
141 | * Aborts all currently executing requests.
142 | */
143 | public void abortAllRequests() {
144 | synchronized (pendingRequests) {
145 | for (HttpUriRequest request : pendingRequests) {
146 | request.abort();
147 | }
148 | pendingRequests.clear();
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/WebServiceRequest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import com.raizlabs.coreutils.collections.MappableSet;
4 | import com.raizlabs.coreutils.functions.Delegate;
5 | import com.raizlabs.coreutils.listeners.ProgressListener;
6 | import com.raizlabs.webservicemanager.HttpMethod;
7 |
8 | import org.apache.http.HttpResponse;
9 |
10 | import java.net.HttpURLConnection;
11 |
12 | /**
13 | * An interface for a generic Web Service Request which can be requested in
14 | * multiple ways and then returns a result from the response data.
15 | *
16 | * Also supports storing a cancelled state and alerting listeners when it is
17 | * cancelled.
18 | *
19 | * @see BaseWebServiceRequest for easier implementation.
20 | * @author Dylan James
21 | *
22 | * @param The type that this will return after the request is complete.
23 | */
24 | public interface WebServiceRequest extends HttpUriRequestable, UrlConnectionRequestable {
25 | public interface CancelListener {
26 | public void onCancel(WebServiceRequest request);
27 | }
28 |
29 | static class CancelListenerSet extends MappableSet> {
30 | public void onCancel(final WebServiceRequest request) {
31 | map(new Delegate>() {
32 | @Override
33 | public void execute(CancelListener listener) {
34 | listener.onCancel(request);
35 | }
36 | });
37 | }
38 | }
39 |
40 | /**
41 | * Called when the {@link HttpURLConnection} is connected, allowing
42 | * data to be written to the output stream etc.
43 | * @param connection The {@link HttpURLConnection} which has been
44 | * connected.
45 | */
46 | void onConnected(HttpURLConnection connection);
47 | /**
48 | * Translates the established {@link HttpURLConnection} into
49 | * a ResultType object.
50 | * @param connection The {@link HttpURLConnection} to get
51 | * data from.
52 | * @return A ResultType object representing this response.
53 | */
54 | ResultType translateConnection(HttpURLConnection connection);
55 |
56 | /**
57 | * Translates the given {@link HttpResponse} into a ResultType
58 | * object.
59 | * @param response The {@link HttpResponse} which was the result
60 | * of this request.
61 | * @param requestMethod The {@link HttpMethod} which was used to
62 | * request this request.
63 | * @return A ResultType object representing this response.
64 | */
65 | ResultType translateHTTPResponse(HttpResponse response, HttpMethod requestMethod);
66 |
67 | /**
68 | * Gets the object which is used as the lock for the status of this request.
69 | * Changes to the status of this request cannot be made without holding this
70 | * lock, including starting and canceling the request. Holding this lock
71 | * guarantees that the status will not change.
72 | * @return The lock to use for status changes.
73 | */
74 | Object getStatusLock();
75 |
76 | /**
77 | * Called to indicate that this request is being started.
78 | */
79 | void onStart();
80 |
81 | /**
82 | * @return True if this request has started.
83 | */
84 | boolean isStarted();
85 |
86 | /**
87 | * Marks this {@link WebServiceRequest} as cancelled and alerts any listeners.
88 | *
89 | * @see #isCancelled()
90 | * @see #addOnCancelListener(SimpleEventListener)
91 | */
92 | void cancel();
93 | /**
94 | * @return True if this {@link WebServiceRequest} has been cancelled.
95 | *
96 | * @see #cancel()
97 | */
98 | boolean isCancelled();
99 | /**
100 | * Adds a listener which will be called if this {@link WebServiceRequest} is
101 | * cancelled. This listener is called immediately if this request has
102 | * already been cancelled.
103 | *
104 | * @see #cancel()
105 | * @see #removeOnCancelListener(CancelListener)
106 | * @param listener The {@link CancelListener} to call on cancellation.
107 | */
108 | void addOnCancelListener(CancelListener listener);
109 | /**
110 | * Removes the given {@link Delegate} from being notified of
111 | * cancellations.
112 | * @param listener The {@link CancelListener} to remove.
113 | * @return True if the listener was removed, false if it was not found.
114 | */
115 | boolean removeOnCancelListener(CancelListener listener);
116 |
117 | /**
118 | * Adds a listener which will be called when the progress is updated.
119 | *
NOTE: Some requests may not publish progress.
120 | * @param listener The {@link ProgressListener} which will be called
121 | * on progress updates.
122 | */
123 | void addProgressListener(ProgressListener listener);
124 | /**
125 | * Removes the given {@link ProgressListener} from being notified
126 | * of progress updates.
127 | * @param listener The {@link ProgressListener} to remove.
128 | * @return True if the listener was removed, false if it was not found.
129 | */
130 | boolean removeProgressListener(ProgressListener listener);
131 | }
132 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/json/JSONNameValuesMap.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.json;
2 |
3 | import org.json.JSONArray;
4 | import org.json.JSONException;
5 | import org.json.JSONObject;
6 |
7 | import java.util.Hashtable;
8 |
9 | /**
10 | * Class for handling a JSON Array of pairs of Name/Value-arrays, of the format:
11 | * [
12 | * {
13 | * "name":"KEY_NAME1"
14 | * "values": ["val1","val2","val3"]
15 | * },
16 | * {
17 | * "name":"KEY_NAME2"
18 | * "values": ["val1","val2","val3"]
19 | * }
20 | * ]
21 | *
22 | * Allows looking up the values JSONArray by their name.
23 | *
24 | * @author Dylan James
25 | *
26 | */
27 | public class JSONNameValuesMap {
28 | private Hashtable valueTable;
29 |
30 | /**
31 | * Creates a {@link JSONNameValuesMap} by parsing the given string
32 | * as a {@link JSONArray}.
33 | * @param string The string to parse.
34 | * @throws JSONException if the given string cannot be parsed as a
35 | * {@link JSONArray}
36 | */
37 | public JSONNameValuesMap(String string) throws JSONException {
38 | this(new JSONArray(string));
39 | }
40 |
41 | /**
42 | * Creates a {@link JSONNameValuesMap} from the given {@link JSONArray}.
43 | * @param jsonArray The {@link JSONArray} to get the name/value pairs from.
44 | */
45 | public JSONNameValuesMap(JSONArray jsonArray) {
46 | this();
47 | parseJSONArray(jsonArray);
48 | }
49 |
50 | private JSONNameValuesMap() {
51 | valueTable = new Hashtable();
52 | }
53 |
54 | private void parseJSONArray(JSONArray array) {
55 | // Clear our values
56 | valueTable.clear();
57 | final int count = array.length();
58 | // Loop through all indices in the array
59 | for (int i = 0; i < count; ++i) {
60 | // Try adding the data from the current index
61 | // If we can't find any piece of data, ignore this item
62 | try {
63 | JSONObject currentObj = array.getJSONObject(i);
64 | String name = currentObj.getString("name");
65 | JSONArray values = currentObj.getJSONArray("values");
66 | valueTable.put(name, values);
67 | } catch (JSONException e) { }
68 | }
69 | }
70 |
71 | /**
72 | * Gets the {@link JSONArray} for the given key, or throws an exception if
73 | * the key does not exist.
74 | * @param key The "name" to look up
75 | * @return The {@link JSONArray} bound to the given key.
76 | * @throws JSONException if the key does not exist.
77 | */
78 | public JSONArray getValues(String key) throws JSONException {
79 | JSONArray values = valueTable.get(key);
80 | if (values == null) {
81 | throw new JSONException("Key not found in map: " + key);
82 | }
83 | return values;
84 | }
85 |
86 | /**
87 | * Gets the {@link JSONArray} for the given key, or null if it does not
88 | * exist.
89 | * @param key The "name" to look up.
90 | * @return The {@link JSONArray} bound to the given key, or null if it does
91 | * not exist.
92 | */
93 | public JSONArray optValues(String key) {
94 | return valueTable.get(key);
95 | }
96 |
97 | /**
98 | * Gets the value for the given key at the given index.
99 | * @param key The "name" to look up.
100 | * @param index The index of the value to get from the values {@link JSONArray}.
101 | * @return The string value at the given index of the given key.
102 | * @throws JSONException If the key does not exist in the map or the value
103 | * does not exist in the keys values.
104 | */
105 | public String getStringValue(String key, int index) throws JSONException {
106 | return getValues(key).getString(index);
107 | }
108 |
109 | /**
110 | * Gets the value for the given key at the given index.
111 | * @param key The "name" to look up.
112 | * @param index The index of the value to get from the values {@link JSONArray}.
113 | * @return Null if the key does not exist or an empty string if there is no
114 | * value at that index. If the value is not a string and is not null, then it
115 | * is converted to a string.
116 | */
117 | public String optStringValue(String key, int index) {
118 | JSONArray values = optValues(key);
119 | if (values != null) {
120 | return values.optString(index);
121 | }
122 | return null;
123 | }
124 |
125 | /**
126 | * Convenience method which gets the string value at the first index of the
127 | * given key.
128 | * @param key The "name" to look up.
129 | * @return The first string value in the array mapped to the given key.
130 | * @throws JSONException If the key does not exist or the value does not exist
131 | * in the keys values.
132 | */
133 | public String getFirstString(String key) throws JSONException {
134 | return getStringValue(key, 0);
135 | }
136 |
137 | /**
138 | * Convenience method which gets the string value at the first index of the
139 | * given key, or null if it doesn't exist.
140 | * @param key The "name" to look up.
141 | * @return The first string value in the array mapped to the given key, or
142 | * null if the key does not exist, maps to no values, or the first value
143 | * is null.
144 | */
145 | public String optFirstString(String key) {
146 | return optStringValue(key, 0);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ssl/SimpleSSLSocketFactory.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.ssl;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import org.apache.http.conn.ConnectTimeoutException;
6 | import org.apache.http.conn.scheme.LayeredSocketFactory;
7 | import org.apache.http.conn.scheme.SocketFactory;
8 | import org.apache.http.params.HttpConnectionParams;
9 | import org.apache.http.params.HttpParams;
10 |
11 | import java.io.IOException;
12 | import java.net.InetAddress;
13 | import java.net.InetSocketAddress;
14 | import java.net.Socket;
15 | import java.net.UnknownHostException;
16 | import java.security.KeyManagementException;
17 | import java.security.NoSuchAlgorithmException;
18 | import java.security.SecureRandom;
19 |
20 | import javax.net.ssl.SSLContext;
21 | import javax.net.ssl.SSLSocket;
22 |
23 | /**
24 | * Simple {@link SocketFactory} implementation which creates SSL sockets
25 | * according to a given {@link TrustManager}.
26 | *
27 | * Based on EasySSLSocketFactory by olamy
28 | * @author Dylan James
29 | *
30 | */
31 | public class SimpleSSLSocketFactory implements SocketFactory, LayeredSocketFactory {
32 |
33 | private static SSLContext createSSLContext(@NonNull TrustManager trustManager, @NonNull TLS tls) {
34 | try {
35 | SSLContext context = SSLContext.getInstance(tls.getVersion());
36 | context.init(null, new TrustManager[] { trustManager }, new SecureRandom());
37 | return context;
38 | } catch (NoSuchAlgorithmException e) {
39 | } catch (KeyManagementException e) { }
40 | return null;
41 | }
42 |
43 | private TrustManager trustManager;
44 | private TLS tls;
45 | /**
46 | * @return The {@link TrustManager} used to verify SSL Sockets.
47 | */
48 | public TrustManager getTrustManager() { return trustManager; }
49 | /**
50 | * Sets the {@link TrustManager} to use to verify SSL Sockets.
51 | * @param manager The trust manager to use.
52 | */
53 | public void setTrustManager(TrustManager manager, @NonNull TLS tls) {
54 | this.trustManager = manager;
55 | this.tls = tls;
56 | this.sslContext = null;
57 | }
58 |
59 | private SSLContext sslContext;
60 | /**
61 | * @return The SSL Context to use to create sockets
62 | */
63 | public SSLContext getSSLContext() {
64 | if (sslContext == null) {
65 | sslContext = createSSLContext(trustManager, tls);
66 | }
67 | return sslContext;
68 | }
69 |
70 | /**
71 | * Constructs an {@link SimpleSSLSocketFactory} that uses the given
72 | * trust manager to verify SSL Sockets.
73 | * @param trustManager The trust manager to use.
74 | */
75 | public SimpleSSLSocketFactory(TrustManager trustManager, @NonNull TLS tls) {
76 | this.trustManager = trustManager;
77 | this.tls = tls;
78 | }
79 |
80 | @Override
81 | public Socket createSocket(Socket socket, String host, int port,
82 | boolean autoClose) throws IOException {
83 | SSLSocket sslSock = (SSLSocket) getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
84 | if (!verifySocket(host, sslSock)) {
85 | throw new IOException("Server was not trusted!");
86 | }
87 |
88 | return sslSock;
89 | }
90 |
91 | @Override
92 | public Socket createSocket() throws IOException {
93 | return getSSLContext().getSocketFactory().createSocket();
94 | }
95 |
96 | @Override
97 | public Socket connectSocket(Socket sock, String host, int port,
98 | InetAddress localAddress, int localPort, HttpParams params)
99 | throws IOException, UnknownHostException, ConnectTimeoutException {
100 | int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
101 | int soTimeout = HttpConnectionParams.getSoTimeout(params);
102 |
103 | InetSocketAddress remoteAddress = new InetSocketAddress(host, port);
104 | SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());
105 |
106 | if (!verifySocket(host, sslsock)) {
107 | throw new IOException("Server was not trusted!");
108 | }
109 |
110 | if ((localAddress != null) || (localPort > 0)) {
111 | // we need to bind explicitly
112 | if (localPort < 0) {
113 | localPort = 0; // indicates "any"
114 | }
115 | InetSocketAddress isa = new InetSocketAddress(localAddress,
116 | localPort);
117 | sslsock.bind(isa);
118 | }
119 | sslsock.connect(remoteAddress, connTimeout);
120 | sslsock.setSoTimeout(soTimeout);
121 | return sslsock;
122 | }
123 |
124 | private boolean verifySocket(String host, SSLSocket socket) {
125 | return trustManager == null || trustManager.verify(host, socket.getSession());
126 | }
127 |
128 | @Override
129 | public boolean isSecure(Socket sock) throws IllegalArgumentException {
130 | return true;
131 | }
132 |
133 | // -------------------------------------------------------------------
134 | // javadoc in org.apache.http.conn.scheme.SocketFactory says :
135 | // Both Object.equals() and Object.hashCode() must be overridden
136 | // for the correct operation of some connection managers
137 | // -------------------------------------------------------------------
138 |
139 | public boolean equals(Object obj) {
140 | if (obj != null && obj instanceof SimpleSSLSocketFactory) {
141 | SimpleSSLSocketFactory other = (SimpleSSLSocketFactory) obj;
142 | return (trustManager != null && trustManager.equals(other.getTrustManager()));
143 | }
144 | return false;
145 | }
146 |
147 | public int hashCode() {
148 | return getTrustManager().hashCode();
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/ssl/DelegatedX509TrustManager.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.ssl;
2 |
3 | import java.security.KeyStore;
4 | import java.security.cert.CertificateException;
5 | import java.security.cert.X509Certificate;
6 |
7 | import javax.net.ssl.HostnameVerifier;
8 | import javax.net.ssl.HttpsURLConnection;
9 | import javax.net.ssl.SSLSession;
10 | import javax.net.ssl.X509TrustManager;
11 |
12 | /**
13 | * {@link TrustManager} which initially checks a default manager and
14 | * hostname verifier and calls a delegate if they don't trust the source.
15 | * @author Dylan James
16 | */
17 | public class DelegatedX509TrustManager implements TrustManager {
18 | private X509TrustManager defaultManager;
19 | /**
20 | * @return The {@link X509TrustManager} which is checked before the delegate
21 | * is invoked.
22 | */
23 | public X509TrustManager getTrustManager() { return defaultManager; }
24 | /**
25 | * Sets the manager to check certificates against before invoking the
26 | * delegate.
27 | * @param trustManager The manager to check. Null to always call the delegate.
28 | */
29 | public void setTrustManager(X509TrustManager trustManager) {
30 | this.defaultManager = trustManager;
31 | }
32 |
33 | private HostnameVerifier defaultVerifier;
34 | /**
35 | * @return THe {@link HostnameVerifier} which is checked before the delegate
36 | * is invoked.
37 | */
38 | public HostnameVerifier getHostnameVerifier() { return defaultVerifier; }
39 | /**
40 | * Sets the verifier to check hostnames against before invoking the
41 | * delegate.
42 | * @param verifier The verifier to check. Null to always call the delegate.
43 | */
44 | public void setHostnameVerifier(HostnameVerifier verifier) {
45 | this.defaultVerifier = verifier;
46 | }
47 |
48 |
49 | private TrustDelegate certDelegate;
50 | /**
51 | * @return The current {@link TrustDelegate} which is being used
52 | * to determine whether certificates or hosts should be trusted.
53 | */
54 | public TrustDelegate getCertificateCheckDelegate() { return certDelegate; }
55 | /**
56 | * Sets the {@link TrustDelegate} to use to determine whether
57 | * certificates should be trusted when the default manager doesn't trust
58 | * the certificate or host.
59 | * @param delegate The delegate to use.
60 | */
61 | public void setCertificateCheckDelegate(TrustDelegate delegate) {
62 | this.certDelegate = delegate;
63 | }
64 |
65 | /**
66 | * Constructs a new {@link DelegatedX509TrustManager} which first checks
67 | * the default trust manager and then the given delegate.
68 | * @param keystore The keystore to use to construct the default manager
69 | * @param delegate The {@link TrustDelegate} to query when the
70 | * default trust manager rejects a certificate.
71 | */
72 | public DelegatedX509TrustManager(KeyStore keystore, TrustDelegate delegate) {
73 | this(new DefaultX509TrustManager(keystore), delegate);
74 | }
75 |
76 | /**
77 | * Constructs a new {@link DelegatedX509TrustManager} which first checks
78 | * the given trust manager and then the given delegate.
79 | * @param trustManager The manager to check certificates against first,
80 | * before checking the delegate. Passing null will always check the delegate.
81 | * @param delegate The {@link TrustDelegate} to query when the
82 | * trust manager rejects a certificate.
83 | */
84 | public DelegatedX509TrustManager(X509TrustManager trustManager,
85 | TrustDelegate delegate) {
86 | super();
87 | setTrustManager(trustManager);
88 | setHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier());
89 | setCertificateCheckDelegate(delegate);
90 | }
91 |
92 |
93 | @Override
94 | public void checkClientTrusted(X509Certificate[] chain, String authType)
95 | throws CertificateException {
96 | checkCertificateTrusted(chain, authType, false);
97 | }
98 |
99 | @Override
100 | public void checkServerTrusted(X509Certificate[] chain, String authType)
101 | throws CertificateException {
102 | checkCertificateTrusted(chain, authType, true);
103 | }
104 |
105 | protected void checkCertificateTrusted(X509Certificate[] chain, String authType, boolean isServer)
106 | throws CertificateException {
107 | // If we have a default manager, try it first
108 | if (defaultManager != null) {
109 | try {
110 | if (isServer)
111 | defaultManager.checkServerTrusted(chain, authType);
112 | else
113 | defaultManager.checkClientTrusted(chain, authType);
114 | } catch (CertificateException e) {
115 | // The default manager rejected the certificate!
116 | // Try the delegate
117 | if (!checkDelegateTrustsCertificate(chain, authType, isServer)) {
118 | // Delegate didn't trust it either! Throw the exception!
119 | throw e;
120 | }
121 | }
122 | } else {
123 | // Just check the delegate
124 | if (!checkDelegateTrustsCertificate(chain, authType, isServer)) {
125 | // Delegate didn't trust the certificate!
126 | throw new CertificateException("Delegate didn't trust the certificate and no default manager found!");
127 | }
128 | }
129 | }
130 |
131 | /**
132 | * Checks if our delegate trusts the certificate
133 | * @param chain
134 | * @param authType
135 | * @param isServer
136 | * @return True if the delegate trusted the certficate, false if it didn't
137 | * or we have no delegate
138 | */
139 | private boolean checkDelegateTrustsCertificate(X509Certificate[] chain, String authType, boolean isServer) {
140 | if (certDelegate != null)
141 | return certDelegate.checkCertificateTrusted(chain, authType, isServer);
142 | else
143 | return false;
144 | }
145 |
146 | @Override
147 | public X509Certificate[] getAcceptedIssuers() {
148 | if (defaultManager != null) {
149 | return defaultManager.getAcceptedIssuers();
150 | }
151 | return null;
152 | }
153 | @Override
154 | public boolean verify(String hostname, SSLSession session) {
155 | if (certDelegate != null) {
156 | return certDelegate.checkHostnameTrusted(hostname, session);
157 | }
158 |
159 | if (defaultVerifier != null && defaultVerifier.verify(hostname, session)) {
160 | return true;
161 | }
162 |
163 | return false;
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/BaseWebServiceRequest.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import com.raizlabs.coreutils.listeners.ProgressListener;
4 | import com.raizlabs.webservicemanager.HttpMethod;
5 | import com.raizlabs.webservicemanager.responses.HttpClientResponse;
6 | import com.raizlabs.webservicemanager.responses.HttpURLConnectionResponse;
7 | import com.raizlabs.webservicemanager.responses.Response;
8 |
9 | import org.apache.http.HttpResponse;
10 | import org.apache.http.client.methods.HttpUriRequest;
11 |
12 | import java.net.HttpURLConnection;
13 | import java.util.HashSet;
14 |
15 | /**
16 | * Base implementation that can be used as a starting point for implementing
17 | * a {@link WebServiceRequest} and simplifies the implementation down to a
18 | * couple functions.
19 | *
20 | * @author Dylan James
21 | *
22 | * @param The type of object that this request will return.
23 | */
24 | public abstract class BaseWebServiceRequest implements WebServiceRequest {
25 |
26 | /**
27 | * Gets the {@link RequestBuilder} to be used to execute this request.
28 | * This should be fully populated and set, as it may be executed
29 | * immediately and may never be called again.
30 | * @return The fully populated {@link RequestBuilder} to use to
31 | * execute this request.
32 | */
33 | protected abstract RequestBuilder getRequestBuilder();
34 |
35 | // Store the RequestBuilder so we only query it once.
36 | private RequestBuilder requestBuilder;
37 | /**
38 | * @return The {@link RequestBuilder} for this request. Only built once.
39 | */
40 | private RequestBuilder getRequest() {
41 | if (requestBuilder == null) {
42 | requestBuilder = getRequestBuilder();
43 | }
44 | return requestBuilder;
45 | }
46 |
47 | @Override
48 | public Object getStatusLock() {
49 | return this;
50 | }
51 |
52 | /**
53 | * Flag that the request has started
54 | */
55 | private boolean isStarted = false;
56 | @Override
57 | public boolean isStarted() {
58 | synchronized (getStatusLock()) {
59 | return isStarted;
60 | }
61 | }
62 |
63 | @Override
64 | public void onStart() {
65 | synchronized (getStatusLock()) {
66 | isStarted = true;
67 | }
68 | }
69 |
70 | /**
71 | * Cancellation flag
72 | */
73 | private boolean cancelled = false;
74 | /**
75 | * Set of cancellation listeners that are subscribed.
76 | */
77 | private CancelListenerSet cancelListeners = new CancelListenerSet();
78 |
79 | @Override
80 | public void cancel() {
81 | synchronized(getStatusLock()) {
82 | cancelled = true;
83 | // Call any listeners that are subscribed.
84 | cancelListeners.onCancel(this);
85 | }
86 | }
87 |
88 | public boolean isCancelled() {
89 | synchronized(getStatusLock()) {
90 | return cancelled;
91 | }
92 | }
93 |
94 | @Override
95 | public void addOnCancelListener(CancelListener listener) {
96 | synchronized(getStatusLock()) {
97 | cancelListeners.add(listener);
98 | // If we're already cancelled, call it immediately.
99 | if (cancelled) {
100 | listener.onCancel(this);
101 | }
102 | }
103 | }
104 |
105 | @Override
106 | public boolean removeOnCancelListener(CancelListener listener) {
107 | synchronized(getStatusLock()) {
108 | return cancelListeners.remove(listener);
109 | }
110 | }
111 |
112 | /**
113 | * Set of {@link ProgressListener}s that are subscribed to progress updates.
114 | */
115 | private HashSet progressListeners = new HashSet();
116 |
117 | @Override
118 | public void addProgressListener(ProgressListener listener) {
119 | synchronized (progressListeners) {
120 | progressListeners.add(listener);
121 | }
122 | }
123 |
124 | @Override
125 | public boolean removeProgressListener(ProgressListener listener) {
126 | synchronized (progressListeners) {
127 | return progressListeners.remove(listener);
128 | }
129 | }
130 |
131 | /**
132 | * Notifies all listeners of the given progress
133 | * @param currentProgress The current progress, or -1 if unknown.
134 | * @param maxProgress The maximum progress, or -1 if unknown.
135 | */
136 | protected void publishProgress(long currentProgress, long maxProgress) {
137 | synchronized(progressListeners) {
138 | for (ProgressListener listener : progressListeners) {
139 | listener.onProgressUpdate(currentProgress, maxProgress);
140 | }
141 | }
142 | }
143 |
144 | @Override
145 | public HttpURLConnection getUrlConnection() {
146 | // Get the URL Connection via the RequestBuilder
147 | return getRequest().getConnection();
148 | }
149 |
150 | @Override
151 | public HttpUriRequest getHttpUriRequest() {
152 | // Get the URI Request via the RequestBuilder
153 | return getRequest().getRequest();
154 | }
155 |
156 | /**
157 | * Called to translate an {@link Response} to a ResultType object.
158 | * @param response The {@link Response} to translate.
159 | * @return The ResultType object represented by the {@link Response}.
160 | */
161 | protected abstract ResultType translate(Response response);
162 |
163 | public ResultType translateHTTPResponse(HttpResponse response, HttpMethod requestMethod) {
164 | // Wrap the HttpResponse into a Response implementation
165 | // and have subclasses translate it
166 | final Response wrappedResponse = new HttpClientResponse(response, requestMethod);
167 | ResultType result = translate(wrappedResponse);
168 | // Close the response to free any resources
169 | wrappedResponse.close();
170 | return result;
171 | }
172 | public ResultType translateConnection(HttpURLConnection connection) {
173 | // Wrap the connection into a Response implementation
174 | // and have subclasses translate it
175 | final Response wrappedResponse = new HttpURLConnectionResponse(connection);
176 | ResultType result = translate(wrappedResponse);
177 | // Close the response to free any resources
178 | wrappedResponse.close();
179 | return result;
180 | };
181 |
182 | @Override
183 | public void onConnected(HttpURLConnection connection) {
184 | // OnConnected, call the RequestBuilder
185 | getRequest().onConnected(connection);
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/BasicHttpClientProvider.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager;
2 |
3 | import org.apache.http.HttpVersion;
4 | import org.apache.http.ProtocolVersion;
5 | import org.apache.http.client.HttpClient;
6 | import org.apache.http.conn.ClientConnectionManager;
7 | import org.apache.http.conn.params.ConnManagerParams;
8 | import org.apache.http.conn.params.ConnPerRoute;
9 | import org.apache.http.conn.routing.HttpRoute;
10 | import org.apache.http.conn.scheme.PlainSocketFactory;
11 | import org.apache.http.conn.scheme.Scheme;
12 | import org.apache.http.conn.scheme.SchemeRegistry;
13 | import org.apache.http.conn.scheme.SocketFactory;
14 | import org.apache.http.conn.ssl.SSLSocketFactory;
15 | import org.apache.http.impl.client.DefaultHttpClient;
16 | import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
17 | import org.apache.http.params.BasicHttpParams;
18 | import org.apache.http.params.HttpConnectionParams;
19 | import org.apache.http.params.HttpParams;
20 | import org.apache.http.params.HttpProtocolParams;
21 |
22 | /**
23 | * A basic implementation of {@link HttpClientProvider} with multiple hooks for
24 | * extending or modifying built in behavior.
25 | * @author Dylan James
26 | *
27 | */
28 | public class BasicHttpClientProvider implements HttpClientProvider{
29 |
30 | private int maxConnections;
31 | /**
32 | * @return The current maximum number of connections.
33 | * @see #setMaxConnections(int)
34 | */
35 | public int getMaxConnections() { return maxConnections; }
36 | public void setMaxConnections(int connections) {
37 | this.maxConnections = connections;
38 | init();
39 | }
40 |
41 |
42 | private int connectionTimeout = Constants.Defaults.ConnectionTimeoutMillis;
43 |
44 | /**
45 | * {@inheritDoc}
46 | */
47 | public int getConnectionTimeout() { return connectionTimeout; }
48 | /**
49 | * {@inheritDoc}
50 | */
51 | public void setConnectionTimeout(int timeoutMillis) { connectionTimeout = timeoutMillis; }
52 |
53 |
54 | private int readTimeout = Constants.Defaults.ReadTimeoutMillis;
55 | /**
56 | * {@inheritDoc}
57 | */
58 | public int getReadTimeout() { return readTimeout; }
59 | /**
60 | * {@inheritDoc}
61 | */
62 | public void setReadTimeout(int timeoutMillis) { readTimeout = timeoutMillis; }
63 |
64 |
65 |
66 | private ProtocolVersion protocolVersion;
67 | /**
68 | * Gets the {@link ProtocolVersion} which will be used to construct the
69 | * connection parameters.
70 | *
71 | * @see #getConnectionParams()
72 | * @see #setProtocolVersion(ProtocolVersion)
73 | * @return The {@link ProtocolVersion}
74 | */
75 | public ProtocolVersion getProtocolVersion() { return protocolVersion; }
76 | /**
77 | * Sets the {@link ProtocolVersion} which will be used to construct the
78 | * connection parameters.
79 | *
80 | * @see #getConnectionParams()
81 | * @see #getProtocolVersion()
82 | * @param version The {@link ProtocolVersion} to use.
83 | */
84 | public void setProtocolVersion(ProtocolVersion version) { this.protocolVersion = version; }
85 |
86 | private SocketFactory httpFactory, httpsFactory;
87 |
88 | /**
89 | * Called to get the {@link SocketFactory} for HTTP calls, which
90 | * will be registered with the {@link SchemeRegistry}.
91 | *
92 | * @see #getSchemeRegistry()
93 | * @return The {@link SocketFactory} to be registered with the
94 | * {@link SchemeRegistry} for HTTP calls.
95 | */
96 | public SocketFactory getHttpSocketFactory() {
97 | return httpFactory;
98 | }
99 |
100 | @Override
101 | public void setHttpSocketFactory(SocketFactory factory) {
102 | this.httpFactory = factory;
103 | }
104 |
105 | /**
106 | * Called to get the {@link SocketFactory} for HTTPS calls, which
107 | * will be registered with the {@link SchemeRegistry}.
108 | *
109 | * @see #getSchemeRegistry()
110 | * @return The {@link SocketFactory} to be registered with the
111 | * {@link SchemeRegistry} for HTTPS calls.
112 | */
113 | public SocketFactory getHttpsSocketFactory() {
114 | return httpsFactory;
115 | }
116 |
117 | @Override
118 | public void setHttpsSocketFactory(SocketFactory factory) {
119 | this.httpsFactory = factory;
120 | }
121 |
122 | /**
123 | * The {@link HttpClient} we will be reusing.
124 | */
125 | private HttpClient client;
126 |
127 | /**
128 | * Constructs a {@link BasicHttpClientProvider} with default values.
129 | */
130 | public BasicHttpClientProvider() {
131 | this(5);
132 | }
133 |
134 | /**
135 | * Constructs a {@link BasicHttpClientProvider} with the given number
136 | * of maximum connections.
137 | * @param maxConnections The maximum number of connections to allow.
138 | */
139 | public BasicHttpClientProvider(int maxConnections) {
140 | this.maxConnections = maxConnections;
141 | this.protocolVersion = HttpVersion.HTTP_1_1;
142 | setHttpSocketFactory(PlainSocketFactory.getSocketFactory());
143 | setHttpsSocketFactory(SSLSocketFactory.getSocketFactory());
144 | }
145 |
146 | /**
147 | * Does some initialization and population of this {@link BasicHttpClientProvider}
148 | * which needs to be run before a client is given. This will be called lazily, but
149 | * you may call this earlier to prevent start up delays.
150 | */
151 | public void init() {
152 | // Populate our params, and construct a client from them
153 | HttpParams connParams = getConnectionParams();
154 | SchemeRegistry schemeRegistry = getSchemeRegistry();
155 | ClientConnectionManager connManager = getClientConnectionManager(connParams, schemeRegistry);
156 |
157 | client = createClient(connManager, connParams);
158 | }
159 |
160 | /**
161 | * Called to create the {@link HttpClient} we will be using. This may be
162 | * overriden by subclasses to use different implementations.
163 | *
164 | * @see #getConnectionParams()
165 | * @see #getClientConnectionManager(HttpParams, SchemeRegistry)
166 | * @param connManager The {@link ClientConnectionManager} which should be
167 | * used to construct the {@link HttpClient}
168 | * @param params The {@link HttpParams} which should be provided to the
169 | * {@link HttpClient}
170 | * @return The created {@link HttpClient} which will be provided to callers
171 | * of {@link #getClient()}
172 | */
173 | protected HttpClient createClient(ClientConnectionManager connManager, HttpParams params) {
174 | return new DefaultHttpClient(connManager, params);
175 | }
176 |
177 | /**
178 | * Called to get the {@link HttpParams} which will be used as the connection
179 | * parameters for the created {@link HttpClient}. Subclasses may override
180 | * this to provide different params.
181 | *
182 | * @see #createClient(ClientConnectionManager, HttpParams)
183 | * @return The {@link HttpParams} which should be sent to the {@link HttpClient}.
184 | */
185 | protected HttpParams getConnectionParams() {
186 | HttpParams params = new BasicHttpParams();
187 | ConnManagerParams.setMaxTotalConnections(params, getMaxConnections());
188 | ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRoute() {
189 | final int maxConnections = getMaxConnections();
190 | @Override
191 | public int getMaxForRoute(HttpRoute route) {
192 | return maxConnections;
193 | }
194 | });
195 | HttpConnectionParams.setConnectionTimeout(params, connectionTimeout);
196 | HttpConnectionParams.setSoTimeout(params, readTimeout);
197 |
198 | HttpProtocolParams.setVersion(params, getProtocolVersion());
199 | return params;
200 | }
201 |
202 | /**
203 | * Called to get the {@link ClientConnectionManager} which will be provided
204 | * to the {@link HttpClient}. Subclasses may override this to provide a
205 | * different manager.
206 | *
207 | * @see #createClient(ClientConnectionManager, HttpParams)
208 | * @param params The {@link HttpParams} which should be inserted into the
209 | * manager.
210 | * @param schemeRegistry The {@link SchemeRegistry} that should be given
211 | * to the manager.
212 | * @return The {@link ClientConnectionManager} which should be sent to
213 | * the {@link HttpClient}.
214 | */
215 | protected ClientConnectionManager getClientConnectionManager(HttpParams params, SchemeRegistry schemeRegistry) {
216 | return new ThreadSafeClientConnManager(params, schemeRegistry);
217 | }
218 |
219 | /**
220 | * Called to get the {@link SchemeRegistry} which should be provided to
221 | * the {@link ClientConnectionManager} for the {@link HttpClient}.
222 | * Subclasses may override this to provide a different registry.
223 | *
224 | * @see #getClientConnectionManager(HttpParams, SchemeRegistry)
225 | * @see #createClient(ClientConnectionManager, HttpParams)
226 | * @return The {@link SchemeRegistry} which should be provided to the
227 | * {@link ClientConnectionManager}.
228 | */
229 | protected SchemeRegistry getSchemeRegistry() {
230 | SchemeRegistry schemeRegistry = new SchemeRegistry();
231 | schemeRegistry.register(new Scheme("http", getHttpSocketFactory(), 80));
232 | schemeRegistry.register(new Scheme("https", getHttpsSocketFactory(), 443));
233 | return schemeRegistry;
234 | }
235 |
236 | @Override
237 | public HttpClient getClient() {
238 | if (client == null) {
239 | init();
240 | }
241 | return client;
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/webservicemanager/WebServiceManager.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.webservicemanager;
2 |
3 | import android.os.Process;
4 | import android.support.annotation.NonNull;
5 | import android.util.Log;
6 |
7 | import com.raizlabs.coreutils.concurrent.Prioritized;
8 | import com.raizlabs.coreutils.concurrent.Prioritized.Priority;
9 | import com.raizlabs.webservicemanager.Constants;
10 | import com.raizlabs.webservicemanager.HttpClientProvider;
11 | import com.raizlabs.webservicemanager.HttpMethod;
12 | import com.raizlabs.webservicemanager.RequestExecutionPool;
13 | import com.raizlabs.webservicemanager.requests.WebServiceRequest;
14 | import com.raizlabs.webservicemanager.requests.WebServiceRequest.CancelListener;
15 | import com.raizlabs.webservicemanager.ssl.SimpleSSLSocketFactory;
16 | import com.raizlabs.webservicemanager.ssl.TLS;
17 | import com.raizlabs.webservicemanager.ssl.TrustManager;
18 | import com.raizlabs.webservicemanager.ssl.TrustManagerFactory;
19 |
20 | import org.apache.http.HttpResponse;
21 | import org.apache.http.client.methods.HttpUriRequest;
22 |
23 | import java.io.IOException;
24 | import java.net.HttpURLConnection;
25 | import java.net.SocketTimeoutException;
26 | import java.util.Date;
27 | import java.util.concurrent.BlockingQueue;
28 | import java.util.concurrent.PriorityBlockingQueue;
29 | import java.util.concurrent.Semaphore;
30 | import java.util.concurrent.ThreadFactory;
31 | import java.util.concurrent.ThreadPoolExecutor;
32 | import java.util.concurrent.TimeUnit;
33 |
34 | import javax.net.ssl.HttpsURLConnection;
35 |
36 | /**
37 | * Class which executes requests and manages a set of maximum connections.
38 | *
39 | * @author Dylan James
40 | *
41 | */
42 | public class WebServiceManager {
43 | private static final int DEFAULT_MAX_CONNECTIONS = 5;
44 |
45 | private RequestExecutionPool requestQueue;
46 | /**
47 | * @return The {@link RequestExecutionPool} which is used for executing
48 | * {@link HttpUriRequest} ({@link RequestMode#HttpClient})
49 | */
50 | public RequestExecutionPool getRequestExectionQueue() { return requestQueue; }
51 | /**
52 | * Sets the {@link RequestExecutionPool} to be used to execute
53 | * {@link HttpUriRequest} ({@link RequestMode#HttpClient})
54 | * @param queue
55 | */
56 | public void setRequestExecutionQueue(RequestExecutionPool queue) {
57 | this.requestQueue = queue;
58 | onConnectionTimeoutChanged(getConnectionTimeout());
59 | onReadTimeoutChanged(getReadTimeout());
60 | }
61 |
62 | private Semaphore connectionSemaphore;
63 | private ThreadPoolExecutor backgroundPoolExecutor;
64 |
65 | private SimpleSSLSocketFactory sslSocketFactory;
66 |
67 | private int maxConnections;
68 | /**
69 | * @return The current maximum number of allowed connections.
70 | */
71 | public int getMaxConnection() { return maxConnections; }
72 | /**
73 | * Sets the maximum number of concurrent connections. This will return
74 | * immediately if increasing, but will block until adequate connections
75 | * have finished if decreasing. This method is synchronized, so
76 | * this may also block if another thread is waiting on a decrease.
77 | * @param maxConnections The new number of maximum concurrent connections.
78 | */
79 | public synchronized void setMaxConnections(int maxConnections) {
80 | if (connectionSemaphore == null) {
81 | connectionSemaphore = new Semaphore(maxConnections);
82 | } else {
83 | int deltaConnections = maxConnections - this.maxConnections;
84 | if (deltaConnections > 0) {
85 | connectionSemaphore.release(deltaConnections);
86 | } else if (deltaConnections < 0) {
87 | connectionSemaphore.acquireUninterruptibly(-deltaConnections);
88 | }
89 | }
90 |
91 | this.maxConnections = maxConnections;
92 | if (getRequestExectionQueue() != null && getRequestExectionQueue().getClientProvider() != null) {
93 | getRequestExectionQueue().getClientProvider().setMaxConnections(maxConnections);
94 | }
95 | if (backgroundPoolExecutor != null) {
96 | backgroundPoolExecutor.setMaximumPoolSize(maxConnections);
97 | backgroundPoolExecutor.setCorePoolSize(maxConnections);
98 | }
99 | }
100 |
101 |
102 | private int connectionTimeout;
103 | /**
104 | * @return The timeout for establishing a connection (in milliseconds)
105 | * @see #setConnectionTimeout(int)
106 | */
107 | public int getConnectionTimeout() { return connectionTimeout; }
108 | /**
109 | * Sets the timeout for establishing a connection. Setting this to zero
110 | * means a timeout is not used.
111 | * @param timeoutMillis The timeout value in milliseconds
112 | */
113 | public void setConnectionTimeout(int timeoutMillis) {
114 | connectionTimeout = timeoutMillis;
115 | onConnectionTimeoutChanged(timeoutMillis);
116 | }
117 |
118 | /**
119 | * Called when the connection timeout may have changed and may need to be
120 | * updated. This may be called more often than when it changes.
121 | * @param timeoutMillis The new connection timeout
122 | */
123 | protected void onConnectionTimeoutChanged(int timeoutMillis) {
124 | RequestExecutionPool requestPool = getRequestExectionQueue();
125 | if (requestPool != null) {
126 | HttpClientProvider clientProvider = requestPool.getClientProvider();
127 | if (clientProvider != null) clientProvider.setConnectionTimeout(timeoutMillis);
128 | }
129 | }
130 |
131 |
132 | private int readTimeout;
133 | /**
134 | * @return The timeout for reading data from the connection (in milliseconds)
135 | */
136 | public int getReadTimeout() { return readTimeout; }
137 | /**
138 | * Sets teh timeout for establishing a connection. Setting this to zero
139 | * means a timeout is not used.
140 | * @param timeoutMillis The timeout value in milliseconds.
141 | */
142 | public void setReadTimeout(int timeoutMillis) {
143 | readTimeout = timeoutMillis;
144 | onReadTimeoutChanged(timeoutMillis);
145 | }
146 |
147 | /**
148 | * Called when the read timeout may have changed and may need to be updated.
149 | * This may be called more often than when it changes.
150 | * @param timeoutMillis The new read timeout
151 | */
152 | protected void onReadTimeoutChanged(int timeoutMillis) {
153 | RequestExecutionPool requestPool = getRequestExectionQueue();
154 | if (requestPool != null) {
155 | HttpClientProvider clientProvider = requestPool.getClientProvider();
156 | if (clientProvider != null) clientProvider.setReadTimeout(timeoutMillis);
157 | }
158 | }
159 |
160 |
161 |
162 |
163 | private RequestMode defaultRequestMode;
164 | /**
165 | * Sets the {@link RequestMode} which will be used by default when
166 | * no method is specified.
167 | *
168 | * @see #doRequest(WebServiceRequest)
169 | * @param mode The default {@link RequestMode}.
170 | */
171 | public void setDefaultRequestMode(RequestMode mode) {
172 | this.defaultRequestMode = mode;
173 | }
174 |
175 | /**
176 | * Constructs a new {@link WebServiceManager} with default values.
177 | */
178 | public WebServiceManager() {
179 | this(new RequestExecutionPool(DEFAULT_MAX_CONNECTIONS));
180 | }
181 |
182 | /**
183 | * Constructs a new {@link WebServiceManager} with the given number
184 | * of maximum concurrent connections.
185 | * @param maxConnections The maximum number of concurrent connections.
186 | */
187 | public WebServiceManager(int maxConnections) {
188 | this(new RequestExecutionPool(maxConnections));
189 | init(maxConnections);
190 | }
191 |
192 | /**
193 | * Constructs a new {@link WebServiceManager} which uses the given
194 | * {@link RequestExecutionPool} for executing {@link HttpUriRequest}s.
195 | *
196 | * @see #setRequestExecutionQueue(RequestExecutionPool)
197 | * @param queue The {@link RequestExecutionPool} for executing
198 | * {@link HttpUriRequest}s.
199 | */
200 | public WebServiceManager(RequestExecutionPool queue) {
201 | setRequestExecutionQueue(queue);
202 | init(DEFAULT_MAX_CONNECTIONS);
203 | this.defaultRequestMode = RequestMode.HttpClient;
204 | }
205 |
206 | /**
207 | * Constructs a new {@link WebServiceManager} which only permits the
208 | * given number of maximum concurrent connections and uses the given
209 | * {@link RequestExecutionPool} for executing {@link HttpUriRequest}s.
210 | * @param maxConnections The maximum number of concurrent connections.
211 | * @param queue The {@link RequestExecutionPool} for executing
212 | * {@link HttpUriRequest}s.
213 | */
214 | public WebServiceManager(int maxConnections, RequestExecutionPool queue) {
215 | setRequestExecutionQueue(queue);
216 | init(maxConnections);
217 | queue.getClientProvider().setMaxConnections(maxConnections);
218 | this.defaultRequestMode = RequestMode.HttpClient;
219 | }
220 |
221 | private void init(int maxConnections) {
222 | backgroundPoolExecutor = createBackgroundThreadPool(maxConnections);
223 | setMaxConnections(maxConnections);
224 |
225 | setConnectionTimeout(Constants.Defaults.ConnectionTimeoutMillis);
226 | setReadTimeout(Constants.Defaults.ReadTimeoutMillis);
227 | }
228 |
229 | /**
230 | * Called to get the {@link ThreadPoolExecutor} to use to execute background
231 | * requests.
232 | * @param maxConnections The maximum number of connections allowed.
233 | * @return The {@link ThreadPoolExecutor} to use to execute background requests.
234 | */
235 | protected ThreadPoolExecutor createBackgroundThreadPool(int maxConnections) {
236 | final BlockingQueue queue = new PriorityBlockingQueue();
237 | // Keep 1 thread alive at all times, keep idle threads alive for 3 seconds
238 | ThreadPoolExecutor executor = new ThreadPoolExecutor(maxConnections, maxConnections, 3, TimeUnit.SECONDS, queue);
239 | executor.setThreadFactory(new ThreadFactory() {
240 | @Override
241 | public Thread newThread(final Runnable r) {
242 | return new Thread(new Runnable() {
243 | @Override
244 | public void run() {
245 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
246 | r.run();
247 | }
248 | });
249 | }
250 | });
251 | return executor;
252 | }
253 |
254 | private void beginConnection() {
255 | connectionSemaphore.acquireUninterruptibly();
256 | }
257 |
258 | private void endConnection() {
259 | connectionSemaphore.release();
260 | }
261 |
262 | /**
263 | * Sets the {@link TrustManager} to use to verify SSL connections. It'll use by default TLSv1.2
264 | * @see TrustManagerFactory
265 | * @param manager The manager to use to verify SSL connections.
266 | */
267 | public void setTrustManager(TrustManager manager) {
268 | setTrustManager(manager, TLS.VERSION_1_2);
269 | }
270 |
271 | /**
272 | * Sets the {@link TrustManager} to use to verify SSL connections.
273 | * @see TrustManagerFactory
274 | * @param manager The manager to use to verify SSL connections.
275 | * @param tls The TLS version to use
276 | */
277 | public void setTrustManager(TrustManager manager, @NonNull TLS tls) {
278 | // Default to the default trust manager if nothing else was given
279 | if (manager == null) {
280 | manager = TrustManagerFactory.getDefaultTrustManager(null);
281 | }
282 | // Set the SSL Socket Factory to use this manager
283 | if (sslSocketFactory == null) {
284 | sslSocketFactory = new SimpleSSLSocketFactory(manager, tls);
285 | } else {
286 | sslSocketFactory.setTrustManager(manager, tls);
287 | }
288 | // Bind the socket factory
289 | getRequestExectionQueue().getClientProvider().setHttpsSocketFactory(sslSocketFactory);
290 | HttpsURLConnection.setDefaultHostnameVerifier(manager);
291 | HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory.getSSLContext().getSocketFactory());
292 | }
293 |
294 | /**
295 | * Performs the given {@link WebServiceRequest} using the default request mode
296 | * and returns the result.
297 | *
298 | * @see #setDefaultRequestMode(RequestMode)
299 | * @param request The {@link WebServiceRequest} to execute.
300 | * @return The result of the request.
301 | */
302 | public ResultInfo doRequest(WebServiceRequest request) {
303 | return doRequest(request, null);
304 | }
305 |
306 | /**
307 | * Performs the given {@link WebServiceRequest} using the given {@link RequestMode}
308 | * and returns the result.
309 | * @param request The {@link WebServiceRequest} to execute.
310 | * @param mode The {@link RequestMode} to use, or null to use the default.
311 | * @return The result of the request.
312 | */
313 | public ResultInfo doRequest(WebServiceRequest request, RequestMode mode) {
314 | if (mode == null) {
315 | mode = defaultRequestMode;
316 | }
317 |
318 | switch(mode) {
319 | case HttpClient:
320 | return doRequestViaClient(request);
321 | case HttpURLConnection:
322 | return doRequestViaURLConnection(request);
323 | default:
324 | return null;
325 | }
326 | }
327 |
328 | /**
329 | * Performs the given {@link WebServiceRequest} using {@link RequestMode#HttpClient}
330 | * and the {@link RequestExecutionPool} and returns the result.
331 | * @param request The {@link WebServiceRequest} to execute.
332 | * @return The result.
333 | */
334 | public ResultInfo doRequestViaClient(final WebServiceRequest request) {
335 | HttpResponse response = null;
336 | final HttpUriRequest httpRequest = request.getHttpUriRequest();
337 |
338 | // If the request isn't cancelled, run it
339 | if (!request.isCancelled()) {
340 | // Wait for a connection to be available.
341 | beginConnection();
342 |
343 | boolean isCancelled = false;
344 | // Lock on the status lock so that we know the status won't change
345 | synchronized (request.getStatusLock()) {
346 | // Indicate whether the request has been cancelled
347 | isCancelled = request.isCancelled();
348 | if (!isCancelled) {
349 | // If it hasn't been cancelled, we're about to start it, so tell it
350 | request.onStart();
351 |
352 | // Listen for future cancels
353 | CancelListener cancelListener = new CancelListener() {
354 | @Override
355 | public void onCancel(WebServiceRequest request) {
356 | // If the request is cancelled, abort the request. This may be called
357 | // asynchronously
358 | requestQueue.abortRequest(httpRequest);
359 | // Remove this listener so we don't get called twice
360 | request.removeOnCancelListener(this);
361 | }
362 | };
363 | request.addOnCancelListener(cancelListener);
364 | }
365 | }
366 |
367 | // If the request wasn't cancelled, execute it
368 | if (!isCancelled) {
369 | try {
370 | // Execute the request
371 | response = requestQueue.doRequest(httpRequest);
372 | } finally {
373 | // Free the connection
374 | endConnection();
375 | }
376 | } else {
377 | // Free the connection
378 | endConnection();
379 | }
380 | }
381 |
382 | // Translate the response, or null if we didn't get one
383 | ResultType result = request.translateHTTPResponse(response, HttpMethod.fromName(httpRequest.getMethod()));
384 | BasicResultInfo resultInfo = new BasicResultInfo(result, new Date(), response);
385 | // If the request was cancelled, indicate it in the result info
386 | if (request.isCancelled()) {
387 | resultInfo.setCancelled(true);
388 | }
389 | // Return the result info
390 | return resultInfo;
391 | }
392 |
393 | /**
394 | * Performs the given {@link WebServiceRequest} using {@link RequestMode#HttpURLConnection}
395 | * and returns the result.
396 | * @param request The {@link WebServiceRequest} to execute.
397 | * @return The result.
398 | */
399 | public ResultInfo doRequestViaURLConnection(final WebServiceRequest request) {
400 | HttpURLConnection outerConnection = null;
401 | ResultInfo resultInfo = null;
402 |
403 | boolean needsRetry = false;
404 | try {
405 | // If the request hasn't been cancelled yet, start the connection
406 | if (!request.isCancelled()) {
407 | // Wait for a connection
408 | beginConnection();
409 |
410 | // Lock on the status lock so that we know the status won't change
411 | synchronized (request.getStatusLock()) {
412 | if (!request.isCancelled()) {
413 | // If it hasn't been cancelled, we're about to start it, so tell it
414 | request.onStart();
415 | }
416 | }
417 |
418 | // Get the connection from the request. This should not actually open
419 | // the connection, merely set it up.
420 | final HttpURLConnection connection = request.getUrlConnection();
421 | setupConnection(connection);
422 | connection.setConnectTimeout(getConnectionTimeout());
423 | connection.setReadTimeout(getReadTimeout());
424 | outerConnection = connection;
425 | // Double check the request is still not cancelled.
426 | if (!request.isCancelled()) {
427 | try {
428 | // Connect
429 | connection.connect();
430 | } catch (SocketTimeoutException e) {
431 | Log.e(WebServiceManager.class.getName(), "Socket timed out. Connection failed.");
432 | }
433 |
434 |
435 | boolean isCancelled = false;
436 | // Lock on the status lock so that we know the status won't change
437 | synchronized(request.getStatusLock()) {
438 | // Indicate whether the request has been cancelled
439 | isCancelled = request.isCancelled();
440 | if (!isCancelled) {
441 | // List for future cancels
442 | CancelListener cancelListener = new CancelListener() {
443 | @Override
444 | public void onCancel(WebServiceRequest request) {
445 | // If cancelled, disconnect the connection. From here on, the
446 | // connection may be dead.
447 | connection.disconnect();
448 | request.removeOnCancelListener(this);
449 | }
450 | };
451 | request.addOnCancelListener(cancelListener);
452 | }
453 | }
454 |
455 | // If the request wasn't cancelled, execute it
456 | if (!isCancelled) {
457 | ResultType result = null;
458 | try {
459 | // Try to allow the request to handle the connection
460 | request.onConnected(connection);
461 | // Try to translate the connection
462 | result = request.translateConnection(connection);
463 | } catch (Exception ex) {
464 | // We may hit errors if the connection is closed etc.
465 | Log.w(getClass().getName(), "Error executing request", ex);
466 | }
467 |
468 | resultInfo = new BasicResultInfo(result, new Date(), connection);
469 | }
470 | }
471 | }
472 | } catch (IOException e) {
473 | needsRetry = onURLConnectionException(request, e);
474 | Log.w(getClass().getName(), "Error in a URLConnection. Retry: " + needsRetry, e);
475 | } finally {
476 | // No matter what, disconnect the connection if we have one
477 | if (outerConnection != null) {
478 | tearDownConnection(outerConnection);
479 | outerConnection.disconnect();
480 | }
481 | // Release the connection
482 | endConnection();
483 | }
484 |
485 | if (needsRetry) {
486 | return doRequestViaURLConnection(request);
487 | }
488 |
489 | // If we never created a result, create a nulled on
490 | if (resultInfo == null) {
491 | resultInfo = new FailedResultInfo(new Date());
492 | }
493 |
494 | // If the request was cancelled, indicate it in the result info
495 | if (request.isCancelled()) {
496 | resultInfo.setCancelled(true);
497 | }
498 |
499 | return resultInfo;
500 | }
501 |
502 | /**
503 | * Called when an exception is caught in an {@link HttpURLConnection} request.
504 | * @param request The {@link WebServiceRequest} that caused the exception.
505 | * @param e The raised exception.
506 | * @return True to retry the request, false to fail immediately.
507 | */
508 | protected boolean onURLConnectionException(WebServiceRequest request, IOException e) {
509 | return false;
510 | }
511 |
512 | /**
513 | * Called to set up an {@link HttpURLConnection}. This is called right
514 | * before the connection is opened so any set up may be done.
515 | * @param connection The connection about to be opened
516 | */
517 | protected void setupConnection(HttpURLConnection connection) { }
518 | /**
519 | * Called to tear down an {@link HttpURLConnection}. This is called after
520 | * the data has been consumed and the connection is about to be closed.
521 | * This can be used to do any final reading of things such as cookies.
522 | * @param connection The connection about to be closed
523 | */
524 | protected void tearDownConnection(HttpURLConnection connection) { }
525 |
526 | /**
527 | * Performs the given {@link WebServiceRequest} on a background thread, with the normal priority,
528 | * calling the given {@link WebServiceRequestListener} when completed.
529 | * @param request The {@link WebServiceRequest} to execute.
530 | * @param listener The {@link WebServiceRequestListener} to call when the request completes. Optional.
531 | * predefined values.
532 | */
533 | public void doRequestInBackground(WebServiceRequest request, WebServiceRequestListener listener) {
534 | doRequestInBackground(request, listener, Priority.NORMAL);
535 | }
536 |
537 | /**
538 | * Performs the given {@link WebServiceRequest} on a background thread, with the given priority,
539 | * calling the given {@link WebServiceRequestListener} when completed.
540 | * @param request The {@link WebServiceRequest} to execute.
541 | * @param listener The {@link WebServiceRequestListener} to call when the request completes. Optional.
542 | * @param priority The priority to execute the request with. See {@link Priority} for
543 | * predefined values.
544 | */
545 | public void doRequestInBackground(
546 | WebServiceRequest request,
547 | WebServiceRequestListener listener,
548 | int priority) {
549 |
550 | doRequestInBackground(request, defaultRequestMode, listener, priority);
551 | }
552 |
553 | /**
554 | * Performs the given {@link WebServiceRequest} on a background thread, with the given priority,
555 | * calling the given {@link WebServiceRequestListener} when completed.
556 | * @param request The {@link WebServiceRequest} to execute.
557 | * @param mode The {@link RequestMode} to use to execute the request.
558 | * @param listener The {@link WebServiceRequestListener} to call when the request completes. Optional.
559 | * @param priority The priority to execute the request with. See {@link Priority} for
560 | * predefined values.
561 | */
562 | public void doRequestInBackground(
563 | WebServiceRequest request,
564 | RequestMode mode,
565 | WebServiceRequestListener listener,
566 | int priority) {
567 | backgroundPoolExecutor.execute(createRunnable(request, mode, listener, priority));
568 | }
569 |
570 | private Runnable createRunnable(
571 | final WebServiceRequest request,
572 | final RequestMode mode,
573 | final WebServiceRequestListener listener,
574 | int priority) {
575 |
576 | return new DownloadRunnable(priority) {
577 | @Override
578 | public void run() {
579 | Process.setThreadPriority(getPriority());
580 | ResultInfo result = WebServiceManager.this.doRequest(request, mode);
581 | if (listener != null) listener.onRequestComplete(WebServiceManager.this, result);
582 | }
583 | };
584 | }
585 |
586 | private static abstract class DownloadRunnable implements Comparable, Runnable, Prioritized {
587 |
588 | private int priority;
589 |
590 | public DownloadRunnable(int priority) {
591 | this.priority = priority;
592 | }
593 |
594 | @Override
595 | public int compareTo(DownloadRunnable another) {
596 | return Prioritized.COMPARATOR_HIGH_FIRST.compare(this, another);
597 | }
598 |
599 | @Override
600 | public int getPriority() {
601 | return priority;
602 | }
603 | }
604 | }
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/requests/RequestBuilder.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.requests;
2 |
3 | import android.util.Log;
4 |
5 | import com.raizlabs.coreutils.io.IOUtils;
6 | import com.raizlabs.coreutils.listeners.ProgressListener;
7 | import com.raizlabs.coreutils.logging.Logger;
8 | import com.raizlabs.webservicemanager.BuildConfig;
9 | import com.raizlabs.webservicemanager.HttpMethod;
10 | import com.raizlabs.webservicemanager.ProgressInputStreamEntity;
11 |
12 | import org.apache.http.Header;
13 | import org.apache.http.HttpEntityEnclosingRequest;
14 | import org.apache.http.NameValuePair;
15 | import org.apache.http.auth.AuthenticationException;
16 | import org.apache.http.auth.UsernamePasswordCredentials;
17 | import org.apache.http.client.entity.UrlEncodedFormEntity;
18 | import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
19 | import org.apache.http.client.methods.HttpRequestBase;
20 | import org.apache.http.client.methods.HttpUriRequest;
21 | import org.apache.http.impl.auth.BasicScheme;
22 | import org.apache.http.message.BasicNameValuePair;
23 |
24 | import java.io.File;
25 | import java.io.FileInputStream;
26 | import java.io.FileNotFoundException;
27 | import java.io.IOException;
28 | import java.io.InputStream;
29 | import java.io.OutputStream;
30 | import java.io.UnsupportedEncodingException;
31 | import java.net.HttpURLConnection;
32 | import java.net.MalformedURLException;
33 | import java.net.URI;
34 | import java.net.URL;
35 | import java.net.URLEncoder;
36 | import java.nio.charset.Charset;
37 | import java.util.Collection;
38 | import java.util.LinkedHashMap;
39 | import java.util.LinkedList;
40 | import java.util.List;
41 | import java.util.Map;
42 | import java.util.Map.Entry;
43 |
44 | /**
45 | * Builder class which allows for the construction of a request. This class
46 | * abstracts the implementation and can build either an {@link HttpURLConnection}
47 | * or an {@link HttpUriRequest} to represent and execute the given request.
48 | *
49 | * @author Dylan James
50 | *
51 | */
52 | public class RequestBuilder {
53 |
54 | protected static class ParamLocation {
55 | private static final int AUTO = 0;
56 | private static final int URL = 10;
57 | private static final int BODY = 20;
58 | }
59 |
60 | protected URI uri;
61 | protected HttpMethod method;
62 | protected LinkedHashMap params;
63 | protected LinkedHashMap forcedBodyParams;
64 | protected LinkedHashMap headers;
65 | protected UsernamePasswordCredentials basicAuthCredentials;
66 | protected int paramLocation = ParamLocation.AUTO;
67 |
68 | /**
69 | * Constructs a {@link RequestBuilder} using the given {@link HttpMethod}
70 | * and pointing to the given url.
71 | * @param method The {@link HttpMethod} to use for the request.
72 | * @param url The url the request targets.
73 | */
74 | public RequestBuilder(HttpMethod method, String url) {
75 | this(method, URI.create(url));
76 | }
77 |
78 | /**
79 | * Constructs a {@link RequestBuilder} using the given {@link HttpMethod}
80 | * and pointing to the given {@link URI}.
81 | * @param method The {@link HttpMethod} to use for the request.
82 | * @param uri The {@link URI} the request targets.
83 | */
84 | public RequestBuilder(HttpMethod method, URI uri) {
85 | this.method = method;
86 | this.uri = uri;
87 | this.params = new LinkedHashMap();
88 | this.forcedBodyParams = new LinkedHashMap();
89 | this.headers = new LinkedHashMap();
90 | }
91 |
92 |
93 | /**
94 | * Sets the target URL for this {@link RequestBuilder}.
95 | * @param url The url the request should target.
96 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
97 | */
98 | public RequestBuilder setURL(String url) {
99 | return setURI(URI.create(url));
100 | }
101 |
102 | /**
103 | * Sets the target {@link URI} for this {@link RequestBuilder}.
104 | * @param uri the {@link URI} the request targets.
105 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
106 | */
107 | public RequestBuilder setURI(URI uri) {
108 | this.uri = uri;
109 | return this;
110 | }
111 |
112 | /**
113 | * Adds a parameter to this request.
114 | * @param key The parameter key.
115 | * @param value The parameter value.
116 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
117 | */
118 | public RequestBuilder addParam(String key, String value) {
119 | params.put(key, value);
120 | return this;
121 | }
122 |
123 | /**
124 | * Adds a parameter to this request.
125 | * @param key The parameter key.
126 | * @param value The parameter value.
127 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
128 | */
129 | public RequestBuilder addParam(String key, int value) {
130 | params.put(key, Integer.toString(value));
131 | return this;
132 | }
133 |
134 | /**
135 | * Adds a parameter to this request.
136 | * @param key The parameter key.
137 | * @param value The parameter value.
138 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
139 | */
140 | public RequestBuilder addParam(String key, long value) {
141 | params.put(key, Long.toString(value));
142 | return this;
143 | }
144 |
145 | /**
146 | * Adds a parameter to this request.
147 | * @param key The parameter key.
148 | * @param value The parameter value.
149 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
150 | */
151 | public RequestBuilder addParam(String key, boolean value) {
152 | params.put(key, Boolean.toString(value));
153 | return this;
154 | }
155 |
156 | /**
157 | * Adds a parameter to the request if the value is not null. Note that this method
158 | * will still add an empty string value to the request.
159 | * @param key The parameter key.
160 | * @param value The parameter value.
161 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
162 | */
163 | public RequestBuilder addParamIfNotNull(String key, String value) {
164 | if (value != null) {
165 | addParam(key, value);
166 | }
167 | return this;
168 | }
169 |
170 | /**
171 | * Adds a {@link Collection} of {@link NameValuePair} as parameters to this
172 | * request. Parameters are added in iteration order.
173 | * @param params The collection of {@link NameValuePair} to add.
174 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
175 | */
176 | public RequestBuilder addParams(Collection params) {
177 | putEntries(params, this.params);
178 | return this;
179 | }
180 |
181 | /**
182 | * Adds a {@link Map} of parameter key value pairs as parameters of this
183 | * request. Parameters are added in iteration order. Params added through this
184 | * method will adhere to settings {@link #setSendParamsInBody()} or
185 | * {@link #setSendParamsInURL()}. If you would like to force params to be
186 | * sent in the body, use {@link #addParamToBodyForced(String, String)}.
187 | * @param params The {@link Map} of parameters.
188 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
189 | */
190 | public RequestBuilder addParams(Map params) {
191 | putEntries(params, this.params);
192 | return this;
193 | }
194 |
195 | /**
196 | * Adds a parameter to this request that will be sent only as part of
197 | * the request's body.
198 | * @param key The parameter key.
199 | * @param value The parameter value.
200 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
201 | */
202 | public RequestBuilder addParamToBodyForced(String key, String value) {
203 | forcedBodyParams.put(key, value);
204 | return this;
205 | }
206 |
207 | /**
208 | * Adds a parameter to this request that will be sent only as part of
209 | * the request's body.
210 | * @param key The parameter key.
211 | * @param value The parameter value.
212 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
213 | */
214 | public RequestBuilder addParamToBodyForced(String key, int value) {
215 | forcedBodyParams.put(key, Integer.toString(value));
216 | return this;
217 | }
218 |
219 | /**
220 | * Adds a parameter to this request that will be sent only as part of
221 | * the request's body.
222 | * @param key The parameter key.
223 | * @param value The parameter value.
224 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
225 | */
226 | public RequestBuilder addParamToBodyForced(String key, long value) {
227 | forcedBodyParams.put(key, Long.toString(value));
228 | return this;
229 | }
230 |
231 | /**
232 | * Adds a parameter to this request that will be sent only as part of
233 | * the request's body.
234 | * @param key The parameter key.
235 | * @param value The parameter value.
236 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
237 | */
238 | public RequestBuilder addParamToBodyForced(String key, boolean value) {
239 | forcedBodyParams.put(key, Boolean.toString(value));
240 | return this;
241 | }
242 |
243 | /**
244 | * Adds a parameter to this request that will be sent only as part of
245 | * the request's body. This is added only if the value is not null. See also:
246 | * {@link #addParamToBodyForced(String, String)}.
247 | * @param key The parameter key.
248 | * @param value The parameter value.
249 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
250 | */
251 | public RequestBuilder addParamToBodyForcedIfNotNull(String key, String value) {
252 | if (value != null) {
253 | addParamToBodyForced(key, value);
254 | }
255 | return this;
256 | }
257 |
258 | /**
259 | * Adds a header to this request with the given name and value.
260 | * @param name The name of the header.
261 | * @param value The value for the header.
262 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
263 | */
264 | public RequestBuilder addHeader(String name, String value) {
265 | headers.put(name, value);
266 | return this;
267 | }
268 |
269 | /**
270 | * Adds a {@link Collection} of {@link NameValuePair} of headers to
271 | * add to this request. Headers are added in iteration order.
272 | * @param headers The {@link Collection} of parameters to add.
273 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
274 | */
275 | public RequestBuilder addHeaders(Collection headers) {
276 | putEntries(headers, this.headers);
277 | return this;
278 | }
279 |
280 | /**
281 | * Adds a {@link Map} of key value pairs as headers to this request.
282 | * Headers are added in iteration order.
283 | * @param headers The {@link Map} of header key value pairs to add.
284 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
285 | */
286 | public RequestBuilder addHeaders(Map headers) {
287 | putEntries(headers, this.headers);
288 | return this;
289 | }
290 |
291 | /**
292 | * Adds basic authentication to this request.
293 | * @param user The username to use for basic auth.
294 | * @param pass The password to use for basic auth.
295 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
296 | */
297 | public RequestBuilder setBasicAuth(String user, String pass) {
298 | basicAuthCredentials = new UsernamePasswordCredentials(user, pass);
299 | return this;
300 | }
301 |
302 |
303 | protected InputStream inputStream;
304 | protected long inputStreamLength;
305 | protected ProgressListener inputStreamProgressListener;
306 | protected int inputStreamProgressUpdateInterval = 128;
307 | /**
308 | * Sets the interval for which input stream progress updates will be sent.
309 | * Setting this low will result in more frequent updates which will slightly
310 | * reduce performance, while setting this high may result in too infrequent updates.
311 | * @param interval The interval, in bytes.
312 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
313 | */
314 | public RequestBuilder setInputStreamProgressUpdateInterval(int interval) {
315 | this.inputStreamProgressUpdateInterval = interval;
316 | return this;
317 | }
318 |
319 | /**
320 | * Sets the {@link InputStream} to be used as the body of the request.
321 | *
322 | * @see #setInputStreamProgressUpdateInterval(int)
323 | * @see #setFileInput(File, ProgressListener)
324 | * @param input The {@link InputStream} to write into the body.
325 | * @param length The length of the {@link InputStream}. May send negative
326 | * if unknown, though the actual request may not support this.
327 | * @param progressListener The {@link ProgressListener} which will be called
328 | * periodically to be notified of the progress.
329 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
330 | */
331 | public RequestBuilder setInputStream(InputStream input, long length, ProgressListener progressListener) {
332 | this.inputStream = input;
333 | this.inputStreamLength = length;
334 | this.inputStreamProgressListener = progressListener;
335 | return this;
336 | }
337 |
338 | /**
339 | * Sets a {@link File} to be used as the body of the request.
340 | *
341 | * @see #setInputStreamProgressUpdateInterval(int)
342 | * @see #setInputStream(InputStream, long, ProgressListener)
343 | * @param file The {@link File} to set as the body.
344 | * @param progressListener The {@link ProgressListener} which will be called
345 | * periodically to be notified of the progress.
346 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
347 | */
348 | public RequestBuilder setFileInput(File file, ProgressListener progressListener) {
349 | if (file.exists()){
350 | try {
351 | setInputStream(new FileInputStream(file), file.length(), progressListener);
352 | } catch (FileNotFoundException e) {
353 | if (BuildConfig.DEBUG) {
354 | Log.e(getClass().getName(), e.getMessage(), e);
355 | }
356 | }
357 | }
358 |
359 | return this;
360 | }
361 |
362 | /**
363 | * Sets a string to be used as the body of the request. This will overwrite
364 | * any existing input.
365 | * @param string The string to write as the body.
366 | * @param progressListener The {@link ProgressListener} which will be called
367 | * periodically to be notified of the progress.
368 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
369 | */
370 | public RequestBuilder setStringInput(String string, ProgressListener progressListener) {
371 | setInputStream(IOUtils.getInputStream(string), string.getBytes().length, progressListener);
372 |
373 | return this;
374 | }
375 |
376 | /**
377 | * Resolves where parameters should be sent and returns the value. This
378 | * will resolve automatic detection and return the final endpoint instead
379 | * of {@link ParamLocation#AUTO}
380 | * @return One of the values defined in {@link ParamLocation} where params
381 | * should be sent
382 | */
383 | protected int getParamLocationResolved() {
384 | if (paramLocation == ParamLocation.AUTO) {
385 | if (method == HttpMethod.Post) {
386 | return ParamLocation.BODY;
387 | } else {
388 | return ParamLocation.URL;
389 | }
390 | } else {
391 | return paramLocation;
392 | }
393 | }
394 |
395 | /**
396 | * Causes the params set by addParam calls to be sent in the URL of this
397 | * request.
398 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
399 | */
400 | public RequestBuilder setSendParamsInURL() {
401 | paramLocation = ParamLocation.URL;
402 | return this;
403 | }
404 |
405 | /**
406 | * Causes the params set by addParam calls to be sent in the body of this
407 | * request.
408 | * @return This {@link RequestBuilder} object to allow for chaining of calls.
409 | */
410 | public RequestBuilder setSendParamsInBody() {
411 | paramLocation = ParamLocation.BODY;
412 | return this;
413 | }
414 |
415 |
416 | private void putEntries(Collection entries, Map map) {
417 | for (NameValuePair entry : entries) {
418 | map.put(entry.getName(), entry.getValue());
419 | }
420 | }
421 |
422 | private void putEntries(Map entries, Map map) {
423 | for (Entry entry : entries.entrySet()) {
424 | map.put(entry.getKey(), entry.getValue());
425 | }
426 | }
427 |
428 | private void writeToStream(OutputStream out) throws IOException {
429 | byte[] buffer = new byte[1024];
430 | long totalRead = 0;
431 | long lastUpdate = 0;
432 | int read;
433 | try {
434 | // Loop through the whole stream and write the data
435 | while ((read = inputStream.read(buffer)) != -1) {
436 | out.write(buffer, 0, read);
437 | out.flush();
438 | totalRead += read;
439 | if (inputStreamProgressListener != null) {
440 | // If we have a listener, and we've read more than the given interval
441 | // since the last update, notify the listener
442 | if (totalRead - lastUpdate >= inputStreamProgressUpdateInterval) {
443 | inputStreamProgressListener.onProgressUpdate(totalRead, inputStreamLength);
444 | lastUpdate = totalRead;
445 | }
446 | }
447 | }
448 | } finally {
449 | // Reset the input stream just in case it is used again
450 | // We are closing it anyway, but resetting just to be safe.
451 | try {
452 | inputStream.reset();
453 | } catch (IOException e) {
454 | // Not a huge deal...
455 | Log.i(getClass().getName(), "Failed to reset input stream after use.", e);
456 | }
457 |
458 | IOUtils.safeClose(inputStream);
459 | IOUtils.safeClose(out);
460 | }
461 | }
462 |
463 |
464 | private List getNameValuePairs(Map map) {
465 | List pairs = new LinkedList();
466 | for (Entry entry : map.entrySet()) {
467 | pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
468 | }
469 | return pairs;
470 | }
471 |
472 | @SuppressWarnings("deprecation")
473 | private String getQueryString(Map map) {
474 | StringBuilder queryBuilder = new StringBuilder();
475 | boolean first = true;
476 | for (Entry entry : map.entrySet()) {
477 | // This will throw a NullPointerException if you call URLEncoder.encode(null).
478 | // Instead caught & thrown with description above.
479 | String value = entry.getValue();
480 | if (value == null) {
481 | // Can't be more specific without jeopardizing security.
482 | throw new NullPointerException("Malformed Request. RequestBuilder entry " +
483 | "has null value for key "+entry.getKey()+" on URI "+this.uri+".");
484 | }
485 |
486 | if (!first) {
487 | queryBuilder.append("&");
488 | }
489 | queryBuilder.append(entry.getKey());
490 | queryBuilder.append("=");
491 | try {
492 | queryBuilder.append(URLEncoder.encode(value, "UTF-8"));
493 | } catch (UnsupportedEncodingException e) {
494 | queryBuilder.append(URLEncoder.encode(value));
495 | }
496 |
497 | first = false;
498 |
499 | }
500 |
501 | return queryBuilder.toString();
502 | }
503 |
504 | /**
505 | * Gets the URL that this {@link RequestBuilder} points to.
506 | * @return The URL the {@link RequestBuilder} is pointing to.
507 | */
508 | protected String getUrl() {
509 | String url = uri.toString();
510 |
511 | // If we should set params in the url and we have params to set, do so
512 | if ((getParamLocationResolved() == ParamLocation.URL) && (params.size() > 0)) {
513 | String queryString = "?" + getQueryString(params);
514 | url = String.format("%s%s", uri, queryString);
515 | }
516 |
517 | return url;
518 | }
519 |
520 | /**
521 | * Gets a {@link HttpURLConnection} which can be used to execute this request.
522 | * @return
523 | */
524 | public HttpURLConnection getConnection() {
525 | try {
526 | // Get our current URL
527 | URL url = new URL(getUrl());
528 | // "Open" the connection, which really just gives us the object, doesn't
529 | // actually connect
530 | HttpURLConnection connection = (HttpURLConnection)url.openConnection();
531 | // Set the request method appropriately
532 | connection.setRequestMethod(method.getMethodName());
533 |
534 | // Add all headers
535 | for (Entry entry : headers.entrySet()) {
536 | connection.setRequestProperty(entry.getKey(), entry.getValue());
537 | }
538 |
539 | // Add any basic auth
540 | if (basicAuthCredentials != null) {
541 | Header authHeader = BasicScheme.authenticate(basicAuthCredentials, Charset.defaultCharset().name(), false);
542 | connection.setRequestProperty(authHeader.getName(), authHeader.getValue());
543 | }
544 |
545 | // If we have params and this is a post, we need to do output
546 | // but they will be written later
547 | if ((params.size() > 0 && (getParamLocationResolved() == ParamLocation.BODY)) || (forcedBodyParams.size() > 0)) {
548 | connection.setDoOutput(true);
549 | }
550 |
551 | // If we have an input stream, set the content length and indicate
552 | // that we will be doing output
553 | if (inputStream != null) {
554 | connection.setRequestProperty("Content-Length", Long.toString(inputStreamLength));
555 | connection.setDoOutput(true);
556 | // Try to set the chunked size, but this doesn't work in HttpsUrlConnections
557 | // due to bugs in Android URLConnection logic. Supposedly fixed in 4.1
558 | connection.setChunkedStreamingMode(8192);
559 | }
560 |
561 | return connection;
562 |
563 | } catch (MalformedURLException e) {
564 | if (BuildConfig.DEBUG) {
565 | Log.e(getClass().getName(), e.getMessage(), e);
566 | }
567 | } catch (IOException e) {
568 | if (BuildConfig.DEBUG) {
569 | Log.e(getClass().getName(), e.getMessage(), e);
570 | }
571 | }
572 |
573 | return null;
574 | }
575 |
576 | /**
577 | * Called once the connection has been established. Should be called after
578 | * {@link #getConnection()} to allow adding the rest of the data.
579 | * @param connection The opened connection
580 | */
581 | public void onConnected(HttpURLConnection connection) {
582 | // If we have params and this is a put, we need to write them here
583 | boolean shouldAddNormalParams = (params.size() > 0 && (getParamLocationResolved() == ParamLocation.BODY));
584 | boolean shouldAddForcedBodyParams = (forcedBodyParams.size() > 0);
585 | LinkedHashMap bodyParams = null;
586 |
587 | if (shouldAddNormalParams && shouldAddForcedBodyParams) {
588 | bodyParams = new LinkedHashMap();
589 | bodyParams.putAll(params);
590 | bodyParams.putAll(forcedBodyParams);
591 | } else if (shouldAddNormalParams) {
592 | bodyParams = params;
593 | } else if (shouldAddForcedBodyParams) {
594 | bodyParams = forcedBodyParams;
595 | }
596 |
597 | if (bodyParams != null) {
598 | // Convert the params to a query string, and write it to the body.
599 | String query = getQueryString(bodyParams);
600 | try {
601 | connection.getOutputStream().write(query.getBytes());
602 | } catch (IOException e) {
603 | if (BuildConfig.DEBUG) {
604 | Log.e(getClass().getName(), e.getMessage(), e);
605 | }
606 | }
607 | }
608 |
609 | // If we have an input stream, we need to write it to the body
610 | if (inputStream != null) {
611 | try {
612 | OutputStream out = connection.getOutputStream();
613 | writeToStream(out);
614 | } catch (IOException e) {
615 | if (BuildConfig.DEBUG) {
616 | Log.e(getClass().getName(), e.getMessage(), e);
617 | }
618 | }
619 | }
620 | }
621 |
622 | /**
623 | * Gets an {@link HttpUriRequest} which can be used to execute this request.
624 | * @return
625 | */
626 | public HttpUriRequest getRequest() {
627 | // Get the base request from the current method
628 | HttpRequestBase request = method.createRequest();
629 | // Set the uri to our url
630 | request.setURI(URI.create(getUrl()));
631 |
632 | // Add all headers
633 | for (Entry entry : headers.entrySet()) {
634 | request.addHeader(entry.getKey(), entry.getValue());
635 | }
636 |
637 | // Add any basic auth
638 | if (basicAuthCredentials != null) {
639 | try {
640 | request.addHeader(new BasicScheme().authenticate(basicAuthCredentials, request));
641 | } catch (AuthenticationException e) {
642 | Logger.e(getClass().getName(), "Basic authentication on request threw Authentication Exception: " + e.getMessage());
643 | return null;
644 | }
645 | }
646 |
647 | // If we have parameters and this is a post, we need to add
648 | // the parameters to the body
649 | if (params.size() > 0 && (getParamLocationResolved() == ParamLocation.BODY)) {
650 | try {
651 | ((HttpEntityEnclosingRequest)request).setEntity(new UrlEncodedFormEntity(getNameValuePairs(params)));
652 | } catch (UnsupportedEncodingException e) {
653 | throw new RuntimeException(e);
654 | }
655 | if (inputStream != null && request instanceof HttpEntityEnclosingRequestBase) {
656 | Logger.w(getClass().getName(), "Both post params and an input stream were declared in a request. The params will be overwritten.");
657 | }
658 | }
659 |
660 | // If we have an input stream and this request supports an entity, add it.
661 | if (inputStream != null && request instanceof HttpEntityEnclosingRequestBase) {
662 | // Use a progress input stream entity which notifies the progress listener
663 | ProgressInputStreamEntity entity =
664 | new ProgressInputStreamEntity(inputStream, inputStreamLength,
665 | inputStreamProgressListener, inputStreamProgressUpdateInterval);
666 | ((HttpEntityEnclosingRequestBase)request).setEntity(entity);
667 | }
668 |
669 | return request;
670 | }
671 | }
672 |
--------------------------------------------------------------------------------
/WebServiceManager/src/main/java/com/raizlabs/webservicemanager/caching/WebFileCache.java:
--------------------------------------------------------------------------------
1 | package com.raizlabs.webservicemanager.caching;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.content.SharedPreferences;
6 | import android.content.SharedPreferences.Editor;
7 | import android.os.Build;
8 | import android.os.Handler;
9 | import android.os.HandlerThread;
10 |
11 | import com.raizlabs.coreutils.collections.MappableSet;
12 | import com.raizlabs.coreutils.concurrent.ConcurrencyUtils;
13 | import com.raizlabs.coreutils.concurrent.Prioritized.Priority;
14 | import com.raizlabs.coreutils.functions.Delegate;
15 | import com.raizlabs.webservicemanager.requests.RequestBuilder;
16 | import com.raizlabs.webservicemanager.requests.WebServiceRequest;
17 | import com.raizlabs.webservicemanager.webservicemanager.ResultInfo;
18 | import com.raizlabs.webservicemanager.webservicemanager.WebServiceManager;
19 | import com.raizlabs.webservicemanager.webservicemanager.WebServiceRequestListener;
20 |
21 | import java.io.File;
22 | import java.util.HashMap;
23 | import java.util.HashSet;
24 | import java.util.Map;
25 | import java.util.Map.Entry;
26 | import java.util.Set;
27 | import java.util.concurrent.ConcurrentHashMap;
28 |
29 | /**
30 | * A class which caches the results of {@link WebServiceRequest}s into local {@link File}s.
31 | * Once a request has been executed, subsequent calls will reuse the data stored in the
32 | * {@link File} again instead of re-performing the request if it is still considered
33 | * to be fresh data.
34 | *
35 | * @author Dylan James
36 | *
37 | * @param The type of object that will be used as the key to distinguish individual
38 | * requests.
39 | */
40 | public abstract class WebFileCache {
41 | /**
42 | * Class which contains the result of an asynchronous {@link WebFileCache} request. *
43 | */
44 | public interface WebFileCacheResult {
45 | /**
46 | * @return True if the request has completed
47 | */
48 | public boolean isCompleted();
49 | /**
50 | * Cancels this request
51 | */
52 | public void cancel();
53 | /**
54 | * @return True if the request has started executing.
55 | */
56 | public boolean isStarted();
57 |
58 | public void addCacheListener(CacheListener listener);
59 | public boolean removeCacheListener(CacheListener listener);
60 | }
61 |
62 | public interface CacheResult {
63 | public File getResultFile();
64 | }
65 |
66 | private static class CacheResultImplementation implements CacheResult {
67 | private File resultFile;
68 |
69 | public CacheResultImplementation(File resultFile) {
70 | this.resultFile = resultFile;
71 | }
72 |
73 | @Override
74 | public File getResultFile() {
75 | return resultFile;
76 | }
77 | }
78 |
79 | public interface FailureInfo {
80 |
81 | }
82 |
83 | private class FailureInfoImp implements FailureInfo {
84 |
85 | }
86 |
87 | public interface CacheListener {
88 | public void onCacheResult(CacheResult result);
89 | public void onCacheFailure(FailureInfo info);
90 | }
91 |
92 | private static class CacheListenerSet extends MappableSet {
93 | public void onResult(final CacheResult result) {
94 | map(new Delegate() {
95 | @Override
96 | public void execute(CacheListener listener) {
97 | listener.onCacheResult(result);
98 | }
99 | });
100 | }
101 |
102 | public void onFailure(final FailureInfo info) {
103 | map (new Delegate() {
104 | @Override
105 | public void execute(CacheListener listener) {
106 | listener.onCacheFailure(info);
107 | }
108 | });
109 | }
110 | }
111 |
112 | private static class BasicWebFileCacheResult implements WebFileCacheResult {
113 | boolean isCompleted;
114 | WebServiceRequest request;
115 | CacheListenerSet listeners;
116 |
117 | public BasicWebFileCacheResult() {
118 | this.isCompleted = false;
119 | this.listeners = new CacheListenerSet();
120 | }
121 |
122 | void setCompleted(boolean completed) {
123 | synchronized (this) {
124 | this.isCompleted = completed;
125 | }
126 | }
127 |
128 | void onCompleted(CacheResult result) {
129 | synchronized (this) {
130 | setCompleted(true);
131 | listeners.onResult(result);
132 | }
133 | }
134 |
135 | void onFailed(FailureInfo info) {
136 | synchronized (this) {
137 | setCompleted(true);
138 | listeners.onFailure(info);
139 | }
140 | }
141 |
142 | public boolean isCompleted() {
143 | synchronized (this) {
144 | return isCompleted;
145 | }
146 | }
147 |
148 | @Override
149 | public void cancel() {
150 | if (request != null) {
151 | request.cancel();
152 | }
153 | }
154 |
155 | @Override
156 | public boolean isStarted() {
157 | if (request != null) {
158 | return request.isStarted();
159 | } else {
160 | return false;
161 | }
162 | }
163 |
164 | @Override
165 | public void addCacheListener(CacheListener listener) {
166 | if (listener != null) {
167 | this.listeners.add(listener);
168 | }
169 | }
170 |
171 | @Override
172 | public boolean removeCacheListener(CacheListener listener) {
173 | return this.listeners.remove(listener);
174 | }
175 | }
176 |
177 | /**
178 | * Class which manages a set of locks for keys of a given type.
179 | * @param The type of key which locks will be mapped to.
180 | */
181 | private static class LockManager {
182 | private HashMap locks;
183 |
184 | public LockManager() {
185 | locks = new HashMap();
186 | }
187 |
188 | public Object getLockForKey(KeyType key) {
189 | Object lock = locks.get(key);
190 | if (lock == null) {
191 | synchronized (this) {
192 | lock = locks.get(key);
193 | if (lock == null) {
194 | lock = new Object();
195 | locks.put(key, lock);
196 | }
197 | }
198 | }
199 |
200 | return lock;
201 | }
202 | }
203 |
204 |
205 | /**
206 | * Map of Events for download completion, containing all listeners.
207 | */
208 | private ConcurrentHashMap cacheListeners;
209 | /**
210 | * Set of keys which are currently downloading.
211 | */
212 | private HashSet currentDownloads;
213 |
214 | private LockManager lockManager;
215 |
216 | private CompletedDownloadManager completedDownloads;
217 | private WebServiceManager webServiceManager;
218 |
219 | private Handler backgroundHandler;
220 | /**
221 | * @return A {@link Handler} which can be used to do background work
222 | */
223 | Handler getBackgroundHandler() { return backgroundHandler; }
224 |
225 | /**
226 | * Constructs a new {@link WebFileCache} with the given parameters.
227 | * @param name The name of the {@link WebFileCache}. This should be unique
228 | * across the application to avoid collisions.
229 | * @param webManager The {@link WebServiceManager} to use to perform web requests.
230 | * @param context A {@link Context} to use to access resources. This is only used
231 | * for initialization and will not be stored.
232 | */
233 | public WebFileCache(String name, WebServiceManager webManager, Context context) {
234 | this.webServiceManager = webManager;
235 | cacheListeners = new ConcurrentHashMap();
236 | currentDownloads = new HashSet();
237 |
238 | HandlerThread handlerThread = new HandlerThread("WebFileCache(" + name + ") Background");
239 | handlerThread.start();
240 | backgroundHandler = new Handler(handlerThread.getLooper());
241 |
242 | completedDownloads = new CompletedDownloadManager(name, context, backgroundHandler);
243 |
244 | lockManager = new LockManager();
245 | }
246 |
247 | /**
248 | * Gets the lock which should be used for the synchronization of the status
249 | * of the given key.
250 | * @param key The key to get the lock for.
251 | * @return The lock to use for the status of the key.
252 | */
253 | protected Object getLockForKey(Key key) {
254 | return lockManager.getLockForKey(key);
255 | }
256 |
257 | /**
258 | * Retrieves the file for the given request immediately if it is already cached.
259 | * @param request The {@link RequestBuilder} to obtain the file for.
260 | * @param freshOnly True if the data should only be returned if it is considered
261 | * fresh. False to ignore the age.
262 | * @return The local file if the request is already cached locally, otherwise null.
263 | */
264 | public File getFileIfCached(RequestBuilder request, boolean freshOnly) {
265 | // Key the files by the URI
266 | final Key key = getKeyForRequest(request);
267 | // Get the local file we will find or store the file at
268 | final File localFile = getFileForKey(key);
269 |
270 | synchronized (getLockForKey(key)) {
271 | if (isDownloaded(localFile)) {
272 | if (freshOnly) {
273 | if (!isFresh(request, localFile)) {
274 | return null;
275 | }
276 | }
277 | return localFile;
278 | }
279 | }
280 | return null;
281 | }
282 |
283 |
284 | ///////////////
285 | // Retrieval //
286 | ///////////////
287 | /**
288 | * Retrieves the file for the given request with normal priority, calling the given listener when it is
289 | * retrieved. If the file is already in the cache, the listener will be called before this function returns
290 | * on the same thread, otherwise it will be retrieved asynchronously.
291 | * @param request The {@link RequestBuilder} to execute to get the file.
292 | * @param cacheListener A listener to call when the file is retrieved. This listener will be
293 | * automatically added to the returned {@link WebFileCacheResult}.
294 | * @return An {@link WebFileCacheResult} object which provides access to the request status and result.
295 | */
296 | public WebFileCacheResult getFile(final RequestBuilder request, CacheListener cacheListener) {
297 | return getFile(request, cacheListener, Priority.NORMAL);
298 | }
299 |
300 | /**
301 | * Retrieves the file for the given request with given priority, calling the given listener when it is
302 | * retrieved. If the file is already in the cache, the listener will be called before this function
303 | * returns on the same thread, otherwise it will be retrieved asynchronously.
304 | * @param request The {@link RequestBuilder} to execute to get the file.
305 | * @param cacheListener A listener to call when the file is retrieved. This listener will be
306 | * automatically added to the returned {@link WebFileCacheResult}.
307 | * @param priority The priority of the download. See {@link Priority} for pre-defined values.
308 | * @return An {@link WebFileCacheResult} object which provides access to the request status and result.
309 | */
310 | public WebFileCacheResult getFile(final RequestBuilder request, CacheListener cacheListener, int priority) {
311 | return getFile(request, cacheListener, false, priority);
312 | }
313 |
314 | /**
315 | * Retrieves the file for the given request with normal priority, calling the given listener when it is
316 | * retrieved. If the file is already in the cache, the listener will be called before this function
317 | * returns on the same thread, otherwise it will be retrieved asynchronously.
318 | * @param request The {@link RequestBuilder} to execute to get the file.
319 | * cacheListener
320 | * @param forceDownload True to force the download, even if it is already cached.
321 | * @return An {@link WebFileCacheResult} object which provides access to the request status and result.
322 | */
323 | public WebFileCacheResult getFile(final RequestBuilder request, CacheListener cacheListener, boolean forceDownload) {
324 | return getFile(request, cacheListener, forceDownload, Priority.NORMAL);
325 | }
326 |
327 |
328 | /**
329 | * Retrieves the file for the given request with given priority, calling the given listener when it is
330 | * retrieved. If the file is already in the cache, the listener will be called before this function
331 | * returns on the same thread, otherwise it will be retrieved asynchronously.
332 | * @param request The {@link RequestBuilder} to execute to get the file.
333 | * cacheListener
334 | * @param forceDownload True to force the download, even if it is already cached.
335 | * @param priority The priority of the download. See {@link Priority} for pre-defined values.
336 | * @return An {@link WebFileCacheResult} object which provides access to the request status and result.
337 | */
338 | public WebFileCacheResult getFile(final RequestBuilder request, CacheListener cacheListener, final boolean forceDownload, final int priority) {
339 | // Get the key for the request
340 | final Key key = getKeyForRequest(request);
341 | // Get the local file we will find or store the file at
342 | final File localFile = getFileForKey(key);
343 |
344 | final BasicWebFileCacheResult requestInfo = new BasicWebFileCacheResult();
345 |
346 | // Synchronize on this for thread safety
347 | // Don't want to try to download the same file twice etc
348 | synchronized (getLockForKey(key)) {
349 | // If it's being downloaded, subscribe the given completion listener to
350 | // the event for the download
351 | if (isDownloading(key)) {
352 | subscribeListener(key, new CacheListener() {
353 |
354 | @Override
355 | public void onCacheResult(CacheResult result) {
356 | requestInfo.onCompleted(result);
357 | }
358 |
359 | @Override
360 | public void onCacheFailure(FailureInfo info) {
361 | requestInfo.onFailed(info);
362 | }
363 | });
364 | requestInfo.setCompleted(false);
365 | requestInfo.addCacheListener(cacheListener);
366 | return requestInfo;
367 | }
368 |
369 | // Otherwise, if it's downloaded, and we aren't forcing a download,
370 | // call the listener
371 | if (isDownloaded(localFile) && !forceDownload && isFresh(request, localFile)) {
372 | if (cacheListener != null) {
373 | cacheListener.onCacheResult(new CacheResultImplementation(localFile));
374 | }
375 |
376 | requestInfo.setCompleted(true);
377 | return requestInfo;
378 | } else if (localFile.exists()) {
379 | // If the local file exists, delete it
380 | localFile.delete();
381 | }
382 |
383 | // Indicate that we are now downloading the file
384 | indicateDownloading(key, cacheListener);
385 | // Set up an event listener to handle the response from the WebServiceManager
386 | WebServiceRequestListener listener = new WebServiceRequestListener() {
387 | @Override
388 | public void onRequestComplete(WebServiceManager manager, ResultInfo result) {
389 | // If the request fails, delete the file and raise completion with no file
390 | if (result == null || result.getResult() == null || !result.getResult()) {
391 | localFile.delete();
392 | FailureInfo info = new FailureInfoImp();
393 | onDownloadFailed(key, request, info);
394 | requestInfo.onFailed(info);
395 | } else {
396 | // Otherwise, call the listener with the local file
397 | CacheResult cacheResult = new CacheResultImplementation(localFile);
398 | onDownloadComplete(key, request, cacheResult);
399 | requestInfo.onCompleted(cacheResult);
400 | }
401 | }
402 | };
403 | // Execute the request in the background with the specified priority
404 | final WebServiceRequest download = getRequest(request, localFile);
405 | requestInfo.request = download;
406 | webServiceManager.doRequestInBackground(download, listener, priority);
407 | requestInfo.setCompleted(false);
408 | }
409 | return requestInfo;
410 | }
411 |
412 | private static class RequestLock {
413 | public File file;
414 | public boolean completed = false;
415 |
416 | public CacheListener completionListener = new CacheListener() {
417 | @Override
418 | public void onCacheResult(CacheResult result) {
419 | onResult(result.getResultFile());
420 | }
421 |
422 | @Override
423 | public void onCacheFailure(FailureInfo info) {
424 | onResult(null);
425 | }
426 | };
427 |
428 | private void onResult(File file) {
429 | RequestLock requestLock = RequestLock.this;
430 | synchronized (requestLock) {
431 | requestLock.file = file;
432 | requestLock.completed = true;
433 | requestLock.notifyAll();
434 | }
435 | }
436 | }
437 |
438 | /**
439 | * Retrieves the file for the given request with default priority. This call will block
440 | * until the request has completed.
441 | * @param request The {@link RequestBuilder} to execute to get the file.
442 | * @param forceDownload True to force the download, even if it is already cached.
443 | * @return The retrieved file, which may be null.
444 | */
445 | public File getFileSynchronous(RequestBuilder request, final boolean forceDownload) {
446 | final RequestLock requestLock = new RequestLock();
447 | getFile(request, requestLock.completionListener, forceDownload);
448 |
449 | return waitForResult(requestLock);
450 | }
451 |
452 | /**
453 | * Retrieves the file for the given request with given priority. This call will block
454 | * until the request has completed.
455 | * @param request The {@link RequestBuilder} to execute to get the file.
456 | * @param forceDownload True to force the download, even if it is already cached.
457 | * @param priority The priority of the download. See {@link Priority} for pre-defined values.
458 | * @return The retrieved file, which may be null.
459 | */
460 | public File getFileSynchronous(RequestBuilder request, final boolean forceDownload, final int priority) {
461 | final RequestLock requestLock = new RequestLock();
462 | getFile(request, requestLock.completionListener, forceDownload, priority);
463 |
464 | return waitForResult(requestLock);
465 | }
466 |
467 | /**
468 | * Removes the download data for the given request if it exists and has
469 | * completed. Does nothing if the data is currently being downloaded.
470 | * @param request The request to remove the data for.
471 | * @return True if data was removed, false if no data was found.
472 | */
473 | public boolean removeFile(RequestBuilder request) {
474 | return removeFileForKey(getKeyForRequest(request));
475 | }
476 |
477 | /**
478 | * Removes the downloaded data for the given key if it exists and has
479 | * completed. Does nothing if the key is currently being downloaded.
480 | * @param key The key to remove the data for.
481 | * @return True if data was removed, false if no data was not found.
482 | */
483 | protected boolean removeFileForKey(Key key) {
484 | File file = getFileForKey(key);
485 | synchronized (getLockForKey(key)) {
486 | if (isDownloaded(file)) {
487 | completedDownloads.removeDownload(file);
488 | file.delete();
489 | return true;
490 | }
491 | }
492 |
493 | return false;
494 | }
495 |
496 | /**
497 | * Removes all currently completed downloads. Will not impact any files
498 | * which are currently downloading.
499 | */
500 | public void clear() {
501 | // Create a "copy" of this set since it will be modified as we remove things
502 | Set files = new HashSet(getDownloadedFiles());
503 | for (File file : files) {
504 | file.delete();
505 | completedDownloads.removeDownload(file);
506 | }
507 | }
508 |
509 | private File waitForResult(RequestLock requestLock) {
510 | while (!requestLock.completed) {
511 | synchronized (requestLock) {
512 | try {
513 | requestLock.wait();
514 | } catch (InterruptedException e) { }
515 | }
516 | }
517 |
518 | return requestLock.file;
519 | }
520 |
521 | /**
522 | * Gets the {@link WebServiceRequest} to use to perform the given request and
523 | * store it in the given {@link File}.
524 | * @param builder The {@link RequestBuilder} to execute.
525 | * @param targetFile The {@link File} to store the results in.
526 | * @return A {@link WebServiceRequest} to execute.
527 | */
528 | protected abstract WebServiceRequest getRequest(RequestBuilder builder, File targetFile);
529 |
530 | ////////////////////
531 | // Download State //
532 | ////////////////////
533 | /**
534 | * Returns true if the given File is already downloaded.
535 | * @param file
536 | * @return
537 | */
538 | private boolean isDownloaded(File file) {
539 | if (completedDownloads.isDownloaded(file)) {
540 | if (file.exists()) {
541 | return true;
542 | } else {
543 | completedDownloads.removeDownload(file);
544 | return false;
545 | }
546 | }
547 | return false;
548 | }
549 |
550 | /**
551 | * Returns true if the given key is currently being downloaded.
552 | * @param key
553 | * @return
554 | */
555 | private boolean isDownloading(Key key) {
556 | return currentDownloads.contains(key);
557 | }
558 |
559 | /**
560 | * @return A set containing all files which are currently downloaded.
561 | */
562 | protected Set getDownloadedFiles() {
563 | return completedDownloads.getCompletedFiles();
564 | }
565 |
566 | private boolean isFresh(RequestBuilder request, File file) {
567 | synchronized (getLockForKey(getKeyForRequest(request))) {
568 | return isFresh(request, completedDownloads.getAge(file));
569 | }
570 | }
571 |
572 | /**
573 | * Gets whether the given request is still considered fresh data for the
574 | * given age.
575 | * @param request The request the data is for.
576 | * @param age The time since the data was obtained in milliseconds.
577 | * @return True if the data is still fresh, false if it should be ignored
578 | * and re-requested.
579 | */
580 | protected abstract boolean isFresh(RequestBuilder request, long age);
581 |
582 | /**
583 | * Call to indicate that a download has been completed. Calls all the listeners and removes
584 | * it from the current download state.
585 | * @param key The key of the item that was finished.
586 | * @param request The request that was executed.
587 | * @param result The cache result.
588 | */
589 | protected void onDownloadComplete(Key key, RequestBuilder request, final CacheResult result) {
590 | onDownloadResult(key, result.getResultFile(), new Delegate() {
591 | @Override
592 | public void execute(CacheListenerSet listenerSet) {
593 | listenerSet.onResult(result);
594 | }
595 | });
596 | }
597 |
598 | protected void onDownloadFailed(Key key, RequestBuilder request, final FailureInfo info) {
599 | onDownloadResult(key, null, new Delegate() {
600 | @Override
601 | public void execute(CacheListenerSet listenerSet) {
602 | listenerSet.onFailure(info);
603 | }
604 | });
605 | }
606 |
607 | private void onDownloadResult(Key key, File file, Delegate listenerAction) {
608 | // Synchronize back on the WebFileCache so that we do not raise the event
609 | // while someone is subscribing
610 | synchronized (getLockForKey(key)) {
611 | currentDownloads.remove(key);
612 | completedDownloads.onDownloadComplete(file);
613 |
614 | // Remove the event from the downloads, so no one can subscribe anymore
615 | CacheListenerSet completionEvent = cacheListeners.remove(key);
616 | // Notify all listeners
617 | if (completionEvent != null) {
618 | if (listenerAction != null) {
619 | listenerAction.execute(completionEvent);
620 | }
621 | completionEvent.clear();
622 | }
623 | }
624 | }
625 |
626 | /**
627 | * Call to indicate that that an item is downloading.
628 | * @param key The key of the item that is downloading.
629 | * @param listener An optional listener to subscribe to the completion event.
630 | */
631 | protected void indicateDownloading(Key key, CacheListener listener) {
632 | synchronized (getLockForKey(key)) {
633 | currentDownloads.add(key);
634 | completedDownloads.removeDownload(getFileForKey(key));
635 |
636 | // Create a new event to put in the list
637 | CacheListenerSet listenerSet = getListenerSetForKey(key);
638 | // Subscribe the current completion listener
639 | if (listener != null) {
640 | listenerSet.add(listener);
641 | }
642 | }
643 | }
644 |
645 |
646 | /////////////
647 | // Helpers //
648 | /////////////
649 | /**
650 | * Subscribes the given listener to the completion event for the download with
651 | * the given key
652 | * @param key The key of the download
653 | * @param listener The listener to subscribe
654 | * @return True if the item was added, otherwise false.
655 | */
656 | protected boolean subscribeListener(Key key, CacheListener listener) {
657 | CacheListenerSet listenerSet = getListenerSetForKey(key);
658 | if (listener != null) {
659 | listenerSet.add(listener);
660 | return true;
661 | }
662 |
663 | return false;
664 | }
665 |
666 | /**
667 | * Gets the download listener set for the item with the given key,
668 | * creating one if it doesn't currently exist
669 | * @param key
670 | * @return
671 | */
672 | private CacheListenerSet getListenerSetForKey(Key key) {
673 | return ConcurrencyUtils.putIfAbsent(cacheListeners, key, new CacheListenerSet());
674 | }
675 |
676 | /**
677 | * Gets the object to use as the key for the given request.
678 | * @param request The {@link RequestBuilder} to obtain the key for.
679 | * @return The object to be used as the key.
680 | */
681 | protected abstract Key getKeyForRequest(RequestBuilder request);
682 | /**
683 | * Gets the {@link File} where the data for the given key should be stored.
684 | * @param key The key to obtain the {@link File} for.
685 | * @return The {@link File} to store the data in.
686 | */
687 | protected abstract File getFileForKey(Key key);
688 |
689 | /**
690 | * Class which handles a set of download states and properties. Note that
691 | * this class is not thread-safe for performance reasons. It is likely
692 | * already synchronized externally or easier/cheaper to do externally
693 | * than it is to do it inside here.
694 | */
695 | private static class CompletedDownloadManager{
696 | private static final String PREFERENCES_NAME_FORMAT = "com.raizlabs.net.caching.WebFileCache:%s";
697 | public static final long VALUE_NOT_DOWNLOADED = Long.MIN_VALUE;
698 | private SharedPreferences preferences;
699 | private Editor preferencesEditor;
700 | private HashMap completedDownloads;
701 |
702 | private Handler backgroundHandler;
703 |
704 | public CompletedDownloadManager(String name, Context context, Handler backgroundHandler) {
705 | this.backgroundHandler = backgroundHandler;
706 | String prefsName = String.format(PREFERENCES_NAME_FORMAT, name);
707 | preferences = context.getSharedPreferences(prefsName, Context.MODE_PRIVATE);
708 | preferencesEditor = preferences.edit();
709 | completedDownloads = new HashMap();
710 | loadFromPreferences();
711 | }
712 |
713 | private void loadFromPreferences() {
714 | final Map downloads = preferences.getAll();
715 | for (Entry entry : downloads.entrySet()) {
716 | final Object value = entry.getValue();
717 | if (value instanceof Long) {
718 | long timeCompleted = ((Long) value).longValue();
719 | if (timeCompleted != VALUE_NOT_DOWNLOADED) {
720 | File file = new File(entry.getKey());
721 | completedDownloads.put(file, timeCompleted);
722 | }
723 | }
724 | }
725 | }
726 |
727 | public void removeDownload(File file) {
728 | preferencesEditor.putLong(file.getAbsolutePath(), VALUE_NOT_DOWNLOADED);
729 | commitPreferences();
730 | completedDownloads.remove(file);
731 | }
732 |
733 | public void onDownloadComplete(File file) {
734 | if (file != null && file.exists()) {
735 | final long completedTime = System.currentTimeMillis();
736 | preferencesEditor.putLong(file.getAbsolutePath(), completedTime);
737 | commitPreferences();
738 | completedDownloads.put(file, completedTime);
739 | }
740 | }
741 |
742 | public Set getCompletedFiles() {
743 | return completedDownloads.keySet();
744 | }
745 |
746 | private Runnable commitPrefsRunnable = new Runnable() {
747 | @Override
748 | public void run() {
749 | preferencesEditor.commit();
750 | }
751 | };
752 |
753 | @SuppressLint("NewApi")
754 | private void commitPreferences() {
755 | if (Build.VERSION.SDK_INT >= 9) {
756 | preferencesEditor.apply();
757 | } else {
758 | backgroundHandler.post(commitPrefsRunnable);
759 | }
760 | }
761 |
762 | public boolean isDownloaded(File file) {
763 | Long timeCompleted = completedDownloads.get(file);
764 | return (timeCompleted != null) && (timeCompleted.longValue() != VALUE_NOT_DOWNLOADED);
765 | }
766 |
767 | /**
768 | * Gets the time since the given {@link File} was downloaded.
769 | * @param file The file to get the age of.
770 | * @return The time since the file was downloaded in milliseconds, or
771 | * {@link #VALUE_NOT_DOWNLOADED} if the File was not downloaded.
772 | */
773 | public long getAge(File file) {
774 | Long timeCompleted = completedDownloads.get(file);
775 | if (timeCompleted != null && timeCompleted.longValue() != VALUE_NOT_DOWNLOADED) {
776 | return System.currentTimeMillis() - timeCompleted;
777 | } else {
778 | return VALUE_NOT_DOWNLOADED;
779 | }
780 | }
781 | }
782 |
783 | }
784 |
--------------------------------------------------------------------------------