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 */
018package org.apache.oozie.client.rest;
019
020import org.apache.oozie.client.BulkResponse;
021import org.apache.oozie.client.BundleJob;
022import org.apache.oozie.client.CoordinatorAction;
023import org.apache.oozie.client.CoordinatorJob;
024import org.apache.oozie.client.JMSConnectionInfo;
025import org.apache.oozie.client.JMSConnectionInfoWrapper;
026import org.apache.oozie.client.WorkflowAction;
027import org.apache.oozie.client.WorkflowJob;
028import org.apache.oozie.AppType;
029import org.json.simple.JSONArray;
030import org.json.simple.JSONObject;
031import org.json.simple.JSONValue;
032
033import java.lang.reflect.InvocationHandler;
034import java.lang.reflect.Method;
035import java.lang.reflect.Proxy;
036import java.util.ArrayList;
037import java.util.Date;
038import java.util.HashMap;
039import java.util.List;
040import java.util.Map;
041import java.util.Properties;
042import 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")
051public 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}