package com.microsoft.services.sharepoint;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import com.microsoft.services.sharepoint.http.HttpConnection;
import com.microsoft.services.sharepoint.http.Request;
import com.microsoft.services.sharepoint.http.Response;
/**
* The type Office client.
*/
public class OfficeClient {
/**
* The M credentials.
*/
Credentials mCredentials;
/**
* The M logger.
*/
Logger mLogger;
/**
* Instantiates a new Office client.
*
* @param credentials the credentials
*/
public OfficeClient(Credentials credentials) {
this(credentials, null);
}
/**
* Instantiates a new Office client.
*
* @param credentials the credentials
* @param logger the logger
*/
public OfficeClient(Credentials credentials, Logger logger) {
if (credentials == null) {
throw new IllegalArgumentException("credentials must not be null");
}
if (logger == null) {
// add an empty logger
mLogger = new Logger() {
@Override
public void log(String message, LogLevel level) {
}
};
} else {
mLogger = logger;
}
mCredentials = credentials;
}
/**
* Log void.
*
* @param message the message
* @param level the level
*/
protected void log(String message, LogLevel level) {
getLogger().log(message, level);
}
/**
* Log void.
*
* @param error the error
*/
protected void log(Throwable error) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
error.printStackTrace(pw);
String stackTrace = sw.toString();
getLogger().log(error.toString() + "\nStack Trace: " + stackTrace, LogLevel.Critical);
}
/**
* Gets logger.
*
* @return the logger
*/
protected Logger getLogger() {
return mLogger;
}
/**
* Gets credentials.
*
* @return the credentials
*/
protected Credentials getCredentials() {
return mCredentials;
}
/**
* Generate o data query string.
*
* @param query the query
* @return the string
*/
protected String generateODataQueryString(Query query) {
StringBuilder sb = new StringBuilder();
if (query != null) {
query.ensureIdProperty();
sb.append("$filter=");
sb.append(queryEncode(query.toString()));
String rowSetModifiers = query.getRowSetModifiers().trim();
if (!rowSetModifiers.equals("")) {
if (!rowSetModifiers.startsWith("&")) {
sb.append("&");
}
sb.append(rowSetModifiers);
}
}
return sb.toString();
}
/**
* Execute request.
*
* @param url the url
* @param method the method
* @return the listenable future
*/
protected ListenableFuture<byte[]> executeRequest(String url, String method) {
return executeRequest(url, method, null, null);
}
/**
* Execute request.
*
* @param url the url
* @param method the method
* @param headers the headers
* @param payload the payload
* @return the listenable future
*/
protected ListenableFuture<byte[]> executeRequest(String url, String method, Map<String, String> headers,
byte[] payload) {
HttpConnection connection = Platform.createHttpConnection();
Request request = new Request(method);
if (headers != null) {
for (String key : headers.keySet()) {
request.addHeader(key, headers.get(key));
}
}
request.setUrl(url);
request.setContent(payload);
prepareRequest(request);
log("Generate request for " + url, LogLevel.Verbose);
request.log(getLogger());
final SettableFuture<byte[]> result = SettableFuture.create();
final ListenableFuture<Response> future = connection.execute(request);
Futures.addCallback(future, new FutureCallback<Response>() {
@Override
public void onFailure(Throwable t) {
result.setException(t);
}
@Override
public void onSuccess(Response response) {
try {
int statusCode = response.getStatus();
if (isValidStatus(statusCode)) {
byte[] responseContentBytes = response.readAllBytes();
result.set(responseContentBytes);
} else {
result.setException(new Exception("Invalid status code " + statusCode + ": "
+ response.readToEnd()));
}
} catch (IOException e) {
log(e);
}
}
});
return result;
}
/**
* Execute request json.
*
* @param url the url
* @param method the method
* @return the listenable future
*/
protected ListenableFuture<JSONObject> executeRequestJson(String url, String method) {
return executeRequestJson(url, method, null, null);
}
/**
* Execute request json.
*
* @param url the url
* @param method the method
* @param headers the headers
* @param payload the payload
* @return the listenable future
*/
protected ListenableFuture<JSONObject> executeRequestJson(String url, String method, Map<String, String> headers,
byte[] payload) {
final SettableFuture<JSONObject> result = SettableFuture.create();
final ListenableFuture<byte[]> request = executeRequest(url, method, headers, payload);
Futures.addCallback(request, new FutureCallback<byte[]>() {
@Override
public void onFailure(Throwable t) {
result.setException(t);
}
@Override
public void onSuccess(byte[] b) {
String string;
try {
string = new String(b, Constants.UTF8_NAME);
if (string.length() == 0) {
result.set(null);
} else {
JSONObject json = new JSONObject(string);
result.set(json);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
});
return result;
}
/**
* Gets discovery info.
*
* @return the discovery info
*/
public ListenableFuture<List<DiscoveryInformation>> getDiscoveryInfo() {
return getDiscoveryInfo("https://api.office.com/discovery/me/services");
}
/**
* Gets discovery info.
*
* @param discoveryEndpoint the discovery endpoint
* @return the discovery info
*/
public ListenableFuture<List<DiscoveryInformation>> getDiscoveryInfo(String discoveryEndpoint) {
final SettableFuture<List<DiscoveryInformation>> result = SettableFuture.create();
final ListenableFuture<JSONObject> request = executeRequestJson(discoveryEndpoint, "GET");
Futures.addCallback(request, new FutureCallback<JSONObject>() {
@Override
public void onFailure(Throwable t) {
result.setException(t);
}
@Override
public void onSuccess(JSONObject json) {
List<DiscoveryInformation> discoveryInfo;
try {
discoveryInfo = DiscoveryInformation.listFromJson(json, DiscoveryInformation.class);
result.set(discoveryInfo);
} catch (JSONException e) {
log(e.getMessage(), LogLevel.Critical);
}
}
});
return result;
}
/**
* Prepare request.
*
* @param request the request
*/
protected void prepareRequest(Request request) {
request.addHeader("Accept", "application/json;odata=verbose");
request.addHeader("X-ClientService-ClientTag", "SDK-JAVA");
int contentLength = 0;
if (request.getContent() != null) {
contentLength = request.getContent().length;
}
request.addHeader("Content-Length", String.valueOf(contentLength));
mCredentials.prepareRequest(request);
}
/**
* Is valid status.
*
* @param status the status
* @return the boolean
*/
protected static boolean isValidStatus(int status) {
return status >= 200 && status <= 299;
}
/**
* Query encode.
*
* @param query the query
* @return the string
*/
protected String queryEncode(String query) {
String encoded;
try {
encoded = query.replaceAll("\\s", "+");
} catch (Exception e) {
encoded = query;
}
return encoded;
}
/**
* Url encode.
*
* @param str the str
* @return the string
*/
protected String urlEncode(String str) {
String encoded;
try {
encoded = URLEncoder.encode(str, Constants.UTF8_NAME);
} catch (UnsupportedEncodingException e) {
encoded = str;
}
encoded = encoded.replaceAll("\\+", "%20").replaceAll("\\%21", "!").replaceAll("\\%27", "'")
.replaceAll("\\%28", "(").replaceAll("\\%29", ")").replaceAll("\\%7E", "~");
return encoded;
}
/**
* UUI dto string.
*
* @param id the id
* @return the string
*/
protected String UUIDtoString(UUID id) {
return id.toString().replace("-", "");
}
}