├── 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 | 5 | 9 | 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 | --------------------------------------------------------------------------------