1 package org.arvados.sdk.java;
3 import com.google.api.client.http.javanet.*;
4 import com.google.api.client.http.ByteArrayContent;
5 import com.google.api.client.http.GenericUrl;
6 import com.google.api.client.http.HttpContent;
7 import com.google.api.client.http.HttpRequest;
8 import com.google.api.client.http.HttpRequestFactory;
9 import com.google.api.client.http.HttpTransport;
10 import com.google.api.client.http.UriTemplate;
11 import com.google.api.client.json.JsonFactory;
12 import com.google.api.client.json.jackson2.JacksonFactory;
13 import com.google.api.client.util.Maps;
14 import com.google.api.services.discovery.Discovery;
15 import com.google.api.services.discovery.model.JsonSchema;
16 import com.google.api.services.discovery.model.RestDescription;
17 import com.google.api.services.discovery.model.RestMethod;
18 import com.google.api.services.discovery.model.RestMethod.Request;
19 import com.google.api.services.discovery.model.RestResource;
21 import java.math.BigDecimal;
22 import java.math.BigInteger;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.List;
30 import org.apache.log4j.Logger;
31 import org.json.simple.JSONArray;
32 import org.json.simple.JSONObject;
35 * This class provides a java SDK interface to Arvados API server.
37 * Please refer to http://doc.arvados.org/api/ to learn about the
38 * various resources and methods exposed by the API server.
42 public class Arvados {
43 // HttpTransport and JsonFactory are thread-safe. So, use global instances.
44 private HttpTransport httpTransport;
45 private final JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
47 private String arvadosApiToken;
48 private String arvadosApiHost;
49 private boolean arvadosApiHostInsecure;
51 private String arvadosRootUrl;
53 private static final Logger logger = Logger.getLogger(Arvados.class);
55 // Get it once and reuse on the call requests
56 RestDescription restDescription = null;
57 String apiName = null;
58 String apiVersion = null;
60 public Arvados (String apiName, String apiVersion) throws Exception {
61 this (apiName, apiVersion, null, null, null);
64 public Arvados (String apiName, String apiVersion, String token,
65 String host, String hostInsecure) throws Exception {
66 this.apiName = apiName;
67 this.apiVersion = apiVersion;
69 // Read needed environmental variables if they are not passed
71 arvadosApiToken = token;
73 arvadosApiToken = System.getenv().get("ARVADOS_API_TOKEN");
74 if (arvadosApiToken == null) {
75 throw new Exception("Missing environment variable: ARVADOS_API_TOKEN");
80 arvadosApiHost = host;
82 arvadosApiHost = System.getenv().get("ARVADOS_API_HOST");
83 if (arvadosApiHost == null) {
84 throw new Exception("Missing environment variable: ARVADOS_API_HOST");
87 arvadosRootUrl = "https://" + arvadosApiHost;
88 arvadosRootUrl += (arvadosApiHost.endsWith("/")) ? "" : "/";
90 if (hostInsecure != null) {
91 arvadosApiHostInsecure = Boolean.valueOf(hostInsecure);
93 arvadosApiHostInsecure =
94 "true".equals(System.getenv().get("ARVADOS_API_HOST_INSECURE")) ? true : false;
97 // Create HTTP_TRANSPORT object
98 NetHttpTransport.Builder builder = new NetHttpTransport.Builder();
99 if (arvadosApiHostInsecure) {
100 builder.doNotValidateCertificate();
102 httpTransport = builder.build();
104 // initialize rest description
105 restDescription = loadArvadosApi();
109 * Make a call to API server with the provide call information.
110 * @param resourceName
116 public Map call(String resourceName, String methodName,
117 Map<String, Object> paramsMap) throws Exception {
118 RestMethod method = getMatchingMethod(resourceName, methodName);
120 HashMap<String, Object> parameters = loadParameters(paramsMap, method);
122 GenericUrl url = new GenericUrl(UriTemplate.expand(
123 arvadosRootUrl + restDescription.getBasePath() + method.getPath(),
127 // construct the request
128 HttpRequestFactory requestFactory;
129 requestFactory = httpTransport.createRequestFactory();
131 // possibly required content
132 HttpContent content = null;
134 if (!method.getHttpMethod().equals("GET") &&
135 !method.getHttpMethod().equals("DELETE")) {
136 String objectName = resourceName.substring(0, resourceName.length()-1);
137 Object requestBody = paramsMap.get(objectName);
138 if (requestBody == null) {
139 error("POST method requires content object " + objectName);
142 content = new ByteArrayContent("application/json",((String)requestBody).getBytes());
145 HttpRequest request =
146 requestFactory.buildRequest(method.getHttpMethod(), url, content);
149 List<String> authHeader = new ArrayList<String>();
150 authHeader.add("OAuth2 " + arvadosApiToken);
151 request.getHeaders().put("Authorization", authHeader);
152 String response = request.execute().parseAsString();
154 Map responseMap = jsonFactory.createJsonParser(response).parse(HashMap.class);
156 logger.debug(responseMap);
159 } catch (Exception e) {
166 * Get all supported resources by the API
169 public Set<String> getAvailableResourses() {
170 return (restDescription.getResources().keySet());
174 * Get all supported method names for the given resource
175 * @param resourceName
179 public Set<String> getAvailableMethodsForResourse(String resourceName)
181 Map<String, RestMethod> methodMap = getMatchingMethodMap (resourceName);
182 return (methodMap.keySet());
186 * Get the parameters for the method in the resource sought.
187 * @param resourceName
192 public Map<String,List<String>> getAvailableParametersForMethod(String resourceName, String methodName)
194 RestMethod method = getMatchingMethod(resourceName, methodName);
195 Map<String, List<String>> parameters = new HashMap<String, List<String>>();
196 List<String> requiredParameters = new ArrayList<String>();
197 List<String> optionalParameters = new ArrayList<String>();
198 parameters.put ("required", requiredParameters);
199 parameters.put("optional", optionalParameters);
202 // get any request parameters
203 Request request = method.getRequest();
204 if (request != null) {
205 Object required = request.get("required");
206 Object requestProperties = request.get("properties");
207 if (requestProperties != null) {
208 if (requestProperties instanceof Map) {
209 Map properties = (Map)requestProperties;
210 Set<String> propertyKeys = properties.keySet();
211 for (String property : propertyKeys) {
212 if (Boolean.TRUE.equals(required)) {
213 requiredParameters.add(property);
215 optionalParameters.add(property);
222 // get other listed parameters
223 Map<String,JsonSchema> methodParameters = method.getParameters();
224 for (Map.Entry<String, JsonSchema> entry : methodParameters.entrySet()) {
225 if (Boolean.TRUE.equals(entry.getValue().getRequired())) {
226 requiredParameters.add(entry.getKey());
228 optionalParameters.add(entry.getKey());
231 } catch (Exception e){
238 private HashMap<String, Object> loadParameters(Map<String, Object> paramsMap,
239 RestMethod method) throws Exception {
240 HashMap<String, Object> parameters = Maps.newHashMap();
242 // required parameters
243 if (method.getParameterOrder() != null) {
244 for (String parameterName : method.getParameterOrder()) {
245 JsonSchema parameter = method.getParameters().get(parameterName);
246 if (Boolean.TRUE.equals(parameter.getRequired())) {
247 Object parameterValue = paramsMap.get(parameterName);
248 if (parameterValue == null) {
249 error("missing required parameter: " + parameter);
251 putParameter(null, parameters, parameterName, parameter, parameterValue);
257 for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
258 String parameterName = entry.getKey();
259 Object parameterValue = entry.getValue();
261 if (parameterName.equals("contentType")) {
262 if (method.getHttpMethod().equals("GET") || method.getHttpMethod().equals("DELETE")) {
263 error("HTTP content type cannot be specified for this method: " + parameterName);
266 JsonSchema parameter = null;
267 if (restDescription.getParameters() != null) {
268 parameter = restDescription.getParameters().get(parameterName);
270 if (parameter == null && method.getParameters() != null) {
271 parameter = method.getParameters().get(parameterName);
273 putParameter(parameterName, parameters, parameterName, parameter, parameterValue);
280 private RestMethod getMatchingMethod(String resourceName, String methodName)
282 Map<String, RestMethod> methodMap = getMatchingMethodMap(resourceName);
284 if (methodName == null) {
285 error("missing method name");
289 methodMap == null ? null : methodMap.get(methodName);
290 if (method == null) {
291 error("method not found: ");
297 private Map<String, RestMethod> getMatchingMethodMap(String resourceName)
299 if (resourceName == null) {
300 error("missing resource name");
303 Map<String, RestMethod> methodMap = null;
304 Map<String, RestResource> resources = restDescription.getResources();
305 RestResource resource = resources.get(resourceName);
306 if (resource == null) {
307 error("resource not found");
309 methodMap = resource.getMethods();
314 * Not thread-safe. So, create for each request.
320 private RestDescription loadArvadosApi()
325 Discovery.Builder discoveryBuilder =
326 new Discovery.Builder(httpTransport, jsonFactory, null);
328 discoveryBuilder.setRootUrl(arvadosRootUrl);
329 discoveryBuilder.setApplicationName(apiName);
331 discovery = discoveryBuilder.build();
333 return discovery.apis().getRest(apiName, apiVersion).execute();
334 } catch (Exception e) {
341 * Convert the input parameter into its equivalent json string.
342 * Add this json string value to the parameters map to be sent to server.
345 * @param parameterName
347 * @param parameterValue
350 private void putParameter(String argName, Map<String, Object> parameters,
351 String parameterName, JsonSchema parameter, Object parameterValue)
353 Object value = parameterValue;
354 if (parameter != null) {
355 if ("boolean".equals(parameter.getType())) {
356 value = Boolean.valueOf(parameterValue.toString());
357 } else if ("number".equals(parameter.getType())) {
358 value = new BigDecimal(parameterValue.toString());
359 } else if ("integer".equals(parameter.getType())) {
360 value = new BigInteger(parameterValue.toString());
361 } else if ("float".equals(parameter.getType())) {
362 value = new BigDecimal(parameterValue.toString());
363 } else if ("Java.util.Calendar".equals(parameter.getType())) {
364 value = new BigDecimal(parameterValue.toString());
365 } else if (("array".equals(parameter.getType())) ||
366 ("Array".equals(parameter.getType()))) {
367 if (parameterValue.getClass().isArray()){
368 value = getJsonValueFromArrayType(parameterValue);
369 } else if (List.class.isAssignableFrom(parameterValue.getClass())) {
370 value = getJsonValueFromListType(parameterValue);
372 } else if (("Hash".equals(parameter.getType())) ||
373 ("hash".equals(parameter.getType()))) {
374 value = getJsonValueFromMapType(parameterValue);
376 if (parameterValue.getClass().isArray()){
377 value = getJsonValueFromArrayType(parameterValue);
378 } else if (List.class.isAssignableFrom(parameterValue.getClass())) {
379 value = getJsonValueFromListType(parameterValue);
380 } else if (Map.class.isAssignableFrom(parameterValue.getClass())) {
381 value = getJsonValueFromMapType(parameterValue);
386 parameters.put(parameterName, value);
390 * Convert the given input array into json string before sending to server.
391 * @param parameterValue
394 private String getJsonValueFromArrayType (Object parameterValue) {
395 String arrayStr = Arrays.deepToString((Object[])parameterValue);
397 // we can expect either an array of array objects or an array of objects
398 if (arrayStr.startsWith("[[") && arrayStr.endsWith("]]")) {
399 Object[][] array = new Object[1][];
400 arrayStr = arrayStr.substring(2, arrayStr.length()-2);
401 String jsonStr = getJsonStringForArrayStr(arrayStr);
402 String value = "[" + jsonStr + "]";
405 arrayStr = arrayStr.substring(1, arrayStr.length()-1);
406 return (getJsonStringForArrayStr(arrayStr));
410 private String getJsonStringForArrayStr(String arrayStr) {
411 Object[] array = arrayStr.split(",");
412 Object[] trimmedArray = new Object[array.length];
413 for (int i=0; i<array.length; i++){
414 trimmedArray[i] = array[i].toString().trim();
416 String value = JSONArray.toJSONString(Arrays.asList(trimmedArray));
421 * Convert the given input List into json string before sending to server.
422 * @param parameterValue
425 private String getJsonValueFromListType (Object parameterValue) {
426 List paramList = (List)parameterValue;
427 Object[] array = new Object[paramList.size()];
428 Arrays.deepToString(paramList.toArray(array));
429 return (getJsonValueFromArrayType(array));
433 * Convert the given input map into json string before sending to server.
434 * @param parameterValue
437 private String getJsonValueFromMapType (Object parameterValue) {
438 JSONObject json = new JSONObject((Map)parameterValue);
439 return json.toString();
442 private static void error(String detail) throws Exception {
443 String errorDetail = "ERROR: " + detail;
445 logger.debug(errorDetail);
446 throw new Exception(errorDetail);
449 public static void main(String[] args){
450 System.out.println("Welcome to Arvados Java SDK.");
451 System.out.println("Please refer to http://doc.arvados.org/sdk/java/index.html to get started with the the SDK.");