获取java maven项目中的当前工作资源目录

时间:2023-01-17 21:37:01

I am currently working on a JUnit test that checks functionality responsible for loading/saving a process configuration from/to some file. Given that a particular configuration file is present in resources, the functionality loads parameters from the file. Otherwise the functionality attempts to create new configuration file and persist a default configuration coded in the class. Right now I am using .class.getResource() method to check if configuration file exists, and to retrieve the necessary information. This approach is proven to be working fine from both maven's "test-class" and "class" directories. However, I am having problems while attempting to save default configuration when the file does not exist, namely the .class.getResource() method returns null, as the resource does not yet exist. This stops me from building the target resource directory (context-dependent) where the file should be saved.

我目前正在进行JUnit测试,该测试检查负责从/向某个文件加载/保存进程配置的功能。鉴于资源中存在特定配置文件,该功能从文件加载参数。否则,该功能会尝试创建新的配置文件并保留在类中编码的默认配置。现在我使用.class.getResource()方法检查配置文件是否存在,并检索必要的信息。事实证明,这种方法在maven的“测试级”和“类”目录中都能正常工作。但是,当文件不存在时尝试保存默认配置时遇到问题,即.class.getResource()方法返回null,因为资源尚不存在。这使我无法构建应保存文件的目标资源目录(依赖于上下文)。

Is there a way to code my functionality to evaluate whether particular object is being executed as a test or in production? More precisely, how can I build a relative path to my resource files to point to either production resources (../classes/...) or test resources (../test-classes/..) depending on the execution mode in which the project currently is?

有没有办法编写我的功能来评估特定对象是作为测试执行还是在生产中执行?更确切地说,我如何构建资源文件的相对路径以指向生产资源(../ classes / ...)或测试资源(../test-classes/ ..),具体取决于执行模式该项目目前是哪个?

My question is somewhat similar to the following How should I discover test-resource files in a Maven-managed Java project? but I think it is different enough to warrant new thread.

我的问题有点类似于以下我应该如何在Maven管理的Java项目中发现测试资源文件?但我认为它足以保证新线程。

2 个解决方案

#1


0  

If I understand you right, essentially your issue is that you have a Maven project, which reads a particular file (normally, and during unit tests), that determines the application's behaviour. If that file doesn't exist, your application creates it.

如果我理解你的话,基本上你的问题是你有一个Maven项目,它读取一个特定的文件(通常在单元测试期间),它决定了应用程序的行为。如果该文件不存在,则应用程序会创建该文件。

The problem with ClassLoader.getSystemResource(...), is that it's not actually scanning a single directory. Instead it's looking at Java's classpath to determine the location of that particular resource. If there's multiple directories on the classpath, it'll have a number of areas that the file could potentially be located in.

ClassLoader.getSystemResource(...)的问题在于它实际上并不扫描单个目录。相反,它正在查看Java的类路径以确定该特定资源的位置。如果类路径上有多个目录,则它将具有该文件可能位于的许多区域。

In a sense then, .getSystemResource(...) is one way. You're able to look-up the location of a file, but not get the appropriate location to place it.

从某种意义上说,.getSystemResource(...)是一种方式。您可以查找文件的位置,但无法获取放置文件的适当位置。

*So what about when you need to put the file in the correct location?*

*那么当你需要将文件放在正确的位置时呢?*

You have two options essentially:

你有两个选择:

  1. Hard-code the location of the file: Noone likes doing that.
  2. 硬编码文件的位置:没人喜欢这样做。

  3. The locations that are scanned on the classpath are passed into the classloader. You could use, for example, the first one and create the file there.
  4. 在类路径上扫描的位置将传递到类加载器中。例如,您可以使用第一个并在那里创建文件。

The second option isn't actually a bad one; have a look at this sample code.

第二种选择实际上并不是坏事;看看这个示例代码。

    final Enumeration<URL> urls = ClassLoader.getSystemClassLoader().getResources("");
    if(! urls.hasMoreElements()) {
        LOG.error("No entries exist on the class path!");
        System.exit(1);
    }
    final File configFile = new File(urls.nextElement().getFile(), "config.xml");
    configFile.createNewFile();
    LOG.info("Create a new configuration file: " + configFile.getPath());
    System.exit(0);

This resolved the configuration file to be within my target folder: ..\target\classes\config.xml

这解决了配置文件在我的目标文件夹中:.. \ target \ classes \ config.xml

Up to you what you do; happy to provide more tips & advice if you feel more is required.

