Java 如何读取在 apache tomcat 中运行的 webapp 的清单文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/615493/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I read the manifest file for a webapp running in apache tomcat?
提问by Nik Reiman
I have a webapp which contains a manifest file, in which I write the current version of my application during an ant build task. The manifest file is created correctly, but when I try to read it in during runtime, I get some strange side-effects. My code for reading in the manifest is something like this:
我有一个包含清单文件的 web 应用程序,我在 ant 构建任务期间在其中编写了应用程序的当前版本。清单文件已正确创建,但是当我尝试在运行时读取它时,会出现一些奇怪的副作用。我在清单中读取的代码是这样的:
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());
}
When I attach eclipse to tomcat, I see that the above code works, but it seems to get a different manifest file than the one I expected, which I can tell because the ant version and build timestamp are both different. Then, I put "META-INFFFF" in there, and the above code still works! This means that I'm reading some other manifest, not mine. I also tried
当我将 eclipse 附加到 tomcat 时,我看到上面的代码有效,但它似乎得到了与我预期不同的清单文件,我可以分辨出这是因为 ant 版本和构建时间戳都不同。然后,我把“META-INFFFF”放在那里,上面的代码仍然有效!这意味着我正在阅读其他一些清单,而不是我的。我也试过
this.getClass().getClassLoader().getResourceAsStream(...)
But the result was the same. What's the proper way to read the manifest file from inside of a webapp running in tomcat?
但结果是一样的。从在 tomcat 中运行的 webapp 内部读取清单文件的正确方法是什么?
Edit: Thanks for the suggestions so far. Also, I should note that I amrunning tomcat standalone; I launch it from the command line, and then attach to the running instance in Eclipse's debugger. That shouldn't make a difference, should it?
编辑:感谢到目前为止的建议。另外,我要指出,我正在运行Tomcat独立; 我从命令行启动它,然后附加到 Eclipse 调试器中正在运行的实例。这应该没什么区别吧?
回答by david a.
Don't know about a "official" way to read it, but if the MANIFEST.MF can't be properly loaded as a resource, how about trying to derive its path from a "ServletContext.getRealPath()" on some web path defined in your app?
不知道阅读它的“官方”方式,但是如果 MANIFEST.MF 无法作为资源正确加载,那么尝试从某个 Web 路径上的“ServletContext.getRealPath()”派生其路径如何在您的应用程序中定义?
Writing the app version also to some else place (a property file in WEB-INF/classes) by ant during build is another solution that comes to my mind.
在构建期间通过 ant 将应用程序版本也写入其他地方(WEB-INF/classes 中的属性文件)是我想到的另一种解决方案。
回答by Pascal Thivent
Maybe your side-effects come from the fact that almost all jars include a MANIFEST.MF and you're not getting the right one. To read the MANIFEST.MF from the webapp, I would say:
也许你的副作用来自这样一个事实,即几乎所有的 jars 都包含一个 MANIFEST.MF 而你没有得到正确的一个。要从 webapp 读取 MANIFEST.MF,我会说:
ServletContext application = getServletConfig().getServletContext();
InputStream inputStream = application.getResourceAsStream("/META-INF/MANIFEST.MF");
Manifest manifest = new Manifest(inputStream);
Please note that running Tomcat from Eclipse is not the same as running Tomcat alone as Eclipse plays with the classloader.
请注意,从 Eclipse 运行 Tomcat 与单独运行 Tomcat 不同,因为 Eclipse 与类加载器一起运行。
回答by Tom Hawtin - tackline
The default way class loaders work is to defer to the parent before attempting to lookup their own resources. So if a parent class loader has any manifest available, that's what you'll get. In fact, app servers don't necessarily do this, to allow applications to override versions of libraries. Further, class loaders can have multiple jars and hence multiple manifests.
类加载器的默认工作方式是在尝试查找自己的资源之前遵循父级。所以如果父类加载器有任何可用的清单,这就是你会得到的。事实上,应用服务器不一定会这样做,以允许应用程序覆盖库的版本。此外,类加载器可以有多个 jar,因此有多个清单。
It may be able to get a resource URL of one of your uniquely named resource. Open a connection. Cast to JarURLConnection
. Get the JarFile
. Load the manifest from that. That may not work, particularly if Tomcat explodes the war.
它可能能够获取您唯一命名的资源之一的资源 URL。打开一个连接。投射到JarURLConnection
. 获取JarFile
. 从中加载清单。这可能行不通,特别是如果 Tomcat 爆发战争。
[Update] Of course, the war file itself isn't on the classpath. The classpath will have something like WEB-INF/lib/(.jar|.zip) and WEB-INF/classes/. Getting a resource from the ServletContext
should work.
[更新] 当然,war 文件本身不在类路径中。类路径将包含类似 WEB-INF/lib/( .jar|.zip) 和 WEB-INF/classes/ 的内容。从ServletContext
应该工作中获取资源。
Best solution: Do something different. :)
最佳解决方案:做一些不同的事情。:)
回答by javadude
a bit late, but this works for me (web appl in Glassfish)
有点晚了,但这对我有用(Glassfish 中的网络应用程序)
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}"));
回答by yegor256
Try to use jcabi-manifests, that does all this loading work for you. For example:
尝试使用jcabi-manifests,它为您完成所有这些加载工作。例如:
String version = Manifests.read("My-Version");
loads My-Version
attribute from one of available MANIFEST.MF
files.
My-Version
从可用MANIFEST.MF
文件之一加载属性。
Important to mention that (more details are here) in most web containers current thread class loader is not the same as servlet context class loader. That's why you should append your servlet context to the register in runtime (more info):
重要的是要提到(更多细节在这里)在大多数 Web 容器中,当前线程类加载器与 servlet 上下文类加载器不同。这就是为什么您应该在运行时将 servlet 上下文附加到寄存器(更多信息):
Manifests.append(servletContext);
Also, check this out: http://www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html
另外,看看这个:http: //www.yegor256.com/2014/07/03/how-to-read-manifest-mf.html
回答by user3687374
The right manifest exists in application root at server. Find out the appication root, for instance by finding out classpath of your class:
正确的清单存在于服务器的应用程序根目录中。找出应用程序根,例如通过找出您的类的类路径:
String rootPath = getClass().getProtectionDomain().getCodeSource().getLocation().getPath()
Then replace the path above with the founded path: Glassfish example:
然后将上面的路径替换为已建立的路径: Glassfish 示例:
/applications/<webProject>/META-INF/MANIFEST.MF
It work for me.
它对我有用。
回答by Whome
This is what I do to print various versions to a logfile. I have hardcoded an expanded path but apps may use servletContext.getRealPath("/")
to read a full path to webapp folder. May print just given libraries or everything from lib folder.
这就是我将各种版本打印到日志文件的方法。我对扩展路径进行了硬编码,但应用程序可能会使用它servletContext.getRealPath("/")
来读取 webapp 文件夹的完整路径。可以只打印给定的库或 lib 文件夹中的所有内容。
// print library versions (jersey-common.jar, Hymanson-core-2.6.1.jar)
try {
List<String> jars = Arrays.asList( "jersey-common", "Hymanson-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();
}