/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* 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 com.alibaba.citrus.webx.servlet;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.FileUtil.*;
import static com.alibaba.citrus.util.ServletUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static org.springframework.web.context.support.WebApplicationContextUtils.*;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.citrus.webx.WebxComponents;
import com.alibaba.citrus.webx.WebxRootController;
import com.alibaba.citrus.webx.config.WebxConfiguration;
import com.alibaba.citrus.webx.context.WebxComponentsContext;
import com.alibaba.citrus.webx.util.RequestURIFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.WebApplicationContext;
/**
* 初始化spring容器的filter。
*
* @author Michael Zhou
*/
public class WebxFrameworkFilter extends FilterBean {
private final Logger log = LoggerFactory.getLogger(getClass());
private String parentContextAttribute;
private WebxComponents components;
private RequestURIFilter excludeFilter;
private RequestURIFilter passthruFilter;
private String internalPathPrefix;
/** 用于在servletContext中保存parent context的attribute key。 */
public final String getParentContextAttribute() {
return parentContextAttribute;
}
/** 设置用于在servletContext中保存parent context的attribute key。 */
public final void setParentContextAttribute(String parentContextAttribute) {
this.parentContextAttribute = trimToNull(parentContextAttribute);
}
/** 设置要排除掉的URL。 */
public void setExcludes(String excludes) {
excludeFilter = new RequestURIFilter(excludes);
}
/**
* 设置不需要执行pipeline的URL。该功能可被用于将webx作为其它servlet的filter,这样,
* 其它的servlet可以使用webx所提供的request context功能,例如:session等。
*/
public void setPassthru(String passthru) {
passthruFilter = new RequestURIFilter(passthru);
}
/** 取得所有components的信息。 */
public WebxComponents getWebxComponents() {
return components;
}
/** 初始化filter。 */
@Override
protected final void init() throws Exception {
WebApplicationContext parentContext = findParentContext();
if (parentContext instanceof WebxComponentsContext) {
components = ((WebxComponentsContext) parentContext).getWebxComponents();
WebxConfiguration configuration = components.getParentWebxConfiguration();
if (configuration != null) {
internalPathPrefix = configuration.getInternalPathPrefix();
internalPathPrefix = normalizeAbsolutePath(internalPathPrefix, true); // 规格化成/internal
}
}
WebxRootController rootController = components.getWebxRootController();
if (passthruFilter != null) {
if (rootController instanceof PassThruSupportable) {
((PassThruSupportable) rootController).setPassthruFilter(passthruFilter);
} else {
log.warn(
"You have specified Passthru Filter in /WEB-INF/web.xml. "
+ "It will not take effect because the implementation of WebxRootController ({}) does not support this feature.",
rootController.getClass().getName());
}
}
}
/**
* 在<code>ServletContext</code>中查找parent context。
* <ul>
* <li>假如未指定<code>parentContextAttribute</code>,则查找默认的attribute key。</li>
* <li>假如指定了init-param <code>parentContextAttribute</code>,则查找指定的attribute
* key。假如没找到,则报错。</li>
* </ul>
*/
private WebApplicationContext findParentContext() {
WebApplicationContext parentContext = null;
String parentContextAttribute = getParentContextAttribute();
if (parentContextAttribute == null) {
parentContext = getWebApplicationContext(getServletContext());
} else {
parentContext = getWebApplicationContext(getServletContext(), parentContextAttribute);
assertNotNull(parentContext, "No WebApplicationContext \"%s\" found: not registered?",
parentContextAttribute);
}
return parentContext;
}
protected void initFrameworkFilter() {
}
@Override
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String path = getResourcePath(request);
if (isExcluded(path)) {
log.debug("Excluded request: {}", path);
chain.doFilter(request, response);
return;
} else {
log.debug("Accepted and started to process request: {}", path);
}
try {
getWebxComponents().getWebxRootController().service(request, response, chain);
} catch (IOException e) {
throw e;
} catch (ServletException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}
boolean isExcluded(String path) {
// 如果指定了excludes,并且当前requestURI匹配任何一个exclude pattern,
// 则立即放弃控制,将控制还给servlet engine。
// 但对于internal path,不应该被排除掉,否则internal页面会无法正常显示。
if (excludeFilter != null && excludeFilter.matches(path)) {
if (!isInternalRequest(path)) {
return true;
}
}
return false;
}
private boolean isInternalRequest(String path) {
if (internalPathPrefix == null) {
return false;
}
if (path.equals(internalPathPrefix)) {
return true;
}
if (path.startsWith(internalPathPrefix) && path.charAt(internalPathPrefix.length()) == '/') {
return true;
}
return false;
}
}