自定义struts2的url标签的生成

7 篇文章 0 订阅

本文是针对struts2的struts-tags中的s:url标签的使用进行扩展。

 

在J2EE开发中,使用struts2的时候我们很多时候会使用"/"来做URL地址定义,即使用项目的绝对路径。因为如果使用相对路径的话会十分麻烦,谁叫struts2中的"相对",指的并不是存放的目录结构,相对的是目标是指action的命名空间。

 

而由于实际服务器环境中的一些原因,可能会造成s:url生成后的地址的访问资源并不存在!

 

举个例子,当你部署的应用的服务器,在a机器的8080端口中。你可以在内网中使用http://ip:8080/a/ 访问,但外网访问时,却被映射到 http://www.foo.com/abc/a/ 中。

 

这样的情况下,通过s:url生成的url会变成是  /a 开头,而实际上是/abc/a/ 才能访问到你的应用。

 

陷入这个困境2天了,找不到好的解决方案,唯一的方案就是把应用映射到root,然后把应用名改成abc,前面用apache做proxy,这样的方案使用AJP的话会失败,但直接做跳转就成功,不过就丢失了request的IP。

 

所以最后只好把原因是使用s:url这个标签的生成不够自由的关系(没办法,我真的努力了,学艺不精啊)

 

基于把责任都推在s:url上的这个前提上就好办了,我决定重构一下他的标签,变得更加适合我用。

 

查了下源码,最后把目标定于org.apache.struts2.components.ComponentUrlProvider.java(其实方案比较多的,个人喜欢吧)

 

直接上源码

 

 

package org.apache.struts2.components;

import java.util.Map;
import java.util.Properties;

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

import org.apache.commons.lang.StringUtils;

import com.foo.utils.file.PropertiesHelper;

import com.opensymphony.xwork2.util.ValueStack;

/**
 * Override org.apache.struts2.components.ComponentUrlProvider(struts2.core.jar)
 *
 * @author KennyLee <br />
 * @version 1.0.0<br/>
 */
public class ComponentUrlProvider implements UrlProvider {

	private static final String URL_SEPARATOR = "/";
	private static final String PROPERTY_FILE_NAME = "config.properties";
	private static final String URL_CONTEXT_PATH_KEY = "url.context.path";
	private static final String URL_SERVER_NAME_KEY = "url.server.name";
	private static boolean isInitPro = false;
	private static String forceContextPath = "";
	private static String forceServerName = "";
	protected HttpServletRequest httpServletRequest;
	protected HttpServletResponse httpServletResponse;

	protected String includeParams;
	protected String scheme;
	protected String value;
	protected String action;
	protected String namespace;
	protected String method;
	protected boolean encode = true;
	protected boolean includeContext = true;
	protected boolean escapeAmp = true;
	protected String portletMode;
	protected String windowState;
	protected String portletUrlType;
	protected String anchor;
	protected boolean forceAddSchemeHostAndPort;
	protected String urlIncludeParams;
	protected ExtraParameterProvider extraParameterProvider;
	protected UrlRenderer urlRenderer;

	protected Component component;
	@SuppressWarnings("rawtypes")
	private Map parameters;

	/**
	 *
	 * @param component
	 *            The component used to delagete some calls to
	 * @param parameters
	 *            parameters passed from <param...>
	 */
	public ComponentUrlProvider(Component component,
			@SuppressWarnings("rawtypes") Map parameters) {
		this.component = component;
		this.parameters = parameters;
	}

	@Override
	public String determineActionURL(String action, String namespace,
			String method, HttpServletRequest req, HttpServletResponse res,
			@SuppressWarnings("rawtypes") Map parameters, String scheme,
			boolean includeContext, boolean encodeResult,
			boolean forceAddSchemeHostAndPort, boolean escapeAmp) {
		// XXX add by KennyLee 2012-05-08 01:21:37, fix URL by Action.
		String url = null;
		String ori_path = component.determineActionURL(action, namespace,
				method, req, res, parameters, scheme, includeContext,
				encodeResult, forceAddSchemeHostAndPort, escapeAmp);
		url = ori_path;
		String forceContextPath = getForceContextPath();
		if (StringUtils.isNotBlank(ori_path)
				&& StringUtils.isNotBlank(forceContextPath)) {
			if (ori_path.startsWith(URL_SEPARATOR)) {
				url = new StringBuilder().append(URL_SEPARATOR)
						.append(forceContextPath).append(ori_path).toString();
			} else if (ori_path.startsWith("http")) {
				String[] splits = StringUtils.split(ori_path, URL_SEPARATOR);
				int count = 0;
				for (String string : splits) {
					count++;
					url += string;
					if (count == 3) {// At after add serverName
						url += URL_SEPARATOR;
						url += forceContextPath;
					}
					if (count != splits.length) {
						url += URL_SEPARATOR;
					}
				}
			}
		}
		return url;
	}

