/**
   * The invoke method is called when phonegap.pluginManager.exec(...) is used from the script
   * environment. It instantiates the appropriate plugin and invokes the specified action.
   * JavaScript arguments are passed in as an array of objects.
   *
   * @param service String containing the service to run
   * @param action String containing the action that the service is supposed to perform. This is
   *     passed to the plugin execute method and it is up to the plugin developer how to deal with
   *     it.
   * @param callbackId String containing the id of the callback that is executed in JavaScript if
   *     this is an async plugin call.
   * @param args An Array literal string containing any arguments needed in the plugin execute
   *     method.
   * @param async Boolean indicating whether the calling JavaScript code is expecting an immediate
   *     return value. If true, either PhoneGap.callbackSuccess(...) or PhoneGap.callbackError(...)
   *     is called once the plugin code has executed.
   * @return JSON encoded string with a response message and status.
   * @see net.rim.device.api.script.ScriptableFunction#invoke(java.lang.Object, java.lang.Object[])
   */
  public Object invoke(Object obj, Object[] oargs) throws Exception {
    final String service = (String) oargs[ARG_SERVICE];
    final String action = (String) oargs[ARG_ACTION];
    final String callbackId = (String) oargs[ARG_CALLBACK_ID];
    boolean async = (oargs[ARG_ASYNC].toString().equals("true") ? true : false);
    PluginResult pr = null;

    try {
      // action arguments
      final JSONArray args = new JSONArray((String) oargs[ARG_ARGS]);

      // get the class for the specified service
      String clazz = this.pluginManager.getClassForService(service);
      Class c = null;
      if (clazz != null) {
        c = getClassByName(clazz);
      }

      if (isPhoneGapPlugin(c)) {
        // Create a new instance of the plugin and set the context
        final Plugin plugin = this.addPlugin(clazz, c);
        async = async && !plugin.isSynch(action);
        if (async) {
          // Run this async on a background thread so that JavaScript can continue on
          Thread thread =
              new Thread(
                  new Runnable() {
                    public void run() {
                      // Call execute on the plugin so that it can do it's thing
                      final PluginResult result = plugin.execute(action, args, callbackId);

                      if (result != null) {
                        int status = result.getStatus();

                        // If plugin status is OK,
                        // or plugin is not going to send an immediate result (NO_RESULT)
                        if (status == PluginResult.Status.OK.ordinal()
                            || status == PluginResult.Status.NO_RESULT.ordinal()) {
                          PhoneGapExtension.invokeSuccessCallback(callbackId, result);
                        }
                        // error
                        else {
                          PhoneGapExtension.invokeErrorCallback(callbackId, result);
                        }
                      }
                    }
                  });
          thread.start();
          return "";
        } else {
          // Call execute on the plugin so that it can do it's thing
          pr = plugin.execute(action, args, callbackId);
        }
      }
    } catch (ClassNotFoundException e) {
      pr =
          new PluginResult(
              PluginResult.Status.CLASSNOTFOUNDEXCEPTION,
              "ClassNotFoundException: " + e.getMessage());
    } catch (IllegalAccessException e) {
      pr =
          new PluginResult(
              PluginResult.Status.ILLEGALACCESSEXCEPTION,
              "IllegalAccessException:" + e.getMessage());
    } catch (InstantiationException e) {
      pr =
          new PluginResult(
              PluginResult.Status.INSTANTIATIONEXCEPTION,
              "InstantiationException: " + e.getMessage());
    } catch (JSONException e) {
      pr = new PluginResult(PluginResult.Status.JSONEXCEPTION, "JSONException: " + e.getMessage());
    }
    // if async we have already returned at this point unless there was an error...
    if (async) {
      PhoneGapExtension.invokeErrorCallback(callbackId, pr);
    }
    return (pr != null ? pr.getJSONString() : "{ status: 0, message: 'all good' }");
  }