001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.oozie.client.rest; 019 020 import org.apache.oozie.client.BulkResponse; 021 import org.apache.oozie.client.BundleJob; 022 import org.apache.oozie.client.CoordinatorAction; 023 import org.apache.oozie.client.CoordinatorJob; 024 import org.apache.oozie.client.WorkflowAction; 025 import org.apache.oozie.client.WorkflowJob; 026 import org.json.simple.JSONArray; 027 import org.json.simple.JSONObject; 028 029 import java.lang.reflect.InvocationHandler; 030 import java.lang.reflect.Method; 031 import java.lang.reflect.Proxy; 032 import java.util.ArrayList; 033 import java.util.Date; 034 import java.util.HashMap; 035 import java.util.List; 036 import java.util.Map; 037 038 /** 039 * JSON to bean converter for {@link WorkflowAction}, {@link WorkflowJob}, {@link CoordinatorAction} 040 * and {@link CoordinatorJob}. 041 * <p/> 042 * It uses JDK dynamic proxy to create bean instances. 043 */ 044 public class JsonToBean { 045 046 private static class Property { 047 String label; 048 Class type; 049 boolean isList; 050 051 public Property(String label, Class type) { 052 this(label, type, false); 053 } 054 055 public Property(String label, Class type, boolean isList) { 056 this.label = label; 057 this.type = type; 058 this.isList = isList; 059 } 060 } 061 062 private static final Map<String, Property> WF_JOB = new HashMap<String, Property>(); 063 private static final Map<String, Property> WF_ACTION = new HashMap<String, Property>(); 064 private static final Map<String, Property> COORD_JOB = new HashMap<String, Property>(); 065 private static final Map<String, Property> COORD_ACTION = new HashMap<String, Property>(); 066 private static final Map<String, Property> BUNDLE_JOB = new HashMap<String, Property>(); 067 private static final Map<String, Property> BULK_RESPONSE = new HashMap<String, Property>(); 068 069 static { 070 WF_ACTION.put("getId", new Property(JsonTags.WORKFLOW_ACTION_ID, String.class)); 071 WF_ACTION.put("getName", new Property(JsonTags.WORKFLOW_ACTION_NAME, String.class)); 072 WF_ACTION.put("getType", new Property(JsonTags.WORKFLOW_ACTION_TYPE, String.class)); 073 WF_ACTION.put("getConf", new Property(JsonTags.WORKFLOW_ACTION_CONF, String.class)); 074 WF_ACTION.put("getStatus", new Property(JsonTags.WORKFLOW_ACTION_STATUS, WorkflowAction.Status.class)); 075 WF_ACTION.put("getRetries", new Property(JsonTags.WORKFLOW_ACTION_RETRIES, Integer.TYPE)); 076 WF_ACTION.put("getStartTime", new Property(JsonTags.WORKFLOW_ACTION_START_TIME, Date.class)); 077 WF_ACTION.put("getEndTime", new Property(JsonTags.WORKFLOW_ACTION_END_TIME, Date.class)); 078 WF_ACTION.put("getTransition", new Property(JsonTags.WORKFLOW_ACTION_TRANSITION, String.class)); 079 WF_ACTION.put("getData", new Property(JsonTags.WORKFLOW_ACTION_DATA, String.class)); 080 WF_ACTION.put("getStats", new Property(JsonTags.WORKFLOW_ACTION_STATS, String.class)); 081 WF_ACTION.put("getExternalChildIDs", new Property(JsonTags.WORKFLOW_ACTION_EXTERNAL_CHILD_IDS, String.class)); 082 WF_ACTION.put("getExternalId", new Property(JsonTags.WORKFLOW_ACTION_EXTERNAL_ID, String.class)); 083 WF_ACTION.put("getExternalStatus", new Property(JsonTags.WORKFLOW_ACTION_EXTERNAL_STATUS, String.class)); 084 WF_ACTION.put("getTrackerUri", new Property(JsonTags.WORKFLOW_ACTION_TRACKER_URI, String.class)); 085 WF_ACTION.put("getConsoleUrl", new Property(JsonTags.WORKFLOW_ACTION_CONSOLE_URL, String.class)); 086 WF_ACTION.put("getErrorCode", new Property(JsonTags.WORKFLOW_ACTION_ERROR_CODE, String.class)); 087 WF_ACTION.put("getErrorMessage", new Property(JsonTags.WORKFLOW_ACTION_ERROR_MESSAGE, String.class)); 088 WF_ACTION.put("toString", new Property(JsonTags.TO_STRING, String.class)); 089 090 WF_JOB.put("getExternalId", new Property(JsonTags.WORKFLOW_EXTERNAL_ID, String.class)); 091 WF_JOB.put("getAppPath", new Property(JsonTags.WORKFLOW_APP_PATH, String.class)); 092 WF_JOB.put("getAppName", new Property(JsonTags.WORKFLOW_APP_NAME, String.class)); 093 WF_JOB.put("getId", new Property(JsonTags.WORKFLOW_ID, String.class)); 094 WF_JOB.put("getConf", new Property(JsonTags.WORKFLOW_CONF, String.class)); 095 WF_JOB.put("getStatus", new Property(JsonTags.WORKFLOW_STATUS, WorkflowJob.Status.class)); 096 WF_JOB.put("getLastModifiedTime", new Property(JsonTags.WORKFLOW_LAST_MOD_TIME, Date.class)); 097 WF_JOB.put("getCreatedTime", new Property(JsonTags.WORKFLOW_CREATED_TIME, Date.class)); 098 WF_JOB.put("getStartTime", new Property(JsonTags.WORKFLOW_CREATED_TIME, Date.class)); 099 WF_JOB.put("getEndTime", new Property(JsonTags.WORKFLOW_END_TIME, Date.class)); 100 WF_JOB.put("getUser", new Property(JsonTags.WORKFLOW_USER, String.class)); 101 WF_JOB.put("getGroup", new Property(JsonTags.WORKFLOW_GROUP, String.class)); 102 WF_JOB.put("getAcl", new Property(JsonTags.WORKFLOW_ACL, String.class)); 103 WF_JOB.put("getRun", new Property(JsonTags.WORKFLOW_RUN, Integer.TYPE)); 104 WF_JOB.put("getConsoleUrl", new Property(JsonTags.WORKFLOW_CONSOLE_URL, String.class)); 105 WF_JOB.put("getActions", new Property(JsonTags.WORKFLOW_ACTIONS, WorkflowAction.class, true)); 106 WF_JOB.put("getParentId", new Property(JsonTags.WORKFLOW_PARENT_ID, String.class)); 107 WF_JOB.put("toString", new Property(JsonTags.TO_STRING, String.class)); 108 109 COORD_ACTION.put("getId", new Property(JsonTags.COORDINATOR_ACTION_ID, String.class)); 110 COORD_ACTION.put("getJobId", new Property(JsonTags.COORDINATOR_JOB_ID, String.class)); 111 COORD_ACTION.put("getActionNumber", new Property(JsonTags.COORDINATOR_ACTION_NUMBER, Integer.TYPE)); 112 COORD_ACTION.put("getCreatedConf", new Property(JsonTags.COORDINATOR_ACTION_CREATED_CONF, String.class)); 113 COORD_ACTION.put("getCreatedTime", new Property(JsonTags.COORDINATOR_ACTION_CREATED_TIME, Date.class)); 114 COORD_ACTION.put("getNominalTime", new Property(JsonTags.COORDINATOR_ACTION_NOMINAL_TIME, Date.class)); 115 COORD_ACTION.put("getExternalId", new Property(JsonTags.COORDINATOR_ACTION_EXTERNALID, String.class)); 116 COORD_ACTION.put("getStatus", new Property(JsonTags.COORDINATOR_ACTION_STATUS, CoordinatorAction.Status.class)); 117 COORD_ACTION.put("getRunConf", new Property(JsonTags.COORDINATOR_ACTION_RUNTIME_CONF, String.class)); 118 COORD_ACTION 119 .put("getLastModifiedTime", new Property(JsonTags.COORDINATOR_ACTION_LAST_MODIFIED_TIME, Date.class)); 120 COORD_ACTION 121 .put("getMissingDependencies", new Property(JsonTags.COORDINATOR_ACTION_MISSING_DEPS, String.class)); 122 COORD_ACTION.put("getExternalStatus", new Property(JsonTags.COORDINATOR_ACTION_EXTERNAL_STATUS, String.class)); 123 COORD_ACTION.put("getTrackerUri", new Property(JsonTags.COORDINATOR_ACTION_TRACKER_URI, String.class)); 124 COORD_ACTION.put("getConsoleUrl", new Property(JsonTags.COORDINATOR_ACTION_CONSOLE_URL, String.class)); 125 COORD_ACTION.put("getErrorCode", new Property(JsonTags.COORDINATOR_ACTION_ERROR_CODE, String.class)); 126 COORD_ACTION.put("getErrorMessage", new Property(JsonTags.COORDINATOR_ACTION_ERROR_MESSAGE, String.class)); 127 COORD_ACTION.put("toString", new Property(JsonTags.TO_STRING, String.class)); 128 129 COORD_JOB.put("getAppPath", new Property(JsonTags.COORDINATOR_JOB_PATH, String.class)); 130 COORD_JOB.put("getAppName", new Property(JsonTags.COORDINATOR_JOB_NAME, String.class)); 131 COORD_JOB.put("getId", new Property(JsonTags.COORDINATOR_JOB_ID, String.class)); 132 COORD_JOB.put("getConf", new Property(JsonTags.COORDINATOR_JOB_CONF, String.class)); 133 COORD_JOB.put("getStatus", new Property(JsonTags.COORDINATOR_JOB_STATUS, CoordinatorJob.Status.class)); 134 COORD_JOB.put("getExecutionOrder", 135 new Property(JsonTags.COORDINATOR_JOB_EXECUTIONPOLICY, CoordinatorJob.Execution.class)); 136 COORD_JOB.put("getFrequency", new Property(JsonTags.COORDINATOR_JOB_FREQUENCY, Integer.TYPE)); 137 COORD_JOB.put("getTimeUnit", new Property(JsonTags.COORDINATOR_JOB_TIMEUNIT, CoordinatorJob.Timeunit.class)); 138 COORD_JOB.put("getTimeZone", new Property(JsonTags.COORDINATOR_JOB_TIMEZONE, String.class)); 139 COORD_JOB.put("getConcurrency", new Property(JsonTags.COORDINATOR_JOB_CONCURRENCY, Integer.TYPE)); 140 COORD_JOB.put("getTimeout", new Property(JsonTags.COORDINATOR_JOB_TIMEOUT, Integer.TYPE)); 141 COORD_JOB.put("getLastActionTime", new Property(JsonTags.COORDINATOR_JOB_LAST_ACTION_TIME, Date.class)); 142 COORD_JOB.put("getNextMaterializedTime", 143 new Property(JsonTags.COORDINATOR_JOB_NEXT_MATERIALIZED_TIME, Date.class)); 144 COORD_JOB.put("getStartTime", new Property(JsonTags.COORDINATOR_JOB_START_TIME, Date.class)); 145 COORD_JOB.put("getEndTime", new Property(JsonTags.COORDINATOR_JOB_END_TIME, Date.class)); 146 COORD_JOB.put("getPauseTime", new Property(JsonTags.COORDINATOR_JOB_PAUSE_TIME, Date.class)); 147 COORD_JOB.put("getUser", new Property(JsonTags.COORDINATOR_JOB_USER, String.class)); 148 COORD_JOB.put("getGroup", new Property(JsonTags.COORDINATOR_JOB_GROUP, String.class)); 149 COORD_JOB.put("getAcl", new Property(JsonTags.COORDINATOR_JOB_ACL, String.class)); 150 COORD_JOB.put("getConsoleUrl", new Property(JsonTags.COORDINATOR_JOB_CONSOLE_URL, String.class)); 151 COORD_JOB.put("getActions", new Property(JsonTags.COORDINATOR_ACTIONS, CoordinatorAction.class, true)); 152 COORD_JOB.put("toString", new Property(JsonTags.TO_STRING, String.class)); 153 154 BUNDLE_JOB.put("getActions", new Property(JsonTags.COORDINATOR_ACTIONS, CoordinatorAction.class, true)); 155 156 BUNDLE_JOB.put("getAppPath",new Property(JsonTags.BUNDLE_JOB_PATH, String.class)); 157 BUNDLE_JOB.put("getAppName",new Property(JsonTags.BUNDLE_JOB_NAME, String.class)); 158 BUNDLE_JOB.put("getId",new Property(JsonTags.BUNDLE_JOB_ID, String.class)); 159 BUNDLE_JOB.put("getExternalId",new Property(JsonTags.BUNDLE_JOB_EXTERNAL_ID, String.class)); 160 BUNDLE_JOB.put("getConf",new Property(JsonTags.BUNDLE_JOB_CONF, String.class)); 161 BUNDLE_JOB.put("getStatus",new Property(JsonTags.BUNDLE_JOB_STATUS, BundleJob.Status.class)); 162 BUNDLE_JOB.put("getTimeUnit",new Property(JsonTags.BUNDLE_JOB_TIMEUNIT, BundleJob.Timeunit.class)); 163 BUNDLE_JOB.put("getTimeout",new Property(JsonTags.BUNDLE_JOB_TIMEOUT, Integer.TYPE)); 164 BUNDLE_JOB.put("getKickoffTime",new Property(JsonTags.BUNDLE_JOB_KICKOFF_TIME, Date.class)); 165 BUNDLE_JOB.put("getStartTime",new Property(JsonTags.BUNDLE_JOB_START_TIME, Date.class)); 166 BUNDLE_JOB.put("getEndTime",new Property(JsonTags.BUNDLE_JOB_END_TIME, Date.class)); 167 BUNDLE_JOB.put("getPauseTime",new Property(JsonTags.BUNDLE_JOB_PAUSE_TIME, Date.class)); 168 BUNDLE_JOB.put("getCreatedTime",new Property(JsonTags.BUNDLE_JOB_CREATED_TIME, Date.class)); 169 BUNDLE_JOB.put("getUser",new Property(JsonTags.BUNDLE_JOB_USER, String.class)); 170 BUNDLE_JOB.put("getGroup",new Property(JsonTags.BUNDLE_JOB_GROUP, String.class)); 171 BUNDLE_JOB.put("getConsoleUrl",new Property(JsonTags.BUNDLE_JOB_CONSOLE_URL, String.class)); 172 BUNDLE_JOB.put("getCoordinators",new Property(JsonTags.BUNDLE_COORDINATOR_JOBS, CoordinatorJob.class, true)); 173 BUNDLE_JOB.put("toString", new Property(JsonTags.TO_STRING, String.class)); 174 175 BULK_RESPONSE.put("getBundle", new Property(JsonTags.BULK_RESPONSE_BUNDLE, BundleJob.class, true)); 176 BULK_RESPONSE.put("getCoordinator", new Property(JsonTags.BULK_RESPONSE_COORDINATOR, CoordinatorJob.class, true)); 177 BULK_RESPONSE.put("getAction", new Property(JsonTags.BULK_RESPONSE_ACTION, CoordinatorAction.class, true)); 178 179 } 180 181 /** 182 * The dynamic proxy invocation handler used to convert JSON values to bean properties using a mapping. 183 */ 184 private static class JsonInvocationHandler implements InvocationHandler { 185 private final Map<String, Property> mapping; 186 private final JSONObject json; 187 188 /** 189 * Invocation handler constructor. 190 * 191 * @param mapping property to JSON/type-info mapping. 192 * @param json the json object to back the property values. 193 */ 194 public JsonInvocationHandler(Map<String, Property> mapping, JSONObject json) { 195 this.mapping = mapping; 196 this.json = json; 197 } 198 199 @Override 200 public Object invoke(Object o, Method method, Object[] objects) throws Throwable { 201 Property prop = mapping.get(method.getName()); 202 if (prop == null) { 203 throw new RuntimeException("Undefined method mapping: " + method.getName()); 204 } 205 if (prop.isList) { 206 if (prop.type == WorkflowAction.class) { 207 return createWorkflowActionList((JSONArray) json.get(prop.label)); 208 } 209 else if (prop.type == CoordinatorAction.class) { 210 return createCoordinatorActionList((JSONArray) json.get(prop.label)); 211 } 212 else if (prop.type == CoordinatorJob.class) { 213 return createCoordinatorJobList((JSONArray) json.get(prop.label)); 214 } 215 else { 216 throw new RuntimeException("Unsupported list type : " + prop.type.getSimpleName()); 217 } 218 } 219 else { 220 return parseType(prop.type, json.get(prop.label)); 221 } 222 } 223 224 @SuppressWarnings("unchecked") 225 private Object parseType(Class type, Object obj) { 226 if (type == String.class) { 227 return obj; 228 } 229 else if (type == Integer.TYPE) { 230 return (obj != null) ? new Integer(((Long) obj).intValue()) : new Integer(0); 231 } 232 else if (type == Long.TYPE) { 233 return (obj != null) ? obj : new Long(0); 234 } 235 else if (type == Date.class) { 236 return JsonUtils.parseDateRfc822((String) obj); 237 } 238 else if (type.isEnum()) { 239 return Enum.valueOf(type, (String) obj); 240 } 241 else if (type == WorkflowAction.class) { 242 return createWorkflowAction((JSONObject) obj); 243 } 244 else { 245 throw new RuntimeException("Unsupported type : " + type.getSimpleName()); 246 } 247 } 248 } 249 250 /** 251 * Creates a workflow action bean from a JSON object. 252 * 253 * @param json json object. 254 * @return a workflow action bean populated with the JSON object values. 255 */ 256 public static WorkflowAction createWorkflowAction(JSONObject json) { 257 return (WorkflowAction) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(), 258 new Class[]{WorkflowAction.class}, 259 new JsonInvocationHandler(WF_ACTION, json)); 260 } 261 262 /** 263 * Creates a list of workflow action beans from a JSON array. 264 * 265 * @param json json array. 266 * @return a list of workflow action beans from a JSON array. 267 */ 268 public static List<WorkflowAction> createWorkflowActionList(JSONArray json) { 269 List<WorkflowAction> list = new ArrayList<WorkflowAction>(); 270 for (Object obj : json) { 271 list.add(createWorkflowAction((JSONObject) obj)); 272 } 273 return list; 274 } 275 276 /** 277 * Creates a workflow job bean from a JSON object. 278 * 279 * @param json json object. 280 * @return a workflow job bean populated with the JSON object values. 281 */ 282 public static WorkflowJob createWorkflowJob(JSONObject json) { 283 return (WorkflowJob) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(), 284 new Class[]{WorkflowJob.class}, 285 new JsonInvocationHandler(WF_JOB, json)); 286 } 287 288 /** 289 * Creates a list of workflow job beans from a JSON array. 290 * 291 * @param json json array. 292 * @return a list of workflow job beans from a JSON array. 293 */ 294 public static List<WorkflowJob> createWorkflowJobList(JSONArray json) { 295 List<WorkflowJob> list = new ArrayList<WorkflowJob>(); 296 for (Object obj : json) { 297 list.add(createWorkflowJob((JSONObject) obj)); 298 } 299 return list; 300 } 301 302 /** 303 * Creates a coordinator action bean from a JSON object. 304 * 305 * @param json json object. 306 * @return a coordinator action bean populated with the JSON object values. 307 */ 308 public static CoordinatorAction createCoordinatorAction(JSONObject json) { 309 return (CoordinatorAction) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(), 310 new Class[]{CoordinatorAction.class}, 311 new JsonInvocationHandler(COORD_ACTION, json)); 312 } 313 314 /** 315 * Creates a list of coordinator action beans from a JSON array. 316 * 317 * @param json json array. 318 * @return a list of coordinator action beans from a JSON array. 319 */ 320 public static List<CoordinatorAction> createCoordinatorActionList(JSONArray json) { 321 List<CoordinatorAction> list = new ArrayList<CoordinatorAction>(); 322 for (Object obj : json) { 323 list.add(createCoordinatorAction((JSONObject) obj)); 324 } 325 return list; 326 } 327 328 /** 329 * Creates a coordinator job bean from a JSON object. 330 * 331 * @param json json object. 332 * @return a coordinator job bean populated with the JSON object values. 333 */ 334 public static CoordinatorJob createCoordinatorJob(JSONObject json) { 335 return (CoordinatorJob) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(), 336 new Class[]{CoordinatorJob.class}, 337 new JsonInvocationHandler(COORD_JOB, json)); 338 } 339 340 /** 341 * Creates a list of coordinator job beans from a JSON array. 342 * 343 * @param json json array. 344 * @return a list of coordinator job beans from a JSON array. 345 */ 346 public static List<CoordinatorJob> createCoordinatorJobList(JSONArray json) { 347 List<CoordinatorJob> list = new ArrayList<CoordinatorJob>(); 348 for (Object obj : json) { 349 list.add(createCoordinatorJob((JSONObject) obj)); 350 } 351 return list; 352 } 353 354 /** 355 * Creates a bundle job bean from a JSON object. 356 * 357 * @param json json object. 358 * @return a bundle job bean populated with the JSON object values. 359 */ 360 public static BundleJob createBundleJob(JSONObject json) { 361 return (BundleJob) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(), 362 new Class[]{BundleJob.class}, 363 new JsonInvocationHandler(BUNDLE_JOB, json)); 364 } 365 366 /** 367 * Creates a list of bundle job beans from a JSON array. 368 * 369 * @param json json array. 370 * @return a list of bundle job beans from a JSON array. 371 */ 372 public static List<BundleJob> createBundleJobList(JSONArray json) { 373 List<BundleJob> list = new ArrayList<BundleJob>(); 374 for (Object obj : json) { 375 list.add(createBundleJob((JSONObject) obj)); 376 } 377 return list; 378 } 379 380 /** 381 * Creates a list of bulk response beans from a JSON array. 382 * 383 * @param json json array. 384 * @return a list of bulk response beans from a JSON array. 385 */ 386 public static List<BulkResponse> createBulkResponseList(JSONArray json) { 387 List<BulkResponse> list = new ArrayList<BulkResponse>(); 388 for (Object obj : json) { 389 BulkResponse bulkObj = (BulkResponse) Proxy.newProxyInstance 390 (JsonToBean.class.getClassLoader(), new Class[]{BulkResponse.class}, 391 new JsonInvocationHandler(BULK_RESPONSE, (JSONObject) obj)); 392 list.add(bulkObj); 393 } 394 return list; 395 } 396 }