	public String determineNamespace(String namespace, ValueStack stack,
			HttpServletRequest req) {
		return component.determineNamespace(namespace, stack, req);
	}

	public String findString(String expr) {
		return component.findString(expr);
	}

	@SuppressWarnings("rawtypes")
	public Map getParameters() {
		return parameters;
	}

	public HttpServletRequest getHttpServletRequest() {
		return httpServletRequest;
	}

	public void setHttpServletRequest(HttpServletRequest httpServletRequest) {
		this.httpServletRequest = httpServletRequest;
	}

	public HttpServletResponse getHttpServletResponse() {
		return httpServletResponse;
	}

	public void setHttpServletResponse(HttpServletResponse httpServletResponse) {
		this.httpServletResponse = httpServletResponse;
	}

	public String getIncludeParams() {
		return includeParams;
	}

	public void setIncludeParams(String includeParams) {
		this.includeParams = includeParams;
	}

	public String getScheme() {
		return scheme;
	}

	public void setScheme(String scheme) {
		this.scheme = scheme;
	}

	public boolean isPutInContext() {
		return component instanceof ContextBean;
	}

	public String getVar() {
		return isPutInContext() ? ((ContextBean) component).getVar() : null;
	}

	public String getValue() {
		// XXX add by KennyLee 2012-05-08 00:43:48, fix URL by value.
		if (StringUtils.isNotBlank(value)) {
			StringBuilder link = new StringBuilder();
			String forceContextPath = getForceContextPath();
			String forceServerName = getForceServerName();
			if (StringUtils.startsWith(value, URL_SEPARATOR)
					&& StringUtils.isNotBlank(forceContextPath)) {
				HttpServletRequest request = httpServletRequest;
				String path = request.getContextPath();
				String serverName = request.getServerName();
				if (StringUtils.isNotBlank(forceServerName)) {
					serverName = forceServerName;
				}
				String basePath = request.getScheme() + "://" + serverName
						+ ":" + request.getServerPort();
				link.append(basePath).append(URL_SEPARATOR)
						.append(forceContextPath).append(path).append(value);
			} else {
				link.append(value);
			}
			return link.toString();
		}
		return value;
	}

	/**
	 * <p>
	 * get forceServerName
	 * </p>
	 *
	 * @return
	 */
	private String getForceServerName() {
		if (StringUtils.isBlank(forceServerName)) {
			initProValues();
		}
		return forceServerName;
	}

	/**
	 * <p>
	 * get forceContextPath
	 * </p>
	 *
	 * @return
	 */
	private String getForceContextPath() {
		if (StringUtils.isBlank(forceContextPath)) {
			initProValues();
		}
		return forceContextPath;
	}

	/**
	 * <p>
	 * init property values
	 * </p>
	 */
	private synchronized void initProValues() {
		if (!isInitPro) {
			Properties pro = PropertiesHelper.getInstance()
					.getPropertiesInstance(PROPERTY_FILE_NAME, false);
			if (pro != null) {
				String contextPath = pro.getProperty(URL_CONTEXT_PATH_KEY, "");
				String serverName = pro.getProperty(URL_SERVER_NAME_KEY, "");
				if (StringUtils.isNotBlank(contextPath)) {
					forceContextPath = contextPath;
				}
				if (StringUtils.isNotBlank(serverName)) {
					forceServerName = serverName;
				}
			}
			isInitPro = true;
		}
	}

	public void setValue(String value) {
		this.value = value;
	}

	public String getAction() {
		return action;
	}

	public void setAction(String action) {
		this.action = action;
	}

	public String getNamespace() {
		return namespace;
	}

	public void setNamespace(String namespace) {
		this.namespace = namespace;
	}

	public String getMethod() {
		return method;
	}

	public void setMethod(String method) {
		this.method = method;
	}

	public boolean isEncode() {
		return encode;
	}

	public void setEncode(boolean encode) {
		this.encode = encode;
	}

	public boolean isIncludeContext() {
		return includeContext;
	}

	public void setIncludeContext(boolean includeContext) {
		this.includeContext = includeContext;
	}

	public boolean isEscapeAmp() {
		return escapeAmp;
	}

	public void setEscapeAmp(boolean escapeAmp) {
		this.escapeAmp = escapeAmp;
	}

	public String getPortletMode() {
		return portletMode;
	}

	public void setPortletMode(String portletMode) {
		this.portletMode = portletMode;
	}

	public String getWindowState() {
		return windowState;
	}

	public void setWindowState(String windowState) {
		this.windowState = windowState;
	}

	public String getPortletUrlType() {
		return portletUrlType;
	}

	public ValueStack getStack() {
		return component.getStack();
	}

	public void setPortletUrlType(String portletUrlType) {
		this.portletUrlType = portletUrlType;
	}

	public String getAnchor() {
		return anchor;
	}

	public void setAnchor(String anchor) {
		this.anchor = anchor;
	}

	public boolean isForceAddSchemeHostAndPort() {
		return forceAddSchemeHostAndPort;
	}

