1.概述
1. jetty的deploymentManager作用是帮助创建ContextHandler并加入到jetty的合适位置,以方便提供静态和动态的服务。比如,把某个位置的war包部署到jetty。
2. deploymentManager一个主要功能是连接app Provider和applifecycle。
3. 有两种典型的appProvider,一种是webappProvider,主要是监视某目录的*.war,并把他们提交到app lifecycle graph中。一种是contextProvider,监视一个目录的*.xml文件,并用jetty xml为app lifecycle graph创建contextHandler(通常是WebAppContext)
4. webAppProvider的start和stop和server是同步的,server.start的时候调用deployer的start。context是在将要部署的war包中发现的。在monitoredDirName中的所有war包和名称非CVS的目录都会被部署,war包的context名称是报名,root.war是/.如果extractWars被设置为true,那么war包会先被解压到一个tmp dir,如果有未编译的jsp等东西,就需要解压。
(如果war包没有解压servletContxt.getRealPath就会返回一个空,这样导致vmx的处理会出问题。)
2.初始化过程
在使用java -jar start.jar命令启动时,start.jar 的main函数在解析完命令行,设置好classpath等后调用invokeMain时,默认是调用Xmlconfiguration的main函数,这里会把一个个的xml拿出来解析(通过configure函数),参照文档中的标签逐一处理,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
for (; i < cfg.size(); i++) { Object o = cfg.get(i); if (o instanceof String) continue; XmlParser.Node node = (XmlParser.Node) o; try { String tag = node.getTag(); if ("Set".equals(tag)) set(obj, node); else if ("Put".equals(tag)) put(obj, node); else if ("Call".equals(tag)) call(obj, node); else if ("Get".equals(tag)) get(obj, node); … } |
考虑如下的jetty配置:
<Call name=”addBean”>
<Arg>
<New id=”DeploymentManager” class=”org.eclipse.jetty.deploy.DeploymentManager”>
<Set name=”contexts”>
<Ref id=”Contexts” />
</Set>
<Call name=”setContextAttribute”>
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
</Call>
<!– Providers of Apps via WAR file existence.
Configured to behave similar to the legacy WebAppDeployer –>
<Call name=”addAppProvider”>
<Arg>
<New class=”org.eclipse.jetty.deploy.providers.WebAppProvider”>
<Set name=”monitoredDir”>D:/alibaba/web-deploy/jetty-server/webapps</Set>
<Set name=”scanInterval”>0</Set>
<Set name=”extractWars”><Property name=”jetty.extractWars” default=”true”/></Set>
<Set name=”defaultsDescriptor”>D:/alibaba/web-deploy/jetty-server/conf/webdefault.xml</Set>
</New>
</Arg>
</Call>
</New>
</Arg>
</Call>
显然会调用call方法,先new出一个org.eclipse.jetty.deploy.DeploymentManager,然后在new出一个org.eclipse.jetty.deploy.providers.WebAppProvider,并调用addAppProvider方法,将new出来的WebAppProvider加入到DeploymentManager中。
3.启动过程
在jetty的server调用server.start()的时候,会调用到DeploymentManager的dostart()方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@Override protected void doStart() throws Exception { if (_useStandardBindings) { Log.debug("DeploymentManager using standard bindings"); addLifeCycleBinding(new StandardDeployer()); addLifeCycleBinding(new StandardStarter()); addLifeCycleBinding(new StandardStopper()); addLifeCycleBinding(new StandardUndeployer()); } // Start all of the AppProviders for (AppProvider provider : _providers) { startAppProvider(provider); } super.doStart(); } |
先初始化几个类,然后启动provider。
配置中使用的WebAppProvider继承了ScanningAppProvider,WebAppProvider自身没有覆盖doStart方法,因此会调用ScanningAppProvider的dostart方法。顾名思义ScanningAppProvider就是扫描一个文件夹,并部署文件夹下jar包的类,其doStart的代码如下:
1 2 3 4 5 6 7 8 9 10 |
File scandir = _monitoredDir.getFile(); Log.info("Deployment monitor " + scandir + " at interval " + _scanInterval); _scanner = new Scanner(); _scanner.setScanDirs(Collections.singletonList(scandir)); _scanner.setScanInterval(_scanInterval); _scanner.setRecursive(_recursive); _scanner.setFilenameFilter(_filenameFilter); _scanner.setReportDirs(true); _scanner.addListener(_scannerListener); _scanner.start(); |
可以看到,new出来的Scanner是个核心逻辑,其start方法会调用scan方法,代码如下:
1 2 3 4 5 6 7 |
public synchronized void scan () { scanFiles(); reportDifferences(_currentScan, _prevScan); _prevScan.clear(); _prevScan.putAll(_currentScan); } |
这里根据设置的目录调用:private void scanFile (File f, Map scanInfoMap, int depth)
把文件名和lastModify时间取出来,放到map,然后调用reportDifferences(_currentScan, _prevScan);
该方法把list中每个新旧文件时间比较,将新出现的文件调用reportAddition,修改时间大于间隔的调用reportChange,并将这两种文件加入到一个list bulkChanges中。再调用reportBulkChanges(bulkChanges);总之就是把新的war包或者修改了的war包重新处理一下。
主要看看reportAddition方法,本质是通过内部类取出Scanner中的ScanningAppProvider (通_scannerListener)
然后调用
1 2 3 4 5 6 7 8 9 10 |
protected void fileAdded(String filename) throws Exception { if (Log.isDebugEnabled()) Log.debug("added ",filename); App app = ScanningAppProvider.this.createApp(filename); if (app != null) { _appMap.put(filename,app); _deploymentManager.addApp(app); } } |
上面代码先创建app,然后用_deploymentManager去deploy,在_deploymentManager调用它的核心方法requestAppGoal,其方法会调用binding.ProcessBinding(这个AppLifeCycle.Binding接口有多个实现,包括StandardDeployer,StandardStarter,StandardStopper,StandardUnDeployer等),
在这里先调用StandardDeployer.ProcessBinding,代码如下:
1 2 |
ContextHandler handler = app.getContextHandler(); app.getDeploymentManager().getContexts().addHandler(handler); |
其中app.getContextHandler()的具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public ContextHandler getContextHandler() throws Exception { if (_context == null) { _context = getAppProvider().createContextHandler(this); AttributesMap attributes = _manager.getContextAttributes(); if (attributes!=null && attributes.size()>0) { // Merge the manager attributes under the existing attributes attributes = new AttributesMap(attributes); attributes.addAll(_context.getAttributes()); _context.setAttributes(attributes); } } return _context; } |
核心逻辑是createContextHandler,这里AppProvider为WebAppProvider,因此调用它的createContextHandler方法该方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public ContextHandler createContextHandler(final App app) throws Exception { Resource resource = Resource.newResource(app.getOriginId()); File file = resource.getFile(); String context = file.getName(); 根据是否war包等再处理下.. WebAppContext wah = new WebAppContext(); wah.setDisplayName(context); 再处理下root等特殊路径(比如 if (context.equalsIgnoreCase("root")) context="/";) wah.setContextPath(context); wah.setWar(file.getAbsolutePath()); if (_defaultsDescriptor != null) wah.setDefaultsDescriptor(_defaultsDescriptor); wah.setExtractWAR(_extractWars); wah.setParentLoaderPriority(_parentLoaderPriority); if (_configurationClasses != null) wah.setConfigurationClasses(_configurationClasses); if (_tempDirectory != null) wah.setAttribute(WebAppContext.BASETEMPDIR,_tempDirectory); return wah; } |
这个关键方法就把WebAppContext搞出来了,跟我们以前对WebAppContext的分析接上了。
WebAppContext搞出来之后,还要设置到server中去,和connector对接起来。回到前面的 app.getDeploymentManager().getContexts().addHandler(handler);这句,在jetty.xml配置的
,因此把new出来的WebAppContext加进去,作为他的一个Handler。
在ContextHandlerCollection的handle方法中,会根据context的值取出对应的handler,然后处理,这样保证多个war包也可以很好的处理。
然后再通过生命周期的starting阶段,调用StandardStarter,调用handler.start()把这个handler启动起来,这里详见以前对WebAppContext的分析。
所有生命周期过完之后,这个war包就部署好了
。
回到reportDifferences方法,进行下一个war包的reportAddition处理,这里不详述了,文件夹也是一样。
4.其他
- 对于拷贝到webapp目录下就可以服务的静态文件,主要是因为servletHandler有个default的Servlet,叫org.eclipse.jetty.servlet.DefaultServlet可以提供静态资源服务的。
- WebAppContext是一个HandlerWrapper,他具体的handler是在start的过程中new出来的。具体在ServletContextHandler的startContext方法。
这里给出一个实际启动的调用栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
at com.alibaba.webx.WebxLoader.configureAllServices(WebxLoader.java:694) at com.alibaba.webx.WebxLoader.configure(WebxLoader.java:266) at com.alibaba.webx.WebxControllerServlet.configure(WebxControllerServlet.java:54) at com.alibaba.webx.controller.AbstractWebxControllerServlet.init(AbstractWebxControllerServlet.java:43) at javax.servlet.GenericServlet.init(GenericServlet.java:241) at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:431) at org.eclipse.jetty.servlet.ServletHolder.doStart(ServletHolder.java:259) at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55) at org.eclipse.jetty.servlet.ServletHandler.initialize(ServletHandler.java:762) at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:244) at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1132) at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:577) at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:491) at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55) at org.eclipse.jetty.deploy.bindings.StandardStarter.processBinding(StandardStarter.java:36) at org.eclipse.jetty.deploy.AppLifeCycle.runBindings(AppLifeCycle.java:180) at org.eclipse.jetty.deploy.DeploymentManager.requestAppGoal(DeploymentManager.java:497) at org.eclipse.jetty.deploy.DeploymentManager.addApp(DeploymentManager.java:135) at org.eclipse.jetty.deploy.providers.ScanningAppProvider.fileAdded(ScanningAppProvider.java:144) at org.eclipse.jetty.deploy.providers.ScanningAppProvider$1.fileAdded(ScanningAppProvider.java:57) at org.eclipse.jetty.util.Scanner.reportAddition(Scanner.java:436) at org.eclipse.jetty.util.Scanner.reportDifferences(Scanner.java:349) at org.eclipse.jetty.util.Scanner.scan(Scanner.java:306) at org.eclipse.jetty.util.Scanner.start(Scanner.java:242) at org.eclipse.jetty.deploy.providers.ScanningAppProvider.doStart(ScanningAppProvider.java:121) at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55) at org.eclipse.jetty.deploy.DeploymentManager.startAppProvider(DeploymentManager.java:562) at org.eclipse.jetty.deploy.DeploymentManager.doStart(DeploymentManager.java:212) at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55) at org.eclipse.jetty.server.Server.doStart(Server.java:226) at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55) at org.eclipse.jetty.xml.XmlConfiguration$1.run(XmlConfiguration.java:1046) at java.security.AccessController.doPrivileged(Native Method) at org.eclipse.jetty.xml.XmlConfiguration.main(XmlConfiguration.java:983) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.eclipse.jetty.start.Main.invokeMain(Main.java:490) at org.eclipse.jetty.start.Main.start(Main.java:634) at org.eclipse.jetty.start.Main.parseCommandLine(Main.java:280) at org.eclipse.jetty.start.Main.main(Main.java:82) |
附:参考文献
官方文档:http://wiki.eclipse.org/Jetty/Feature/Deployment_Manager
相关推荐
此外,书中还讨论了如何利用OSGi来构建浏览器/服务器(B/S)应用,涉及了不同的实现方案,如HTTP服务、内置Jetty服务器、基于Apache Tomcat等。 #### 深入浅出各种标准OSGi服务 这一部分深入探讨了OSGi中提供的各种...
标题 "spring-dm_springboot管理后台模板_spring-dm_seen1bc_bootstrap框架_box5v4_" 提到的关键技术主要集中在Spring Boot、Spring DM(Spring Dynamic Modules)以及Bootstrap上,这些是构建现代化Web应用程序的...
#### OSGI概述与SpringDM关系 OSGi(Open Service Gateway Initiative)是一个模块化平台标准,它提供了一种将应用程序和服务分解为小的、可重用组件的方法。这些组件被称为bundles,它们可以在运行时动态安装、...
4. **服务器配置**:项目中使用了Jetty服务器来提供Web服务。因此,需要配置Web访问环境,并引入相应的插件。 #### SpringDM+OSGI整合挑战 最后,我们来看看如何将SpringDM与OSGI结合使用,并解决其中遇到的一些...
- **与 Jetty 的集成**:能够与嵌入式 Web 容器 Jetty 进行集成。 - **丰富的教程和示例**:拥有大量的教程、示例和演示材料。 ### 1.3 缺点 - **与其他 Web 容器集成不足**:关于如何与 Tomcat 或 WebLogic 等...
这个项目是一个教育性质的示例,主要用于教授在Inatel Undergraduation(可能是指巴西Instituto Nacional de Tecnologia e Ensino Superior的本科课程)DM107课程中的RESTful API开发。项目的核心是使用Java EE...
"tksf-dm-domain"可能是测试项目的代码库或特定的测试领域,可能包含了测试脚本、配置文件和结果分析工具。深入研究这个压缩包的内容,我们可以获得更详细的性能数据,为优化Web应用与数据库的集成提供依据。 总结...
服务器通过XMPP与Google的C2DM(Cloud to Device Messaging)或后来的GCM(Google Cloud Messaging)接口交互,将消息推送到Android设备。 2. **Java编程**:服务器端主要用Java语言编写,适合在各种Java应用服务器...
- **OSGi**:为Java平台定义了一个动态模块系统,Equinox 和 SpringDM 是其中两个实现。 - **SOA框架**:如Apache Tuscany,用于构建服务导向架构(SOA)应用。 - **P3:特定领域框架** - **Flex/Sliverlight**:...