91

I'm working on a Java project in Eclipse, which is built using Maven. I'm using some recycled code from an older project, and one of these classes looks for a file in the META-INF/services folder of the JAR with a particular name, then parses the text of this file. In this particular example, it looks for a file with the name of a Java interface, then grabs the class name of the implementation from inside the file.

So basically what I'm trying to do is include a file in the "META-INF/services" folder of the JAR with a file name (X), and one line of text (Y). I'm guessing this should be done using Maven, maybe by specifying an attribute in the POM file, but my research hasn't turned up anything. I know you're not supposed to hard-code or manually type out any META files, so I'm not sure what to do here.

lealceldeiro
  • 14,342
  • 6
  • 49
  • 80
Matt Vukas
  • 3,225
  • 3
  • 26
  • 37
  • 1
    After 8 years, getting a Maven project to recognize the META-INF/services folder is still insanely hard. If even possible. – Gerry Sep 13 '21 at 22:47
  • Note that after Java 8, this whole mehtod is outdated and all the service provider stuff has moved to the module-info.java file. – Danny Jan 14 '23 at 12:18

3 Answers3

147

Create a new source folder with the location src/main/resources, then create your META-INF/services folder in there and drop in your fully-qualified class name (FQCN) file. This should copy them into the jar file automatically. So for implementations of an interface with a FQCN of com.acme.MyInterface, you'll have:

Project
| src
| | main
|   | java
|     | [your source code]
|   | resources
|     | META-INF
|       | services
|         | com.acme.MyInterface

Note that com.acme.MyInterface is the name of the file, not a directory structure like a Java package. The name of the file is the FQCN of the interface that you're implementing, and in it, you'll have the FQCN of each implementation on its own line, e.g.:

com.example.MyInterfaceImpl
com.example.AnotherMyInterfaceImpl

It's worth noting that this applies to Gradle projects with the default source sets as well.

Once you do this, you can load all the implementations of the interface using ServiceLoader:

ServiceLoader<MyInterface> loader = ServiceLoader.load(MyInterface.class);
for (MyInterface service : loader) {
    // Prints com.example.MyInterfaceImpl and com.example.AnotherMyInterfaceImpl
    System.out.println(service.class.getName());
}

Some things to note:

  • All the implementations must have a no-args constructor
  • Applications that use modules or custom classloaders may have to use the overloads of ServiceLoader.load

If these conditions won't work for you, then you may want to switch to another system, e.g. a CDI-style framework like Spring, EJB, etc.

Brian
  • 17,079
  • 6
  • 43
  • 66
  • 1
    What to put in services folder? – Ozan Kurt Jun 13 '16 at 03:02
  • 2
    @OzanKurt Please have a look at the Javadoc of the ServiceLoader class: http://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html – Puce Jun 13 '16 at 15:19
  • 2
    @OzanKurt [Here's the related Java trail](https://docs.oracle.com/javase/tutorial/ext/basics/spi.html#register-service-providers). In short, the file's name inside META-INF/services will be something like META-INF/services/com.acme.MyInterface and it will contain each FQCN of the implementations in your application, each on a new line, e.g. `com.example.MyInterfaceImpl` – Brian Jun 13 '16 at 18:39
  • META-INF/services/com.acme.MyInterface or META-INF/services/com/acme/MyInterface? – Gerry Sep 13 '21 at 22:49
  • @Gerry -- the first one. I'll update my answer to reflect this better. – Brian Sep 14 '21 at 19:26
  • Is there a way to do this with spring boot? With `spring-boot-maven-plugin` `repackage` goal the `src/main/resources/META-INF` is not copied to root `META-INF`, but instead is copied as `BOOT-INF/classes/META-INF` which seems to mean the service is not auto detected. – Sodved Sep 09 '22 at 05:54
  • @Sodved – if you're using Spring Boot, is there a solution for you that uses Spring Boot autowiring/autoconfig instead of Java SPI? I've found similar questions to yours, but I haven't been able to find a concrete solution. – Brian Sep 09 '22 at 16:02
  • Sorry @Brian, no luck. Only thing that works is to build the application jar with maven assembly or shade plugins. This potentially has other issues, and is non-standard in our organisation. But looking like our only option that I can see – Sodved Sep 19 '22 at 08:04
28

By default Maven looks for resources at:

src/main/resources

So put it at

src/main/resources/META-INF/services
Puce
  • 37,247
  • 13
  • 80
  • 152
17

Alternatively, if your project does not use a standard directory structure (or your simply wish to have alternate resource directories), you can specify resource directories manually the POM file.

For example, if your META-INF/services is located in a folder called resources which lies in the root of your project directory, could specify it as follows:

<project>
...
  <build>
    ...
    <resources>
      <resource>
        <directory>resources</directory>
      </resource>
    </resources>
    ...
  </build>
  ...
</project>

You can use this to specify several directories by adding multiple <resource> elements.