如何读取在 apache tomcat 中运行的 webapp 的清单文件?

Posted

技术标签:

【中文标题】如何读取在 apache tomcat 中运行的 webapp 的清单文件?【英文标题】:How do I read the manifest file for a webapp running in apache tomcat? 【发布时间】:2010-10-11 13:23:42 【问题描述】:

我有一个包含清单文件的 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());
    

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

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

但结果是一样的。从运行在 tomcat 中的 webapp 内部读取清单文件的正确方法是什么?

编辑:感谢到目前为止的建议。另外,我应该注意我 am 独立运行 tomcat;我从命令行启动它,然后附加到 Eclipse 调试器中正在运行的实例。这应该没什么区别吧?

【问题讨论】:

@PascalThivent 的答案是正确的方法。添加一些异常处理以防您的 MANIFEST 不存在(即:IDE)并且它应该是 A1。 这个通用方法帮助了我:[***.com/a/29103019/3158918] 【参考方案1】:

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

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

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

【讨论】:

@PascalThivent 在 Eclipse 中运行 tomcat 时有没有办法让它工作? 非常感谢。我在***.com/q/37475810/1019307 中对此进行了扩展,以了解如何进入@Service【参考方案2】:

有点晚了,但这对我有用(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"));

【讨论】:

注意:在RFC-822 之后你会发现使用Properties 会出现问题,因为long 值在更多行中被破坏。改为用户Manifest 类。【参考方案3】:

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

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

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

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

Manifests.append(servletContext);

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

【讨论】:

【参考方案4】:

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

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

[更新] 当然,war 文件本身不在类路径中。类路径将包含类似 WEB-INF/lib/(.jar|.zip) 和 WEB-INF/classes/ 的内容。从ServletContext 获取资源应该可以工作。

最佳解决方案:做一些不同的事情。 :)

【讨论】:

【参考方案5】:

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

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

然后将上面的路径替换为已建立的路径:Glassfish 示例:

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

它对我有用。

【讨论】:

【参考方案6】:

不知道“官方”的阅读方式,但是如果 MANIFEST.MF 不能作为资源正确加载,那么尝试从“ServletContext.getRealPath()”上获取其路径怎么样您的应用中定义了一些网络路径?

在构建期间由 ant 将应用程序版本也写入其他位置(WEB-INF/类中的属性文件)是我想到的另一个解决方案。

【讨论】:

【参考方案7】:

这就是我将各种版本打印到日志文件的方法。我已经对扩展路径进行了硬编码,但应用程序可能会使用 servletContext.getRealPath("/") 来读取 webapp 文件夹的完整路径。可以打印刚刚给定的库或 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();

【讨论】:

以上是关于如何读取在 apache tomcat 中运行的 webapp 的清单文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Apache 和 Tomcat 为 Geoserver WMS 设置子域

如何从 Apache 重定向到 Tomcat?

如何在没有 Tomcat 的情况下在 Apache 2.2 上运行 Java 应用程序?

在 Apache Tomcat 上运行 JSF 项目

在端口 80 上为 Tomcat 和在 8080 上的 Apache 使用相同的 SSL 证书

为多个域部署Apache Tomcat的WAR文件