Package de.odysseus.calyxo.control.impl

Source Code of de.odysseus.calyxo.control.impl.DefaultModule$FilteredCommand

/*
* Copyright 2004, 2005, 2006 Odysseus Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.odysseus.calyxo.control.impl;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;

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

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

import de.odysseus.calyxo.base.I18nSupport;
import de.odysseus.calyxo.base.ModuleContext;
import de.odysseus.calyxo.base.conf.ConfigException;
import de.odysseus.calyxo.base.misc.BaseAccessor;
import de.odysseus.calyxo.base.access.AccessSupport;
import de.odysseus.calyxo.control.Action;
import de.odysseus.calyxo.control.Command;
import de.odysseus.calyxo.control.Dispatcher;
import de.odysseus.calyxo.control.ExceptionHandler;
import de.odysseus.calyxo.control.Filter;
import de.odysseus.calyxo.control.MessageSupport;
import de.odysseus.calyxo.control.Module;
import de.odysseus.calyxo.control.Plugin;
import de.odysseus.calyxo.control.PluginContext;
import de.odysseus.calyxo.control.conf.ActionConfig;
import de.odysseus.calyxo.control.conf.ActionsConfig;
import de.odysseus.calyxo.control.conf.ControlConfig;
import de.odysseus.calyxo.control.conf.DispatchConfig;
import de.odysseus.calyxo.control.conf.ExceptionHandlerConfig;
import de.odysseus.calyxo.control.conf.ExceptionHandlersConfig;
import de.odysseus.calyxo.control.conf.FilterConfig;
import de.odysseus.calyxo.control.conf.PluginConfig;
import de.odysseus.calyxo.control.conf.PluginsConfig;
import de.odysseus.calyxo.control.conf.impl.ControlConfigParser;
import de.odysseus.calyxo.control.misc.ControlAccessor;
import de.odysseus.calyxo.control.misc.SimpleExceptionHandler;

/**
* Module class.
*
* @author Christoph Beck
*/
public class DefaultModule implements Module {
  private static final Log log = LogFactory.getLog(DefaultModule.class);

  /**
   * Wrapper for action config and filtered command
   */
  private static final class ActionCommand implements Command {
    final ActionConfig config;
    final Command command;

    ActionCommand(ActionConfig config, Command command) {
      this.config = config;
      this.command = command;
    }
   
    ActionConfig getActionConfig() {
      return config;
    }
   
    public DispatchConfig execute(HttpServletRequest request,
        HttpServletResponse response) throws Exception {
      return command.execute(request, response);
    }
  }

  /**
   * Chain of filters, that prepend an action
   */
  private static final class FilteredCommand implements Command {
    private final FilterConfig config;
    private final Filter first;
    private final Command next;

    FilteredCommand(FilterConfig config, Filter first, Command next) {
      this.config = config;
      this.first = first;
      this.next = next;
    }
     
    public DispatchConfig execute(
      HttpServletRequest request,
      HttpServletResponse response) throws Exception {
      if (config.when(request)) {
        return first.filter(request, response, next);
      } else {
        return next.execute(request, response);
      }
    }
  }

  private ControlConfig config;
  private final ArrayList plugins = new ArrayList();
  private ModuleContext context;
  private PluginContext pluginContext;
  // action commands by path
  private final HashMap commands = new HashMap();
  // exception handlers by id
  private final HashMap handlers = new HashMap();

  /**
   * Constructor.
   *
   * @param context
   * @throws ServletException
   */
  public void init(ModuleContext context) throws ServletException {
    this.context = context;
    this.pluginContext = new PluginContext(context, DefaultAction.class, new DefaultDispatcher(context));

    try {
      initI18n();
    } catch (Exception e) {
      throw new ServletException("Could not initialize i18n", e);
    }
    try {
      initMessages();
    } catch (Exception e) {
      throw new ServletException("Could not initialize messages", e);
    }
    initAccess();
    try {
      initConfig();
    } catch (Exception e) {
      throw new ServletException("Could not initialize configuration", e);
    }
    try {
      initPlugins();
    } catch (Exception e) {
      throw new ServletException("Could not initialize plugins", e);
    }
    try {
      initActions();
    } catch (Exception e) {
      throw new ServletException("Could not initialize actions", e);
    }
    try {
      initExceptionHandlers();
    } catch (Exception e) {
      throw new ServletException("Could not initialize exceptions", e);
    }
  }

  /**
   * Destroy module.
   */
  public void destroy() {
    destroyPlugins();
    destroyConfig();
    destroyAccess();
    destroyMessages();
    destroyI18n();
  }

  /**
   * Initialize plugins.
   * @throws ServletException
   */
  protected void initPlugins() throws Exception {
    log.debug("Initializing plugins");
    PluginsConfig pluginsConfig = config.getPluginsConfig();
    if (pluginsConfig != null) {
      Iterator pluginConfigs = pluginsConfig.getPluginConfigs();
      while (pluginConfigs.hasNext()) {
        PluginConfig pluginConfig = (PluginConfig)pluginConfigs.next();
        log.debug("Initializing plugin " + pluginConfig.getClassName());
        Class c = context.getClassLoader().loadClass(pluginConfig.getClassName());
        Plugin plugin = (Plugin)c.newInstance();
        plugin.init(pluginConfig, pluginContext);
        plugins.add(plugin);
      }
    }
  }

