/**
   * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method,
   *     java.lang.Object[])
   */
  public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
    LOGGER.logTrace("Invoking method {0} on {1} in {2}.", method, model, workspace);
    if (null != args && 0 < args.length && LOGGER.isDebugEnabled()) {
      for (int i = 0; i < args.length; i++) LOGGER.logDebug("args[{0}]:{1}", i, args[i]);
    }
    final Object lock;
    switch (extractLock(method)) {
      case NONE:
        lock = new Object();
        break;
      case EXCLUSIVE: // deliberate fall-through
      case LOCAL_READ: // deliberate fall-through
      default:
        lock = workspace;
    }
    synchronized (lock) {
      final Transaction transaction = workspace.getTransaction();
      final TransactionContext transactionContext = newXAContext(method);
      beginXA(transaction, transactionContext);
      try {
        /* if the method is annotated as online, ensure that we are
         * online */
        if (isOnline(method)) model.ensureOnline();
        model.setInvocationContext(
            new ModelInvocationContext() {
              public Object[] getArguments() {
                return null == args ? NO_ARGS : args;
              }

              public Method getMethod() {
                return method;
              }
            });
        final Object metricsContext = newMetricsContext(lock, method);
        ModelInvocationMetrics.begin(metricsContext);
        final Object result = method.invoke(model, args);
        ModelInvocationMetrics.end(metricsContext);
        model.notifyListeners();
        return LOGGER.logVariable("result", result);
      } catch (final InvocationTargetException itx) {
        rollbackXA(transaction, transactionContext);
        throw itx.getTargetException();
      } catch (final Throwable t) {
        rollbackXA(transaction, transactionContext);
        throw t;
      } finally {
        completeXA(transaction, transactionContext);
      }
    }
  }