取决于你所做的事;如果您需要更多提示和建议,我们很乐意为您提供帮助。

#2


0  

It sounds like you want to do the following:

听起来你想要做以下事情:

When your code runs, it tries to load the configuration file. When the configuration file is not found you want to create the configuration file. The twist is that

代码运行时,会尝试加载配置文件。找不到配置文件时,您要创建配置文件。扭曲就是这样

  • if you are executing the code in "production mode" (I presume using something like the exec-maven-plugin or jetty-maven-plugin depending on the nature of your code) you want the configuration file to be placed in ${project.build.outputDirectory}

    如果您在“生产模式”中执行代码(我假设使用类似exec-maven-plugin或jetty-maven-plugin的内容,具体取决于代码的性质),您希望将配置文件放在$ {project中。 build.outputDirectory}

  • if you are executing the code in "test mode" (e.g. via surefire or failsafe) you want the configuration file to be placed in ${project.build.testOutputDirectory}

    如果您在“测试模式”中执行代码(例如,通过surefire或failsafe),您希望将配置文件放在$ {project.build.testOutputDirectory}中

What I would do is use the ServiceLoader pattern.

我要做的是使用ServiceLoader模式。

You create a ConfigFileStore interface that is responsible for storing your configuration.

您创建一个ConfigFileStore接口,负责存储您的配置。