  private void addHandler(ExceptionHandlerConfig config) throws Exception {
    Class clazz = null;
    if (config.getClassName() != null) {
      clazz = context.getClassLoader().loadClass(config.getClassName());
    } else {
      clazz = SimpleExceptionHandler.class;
    }
    ExceptionHandler handler = (ExceptionHandler)clazz.newInstance();
    handler.init(config, context);
    handlers.put(config.getId(), handler);
  }

  private ExceptionHandler getHandler(ExceptionHandlerConfig config) {
    return (ExceptionHandler)handlers.get(config.getId());
  }


  /**
   * Initialize global exception handlers.
   * @throws ServletException
   */
  protected void initExceptionHandlers() throws Exception {
    log.debug("Initializing exception handlers");
    ExceptionHandlersConfig exceptionsConfig = config.getExceptionsConfig();
    if (exceptionsConfig != null) {
      Iterator exceptionConfigs = exceptionsConfig.getExceptionHandlerConfigs();
      while (exceptionConfigs.hasNext()) {
        addHandler((ExceptionHandlerConfig)exceptionConfigs.next());
      }
    }   
    ActionsConfig actionsConfig = this.config.getActionsConfig();
    if (actionsConfig != null) {
      Iterator configs = actionsConfig.getActionConfigs();
      while (configs.hasNext()) {
        ActionConfig actionConfig = (ActionConfig)configs.next();
        Iterator exceptionConfigs = actionConfig.getExceptionHandlerConfigs();
        while (exceptionConfigs.hasNext()) {
          addHandler((ExceptionHandlerConfig)exceptionConfigs.next());
        }
      }
    }
  }

  /**
   * Destroy plugins.
   */
  protected void destroyPlugins() {
    log.debug("Destroying plugins");
    Iterator plugins = this.plugins.iterator();
    while (plugins.hasNext()) {
      Plugin plugin = (Plugin)plugins.next();
      plugin.destroy();
    }
  }

  /**
   * Initialize access
   */
  protected void initAccess() {
    log.debug("Initializing access support");
    AccessSupport support = new AccessSupport();
    // access base via ${calyxo.base}
    support.put("base", new BaseAccessor(context));
    // access control via ${calyxo.control}
    support.put("control", new ControlAccessor(context));
    context.setAttribute(AccessSupport.ACCESS_SUPPORT_KEY, support);
  }

  /**
   * Destroy access.
   */
  protected void destroyAccess() {
    log.debug("Destroying access support");
    context.removeAttribute(AccessSupport.ACCESS_SUPPORT_KEY);
  }

  /**
   * Initialize i18n
   */
  protected void initI18n() throws Exception {
    log.debug("Initializing i18n support");
    String type = context.getInitParameter("i18n-support");
    I18nSupport i18n = null;
    if (type == null) {
      i18n = new DefaultI18nSupport();
    } else {
      i18n = (I18nSupport)context.getClassLoader().loadClass(type).newInstance();
    }
    context.setAttribute(I18nSupport.I18N_SUPPORT_KEY, i18n);
  }

  /**
   * Destroy i18n.
   */
  protected void destroyI18n() {
    log.debug("Destroying i18n support");
    context.removeAttribute(I18nSupport.I18N_SUPPORT_KEY);
  }

  /**
   * Initialize message
   */
  protected void initMessages() throws Exception {
    log.debug("Initializing message support");
    String type = context.getInitParameter("message-support");
    MessageSupport messages = null;
    if (type == null) {
      messages = new DefaultMessageSupport();
    } else {
      messages = (MessageSupport)context.getClassLoader().loadClass(type).newInstance();
    }
    context.setAttribute(MessageSupport.MESSAGE_SUPPORT_KEY, messages);
  }

  /**
   * Destroy message.
   */
  protected void destroyMessages() {
    log.debug("Destroying message support");
    context.removeAttribute(MessageSupport.MESSAGE_SUPPORT_KEY);
  }

  /**
   * Parse control configuration
   */
  protected void initConfig() throws Exception {
    log.debug("Initializing control configuration");
    String config = context.getInitParameter("config");
    ArrayList list = new ArrayList();
    // add config-files
    StringTokenizer tokens = new StringTokenizer(config, ",");
    while (tokens.hasMoreTokens()) {
      String token = tokens.nextToken().trim();
      URL url = null;
      url = context.getServletContext().getResource(token);
      if (url == null) {
        throw new ConfigException("Could not find resource " + token);
      } else {
        list.add(url);
      }
    }
    URL[] inputs = (URL[])list.toArray(new URL[list.size()]);
    ControlConfigParser parser =
      new ControlConfigParser(context);
    this.config = parser.parse(inputs);
    context.setAttribute(ACTIONS_CONFIG_KEY, this.config.getActionsConfig());
  }

