Java tutorial
/******************************************************************************* * Copyright 2014 eTilbudsavis * * 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.eTilbudsavis.etasdk.network; import java.util.HashMap; import java.util.Map; import java.util.concurrent.BlockingQueue; import org.json.JSONException; import org.json.JSONObject; import android.os.Process; import com.eTilbudsavis.etasdk.Eta; import com.eTilbudsavis.etasdk.SessionManager; import com.eTilbudsavis.etasdk.log.EtaLog; import com.eTilbudsavis.etasdk.network.Request.Method; import com.eTilbudsavis.etasdk.utils.Api.Endpoint; import com.eTilbudsavis.etasdk.utils.HashUtils; import com.eTilbudsavis.etasdk.utils.HeaderUtils; public class NetworkDispatcher extends Thread { public static final String TAG = Eta.TAG_PREFIX + NetworkDispatcher.class.getSimpleName(); /** Eta object controlling the whole lot */ private final Eta mEta; /** The queue of requests to service. */ private final BlockingQueue<Request<?>> mQueue; /** The RequestQueue this NetworkDispatcher receives Requests from */ private final RequestQueue mRequestQueue; /** The network interface for processing requests. */ private final Network mNetwork; /** The cache to write to. */ private final Cache mCache; /** For posting responses and errors. */ private final Delivery mDelivery; /** Used for telling us to die. */ private volatile boolean mQuit = false; public NetworkDispatcher(Eta eta, RequestQueue requestQueue, BlockingQueue<Request<?>> queue, Network network, Cache cache, Delivery delivery) { mQueue = queue; mNetwork = network; mCache = cache; mDelivery = delivery; mRequestQueue = requestQueue; mEta = eta; } /** * Terminate this NetworkDispatcher. Once terminated, no further requests will be processed. */ public void quit() { mQuit = true; } @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); Request request; while (true) { try { // Take a request from the queue. request = mQueue.take(); } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { // If the request was cancelled already, do not perform the network request. if (request.isCanceled()) { request.finish("network-dispatcher-cancelled-on-recieved"); continue; } else { request.addEvent("recieved-by-network-dispatcher"); } prepare(request); // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request); appendLogging(request, networkResponse); request.addEvent("parsing-network-response"); Response<?> response = request.parseNetworkResponse(networkResponse); if (response.isSuccess()) { updateSessionInfo(networkResponse.headers); mCache.put(request, response); mDelivery.postResponse(request, response); } else { if (SessionManager.recoverableError(response.error)) { request.addEvent("recoverable-session-error"); if (isSessionEndpoint(request)) { mDelivery.postResponse(request, response); } else { // Query the session manager to perform an update if (mEta.getSessionManager().recover(response.error)) { mRequestQueue.add(request); } else { mDelivery.postResponse(request, response); } } } else { request.addEvent("non-recoverable-error"); mDelivery.postResponse(request, response); } } } catch (EtaError e) { request.addEvent("network-error"); mDelivery.postResponse(request, Response.fromError(e)); } } } /** * Wrapper to check for session endpoint * @param request to check * @return true if session-endpoint, eler false */ private boolean isSessionEndpoint(Request request) { return request.getUrl().contains(Endpoint.SESSIONS); } /** * If it's a post to sessions, it's to create a new Session, then the API key is needed. * In any other case, just set the headers, with the current session token and signature. * @param request */ private void prepare(Request<?> request) { request.addEvent("preparing-headers"); boolean newSession = (request.getMethod() == Method.POST && request.getUrl().contains(Endpoint.SESSIONS)); if (!newSession) { Map<String, String> headers = new HashMap<String, String>(); String token = mEta.getSessionManager().getSession().getToken(); headers.put(HeaderUtils.X_TOKEN, token); String sha256 = HashUtils.sha256(mEta.getApiSecret() + token); headers.put(HeaderUtils.X_SIGNATURE, sha256); request.setHeaders(headers); } } private void appendLogging(Request req, NetworkResponse resp) { try { // Server Response JSONObject r = new JSONObject(); r.put("statuscode", resp.statusCode); if (resp.headers != null) { r.put("headers", new JSONObject(resp.headers)); } req.getNetworkLog().put("response", r); } catch (JSONException e) { EtaLog.e(TAG, "", e); } } /** * Method checks headers to find X-Token and X-Token-Expires.<br> * If they do not exist, nothing happens as the call has a wrong endpoint, or other * non-API regarding error. If they do exist, then they are checked by the Session * to find out if there are any changes. * @param headers to check for new token. */ private void updateSessionInfo(Map<String, String> headers) { String token = headers.get(HeaderUtils.X_TOKEN); String expire = headers.get(HeaderUtils.X_TOKEN_EXPIRES); if (!(token == null || expire == null)) { mEta.getSessionManager().updateTokens(token, expire); } } }