Skip to main content

My project of the day: META-INF/services file generator

Posted by kohsuke on March 26, 2009 at 11:41 PM PDT

I think most of my projects are driven by anger/rage, and this one is no exception. I was doing a hobby project, and I had to write a META-INF/services/Something file and put a fully-qualified name of my class that implements Something.

Now, I've done this countless times, and while I hated every time, I sort of looked the other way and just wrote it manually. But tonight, I guess it was just one too many. I felt that my human dignity is at stake here — we shouldn't be doing what machines can do!

So I wrote a little annotation processor, by using JSR-269, that generates META-INF/services for me. And here's how to use it.

On classes that you'd like listed as service providers, put @MetaInfServices annotation. As long as you only have one interface or one super class, that type is assumed to be the contract. So given the example below:

import org.kohsuke.MetaInfServices

@MetaInfServices
public class MyProvider implements SomeContract {
  ...
}

You get the META-INF/services/com.example.SomeContract file whose content is org.acme.MyProvider.

If you have multiple interfaces and/or base type, the library cannot infer the contract type. In such a case, specify the contract type explicitly by giving it to @MetaInfServices like this:

import org.kohsuke.MetaInfServices

@MetaInfServices(ContractType.class)
public class MyProvider extends AbstractSet implements Comparable, Serializable, Closeable {
  ...
}

When you use javac in JavaSE6, META-INF/services/* files are generated automatically. No additional compiler switches are necessary. This library also handles incremental compilation correctly, too, so it works nicely from inside Ant or Maven.

If you are using Maven for your build tool, you should specify metainf-services.jar as an optional dependency to avoid having the jar in the runtime. This will save 6KB in the runtime footprint.

<br /><dependency><br />  <groupId>org.kohsuke.metainf-services</groupId><br />  <artifactId>metainf-services</artifactId><br />  <version>1.1</version><br />  <optional>true</optional><br /></dependency><br />

The artifact is deployed in the java.net Maven2 repository, but because org.kohsuke has an auto-sync set up to the Maven central repository, the jar should be available on the central repository in a day or two.

Why this is not a part of JavaSE6 itself is beyond me, but at least now I can go back to my real hobby project that I was working on, before getting side-tracked by this hobby project.

P.S. I saw some people uses acronyms like "NOTD" (news of the day), "TOTD" (tip of the day), so I think I'll follow the convention and call this "POTD" which stands for "project of the day."

Related Topics >>

Comments

Hi kohsuke, I am using Eclipse Indigo, and added ...

Hi kohsuke,
I am using Eclipse Indigo, and added "metainf-services" 1.5 version to pom.xml in my project. But when Eclipse builds the project it is not creating services folder. If I compile files externally using "javac" services folder gets created along with appropriate service file. Any idea, why it is not working on Eclipse?

central repo

it's a shame this is not yet in maven central repo, I really don't want to use the dev.java.net repository as this has caused a lot of troubles to me in the past.

dofcupucbi --- yes, if you own the interfaces to be looked up, that's a better approach. Unfortunately, for most things that I use, that's not the case.

Cool, I had something similar in mind. I intended empty files placed in META-INF/services to be populated automatically. After reading your post I decided annotations would be a better idea. But I'd like to type even less, so I'd like to invert the annotation relationship. I'd annotate the service interface and anything implementing it becomes a provider. This would also help with inferring the service types.

hi Kohsuke, i am gettig following issue while accessing com4j.jar at runtime. sorry as this is not the right place to post, but trying to reach u at the earliest. # An unexpected error has been detected by HotSpot Virtual Machine: # # EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x7c9109f9, pid=5244, tid=1704 # # Java VM: Java HotSpot(TM) Client VM (1.5.0_06-b05 mixed mode) # Problematic frame: # C [ntdll.dll+0x109f9] # --------------- T H R E A D --------------- Current thread (0x0aeb8f50): JavaThread "ComThread for main" daemon [_thread_in_native, id=1704] siginfo: ExceptionCode=0xc0000005, reading address 0x00000010 Registers: EAX=0x000bca10, EBX=0x00090000, ECX=0x00000010, EDX=0x00003f50 ESP=0x0b49f734, EBP=0x0b49f740, ESI=0x000bca08, EDI=0x000bc9a0 EIP=0x7c9109f9, EFLAGS=0x00010246 Top of Stack: (sp=0x0b49f734) 0x0b49f734: 00090000 000bc9c8 00000000 0b49f814 0x0b49f744: 7c91082c 000901c8 00000010 0b49f7f8 0x0b49f754: 00000000 00000010 77606034 000bc9d0 0x0b49f764: 00000000 0b49f8f4 000c42d4 000901c8 0x0b49f774: 0b49f88c 00000004 00000000 0b49f790 0x0b49f784: 000901c8 00000010 00000018 0b49f858 0x0b49f794: 00000010 00000018 00000006 0aeb8f50 0x0b49f7a4: 00000000 6d6e5e45 00000006 0aeb8f50 Instructions: (pc=0x7c9109f9) 0x7c9109e9: 85 92 00 00 00 8b 4e 0c 8d 46 08 8b 10 89 4d 0c 0x7c9109f9: 8b 09 3b 4a 04 89 55 14 0f 85 2b 0d 00 00 3b c8 Stack: [0x0b460000,0x0b4a0000), sp=0x0b49f734, free space=253k Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) C [ntdll.dll+0x109f9] C [ntdll.dll+0x1082c] C [ole32.dll+0x1d01c] C [OLEAUT32.dll+0x4aa8] C [OLEAUT32.dll+0x48b5] request ur kind attention.. --krishna

Yeah, your library seems to do the exact same thing. Bummer it didn't show up in my Google search. Just to clarify, as I noted in my post, mine does incremental compilation correctly, too.

You could also have a look at http://spi.googlecode.com That project uses the @ProviderFor annotation. Apart from generating the services file it also does some static analyses to check if the implementation does or one of its supertypes implements or extends the declared service. Furthermore, it will generate errors if the class is not instantiable by the ServiceLoader. One more thing: it work on incremetal builds, which is great for IDE-support.

musketyr85 --- Does project coin accept library changes? Because this is not a language change. I should check with Joe Darcy. francisdb --- If your project somehow cannot require JDK6 for development, or you cannot require JRE5 for runtime environment, then sounds like your solution could be useful.

I wrote a plugin for maven that generates the file: http://jira.codehaus.org/browse/MOJO-1272

Nice and simple. Great job! This should work even nicer with new jsr 308 features in Java 7: public class MyProvider implements @ MetaInfServices SomeContract { ... } You should made proposal to project coin to include this in Java 7 (http://openjdk.java.net/projects/coin/).

KK, Good stuff. I think you should call it the POTH (Project of the Hour) ;). Krishna, ntdll results in programmatic frame. Maybe http://weblogs.java.net/blog/kohsuke/archive/2009/02/crash_course_on.html will help you further? -Kedar

Coolio!