//------------------------------------------------------------------------------
// Copyright (c) 2012 Microsoft Corporation. All rights reserved.
//
// Description: See the class level JavaDoc comments.
//------------------------------------------------------------------------------
package com.microsoft.live;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONException;
import org.json.JSONObject;
import android.os.AsyncTask;
import android.text.TextUtils;
/**
* {@code LiveConnectClient} is a class that is responsible for making requests over to the
* Live Connect REST API. In order to perform requests, a {@link LiveConnectSession} is required.
* A {@link LiveConnectSession} can be created from a {@link LiveAuthClient}.
*
* {@code LiveConnectClient} provides methods to perform both synchronous and asynchronous calls
* on the Live Connect REST API. A synchronous method's corresponding asynchronous method is
* suffixed with "Async" (e.g., the synchronous method, get, has a corresponding asynchronous
* method called, getAsync). Asynchronous methods require a call back listener that will be called
* back on the main/UI thread on completion, error, or progress.
*/
public class LiveConnectClient {
/** Gets the ContentLength when a request finishes and sets it in the given operation. */
private static class ContentLengthObserver implements ApiRequest.Observer {
private final LiveDownloadOperation operation;
public ContentLengthObserver(LiveDownloadOperation operation) {
assert operation != null;
this.operation = operation;
}
@Override
public void onComplete(HttpResponse response) {
Header header = response.getFirstHeader(HTTP.CONTENT_LEN);
// Sometimes this header is not included in the response.
if (header == null) {
return;
}
int contentLength = Integer.valueOf(header.getValue());
this.operation.setContentLength(contentLength);
}
}
/**
* Listens to an {@link ApiRequestAsync} for onComplete and onError events and calls the proper
* method on the given {@link LiveDownloadOperationListener} on a given event.
*/
private static class DownloadObserver implements ApiRequestAsync.Observer<InputStream> {
private final LiveDownloadOperationListener listener;
private final LiveDownloadOperation operation;
public DownloadObserver(LiveDownloadOperation operation,
LiveDownloadOperationListener listener) {
assert operation != null;
assert listener != null;
this.operation = operation;
this.listener = listener;
}
@Override
public void onComplete(InputStream result) {
this.operation.setStream(result);
this.listener.onDownloadCompleted(this.operation);
}
@Override
public void onError(LiveOperationException e) {
this.listener.onDownloadFailed(e, this.operation);
}
}
/**
* Listens to an {@link ApiRequestAsync} for onComplete and onError events and calls the proper
* method on the given {@link LiveDownloadOperationListener} on a given event. When the download
* is complete this writes the results to a file, and publishes progress updates.
*/
private static class FileDownloadObserver extends AsyncTask<InputStream, Integer, Runnable>
implements ApiRequestAsync.Observer<InputStream> {
private class OnErrorRunnable implements Runnable {
private final LiveOperationException exception;
public OnErrorRunnable(LiveOperationException exception) {
this.exception = exception;
}
@Override
public void run() {
listener.onDownloadFailed(exception, operation);
}
}
private final File file;
private final LiveDownloadOperationListener listener;
private final LiveDownloadOperation operation;
public FileDownloadObserver(LiveDownloadOperation operation,
LiveDownloadOperationListener listener,
File file) {
assert operation != null;
assert listener != null;
assert file != null;
this.operation = operation;
this.listener = listener;
this.file = file;
}
@Override
protected Runnable doInBackground(InputStream... params) {
InputStream is = params[0];
byte[] buffer = new byte[BUFFER_SIZE];
OutputStream out;
try {
out = new BufferedOutputStream(new FileOutputStream(file));
} catch (FileNotFoundException e) {
LiveOperationException exception =
new LiveOperationException(ErrorMessages.CLIENT_ERROR, e);
return new OnErrorRunnable(exception);
}
try {
int totalBytes = operation.getContentLength();
int bytesRemaining = totalBytes;
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
bytesRemaining -= bytesRead;
publishProgress(totalBytes, bytesRemaining);
}
} catch (IOException e) {
LiveOperationException exception =
new LiveOperationException(ErrorMessages.CLIENT_ERROR, e);
return new OnErrorRunnable(exception);
} finally {
closeSilently(out);
closeSilently(is);
}
return new Runnable() {
@Override
public void run() {
listener.onDownloadCompleted(operation);
}
};
}
@Override
protected void onPostExecute(Runnable result) {
result.run();
}
@Override
protected void onProgressUpdate(Integer... values) {
int totalBytes = values[0];
int bytesRemaining = values[1];
assert totalBytes >= 0;
assert bytesRemaining >= 0;
assert totalBytes >= bytesRemaining;
listener.onDownloadProgress(totalBytes, bytesRemaining, operation);
}
@Override
public void onComplete(InputStream result) {
this.execute(result);
}
@Override
public void onError(LiveOperationException e) {
this.listener.onDownloadFailed(e, this.operation);
}
}
/**
* Listens to an {@link ApiRequestAsync} for onComplete and onError events and calls the proper
* method on the given {@link LiveOperationListener} on a given event.
*/
private static class OperationObserver implements ApiRequestAsync.Observer<JSONObject> {
private final LiveOperationListener listener;
private final LiveOperation operation;
public OperationObserver(LiveOperation operation,
LiveOperationListener listener) {
assert operation != null;
assert listener != null;
this.operation = operation;
this.listener = listener;
}
@Override
public void onComplete(JSONObject result) {
this.operation.setResult(result);
this.listener.onComplete(this.operation);
}
@Override
public void onError(LiveOperationException e) {
this.listener.onError(e, this.operation);
}
}
/** non-instantiable class that contains static constants for parameter names. */
private static final class ParamNames {
public static final String ACCESS_TOKEN = "session.getAccessToken()";
public static final String BODY = "body";
public static final String DESTINATION = "destination";
public static final String FILE = "file";
public static final String FILENAME = "filename";
public static final String OVERWRITE = "overwrite";
public static final String PATH = "path";
public static final String SESSION = "session";
private ParamNames() { throw new AssertionError(ErrorMessages.NON_INSTANTIABLE_CLASS); }
}
private enum SessionState {
LOGGED_IN {
@Override
public void check() {
// nothing. valid state.
}
},
LOGGED_OUT {
@Override
public void check() {
throw new IllegalStateException(ErrorMessages.LOGGED_OUT);
}
};
public abstract void check();
}
/**
* Listens to an {@link ApiRequestAsync} for onComplete and onError events, and listens to an
* {@link EntityEnclosingApiRequest} for onProgress events and calls the
* proper {@link LiveUploadOperationListener} on such events.
*/
private static class UploadRequestListener implements ApiRequestAsync.Observer<JSONObject>,
ApiRequestAsync.ProgressObserver {
private final LiveUploadOperationListener listener;
private final LiveOperation operation;
public UploadRequestListener(LiveOperation operation,
LiveUploadOperationListener listener) {
assert operation != null;
assert listener != null;
this.operation = operation;
this.listener = listener;
}
@Override
public void onComplete(JSONObject result) {
this.operation.setResult(result);
this.listener.onUploadCompleted(this.operation);
}
@Override
public void onError(LiveOperationException e) {
assert e != null;
this.listener.onUploadFailed(e, this.operation);
}
@Override
public void onProgress(Long... values) {
long totalBytes = values[0].longValue();
long numBytesWritten = values[1].longValue();
assert totalBytes >= 0L;
assert numBytesWritten >= 0L;
assert numBytesWritten <= totalBytes;
long bytesRemaining = totalBytes - numBytesWritten;
this.listener.onUploadProgress((int)totalBytes, (int)bytesRemaining, this.operation);
}
}
private static int BUFFER_SIZE = 1 << 10;
private static int CONNECT_TIMEOUT_IN_MS = 30 * 1000;
/** The key used for HTTP MOVE and HTTP COPY requests. */
private static final String DESTINATION_KEY = "destination";
private static volatile HttpClient HTTP_CLIENT;
private static Object HTTP_CLIENT_LOCK = new Object();
/**
* A LiveDownloadOperationListener that does nothing on each of the call backs.
* This is used so when a null listener is passed in, this can be used, instead of null,
* to avoid if (listener == null) checks.
*/
private static final LiveDownloadOperationListener NULL_DOWNLOAD_OPERATION_LISTENER;
/**
* A LiveOperationListener that does nothing on each of the call backs.
* This is used so when a null listener is passed in, this can be used, instead of null,
* to avoid if (listener == null) checks.
*/
private static final LiveOperationListener NULL_OPERATION_LISTENER;
/**
* A LiveUploadOperationListener that does nothing on each of the call backs.
* This is used so when a null listener is passed in, this can be used, instead of null,
* to avoid if (listener == null) checks.
*/
private static final LiveUploadOperationListener NULL_UPLOAD_OPERATION_LISTENER;
private static int SOCKET_TIMEOUT_IN_MS = 30 * 1000;
static {
NULL_DOWNLOAD_OPERATION_LISTENER = new LiveDownloadOperationListener() {
@Override
public void onDownloadCompleted(LiveDownloadOperation operation) {
assert operation != null;
}
@Override
public void onDownloadFailed(LiveOperationException exception,
LiveDownloadOperation operation) {
assert exception != null;
assert operation != null;
}
@Override
public void onDownloadProgress(int totalBytes,
int bytesRemaining,
LiveDownloadOperation operation) {
assert totalBytes >= 0;
assert bytesRemaining >= 0;
assert totalBytes >= bytesRemaining;
assert operation != null;
}
};
NULL_OPERATION_LISTENER = new LiveOperationListener() {
@Override
public void onComplete(LiveOperation operation) {
assert operation != null;
}
@Override
public void onError(LiveOperationException exception, LiveOperation operation) {
assert exception != null;
assert operation != null;
}
};
NULL_UPLOAD_OPERATION_LISTENER = new LiveUploadOperationListener() {
@Override
public void onUploadCompleted(LiveOperation operation) {
assert operation != null;
}
@Override
public void onUploadFailed(LiveOperationException exception,
LiveOperation operation) {
assert exception != null;
assert operation != null;
}
@Override
public void onUploadProgress(int totalBytes,
int bytesRemaining,
LiveOperation operation) {
assert totalBytes >= 0;
assert bytesRemaining >= 0;
assert totalBytes >= bytesRemaining;
assert operation != null;
}
};
}
/**
* Checks to see if the given path is a valid uri.
*
* @param path to check.
* @return the valid URI object.
*/
private static URI assertIsUri(String path) {
try {
return new URI(path);
} catch (URISyntaxException e) {
String message = String.format(ErrorMessages.INVALID_URI, ParamNames.PATH);
throw new IllegalArgumentException(message);
}
}
/**
* Checks to see if the path is null, empty, or a valid uri.
*
* This method will be used for Download and Upload requests.
* This method will NOT be used for Copy, Delete, Get, Move, Post and Put requests.
*
* @param path object_id to check.
* @throws IllegalArgumentException if the path is empty or an invalid uri.
* @throws NullPointerException if the path is null.
*/
private static void assertValidPath(String path) {
LiveConnectUtils.assertNotNullOrEmpty(path, ParamNames.PATH);
assertIsUri(path);
}
private static void closeSilently(Closeable c) {
try {
c.close();
} catch (Exception e) {
// Silently...ssshh
}
}
/**
* Checks to see if the path is null, empty, or is an absolute uri and throws
* the proper exception if it is.
*
* This method will be used for Copy, Delete, Get, Move, Post, and Put requests.
* This method will NOT be used for Download and Upload requests.
*
* @param path object_id to check.
* @throws IllegalArgumentException if the path is empty or an absolute uri.
* @throws NullPointerException if the path is null.
*/
private static void assertValidRelativePath(String path) {
LiveConnectUtils.assertNotNullOrEmpty(path, ParamNames.PATH);
if (path.toLowerCase().startsWith("http") || path.toLowerCase().startsWith("https")) {
String message = String.format(ErrorMessages.ABSOLUTE_PARAMETER, ParamNames.PATH);
throw new IllegalArgumentException(message);
}
}
/**
* Creates a new JSONObject body that has one key-value pair.
* @param key
* @param value
* @return a new JSONObject body with one key-value pair.
*/
private static JSONObject createJsonBody(String key, String value) {
Map<String, String> tempBody = new HashMap<String, String>();
tempBody.put(key, value);
return new JSONObject(tempBody);
}
private static HttpClient getHttpClient() {
// The LiveConnectClients can share one HttpClient with a ThreadSafeConnManager.
if (HTTP_CLIENT == null) {
synchronized (HTTP_CLIENT_LOCK) {
if (HTTP_CLIENT == null) {
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, CONNECT_TIMEOUT_IN_MS);
HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT_IN_MS);
ConnManagerParams.setMaxTotalConnections(params, 100);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http",
PlainSocketFactory.getSocketFactory(),
80));
schemeRegistry.register(new Scheme("https",
SSLSocketFactory.getSocketFactory(),
443));
// Create an HttpClient with the ThreadSafeClientConnManager.
// This connection manager must be used if more than one thread will
// be using the HttpClient, which is a common scenario.
ClientConnectionManager cm =
new ThreadSafeClientConnManager(params, schemeRegistry);
HTTP_CLIENT = new DefaultHttpClient(cm, params);
}
}
}
return HTTP_CLIENT;
}
/**
* Constructs a new LiveOperation and calls the listener's onError method.
*
* @param e
* @param listener
* @param userState arbitrary object that is used to determine the caller of the method.
* @return a new LiveOperation
*/
private static LiveOperation handleException(String method,
String path,
LiveOperationException e,
LiveOperationListener listener,
Object userState) {
LiveOperation operation =
new LiveOperation.Builder(method, path).userState(userState).build();
OperationObserver requestListener =
new OperationObserver(operation, listener);
requestListener.onError(e);
return operation;
}
/**
* Constructs a new LiveOperation and calls the listener's onUploadFailed method.
*
* @param e
* @param listener
* @param userState arbitrary object that is used to determine the caller of the method.
* @return a new LiveOperation
*/
private static LiveOperation handleException(String method,
String path,
LiveOperationException e,
LiveUploadOperationListener listener,
Object userState) {
LiveOperation operation =
new LiveOperation.Builder(method, path).userState(userState).build();
UploadRequestListener requestListener = new UploadRequestListener(operation, listener);
requestListener.onError(e);
return operation;
}
/**
* Converts an InputStream to a {@code byte[]}.
*
* @param is to convert to a {@code byte[]}.
* @return a new {@code byte[]} from the InputStream.
* @throws java.io.IOException if there was an error reading or closing the InputStream.
*/
private static byte[] toByteArray(InputStream is) throws IOException {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
OutputStream out = new BufferedOutputStream(byteOut);
is = new BufferedInputStream(is);
byte[] buffer = new byte[BUFFER_SIZE];
try {
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
} finally {
// we want to perform silent close operations
closeSilently(is);
closeSilently(out);
}
return byteOut.toByteArray();
}
/** Change this to mock the HTTP responses. */
private HttpClient httpClient;
private final LiveConnectSession session;
private SessionState sessionState;
/**
* Constructs a new {@code LiveConnectClient} instance and initializes it.
*
* @param session that will be used to authenticate calls over to the Live Connect REST API.
* @throws NullPointerException if session is null or if session.getAccessToken() is null.
* @throws IllegalArgumentException if session.getAccessToken() is empty.
*/
public LiveConnectClient(LiveConnectSession session) {
LiveConnectUtils.assertNotNull(session, ParamNames.SESSION);
String accessToken = session.getAccessToken();
LiveConnectUtils.assertNotNullOrEmpty(accessToken, ParamNames.ACCESS_TOKEN);
this.session = session;
this.sessionState = SessionState.LOGGED_IN;
// set a listener for the accessToken. If it is set to null, then the session was logged
// out.
this.session.addPropertyChangeListener("accessToken", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
String newValue = (String)event.getNewValue();
if (TextUtils.isEmpty(newValue)) {
LiveConnectClient.this.sessionState = SessionState.LOGGED_OUT;
} else {
LiveConnectClient.this.sessionState = SessionState.LOGGED_IN;
}
}
});
this.httpClient = getHttpClient();
}
/**
* Performs a synchronous HTTP COPY on the Live Connect REST API.
*
* A COPY duplicates a resource.
*
* @param path object_id of the resource to copy.
* @param destination the folder_id where the resource will be copied to.
* @return The LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
* @throws IllegalArgumentException if the path or destination is empty or if the path is an
* absolute uri.
* @throws NullPointerException if either the path or destination parameters are null.
*/
public LiveOperation copy(String path, String destination) throws LiveOperationException {
assertValidRelativePath(path);
LiveConnectUtils.assertNotNullOrEmpty(destination, ParamNames.DESTINATION);
CopyRequest request = this.createCopyRequest(path, destination);
return execute(request);
}
/**
* Performs an asynchronous HTTP COPY on the Live Connect REST API.
*
* A COPY duplicates a resource.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the resource to copy.
* @param destination the folder_id where the resource will be copied to.
* @param listener called on either completion or error during the copy request.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path or destination is empty or if the path is an
* absolute uri.
* @throws NullPointerException if either the path or destination parameters are null.
*/
public LiveOperation copyAsync(String path,
String destination,
LiveOperationListener listener) {
return this.copyAsync(path, destination, listener, null);
}
/**
* Performs an asynchronous HTTP COPY on the Live Connect REST API.
*
* A COPY duplicates a resource.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the resource to copy.
* @param destination the folder_id where the resource will be copied to
* @param listener called on either completion or error during the copy request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path or destination is empty or if the path is an
* absolute uri.
* @throws NullPointerException if either the path or destination parameters are null.
*/
public LiveOperation copyAsync(String path,
String destination,
LiveOperationListener listener,
Object userState) {
assertValidRelativePath(path);
LiveConnectUtils.assertNotNullOrEmpty(destination, ParamNames.DESTINATION);
if (listener == null) {
listener = NULL_OPERATION_LISTENER;
}
CopyRequest request;
try {
request = this.createCopyRequest(path, destination);
} catch (LiveOperationException e) {
return handleException(CopyRequest.METHOD, path, e, listener, userState);
}
return executeAsync(request, listener, userState);
}
/**
* Performs a synchronous HTTP DELETE on the Live Connect REST API.
*
* HTTP DELETE deletes a resource.
*
* @param path object_id of the resource to delete.
* @return The LiveOperation that contains the delete response
* @throws LiveOperationException if there is an error during the execution of the request.
* @throws IllegalArgumentException if the path is empty or an absolute uri.
* @throws NullPointerException if the path is null.
*/
public LiveOperation delete(String path) throws LiveOperationException {
assertValidRelativePath(path);
DeleteRequest request = new DeleteRequest(this.session, this.httpClient, path);
return execute(request);
}
/**
* Performs an asynchronous HTTP DELETE on the Live Connect REST API.
*
* HTTP DELETE deletes a resource.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the resource to delete.
* @param listener called on either completion or error during the delete request.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or an absolute uri.
* @throws NullPointerException if the path is null.
*/
public LiveOperation deleteAsync(String path, LiveOperationListener listener) {
return this.deleteAsync(path, listener, null);
}
/**
* Performs an asynchronous HTTP DELETE on the Live Connect REST API.
*
* HTTP DELETE deletes a resource.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the resource to delete.
* @param listener called on either completion or error during the delete request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or an absolute uri.
* @throws NullPointerException if the path is null.
*/
public LiveOperation deleteAsync(String path,
LiveOperationListener listener,
Object userState) {
assertValidRelativePath(path);
if (listener == null) {
listener = NULL_OPERATION_LISTENER;
}
DeleteRequest request = new DeleteRequest(this.session, this.httpClient, path);
return executeAsync(request, listener, userState);
}
/**
* Downloads a resource by performing a synchronous HTTP GET on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* @param path object_id of the resource to download.
* @throws LiveOperationException if there is an error during the execution of the request.
* @throws IllegalArgumentException if the path is empty or an invalid uri.
* @throws NullPointerException if the path is null.
*/
public LiveDownloadOperation download(String path) throws LiveOperationException {
assertValidPath(path);
DownloadRequest request = new DownloadRequest(this.session, this.httpClient, path);
LiveDownloadOperation operation =
new LiveDownloadOperation.Builder(request.getMethod(), request.getPath()).build();
request.addObserver(new ContentLengthObserver(operation));
InputStream stream = request.execute();
operation.setStream(stream);
return operation;
}
/**
* Downloads a resource by performing an asynchronous HTTP GET on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* {@link LiveDownloadOperationListener#onDownloadCompleted(LiveDownloadOperation)} will be
* called on success.
* On any download progress
* {@link LiveDownloadOperationListener#onDownloadProgress(int, int, LiveDownloadOperation)}
* will be called.
* Otherwise on error,
* {@link LiveDownloadOperationListener#onDownloadFailed(LiveOperationException,
* LiveDownloadOperation)} will
* be called. All of these methods will be called on the main/UI thread.
*
* @param path object_id of the resource to download.
* @param listener called on either completion or error during the download request.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or an invalid uri.
* @throws NullPointerException if the path is null.
*/
public LiveDownloadOperation downloadAsync(String path,
LiveDownloadOperationListener listener) {
return this.downloadAsync(path, listener, null);
}
/**
* Downloads a resource by performing an asynchronous HTTP GET on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* {@link LiveDownloadOperationListener#onDownloadCompleted(LiveDownloadOperation)} will be
* called on success.
* On any download progress
* {@link LiveDownloadOperationListener#onDownloadProgress(int, int, LiveDownloadOperation)}
* will be called.
* Otherwise on error,
* {@link LiveDownloadOperationListener#onDownloadFailed(LiveOperationException,
* LiveDownloadOperation)} will
* be called. All of these methods will be called on the main/UI thread.
*
* @param path object_id of the resource to download.
* @param listener called on either completion or error during the download request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or an invalid uri.
* @throws NullPointerException if the path is null.
*/
public LiveDownloadOperation downloadAsync(String path,
LiveDownloadOperationListener listener,
Object userState) {
assertValidPath(path);
if (listener == null) {
listener = NULL_DOWNLOAD_OPERATION_LISTENER;
}
DownloadRequest request = new DownloadRequest(this.session, this.httpClient, path);
return executeAsync(request, listener, userState);
}
public LiveDownloadOperation downloadAsync(String path,
File file,
LiveDownloadOperationListener listener) {
return this.downloadAsync(path, file, listener, null);
}
public LiveDownloadOperation downloadAsync(String path,
File file,
LiveDownloadOperationListener listener,
Object userState) {
assertValidPath(path);
if (listener == null) {
listener = NULL_DOWNLOAD_OPERATION_LISTENER;
}
DownloadRequest request = new DownloadRequest(this.session, this.httpClient, path);
ApiRequestAsync<InputStream> asyncRequest = ApiRequestAsync.newInstance(request);
LiveDownloadOperation operation =
new LiveDownloadOperation.Builder(request.getMethod(), request.getPath())
.userState(userState)
.apiRequestAsync(asyncRequest)
.build();
request.addObserver(new ContentLengthObserver(operation));
asyncRequest.addObserver(new FileDownloadObserver(operation, listener, file));
asyncRequest.execute();
return operation;
}
/**
* Performs a synchronous HTTP GET on the Live Connect REST API.
*
* HTTP GET retrieves the representation of a resource.
*
* @param path object_id of the resource to retrieve.
* @return The LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
* @throws IllegalArgumentException if the path is empty or an absolute uri.
* @throws NullPointerException if the path is null.
*/
public LiveOperation get(String path) throws LiveOperationException {
assertValidRelativePath(path);
GetRequest request = new GetRequest(this.session, this.httpClient, path);
return execute(request);
}
/**
* Performs an asynchronous HTTP GET on the Live Connect REST API.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path of the resource to retrieve.
* @param listener called on either completion or error during the get request.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or an absolute uri.
* @throws NullPointerException if the path is null.
*/
public LiveOperation getAsync(String path, LiveOperationListener listener) {
return this.getAsync(path, listener, null);
}
/**
* Performs an asynchronous HTTP GET on the Live Connect REST API.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the resource to retrieve.
* @param listener called on either completion or error during the get request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or an absolute uri.
* @throws NullPointerException if the path is null.
*/
public LiveOperation getAsync(String path, LiveOperationListener listener, Object userState) {
assertValidRelativePath(path);
if (listener == null) {
listener = NULL_OPERATION_LISTENER;
}
GetRequest request = new GetRequest(this.session, this.httpClient, path);
return executeAsync(request, listener, userState);
}
/** @return the {@link LiveConnectSession} instance used by this {@code LiveConnectClient}. */
public LiveConnectSession getSession() {
return this.session;
}
/**
* Performs a synchronous HTTP MOVE on the Live Connect REST API.
*
* A MOVE moves the location of a resource.
*
* @param path object_id of the resource to move.
* @param destination the folder_id to where the resource will be moved to.
* @return The LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
* @throws IllegalArgumentException if the path or destination is empty or if the path is an
* absolute uri.
* @throws NullPointerException if either the path or destination parameters are null.
*/
public LiveOperation move(String path, String destination) throws LiveOperationException {
assertValidRelativePath(path);
LiveConnectUtils.assertNotNullOrEmpty(destination, ParamNames.DESTINATION);
MoveRequest request = this.createMoveRequest(path, destination);
return execute(request);
}
/**
* Performs an asynchronous HTTP MOVE on the Live Connect REST API.
*
* A MOVE moves the location of a resource.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the resource to move.
* @param destination the folder_id to where the resource will be moved to.
* @param listener called on either completion or error during the copy request.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path or destination is empty or if the path is an
* absolute uri.
* @throws NullPointerException if either the path or destination parameters are null.
*/
public LiveOperation moveAsync(String path,
String destination,
LiveOperationListener listener) {
return this.moveAsync(path, destination, listener, null);
}
/**
* Performs an asynchronous HTTP MOVE on the Live Connect REST API.
*
* A MOVE moves the location of a resource.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the resource to move.
* @param destination the folder_id to where the resource will be moved to.
* @param listener called on either completion or error during the copy request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path or destination is empty or if the path is an
* absolute uri.
* @throws NullPointerException if either the path or destination parameters are null.
*/
public LiveOperation moveAsync(String path,
String destination,
LiveOperationListener listener,
Object userState) {
assertValidRelativePath(path);
LiveConnectUtils.assertNotNullOrEmpty(destination, ParamNames.DESTINATION);
if (listener == null) {
listener = NULL_OPERATION_LISTENER;
}
MoveRequest request;
try {
request = this.createMoveRequest(path, destination);
} catch (LiveOperationException e) {
return handleException(MoveRequest.METHOD, path, e, listener, userState);
}
return executeAsync(request, listener, userState);
}
/**
* Performs a synchronous HTTP POST on the Live Connect REST API.
*
* A POST adds a new resource to a collection.
*
* @param path object_id of the post request.
* @param body body of the post request.
* @return a LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation post(String path, JSONObject body) throws LiveOperationException {
assertValidRelativePath(path);
LiveConnectUtils.assertNotNull(body, ParamNames.BODY);
PostRequest request = createPostRequest(path, body);
return execute(request);
}
/**
* Performs a synchronous HTTP POST on the Live Connect REST API.
*
* A POST adds a new resource to a collection.
*
* @param path object_id of the post request.
* @param body body of the post request.
* @return a LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation post(String path, String body) throws LiveOperationException {
LiveConnectUtils.assertNotNullOrEmpty(body, ParamNames.BODY);
JSONObject jsonBody;
try {
jsonBody = new JSONObject(body.toString());
} catch (JSONException e) {
throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e);
}
return this.post(path, jsonBody);
}
/**
* Performs an asynchronous HTTP POST on the Live Connect REST API.
*
* A POST adds a new resource to a collection.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the post request.
* @param body body of the post request.
* @param listener called on either completion or error during the copy request.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation postAsync(String path, JSONObject body, LiveOperationListener listener) {
return this.postAsync(path, body, listener, null);
}
/**
* Performs an asynchronous HTTP POST on the Live Connect REST API.
*
* A POST adds a new resource to a collection.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the post request.
* @param body body of the post request.
* @param listener called on either completion or error during the copy request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation postAsync(String path,
JSONObject body,
LiveOperationListener listener,
Object userState) {
assertValidRelativePath(path);
LiveConnectUtils.assertNotNull(body, ParamNames.BODY);
if (listener == null) {
listener = NULL_OPERATION_LISTENER;
}
PostRequest request;
try {
request = createPostRequest(path, body);
} catch (LiveOperationException e) {
return handleException(PostRequest.METHOD, path, e, listener, userState);
}
return executeAsync(request, listener, userState);
}
/**
* Performs an asynchronous HTTP POST on the Live Connect REST API.
*
* A POST adds a new resource to a collection.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the post request.
* @param body body of the post request.
* @param listener called on either completion or error during the copy request.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation postAsync(String path, String body, LiveOperationListener listener) {
return this.postAsync(path, body, listener, null);
}
/**
* Performs an asynchronous HTTP POST on the Live Connect REST API.
*
* A POST adds a new resource to a collection.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the post request.
* @param body body of the post request.
* @param listener called on either completion or error during the copy request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation postAsync(String path,
String body,
LiveOperationListener listener,
Object userState) {
LiveConnectUtils.assertNotNullOrEmpty(body, ParamNames.BODY);
JSONObject jsonBody;
try {
jsonBody = new JSONObject(body.toString());
} catch (JSONException e) {
return handleException(PostRequest.METHOD,
path,
new LiveOperationException(ErrorMessages.CLIENT_ERROR, e),
listener,
userState);
}
return this.postAsync(path, jsonBody, listener, userState);
}
/**
* Performs a synchronous HTTP PUT on the Live Connect REST API.
*
* A PUT updates a resource or if it does not exist, it creates a one.
*
* @param path object_id of the put request.
* @param body body of the put request.
* @return a LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation put(String path, JSONObject body) throws LiveOperationException {
assertValidRelativePath(path);
LiveConnectUtils.assertNotNull(body, ParamNames.BODY);
PutRequest request = createPutRequest(path, body);
return execute(request);
}
/**
* Performs a synchronous HTTP PUT on the Live Connect REST API.
*
* A PUT updates a resource or if it does not exist, it creates a one.
*
* @param path object_id of the put request.
* @param body body of the put request.
* @return a LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation put(String path, String body) throws LiveOperationException {
LiveConnectUtils.assertNotNullOrEmpty(body, ParamNames.BODY);
JSONObject jsonBody;
try {
jsonBody = new JSONObject(body.toString());
} catch (JSONException e) {
throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e);
}
return this.put(path, jsonBody);
}
/**
* Performs an asynchronous HTTP PUT on the Live Connect REST API.
*
* A PUT updates a resource or if it does not exist, it creates a one.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the put request.
* @param body body of the put request.
* @param listener called on either completion or error during the put request.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation putAsync(String path, JSONObject body, LiveOperationListener listener) {
return this.putAsync(path, body, listener, null);
}
/**
* Performs an asynchronous HTTP PUT on the Live Connect REST API.
*
* A PUT updates a resource or if it does not exist, it creates a one.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path of the put request.
* @param body of the put request.
* @param listener called on either completion or error during the put request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation putAsync(String path,
JSONObject body,
LiveOperationListener listener,
Object userState) {
assertValidRelativePath(path);
LiveConnectUtils.assertNotNull(body, ParamNames.BODY);
if (listener == null) {
listener = NULL_OPERATION_LISTENER;
}
PutRequest request;
try {
request = createPutRequest(path, body);
} catch (LiveOperationException e) {
return handleException(PutRequest.METHOD, path, e, listener, userState);
}
return executeAsync(request, listener, userState);
}
/**
* Performs an asynchronous HTTP PUT on the Live Connect REST API.
*
* A PUT updates a resource or if it does not exist, it creates a one.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the put request.
* @param body body of the put request.
* @param listener called on either completion or error during the put request.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation putAsync(String path, String body, LiveOperationListener listener) {
return this.putAsync(path, body, listener, null);
}
/**
* Performs an asynchronous HTTP PUT on the Live Connect REST API.
*
* A PUT updates a resource or if it does not exist, it creates a one.
*
* {@link LiveOperationListener#onComplete(LiveOperation)} will be called on success.
* Otherwise, {@link LiveOperationListener#onError(LiveOperationException, LiveOperation)} will
* be called. Both of these methods will be called on the main/UI thread.
*
* @param path object_id of the put request.
* @param body body of the put request.
* @param listener called on either completion or error during the put request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
* @throws IllegalArgumentException if the path is empty or is an absolute uri.
* @throws NullPointerException if either the path or body parameters are null.
*/
public LiveOperation putAsync(String path,
String body,
LiveOperationListener listener,
Object userState) {
LiveConnectUtils.assertNotNullOrEmpty(body, ParamNames.BODY);
JSONObject jsonBody;
try {
jsonBody = new JSONObject(body.toString());
} catch (JSONException e) {
return handleException(PutRequest.METHOD,
path,
new LiveOperationException(ErrorMessages.CLIENT_ERROR, e),
listener,
userState);
}
return this.putAsync(path, jsonBody, listener, userState);
}
/**
* Uploads a resource by performing a synchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* If a file with the same name exists the upload will fail.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param file contents of the upload.
* @return a LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
*/
public LiveOperation upload(String path,
String filename,
InputStream file) throws LiveOperationException {
return this.upload(path, filename, file, OverwriteOption.DoNotOverwrite);
}
/**
* Uploads a resource by performing a synchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param file contents of the upload.
* @param overwrite specifies what to do when a file with the same name exists.
* @return a LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
*/
public LiveOperation upload(String path,
String filename,
InputStream file,
OverwriteOption overwrite) throws LiveOperationException {
assertValidPath(path);
LiveConnectUtils.assertNotNullOrEmpty(filename, ParamNames.FILENAME);
LiveConnectUtils.assertNotNull(file, ParamNames.FILE);
LiveConnectUtils.assertNotNull(overwrite, ParamNames.OVERWRITE);
// Currently, the API Service does not support chunked uploads,
// so we must know the length of the InputStream, before we send it.
// Load the stream into memory to get the length.
byte[] bytes;
try {
bytes = LiveConnectClient.toByteArray(file);
} catch (IOException e) {
throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e);
}
UploadRequest request = createUploadRequest(path,
filename,
new ByteArrayInputStream(bytes),
bytes.length,
overwrite);
return execute(request);
}
/**
* Uploads a resource by performing a synchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* If a file with the same name exists the upload will fail.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param file contents of the upload.
* @return a LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
*/
public LiveOperation upload(String path,
String filename,
File file) throws LiveOperationException {
return this.upload(path, filename, file, OverwriteOption.DoNotOverwrite);
}
/**
* Uploads a resource by performing a synchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param file contents of the upload.
* @param overwrite specifies what to do when a file with the same name exists.
* @return a LiveOperation that contains the JSON result.
* @throws LiveOperationException if there is an error during the execution of the request.
*/
public LiveOperation upload(String path,
String filename,
File file,
OverwriteOption overwrite) throws LiveOperationException {
assertValidPath(path);
LiveConnectUtils.assertNotNullOrEmpty(filename, ParamNames.FILENAME);
LiveConnectUtils.assertNotNull(file, ParamNames.FILE);
LiveConnectUtils.assertNotNull(overwrite, ParamNames.OVERWRITE);
InputStream is = null;
try {
is = new FileInputStream(file);
} catch (FileNotFoundException e) {
throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e);
}
UploadRequest request;
request = createUploadRequest(path,
filename,
is,
file.length(),
overwrite);
return execute(request);
}
/**
* Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on
* success.
* {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called
* on upload progress. Both of these methods will be called on the main/UI thread.
* Otherwise,
* {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)}
* will be called. This method will NOT be called on the main/UI thread.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param file contents of the upload.
* @param overwrite specifies what to do when a file with the same name exists.
* @param listener called on completion, on progress, or on an error of the upload request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
*/
public LiveOperation uploadAsync(String path,
String filename,
InputStream file,
OverwriteOption overwrite,
LiveUploadOperationListener listener,
Object userState) {
assertValidPath(path);
LiveConnectUtils.assertNotNullOrEmpty(filename, ParamNames.FILENAME);
LiveConnectUtils.assertNotNull(file, ParamNames.FILE);
LiveConnectUtils.assertNotNull(overwrite, ParamNames.OVERWRITE);
if (listener == null) {
listener = NULL_UPLOAD_OPERATION_LISTENER;
}
// Currently, the API Service does not support chunked uploads,
// so we must know the length of the InputStream, before we send it.
// Load the stream into memory to get the length.
byte[] bytes;
try {
bytes = LiveConnectClient.toByteArray(file);
} catch (IOException e) {
LiveOperationException exception =
new LiveOperationException(ErrorMessages.CLIENT_ERROR, e);
return handleException(UploadRequest.METHOD, path, exception, listener, userState);
}
UploadRequest request;
try {
request = createUploadRequest(path,
filename,
new ByteArrayInputStream(bytes),
bytes.length,
overwrite);
} catch (LiveOperationException e) {
return handleException(UploadRequest.METHOD, path, e, listener, userState);
}
ApiRequestAsync<JSONObject> asyncRequest = ApiRequestAsync.newInstance(request);
LiveOperation operation = new LiveOperation.Builder(request.getMethod(), request.getPath())
.userState(userState)
.apiRequestAsync(asyncRequest)
.build();
UploadRequestListener operationListener = new UploadRequestListener(operation, listener);
asyncRequest.addObserver(operationListener);
asyncRequest.addProgressObserver(operationListener);
asyncRequest.execute();
return operation;
}
/**
* Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on
* success.
* {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called
* on upload progress. Both of these methods will be called on the main/UI thread.
* Otherwise,
* {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)}
* will be called. This method will NOT be called on the main/UI thread.
*
* If a file with the same name exists the upload will fail.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param input contents of the upload.
* @param listener called on completion, on progress, or on an error of the upload request.
* @return the LiveOperation associated with the request.
*/
public LiveOperation uploadAsync(String path,
String filename,
InputStream input,
LiveUploadOperationListener listener) {
return this.uploadAsync(path, filename, input, listener, null);
}
/**
* Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on
* success.
* {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called
* on upload progress. Both of these methods will be called on the main/UI thread.
* Otherwise,
* {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)}
* will be called. This method will NOT be called on the main/UI thread.
*
* If a file with the same name exists the upload will fail.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param file contents of the upload.
* @param listener called on completion, on progress, or on an error of the upload request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
*/
public LiveOperation uploadAsync(String path,
String filename,
InputStream input,
LiveUploadOperationListener listener,
Object userState) {
return this.uploadAsync(
path,
filename,
input,
OverwriteOption.DoNotOverwrite,
listener,
userState);
}
/**
* Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on
* success.
* {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called
* on upload progress. Both of these methods will be called on the main/UI thread.
* Otherwise,
* {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)}
* will be called. This method will NOT be called on the main/UI thread.
*
* If a file with the same name exists the upload will fail.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param file contents of the upload.
* @param listener called on completion, on progress, or on an error of the upload request.
* @return the LiveOperation associated with the request.
*/
public LiveOperation uploadAsync(String path,
String filename,
File file,
LiveUploadOperationListener listener) {
return this.uploadAsync(path, filename, file, listener, null);
}
/**
* Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on
* success.
* {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called
* on upload progress. Both of these methods will be called on the main/UI thread.
* Otherwise,
* {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)}
* will be called. This method will NOT be called on the main/UI thread.
*
* If a file with the same name exists the upload will fail.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param file contents of the upload.
* @param listener called on completion, on progress, or on an error of the upload request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
*/
public LiveOperation uploadAsync(String path,
String filename,
File file,
LiveUploadOperationListener listener,
Object userState) {
return this.uploadAsync(
path,
filename,
file,
OverwriteOption.DoNotOverwrite,
listener,
userState);
}
/**
* Uploads a resource by performing an asynchronous HTTP PUT on the Live Connect REST API that
* returns the response as an {@link java.io.InputStream}.
*
* {@link LiveUploadOperationListener#onUploadCompleted(LiveOperation)} will be called on
* success.
* {@link LiveUploadOperationListener#onUploadProgress(int, int, LiveOperation) will be called
* on upload progress. Both of these methods will be called on the main/UI thread.
* Otherwise,
* {@link LiveUploadOperationListener#onUploadFailed(LiveOperationException, LiveOperation)}
* will be called. This method will NOT be called on the main/UI thread.
*
* @param path location to upload to.
* @param filename name of the new resource.
* @param file contents of the upload.
* @param overwrite specifies what to do when a file with the same name exists.
* @param listener called on completion, on progress, or on an error of the upload request.
* @param userState arbitrary object that is used to determine the caller of the method.
* @return the LiveOperation associated with the request.
*/
public LiveOperation uploadAsync(String path,
String filename,
File file,
OverwriteOption overwrite,
LiveUploadOperationListener listener,
Object userState) {
assertValidPath(path);
LiveConnectUtils.assertNotNullOrEmpty(filename, ParamNames.FILENAME);
LiveConnectUtils.assertNotNull(file, ParamNames.FILE);
LiveConnectUtils.assertNotNull(overwrite, ParamNames.OVERWRITE);
if (listener == null) {
listener = NULL_UPLOAD_OPERATION_LISTENER;
}
UploadRequest request;
try {
request = createUploadRequest(path,
filename,
new FileInputStream(file),
file.length(),
overwrite);
} catch (LiveOperationException e) {
return handleException(UploadRequest.METHOD, path, e, listener, userState);
} catch (FileNotFoundException e) {
LiveOperationException exception =
new LiveOperationException(ErrorMessages.CLIENT_ERROR, e);
return handleException(UploadRequest.METHOD, path, exception, listener, userState);
}
ApiRequestAsync<JSONObject> asyncRequest = ApiRequestAsync.newInstance(request);
LiveOperation operation = new LiveOperation.Builder(request.getMethod(), request.getPath())
.userState(userState)
.apiRequestAsync(asyncRequest)
.build();
UploadRequestListener operationListener = new UploadRequestListener(operation, listener);
asyncRequest.addObserver(operationListener);
asyncRequest.addProgressObserver(operationListener);
asyncRequest.execute();
return operation;
}
/**
* Sets the HttpClient that is used in requests.
*
* This is here to be able to mock the server for testing purposes.
*
* @param client
*/
void setHttpClient(HttpClient client) {
assert client != null;
this.httpClient = client;
}
/**
* Creates a {@link CopyRequest} and its json body.
* @param path location of the request.
* @param destination value for the json body.
* @return a new {@link CopyRequest}.
* @throws LiveOperationException if there is an error creating the request.
*/
private CopyRequest createCopyRequest(String path,
String destination) throws LiveOperationException {
assert !TextUtils.isEmpty(path);
assert !TextUtils.isEmpty(destination);
JSONObject body = LiveConnectClient.createJsonBody(DESTINATION_KEY, destination);
HttpEntity entity = createJsonEntity(body);
return new CopyRequest(this.session, this.httpClient, path, entity);
}
private JsonEntity createJsonEntity(JSONObject body) throws LiveOperationException {
assert body != null;
try {
return new JsonEntity(body);
} catch (UnsupportedEncodingException e) {
throw new LiveOperationException(ErrorMessages.CLIENT_ERROR, e);
}
}
private MoveRequest createMoveRequest(String path,
String destination) throws LiveOperationException {
assert !TextUtils.isEmpty(path);
assert !TextUtils.isEmpty(destination);
JSONObject body = LiveConnectClient.createJsonBody(DESTINATION_KEY, destination);
HttpEntity entity = createJsonEntity(body);
return new MoveRequest(this.session, this.httpClient, path, entity);
}
private PostRequest createPostRequest(String path,
JSONObject body) throws LiveOperationException {
assert !TextUtils.isEmpty(path);
assert body != null;
HttpEntity entity = createJsonEntity(body);
return new PostRequest(this.session, this.httpClient, path, entity);
}
private PutRequest createPutRequest(String path,
JSONObject body) throws LiveOperationException {
assert !TextUtils.isEmpty(path);
assert body != null;
HttpEntity entity = createJsonEntity(body);
return new PutRequest(this.session, this.httpClient, path, entity);
}
private UploadRequest createUploadRequest(String path,
String filename,
InputStream is,
long length,
OverwriteOption overwrite) throws LiveOperationException {
assert !TextUtils.isEmpty(path);
assert !TextUtils.isEmpty(filename);
assert is != null;
InputStreamEntity entity = new InputStreamEntity(is, length);
return new UploadRequest(this.session, this.httpClient, path, entity, filename, overwrite);
}
/**
* Creates a new LiveOperation and executes it synchronously.
*
* @param request
* @param listener
* @param userState arbitrary object that is used to determine the caller of the method.
* @return a new LiveOperation.
*/
private LiveOperation execute(ApiRequest<JSONObject> request) throws LiveOperationException {
this.sessionState.check();
JSONObject result = request.execute();
LiveOperation.Builder builder =
new LiveOperation.Builder(request.getMethod(), request.getPath()).result(result);
return builder.build();
}
/**
* Creates a new LiveDownloadOperation and executes it asynchronously.
*
* @param request
* @param listener
* @param userState arbitrary object that is used to determine the caller of the method.
* @return a new LiveDownloadOperation.
*/
private LiveDownloadOperation executeAsync(ApiRequest<InputStream> request,
LiveDownloadOperationListener listener,
Object userState) {
this.sessionState.check();
ApiRequestAsync<InputStream> asyncRequest = ApiRequestAsync.newInstance(request);
LiveDownloadOperation operation =
new LiveDownloadOperation.Builder(request.getMethod(), request.getPath())
.userState(userState)
.apiRequestAsync(asyncRequest)
.build();
request.addObserver(new ContentLengthObserver(operation));
asyncRequest.addObserver(new DownloadObserver(operation, listener));
asyncRequest.execute();
return operation;
}
/**
* Creates a new LiveOperation and executes it asynchronously.
*
* @param request
* @param listener
* @param userState arbitrary object that is used to determine the caller of the method.
* @return a new LiveOperation.
*/
private LiveOperation executeAsync(ApiRequest<JSONObject> request,
LiveOperationListener listener,
Object userState) {
this.sessionState.check();
ApiRequestAsync<JSONObject> asyncRequest = ApiRequestAsync.newInstance(request);
LiveOperation operation = new LiveOperation.Builder(request.getMethod(), request.getPath())
.userState(userState)
.apiRequestAsync(asyncRequest)
.build();
asyncRequest.addObserver(new OperationObserver(operation, listener));
asyncRequest.execute();
return operation;
}
}