/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2006, 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.web;

import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.concurrent.Executor;

import javax.management.MBeanServer;
import javax.management.ObjectName;

import org.jboss.bootstrap.api.as.config.JBossASServerConfig;
import org.jboss.config.ServerConfigUtil;
import org.jboss.system.ServiceMBeanSupport;

/**
 * The WebService implementation. It configures a WebServer instance to
 * perform dynamic class and resource loading.
 *
 * @jmx:mbean
 *      extends="org.jboss.system.ServiceMBean"
 *      name="jboss:service=WebService"
 *
 * @version <tt>$Revision: 97479 $</tt>
 * @author <a href="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
 * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a>
 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
 * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
 */
public class WebService extends ServiceMBeanSupport
   implements WebServiceMBean
{
   //
   // jason: WebService and WebServer classes should be merged into one
   //        WebService only provides a thin layer of JMX which WebServer
   //        can do just as well.
   //
   //        Doing so will dramatically simplify this component.
   //
   //        Rename to HTTPClassLoaderService to be clear that this is not
   //        a fully functional web server.
   //

   /**
    * The web server instance which does the work.
    * <p>
    * Note: This value shadows the MBeanServer server value
    * from ServiceMBeanSupport.
    */
   private WebServer server = new WebServer();

   /** The host portion of the RMI codebase URL */
   private String host;
   
   /**
    * @jmx:managed-constructor
    */
   public WebService()
   {
      super();
   }

   /**
    * @jmx:managed-operation
    */
   public URL addClassLoader(ClassLoader cl)
   {
      return server.addClassLoader(cl);
   }

   /**
    * @jmx:managed-operation
    */
   public void removeClassLoader(ClassLoader cl)
   {
      server.removeClassLoader(cl);
   }

   /**
    * Set the WebService listening port.
    * 
    * @jmx:managed-attribute
    *
    * @param port  The listening port; 0 for anonymous
    */
   public void setPort(int port)
   {
      server.setPort(port);
   }

   /**
    * Get the WebService listening port.
    * 
    * @jmx:managed-attribute
    */
   public int getPort()
   {
      return server.getPort();
   }

   /**
    * Set the name of the interface to use for the host portion of the
    * RMI codebase URL.
    * 
    * @jmx:managed-attribute
    */
   public void setHost(final String hostname)
   {
      this.host = ServerConfigUtil.fixRemoteAddress(hostname);
   }

   /**
    * Get the name of the interface to use for the host portion of the
    * RMI codebase URL.
    * 
    * @jmx:managed-attribute
    */
   public String getHost()
   {
      return host;
   }

   /**
    * Set the specific address the WebService listens on.  This can be used on
    * a multi-homed host for a ServerSocket that will only accept connect requests
    * to one of its addresses.
    *
    * @jmx:managed-attribute
    * 
    * @param address  the interface name or IP address to bind. If host is null,
    *                 connections on any/all local addresses will be allowed.
    */
   public void setBindAddress(String address) throws UnknownHostException
   {
      InetAddress bindAddress = toInetAddress(address);
      server.setBindAddress(bindAddress);
   }
   
   /**
    * Get the specific address the WebService listens on.
    *
    * @jmx:managed-attribute
    * 
    * @return the interface name or IP address the WebService binds to.
    */
   public String getBindAddress()
   {
      InetAddress bindAddress = server.getBindAddress();
      
      if (bindAddress != null)
         return bindAddress.getHostAddress();
      else
         return null;
   }

   /**
    * Get the WebService listen queue backlog limit. The maximum queue length
    * for incoming connection indications (a request to connect) is set to the
    * backlog parameter. If a connection indication arrives when the queue is
    * full, the connection is refused.
    *
    * @jmx:managed-attribute
    * 
    * @return the queue backlog limit. 
    */
   public int getBacklog()
   {
      return server.getBacklog();
   }

   /**
    * Set the WebService listen queue backlog limit. The maximum queue length
    * for incoming connection indications (a request to connect) is set to the
    * backlog parameter. If a connection indication arrives when the queue is
    * full, the connection is refused.
    *
    * @jmx:managed-attribute
    * 
    * @param backlog, the queue backlog limit. 
    */
   public void setBacklog(int backlog)
   {
      server.setBacklog(backlog);
   }

   /** Set the thread pool used for the WebServer class loading.
    * @jmx:managed-attribute
    * 
    */ 
   public void setExecutor(Executor executor)
   {
      server.setThreadPool(executor);
   }

   /**
    * A flag indicating if the server should attempt to download classes from
    * thread context class loader when a request arrives that does not have a
    * class loader key prefix.
    *
    * @jmx:managed-attribute
    */
   public boolean getDownloadServerClasses()
   {
      return server.getDownloadServerClasses();
   }

   /**
    * @jmx:managed-attribute
    */
   public void setDownloadServerClasses(boolean flag)
   {
      server.setDownloadServerClasses(flag);
   }

   /**
    * A flag indicating if the server should attempt to download resources,
    * i.e. resource paths that don't end in .class
    *
    * @jmx:managed-attribute
    */
   public boolean getDownloadResources()
   {
      return server.getDownloadResources();
   }

   /**
    * @jmx:managed-attribute
    */
   public void setDownloadResources(boolean flag)
   {
      server.setDownloadResources(flag);
   }

   /**
    * The RMI codebase URL.
    * 
    * @jmx:managed-attribute
    */
   public String getCodebase()
   {
      return System.getProperty("java.rmi.server.codebase");
   }
   
   protected ObjectName getObjectName(MBeanServer server, ObjectName name)
      throws javax.management.MalformedObjectNameException
   {
      return name == null ? OBJECT_NAME : name;
   }

   protected void createService() throws Exception
   {
      // Load the file mime.types into the mapping list
      Properties mimeTypes = new Properties();

      try
      {
         mimeTypes.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("org/jboss/web/mime.types"));

         Enumeration keys = mimeTypes.keys();
         while (keys.hasMoreElements())
         {
            String extension = (String) keys.nextElement();
            String type = mimeTypes.getProperty(extension);
            server.addMimeType(extension, type);
         }
      }
      catch (Exception e)
      {
         log.error("Failed to load org/jboss/web/mime.types; ignoring", e);
      }

      // if no override has been specified, default to the jboss bind address
      if (getBindAddress() == null)
         setBindAddress(System.getProperty(JBossASServerConfig.PROP_KEY_JBOSSAS_BIND_ADDRESS));
      
      // if no host specified, default to the java.rmi.server.hostname property value
      if (getHost() == null)
         setHost(System.getProperty("java.rmi.server.hostname"));
      
      // Set the rmi codebase if it is not already set
      String codebase = getCodebase();
      if (codebase == null)
      {
         codebase = "http://" + getHost() + ":" + getPort() + "/";
         System.setProperty("java.rmi.server.codebase", codebase);
      }
      log.info("Using RMI server codebase: " + codebase);
   }

   /**
    * Start the web server for dynamic downloading of classes and resources.
    * 
    * <p>
    * The system <tt>java.rmi.server.codebase</tt> is also set to
    * <em>http://<host>:<port>/</em> if the property has not been set.
    */
   protected void startService() throws Exception
   {
      // Start the WebServer running
      server.start();
      log.debug("Started WebServer with address: " + server.getBindAddress() + ":" + getPort());
   }

   protected void stopService() throws Exception
   {
      server.stop();
      log.debug("Stopped WebServer with address: " + server.getBindAddress() + ":" + getPort());
   }
   
   /**
    * Safely convert a host string to InetAddress or null
    */
   private InetAddress toInetAddress(String host) throws UnknownHostException
   {
      if (host == null || host.length() == 0)
         return null;
      else
         return InetAddress.getByName(host);
   }
}
