`
Kenny.Lee
  • 浏览: 511112 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

自定义struts2的url标签的生成

 
阅读更多

本文是针对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

1
2
分享到:
评论

相关推荐

    struts2 标签库 帮助文档

    Struts 2 标签库(文档手册) Tags-API-CLSW-JSP &lt;%@ taglib prefix="s" uri="/struts-tags" %&gt; 就能使用struts2.0的标签库 下面就介绍每个标签的具体应用实例说明:按字母排列 A: 1. 2. &lt;s:a href=""&gt;&lt;/s:a&gt;-...

    Struts2+API+标签全解+网页说明

    component标签:生成一个自定义的组件。 div标签:AJAX标签,生成一个div片段。 fielderror标签:输出异常提示信息。 tabbedPanel:AJAX标签,生成HTML中的Tab页。 tree标签:生成一个树形结构。 treenode标签:...

    Struts2属性文件详解

    该属性指定是否允许在Struts 2标签中使用表达式语法,因为通常都需要在标签中使用表达式语法,故此属性应该设置为true,该属性的默认值是true. struts.devMode 该属性设置Struts 2应用是否使用开发模式.如果设置该属性...

    JavaWeb框架Wint.zip

    wint 是一个基于mvc易用的 java web框架,抛开了struts2 繁琐的配置,结合django,RoR,webx,play等框架的特点,使用约定优于配置的原则,使开发者能快速的搭建web和进行快速开发。 wint提供的主要功能有: 模板与...

    Struts标记库

    JSP视窗组件所使用的struts标记库由四类标记组成: Bean标记:用来在JSP页中管理bean 逻辑标记:用来在JSP页中控制流程 HTML标记:用来生成HTML标记,在表单中显示数据,使用会话ID对URL进行编程 模板标记:使用...

    JSP+Struts+JSTL+EL新闻发布系统jsp

    前台制作采用标签制,分为系统标签和用户自义义标签两种,系统标签是自代的,用户不可以修改,而自定义标签则是用户根据相应的功能自己定制出来的,固此,可以自行删除或修改! 4、秉承JAVA开源理念,本新闻发布系统...

    从J2SE到J2EE知识点介绍

    3. Struts 2 标签库说明及使用 160 4. set 描述 169 5. text 描述 170 6. property 描述 170 7. Struts的异常处理 171 8. Struts的上传与下载 178 五、 Hibernate 192 (一) 前言 192 (二) 持久化层 193 (三) ORM介绍...

    Ladon5.8大型网络渗透扫描器&源码

    漏洞检测包含MS17010、Weblogic、ActiveMQ、Tomcat、Struts2等,密码爆破11种含数据库(Mysql、Oracle、MSSQL)、FTP、SSH(Linux主机)、VNC、Windows密码(IPC、WMI、SMB)、Weblogic后台、Rar压缩包密码等,Web指纹识别...

    Java学习笔记-个人整理的

    \contentsline {chapter}{Contents}{2}{section*.1} {1}Java基础}{17}{chapter.1} {1.1}基本语法}{17}{section.1.1} {1.2}数字表达方式}{17}{section.1.2} {1.3}补码}{19}{section.1.3} {1.3.1}总结}{23}{...

    Wicket 8.X开发文档(中文翻译+英文原版)

    10. Wicket链接和URL生成 10.1。PageParameters 10.2。可收藏的链接 10.3。使用标记wicket自动创建可收藏的链接:链接 10.4。外部链接 10.5。无状态链接 10.6。生成结构清晰的URL 10.7。摘要 11. Wicket模型和表格 ...

    Spring面试题

    2.spring支持用户自定义的切面 面向切面编程(aop)是对面向对象编程(oop)的补充, 面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面。 AOP从程序运行角度考虑程序的结构,...

    java开源包2

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

    Spring攻略(第二版 中文高清版).part2

    11.6 为一个应用的领域类生成CRUD控制器和视图 454 11.6.1 问题 454 11.6.2 解决方案 454 11.6.3 工作原理 455 11.7 国际化(I18n)信息属性 458 11.7.1 问题 458 11.7.2 解决方案 458 11.7.3 工作原理...

    ssh(structs,spring,hibernate)框架中的上传下载

    6. <property name="url" value="jdbc:oracle:thin:@localhost:1521:ora9i"/> 7. 8. 9. 10. !-- Hibernate会话工厂配置 //--> 11. 12. class="org.springframework.orm.hibernate3.LocalSessionFactoryBean...

    JAVA上百实例源码以及开源项目

    Java生成自定义控件源代码 2个目标文件 Java实现HTTP连接与浏览,Java源码下载 1个目标文件 摘要:Java源码,网络相关,HTTP  Java实现HTTP连接与浏览,Java源码下载,输入html文件地址或网址,显示页面和HTML源文件...

    JAVA上百实例源码以及开源项目源代码

    Java生成自定义控件源代码 2个目标文件 Java实现HTTP连接与浏览,Java源码下载 1个目标文件 摘要:Java源码,网络相关,HTTP  Java实现HTTP连接与浏览,Java源码下载,输入html文件地址或网址,显示页面和HTML源文件...

    java开源包1

    JCaptcha4Struts2 是一个 Struts2的插件,用来增加验证码的支持,使用时只需要用一个 JSP 标签 (&lt;jcaptcha:image label="Type the text "/&gt; ) 即可,直接在 struts.xml 中进行配置,使用强大的 JCaptcha来生成验证码...

Global site tag (gtag.js) - Google Analytics