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