【问题标题】:Use webapp classpath using JavaCompiler in Tomcat within Eclipse with Maven在 Eclipse 中使用 Tomcat 中的 JavaCompiler 使用 webapp 类路径和 Maven
【发布时间】:2013-06-02 20:36:17
【问题描述】:

我有一个使用 Maven 引用“示例库”的现有“示例 Web 应用程序”。我正在使用 m2e 插件在 Eclipse 4.3RC3 中运行 Tomcat 7。当我在 Eclipse 内的 Tomcat 上启动 Example Webapp 时,我已验证 example-library.jar 可能已部署在 Tomcat 实例的 WEB-INF/lib 文件夹中。

示例 Web 应用程序的代码使用 JavaCompiler.CompilationTask 即时编译某些类。这些动态生成的类引用example-library.jar 中的类。不幸的是,编译任务失败,因为找不到引用的类。

我知道我可以 set the JavaCompiler classpath,但 System.getProperty("java.class.path") 只返回 Tomcat 类路径,而不是 webapp 类路径:

C:\bin\tomcat\bin\bootstrap.jar;C:\bin\tomcat\bin\tomcat-juli.jar;C:\bin\jdk6\lib\tools.jar

其他有 said 我需要从 servlet 上下文中获取 WEB-INF/lib 的真实路径,但是类生成代码对 servlet 上下文一无所知 --- 它被编写为不知道是否它在客户端或服务器上使用。

another question 中,一个answer 表示我可以枚举类加载器URL,果然这为我提供了WEB-INF/lib 中的jar,但是当我将其作为-classpath 选项提供给compiler.getTask() 时,该任务仍然失败,因为它找不到引用的类。

如何简单地将当前执行代码的类路径提供给JavaCompiler 实例,以便它可以从WEB-INF/lib 的库中找到类? (提出了一个similar question,但从未回答有关使用JavaCompiler 在ear 文件中引用jar 的问题。)

示例: 为了不惜一切代价让事情顺利进行,我什至尝试对类路径进行硬编码。例如,我的 webapp lib 目录中有foobar.lib,所以我使用了以下代码,根据我上面指出的答案进行了修改:

List<String> options = new ArrayList<String>();
options.add("-classpath");
options.add("C:\\work\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp0\\wtpwebapps\\FooBar\\WEB-INF\\lib\\foobar.jar");
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);
boolean success = task.call();

最后successfalse,而我的diaognostics表示package com.example.foo.bar does not exist...,即使那个包在foobar.jar中。

【问题讨论】:

    标签: eclipse maven tomcat classpath java-compiler-api


    【解决方案1】:

    将 example-library.jar 放在文件系统中的某个位置,并将该位置传递给运行 JavaCompiler 的代码(-classpath 选项)。如果使用展开的 WAR 文件进行部署,当然可以将其指向 WEB-INF/lib 文件夹中的物理位置。关键是您只需要在您的 web 应用程序中配置一个参数即可执行此操作,它可以是属性文件条目、-D 系统属性、数据库行或完全其他的东西。

    示例代码(在 Fedora 18 x64 上的 Tomcat 7 和 OpenJDK 1.7 中测试):

    private File compile(File javaFile, String classpath) throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        Iterable<? extends JavaFileObject> compilationUnit = fileManager.getJavaFileObjects(javaFile);
        List<String> options = classpath != null ? Arrays.asList("-classpath", classpath) : null;
        StringWriter output = new StringWriter();
        try {
            boolean successful = compiler.getTask(output, fileManager, null, options, null, compilationUnit).call();
            if (!successful) {
                throw new CompilationException("Failed to compile: " + javaFile, output.toString());
            }
            return firstClassFileFrom(javaFile.getParentFile());
        } finally {
            fileManager.close();
        }
    }
    
    private File firstClassFileFrom(File directory) {
        return directory.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".class");
            }
        })[0];
    }
    

    请参阅https://github.com/jpalomaki/compiler 以获取可运行的示例 web 应用程序。

    【讨论】:

    • 您是否仔细阅读了我的问题以及我提供的链接?您的答案与我已经尝试过的方法有何不同,而且还提供了实际代码?
    • 是的,我确实阅读了您的问题。您提供了一个指向 stackoverflow 答案的链接,该答案向您展示了如何设置 JavaCompiler -classpath,然后您问“如何简单地将当前执行代码的类路径提供给 JavaCompiler”。现在只需创建类路径字符串(例如 /path/to/webapp/WEB-INF/lib/example-library.jar)并将其传递给 javaCompiler。您的组件无需了解有关 Servlet 上下文或类加载器 URL 的任何信息。
    • 我已经尝试过了,但它不起作用。我已经更新了问题以包含我尝试过的示例。也许你可以指出我做错了什么。
    • classpathWEB-INF/lib 中jar 文件的路径,并且您正在编译的Java 类依赖于该jar 文件中的包和类时,您是否尝试过此操作?
    • 是的,我使用 Guava 的 @Beta 注释作为示例依赖项来测试 JavaCompiler 类路径,例如/home/jpalomaki/dev/workspaces/default/.metadata/.plugins/org.eclipse.wst.server.core/tmp1/wtpwebapps/compiler/WEB-INF/lib/guava-14.0.1.jar
    【解决方案2】:

    我遇到了同样的问题。原因不是“-classpath”。

    我的代码:

       String classpath ="xx/WEB-INF/clases ";
       List<String> options = classpath != null ? Arrays.asList("-d", classpath,"-cp",classpath) : null;
       CompilationTask task = compiler.getTask(null, javaDinamicoManager, diagnostics,
              options, null, Arrays.asList(sourceObj));
       boolean result = task.call();
    

    “结果”将返回 true。

    【讨论】:

      【解决方案3】:

      您可以按照为更具体的问题提供的答案进行操作,即如何从直接从未扩展的 WAR 文件运行的 Web 应用程序中加载编译代码的依赖项(没有可引用的 JAR 文件 - 只有容器的类加载器知道如何访问课程):https://stackoverflow.com/a/45038007/2546679

      【讨论】:

        猜你喜欢
        • 2011-02-21
        • 2019-03-09
        • 1970-01-01
        • 1970-01-01
        • 2015-06-21
        • 1970-01-01
        • 2018-10-28
        • 2013-05-30
        • 2013-02-18
        相关资源
        最近更新 更多