/***************************************
 *                                     *
 *  JBoss: The OpenSource J2EE WebOS   *
 *                                     *
 *  Distributable under LGPL license.  *
 *  See terms of license at gnu.org.   *
 *                                     *
 ***************************************/

package org.jboss.cache;

import org.jboss.logging.Logger;
import org.jboss.remoting.InvocationRequest;
import org.jboss.remoting.InvokerCallbackHandler;
import org.jboss.remoting.ServerInvocationHandler;
import org.jboss.remoting.ServerInvoker;
import org.jboss.remoting.ident.Identity;
import org.jboss.remoting.invocation.NameBasedInvocation;

import javax.management.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;

/**
 * TreeCacheInvocationHandler is a ServerInvocationHandler that will forward requests to the
 * MBeanServer and return the results from the MBeanServer.
 *
 * @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
 * @author Bela Ban
 * @version $Id: TreeCacheInvocationHandler.java,v 1.1 2004/12/02 15:40:41 belaban Exp $
 */
public class TreeCacheInvocationHandler implements ServerInvocationHandler {
   private static final Logger log=Logger.getLogger(TreeCacheInvocationHandler.class);
   private MBeanServer server;
   private Identity identity;

   private static Method getObjectInstance;
   private static Method isRegistered;
   private static Method getAttribute;
   private static Method getAttributes;
   private static Method setAttribute;
   private static Method setAttributes;
   private static Method invoke;
   private static Method getMBeanInfo;

   static {
      try {
         Class LObject=(new Object[0]).getClass();
         Class LString=(new String[0]).getClass();

         Class[] Sig_ObjectName=new Class[]{ObjectName.class};
         Class[] Sig_ObjectName_String=new Class[]{ObjectName.class, String.class};
         Class[] Sig_ObjectName_LString=new Class[]{ObjectName.class, LString};
         Class[] Sig_ObjectName_Attribute=new Class[]{ObjectName.class, Attribute.class};
         Class[] Sig_ObjectName_AttributeList=new Class[]{ObjectName.class, AttributeList.class};
         Class[] Sig_ObjectName_String_LObject_LString=new Class[]{ObjectName.class, String.class, LObject, LString};

         getObjectInstance=MBeanServer.class.getMethod("getObjectInstance", Sig_ObjectName);
         isRegistered=MBeanServer.class.getMethod("isRegistered", Sig_ObjectName);
         getAttribute=MBeanServer.class.getMethod("getAttribute", Sig_ObjectName_String);
         getAttributes=MBeanServer.class.getMethod("getAttributes", Sig_ObjectName_LString);
         setAttribute=MBeanServer.class.getMethod("setAttribute", Sig_ObjectName_Attribute);
         setAttributes=MBeanServer.class.getMethod("setAttributes", Sig_ObjectName_AttributeList);
         invoke=MBeanServer.class.getMethod("invoke", Sig_ObjectName_String_LObject_LString);
         getMBeanInfo=MBeanServer.class.getMethod("getMBeanInfo", Sig_ObjectName);
      }
      catch(Exception e) {
         throw new RuntimeException("Error resolving methods", e);
      }
   }

   public TreeCacheInvocationHandler() {
      super();
   }

   /**
    * set the mbean server that the handler can reference
    *
    * @param server
    */
   public void setMBeanServer(MBeanServer server) {
      if(server == null)
         return;
      this.server=server;
      identity=Identity.get(server);
      // make sure our local server is set
      if(log.isDebugEnabled()) {
         log.debug("setMBeanServer called with: " + server + " with identity: " + identity);
      }
   }

   public void setInvoker(ServerInvoker invoker) {
   }



   public Object invoke(InvocationRequest invocation) throws Throwable {
      if(this.server == null) {
         throw new IllegalStateException("invoke called prior to mbean server being set");
      }
      try {
         NameBasedInvocation nbi=(NameBasedInvocation)invocation.getParameter();
         String methodName=nbi.getMethodName();
         Object args []=nbi.getParameters();
         String signature []=nbi.getSignature();

         Object _args[]=(args == null && signature != null) ? new Object[signature.length] : args;
         // get the mbean server method that's being invoked
         Method method=getMethod(methodName, signature);
         // transport against the mbean server
         return method.invoke(server, _args);
      }
      catch(Throwable ex) {
         if(ex instanceof UndeclaredThrowableException) {
            UndeclaredThrowableException ut=(UndeclaredThrowableException)ex;
            Throwable ute=ut.getUndeclaredThrowable();
            if(ute instanceof Exception) {
               throw new MBeanException((Exception)ute, ut.getUndeclaredThrowable().getMessage());
            }
            else {
               throw new MBeanException(new Exception(ute.getMessage()), ute.getMessage());
            }
         }
         if(ex instanceof InvocationTargetException)
            throw ((InvocationTargetException)ex).getTargetException();
         throw ex;
      }
   }

   public void addListener(InvokerCallbackHandler callbackHandler) {
   }

   public void removeListener(InvokerCallbackHandler callbackHandler) {
   }

   /**
    * convenience method to lookup the Method object for a given method and signature
    *
    * @param methodName
    * @param sig
    * @return
    * @throws java.lang.Throwable
    */
   private Method getMethod(String methodName, String sig[])
         throws Throwable {
      if(methodName.equals("invoke"))
         return invoke;
      else if(methodName.equals("getAttribute"))
         return getAttribute;
      else if(methodName.equals("setAttribute"))
         return setAttribute;
      else if(methodName.equals("getAttributes"))
         return getAttributes;
      else if(methodName.equals("setAttributes"))
         return setAttributes;
      else if(methodName.equals("setAttributes"))
         return setAttributes;
      else if(methodName.equals("getMBeanInfo"))
         return getMBeanInfo;
      else if(methodName.equals("getObjectInstance"))
         return getObjectInstance;
      else if(methodName.equals("isRegistered"))
         return isRegistered;

      Class[] params=null;
      if(sig != null) {
         params=new Class[sig.length];
         for(int i=0; i < sig.length; ++i)
            params[i]=Class.forName(sig[i]);
      }
      return MBeanServer.class.getMethod(methodName, params);
   }

}