`

刚完成Struts的Virgo插件,分享一下设计思路和Virgo OSGi内部的独特机制

    博客分类:
  • osgi
阅读更多

2014-3-13:因有很多网友有类似的问题,我把我的代码放到sourceforge里了,网址如下:

https://sourceforge.net/projects/s4vp/

-----------------------------------------------------------

首先说一下此插件的功能:使用此插件可以在有一个主Struts Bundle的前提下,编写其他附属struts bundle,他们的Struts配置信息可以共享,而内部JSP等文件的定位是独立的。

 

可能目前人们对OSGi的关注度不高,Virgo的可能更少,要不然Spring也不会捐出去,但我相信OSGi也将是一个趋势,比如anroid的低层容器就是通过OSGi实现的。

 

在选择OSGi容器的时候,我本来是想用felix的,因为Struts2官方的OSGi的插件是使用felix的,但存在一些缺点:

 

  • 只能用Struts的2.1.8.1的版本,和felix的1.X.X版本(Felix都到4了好不好)
  • 不支持默认的JSP result,只支持freemark和velocity
  • 在tomcat的容器上再创建OSGi容器,在性能上肯定有所损失
  • 无法在主web context和其bundle里共享spring的配置信息(我是在配置Spring Security时发现的)
  • 最严重的是Struts项目对OSGi的投入力度太少,热情不高,我在apache官网提了一个BUG,居然要到下一个大版本才给发布。。。

 

后来,我就想直接使用Spring的OSGi容器,不过已经捐给Eclipse了,也就是Virgo。当然virgo也有缺点,就是没有支持的struts插件,这也是我不得不花很时间写此插件的原因。

 

我的设计思路主要参照Struts的OSGi插件和Virgo的snap插件。其中使用struts-osgi插件主要实现Struts的配置OSGi化。而virgo-snap主要实现JSP等文件的定位问题。

 

配置的OSGi化:通过修改原Struts的配置来实现的。

实现要点:(大家可以对照Struts-osgi插件的实现)

 

  • 增加struts-plugin.xml配置文件,将其在META-INF中export出去,主struts bundle会自动加载此配置文件

  • 配置文件中ObjectFactory,PackageProvider,ClassloaderInterface需要重新实现

  • PackageProvider中主要是加入对附属Struts bundle的过滤,找到之后就加载bundle内的struts.xml配置文件,使之与主bundle的配置合并。然后就是对之后容器内的bundle进行监听,新增bundle就添加,停用的bundle就删除

  • ObjectFactory中主要是加入对无法找到的类,重新定位到当前附属bundle内查找实现

  • 获取当前附属bundle的方法是,在加载package的配置时,记录package name与bundle的对应关系,然后在之后查询是,通过ActionContext间接获取当前的Action所属的package name,再对应其bundle就OK了

  • ClassloaderInterface与上面的原理差不多就不多说了

  • 还有就是Freemark,Velocity和其他静态资源的访问定位,差不多都是通过修改内容文件的入口点至当前bundle就行,具体可以看原Struts-osgi插件的实现

 

bundel内文件的定位:通过更改ServletContext来编译jsp来实现的。

实现要点:(大家可以对照Virgo-Snaps插件的实现)

 

 

  • 因tomcat最终定位JSP文件位置的地方是在ServletContext内,而ServletContext的实现只能通过容器来获取。最终实现是通过监听和搜索主Struts bundle发布的ServletContext服务得到的。

  • JSP的编译用的是原tomcat内的JspServlet类,可以直接实例化,然后wrapper下容器ServletContext,改变其获取资源的方式,用其对servlet进行init,然后这个servlet就可以编译jsp了

  • 其中JspServlet的初始化时所使用的classloader一定要改成附属bundle的Classloader,而这个classloader还必须是WebBundleClassLoader,所以必须将附属bundle通过virgo的 Transformer 进行转化,而classloader可以通过Spring进来的WebBundleClassLoaderFactory进行创建

    要注意的是web bundle默认的classpath的路径为"WEB-INF/classes",如果你的JSP文件是想放在根目录就必须添加"."到classpath中

    相关代码:
    @Override
        public void transform(GraphNode<InstallArtifact> installGraph, InstallEnvironment installEnvironment) throws DeploymentException {
            installGraph.visit(new ExceptionThrowingDirectedAcyclicGraphVisitor<InstallArtifact, DeploymentException>() {
    
                public boolean visit(GraphNode<InstallArtifact> node) throws DeploymentException {
                    InstallArtifact installArtifact = node.getValue();
                    if (OsgiUtil.isNeedStrutsVirgoSupport(installArtifact)) {
                        BundleManifest bundleManifest = OsgiUtil.getBundleManifest((BundleInstallArtifact) installArtifact);
                        doTransform(bundleManifest, getSourceUrl(installArtifact));
                    }
                    return true;
                }
            });
        }
        
        void doTransform(BundleManifest bundleManifest, URL sourceUrl) throws DeploymentException {
            logger.info("Transforming bundle at '{}'", sourceUrl.toExternalForm());
            
            try {
                bundleManifest.setModuleType(STRUTS_MODULE_TYPE);
                bundleManifest.setHeader("SpringSource-DefaultWABHeaders", "true");
                bundleManifest.setHeader(Constants.BUNDLE_CLASSPATH, ".");
                InstallationOptions installationOptions = installOptionFactory.createDefaultInstallOptions();
                this.manifestTransformer.transform(bundleManifest, sourceUrl, installationOptions, false);
            } catch (IOException ioe) {
                logger.error(String.format("Error transforming manifest for struts '%s' version '%s'",
                    bundleManifest.getBundleSymbolicName().getSymbolicName(), bundleManifest.getBundleVersion()), ioe);
                throw new DeploymentException("Error transforming manifest for struts '" + bundleManifest.getBundleSymbolicName().getSymbolicName()
                    + "' version '" + bundleManifest.getBundleVersion() + "'", ioe);
            }
        }
     
    Transformer的实现类需要通过spring配置文件进行声明:
    <osgi:reference id="webBundleManifestTransformer" interface="org.eclipse.gemini.web.core.WebBundleManifestTransformer"/>
    <osgi:reference id="webBundleClassLoaderFactory" interface="org.eclipse.gemini.web.tomcat.spi.WebBundleClassLoaderFactory"/>
    <osgi:reference id="eventLogger" interface="org.eclipse.virgo.medic.eventlog.EventLogger"/>
    	
    <osgi:service ref="lifecycleListener" interface="org.eclipse.virgo.kernel.install.artifact.InstallArtifactLifecycleListener"/>
    	
    <osgi:service ref="transformer" interface="org.eclipse.virgo.kernel.install.pipeline.stage.transform.Transformer" ranking="1500"/>
    	
     

            <bean id="lifecycleListener" class="org.apache.struts2.osgi.virgo.internal.deployer.StrutsLifecycleListener">
    		<constructor-arg ref="webBundleClassLoaderFactory"/>
    		<constructor-arg ref="eventLogger"/>
    	</bean>
    	
    	<bean id="transformer" class="org.apache.struts2.osgi.virgo.internal.deployer.StrutsVirgoTransformer">
    		<constructor-arg ref="webBundleManifestTransformer"/>
    	</bean>
    		
    
    	<bean id="strutsFactoryMonitor" class="org.apache.struts2.osgi.virgo.internal.StrutsFactoryMonitor" init-method="start" destroy-method="stop">
    		<constructor-arg ref="bundleContext"/>
    		<constructor-arg ref="eventLogger"/>
    	</bean>
      

  • 下面要解决的问题是主Struts bundle和附属Struts bundle之间如何进行信息传递的问题。这个问题的产生原因是,我们监听事件的bundle是我们的插件, 而不是主bundle。

    如果你要问,为什么不通过主Struts bundle来实现此功能。那么我的回答是:当然可以,不过你要把class文件放到你的主struts bundle内。也就是说class文件在哪个bundle内,哪么这个类的实现就在哪个bundle的领空内,这个你无法改变,因为这个是virgo内部的classloader机制的基础。

    解决问题的方法是通过我们的插件来传递信息,传递的方向是:附属bundle -> 插件 ->主bundle。之所以是这个方向,是因为主bundle才是外界访问的接口。

    传递信息前需要做的一件事情是:收集信息。

    · 首先通过Virgo的InstallArtifactLifecycleListenerSupport,在附属bundle在安装时,动态添加一个StrutsFactory服务,里面包含重要的classloader等信息
    · 然后插件在factorymonitor里监听这个服务,收到信息后,通过查找对应的主bundle来创建struts实例,并动态添加为服务
    · 最后主bundle通过监听这个服务,获取struts类,而struts类里包含编译jsp文件的所有功能

    所以我们可以通过添加一个简单的filter,将jsp文件通过struts来进行解析编译,就OK了



    代码如下:
    final class StrutsLifecycleListener extends InstallArtifactLifecycleListenerSupport {
    
        private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
        private final Map<InstallArtifact, ServiceRegistrationTracker> registrationTrackers = new ConcurrentHashMap<InstallArtifact, ServiceRegistrationTracker>();
    
        private final WebBundleClassLoaderFactory classLoaderFactory;
    
        private final EventLogger eventLogger;
    
        public StrutsLifecycleListener(WebBundleClassLoaderFactory classLoaderFactory, EventLogger eventLogger) {
            this.classLoaderFactory = classLoaderFactory;
            this.eventLogger = eventLogger;
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void onStarted(InstallArtifact installArtifact) throws DeploymentException {
            if (OsgiUtil.isNeedStrutsVirgoSupport(installArtifact)) {
                Bundle bundle = ((BundleInstallArtifact) installArtifact).getBundle();
                BundleManifest bundleManifest = OsgiUtil.getBundleManifest((BundleInstallArtifact) installArtifact);
    
                ServiceRegistration<StrutsFactory> registration = createAndRegisterStrutsFactoryService(bundle, bundleManifest);
    
                ServiceRegistrationTracker registrationTracker = new ServiceRegistrationTracker();
                registrationTracker.track(registration);
    
                this.registrationTrackers.put(installArtifact, registrationTracker);
            }
        }
    
        ServiceRegistration<StrutsFactory> createAndRegisterStrutsFactoryService(Bundle bundle, BundleManifest bundleManifest) {
            logger.info("Creating a StrutsFactory for bundle '{}'", bundle);
            StrutsFactory strutsFactory = new WebAppStrutsFactory(bundle, this.classLoaderFactory, this.eventLogger);
    
            StrutsHostDefinition hostDefinition = OsgiUtil.getStrutsHostHeader(bundleManifest);
    
            Dictionary<String, String> serviceProperties= new Hashtable<String, String>();
            serviceProperties.put(Scope.PROPERTY_SERVICE_SCOPE, Scope.SCOPE_ID_GLOBAL); // expose service outside any containing scope
            serviceProperties.put(StrutsFactory.FACTORY_NAME_PROPERTY, hostDefinition.getSymbolicName());
            serviceProperties.put(StrutsFactory.FACTORY_RANGE_PROPERTY, hostDefinition.getVersionRange().toParseString());
    
            ServiceRegistration<StrutsFactory> registration = bundle.getBundleContext().registerService(StrutsFactory.class, strutsFactory, serviceProperties);
            return registration;
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public void onStopping(InstallArtifact installArtifact) {
            logger.info("Destroying StrutsFactory for bundle '{}'", installArtifact.getName());
            ServiceRegistrationTracker serviceRegistrationTracker = this.registrationTrackers.remove(installArtifact);
            if (serviceRegistrationTracker != null) {
                serviceRegistrationTracker.unregisterAll();
            }
        }
        
    }
     
    当然也需要spring配置文件的声明,之前的代码已经贴出来了。

    因为附属bundle的ServletContext需要transform的转换,所以我们不能同时获取,因此,我们需要先发布下StrutsFacotry服务,言外之意是我已经收集好前面的信息了;然后通过监听此服务,当收到信息时再监听ServletContext服务,来收集全部信息。
    代码如下:
    package org.apache.struts2.osgi.virgo.internal;
    
    import java.util.Collection;
    import java.util.Dictionary;
    import java.util.HashSet;
    import java.util.Hashtable;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    
    import org.apache.struts2.osgi.virgo.internal.deployer.StrutsFactory;
    import org.eclipse.virgo.medic.eventlog.EventLogger;
    import org.eclipse.virgo.util.osgi.ServiceRegistrationTracker;
    import org.osgi.framework.Bundle;
    import org.osgi.framework.BundleContext;
    import org.osgi.framework.Constants;
    import org.osgi.framework.InvalidSyntaxException;
    import org.osgi.framework.ServiceEvent;
    import org.osgi.framework.ServiceListener;
    import org.osgi.framework.ServiceReference;
    import org.osgi.framework.ServiceRegistration;
    import org.osgi.util.tracker.ServiceTracker;
    import org.osgi.util.tracker.ServiceTrackerCustomizer;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public final class StrutsFactoryMonitor implements ServiceTrackerCustomizer<StrutsFactory, Object> {
    	public final static String KEY_HOST_ID = "struts.host.id";
    	public final static String KEY_CONTEXT_PATH = "struts.context.path";
    	public final static String KEY_NAME = "struts.name";
    	
    	
    	private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
    	private final BundleContext bundleContext;
    	private final ServiceTracker<StrutsFactory, Object> strutsFactoryTracker;
    	private final EventLogger eventLogger;
    
    	public StrutsFactoryMonitor(BundleContext bundleContext, EventLogger eventLogger) {
    		this.bundleContext = bundleContext;
    		this.strutsFactoryTracker = new ServiceTracker<StrutsFactory, Object>(bundleContext, StrutsFactory.class, this);
    		this.eventLogger = eventLogger;
    	}
    
    	public void start() {
    		this.strutsFactoryTracker.open();
    	}
    
    	public void stop() {
    		this.strutsFactoryTracker.close();
    	}
    
    	public Object addingService(ServiceReference<StrutsFactory> reference) {
    		StrutsFactory strutsFactory = this.bundleContext.getService(reference);
    		if (strutsFactory != null) {
    			BundleContext strutsBundleContext = reference.getBundle().getBundleContext();
    			StrutsBinder strutsBinder = new StrutsBinder(strutsBundleContext, strutsFactory,
    					StrutsHostDefinition.fromServiceReference(reference), this.eventLogger);
    			strutsBinder.start();
    			return strutsBinder;
    		}
    		logger.warn("Unable to create StrutsBinder due to missing StrutsFactory");
    		return null;
    	}
    
    	public void modifiedService(ServiceReference<StrutsFactory> reference, Object service) {
    	}
    
    	public void removedService(ServiceReference<StrutsFactory> reference, Object service) {
    		logger.info("Destroying StrutsBinder for bundle '{}'", reference.getBundle());
    		((StrutsBinder) service).destroy();
    	}
    
    	private static enum StrutsLifecycleState {
    		AWAITING_INIT, INIT_SUCCEEDED, INIT_FAILED
    	}
    
    	private static final class StrutsBinder implements ServiceListener {
    		private static final String SNAP_ORDER = "struts.order";
    		private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
    		private final BundleContext context;
    		private final HostSelector hostSelector;
    		private final Object hostStateMonitor = new Object();
    		private final Object strutsStateMonitor = new Object();
    		private boolean queriedInitialHosts = false;
    		private ServiceReference<ServletContext> hostReference;
    		private final ServiceRegistrationTracker registrationTracker = new ServiceRegistrationTracker();
    		private final EventLogger eventLogger;
    		private Struts struts;
    		private final StrutsFactory factory;
    
    		public StrutsBinder(final BundleContext context, final StrutsFactory strutsFactory, final StrutsHostDefinition hostDefinition,
    				final EventLogger eventLogger) {
    			this.context = context;
    			this.hostSelector = new HostSelector(hostDefinition, (String) context.getBundle().getHeaders().get("Module-Scope"));
    			this.eventLogger = eventLogger;
    			this.factory = strutsFactory;
    		}
    
    		private void start() {
    			registerHostListener();
    		}
    
    		private void registerHostListener() {
    			try {
    				this.context.addServiceListener(this, "(objectClass=javax.servlet.ServletContext)");
    				logger.info("Listening for hosts to be registered.");
    				searchForExistingHost();
    			} catch (InvalidSyntaxException e) {
    				logger.error("Filter syntax invalid");
    			}
    		}
    
    		private void hostPublished(ServiceReference<ServletContext> hostReference) {
    			assert (!Thread.holdsLock(this.hostStateMonitor));
    
    			ServletContext servletContext = this.context.getService(hostReference);
    			if (servletContext != null) {
    				synchronized (this.hostStateMonitor) {
    					Collection<ServiceReference<ServletContext>> references = new HashSet<ServiceReference<ServletContext>>();
    					references.add(hostReference);
    					ServiceReference<ServletContext> matchedHost = this.hostSelector.selectHost(references);
    
    					if (matchedHost == null) {
    						logger.info("Host {} did not match {} ", hostReference.getBundle().getSymbolicName(), this.hostSelector.getHostDefinition().toString());
    						return;
    					}
    				}
    
    				Bundle hostBundle = hostReference.getBundle();
    
    				StrutsLifecycleState newState = StrutsLifecycleState.INIT_FAILED;
    
    				Struts struts = this.factory.createStruts(new Host(hostBundle, servletContext));
    				try {
    					logger.info("Initializing struts '{}'", struts.getContextPath());
    					struts.init();
    
    					newState = StrutsLifecycleState.INIT_SUCCEEDED;
    
    					logger.info("Publishing struts '{}'", struts.getContextPath());
    					publishStrutsService(struts, hostBundle);
    
    				} catch (ServletException e) {
    					this.eventLogger.log(StrutsLogEvents.STRUTS_INIT_FAILURE,
    							servletContext.getContextPath() + " --> " + struts.getContextPath(), e.getMessage());
    				} finally {
    					synchronized (this.strutsStateMonitor) {
    						if (newState == StrutsLifecycleState.INIT_SUCCEEDED) {
    							this.struts = struts;
    						}
    					}
    				}
    			}
    		}
    
    		private void publishStrutsService(Struts struts, Bundle hostBundle) {
    			Hashtable<Object, Object> props = struts.getStrutsProperties();
    			Dictionary<String, Object> serviceProperties = new Hashtable<String, Object>();
    
    			for (Object key : props.keySet()) {
    				serviceProperties.put(key.toString(), props.get(key));
    			}
    
    			String strutsOrder = (String) serviceProperties.get(SNAP_ORDER);
    			if (strutsOrder != null) {
    				serviceProperties.put(Constants.SERVICE_RANKING, Integer.parseInt(strutsOrder));
    			}
    			serviceProperties.put(KEY_HOST_ID, Long.toString(hostBundle.getBundleId()));
    			serviceProperties.put(KEY_CONTEXT_PATH, struts.getContextPath());
    			serviceProperties.put(KEY_NAME, (String) this.context.getBundle().getHeaders().get("Bundle-Name"));
    
    			ServiceRegistration<Struts> registration = this.context.registerService(Struts.class, struts, serviceProperties);
    			this.registrationTracker.track(registration);
    			logger.info("Published struts service for '{}'", struts.getContextPath());
    		}
    
    		private void destroy() {
    			try {
    				destroyStruts();
    			} finally {
    				unregisterHostListener();
    			}
    		}
    
    		private void unregisterHostListener() {
    			logger.info("No longer listening for hosts to be registered.");
    			this.context.removeServiceListener(this);
    		}
    
    		public void serviceChanged(ServiceEvent event) {
    			synchronized (this.hostStateMonitor) {
    				while (!queriedInitialHosts) {
    					try {
    						this.hostStateMonitor.wait();
    					} catch (InterruptedException e) {
    						Thread.currentThread().interrupt();
    					}
    				}
    			}
    
    			int type = event.getType();
    			@SuppressWarnings("unchecked")
    			ServiceReference<ServletContext> serviceReference = (ServiceReference<ServletContext>) event.getServiceReference();
    
    			if (type == ServiceEvent.REGISTERED && this.hostReference == null) {
    				hostPublished(serviceReference);
    			} else if (type == ServiceEvent.UNREGISTERING) {
    				if (serviceReference.equals(this.hostReference)) {
    					hostRetracted(serviceReference);
    				}
    			}
    		}
    
    		private void hostRetracted(ServiceReference<ServletContext> serviceReference) {
    			try {
    				destroyStruts();
    			} finally {
    				synchronized (this.hostStateMonitor) {
    					this.hostReference = null;
    				}
    			}
    		}
    
    		private void destroyStruts() {
    			Struts s = null;
    			synchronized (this.strutsStateMonitor) {
    				s = this.struts;
    				this.struts = null;
    			}
    			this.registrationTracker.unregisterAll();
    			if (s != null) {
    				logger.info("Retracted struts service for '{}'", s.getContextPath());
    				s.destroy();
    			}
    		}
    
    		private void searchForExistingHost() {
    			ServiceReference<ServletContext> existingHost = null;
    			Collection<ServiceReference<ServletContext>> candidates = findHostCandidiates();
    			if (candidates != null && !candidates.isEmpty()) {
    				logger.info("{} host candidates found", candidates.size());
    			} else {
    				logger.info("No host candidates found");
    			}
    
    			synchronized (this.hostStateMonitor) {
    				try {
    					existingHost = this.hostSelector.selectHost(candidates);
    					this.queriedInitialHosts = true;
    				} finally {
    					this.hostStateMonitor.notifyAll();
    				}
    			}
    			if (existingHost != null) {
    				hostPublished(existingHost);
    			}
    		}
    
    		private Collection<ServiceReference<ServletContext>> findHostCandidiates() {
    			try {
    				return this.context.getServiceReferences(ServletContext.class, null);
    			} catch (InvalidSyntaxException ise) {
    				throw new IllegalStateException("Unexpected invalid filter syntax with null filter", ise);
    			}
    		}
    	}
    }
     关于spring配置的问题同上就不再多说了。

    Struts类的关键代码
     /**
         * {@inheritDoc}
         * 
         * @throws ServletException
         */
        public final void init() throws ServletException {
            logger.info("Initializing struts '{}'", this.strutsBundle.getSymbolicName());
            StrutsServletContext servletContext = new StrutsServletContext(this.host.getServletContext(), this.strutsBundle);
            servletContext.setAttribute(WebContainer.ATTRIBUTE_BUNDLE_CONTEXT, this.strutsBundle.getBundleContext());
    
            this.strutsClassLoader = this.classLoaderFactory.createWebBundleClassLoader(this.strutsBundle);
    
            try {
                ((Lifecycle) strutsClassLoader).start();
            } catch (LifecycleException e) {
                logger.error("Failed to start struts's class loader", e);
                throw new ServletException("Failed to start web bundle's class loader", e);
            }
            
            this.initServlet(servletContext);
    
            this.eventLogger.log(StrutsLogEvents.STRUTS_BOUND, this.strutsBundle.getSymbolicName());
        }
        
        private final void initServlet(final StrutsServletContext servletContext) throws ServletException {
        	try {
                ManagerUtils.doWithThreadContextClassLoader(this.strutsClassLoader, new ClassLoaderCallback<Void>() {
                    public Void doWithClassLoader() throws ServletException {
                    	try {
    						WebAppStruts.this.servlet = (Servlet)SERVLET_CLASS.newInstance();
    					} catch (Exception e) {
    						throw new ServletException("Create Servlet Fail", e);
    					}
                    	
                    	ImmutableServletConfig servletConfig = new ImmutableServletConfig(servletContext);
                    	WebAppStruts.this.servlet.init(servletConfig);
                    	
                        return null;
                    }
                });
            } catch (IOException e) {
                logger.error("Unexpected IOException from servlet init", e);
                throw new ServletException("Unexpected IOException from servlet init", e);
            }
        }
    
        /**
         * {@inheritDoc}
         */
        public final void destroy() {
            ClassLoader strutsClassLoader = this.strutsClassLoader;
    
            if (strutsClassLoader != null) {
                try {
                    ((Lifecycle) strutsClassLoader).stop();
                } catch (LifecycleException e) {
                    logger.error("Failed to stop struts's class loader", e);
                    throw new StrutsException("Failed to stop web bundle class loader", e);
                }
            } else {
                // TODO Log warning that class loader was null during destroy
            }
            this.eventLogger.log(StrutsLogEvents.STRUTS_UNBOUND, this.strutsBundle.getSymbolicName());
        }
    
        /**
         * {@inheritDoc}
         */
        public final void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            if (servlet != null) {
            	servlet.service(request, response);
            } else {
                // TODO Log warning that dispatcher is not present
                throw new ServletException("handleRequest invoked when virtual container was null");
            }
        }
     

 

先说这么多,没有将全部代码放出来,主要是因为刚刚实现我想要的功能,细节地方和测试工作还没有完善,放出来也是不能直接使用的东西,就不误导大家了。而写这篇文章,主要是在写代码的过程中一直在摸索,写完了没有整体的理解,这次也算是给自己一次沉淀的过程,也希望能给使用virgo的同学一点可以参考的资料,毕竟国内关于virgo的资料太少了。

最后有什么问题还请大家拍砖

 

 

分享到:
评论
3 楼 snoopy7713 2014-03-14  
[2014-03-14 17:55:06.651]  TCP Connection(2)-127.0.0.1 <DE0002E> Installation of bundle 'org.apache.struts.struts2-virgo-plugin' version '1.0.0' failed. org.eclipse.virgo.nano.serviceability.Assert$FatalAssertionException: input packageImports must not contain duplicate items
at org.eclipse.virgo.nano.serviceability.Assert.isNull(Assert.java:103)
at org.eclipse.virgo.kernel.userregion.internal.importexpansion.AbstractTrackedPackageImports.convertImportedPackageListToMap(AbstractTrackedPackageImports.java:323)
at org.eclipse.virgo.kernel.userregion.internal.importexpansion.BundleTrackedPackageImports.getInitialImportedPackages(BundleTrackedPackageImports.java:58)
at org.eclipse.virgo.kernel.userregion.internal.importexpansion.BundleTrackedPackageImports.<init>(BundleTrackedPackageImports.java:43)
at org.eclipse.virgo.kernel.userregion.internal.importexpansion.StandardTrackedPackageImportsFactory.create(StandardTrackedPackageImportsFactory.java:37)
at org.eclipse.virgo.kernel.userregion.internal.importexpansion.ImportExpansionHandler.mergeImports(ImportExpansionHandler.java:161)
at org.eclipse.virgo.kernel.userregion.internal.importexpansion.ImportExpansionHandler.mergePromotedImports(ImportExpansionHandler.java:152)
at org.eclipse.virgo.kernel.userregion.internal.importexpansion.ImportExpansionHandler.mergePromotedImports(ImportExpansionHandler.java:145)
at org.eclipse.virgo.kernel.userregion.internal.importexpansion.ImportExpansionHandler.expandImports(ImportExpansionHandler.java:122)
at sun.reflect.GeneratedMethodAccessor61.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:131)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:131)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy42.expandImports(Unknown Source)
at org.eclipse.virgo.kernel.deployer.core.internal.ImportExpandingTransformer.expandImports(ImportExpandingTransformer.java:107)
at org.eclipse.virgo.kernel.deployer.core.internal.ImportExpandingTransformer$ImportExpandingGraphVisitor.visit(ImportExpandingTransformer.java:80)
at org.eclipse.virgo.util.common.ThreadSafeGraphNode.visitInternal(ThreadSafeGraphNode.java:193)
at org.eclipse.virgo.util.common.ThreadSafeGraphNode.visit(ThreadSafeGraphNode.java:184)
at org.eclipse.virgo.kernel.deployer.core.internal.ImportExpandingTransformer.transform(ImportExpandingTransformer.java:58)
at org.eclipse.virgo.kernel.install.pipeline.stage.transform.internal.TransformationStage.doProcessGraph(TransformationStage.java:54)
at org.eclipse.virgo.kernel.install.pipeline.stage.AbstractPipelineStage.process(AbstractPipelineStage.java:41)
at org.eclipse.virgo.kernel.install.pipeline.internal.StandardPipeline.doProcessGraph(StandardPipeline.java:62)
at org.eclipse.virgo.kernel.install.pipeline.internal.CompensatingPipeline.doProcessGraph(CompensatingPipeline.java:73)
at org.eclipse.virgo.kernel.install.pipeline.stage.AbstractPipelineStage.process(AbstractPipelineStage.java:41)
at org.eclipse.virgo.kernel.install.pipeline.internal.StandardPipeline.doProcessGraph(StandardPipeline.java:62)
at org.eclipse.virgo.kernel.install.pipeline.stage.AbstractPipelineStage.process(AbstractPipelineStage.java:41)
at org.eclipse.virgo.kernel.deployer.core.internal.PipelinedApplicationDeployer.driveInstallPipeline(PipelinedApplicationDeployer.java:359)
at org.eclipse.virgo.kernel.deployer.core.internal.PipelinedApplicationDeployer.doInstall(PipelinedApplicationDeployer.java:185)
at org.eclipse.virgo.kernel.deployer.core.internal.PipelinedApplicationDeployer.install(PipelinedApplicationDeployer.java:140)
at org.eclipse.virgo.kernel.deployer.core.internal.PipelinedApplicationDeployer.deploy(PipelinedApplicationDeployer.java:253)
at org.eclipse.virgo.kernel.deployer.management.StandardDeployer.deploy(StandardDeployer.java:52)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:279)
at com.sun.jmx.mbeanserver.ConvertingMethod.invokeWithOpenReturn(ConvertingMethod.java:193)
at com.sun.jmx.mbeanserver.ConvertingMethod.invokeWithOpenReturn(ConvertingMethod.java:175)
at com.sun.jmx.mbeanserver.MXBeanIntrospector.invokeM2(MXBeanIntrospector.java:117)
at com.sun.jmx.mbeanserver.MXBeanIntrospector.invokeM2(MXBeanIntrospector.java:54)
at com.sun.jmx.mbeanserver.MBeanIntrospector.invokeM(MBeanIntrospector.java:237)
at com.sun.jmx.mbeanserver.PerInterface.invoke(PerInterface.java:138)
at com.sun.jmx.mbeanserver.MBeanSupport.invoke(MBeanSupport.java:252)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
at sun.rmi.transport.Transport$1.run(Transport.java:177)
at sun.rmi.transport.Transport$1.run(Transport.java:174)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:556)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:811)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:670)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
2 楼 snoopy7713 2014-03-13  
需要看一下,你的代码说的挺模糊的。我的联系方式QQ 16200780
1 楼 huangcanqin 2013-03-17  
不知道你现在struts 有没有整合成功?我按照你的思路还是出现一些bug:
Exception starting filter struts2 java.lang.NoClassDefFoundError: Could not initialize class com.opensymphony.xwork2.util.DomHelper$DOMBuilder
at com.opensymphony.xwork2.util.DomHelper.parse(DomHelper.java:107)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.loadConfigurationFiles(XmlConfigurationProvider.java:904)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.loadDocuments(XmlConfigurationProvider.java:154)
at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.init(XmlConfigurationProvider.java:121)
at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reloadContainer(DefaultConfiguration.java:179)
at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:66)
at org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Dispatcher.java:380)
at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:424)
at org.apache.struts2.dispatcher.ng.InitOperations.initDispatcher(InitOperations.java:69)
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.init(StrutsPrepareAndExecuteFilter.java:51)
at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:277)
at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:258)
at org.apache.catalina.core.ApplicationFilterConfig.setFilterDef(ApplicationFilterConfig.java:382)
at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:103)
at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4638)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5294)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:895)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:871)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:615)
at org.eclipse.gemini.web.tomcat.internal.TomcatServletContainer.startWebApplication(TomcatServletContainer.java:125)
at org.eclipse.gemini.web.internal.StandardWebApplication.start(StandardWebApplication.java:95)
at org.eclipse.virgo.web.core.internal.WebBundleLifecycleListener.onStarted(WebBundleLifecycleListener.java:122)
at org.eclipse.virgo.kernel.install.artifact.internal.StandardArtifactStateMonitor.onStarted(StandardArtifactStateMonitor.java:271)
at org.eclipse.virgo.kernel.install.artifact.internal.AbstractInstallArtifact.asyncStartSucceeded(AbstractInstallArtifact.java:319)
at org.eclipse.virgo.kernel.install.artifact.internal.AbstractInstallArtifact.access$0(AbstractInstallArtifact.java:316)
at org.eclipse.virgo.kernel.install.artifact.internal.AbstractInstallArtifact$StateMonitorSignal.signalSuccessfulCompletion(AbstractInstallArtifact.java:252)
at org.eclipse.virgo.kernel.core.internal.BundleStartTracker$1.run(BundleStartTracker.java:140)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:619)

相关推荐

Global site tag (gtag.js) - Google Analytics