	public void setForceAddSchemeHostAndPort(boolean forceAddSchemeHostAndPort) {
		this.forceAddSchemeHostAndPort = forceAddSchemeHostAndPort;
	}

	public void putInContext(String result) {
		if (isPutInContext()) {
			((ContextBean) component).putInContext(result);
		}
	}

	public String getUrlIncludeParams() {
		return urlIncludeParams;
	}

	public void setUrlIncludeParams(String urlIncludeParams) {
		this.urlIncludeParams = urlIncludeParams;
	}

	public ExtraParameterProvider getExtraParameterProvider() {
		return extraParameterProvider;
	}

	public void setExtraParameterProvider(
			ExtraParameterProvider extraParameterProvider) {
		this.extraParameterProvider = extraParameterProvider;
	}

	public UrlRenderer getUrlRenderer() {
		return urlRenderer;
	}

	public void setUrlRenderer(UrlRenderer urlRenderer) {
		this.urlRenderer = urlRenderer;
	}
}

 

说明:

 

回头想想我刚刚举的例子,其中最主要是那个我们预想之外的abc子域名,如果我们能把它都添加在应用名之前就完毕了,而且以后就算前面加多少个子域名目录,也依然可以正常访问。

 

所以,这里我用一个forceContextPath来存放这个值。

 

而另外一个关键的地方是 serverName,即我们访问时用的IP或者域名。用原生的requet方法getServerName的话,有些时候会造成跟访问域名不一致的情况。例如你用 www.foo.com 访问的,但request.getServerName得出的结果是 192.168.1.1 。这种情况下,有时候可能影响变大,但如果域名做了多映射的条件下,也会造成访问障碍,为了安全起见,也提供强制定义的方式来定义serverName。这也是为什么我不使用原生的forceAddSchemeHostAndPort参数来获取完整路径的原因。

 

即另外一个重要的参数值 forceServerName。

 

这个两个值我是放在classes根目录下的config.properties目录下,相信如果专注于J2EE开发的人对这类型文件不会陌生吧。forceContextPath在config.properties中的key为url.context.path,而forceServerName的key为url.server.name。

 

Q:为什么使用value构造URL时,我把http和域名等信息都加上了?

 

A:对于这点,其实我也挺无奈的。因为如果不这样做,我遇到会产生forceContextPath被重叠了两次的情况。即变成了 /abc/abc/ 但我实际需要的只是 /abc/ (注:我的应用映射到root中,而应用名跟子级域名一致,即应用名也叫abc)。也想过用javascript来解决这个问题,不过最后还是懒得再继续探究下去了。因为其实上线环境中,绑定域名其实没多大问题的。

 

附上上面代码中使用到的一个工具类PropertiesHelper的的代码

 

 

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

/**
 * <b>类名称:</b>PropertiesHelper<br/>
 * <b>类描述:</b>java.util.Properties的工具类<br/>
 * <b>创建时间:</b>2009-10-12 下午2:48:05<br/>
 * <b>备注:</b><br/>
 *
 * @author KennyLee <br />
 * @version 1.0.0<br/>
 */
public class PropertiesHelper {

	private volatile static PropertiesHelper uniqueInstance;
	private static final Map<String, Properties> propertiesMap = new HashMap<String, Properties>();

	private PropertiesHelper() {
	};

	public static PropertiesHelper getInstance() {
		if (uniqueInstance == null) {
			synchronized (PropertiesHelper.class) {
				if (uniqueInstance == null) {
					uniqueInstance = new PropertiesHelper();
				}
			}
		}
		return uniqueInstance;
	}

	/**
	 * <p>
	 * Get properties instance by fileName.
	 * </p>
	 *
	 * @param fileName
	 * @param isKeepProperties
	 *            是否保存Properties对象到全局。
	 * @return
	 */
	public Properties getPropertiesInstance(String fileName,
			boolean isKeepProperties) {
		Properties resultInstence = null;
		if (StringUtils.isBlank(fileName))
			return null;
		resultInstence = propertiesMap.get(fileName);
		if (resultInstence == null) {
			resultInstence = new Properties();
			InputStream inputStream = null;
			try {
				inputStream = this.getClass().getClassLoader()
						.getResourceAsStream(fileName);
				resultInstence.load(inputStream);
			} catch (IOException e) {
				resultInstence = null;
				e.printStackTrace();
			} finally {
				IOUtils.closeQuietly(inputStream);
			}
			if (resultInstence != null && isKeepProperties)
				propertiesMap.put(fileName, resultInstence);
		}
		return resultInstence;
	}

	/**
	 * <p>
	 * Get properties instance by fileName.
	 * </p>
	 *
	 * @param fileName
	 * @return
	 */
	public Properties getPropertiesInstance(String fileName) {
		return getPropertiesInstance(fileName, true);
	}
}

 

 

希望对大家有用,或者给你带来一定的启发!

 

EOF

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值