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.JMSConnectionInfo;
025    import org.apache.oozie.client.JMSConnectionInfoWrapper;
026    import org.apache.oozie.client.WorkflowAction;
027    import org.apache.oozie.client.WorkflowJob;
028    import org.apache.oozie.AppType;
029    import org.json.simple.JSONArray;
030    import org.json.simple.JSONObject;
031    import org.json.simple.JSONValue;
032    
033    import java.lang.reflect.InvocationHandler;
034    import java.lang.reflect.Method;
035    import java.lang.reflect.Proxy;
036    import java.util.ArrayList;
037    import java.util.Date;
038    import java.util.HashMap;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Properties;
042    import java.util.Set;
043    
044    /**
045     * JSON to bean converter for {@link WorkflowAction}, {@link WorkflowJob}, {@link CoordinatorAction}
046     * and {@link CoordinatorJob}.
047     * <p/>
048     * It uses JDK dynamic proxy to create bean instances.
049     */
050    @SuppressWarnings("rawtypes")
051    public class JsonToBean {
052    
053        private static class Property {
054            String label;
055            Class type;
056            boolean isList;
057    
058            public Property(String label, Class type) {
059                this(label, type, false);
060            }
061    
062            public Property(String label, Class type, boolean isList) {
063                this.label = label;
064                this.type = type;
065                this.isList = isList;
066            }
067        }
068    
069        private static final Map<String, Property> WF_JOB = new HashMap<String, Property>();
070        private static final Map<String, Property> WF_ACTION = new HashMap<String, Property>();
071        private static final Map<String, Property> COORD_JOB = new HashMap<String, Property>();
072        private static final Map<String, Property> COORD_ACTION = new HashMap<String, Property>();
073        private static final Map<String, Property> BUNDLE_JOB = new HashMap<String, Property>();
074        private static final Map<String, Property> BULK_RESPONSE = new HashMap<String, Property>();
075        private static final Map<String, Property> JMS_CONNECTION_INFO = new HashMap<String, Property>();
076    
077        static {
078            WF_ACTION.put("getId", new Property(JsonTags.WORKFLOW_ACTION_ID, String.class));
079            WF_ACTION.put("getName", new Property(JsonTags.WORKFLOW_ACTION_NAME, String.class));
080            WF_ACTION.put("getType", new Property(JsonTags.WORKFLOW_ACTION_TYPE, String.class));
081            WF_ACTION.put("getConf", new Property(JsonTags.WORKFLOW_ACTION_CONF, String.class));
082            WF_ACTION.put("getStatus", new Property(JsonTags.WORKFLOW_ACTION_STATUS, WorkflowAction.Status.class));
083            WF_ACTION.put("getRetries", new Property(JsonTags.WORKFLOW_ACTION_RETRIES, Integer.TYPE));
084            WF_ACTION.put("getStartTime", new Property(JsonTags.WORKFLOW_ACTION_START_TIME, Date.class));
085            WF_ACTION.put("getEndTime", new Property(JsonTags.WORKFLOW_ACTION_END_TIME, Date.class));
086            WF_ACTION.put("getTransition", new Property(JsonTags.WORKFLOW_ACTION_TRANSITION, String.class));
087            WF_ACTION.put("getData", new Property(JsonTags.WORKFLOW_ACTION_DATA, String.class));
088            WF_ACTION.put("getStats", new Property(JsonTags.WORKFLOW_ACTION_STATS, String.class));
089            WF_ACTION.put("getExternalChildIDs", new Property(JsonTags.WORKFLOW_ACTION_EXTERNAL_CHILD_IDS, String.class));
090            WF_ACTION.put("getExternalId", new Property(JsonTags.WORKFLOW_ACTION_EXTERNAL_ID, String.class));
091            WF_ACTION.put("getExternalStatus", new Property(JsonTags.WORKFLOW_ACTION_EXTERNAL_STATUS, String.class));
092            WF_ACTION.put("getTrackerUri", new Property(JsonTags.WORKFLOW_ACTION_TRACKER_URI, String.class));
093            WF_ACTION.put("getConsoleUrl", new Property(JsonTags.WORKFLOW_ACTION_CONSOLE_URL, String.class));
094            WF_ACTION.put("getErrorCode", new Property(JsonTags.WORKFLOW_ACTION_ERROR_CODE, String.class));
095            WF_ACTION.put("getErrorMessage", new Property(JsonTags.WORKFLOW_ACTION_ERROR_MESSAGE, String.class));
096            WF_ACTION.put("toString", new Property(JsonTags.TO_STRING, String.class));
097    
098            WF_JOB.put("getExternalId", new Property(JsonTags.WORKFLOW_EXTERNAL_ID, String.class));
099            WF_JOB.put("getAppPath", new Property(JsonTags.WORKFLOW_APP_PATH, String.class));
100            WF_JOB.put("getAppName", new Property(JsonTags.WORKFLOW_APP_NAME, String.class));
101            WF_JOB.put("getId", new Property(JsonTags.WORKFLOW_ID, String.class));
102            WF_JOB.put("getConf", new Property(JsonTags.WORKFLOW_CONF, String.class));
103            WF_JOB.put("getStatus", new Property(JsonTags.WORKFLOW_STATUS, WorkflowJob.Status.class));
104            WF_JOB.put("getLastModifiedTime", new Property(JsonTags.WORKFLOW_LAST_MOD_TIME, Date.class));
105            WF_JOB.put("getCreatedTime", new Property(JsonTags.WORKFLOW_CREATED_TIME, Date.class));
106            WF_JOB.put("getStartTime", new Property(JsonTags.WORKFLOW_START_TIME, Date.class));
107            WF_JOB.put("getEndTime", new Property(JsonTags.WORKFLOW_END_TIME, Date.class));
108            WF_JOB.put("getUser", new Property(JsonTags.WORKFLOW_USER, String.class));
109            WF_JOB.put("getGroup", new Property(JsonTags.WORKFLOW_GROUP, String.class));
110            WF_JOB.put("getAcl", new Property(JsonTags.WORKFLOW_ACL, String.class));
111            WF_JOB.put("getRun", new Property(JsonTags.WORKFLOW_RUN, Integer.TYPE));
112            WF_JOB.put("getConsoleUrl", new Property(JsonTags.WORKFLOW_CONSOLE_URL, String.class));
113            WF_JOB.put("getActions", new Property(JsonTags.WORKFLOW_ACTIONS, WorkflowAction.class, true));
114            WF_JOB.put("getParentId", new Property(JsonTags.WORKFLOW_PARENT_ID, String.class));
115            WF_JOB.put("toString", new Property(JsonTags.TO_STRING, String.class));
116    
117            COORD_ACTION.put("getId", new Property(JsonTags.COORDINATOR_ACTION_ID, String.class));
118            COORD_ACTION.put("getJobId", new Property(JsonTags.COORDINATOR_JOB_ID, String.class));
119            COORD_ACTION.put("getActionNumber", new Property(JsonTags.COORDINATOR_ACTION_NUMBER, Integer.TYPE));
120            COORD_ACTION.put("getCreatedConf", new Property(JsonTags.COORDINATOR_ACTION_CREATED_CONF, String.class));
121            COORD_ACTION.put("getCreatedTime", new Property(JsonTags.COORDINATOR_ACTION_CREATED_TIME, Date.class));
122            COORD_ACTION.put("getNominalTime", new Property(JsonTags.COORDINATOR_ACTION_NOMINAL_TIME, Date.class));
123            COORD_ACTION.put("getExternalId", new Property(JsonTags.COORDINATOR_ACTION_EXTERNALID, String.class));
124            COORD_ACTION.put("getStatus", new Property(JsonTags.COORDINATOR_ACTION_STATUS, CoordinatorAction.Status.class));
125            COORD_ACTION.put("getRunConf", new Property(JsonTags.COORDINATOR_ACTION_RUNTIME_CONF, String.class));
126            COORD_ACTION
127                    .put("getLastModifiedTime", new Property(JsonTags.COORDINATOR_ACTION_LAST_MODIFIED_TIME, Date.class));
128            COORD_ACTION
129                    .put("getMissingDependencies", new Property(JsonTags.COORDINATOR_ACTION_MISSING_DEPS, String.class));
130            COORD_ACTION.put("getPushMissingDependencies", new Property(JsonTags.COORDINATOR_ACTION_PUSH_MISSING_DEPS,
131                    String.class));
132            COORD_ACTION.put("getExternalStatus", new Property(JsonTags.COORDINATOR_ACTION_EXTERNAL_STATUS, String.class));
133            COORD_ACTION.put("getTrackerUri", new Property(JsonTags.COORDINATOR_ACTION_TRACKER_URI, String.class));
134            COORD_ACTION.put("getConsoleUrl", new Property(JsonTags.COORDINATOR_ACTION_CONSOLE_URL, String.class));
135            COORD_ACTION.put("getErrorCode", new Property(JsonTags.COORDINATOR_ACTION_ERROR_CODE, String.class));
136            COORD_ACTION.put("getErrorMessage", new Property(JsonTags.COORDINATOR_ACTION_ERROR_MESSAGE, String.class));
137            COORD_ACTION.put("toString", new Property(JsonTags.TO_STRING, String.class));
138    
139            COORD_JOB.put("getAppPath", new Property(JsonTags.COORDINATOR_JOB_PATH, String.class));
140            COORD_JOB.put("getAppName", new Property(JsonTags.COORDINATOR_JOB_NAME, String.class));
141            COORD_JOB.put("getId", new Property(JsonTags.COORDINATOR_JOB_ID, String.class));
142            COORD_JOB.put("getConf", new Property(JsonTags.COORDINATOR_JOB_CONF, String.class));
143            COORD_JOB.put("getStatus", new Property(JsonTags.COORDINATOR_JOB_STATUS, CoordinatorJob.Status.class));
144            COORD_JOB.put("getExecutionOrder",
145                          new Property(JsonTags.COORDINATOR_JOB_EXECUTIONPOLICY, CoordinatorJob.Execution.class));
146            COORD_JOB.put("getFrequency", new Property(JsonTags.COORDINATOR_JOB_FREQUENCY, String.class));
147            COORD_JOB.put("getTimeUnit", new Property(JsonTags.COORDINATOR_JOB_TIMEUNIT, CoordinatorJob.Timeunit.class));
148            COORD_JOB.put("getTimeZone", new Property(JsonTags.COORDINATOR_JOB_TIMEZONE, String.class));
149            COORD_JOB.put("getConcurrency", new Property(JsonTags.COORDINATOR_JOB_CONCURRENCY, Integer.TYPE));
150            COORD_JOB.put("getTimeout", new Property(JsonTags.COORDINATOR_JOB_TIMEOUT, Integer.TYPE));
151            COORD_JOB.put("getLastActionTime", new Property(JsonTags.COORDINATOR_JOB_LAST_ACTION_TIME, Date.class));
152            COORD_JOB.put("getNextMaterializedTime",
153                          new Property(JsonTags.COORDINATOR_JOB_NEXT_MATERIALIZED_TIME, Date.class));
154            COORD_JOB.put("getStartTime", new Property(JsonTags.COORDINATOR_JOB_START_TIME, Date.class));
155            COORD_JOB.put("getEndTime", new Property(JsonTags.COORDINATOR_JOB_END_TIME, Date.class));
156            COORD_JOB.put("getPauseTime", new Property(JsonTags.COORDINATOR_JOB_PAUSE_TIME, Date.class));
157            COORD_JOB.put("getUser", new Property(JsonTags.COORDINATOR_JOB_USER, String.class));
158            COORD_JOB.put("getGroup", new Property(JsonTags.COORDINATOR_JOB_GROUP, String.class));
159            COORD_JOB.put("getAcl", new Property(JsonTags.COORDINATOR_JOB_ACL, String.class));
160            COORD_JOB.put("getConsoleUrl", new Property(JsonTags.COORDINATOR_JOB_CONSOLE_URL, String.class));
161            COORD_JOB.put("getActions", new Property(JsonTags.COORDINATOR_ACTIONS, CoordinatorAction.class, true));
162            COORD_JOB.put("toString", new Property(JsonTags.TO_STRING, String.class));
163    
164            BUNDLE_JOB.put("getActions", new Property(JsonTags.COORDINATOR_ACTIONS, CoordinatorAction.class, true));
165    
166            BUNDLE_JOB.put("getAppPath",new Property(JsonTags.BUNDLE_JOB_PATH, String.class));
167            BUNDLE_JOB.put("getAppName",new Property(JsonTags.BUNDLE_JOB_NAME, String.class));
168            BUNDLE_JOB.put("getId",new Property(JsonTags.BUNDLE_JOB_ID, String.class));
169            BUNDLE_JOB.put("getExternalId",new Property(JsonTags.BUNDLE_JOB_EXTERNAL_ID, String.class));
170            BUNDLE_JOB.put("getConf",new Property(JsonTags.BUNDLE_JOB_CONF, String.class));
171            BUNDLE_JOB.put("getStatus",new Property(JsonTags.BUNDLE_JOB_STATUS, BundleJob.Status.class));
172            BUNDLE_JOB.put("getTimeUnit",new Property(JsonTags.BUNDLE_JOB_TIMEUNIT, BundleJob.Timeunit.class));
173            BUNDLE_JOB.put("getTimeout",new Property(JsonTags.BUNDLE_JOB_TIMEOUT, Integer.TYPE));
174            BUNDLE_JOB.put("getKickoffTime",new Property(JsonTags.BUNDLE_JOB_KICKOFF_TIME, Date.class));
175            BUNDLE_JOB.put("getStartTime",new Property(JsonTags.BUNDLE_JOB_START_TIME, Date.class));
176            BUNDLE_JOB.put("getEndTime",new Property(JsonTags.BUNDLE_JOB_END_TIME, Date.class));
177            BUNDLE_JOB.put("getPauseTime",new Property(JsonTags.BUNDLE_JOB_PAUSE_TIME, Date.class));
178            BUNDLE_JOB.put("getCreatedTime",new Property(JsonTags.BUNDLE_JOB_CREATED_TIME, Date.class));
179            BUNDLE_JOB.put("getUser",new Property(JsonTags.BUNDLE_JOB_USER, String.class));
180            BUNDLE_JOB.put("getGroup",new Property(JsonTags.BUNDLE_JOB_GROUP, String.class));
181            BUNDLE_JOB.put("getConsoleUrl",new Property(JsonTags.BUNDLE_JOB_CONSOLE_URL, String.class));
182            BUNDLE_JOB.put("getCoordinators",new Property(JsonTags.BUNDLE_COORDINATOR_JOBS, CoordinatorJob.class, true));
183            BUNDLE_JOB.put("toString", new Property(JsonTags.TO_STRING, String.class));
184    
185            BULK_RESPONSE.put("getBundle", new Property(JsonTags.BULK_RESPONSE_BUNDLE, BundleJob.class, false));
186            BULK_RESPONSE.put("getCoordinator", new Property(JsonTags.BULK_RESPONSE_COORDINATOR, CoordinatorJob.class, false));
187            BULK_RESPONSE.put("getAction", new Property(JsonTags.BULK_RESPONSE_ACTION, CoordinatorAction.class, false));
188    
189            JMS_CONNECTION_INFO.put("getTopicPatternProperties", new Property(JsonTags.JMS_TOPIC_PATTERN, Properties.class));
190            JMS_CONNECTION_INFO.put("getJNDIProperties", new Property(JsonTags.JMS_JNDI_PROPERTIES, Properties.class));
191            JMS_CONNECTION_INFO.put("getTopicPrefix", new Property(JsonTags.JMS_TOPIC_PREFIX, String.class));
192        }
193    
194        /**
195         * The dynamic proxy invocation handler used to convert JSON values to bean properties using a mapping.
196         */
197        private static class JsonInvocationHandler implements InvocationHandler {
198            private final Map<String, Property> mapping;
199            private final JSONObject json;
200    
201            /**
202             * Invocation handler constructor.
203             *
204             * @param mapping property to JSON/type-info mapping.
205             * @param json the json object to back the property values.
206             */
207            public JsonInvocationHandler(Map<String, Property> mapping, JSONObject json) {
208                this.mapping = mapping;
209                this.json = json;
210            }
211    
212            @Override
213            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
214                Property prop = mapping.get(method.getName());
215                if (prop == null) {
216                    throw new RuntimeException("Undefined method mapping: " + method.getName());
217                }
218                if (prop.isList) {
219                    if (prop.type == WorkflowAction.class) {
220                        return createWorkflowActionList((JSONArray) json.get(prop.label));
221                    }
222                    else if (prop.type == CoordinatorAction.class) {
223                        return createCoordinatorActionList((JSONArray) json.get(prop.label));
224                    }
225                    else if (prop.type == CoordinatorJob.class) {
226                        return createCoordinatorJobList((JSONArray) json.get(prop.label));
227                    }
228                    else {
229                        throw new RuntimeException("Unsupported list type : " + prop.type.getSimpleName());
230                    }
231                }
232                else {
233                    return parseType(prop.type, json.get(prop.label));
234                }
235            }
236    
237            @SuppressWarnings("unchecked")
238            private Object parseType(Class type, Object obj) {
239                if (type == String.class) {
240                    return obj == null ? obj : obj.toString();
241                }
242                else if (type == Integer.TYPE) {
243                    return (obj != null) ? new Integer(((Long) obj).intValue()) : new Integer(0);
244                }
245                else if (type == Long.TYPE) {
246                    return (obj != null) ? obj : new Long(0);
247                }
248                else if (type == Date.class) {
249                    return JsonUtils.parseDateRfc822((String) obj);
250                }
251                else if (type.isEnum()) {
252                    return Enum.valueOf(type, (String) obj);
253                }
254                else if (type == WorkflowAction.class) {
255                    return createWorkflowAction((JSONObject) obj);
256                }
257                else if (type == Properties.class){
258                    JSONObject jsonMap = (JSONObject)JSONValue.parse((String)obj);
259                    Properties props = new Properties();
260                    Set<Map.Entry> entrySet = jsonMap.entrySet();
261                    for (Map.Entry jsonEntry: entrySet){
262                        props.put(jsonEntry.getKey(), jsonEntry.getValue());
263                    }
264                    return props;
265                }
266                else if (type == CoordinatorJob.class) {
267                    return createCoordinatorJob((JSONObject) obj);
268                }
269                else if (type == CoordinatorAction.class) {
270                    return createCoordinatorAction((JSONObject) obj);
271                }
272                else if (type == BundleJob.class) {
273                    return createBundleJob((JSONObject) obj);
274                }
275                else {
276                    throw new RuntimeException("Unsupported type : " + type.getSimpleName());
277                }
278            }
279        }
280    
281        /**
282         * Creates a workflow action bean from a JSON object.
283         *
284         * @param json json object.
285         * @return a workflow action bean populated with the JSON object values.
286         */
287        public static WorkflowAction createWorkflowAction(JSONObject json) {
288            return (WorkflowAction) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(),
289                                                           new Class[]{WorkflowAction.class},
290                                                           new JsonInvocationHandler(WF_ACTION, json));
291        }
292    
293        /**
294         * Creates a list of workflow action beans from a JSON array.
295         *
296         * @param json json array.
297         * @return a list of workflow action beans from a JSON array.
298         */
299        public static List<WorkflowAction> createWorkflowActionList(JSONArray json) {
300            List<WorkflowAction> list = new ArrayList<WorkflowAction>();
301            for (Object obj : json) {
302                list.add(createWorkflowAction((JSONObject) obj));
303            }
304            return list;
305        }
306    
307        /**
308         * Creates a workflow job bean from a JSON object.
309         *
310         * @param json json object.
311         * @return a workflow job bean populated with the JSON object values.
312         */
313        public static WorkflowJob createWorkflowJob(JSONObject json) {
314            return (WorkflowJob) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(),
315                                                        new Class[]{WorkflowJob.class},
316                                                        new JsonInvocationHandler(WF_JOB, json));
317        }
318    
319        /**
320         * Creates a list of workflow job beans from a JSON array.
321         *
322         * @param json json array.
323         * @return a list of workflow job beans from a JSON array.
324         */
325        public static List<WorkflowJob> createWorkflowJobList(JSONArray json) {
326            List<WorkflowJob> list = new ArrayList<WorkflowJob>();
327            for (Object obj : json) {
328                list.add(createWorkflowJob((JSONObject) obj));
329            }
330            return list;
331        }
332    
333        /**
334         * Creates a coordinator action bean from a JSON object.
335         *
336         * @param json json object.
337         * @return a coordinator action bean populated with the JSON object values.
338         */
339        public static CoordinatorAction createCoordinatorAction(JSONObject json) {
340            return (CoordinatorAction) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(),
341                                                              new Class[]{CoordinatorAction.class},
342                                                              new JsonInvocationHandler(COORD_ACTION, json));
343        }
344    
345        /**
346         * Creates a list of coordinator action beans from a JSON array.
347         *
348         * @param json json array.
349         * @return a list of coordinator action beans from a JSON array.
350         */
351        public static List<CoordinatorAction> createCoordinatorActionList(JSONArray json) {
352            List<CoordinatorAction> list = new ArrayList<CoordinatorAction>();
353            for (Object obj : json) {
354                list.add(createCoordinatorAction((JSONObject) obj));
355            }
356            return list;
357        }
358    
359        /**
360         * Creates a coordinator job bean from a JSON object.
361         *
362         * @param json json object.
363         * @return a coordinator job bean populated with the JSON object values.
364         */
365        public static CoordinatorJob createCoordinatorJob(JSONObject json) {
366            return (CoordinatorJob) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(),
367                                                           new Class[]{CoordinatorJob.class},
368                                                           new JsonInvocationHandler(COORD_JOB, json));
369        }
370    
371    
372        /**
373         * Creates a JMSInfo bean from a JSON object.
374         *
375         * @param json json object.
376         * @return a coordinator job bean populated with the JSON object values.
377         */
378        public static JMSConnectionInfo createJMSConnectionInfo(JSONObject json) {
379            final JMSConnectionInfoWrapper jmsInfo = (JMSConnectionInfoWrapper) Proxy.newProxyInstance(
380                    JsonToBean.class.getClassLoader(), new Class[] { JMSConnectionInfoWrapper.class },
381                    new JsonInvocationHandler(JMS_CONNECTION_INFO, json));
382    
383            return new JMSConnectionInfo() {
384                @Override
385                public String getTopicPrefix() {
386                    return jmsInfo.getTopicPrefix();
387                }
388    
389                @Override
390                public String getTopicPattern(AppType appType) {
391                    return (String)jmsInfo.getTopicPatternProperties().get(appType.name());
392                }
393    
394                @Override
395                public Properties getJNDIProperties() {
396                    return jmsInfo.getJNDIProperties();
397                }
398            };
399        }
400    
401        /**
402         * Creates a list of coordinator job beans from a JSON array.
403         *
404         * @param json json array.
405         * @return a list of coordinator job beans from a JSON array.
406         */
407        public static List<CoordinatorJob> createCoordinatorJobList(JSONArray json) {
408            List<CoordinatorJob> list = new ArrayList<CoordinatorJob>();
409            for (Object obj : json) {
410                list.add(createCoordinatorJob((JSONObject) obj));
411            }
412            return list;
413        }
414    
415        /**
416         * Creates a bundle job bean from a JSON object.
417         *
418         * @param json json object.
419         * @return a bundle job bean populated with the JSON object values.
420         */
421        public static BundleJob createBundleJob(JSONObject json) {
422            return (BundleJob) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(),
423                                                           new Class[]{BundleJob.class},
424                                                           new JsonInvocationHandler(BUNDLE_JOB, json));
425        }
426    
427        /**
428         * Creates a list of bundle job beans from a JSON array.
429         *
430         * @param json json array.
431         * @return a list of bundle job beans from a JSON array.
432         */
433        public static List<BundleJob> createBundleJobList(JSONArray json) {
434            List<BundleJob> list = new ArrayList<BundleJob>();
435            for (Object obj : json) {
436                list.add(createBundleJob((JSONObject) obj));
437            }
438            return list;
439        }
440    
441        /**
442         * Creates a Bulk response object from a JSON object.
443         *
444         * @param json json object.
445         * @return a Bulk response object populated with the JSON object values.
446         */
447        public static BulkResponse createBulkResponse(JSONObject json) {
448            return (BulkResponse) Proxy.newProxyInstance(JsonToBean.class.getClassLoader(),
449                                                        new Class[]{BulkResponse.class},
450                                                        new JsonInvocationHandler(BULK_RESPONSE, json));
451        }
452    
453        /**
454         * Creates a list of bulk response beans from a JSON array.
455         *
456         * @param json json array.
457         * @return a list of bulk response beans from a JSON array.
458         */
459        public static List<BulkResponse> createBulkResponseList(JSONArray json) {
460            List<BulkResponse> list = new ArrayList<BulkResponse>();
461            for (Object obj : json) {
462                list.add(createBulkResponse((JSONObject) obj));
463            }
464            return list;
465        }
466    
467    }