/*
 * Copyright (C) 2006-2007 Helge Hess
 * 
 * This file is part of SOPE/J.
 * 
 * SOPE/J 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, or (at your option) any later version.
 * 
 * SOPE/J 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 SOPE/J; see the file COPYING. If not, write to the Free Software
 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package org.opengroupware.jope.jetty;

import java.io.File;
import java.net.BindException;
import java.net.URL;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.ResourceHandler;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.resource.Resource;
import org.opengroupware.jope.appserver.core.WOApplication;
import org.opengroupware.jope.foundation.NSJavaRuntime;
import org.opengroupware.jope.servlets.WOServletAdaptor;

/**
 * WOJettyRunner
 * <p>
 * Configures and starts a Jetty HTTP server for Servlet execution.
 */
public class WOJettyRunner extends Object {

  protected final Log log = LogFactory.getLog("WOJettyRunner");
  protected Server server;

  
  /* initialization */

  public WOJettyRunner(Class _appCls) {
    //this.logSystemProperties();
    
    // TODO: for unknown reasons this does not return arguments set with
    //       -DWOPort=1234
    //       => reason is: Jetty clears the System properties?
    String ps = System.getProperty("WOPort");
    if (ps == null || ps.length() == 0)
      ps = "8181";
    
    this.initWithNameAndPort(_appCls.getName(), Integer.parseInt(ps));
  }
  
  public WOJettyRunner(Class _appCls, String[] _args) {
    String appName = null;
    int port = 8181;
    
    for (String arg: _args) {
      if (arg.startsWith("-DWOPort="))
        port = Integer.parseInt(arg.substring(9));
      else if (arg.startsWith("-DWOAppName="))
        appName = arg.substring(9);
    }
    
    if (appName == null)
      appName = _appCls.getName();
    
    this.initWithNameAndPort(appName, port);
  }
  
  public WOJettyRunner(int _port, String _appName) {
    this.initWithNameAndPort(_appName, _port);
  }
  public WOJettyRunner(int _port, Class _appCls) {
    this.initWithNameAndPort(_appCls.getName(), _port);
  }
  
  
  public void initWithNameAndPort(String _appName, int _port) {
    this.log.debug("setting up Jetty ...");
    
    Class appClass = NSJavaRuntime.NSClassFromString(_appName);
    if (appClass == null) {
      this.log.warn("did not find application class: " + _appName);
      appClass = WOApplication.class;
    }
    String shortAppName = appClass.getSimpleName();

    this.server = new Server(_port);
    this.log.info(shortAppName + " starts on port: " + _port);

    
    
    /* Create a Jetty Context. "org.mortbay.jetty.servlet.Context" manages
     * a (or many?) ServletContexts in Jetty.
     * 
     * Note that we map the whole context to the appname!
     */
    Context root = new Context(this.server, "/" + shortAppName,
        Context.NO_SESSIONS | Context.NO_SECURITY);


    /* add a Servlet to the container */
    
    //root.addServlet("org.mortbay.servlet.Dump", "/Dump/*");
    
    /* a ServletHolder wraps a Servlet configuration in Jetty */
    ServletHolder servletHolder = new ServletHolder(WOServletAdaptor.class);
    servletHolder.setName(_appName);
    servletHolder.setInitParameter("WOAppName", _appName);
    
    /* This makes the Servlet being initialize on startup (instead of first
     * request).
     */
    servletHolder.setInitOrder(10); /* positive values: init asap */
    
    /* add Servlet to the Jetty Context */
    
    root.addServlet(servletHolder, "/");
    this.log.info("mapped application to URL: /" + shortAppName);
    
    
    /* add resource handler */
    
    this.addResourceHandler(root, appClass);
    
    /* done */
    
    this.log.debug("finished setting up Servlets.");
  }
  
  protected void addResourceHandler(Context _root, Class appClass) {
    Resource baseResource = null;

    if (baseResource == null) {
      File home = new File(new File(System.getProperty("user.dir")), "www");
      if (home.exists()) {
        try {
          baseResource = Resource.newResource(home.getAbsolutePath());
          this.log.info("mapped public www to: " + home);
        }
        catch (Exception e) {}
      }
    }
    
    if (baseResource == null) {
      /* Map 'www' directory inside the application package */
      URL www = appClass.getResource("www");
      if (www != null) {
        try {
          baseResource = Resource.newResource(www);
          this.log.info("mapped public www to: " + www);
        }
        catch (Exception e) {}
      }
    }
    
    if (baseResource != null) {
      ResourceHandler rh = new ResourceHandler();
      rh.setBaseResource(baseResource);
      this.server.addHandler(rh);
    }
    else
      this.log.debug("did not find a static base resource.");
  }
  
  
  public void logSystemProperties() {
    Properties props = System.getProperties();
    for (Object k: props.keySet()) {
      System.err.println(k + ": " + props.getProperty((String)k));
    }
  }

  /* runner */

  public void run() {
    try {
      this.log.debug("starting Jetty ...");
      this.server.start();
      this.log.debug("Jetty is running ...");
      
      /* execution continues in a Jetty thread ...*/
    }
    catch (BindException be) {
      this.log.error("Could not bind to socket", be);
      
      System.out.flush();
      System.err.println("WOJettyRunner: exit(1).");
      System.exit(1);
    }
    catch (Exception e) {
      this.log.error("Uncaught exception at top level", e);
    }
  }

  /* main entry point */

  public static void main(String[] args) {
    WOJettyRunner runner;

    runner = new WOJettyRunner(8181, "Application");
    runner.run();
  }
}