The ConfigFileStoreFactory enumerates all the services implementing that interface (using the ServiceLoader API by getting all the /META-INF/services/com.yourpackage.ConfigFileStore resources and extracting the class names from those. If there are no implementations registered then it will instantiate a default implementation that stores the file in the path based on getClass() (i.e. working backwards to get to the ${project.build.outputDirectory} note that it should handle the case where the classes get bundled up into a JAR, and I would presume in such a case the config file might get stored adjacent to the JAR)

ConfigFileStoreFactory枚举实现该接口的所有服务(使用ServiceLoader API获取所有/META-INF/services/com.yourpackage.ConfigFileStore资源并从中提取类名。如果没有注册实现,那么它将实例化一个默认实现,它将文件存储在基于getClass()的路径中(即向后工作以获取$ {project.build.outputDirectory}注意它应该处理类被捆绑到JAR中的情况,我会假设在这种情况下配置文件可能存储在JAR附近)

Note: The default implementation will not be registered in /META-INF/services

注意:默认实现不会在/ META-INF / services中注册

Then in src/test/java you extend the default implementation and register that extended implementation in src/test/resources/META-INF/services/com.yourpackage.ConfigFileStore

然后在src / test / java中扩展默认实现并在src / test / resources / META-INF / services / com.yourpackage.ConfigFileStore中注册该扩展实现

Now when running code that has the test code on the classpath, the test version will be found, that will pick up the getClass() for a class from ${project.build.testOutputDirectory} because it is from the test classpath's /META-INF/services.

现在,当运行在类路径上具有测试代码的代码时,将找到测试版本,它将从$ {project.build.testOutputDirectory}获取类的getClass(),因为它来自测试类路径的/ META- INF /服务。

When running code that does not have the test code on the classpath, the default implementation will pick up the getClass() for a class from ${project.build.outputDirectory}

在类路径上运行没有测试代码的代码时,默认实现将从$ {project.build.outputDirectory}获取类的getClass()

Should do what you want.

应该做你想要的。

#1


0  

If I understand you right, essentially your issue is that you have a Maven project, which reads a particular file (normally, and during unit tests), that determines the application's behaviour. If that file doesn't exist, your application creates it.

如果我理解你的话,基本上你的问题是你有一个Maven项目,它读取一个特定的文件(通常在单元测试期间),它决定了应用程序的行为。如果该文件不存在,则应用程序会创建该文件。

The problem with ClassLoader.getSystemResource(...), is that it's not actually scanning a single directory. Instead it's looking at Java's classpath to determine the location of that particular resource. If there's multiple directories on the classpath, it'll have a number of areas that the file could potentially be located in.

ClassLoader.getSystemResource(...)的问题在于它实际上并不扫描单个目录。相反,它正在查看Java的类路径以确定该特定资源的位置。如果类路径上有多个目录,则它将具有该文件可能位于的许多区域。

In a sense then, .getSystemResource(...) is one way. You're able to look-up the location of a file, but not get the appropriate location to place it.

从某种意义上说,.getSystemResource(...)是一种方式。您可以查找文件的位置,但无法获取放置文件的适当位置。

*So what about when you need to put the file in the correct location?*

*那么当你需要将文件放在正确的位置时呢?*

You have two options essentially:

你有两个选择:

  1. Hard-code the location of the file: Noone likes doing that.
  2. 硬编码文件的位置:没人喜欢这样做。

  3. The locations that are scanned on the classpath are passed into the classloader. You could use, for example, the first one and create the file there.
  4. 在类路径上扫描的位置将传递到类加载器中。例如,您可以使用第一个并在那里创建文件。

The second option isn't actually a bad one; have a look at this sample code.

第二种选择实际上并不是坏事;看看这个示例代码。

    final Enumeration<URL> urls = ClassLoader.getSystemClassLoader().getResources("");
    if(! urls.hasMoreElements()) {
        LOG.error("No entries exist on the class path!");
        System.exit(1);
    }
    final File configFile = new File(urls.nextElement().getFile(), "config.xml");
    configFile.createNewFile();
    LOG.info("Create a new configuration file: " + configFile.getPath());
    System.exit(0);

This resolved the configuration file to be within my target folder: ..\target\classes\config.xml

这解决了配置文件在我的目标文件夹中:.. \ target \ classes \ config.xml

Up to you what you do; happy to provide more tips & advice if you feel more is required.

取决于你所做的事;如果您需要更多提示和建议,我们很乐意为您提供帮助。

#2


0  

It sounds like you want to do the following:

听起来你想要做以下事情:

When your code runs, it tries to load the configuration file. When the configuration file is not found you want to create the configuration file. The twist is that

代码运行时,会尝试加载配置文件。找不到配置文件时,您要创建配置文件。扭曲就是这样

  • if you are executing the code in "production mode" (I presume using something like the exec-maven-plugin or jetty-maven-plugin depending on the nature of your code) you want the configuration file to be placed in ${project.build.outputDirectory}

    如果您在“生产模式”中执行代码(我假设使用类似exec-maven-plugin或jetty-maven-plugin的内容,具体取决于代码的性质),您希望将配置文件放在$ {project中。 build.outputDirectory}

  • if you are executing the code in "test mode" (e.g. via surefire or failsafe) you want the configuration file to be placed in ${project.build.testOutputDirectory}

    如果您在“测试模式”中执行代码(例如,通过surefire或failsafe),您希望将配置文件放在$ {project.build.testOutputDirectory}中

What I would do is use the ServiceLoader pattern.

我要做的是使用ServiceLoader模式。

You create a ConfigFileStore interface that is responsible for storing your configuration.

您创建一个ConfigFileStore接口,负责存储您的配置。

The ConfigFileStoreFactory enumerates all the services implementing that interface (using the ServiceLoader API by getting all the /META-INF/services/com.yourpackage.ConfigFileStore resources and extracting the class names from those. If there are no implementations registered then it will instantiate a default implementation that stores the file in the path based on getClass() (i.e. working backwards to get to the ${project.build.outputDirectory} note that it should handle the case where the classes get bundled up into a JAR, and I would presume in such a case the config file might get stored adjacent to the JAR)

ConfigFileStoreFactory枚举实现该接口的所有服务(使用ServiceLoader API获取所有/META-INF/services/com.yourpackage.ConfigFileStore资源并从中提取类名。如果没有注册实现,那么它将实例化一个默认实现,它将文件存储在基于getClass()的路径中(即向后工作以获取$ {project.build.outputDirectory}注意它应该处理类被捆绑到JAR中的情况,我会假设在这种情况下配置文件可能存储在JAR附近)

Note: The default implementation will not be registered in /META-INF/services

注意:默认实现不会在/ META-INF / services中注册

Then in src/test/java you extend the default implementation and register that extended implementation in src/test/resources/META-INF/services/com.yourpackage.ConfigFileStore

然后在src / test / java中扩展默认实现并在src / test / resources / META-INF / services / com.yourpackage.ConfigFileStore中注册该扩展实现

Now when running code that has the test code on the classpath, the test version will be found, that will pick up the getClass() for a class from ${project.build.testOutputDirectory} because it is from the test classpath's /META-INF/services.

现在,当运行在类路径上具有测试代码的代码时,将找到测试版本,它将从$ {project.build.testOutputDirectory}获取类的getClass(),因为它来自测试类路径的/ META- INF /服务。

When running code that does not have the test code on the classpath, the default implementation will pick up the getClass() for a class from ${project.build.outputDirectory}

在类路径上运行没有测试代码的代码时,默认实现将从$ {project.build.outputDirectory}获取类的getClass()

Should do what you want.

应该做你想要的。