/*
 * $Revision: 223 $ $Date: 2007-10-14 17:07:40 +0900 $
 * Copyright (C) 2004-2006 SUGIMOTO Ken-ichi
 */

package feat2;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Iterator;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import feat2.config.ConfigReader;
import feat2.config.ConfigurationException;
import feat2.config.ConfigurationIOException;
import feat2.config.FeatConfig;
import feat2.config.FeatureConfig;
import feat2.config.FileLocator;
import feat2.config.ServletFileLocator;
import feat2.validation.ValidatorUtil;
import feat2.xmlcommand.XMLCommandProcessor;

public class ActionServlet extends HttpServlet {

    private static final long serialVersionUID = "$Id: ActionServlet.java 223 2007-10-14 08:07:40Z sugimotokenichi $".hashCode();
    protected static Log log = LogFactory.getLog(ActionServlet.class);

    private long checktime;
    private FileLocator configLocation;
    private Processor processor;
    private XMLCommandProcessor xcProcessor;


    public ActionServlet() {
        checktime = 0L;
    }


    public void init() throws ServletException {

        Util.setFeatConfig(getServletContext(), readConfig());

        processor = new Processor();
        xcProcessor = new XMLCommandProcessor();

    }


    private FeatConfig readConfig() throws ServletException {

        FeatConfig ret = null;
        try {

            FileResourceManager rm = FileResourceManager.getInstance();
            FileLocator locator = getConfigFileLocation();
            FileLocator appRoot = new ServletFileLocator("/", getServletContext());
            ret = ConfigReader.parseFeatConfig(appRoot, locator);

            FeatErrors errs = new FeatErrors(rm);

            boolean valid = ret.validate(errs);
            FeatError[] errors = errs.getErrors(FeatError.ERROR);

            if ( !valid || errors.length > 0 ) {

                log.fatal(getResource("exception.ActionServlet.configerror"));

                for (int i = 0; i < errors.length; i++) {
                    log.fatal(errors[i].getMessage());
                }

                throw new ServletException("exception.ActionServlet.configerror");

            }

            log.info(rm.getString("ActionServlet.config.loaded", locator.toString(), null, null));

        }

        catch (ConfigurationIOException ex) {
            log.fatal(getResource("exception.ActionServlet.configioerror"), ex);
            throw new ServletException(ex);
        }

        catch (ConfigurationException ex) {
            log.fatal(getResource("exception.ActionServlet.configerror"), ex);
            throw new ServletException(ex);
        }

        catch (URISyntaxException ex) {
            throw new ServletException(ex);
        }

        return ret;

    }


    private FileLocator getConfigFileLocation() throws ServletException {

        if ( configLocation == null ) {

            // サーブレットのパラメータからコンフィグファイルの場所を特定する

            String configFilePath = getServletConfig().getInitParameter("config");
            if (configFilePath == null) {
                configFilePath = "/WEB-INF/feat-config.xml";
            }

            try {

                configLocation = new ServletFileLocator(configFilePath, getServletContext());

            }
            catch (URISyntaxException ex) {
                throw new ServletException(ex);
            }

        }

        return configLocation;

    }


    /**
     * 設定ファイルが最後にロードした時刻より新しくなっていたらtrueを返す。
     * @return boolean
     */
    private boolean checkConfig() throws ServletException {

        if ( System.currentTimeMillis() - checktime > 1000L ) {

            try {

                // feat-config.xmlが更新されたか

                FileLocator locator = getConfigFileLocation();

                long lastUpdate = Util.getConfigLastUpdateTime(getServletContext());
                if ( lastUpdate < locator.getLastModified() )
                    return true;

                // 各Featureファイルが更新されたか

                FeatConfig conf = Util.getFeatConfig(getServletContext());

                Iterator it = conf.getFeatures();
                while( it.hasNext() ) {

                    FeatureConfig fc = (FeatureConfig)it.next();
                    if ( lastUpdate < fc.getConfigFileLocator().getLastModified() )
                        return true;

                }

            }

            catch(IOException ex) {
                log.warn("", ex);
            }

            finally {
                checktime = System.currentTimeMillis();
            }

        }

        return false;

    }


    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
            ServletException, IOException {

        process(req, resp);

    }


    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws
            ServletException, IOException {

        process(req, resp);

    }


    /**
     * GET, POSTメソッドの処理
     */
    private void process(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        try {

            ServletContext sctx = getServletContext();

            // 設定ファイルが新しくなっていたらリロード

            FeatConfig config = null;
            synchronized(this) {

                if ( checkConfig() )
                    Util.setFeatConfig(sctx, readConfig());

                config = Util.getFeatConfig(sctx);

            }


            // フィーチャー、コマンド名を取り出す

            String[] fc = null;
            String xmlCommandHeader = req.getHeader("x-xmlcommand");
            if ( !ValidatorUtil.isBlankOrNull(xmlCommandHeader) ) {
                fc = xcProcessor.process(req, resp);
            }

            else {
                fc = getFeatureCommand(req);
            }

            // 実行

            CommandContext cctx = new CommandContext(config, sctx, req, resp, fc[0], fc[1]);
            processor.process(cctx);

        }

        catch (HttpNotFoundException ex) {
            log.warn(ex.getMessage());
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }

        /*
        catch (NotHandledException ex) {
            Throwable cause = ex.getCause();
            log.error("アプリケーションで例外が発生", cause);
            throw new ServletException(ex.getCause());
        }*/

        catch (FeatException ex) {
            throw new ServletException(ex);
        }
    }


    /**
     * リクエストからフィーチャー名、コマンド名を取り出す。
     * @param req
     * @return [0]がフィーチャー名、[1]がコマンド名
     */
    private String[] getFeatureCommand(HttpServletRequest req) {

        String[] ret = new String[2];

        String servletPath = req.getServletPath();

        if ( servletPath != null ) {

            String[] pathInfo = StringUtil.split(servletPath, "/");
            if ( pathInfo.length > 0 )
                ret[0] = pathInfo[0];
            if ( pathInfo.length > 1 )
                ret[1] = StringUtil.substringBefore(pathInfo[1], ".");

        }

        return ret;

    }


    private String getResource(String name) {

        return FileResourceManager.getInstance().getStringResource(name);

    }

}
