转载于:http://www.diybl.com/course/3_program/java/javajs/20100719/460908.html
目前在做JBoss下布署String2.5 & Struts2集成的工程,在工程中用Spring2.5 的component scan, Struts2 的convention 和 rest plugins。在JBoss下部署都有问题:
Spring 2.5 component scan所有annotation标注的component都无法找到。原因是JBoss用了VFS,所以在Spring中找不到。
解决方法:使用jboss的 spring-int-vfs 中提供的 org.jboss.spring.vfs.context.VFSClassPathXmlApplicationContext。这个包可以在http://sourceforge.net/projects/jboss/files/JBoss-Spring%20Integration/
下载到,在页面中部,可以找到JBoss-Spring Integration。下面是我的一段代码
ApplicationContext appContext = null;
switch(serverType) {
case tomcat:
appContext = new ClassPathXmlApplicationContext(configFiles);
break;
case jboss:
appContext = new VFSClassPathXmlApplicationContext(configFiles);
break;
}
Struts2 convention, 原因也是JBoss用了VFS,于是URL的protocol都变成了vfsfile, vfszip等等。查看xword的源码,在类com.opensymphony.xwork2.util.finder.ClassFinder的 122行左右,里面是写死的,"jar".equals(location.getProtocol(), "file".equals(location.getProtocol()。
解决方法:由于不能影响到非jboss server,如tomcat,所以不好改写ClassFinder。采用改写struts2 convention插件的方案,替换org.apache.struts2.convention.ActionConfigBuilder如下:
File: src\plugins\convention\src\main\resources\struts-plugin.xml
<bean type="org.apache.struts2.convention.ActionConfigBuilder" class="com.playphone.struts.convention.MyActionConfigBuilder"/>
MyActionConfigBuilder类的内容如下,这里只是简单的解析WEB-INF/classes下的类,因为我没用到jar包需要被认为action的情况,所以简化。
package com.playphone.struts.convention;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.convention.ActionNameBuilder;
import org.apache.struts2.convention.InterceptorMapBuilder;
import org.apache.struts2.convention.PackageBasedActionConfigBuilder;
import org.apache.struts2.convention.ResultMapBuilder;
import org.apache.struts2.convention.StringTools;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.finder.ClassFinder;
import com.opensymphony.xwork2.util.finder.Test;
import com.playphone.spring.EnvVariable;
import com.playphone.spring.ServerType;
/**
* Solve the problem that could not found action under JBoss.
*
* @author <a href="mailto:[email protected]">Roy</a> on Jul 6, 2009
*/
public class MyActionConfigBuilder extends PackageBasedActionConfigBuilder {
private static Log log = LogFactory.getLog(MyActionConfigBuilder.class);
private static final String BASE_FILE = "appContext.xml";
private String[] actionPackages;
private String[] packageLocators;
/**
* Constructs actions based on a list of packages.
*
* @param configuration The XWork configuration that the new package configs and action configs
* are added to.
* @param actionNameBuilder The action name builder used to convert action class names to action
* names.
* @param resultMapBuilder The result map builder used to create ResultConfig mappings for each
* action.
* @param interceptorMapBuilder The interceptor map builder used to create InterceptorConfig mappings for each
* action.
* @param objectFactory The ObjectFactory used to create the actions and such.
* @param redirectToSlash A boolean parameter that controls whether or not this will create an
* action for indexes. If this is set to true, index actions are not created because
* the unknown handler will redirect from /foo to /foo/. The only action that is created
* is to the empty action in the namespace (e.g. the namespace /foo and the action "").
* @param defaultParentPackage The default parent package for all the configuration.
*/
@Inject
public MyActionConfigBuilder(
Configuration configuration,
ActionNameBuilder actionNameBuilder,
ResultMapBuilder resultMapBuilder,
InterceptorMapBuilder interceptorMapBuilder,
ObjectFactory objectFactory,
@Inject("struts.convention.redirect.to.slash") String redirectToSlash,
@Inject("struts.convention.default.parent.package") String defaultParentPackage) {
super(
configuration,
actionNameBuilder,
resultMapBuilder,
interceptorMapBuilder,
objectFactory,
redirectToSlash,
defaultParentPackage);
}
/**
* @param actionPackages (Optional) An optional list of action packages that this should create
* configuration for.
*/
@Inject(value = "struts.convention.action.packages", required = false)
public void setActionPackages(String actionPackages) {
super.setActionPackages(actionPackages);
if (!StringTools.isTrimmedEmpty(actionPackages)) {
this.actionPackages = actionPackages.split("\\s*[,]\\s*");
}
}
/**
* @param packageLocators (Optional) A list of names used to find action packages.
*/
@Inject(value = "struts.convention.package.locators", required = false)
public void setPackageLocators(String packageLocators) {
super.setPackageLocators(packageLocators);
this.packageLocators = packageLocators.split("\\s*[,]\\s*");
}
@Override
@SuppressWarnings("unchecked")
protected Set<Class> findActions() {
if(EnvVariable.getServerType() == ServerType.tomcat) {
return super.findActions();
} else {
Set<Class> classes = new HashSet<Class>();
try {
ClassFinder finder = new ClassFinder(getClassLoaderForFinder(), buildUrls(), true);
// named packages
if (actionPackages != null) {
for (String packageName : actionPackages) {
Test<ClassFinder.ClassInfo> test = getPackageFinderTest(packageName);
classes.addAll(finder.findClasses(test));
}
}
// package locators
if (packageLocators != null) {
for (String packageLocator : packageLocators) {
Test<ClassFinder.ClassInfo> test = getPackageLocatorTest(packageLocator);
classes.addAll(finder.findClasses(test));
}
}
} catch (Exception ex) {
if (log.isErrorEnabled()) {
log.error("Unable to scan named packages", ex);
}
}
return classes;
}
}
private List<URL> buildUrls() throws MalformedURLException {
List<URL> urls = new ArrayList<URL>();
URL classesUrl = getClassLoader().getResource(BASE_FILE);
if(classesUrl == null) {
throw new IllegalStateException("File appContext.xml was not found. The folder WEB-INF/classes discovery base on file classes/appContext.xml.");
}
String baseFilePath = classesUrl.getFile();
URL url = new URL("file", "", baseFilePath.substring(0, baseFilePath.indexOf(BASE_FILE)));
if (log.isInfoEnabled()) {
log.info("Struts2 ActionConfigBuilder, classes directory: " + url.getFile());
}
urls.add(url);
return urls;
}
private ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
}
为了调试方便,可以打开org.apache.struts2.convention log level为debug,然后你就可以清晰地看到哪些action被认出来了。