要完成Spring DM与Struts2的集成,主要完成两件事
- 将Struts2集成到OSGi环境中。
- 将Spring DM与Struts2集成,使Struts2可以使用Spring DM中定义的Bean。
此文章采用的方法不是Spring DM Web Extender的方式,由Spring DM Web是将工程手动注册到Web容器中,暂时只支持tomcat与jetty。
Struts2集成到OSGi环境中
这步的目的是让Struts2的bundle可以读到其实bundle下的Struts配置文件,由于Struts2本身只处理了classpath下面的三类配置文件,分别为struts-default.xml、struts-plugin.xml、struts.xml文件,所以首先我们要增加读取bundle下面配置文件的ConfigurationProvider,代码在org.apache.struts2.dispatcher.Dispatcher,将此类的源代码找出来,然后修改private void init_TraditionalXmlConfigurations()方法
private void init_TraditionalXmlConfigurations() {
String configPaths = initParams.get("config");
if (configPaths == null) {
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
String[] files = configPaths.split("\\s*[,]\\s*");
for (String file : files) {
if (file.endsWith(".xml")) {
if ("xwork.xml".equals(file)) {
configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
} else {
configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
throw new IllegalArgumentException("Invalid configuration file name");
}
}
//FIXME 增加读取bundle中配置文件的类。
configurationManager.addContainerProvider(new OSGiXmlConfigurationProvider("struts_osgi.xml",false,servletContext));
}
其中OSGiXmlConfigurationProvider的代码见附件, 主要的代码在loadConfigurationFiles方法中
Bundle[] bundles=Activator.getContext().getBundles();
ByteArrayOutputStream bos=new ByteArrayOutputStream();
for(Bundle bundle:bundles){
try {
//不加载本bundle的struts.xml文件
if(bundle.getSymbolicName().equals(Activator.getContext().getBundle().getSymbolicName())) continue;
url=bundle.getEntry(DEFAULT_CONFIG_NAME);
if(url==null) continue;
is=url.openStream();
IOUtils.copy(is, bos);
is=new ByteArrayInputStream(bos.toByteArray());
InputSource in = new InputSource(is);
in.setSystemId(is.toString());
Document doc=DomHelper.parse(in,dtdMappings);
try{
//增加默认名称空间
addDefaultNamespace(doc,bundle.getHeaders().get(WEB_CONTEXT),HashFile.getHash(bos.toByteArray()),bundle.getBundleId());
}catch(Exception e){
LOG.error("增加包名到缓存失败", e, new String[]{});
}
docs.add(doc);
} catch (IOException e) {
LOG.error("加载ID为"+bundle.getBundleId()+" 的bundle中的配置文件失败!", e, new String[]{});
}finally{
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
接下来就是注册Struts2的servlet,由于采用的是Equinox的http实现,所以要用HttpService来注册
package com.yinhai.osgi.web.struts2;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import org.apache.log4j.Logger;
import org.apache.struts2.dispatcher.ng.servlet.StrutsServlet;
import org.eclipse.equinox.http.helper.BundleEntryHttpContext;
import org.eclipse.equinox.http.helper.ContextPathServletAdaptor;
import org.eclipse.equinox.jsp.jasper.JspServlet;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
import org.osgi.util.tracker.ServiceTracker;
/**
* 用于注册struts2的servlet和web bundle的上下文。
* @author Dream.Lee
* @version 2013-6-5
*/
public class StrutsController implements BundleListener {
private static Logger logger=Logger.getLogger(StrutsController.class);
public static final String WEB_CONTEXT="Web-Context";
public static final String STATIC_SUBFIX="_res";
private HttpService httpService;
public static final String WEB_CONTENT_PATH = "/WebContent";
public void activate(ComponentContext context) {
registerController("/*.do");
//对已有的bundle进行web上下文注册
Bundle[] bundles=Activator.getContext().getBundles();
for(Bundle bundle:bundles){
if(bundle.getHeaders().get(WEB_CONTEXT)!=null&&bundle.getState()==Bundle.ACTIVE){
try {
ServiceTracker<HttpService, HttpService> sTracker=new ServiceTracker<HttpService, HttpService>(bundle.getBundleContext(), HttpService.class.getName(), null);
sTracker.open();
HttpService httpService=sTracker.getService();
if(httpService!=null){
httpService.registerResources(bundle.getHeaders().get(WEB_CONTEXT)+STATIC_SUBFIX,WEB_CONTENT_PATH,
null);
// 注册JSP文件
HttpContext commonContext = new BundleEntryHttpContext(
bundle, WEB_CONTENT_PATH);
Servlet adaptedJspServlet = new ContextPathServletAdaptor(
new JspServlet(bundle,
WEB_CONTENT_PATH), bundle.getHeaders().get(
WEB_CONTEXT));
httpService.registerServlet(
bundle.getHeaders().get(WEB_CONTEXT) + "/*.jsp",
adaptedJspServlet, null, commonContext);
}
// sTracker.close();
} catch (NamespaceException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
}
Activator.getContext().addBundleListener(this);
Activator.getContext().addBundleListener(new SynchronousBundleListener() {
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public void bundleChanged(BundleEvent event) {
Bundle bundle=event.getBundle();
if (bundle.getHeaders().get(WEB_CONTEXT) != null) {
switch(event.getType()){
case BundleEvent.STOPPING:
ServiceReference sf=bundle.getBundleContext().getServiceReference(HttpService.class.getName());
HttpService httpService = (HttpService) bundle
.getBundleContext().getService(sf);
try{
httpService.unregister(bundle.getHeaders().get(WEB_CONTEXT)+STATIC_SUBFIX);
logger.info("卸载"+bundle.getHeaders().get(WEB_CONTEXT));
}catch(Exception e){
logger.info("卸载失败:"+bundle.getHeaders().get(WEB_CONTEXT), e);
}finally{
bundle.getBundleContext().ungetService(sf);
}
break;
}
}
}
});
}
public void setHttpService(HttpService httpService) {
this.httpService = httpService;
}
private void registerController(String webcontext) {
try {
StrutsServlet controller = new StrutsServlet();
httpService.registerServlet(webcontext, controller, null, null);
controller.init();
logger.info("已注册:" + webcontext);
} catch (Exception e) {
logger.error("注册扩展的:" + webcontext + "失败", e);
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public void bundleChanged(BundleEvent event) {
Bundle bundle = event.getBundle();
if (bundle.getHeaders().get(WEB_CONTEXT) != null) {
if (event.getType() == BundleEvent.STARTED) {
logger.info("注册 "
+ bundle.getHeaders().get(WEB_CONTEXT)+STATIC_SUBFIX + ","
+ bundle.getHeaders().get(WEB_CONTEXT)
+ "/*.jsp");
BundleContext context = bundle.getBundleContext();
ServiceReference sf=context.getServiceReference(HttpService.class.getName());
HttpService httpService = (HttpService) bundle
.getBundleContext().getService(sf);
try {
// 注册静态文件
httpService.registerResources(bundle.getHeaders().get(WEB_CONTEXT)+STATIC_SUBFIX,
WEB_CONTENT_PATH, null);
// 注册JSP文件
Servlet adaptedJspServlet = new ContextPathServletAdaptor(
new JspServlet(bundle,
WEB_CONTENT_PATH), bundle
.getHeaders().get(WEB_CONTEXT));
httpService.registerServlet(bundle.getHeaders()
.get(WEB_CONTEXT) + "/*.jsp",
adaptedJspServlet, null, null);
} catch (NamespaceException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
}
}
}
}
此类采用声明式的方式发布,配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <component name="strutscontroller"> <implementation class="com.yinhai.osgi.web.struts2.StrutsController"/> <reference name="HttpService" interface="org.osgi.service.http.HttpService" bind="setHttpService" unbind="unsetHttpService" policy="dynamic"/> </component>