/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.invocation.http.server;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import javax.management.ObjectName;
import javax.naming.InitialContext;

import org.jboss.invocation.Invoker;
import org.jboss.invocation.InvokerInterceptor;
import org.jboss.invocation.http.interfaces.HttpInvokerProxy;
import org.jboss.invocation.http.interfaces.ClientMethodInterceptor;
import org.jboss.naming.Util;
import org.jboss.proxy.GenericProxyFactory;
import org.jboss.system.Registry;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.util.StringPropertyReplacer;
import org.jboss.metadata.MetaData;
import org.jboss.config.ServerConfigUtil;
import org.w3c.dom.Element;

/** Create an interface proxy that uses HTTP to communicate with the server
 * side object that exposes the corresponding JMX invoke operation. Any request
 * to this servlet receives a serialized object stream containing a
 * MarshalledValue with the Naming proxy as its content.
 *
 * @author Scott.Stark@jboss.org
 * @version $Revision: 91254 $
 */
public class HttpProxyFactory extends ServiceMBeanSupport
   implements HttpProxyFactoryMBean
{
   /** The server side mbean that exposes the invoke operation for the
    exported interface */
   private ObjectName jmxInvokerName;
   /** The Proxy object which uses the HttpInvokerProxy as its handler */
   private Object theProxy;
   /** The http URL to the InvokerServlet */
   private String invokerURL;
   /** The alternative prefix used to build the invokerURL */
   private String invokerURLPrefix = "http://";
   /** The alternative suffix used to build the invokerURL */
   private String invokerURLSuffix = ":8080/invoker/JMXInvokerServlet";
   /** The alternative host or ip flag used to build the invokerURL */
   private boolean useHostName = false;
   /** The JNDI name under which the HttpInvokerProxy will be bound */
   private String jndiName;
   /** The interface that the HttpInvokerProxy implements */
   private Class exportedInterface;
   private Element interceptorConfig;
   private ArrayList interceptorClasses;

   public HttpProxyFactory()
   {
   }

   public ObjectName getInvokerName()
   {
      return jmxInvokerName;
   }
   public void setInvokerName(ObjectName jmxInvokerName)
   {
      this.jmxInvokerName = jmxInvokerName;
   }

   public String getJndiName()
   {
      return jndiName;
   }
   public void setJndiName(String jndiName)
   {
      this.jndiName = jndiName;
   }

   public String getInvokerURL()
   {
      return invokerURL;
   }
   public void setInvokerURL(String invokerURL)
   {
      // Replace any system properties in the URL
      String tmp = StringPropertyReplacer.replaceProperties(invokerURL);
      this.invokerURL = tmp;
      log.debug("Set invokerURL to "+this.invokerURL);
   }

   public String getInvokerURLPrefix()
   {
      return invokerURLPrefix;
   }
   public void setInvokerURLPrefix(String invokerURLPrefix)
   {
      this.invokerURLPrefix = invokerURLPrefix;
   }

   public String getInvokerURLSuffix()
   {
      return invokerURLSuffix;
   }
   public void setInvokerURLSuffix(String invokerURLSuffix)
   {
      this.invokerURLSuffix = invokerURLSuffix;
   }

   public boolean getUseHostName()
   {
      return useHostName;
   }
   public void setUseHostName(boolean flag)
   {
      this.useHostName = flag;
   }

   public Class getExportedInterface()
   {
      return exportedInterface;
   }
   public void setExportedInterface(Class exportedInterface)
   {
      this.exportedInterface = exportedInterface;
   }

   public Element getClientInterceptors()
   {
      return interceptorConfig;
   }
   public void setClientInterceptors(Element config) throws Exception
   {
      this.interceptorConfig = config;
      Iterator interceptorElements = MetaData.getChildrenByTagName(interceptorConfig, "interceptor");
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      if( interceptorClasses != null )
         interceptorClasses.clear();
      else
         interceptorClasses = new ArrayList();
      while( interceptorElements != null && interceptorElements.hasNext() )
      {
         Element ielement = (Element) interceptorElements.next();
         String className = null;
         className = MetaData.getElementContent(ielement);
         Class clazz = loader.loadClass(className);
         interceptorClasses.add(clazz);
      }
   }

   public Object getProxy()
   {
      return theProxy;
   }

   public Object getProxy(Object id)
   {
      Class[] ifaces = {exportedInterface};
      ArrayList interceptorClasses = null; //defineInterceptors();
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      GenericProxyFactory proxyFactory = new GenericProxyFactory();
      Object newProxy = null;
      /*
      Object newProxy = proxyFactory.createProxy(id, jmxInvokerName,
         null, null, null, interceptorClasses, loader, ifaces);
         */
      return newProxy;
   }

   /** Initializes the servlet.
    */
   protected void startService() throws Exception
   {
      /** Create an HttpInvokerProxy that posts invocations to the
       externalURL. This proxy will be associated with a naming JMX invoker
       given by the jmxInvokerName.
       */
      Invoker delegateInvoker = createInvoker();
      Integer nameHash = new Integer(jmxInvokerName.hashCode());
      log.debug("Bound delegate: "+delegateInvoker
         +" for invoker="+jmxInvokerName);
      /* Create a binding betweeh the invoker name hash and the jmx name
      This is used by the HttpInvoker to map from the Invocation ObjectName
      hash value to the target JMX ObjectName.
      */
      Registry.bind(nameHash, jmxInvokerName);

      Object cacheID = null;
      String proxyBindingName = null;
      Class[] ifaces = {exportedInterface};
      /* Initialize interceptorClasses with default client interceptor list
         if no client interceptor configuration was provided */
      if( interceptorClasses == null )
         interceptorClasses = defineDefaultInterceptors();
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      GenericProxyFactory proxyFactory = new GenericProxyFactory();
      theProxy = proxyFactory.createProxy(cacheID, jmxInvokerName,
         delegateInvoker, jndiName, proxyBindingName, interceptorClasses,
         loader, ifaces);
      log.debug("Created HttpInvokerProxy for invoker="+jmxInvokerName
         +", nameHash="+nameHash);

      if( jndiName != null )
      {
         InitialContext iniCtx = new InitialContext();
         Util.bind(iniCtx, jndiName, theProxy);
         log.debug("Bound proxy under jndiName="+jndiName);
      }
   }

   protected void stopService() throws Exception
   {
      Integer nameHash = new Integer(jmxInvokerName.hashCode());
      Registry.unbind(jmxInvokerName);
      Registry.unbind(nameHash);
      if( jndiName != null )
      {
         InitialContext iniCtx = new InitialContext();
         Util.unbind(iniCtx, jndiName);
      }
   }

   /** Build the default interceptor list. This consists of:
    * ClientMethodInterceptor
    * InvokerInterceptor
    */
   protected ArrayList defineDefaultInterceptors()
   {
      ArrayList tmp = new ArrayList();
      tmp.add(ClientMethodInterceptor.class);
      tmp.add(InvokerInterceptor.class);
      return tmp;
   }

   /** Create the Invoker
    */
   protected Invoker createInvoker() throws Exception
   {
      checkInvokerURL();
      HttpInvokerProxy delegateInvoker = new HttpInvokerProxy(invokerURL);
      return delegateInvoker;
   }

   /** Validate that the invokerURL is set, and if not build it from
    * the invokerURLPrefix + host + invokerURLSuffix. The host value will be
    * taken from the jboss.bind.address system property if its a valid
    * address, InetAddress.getLocalHost otherwise.
    */
   protected void checkInvokerURL() throws UnknownHostException
   {
      if( invokerURL == null )
      {
         // First check for a global bind address
         String host = ServerConfigUtil.getSpecificBindAddress();
         if( host == null )
         {
            InetAddress addr = InetAddress.getLocalHost();
            host = useHostName ? addr.getHostName() : addr.getHostAddress();
         }
         String url = invokerURLPrefix + host + invokerURLSuffix;
         setInvokerURL(url);
      }
   }

}

