
com.stackmob.sdk.request.StackMobRequest Maven / Gradle / Ivy
The newest version!
/**
* Copyright 2011 StackMob
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.stackmob.sdk.request;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.stackmob.sdk.api.*;
import com.stackmob.sdk.api.StackMob.OAuthVersion;
import com.stackmob.sdk.callback.StackMobRawCallback;
import com.stackmob.sdk.callback.StackMobRedirectedCallback;
import com.stackmob.sdk.exception.StackMobException;
import com.stackmob.sdk.net.*;
import com.stackmob.sdk.push.StackMobPushToken;
import com.stackmob.sdk.util.*;
import org.scribe.builder.ServiceBuilder;
import org.scribe.exceptions.OAuthException;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.oauth.OAuthService;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Modifier;
import java.net.*;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* The base class for StackMob's internal representation of a request. This class is only meant to be used inside the sdk
*/
public abstract class StackMobRequest {
public static final List> EmptyHeaders = new ArrayList>();
public static final List> EmptyParams = new ArrayList>();
protected static final String SECURE_SCHEME = "https";
protected static final String REGULAR_SCHEME = "http";
protected static final String API_KEY_HEADER = "X-StackMob-API-Key";
protected static final String AUTHORIZATION_HEADER = "Authorization";
protected final ExecutorService executor;
protected final StackMobSession session;
protected StackMobRawCallback callback;
protected final StackMobRedirectedCallback redirectedCallback;
protected HttpVerb httpVerb;
protected String methodName;
protected String urlFormat = StackMob.DEFAULT_API_HOST;
protected Boolean isSecure = false;
protected List> params = new ArrayList>();
protected List> headers = new ArrayList>();
private AtomicBoolean triedRefreshToken = new AtomicBoolean(false);
private OAuthVersion oauthVersionOverride;
protected Gson gson;
private OAuthService oAuthService;
protected StackMobRequest(ExecutorService executor,
StackMobSession session,
OAuthVersion oauthVersionOverride,
HttpVerb verb,
StackMobOptions options,
List> params,
String method,
StackMobRawCallback cb,
StackMobRedirectedCallback redirCb) {
this.executor = executor;
this.session = session;
this.isSecure = options.isHTTPS();
this.httpVerb = verb;
this.headers = options.getHeaders();
this.params = params;
this.methodName = method;
this.callback = cb;
this.redirectedCallback = redirCb;
this.oauthVersionOverride = oauthVersionOverride;
GsonBuilder gsonBuilder = new GsonBuilder()
.registerTypeAdapter(StackMobPushToken.class, new StackMobPushToken.Deserializer())
.registerTypeAdapter(StackMobPushToken.class, new StackMobPushToken.Serializer())
.registerTypeAdapter(StackMobForgotPasswordEmail.class, new StackMobForgotPasswordEmail.Deserializer())
.registerTypeAdapter(StackMobForgotPasswordEmail.class, new StackMobForgotPasswordEmail.Serializer())
.registerTypeAdapter(StackMobNull.class, new StackMobNull.Adapter())
.excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.PROTECTED, Modifier.TRANSIENT, Modifier.STATIC);
gson = gsonBuilder.create();
if(!isOAuth2()) oAuthService = new ServiceBuilder().provider(StackMobApi.class).apiKey(session.getKey()).apiSecret(session.getSecret()).build();
}
public StackMobRequest setUrlFormat(String urlFmt) {
this.urlFormat = urlFmt;
return this;
}
protected abstract String getRequestBody();
public void sendRequest() {
try {
if(HttpVerbWithoutPayload.GET == httpVerb) {
sendGetRequest();
}
else if(HttpVerbWithoutPayload.HEAD == httpVerb) {
sendHeadRequest();
}
else if(HttpVerbWithPayload.POST == httpVerb) {
sendPostRequest();
}
else if(HttpVerbWithPayload.PUT == httpVerb) {
sendPutRequest();
}
else if(HttpVerbWithoutPayload.DELETE == httpVerb) {
sendDeleteRequest();
}
else {
StackMobException ex = new StackMobException(String.format("The StackMob SDK doesn't support the HTTP verb %s at this time", httpVerb.toString()));
callback.unsent(ex);
}
}
catch(StackMobException e) {
callback.unsent(e);
}
}
protected void sendGetRequest() throws StackMobException {
try {
String query = formatQueryString(this.params);
URI uri = createURI(getScheme(), urlFormat, getPath(), query);
OAuthRequest req = getOAuthRequest(uri.getScheme(), HttpVerbWithoutPayload.GET, uri.toString());
sendRequest(req);
}
catch (URISyntaxException e) {
throw new StackMobException(e.getMessage());
}
catch (InterruptedException e) {
throw new StackMobException(e.getMessage());
}
catch (ExecutionException e) {
throw new StackMobException(e.getMessage());
}
}
protected void sendHeadRequest() throws StackMobException {
try {
String query = formatQueryString(this.params);
URI uri = createURI(getScheme(), urlFormat, getPath(), query);
OAuthRequest req = getOAuthRequest(uri.getScheme(), HttpVerbWithoutPayload.HEAD, uri.toString());
sendRequest(req);
}
catch (URISyntaxException e) {
throw new StackMobException(e.getMessage());
}
catch (InterruptedException e) {
throw new StackMobException(e.getMessage());
}
catch (ExecutionException e) {
throw new StackMobException(e.getMessage());
}
}
protected void sendPostRequest() throws StackMobException {
try {
URI uri = createURI(getScheme(), urlFormat, getPath(), "");
String payload = getRequestBody();
OAuthRequest req = getOAuthRequest(uri.getScheme(), HttpVerbWithPayload.POST, uri.toString(), payload);
sendRequest(req);
}
catch (URISyntaxException e) {
throw new StackMobException(e.getMessage());
}
catch (InterruptedException e) {
throw new StackMobException(e.getMessage());
}
catch (ExecutionException e) {
throw new StackMobException(e.getMessage());
}
}
protected void sendPutRequest() throws StackMobException {
try {
URI uri = createURI(getScheme(), urlFormat, getPath(), "");
String payload = getRequestBody();
OAuthRequest req = getOAuthRequest(uri.getScheme(), HttpVerbWithPayload.PUT, uri.toString(), payload);
sendRequest(req);
}
catch (URISyntaxException e) {
throw new StackMobException(e.getMessage());
}
catch (InterruptedException e) {
throw new StackMobException(e.getMessage());
}
catch (ExecutionException e) {
throw new StackMobException(e.getMessage());
}
}
protected void sendDeleteRequest() throws StackMobException {
try {
String query = formatQueryString(this.params);
URI uri = createURI(getScheme(), urlFormat, getPath(), query);
OAuthRequest req = getOAuthRequest(uri.getScheme(), HttpVerbWithoutPayload.DELETE, uri.toString());
sendRequest(req);
}
catch (URISyntaxException e) {
throw new StackMobException(e.getMessage());
}
catch (InterruptedException e) {
throw new StackMobException(e.getMessage());
}
catch (ExecutionException e) {
throw new StackMobException(e.getMessage());
}
}
protected URI createURI(String scheme, String host, String path, String query) throws URISyntaxException {
String domain = Http.fullDomain(scheme, host);
StringBuilder uriBuilder = new StringBuilder().append(session.getRedirect(domain));
if(!path.startsWith("/")) {
uriBuilder.append("/");
}
uriBuilder.append(escapePath(path));
if(query != null && query.length() > 0) {
uriBuilder.append("?").append(query);
}
return new URI(uriBuilder.toString());
}
private String escapePath(String path) throws URISyntaxException {
String[] parts = path.split("/");
StringBuilder sb = new StringBuilder();
for(int i = 0; i < parts.length; i++) {
try {
if(i == parts.length-1) {
sb.append(URLEncoder.encode(parts[i], "utf-8"));
} else {
sb.append(parts[i]);
}
} catch(UnsupportedEncodingException e) {
throw new URISyntaxException(parts[i], "could not be URL-encoded as UTF-8");
}
if(i != parts.length-1) {
sb.append("/");
}
}
return sb.toString();
}
protected String getPath() {
if(methodName.startsWith("/")) {
return methodName;
}
else {
return "/" + methodName;
}
}
protected String getScheme() {
if(session.getHTTPSOverride() == null) {
return isSecure ? SECURE_SCHEME : REGULAR_SCHEME;
} else {
return session.getHTTPSOverride() ? SECURE_SCHEME : REGULAR_SCHEME;
}
}
protected static String percentEncode(String s) throws UnsupportedEncodingException {
return URLEncoder.encode(s, "UTF-8").replace("+", "%20");
}
protected static String formatQueryString(List> params) {
List paramList = new LinkedList();
for(Map.Entry pair : params) {
String key = pair.getKey();
String value = pair.getValue();
try {
paramList.add(String.format("%s=%s", percentEncode(key), percentEncode(value)));
}
catch(UnsupportedEncodingException e) {
//do nothing
}
}
return ListHelpers.join(paramList, "&");
}
protected String getContentType() {
return "application/json; charset=utf-8";
}
protected OAuthVersion getOAuthVersion() {
if(this.oauthVersionOverride != null) return oauthVersionOverride;
return session.getOAuthVersion();
}
protected boolean isOAuth2() {
return getOAuthVersion() == OAuthVersion.Two;
}
protected OAuthRequest getOAuthRequest(String scheme, HttpVerb method, String url) {
Verb verb = Verb.valueOf(method.toString());
OAuthRequest oReq = new OAuthRequest(verb, url);
int apiVersion = session.getApiVersionNumber();
final String accept = "application/vnd.stackmob+json; version="+apiVersion;
List> headerList = new ArrayList>();
//build basic headers
if(!verb.equals(Verb.GET) && !verb.equals(Verb.DELETE) && !verb.equals(Verb.HEAD)) {
headerList.add(new Pair("Content-Type", getContentType()));
}
//build user headers
boolean hasAcceptHeader = false;
if(this.headers != null) {
for(Map.Entry header : this.headers) {
if(header.getKey().equals("Accept")) hasAcceptHeader = true;
headerList.add(new Pair(header.getKey(), header.getValue()));
}
}
if(!hasAcceptHeader) headerList.add(new Pair("Accept", accept));
headerList.add(new Pair("User-Agent", session.getUserAgent()));
String cookieHeader = session.getCookieManager().cookieHeader();
if(cookieHeader.length() > 0) headerList.add(new Pair("Cookie", cookieHeader));
//add headers to request
for(Map.Entry header: headerList) {
oReq.addHeader(header.getKey(), header.getValue());
}
switch(getOAuthVersion()) {
case One: oAuthService.signRequest(new Token("", ""), oReq); break;
case Two: {
oReq.addHeader(API_KEY_HEADER, session.getKey());
if(session.oauth2TokenValid()) {
String urlNoScheme = url.substring(scheme.length() + 3);
int firstSlash = urlNoScheme.indexOf("/");
String[] hostAndPort = urlNoScheme.substring(0, firstSlash).split(":");
String host = hostAndPort[0];
String port = getPort(scheme, hostAndPort);
String uri = urlNoScheme.substring(firstSlash);
oReq.addHeader(AUTHORIZATION_HEADER, session.generateMacToken(method.toString(), uri, host, port));
}
break;
}
}
return oReq;
}
private String getPort(String scheme, String[] hostAndPort) {
if(hostAndPort.length > 1) {
return hostAndPort[1];
} else {
return scheme.equals(SECURE_SCHEME) ? "443" : "80";
}
}
private byte[] getByteArray(InputStream is) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[16384];
try {
while ((nRead = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
} catch (IOException ex) {
return new byte[0];
}
return buffer.toByteArray();
}
protected OAuthRequest getOAuthRequest(String scheme, HttpVerb method, String url, String payload) {
OAuthRequest req = getOAuthRequest(scheme, method, url);
req.addPayload(payload);
return req;
}
protected static HttpVerb getRequestVerb(OAuthRequest req) {
HttpVerb requestVerb = HttpVerbWithoutPayload.GET;
if(req.getVerb() == Verb.POST) requestVerb = HttpVerbWithPayload.POST;
else if(req.getVerb() == Verb.PUT) requestVerb = HttpVerbWithPayload.PUT;
else if(req.getVerb() == Verb.DELETE) requestVerb = HttpVerbWithoutPayload.DELETE;
else if(req.getVerb() == Verb.HEAD) requestVerb = HttpVerbWithoutPayload.HEAD;
return requestVerb;
}
protected static List> getRequestHeaders(OAuthRequest req) {
List> requestHeaders = new ArrayList>();
for(Map.Entry header : req.getHeaders().entrySet()) {
requestHeaders.add(header);
}
return requestHeaders;
}
protected boolean tryRefreshToken() {
return true;
}
private boolean canDoRefreshToken() {
return isOAuth2() && session.oauth2RefreshTokenValid() && tryRefreshToken() && !triedRefreshToken.get();
}
protected void refreshTokenAndResend() {
triedRefreshToken.set(true);
StackMobAccessTokenRequest.newRefreshTokenRequest(executor, session, redirectedCallback, new StackMobRawCallback() {
@Override
public void unsent(StackMobException e) {
sendRequest();
}
@Override
public void temporaryPasswordResetRequired(StackMobException e) {
sendRequest();
}
@Override
public void done(HttpVerb requestVerb, String requestURL, List> requestHeaders, String requestBody, Integer responseStatusCode, List> responseHeaders, byte[] responseBody) {
sendRequest();
}
@Override
public void circularRedirect(String originalUrl, Map redirectHeaders, String redirectBody, String newURL) {
}
}).setUrlFormat(urlFormat).sendRequest();
}
protected void sendRequest(final OAuthRequest req) throws InterruptedException, ExecutionException {
final StackMobRawCallback cb = this.callback;
if(isOAuth2() && !session.oauth2TokenValid() && canDoRefreshToken()) {
refreshTokenAndResend();
} else {
executor.submit(new Callable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy