/FileExtraction/Java_unseen/dlitz_resin/modules/kernel/src/com/caucho/jmx/JmxInvocationHandler.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.jmx;

import javax.management.Attribute;
import javax.management.MBeanServer;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Proxy hander for mbeans.
 */
public class JmxInvocationHandler implements InvocationHandler {
  private MBeanServer _server;
  private ClassLoader _loader;
  private ObjectName _name;

  /**
   * Creates the invocation handler.
   */
  public JmxInvocationHandler(MBeanServer mbeanServer,
                              ClassLoader loader,
                              ObjectName objectName)
  {
    _server = mbeanServer;
    _loader = loader;
    _name = objectName;
  }

  /**
   * Creates a new proxy instance.
   */
  public static Object newProxyInstance(MBeanServer server,
                                        ClassLoader loader,
                                        ObjectName objectName,
                                        Class interfaceClass,
                                        boolean notificationBroadcaster)
  {
    Class []interfaces;

    if (notificationBroadcaster)
      interfaces = new Class[] { interfaceClass, NotificationEmitter.class };
    else
      interfaces = new Class[] { interfaceClass };

    JmxInvocationHandler handler;
    
    handler = new JmxInvocationHandler(server, loader, objectName);
    
    return Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                                  interfaces,
                                  handler);
  }

  /**
   * Handles the object invocation.
   *
   * @param proxy the proxy object to invoke
   * @param method the method to call
   * @param args the arguments to the proxy object
   */
  public Object invoke(Object proxy, Method method, Object []args)
    throws Throwable
  {
    String methodName = method.getName();
    Class []params = method.getParameterTypes();

    // equals and hashCode are special cased
    if (methodName.equals("equals")
        && params.length == 1 && params[0].equals(Object.class)) {
      Object value = args[0];
      if (value == null || ! Proxy.isProxyClass(value.getClass()))
        return Boolean.FALSE;

      JmxInvocationHandler handler;

      handler = (JmxInvocationHandler) Proxy.getInvocationHandler(value);

      return new Boolean(_name.equals(handler._name));
    }
    else if (methodName.equals("hashCode") && params.length == 0)
      return new Integer(_name.hashCode());

    int len = methodName.length();
    String attrName;

    Class returnType = method.getReturnType();
    
    if (params.length == 0 && methodName.startsWith("get") && len > 3) {
      attrName = methodName.substring(3);

      return marshall(_server.getAttribute(_name, attrName), returnType);
    }
    else if (params.length == 1 && returnType.equals(void.class) &&
             methodName.startsWith("set") && len > 3) {
      attrName = methodName.substring(3);

      Attribute attr = new Attribute(attrName, args[0]);

      _server.setAttribute(_name, attr);

      return null;
    }
    else if (methodName.equals("addNotificationListener")) {
      if (args.length != 3) {
      }
      else if (args[0] instanceof NotificationListener) {
        _server.addNotificationListener(_name,
                                        (NotificationListener) args[0],
                                        (NotificationFilter) args[1],
                                        args[2]);
        return null;
      }
      else if (args[0] instanceof ObjectName) {
        _server.addNotificationListener(_name,
                                        (ObjectName) args[0],
                                        (NotificationFilter) args[1],
                                        args[2]);
        return null;
      }
    }
    else if (methodName.equals("removeNotificationListener")) {
      if (args.length == 3) {
        if (args[0] instanceof NotificationListener) {
          _server.removeNotificationListener(_name,
                                             (NotificationListener) args[0],
                                             (NotificationFilter) args[1],
                                             args[2]);
          return null;
        }
        else if (args[0] instanceof ObjectName) {
          _server.removeNotificationListener(_name,
                                             (ObjectName) args[0],
                                             (NotificationFilter) args[1],
                                             args[2]);
          return null;
        }
      }
      else if (args.length == 1) {
        if (args[0] instanceof NotificationListener) {
          _server.removeNotificationListener(_name,
                                             (NotificationListener) args[0]);
          return null;
        }
        else if (args[0] instanceof ObjectName) {
          _server.removeNotificationListener(_name, (ObjectName) args[0]);

          return null;
        }
      }
    }

    String []sig = new String[params.length];

    for (int i = 0; i < sig.length; i++)
      sig[i] = params[i].getName();

    Object value = _server.invoke(_name, methodName, args, sig);

    return marshall(value, returnType);
  }

  private Object marshall(Object value, Class retType)
  {
    if (retType == null || value == null ||
        retType.isAssignableFrom(value.getClass()))
      return value;

    if (value instanceof ObjectName) {
      ObjectName name = (ObjectName) value;

      Object proxy = Jmx.find(name, _loader, _server);

      if (retType.isInstance(proxy))
        return proxy;
      else
        return value;
    }
    else if (value instanceof ObjectName[] && retType.isArray()) {
      Class type = retType.getComponentType();
      ObjectName []names = (ObjectName []) value;
      
      Object proxies = Array.newInstance(type, names.length);

      for (int i = 0; i < names.length; i++) {
        Object proxy = Jmx.find(names[i], _loader, _server);

        if (proxy == null) {
          Array.set(proxies, i, null);
          continue;
        }

        if (! type.isInstance(proxy))
          return value;

        Array.set(proxies, i, Jmx.find(names[i], _loader, _server));
      }

      return proxies;
    }

    return value;
  }
}
