00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 package org.xbmc.android.jsonrpc.io;
00023
00024 import java.io.BufferedReader;
00025 import java.io.IOException;
00026 import java.io.InputStreamReader;
00027 import java.io.OutputStreamWriter;
00028 import java.io.UnsupportedEncodingException;
00029 import java.net.HttpURLConnection;
00030 import java.net.MalformedURLException;
00031 import java.net.SocketTimeoutException;
00032 import java.net.URL;
00033
00034 import org.codehaus.jackson.JsonProcessingException;
00035 import org.codehaus.jackson.map.ObjectMapper;
00036 import org.codehaus.jackson.node.ObjectNode;
00037 import org.codehaus.jackson.node.TextNode;
00038
00039 import android.util.Log;
00040
00050 public class JsonApiRequest {
00051
00052 private static final String TAG = JsonApiRequest.class.getSimpleName();
00053
00054 private static final int REQUEST_TIMEOUT = 5000;
00055 private static final ObjectMapper OM = new ObjectMapper();
00056
00066 public static ObjectNode execute(String url, String user, String pass, ObjectNode entity) throws ApiException {
00067 try {
00068 String response = postRequest(new URL(url), user, pass, entity.toString());
00069 return parseResponse(response);
00070 } catch (MalformedURLException e) {
00071 throw new ApiException(ApiException.MALFORMED_URL, e.getMessage(), e);
00072 }
00073 }
00074
00084 private static String postRequest(URL url, String user, String pass, String entity) throws ApiException {
00085 try {
00086 final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
00087
00088 conn.setRequestMethod("POST");
00089
00090
00091 if (user != null && !user.isEmpty() && pass != null && !pass.isEmpty()) {
00092 final String token = Base64.encodeToString((user + ":" + pass).getBytes(), false);
00093 conn.setRequestProperty("Authorization", "Basic " + token);
00094 }
00095 conn.setRequestProperty("Content-Type", "application/json");
00096 conn.setRequestProperty("User-Agent", buildUserAgent());
00097
00098 conn.setConnectTimeout(REQUEST_TIMEOUT);
00099 conn.setReadTimeout(REQUEST_TIMEOUT);
00100
00101 conn.setDoOutput(true);
00102
00103 try {
00104 OutputStreamWriter output = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
00105 output.write(entity);
00106 output.close();
00107 } catch (UnsupportedEncodingException e) {
00108 throw new ApiException(ApiException.UNSUPPORTED_ENCODING, "Unable to convert request to UTF-8", e);
00109 }
00110
00111 Log.i(TAG, "POST request: " + conn.getURL());
00112 Log.i(TAG, "POST entity:" + entity);
00113
00114 StringBuilder response = new StringBuilder();
00115 BufferedReader reader = null;
00116
00117 final int code = conn.getResponseCode();
00118 if (code == 200) {
00119 try {
00120 reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"), 8192);
00121 String line;
00122 while ((line = reader.readLine()) != null) {
00123 response.append(line);
00124 }
00125 } catch (UnsupportedEncodingException e) {
00126 throw new ApiException(ApiException.UNSUPPORTED_ENCODING, "Unable to convert HTTP response to UTF-8", e);
00127 } finally {
00128 if (reader != null) {
00129 reader.close();
00130 }
00131 }
00132
00133 Log.i(TAG, "POST response: " + response.toString());
00134 return response.toString();
00135
00136 } else {
00137 switch (code) {
00138 case 400:
00139 throw new ApiException(ApiException.HTTP_BAD_REQUEST, "Server says \"400 Bad HTTP request\".");
00140 case 401:
00141 throw new ApiException(ApiException.HTTP_UNAUTHORIZED, "Server says \"401 Unauthorized\".");
00142 case 403:
00143 throw new ApiException(ApiException.HTTP_FORBIDDEN, "Server says \"403 Forbidden\".");
00144 case 404:
00145 throw new ApiException(ApiException.HTTP_NOT_FOUND, "Server says \"404 Not Found\".");
00146 default:
00147 if (code >= 100 && code < 200) {
00148 throw new ApiException(ApiException.HTTP_INFO, "Server returned informational code " + code + " instead of 200.");
00149 } else if (code >= 200 && code < 300) {
00150 throw new ApiException(ApiException.HTTP_SUCCESS, "Server returned success code " + code + " instead of 200.");
00151 } else if (code >= 300 && code < 400) {
00152 throw new ApiException(ApiException.HTTP_REDIRECTION, "Server returned redirection code " + code + " instead of 200.");
00153 } else if (code >= 400 && code < 500) {
00154 throw new ApiException(ApiException.HTTP_CLIENT_ERROR, "Server returned client error " + code + ".");
00155 } else if (code >= 500 && code < 600) {
00156 throw new ApiException(ApiException.HTTP_SERVER_ERROR, "Server returned server error " + code + ".");
00157 } else {
00158 throw new ApiException(ApiException.HTTP_UNKNOWN, "Server returned unspecified code " + code + ".");
00159 }
00160 }
00161 }
00162
00163 } catch (SocketTimeoutException e) {
00164 throw new ApiException(ApiException.IO_SOCKETTIMEOUT, e.getMessage(), e);
00165 } catch (IOException e) {
00166 throw new ApiException(ApiException.IO_EXCEPTION, e.getMessage(), e);
00167 }
00168 }
00169
00181 private static ObjectNode parseResponse(String response) throws ApiException {
00182 try {
00183 final ObjectNode node = (ObjectNode) OM.readTree(response.toString());
00184
00185 if (node.has("error")) {
00186 if (node.get("error").isTextual()) {
00187 final TextNode error = (TextNode) node.get("error");
00188 Log.e(TAG, "[JSON-RPC] " + error.getTextValue());
00189 Log.e(TAG, "[JSON-RPC] " + response);
00190 throw new ApiException(ApiException.API_ERROR, "Error: " + error.getTextValue(), null);
00191 } else {
00192 final ObjectNode error = (ObjectNode) node.get("error");
00193 Log.e(TAG, "[JSON-RPC] " + error.get("message").getTextValue());
00194 Log.e(TAG, "[JSON-RPC] " + response);
00195 throw new ApiException(ApiException.API_ERROR, "Error " + error.get("code").getIntValue() + ": " + error.get("message").getTextValue(), null);
00196 }
00197 }
00198
00199 if (!node.has("result")) {
00200 Log.e(TAG, "[JSON-RPC] " + response);
00201 throw new ApiException(ApiException.RESPONSE_ERROR, "Neither result nor error object found in response.", null);
00202 }
00203
00204 if (node.get("result").isNull()) {
00205 return null;
00206 }
00207
00208 return node;
00209 } catch (JsonProcessingException e) {
00210 throw new ApiException(ApiException.JSON_EXCEPTION, "Parse error: " + e.getMessage(), e);
00211 } catch (IOException e) {
00212 throw new ApiException(ApiException.JSON_EXCEPTION, "Parse error: " + e.getMessage(), e);
00213 }
00214 }
00215
00223 private static String buildUserAgent() {
00224 return "xbmc-jsonrpclib-android";
00225 }
00226 }