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的资料太少了。
最后有什么问题还请大家拍砖
相关推荐
在本文中,我们将深入探讨如何基于VirgoServer进行Spring Osgi Web开发,这是一个涉及OSGi容器、Spring框架和Web应用程序的集成技术。首先,我们需要确保拥有正确的开发环境和工具,包括Spring Tool Suite (STS),...
在Virgo Server中,开发者可以利用Maven的生命周期和插件机制来构建、部署和管理OSGi模块,这大大简化了在Virgo环境中开发和部署应用的工作流程。 在"virgo-tomcat-server-3.5.0.RELEASE"这个压缩包中,包含了Virgo...
在Spring OSGi中,每个服务或组件都被封装成一个独立的Bundle,这些Bundle之间通过OSGi服务注册和查找机制实现依赖注入和通信。开发者需要在Sample中定义 Bundle Activator,它是Bundle启动和停止时执行的入口点,...
《Virgo编程手册》是专为开发者们设计的一份详尽指南,旨在深入解析Virgo这一强大企业级应用服务器的特性和使用方法。本手册由多位行业专家共同编撰,包括Ramnivas Laddad、Colin Yates、Sam Brannen、Rob Harrop等...
标题中的“基于virgo环境的OSGI+Maven的web开发代码下载”表明这是一个使用OSGi(模块化Java系统)和Maven构建的Web应用程序项目,运行在Virgo服务器上。Virgo是SpringSource推出的一个OSGi应用服务器,它支持Spring...
Virgo服务器是SpringSource公司(现已被VMware收购)开发的一款轻量级应用服务器,其核心设计目标是支持OSGi(Open Services Gateway Initiative)规范,使得应用程序能够以模块化的形式进行构建、部署和管理。...
辛苦研究了一个多月,终于搭建成了一个可用于实际应用开发的OSGI-WEB框架,采用OSGi+SpringMVC+Spring+Hibernate+Virgo技术,鉴于此类技术架构甚少,特提供出来供大家参考。 使用步骤: 一、下载STS搭建Osgi开发...
【最新virgo-jetty-server】是一款专为Jetty设计的Virgo服务器,它提供了一种高效、可扩展的运行环境,使得基于Java技术的应用能够快速部署和管理。Virgo服务器是SpringSource公司(现已被VMware收购)开发的一款轻...
此外,部署应用程序到Virgo服务器通常涉及创建或修改`.war`文件,这些文件被设计为在OSGi环境中运行,它们的MANIFEST.MF文件需要包含正确的导入和导出包声明,以便OSGi容器能够正确解析和加载类。 在实际操作中,...
Virgo,全称为OSGi Enterprise Platform,是SpringSource推出的一款基于OSGi规范的应用服务器,旨在为开发和部署企业级应用程序提供一个模块化、可扩展的平台。而Hibernate,则是一款流行的Java ORM(对象关系映射)...
通过使用OSGi,Virgo可以实现服务的热更新和细粒度的依赖管理,这对于持续部署和维护大型系统来说是非常重要的。 描述中的"virgo virgo-web-server"可能是在强调该服务器的版本或者强调它是Virgo项目的核心组成部分...
Virgo是SpringSource公司开发的一款基于OSGi技术的开源企业级服务器,主要用于构建模块化、可扩展的应用系统。Virgo提供了强大的服务管理和部署环境,它支持Spring应用和服务的动态部署和管理,使得开发者能够轻松地...
Virgo是一个先进的应用程序服务器,专为构建、部署和管理服务导向架构(SOA)的应用程序而设计。以下将对Virgo的核心概念、功能、开发与部署流程进行深入解析。 1. Virgo概述: Virgo是由SpringSource公司开发的轻...
3. Spring Virgo服务器:学习如何配置、启动和管理Virgo服务器,以及如何发布和更新OSGi服务。 4. HTML5:掌握HTML5的新特性,如语义化标签、canvas、geolocation、离线存储(localStorage)等,并能编写符合标准的...
学位论文-基于VirgoServer进行Spring Osgi Web开发 开发工具 在基于VirgoServer进行Spring Osgi Web开发中,需要使用以下开发工具: 1. Spring-tool-suite-3.3.0(STS):集成了Maven插件和Virgo插件。 2. Maven...
Virgo是SpringSource推出的一个基于OSGi的服务器,它提供了强大的企业级服务和管理功能,特别适合构建大型、复杂的Java应用程序。 在这个名为"java_osgi_virgo_cms"的项目中,开发者使用了Java、Spring框架以及...
Virgo分屏软件是一款专为提高办公效率设计的桌面应用,尤其适合那些需要同时处理多个任务的用户。这款软件以其独特的小众特性赢得了部分用户的喜爱,成为他们在日常工作中不可或缺的工具。Virgo分屏功能强大,操作...
这是一个基于Virgo服务器,结合Hibernate、Spring和SpringMVC的集成框架模板项目。这个项目旨在为开发者提供一个快速开发企业级应用的基础结构。下面将详细解释这些技术以及它们在项目中的作用。 **Virgo服务器** ...