package com.kth.baasio; import com.google.android.gcm.GCMRegistrar; import com.kth.baasio.callback.BaasioDeviceAsyncTask; import com.kth.baasio.callback.BaasioDeviceCallback; import com.kth.baasio.entity.file.multipart.MultipartEntity; import com.kth.baasio.entity.push.BaasioPush; import com.kth.baasio.entity.user.BaasioUser; import com.kth.baasio.exception.BaasioError; import com.kth.baasio.exception.BaasioException; import com.kth.baasio.preferences.BaasioPreferences; import com.kth.baasio.response.BaasioResponse; import com.kth.baasio.ssl.HttpUtils; import com.kth.baasio.utils.JsonUtils; import com.kth.baasio.utils.ObjectUtils; import com.kth.baasio.utils.UrlUtils; import com.kth.common.utils.LogUtils; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.codehaus.jackson.map.DeserializationConfig; import org.codehaus.jackson.map.ObjectMapper; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.converter.ByteArrayHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.ResourceHttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; import android.content.Context; import android.content.Intent; import android.os.Build; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import javax.net.ssl.HttpsURLConnection; public class Baas { private static final String TAG = LogUtils.makeLogTag(Baas.class); protected static RestTemplate restTemplate = new RestTemplate(); private boolean isInited = false; private String accessToken; private String baasioUrl; private String baasioId; private String applicationId; private String[] gcmSenderId; private BaasioUser mLoggedInUser; private boolean gcmEnabled; public static final int MIN_BUFFER_SIZE = 1024 * 4; private static int mUploadBuffSize = MIN_BUFFER_SIZE; private static int mDownloadBuffSize = MIN_BUFFER_SIZE; public static final String ACTION_UNAUTHORIZED = "com.kth.baasio.ACTION_UNAUTHORIZED"; private Context applicationContext; private Baas() { HttpURLConnection.setFollowRedirects(true); HttpsURLConnection.setFollowRedirects(true); List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>(); messageConverters.add(new ByteArrayHttpMessageConverter()); messageConverters.add(new StringHttpMessageConverter()); messageConverters.add(new ResourceHttpMessageConverter()); MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter(); ObjectMapper mapper = converter.getObjectMapper(); mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); messageConverters.add(converter); restTemplate.setMessageConverters(messageConverters); if (Build.VERSION.SDK_INT < 9) { restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(HttpUtils .getNewHttpClient())); } } // References: // "http://stackoverflow.com/questions/11165852/java-singleton-and-synchronization" private static class InstanceHolder { private static final Baas mSingleton = new Baas(); } /** * Get baas.io singleton object. * * @return baas.io singleton object */ public static Baas io() { return InstanceHolder.mSingleton; } /** * Set read timeout and connect timeout * * @param readTimeout milliseconds * @param connectTimeout milliseconds */ public static void setTimeout(int readTimeout, int connectTimeout) { if (restTemplate.getRequestFactory() instanceof SimpleClientHttpRequestFactory) { LogUtils.LOGD(TAG, "HttpUrlConnection is used"); ((SimpleClientHttpRequestFactory)restTemplate.getRequestFactory()) .setConnectTimeout(connectTimeout); ((SimpleClientHttpRequestFactory)restTemplate.getRequestFactory()) .setReadTimeout(readTimeout); } else if (restTemplate.getRequestFactory() instanceof HttpComponentsClientHttpRequestFactory) { LogUtils.LOGD(TAG, "HttpClient is used"); ((HttpComponentsClientHttpRequestFactory)restTemplate.getRequestFactory()) .setReadTimeout(readTimeout); ((HttpComponentsClientHttpRequestFactory)restTemplate.getRequestFactory()) .setConnectTimeout(connectTimeout); } } /** * Get baas.io URL. * * @return baas.io singleton object */ public String getBaasioUrl() { return baasioUrl; } /** * Get baas.io member id. * * @return baas.io member id */ public String getBaasioId() { return baasioId; } /** * Get baas.io application id. * * @return baas.io application id */ public String getApplicationId() { return applicationId; } protected void assertInited() { if (!isInited) { throw new IllegalArgumentException(BaasioError.ERROR_NEED_INIT); } } protected void assertValidBaasioUrl() { if (ObjectUtils.isEmpty(baasioUrl)) { throw new IllegalArgumentException(BaasioError.ERROR_MISSING_BAASIO_URL); } } protected void assertValidBaasioId() { if (ObjectUtils.isEmpty(baasioId)) { throw new IllegalArgumentException(BaasioError.ERROR_MISSING_BAASIO_ID); } } protected void assertValidApplicationId() { if (ObjectUtils.isEmpty(applicationId)) { throw new IllegalArgumentException(BaasioError.ERROR_MISSING_APPLICATION_ID); } } /** * Initialize baas.io module. * * @param context * @param baasioUrl baas.io API server URL * @param baasioId baas.io member id * @param applicationId baas.io application id */ public void init(Context context, String baasioUrl, String baasioId, String applicationId) { this.applicationContext = context; this.baasioUrl = baasioUrl; this.baasioId = baasioId; this.applicationId = applicationId; assertValidBaasioUrl(); assertValidBaasioId(); assertValidApplicationId(); if (!baasioUrl.startsWith("http://") && !baasioUrl.startsWith("https://")) { throw new IllegalArgumentException(BaasioError.ERROR_WRONG_BAASIO_URL); } String accessToken = BaasioPreferences.getAccessToken(context); if (!ObjectUtils.isEmpty(accessToken)) { setAccessToken(accessToken); } String userInfo = BaasioPreferences.getUserString(context); if (!ObjectUtils.isEmpty(userInfo)) { BaasioUser user = JsonUtils.parse(userInfo, BaasioUser.class); setSignedInUser(user); } isInited = true; } /** * Set to enable GCM. * * @param context * @param tags Tags * @param callback GCM registration result callback * @param gcmSenderId GCM sender ID * @return GCM registration task */ public BaasioDeviceAsyncTask setGcmEnabled(Context context, String tags, BaasioDeviceCallback callback, String... gcmSenderId) { if (!ObjectUtils.isEmpty(gcmSenderId) && !ObjectUtils.isEmpty(gcmSenderId[0])) { this.gcmSenderId = gcmSenderId; gcmEnabled = true; if (!ObjectUtils.isEmpty(tags)) { return BaasioPush.registerWithTagsInBackground(context, tags, callback); } else { return BaasioPush.registerInBackground(context, callback); } } return null; } /** * This method must be placed in Application.onDestroy() * * @param context */ public void uninit(Context context) { if (Baas.io().isGcmEnabled()) { try { GCMRegistrar.onDestroy(context); } catch (Exception e) { LogUtils.LOGV(TAG, "GCMRegistrar.onDestroy failed", e); } } } /** * "com.kth.baasio.ACTION_UNAUTHORIZED" action fire when access token is * expired or access denied. */ public void fireUnauthorized() { Intent intent = new Intent(); intent.setAction(ACTION_UNAUTHORIZED); applicationContext.sendBroadcast(intent); } /** * Send baas.io API request * * @param method HTTP method * @param params URL parameters * @param data HTTP entity * @param segments URL segments(paths) * @return Response for API request */ public BaasioResponse apiRequest(HttpMethod method, Map<String, Object> params, Object data, String... segments) throws BaasioException { assertInited(); assertValidBaasioUrl(); assertValidBaasioId(); assertValidApplicationId(); ArrayList<String> list = new ArrayList<String>(); list.add(baasioId); list.add(applicationId); list.addAll(Arrays.asList(segments)); String[] newSegments = list.toArray(new String[list.size()]); BaasioResponse response = null; try { response = httpRequest(baasioUrl, method, BaasioResponse.class, params, data, newSegments); } catch (ResourceAccessException e) { String message = e.getMessage(); if (message != null && message.contains("No authentication challenges found")) { LogUtils.LOGV(TAG, "Need Login"); fireUnauthorized(); } throw new BaasioException(e); } catch (HttpClientErrorException e) { if (e.getStatusCode().equals(HttpStatus.UNAUTHORIZED)) { LogUtils.LOGV(TAG, "Need Login"); fireUnauthorized(); } throw new BaasioException(e.getStatusCode(), e.getResponseBodyAsString()); } catch (HttpServerErrorException e) { throw new BaasioException(e.getStatusCode(), e.getResponseBodyAsString()); } return response; } /** * Send baas.io custom API request * * @param method HTTP method * @param cls Result object class * @param params URL parameters * @param data HTTP entity * @param segments URL segments(paths) * @return Response for API request with class type */ public <T> T customApiRequest(HttpMethod method, Class<T> cls, Map<String, Object> params, Object data, String... segments) throws BaasioException { assertInited(); assertValidBaasioUrl(); assertValidBaasioId(); assertValidApplicationId(); ArrayList<String> list = new ArrayList<String>(); list.add(baasioId); list.add(applicationId); list.addAll(Arrays.asList(segments)); String[] newSegments = list.toArray(new String[list.size()]); T response = null; try { response = httpRequest(baasioUrl, method, cls, params, data, newSegments); } catch (ResourceAccessException e) { String message = e.getMessage(); if (message != null && message.contains("No authentication challenges found")) { LogUtils.LOGV(TAG, "Need Login"); fireUnauthorized(); } throw new BaasioException(e); } catch (HttpClientErrorException e) { if (e.getStatusCode().equals(HttpStatus.UNAUTHORIZED)) { LogUtils.LOGV(TAG, "Need Login"); fireUnauthorized(); } throw new BaasioException(e.getStatusCode(), e.getResponseBodyAsString()); } catch (HttpServerErrorException e) { throw new BaasioException(e.getStatusCode(), e.getResponseBodyAsString()); } return response; } /** * Send baas.io custom API request * * @param method HTTP method * @param cls Result object class * @param params URL parameters * @param data HTTP entity * @param segments URL segments(paths) * @return Response for API request with class type */ public <T> T pastaApiRequest(HttpMethod method, Class<T> cls, Map<String, Object> params, Object data, String... segments) throws BaasioException { assertInited(); assertValidBaasioUrl(); assertValidBaasioId(); assertValidApplicationId(); ArrayList<String> list = new ArrayList<String>(); list.add(baasioId); list.add(applicationId); list.add("pasta"); list.addAll(Arrays.asList(segments)); String[] newSegments = list.toArray(new String[list.size()]); URL url = UrlUtils.url(baasioUrl); String host = url.getHost(); String pastaUrl = url.getProtocol() + "://pasta-" + host; T response = null; try { response = httpRequest(pastaUrl, method, cls, params, data, newSegments); } catch (ResourceAccessException e) { String message = e.getMessage(); if (message != null && message.contains("No authentication challenges found")) { LogUtils.LOGV(TAG, "Need Login"); fireUnauthorized(); } throw new BaasioException(e); } catch (HttpClientErrorException e) { if (e.getStatusCode().equals(HttpStatus.UNAUTHORIZED)) { LogUtils.LOGV(TAG, "Need Login"); fireUnauthorized(); } throw new BaasioException(e.getStatusCode(), e.getResponseBodyAsString()); } catch (HttpServerErrorException e) { throw new BaasioException(e.getStatusCode(), e.getResponseBodyAsString()); } return response; } /** * Send upload file API request * * @param method HTTP method * @param entity Multipart-form entity * @param segments Paths * @return HTTP response for upload API request */ public HttpResponse uploadFileRequest(HttpMethod method, MultipartEntity entity, String... segments) throws BaasioException { assertValidBaasioUrl(); assertValidBaasioId(); assertValidApplicationId(); ArrayList<String> list = new ArrayList<String>(); list.add(baasioId); list.add(applicationId); list.addAll(Arrays.asList(segments)); String[] newSegments = list.toArray(new String[list.size()]); String url = UrlUtils.path(baasioUrl, newSegments); LogUtils.LOGV(TAG, "Client.httpRequest(): " + method + " url: " + url); HttpClient client = HttpUtils.getNewHttpClient(); if (method == HttpMethod.POST) { HttpPost post = new HttpPost(url); if (!ObjectUtils.isEmpty(entity)) { post.setEntity(entity); } if (!ObjectUtils.isEmpty(getAccessToken())) { post.setHeader("Authorization", "Bearer " + getAccessToken()); } HttpResponse httpResponse = null; try { httpResponse = client.execute(post); } catch (ClientProtocolException e) { throw new BaasioException(e); } catch (IOException e) { throw new BaasioException(e); } return httpResponse; } else if (method == HttpMethod.PUT) { HttpPut put = new HttpPut(url); if (!ObjectUtils.isEmpty(entity)) { put.setEntity(entity); } if (!ObjectUtils.isEmpty(getAccessToken())) { put.setHeader("Authorization", "Bearer " + getAccessToken()); } HttpResponse httpResponse = null; try { httpResponse = client.execute(put); } catch (ClientProtocolException e) { throw new BaasioException(e); } catch (IOException e) { throw new BaasioException(e); } return httpResponse; } throw new BaasioException(BaasioError.ERROR_NOT_IMPLEMENTED); } /** * Send download file API request * * @param method HTTP method * @param segments Paths * @return HTTP response for download API request */ public HttpResponse downloadFileRequest(HttpMethod method, String... segments) throws BaasioException { assertValidBaasioUrl(); assertValidBaasioId(); assertValidApplicationId(); ArrayList<String> list = new ArrayList<String>(); list.add(baasioId); list.add(applicationId); list.addAll(Arrays.asList(segments)); String[] newSegments = list.toArray(new String[list.size()]); String fileDownloadUrl; if (baasioUrl.contains("api.")) { fileDownloadUrl = baasioUrl.replace("api", "blob"); } else { throw new IllegalArgumentException(BaasioError.ERROR_WRONG_BAASIO_URL); } String url = UrlUtils.path(fileDownloadUrl, newSegments); LogUtils.LOGV(TAG, "Client.httpRequest(): " + method + " url: " + url); HttpClient client = HttpUtils.getNewHttpClient(); HttpGet get = new HttpGet(url); if (!ObjectUtils.isEmpty(getAccessToken())) { get.setHeader("Authorization", "Bearer " + getAccessToken()); } HttpResponse httpResponse = null; try { httpResponse = client.execute(get); } catch (ClientProtocolException e) { throw new BaasioException(e); } catch (IOException e) { throw new BaasioException(e); } return httpResponse; } /** * Send HTTP request. * * @param method HTTP method * @param cls Result object class * @param params URL parameters * @param data HTTP entity * @param segments Paths * @return Response for API request with class type */ public <T> T httpRequest(String baseUrl, HttpMethod method, Class<T> cls, Map<String, Object> params, Object data, String... segments) throws BaasioException { // assertValidBaasioUrl(); HttpHeaders requestHeaders = new HttpHeaders(); requestHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); if (accessToken != null) { String auth = "Bearer " + accessToken; requestHeaders.set("Authorization", auth); LogUtils.LOGV(TAG, "Authorization: " + auth); } String url = UrlUtils.path(baseUrl, segments); MediaType contentType = MediaType.APPLICATION_JSON; if (method.equals(HttpMethod.POST) && ObjectUtils.isEmpty(data) && !ObjectUtils.isEmpty(params)) { data = UrlUtils.encodeParams(params); contentType = MediaType.APPLICATION_FORM_URLENCODED; } else { url = UrlUtils.addQueryParams(url, params); } requestHeaders.setContentType(contentType); List<Charset> accept = new ArrayList<Charset>(); accept.add(Charset.forName("UTF-8")); requestHeaders.setAcceptCharset(accept); HttpEntity<?> requestEntity = null; if (method.equals(HttpMethod.POST) || method.equals(HttpMethod.PUT)) { LogUtils.LOGV(TAG, "Client.httpRequest(): header: " + requestHeaders.toString()); if (ObjectUtils.isEmpty(data)) { requestEntity = new HttpEntity<Object>(requestHeaders); } else { requestEntity = new HttpEntity<Object>(data, requestHeaders); LogUtils.LOGV(TAG, "Client.httpRequest(): request: " + data.toString()); } } else { requestEntity = new HttpEntity<Object>(requestHeaders); } LogUtils.LOGV(TAG, "Client.httpRequest(): " + method + " url: " + url); ResponseEntity<T> responseEntity = restTemplate.exchange(url, method, requestEntity, cls); if (responseEntity != null) { if (responseEntity.getBody() != null) { LogUtils.LOGV(TAG, "Client.httpRequest(): reponse body: " + responseEntity.getBody().toString()); } else { LogUtils.LOGV(TAG, "Client.httpRequest(): reponse body is null: " + responseEntity.toString()); } } return responseEntity.getBody(); } /** * Set signed in user. * * @param user user to set signed-in user */ public void setSignedInUser(BaasioUser user) { mLoggedInUser = user; } /** * Get signed-in user. * * @return signed-in user */ public BaasioUser getSignedInUser() { return mLoggedInUser; } /** * Set access token. * * @param accessToken access token */ public void setAccessToken(String accessToken) { this.accessToken = accessToken; } /** * Get access token. * * @return access token */ public String getAccessToken() { return accessToken; } /** * To check GCM enabled. * * @return true is enabled. */ public boolean isGcmEnabled() { return gcmEnabled; } /** * Get GCM sender id. * * @return GCM sender id */ public String[] getGcmSenderId() { return gcmSenderId; } /** * Get file upload buffer size(byte). * * @return upload buffer size(byte) */ public static int getUploadBuffSize() { return mUploadBuffSize; } /** * Set file upload buffer size(byte). * * @param uploadBuffSize upload buffer size */ public static boolean setUploadBuffSize(int uploadBuffSize) { if (uploadBuffSize > MIN_BUFFER_SIZE) { Baas.mUploadBuffSize = uploadBuffSize; return true; } return false; } /** * Get file download buffer size(byte). * * @return file download buffer size(byte) */ public static int getDownloadBuffSize() { return mDownloadBuffSize; } /** * Set download buffer size(byte) * * @param downloadBuffSize download buffer size */ public static boolean setDownloadBuffSize(int downloadBuffSize) { if (downloadBuffSize > MIN_BUFFER_SIZE) { Baas.mDownloadBuffSize = downloadBuffSize; return true; } return false; } }