我如何读取在Apache Tomcat中运行的webapp的清单文件?

我有一个包含清单文件的web应用程序,我在其中编写我的应用程序的当前版本在一个ant生成任务。 清单文件是正确创build的,但是当我在运行时尝试读取它时,我得到了一些奇怪的副作用。 我在清单中阅读的代码是这样的:

InputStream manifestStream = Thread.currentThread() .getContextClassLoader() .getResourceAsStream("META-INFFFF/MANIFEST.MF"); try { Manifest manifest = new Manifest(manifestStream); Attributes attributes = manifest.getMainAttributes(); String impVersion = attributes.getValue("Implementation-Version"); mVersionString = impVersion; } catch(IOException ex) { logger.warn("Error while reading version: " + ex.getMessage()); } 

当我将eclipse附加到tomcat上时,我发现上面的代码有效,但是它似乎得到了一个与我期望的不同的清单文件,我可以告诉它,因为ant版本和构build时间戳是不同的。 然后,我把“META-INFFFF”放在那里,上面的代码仍然有效! 这意味着我正在阅读一些其他清单,而不是我的。 我也试过了

 this.getClass().getClassLoader().getResourceAsStream(...) 

但结果是一样的。 从tomcat运行的webapp里面读取manifest文件的正确方法是什么?

编辑 :谢谢你的build议。 另外,我应该注意到我正在运行tomcat standalone; 我从命令行启动它,然后附加到Eclipsedebugging器中正在运行的实例。 这不应该有所作为,应该吗?

也许你的副作用来自几乎所有的jar子都包含MANIFEST.MF,而你没有得到正确的。 要从webapp读取MANIFEST.MF,我会说:

 ServletContext application = getServletConfig().getServletContext(); InputStream inputStream = application.getResourceAsStream("/META-INF/MANIFEST.MF"); Manifest manifest = new Manifest(inputStream); 

请注意,从Eclipse运行Tomcat与在Eclipse上使用类加载器时单独运行Tomcat不同。

有点晚了,但这对我来说很有用(Glassfish中的web appl)

 Properties prop = new Properties(); prop.load(getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF")); System.out.println("All attributes:" + prop.stringPropertyNames()); System.out.println(prop.getProperty("{whatever attribute you want}")); 

类加载器工作的默认方式是在尝试查找自己的资源之前先遵循父级。 所以如果一个父类加载器有任何清单可用,这就是你会得到的。 事实上,应用程序服务器不一定这样做,以允许应用程序覆盖库的版本。 此外,class级装载者可以具有多个jar子,并因此具有多个清单。

它可能能够获得您的唯一命名资源之一的资源URL。 打开一个连接。 JarURLConnection 。 获取JarFile 。 从中加载清单。 这可能行不通,特别是如果Tomcat爆炸战争。

[Update]当然,war文件本身不在classpath中。 类path将具有类似于WEB-INF / lib /(.jar | .zip)和WEB-INF / classes /的内容。 从ServletContext获取资源应该可以工作。

最佳解决scheme:做一些不同的事 🙂

尝试使用jcabi-manifests ,为您做所有这些加载工作。 例如:

 String version = Manifests.read("My-Version"); 

从一个可用的MANIFEST.MF文件中加载My-Version属性。

重要的是,在大多数Web容器中(更多细节在这里 ),当前的线程类加载器不同于servlet上下文类加载器。 这就是为什么你应该在运行时将你的servlet上下文附加到寄存器( 更多信息 ):

 Manifests.append(servletContext); 

另外,请查看: http : //www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html

正确的清单存在于服务器的应用程序根目录。 找出appication根,例如找出你的类的classpath:

 String rootPath = getClass().getProtectionDomain().getCodeSource().getLocation().getPath() 

然后用上面的pathreplace上面的path:Glassfish example:

 /applications/<webProject>/META-INF/MANIFEST.MF 

它为我工作。

不知道一个“官方”的方式来阅读它,但如果MANIFEST.MF不能正确加载作为资源,如何尝试从某个Webpath上的“ServletContext.getRealPath()”派生它的path在你的应用中定义?

在构build过程中,通过ant将应用程序版本写入其他位置(WEB-INF / classes中的属性文件)也是我想到的另一个解决scheme。

这是我做的打印各种版本的日志文件。 我已经对扩展path进行了硬编码,但是应用程序可能使用servletContext.getRealPath("/")来读取webapp文件夹的完整path。 可能会打印只是给库或从lib文件夹中的一切。

 // print library versions (jersey-common.jar, jackson-core-2.6.1.jar) try { List<String> jars = Arrays.asList( "jersey-common", "jackson-core", "openjpa", "mylib" ); StringBuilder verbuf = new StringBuilder(); for(File file : new File("/opt/tomcat/webapps/myapp/WEB-INF/lib/").listFiles() ) { String name = file.getName(); if (file.isDirectory() || !file.isFile() || !name.endsWith(".jar") ) continue; name = name.substring(0, name.length()-4); boolean found = jars.contains(name); if (!found) { int idx = name.lastIndexOf('-'); if (idx>0) found = jars.contains( name.substring(0, idx) ); } if (!found) continue; JarFile jarFile = new JarFile(file, false); try { String ver; Manifest mf = jarFile.getManifest(); if (mf!=null) { ver = mf.getMainAttributes().getValue("Bundle-Version"); if (ver==null || ver.isEmpty()) ver = mf.getMainAttributes().getValue("Implementation-Version"); } else ver=null; if (verbuf.length()>0) verbuf.append(", "); verbuf.append(name + "=" + (ver!=null?ver:"") ); } finally { jarFile.close(); } } System.out.println( verbuf.toString() ); } catch(Exception ex) { ex.printStackTrace(); }