/*
* 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.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Iterator;
import javax.servlet.RequestDispatcher;
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.ModuleContext;
import de.odysseus.calyxo.base.ModuleSupport;
import de.odysseus.calyxo.control.Dispatcher;
import de.odysseus.calyxo.control.conf.DispatchConfig;
import de.odysseus.calyxo.control.conf.ParamConfig;
import de.odysseus.calyxo.control.conf.ParamsConfig;
/**
* Dispatcher class.
* The dispatcher is responsible to perform a forward, include or redirect
* according to a dispatch config. Each module has its own dispatcher and
* dispatches to actions in its module or to resource paths.
* <p/>
* This class may be subclassed to modify dispatching behaviour.
*
* @author Christoph Beck
*/
public class DefaultDispatcher implements Dispatcher {
protected static final Log log = LogFactory.getLog(DefaultDispatcher.class);
private ModuleContext context;
private ModuleSupport support;
public DefaultDispatcher(ModuleContext context) {
this.context = context;
this.support = ModuleSupport.getInstance(context.getServletContext());
}
/**
* Dispatch request.
* </p>
* If the dispatch element specifies an action, dispatch to that action.
* Otherwise, the dispatch element must specify a path. If that path starts
* with a "/", treat it as a context-relative path and dispatch accordingly.
* If the path doesn't start with a "/" leave it unchanged.
* </p>
* If the redirection flag is set, dispatch by redirection.
* Otherwise, dispatch by forward or include, depending on if the
* response has already been committed.
*
* @param request the request we process
* @param response the response we process
* @param dispatch the dispatch target
* @throws IOException passed through
* @throws ServletException passed through
*/
public void dispatch(
HttpServletRequest request,
HttpServletResponse response,
DispatchConfig dispatch
) throws IOException, ServletException {
String path = null;
if (dispatch.getAction() != null) {
ModuleContext module = context;
if (dispatch.getModule() != null) { // switch module
module = support.getModuleContext(dispatch.getModule());
if (module == null) {
throw new ServletException("Unknown module in '" + dispatch.toInlineString() + "'");
}
}
path = module.getPath(dispatch.getAction());
} else /* if (dispatch.getPath() != null) */ {
path = dispatch.getPath();
}
path = addParams(path, dispatch, "UTF-8");
dispatch(request, response, path, dispatch.isRedirect());
}
/**
* Append the dispatch parameters as URL parameters to the
* specified path.
* @param path uri
* @param config parameters
* @param encoding encoding to be used, e.g. <code>"UTF-8"</code>
* @return uri with query string according to the dispatch parameters.
* @throws UnsupportedEncodingException
*/
protected String addParams(String path, ParamsConfig config, String encoding)
throws UnsupportedEncodingException {
// append query parameters
Iterator params = config.getParamConfigs();
if (params.hasNext()) {
StringBuffer buf = new StringBuffer(path);
// save anchor (if any)
String anchor = null;
int hash = path.indexOf('#');
if (hash > 0) {
anchor = path.substring(hash);
buf.setLength(hash);
}
buf.append(path.indexOf('?') > 0 ? '&' : '?');
do {
ParamConfig param = (ParamConfig)params.next();
buf.append(URLEncoder.encode(param.getName(), encoding));
buf.append('=');
buf.append(URLEncoder.encode(param.getValue(), encoding));
if (params.hasNext()) {
buf.append('&');
} else {
break;
}
} while (true);
// restore anchor (if any)
if (anchor != null) {
buf.append(anchor);
}
return buf.toString();
}
return path;
}
/**
* Dispatch to specified uri.
* If the redirection flag is set, send a redirect to the specified uri.
* Otherwise, use a request dispatcher to dispatch to specified uri:
* Use include, if the response is already committed, forward else.
* In case of redirection, the path is prepended with the request context
* path, if it starts with a slash (<code>'/'</code>).
* @param request the request we process
* @param response the response we process
* @param uri context-relative path (may be an absolute URL if redirection
* is used).
* @param redirect use redirection flag
* @throws IOException passed through
* @throws ServletException passed through
*/
protected void dispatch(HttpServletRequest request, HttpServletResponse response, String uri, boolean redirect) throws IOException, ServletException {
if (redirect) {
if (uri.startsWith("/")) {
uri = request.getContextPath() + uri;
}
response.sendRedirect(response.encodeRedirectURL(uri));
} else {
RequestDispatcher dispatcher = request.getRequestDispatcher(uri);
if (response.isCommitted()) {
if (log.isDebugEnabled())
log.debug("include " + uri);
dispatcher.include(request, response);
} else {
if (log.isDebugEnabled())
log.debug("forward " + uri);
dispatcher.forward(request, response);
}
}
}
}