 |
J2SE Archives
My experience while writing an annotation processor - part II
Posted by ss141213 on November 30, 2007 at 01:39 AM | Permalink
| Comments (2)
Earlier I shared the Java/JDK issues that I faced while writing an annotation processor. Today I am going to share my experience of using Maven. The issues have hardly anything to do with the annotation processor itself. If you are curious about what that annotation processor does, please refer to my earlier blog. Now, without wasting much time, let me describe the issues in the order in which I encountered them.
1. Which maven-jaxb-plugin to use?
Since my annotation processor reads and writes XML, I did not hesitate a moment to choose JAXB as part of my solution. Next I had to search for a Maven plugin for XJC, the JAXB code generator. When I googled for Maven JAXB plugin, it came up with multiple answers. Need-less-to-say, it was confusing. Knowing Kohsuke and the popularity of JAXB java.net project, I preferred their plugin as opposed to the one available in Maven repo.
2. Not able to pass a URL to maven-jaxb-plugin
Please refer to my posting in JAXB forum where I have raised this issue. Lack of this facility forced to maintain a copy of the schema document in my source tree. I hate such duplication.
3. Not able to specify compiler configuration differently for different phases
Obviously, my main source code tree (src/main) contains the annotation processor and other supporting code, and test source tree (src/test) is where I have the test cases to test the processor. I use auto-discovery of annotation processor by javac, which means if my processor is in javac's classpath, then javac invokes it automatically. Of course, I do not want my processor to be active when I am compiling the processor itself; I only want it to be used during test compilation. Since a previous successful compilation makes it available to javac to execute, I had to find a way to avoid annotation processing during main source code compilation. javac has an option -proc:none that fits my requirement. All I had to do is to use that option during compilation, but not use it during testCompilation goal of the maven-compiler-plugin. I was surprised to find that there is no straight forward way to configure maven-compiler-plugin differently for different phases. The compiler always has one configuration that it applies to all the phases. In addition to that, you can specify extra configurations that it applies when executing certain goal. My discussion in Maven User forum has not made much progress, except that I found a work around to my problem. Eventually, I configured the plugin like this:
< plugin>
< groupId> org.apache.maven.plugins</groupId>
< artifactId> maven-compiler-plugin</artifactId>
< configuration>
< compilerArgument> -proc:none</compilerArgument>
</configuration>
< executions>
< execution>
< id> run-annotation-processor-only</id>
< phase> process-test-resources</phase>
< goals> < goal> testCompile</goal> </goals>
< configuration>
< compilerArgument> -proc:only< /compilerArgument>
</configuration>
</execution>
</executions>
</plugin>
If you carefully look at it, you can see that the above pom would invoke the compiler-plugin three times in the following order to execute test phase:
1) compile goal - during compile phase, during which it uses -proc:none option.
2) testCompile goal - during process-test-resources, during which it uses -proc:only option.
3) testCompile goal - during test-compile phase, during which it uses same configuration as compile goal.
An excellent overview Maven's build life cycle and various phases that it comprises of is available here.
4. Not able to pass multiple compilerArguments to javac
I have already discussed this in Maven forum and the outcome is enhancement request.
5. Maven not printing information printed using Messager
In my code, I use Messager object to report progress of my annotation processor. Those messages appear in my console when I run javac directly, but they appear nowhere when maven invokes the compiler using maven-compiler-plugin. Finally, I switched to using System.out, not a bright idea as it defeats the purpose of a Messager in the first place. Debugging javac launched via Maven, I gather some more information and have sent a mail to Maven User forum. Let's see what they respond. Until then, I am afraid, one has to use System.out if they want their annotation processor output to be visible in console while using Maven.
Conclusion:
Other than these issues, I had a pleasant experience of using Maven. I am not a maven expert - I am just starting. I have earlier used Maven (remember being a GlassFish developer, I execute maven scripts every day), but those were maven1 scripts that someone else has written, and I just run them. This was the first time I wrote maven2 scripts and used them. I do have a reasonable amount of experience of using build tools like Make, Ant, etc. Make is still my favorite tool - I used GNU Make quite a lot in the past when I used to work primarily in C++. I am particular interested in Maven because it addresses a key issue in software development - that of dependency management. It's a bill of materials issue in software well addressed by Maven. I don't think I need to praise Maven any more as lots of people have started to use it and we at GlassFish project have started using it in very actively. Here are a couple of thoughts:
1. I think any new addition to the maven central repository needs to be carefully evaluated. It would be better to have a staging (or beta) repository where experimental stuff can go. Only when an artifact is popular or standardized, it should find its way to the central repository. I know there is a process in place, but I still think the repository has far too many stuff out there than what it should have at this point of time.
2. Having wrappers(plugins) for underlying tools has its merits and demerits. e.g. underlying XJC plugin allows me to pass a URL, where as the maven-jaxb-plugin does not. Would it be better to just let people invoke the underlying tool directly, just as they did in Make? Or is that out of fashion now?
I look forward to your comments. Thanks for reading.
Continue Reading...
A javac plugin to manage your persistence.xml
Posted by ss141213 on November 26, 2007 at 07:54 AM | Permalink
| Comments (3)
I am glad to say that in most cases, I do not write a persistence.xml file any more. No, I am not using any IDE to generate it for me, nor has the spec relaxed any requirement. We will discuss some other time why persistence.xml is a necessary evil. Then what has changed? Well, my compiler creates it for me - yes, I said javac creates it for me. When I execute
javac -d . -classpath ... FooEntity.java BarEntity.java Main.java
, a META-INF/persistence.xml file with necessary entries is created next to my .class file. See an example
of such a generated file. If there already exists a persistence.xml, then the above command just updates the managed persistence class names in it. It does not matter whether javac is invoked from command line, or Ant or Maven. To a certain extent, I can even control what goes into that file. e.g. I can choose the provider name, and it generates appropriate entries specific to the provider. Similarly I can also specify what should be the persistence-unit name and so on. Interested? You can use it. Read on...
No, I have not modified javac. Starting with Java SE 6, the Java Compiler allows users to plug in an annotation processor which gets called in an appropriate phase of compilation. If an annotation processor is available in javac's classpath, then javac can discover it using service discovery mechanism. Read Annotation Processing in javac for further information. Using the said feature, I have written a simple, standard compliant javac plugin to provide the necessary functionality.
How you can use?
You can download the plugin from here
; it is free, comes with no warranty. I will try to support it as time permits. To use it, just add thet jar file to classpath during compilation. That's it, now go onto compile your code as usual. Being a compiler plugin, it is not even required at runtime. Even if it's there in runtime classpath, it is harmless. So, the easiest option is to copy the jar to $JAVA_HOME/lib/ext. Since lib/ext is used in runtime as well, no matter how harmless the jar is, some people don't like this approach. More over, in a shared system, you may not even have permission to write to lib/ext. In such a case, you can follow one of the following options:
For command line javac invocation, just add it to compiler classpath using -classpath option. e.g.,
javac -classpath $HOME/pxml-javac-plugin.jar:$CLASSPATH *.java
Ant users can modify the classpath attribute of the javac task. IDE users can add the plugin.jar in their project's compilation classpath.
Maven users:
Step 1: Download the special maven2 repository format zip file from here
.
Step 2: Install it in your local maven2 repository (by default ~/.m2/repository) by running following command:
unzip -d ~/.m2/repository pxml-javac-plugin-maven.zip
Step 3: Modify your pom to add a compile time dependency to this plugin like this:
< dependency>
< groupId> org.sanjeebsahoo</groupId>
< artifactId> pxml-javac-plugin</artifactId>
< version> 1.0-SNAPSHOT</version>
< scope> compile</scope>
</dependency>
That's it, no other change is needed. Isn't it straight forward to use?
How to pass additional options to the plugin:
Sometimes you may like to pass additional options to our plugin to control what goes inside the persistence.xml. Currently supported options are described below. Unless specified, all options are optional. Since there can be multiple plugins for the javac, in order reduce probability of name collision, each option is prefixed with "pxmlplugin."
1. pxmlplugin.provider: Acceptable values are toplink, hibernate, openjpa. Default value is toplink. Use this option when you don't have a persistence.xml file to start with and you want the provider to be other than toplink.
2. pxmlplugin.pu: name of the persistence-unit. Default value is pu1. It solves two purpose, viz:
a) when a new persistence.xml is created, this will be used as the name of the persistence-unit.
b) when there already exists a persistence.xml with multiple persistence-units, this is used to select the desired one for update.
3. pxmlplugin.verbose: prints additional information as debugging aid. By default, it is switched off.
How you specify the option depends on which javac you are using. While using javac that comes with Sun JDK, prefix the option by -A. e.g.,
javac -Apxmlplugin.provider=hibernate -Apxmlplugin.pu=pu1 -Apxmlplugin.verbose -classpath pxml-javac-plugin-1.0-SNAPSHOT.jar Foo.java
For non-Sun JDK, check your javac documentation.
Conclusion:
I find this plugin very useful. It not only saves me from writing persistence.xml file in most of the case, it allows me to avoid duplicating class names in persistence.xml, thus saves me from troubles that can arise because of duplication of information. Would love to know your comments. Suggest improvements. I will be happy to make it when I get the time. Since the plugin uses all the standard APIs, I expect it to work with all Java SE 6 compatible javac. You can try and let me know.
Continue Reading...
|