要完成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>