  /**
   * Destroy control.
   */
  protected void destroyConfig() {
    log.debug("Destroying control configuration");
    context.removeAttribute(ACTIONS_CONFIG_KEY);
  }

  /**
   * Instantiate/initialize actions.
   */
  protected void initActions() throws Exception {
    // Instantiate and initialized action chains
    ActionsConfig actionsConfig = this.config.getActionsConfig();
    if (actionsConfig != null) {
      Iterator configs = actionsConfig.getActionConfigs();
      while (configs.hasNext()) {
        ActionConfig actionConfig = (ActionConfig)configs.next();
        String className = actionConfig.getClassName();
        Class clazz = null;
        if (className == null) {
          clazz = pluginContext.getDefaultActionClass();
        } else {
          clazz = context.getClassLoader().loadClass(className);
        }
        Action action = (Action)clazz.newInstance();
        action.init(actionConfig, context);
        Command command = chain(actionConfig.getFilterConfigs(), action);
        ActionCommand item = new ActionCommand(actionConfig, command);
        commands.put(actionConfig.getPath(), item);
      }
    }
  }

  /**
   * Get the context
   */
  public ModuleContext getContext() {
    return context;
  }

  /**
   * Build filter chain.
   * @param configs filter configs
   * @param last action to be filtered
   * @return linked action chain
   * @throws Exception
   */
  private Command chain(Iterator configs, Action last) throws Exception {
    if (configs.hasNext()) {
      FilterConfig config = (FilterConfig)configs.next();
      Class clazz = null;
      if (config.getName() != null) {
        clazz = pluginContext.getFilterClass(config.getName());
        if (clazz == null) {
          throw new ConfigException("Unknown filter name in '" + config.toInlineString() + "'");
        }
      } else {
        clazz = context.getClassLoader().loadClass(config.getClassName());
      }
      Filter filter = (Filter)clazz.newInstance();
      filter.init(config, context);
      return new FilteredCommand(config, filter, chain(configs, last));
    } else {
      return last;
    }
  }

  /**
   * Handle exception.
   */
  protected DispatchConfig handle(
    HttpServletRequest request,
    HttpServletResponse response,
    ActionConfig action,
    Exception exception) throws IOException, ServletException {

    ExceptionHandlerConfig config = action.findExceptionHandlerConfig(exception);
    if (config == null) {
      if (exception instanceof IOException) {
        throw (IOException)exception;
      } else if (exception instanceof ServletException) {
        throw (ServletException)exception;
      } else {
        throw new ServletException(exception);
      }
    }
    return getHandler(config).handle(request, response, action, exception);
  }

  /**
   * Lookup action command
   */
  private ActionCommand findActionCommand(String path) {
    ActionCommand command = (ActionCommand)commands.get(path);
//    while (command == null && path.length() > 1) { // try prefix patterns
//      command = (ActionCommand)commands.get(path + "/*");
//      path = path.substring(0, path.lastIndexOf('/'));
//    }
    if (command == null) {
      command = (ActionCommand)commands.get("/*");
    }
    return command;
  }
 
  /**
   * Process request.
   * <p/>
   * Invoke action and dispatch according to the result.
   * @param request the request we are processing
   * @param response the response we are creating
   * @param path module path used to lookup action configuration
   * @throws ServletException if action lookup fails or if action instantiation
   *  fails. Also exceptions passed through by the invoker are caught, wrapped
   *  and thrown.
   * @throws IOException passed through from dispatcher
   */
  public void process(
    HttpServletRequest request,
    HttpServletResponse response,
    String path
  ) throws ServletException, IOException {
    if (log.isTraceEnabled()) {
      log.trace("Module '" + context.getName() + "': process " + path + " begin");
    }

    request.setAttribute(MODULE_PATH_KEY, path);

    // lookup action command
    ActionCommand command = findActionCommand(path);
    if (command == null) {
      throw new ServletException("Unknown action path '" + path + "' in module '" + context.getName() + "'");
    }

    request.setAttribute(ACTION_PATH_KEY, command.getActionConfig().getPath());

    // execute action command
    DispatchConfig result = null;
    try {
      result = command.execute(request, response);
    } catch (Exception e) {
      result = handle(request, response, command.getActionConfig(), e);
    }

    // dispatch request
    if (result != null) {
      Dispatcher dispatcher = pluginContext.getDefaultDispatcher();
      String name = result.getDispatcher();
      if (name == null) {
        name = command.getActionConfig().getDispatcher();
      }
      if (name != null) {
        dispatcher = pluginContext.getDispatcher(name);
        if (dispatcher == null) {
          throw new ServletException("Unknown dispatcher '" + name + "' for action '" + config.toInlineString() + "'");
        }
      }
      dispatcher.dispatch(request, response, result);
    }
  }
}
TOP

Related Classes of de.odysseus.calyxo.control.impl.DefaultModule$FilteredCommand

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.