It is a library that allows the parameter names of non-private methods and constructors to be accessed at runtime. Normally this information is dropped by the compiler. In effect, methods like 'doSometing(mypkg.Person toMe)' currently look like 'doSomething(mypackage.Person ???)' to people using Java's reflection to inspect methods.
To date parameter name access has not been very useful to Java application developers, but with the advent of advanced scripting languages and web action frameworks for the JVM it is of increasing importance to be able to leverage a method's parameter names. Scripting languages like Groovy & JRuby, web action frameworks like Waffle and VRaptor (that verge on the transparent) and the compelling Grails. SOAP and REST designs could also benefit.
ParaNamer allows you to generate and use parameter name info for versions of Java prior to JDK 5.0 and above. Parameter name access was scheduled for JDK 6.0, but was cancelled at a late stage as the spec-lead suggested the development team ran out of time to implement it. It is sadly not shipping in JDK 7.0 either. Sun also had misgivings about the appropriateness of the this change to Java. It was felt that applications could end up depending on parameter names, and that they essentially became part of constructor/method signatures and could never be changed if you wanted to be backwards compatible. The view of the authors of Paranamer is that you should be aware that parameter names may change between releases, and code to not depend on them.
Paranamer is Open Source, and licensed as BSD. It is compatible with commercial/proprietary, GPL, Apache use.
There is a method called 'lookupParameterNames' that returns an array of strings for a method or constructor.
Method method = Foo.class.getMethod(...);
Paranamer paranamer = new DefaultParanamer();
String[] parameterNames = paranamer.lookupParameterNames(method) // throws ParameterNamesNotFoundException if not found
// or ...
parameterNames = paranamer.lookupParameterNames(method, false) // will return null if not found
ParaNamer does not have any runtime jar dependencies while looking up parameter info previously generated that's been zipped into a jar.
DefaultParanamer tries to read parameter name data from an extra public static field on the class. This field need to be added after compilation of the class, and before you zip the resulting classes in a jar.
The static field essentially looks like the following. You really do not need to know this unless your going to make something compatible with Paranamer:
private static final String __PARANAMER_DATA = "v1.0 \n"
+ "<init> com.example.PeopleService peopleService \n"
+ "setName java.lang.String,java.lang.String givenName,familyName \n";
+ "setDateOfBirth int,int,int day,month,year \n";
Clearly the method's source needs to be analysed and lines added per method to that __PARANAMER_DATA field. See below.
If generating meta data for parameter names at compile time is not for you, try class BytecodeReadingParanamer as a runtime only solution. This uses a cut down forked and cut-down version of ASM to extract debug information from a class at runtime. As it happens this is the fallback implementation for CachingParanamer when DefaultParanamer reports that there is no meta data for a class.
As it happens Groovy classes loaded through parseClass are compatible with Paranamer. That said, GroovyClassLoader does not make the bytecode available by default after loading. Take a look at the way this was solved by the JBehave team.
AnnotationParanamer uses the @Named annotation from JSR 330 and extracts names pertinent to parameters from that.
public static class Something { public void doSomething(@Named("usedName") String ignoredName) { } }
AnnotationParanamer takes a delegate paranamer instance as an optional constructor arg. This will allow constructors and methods to only partly leverage @Named, with other parameters having non-annotated parameter names (the via say DefaulParanamer or BytecodeReadingParanamer).
If you have an alternate annotation to @Named, then you can specify that in a subclass of AnnotationParanamer that overrides two methods isNamed and getNamedValue. Your overridden methods should do the equivalent of 'return ann instanceof Named;' and 'return ((Named) ann).value();' respectively.
If you are using @Named from JSR 330, you will need it in your classpath of course. In Maven terms, Paranamer is built with the 'javax.atinject' module as an optional dependency.
CachingParanamer stores the results of each parameter name lookup, so that second and subsequent invocations will be far quicker. Pass in another Paranamer implementation via constructor so that the CachingParanamer can defer to it for cache misses.
AdaptiveParanamer is designed for using a series of Paranamer implementations together. The first supplied is asked if it can supply parameter name data for a constructor/method. If it cannot, then the next one is asked and so on. The default constructor for this uses DefaultParanamer with ByteCodeReadingParanamer as its contingency.
This is only needed for DefaultParanamer as BytecodeReadingParanamer relies on the presence of a debug table in bytecode. It is also only a solution for Java source, as Groovy source is not compatible with the underlying technologies.
You need to download:
<taskdef name="paranamer"
classname="com.thoughtworks.paranamer.ant.ParanamerGeneratorTask"/>
<paranamer sourceDirectory="src/java" outputDirectory="target/classes"/>
Classes are changed to have an extra static String member variable that contains the member functions and their parameter names. Be sure to zip them up in to you final jar.
<plugin>
<groupId>com.thoughtworks.paranamer</groupId>
<artifactId>paranamer-maven-plugin</artifactId>
<executions>
<execution>
<id>run</id> <!-- id is optional -->
<configuration>
<sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
</configuration>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions> <dependencies> <!-- if some of parameter names you need to retain are held in pre-existing jars, they need to be added to the classpath -->
<dependency>
<groupId>some-artifact-group</groupId>
<artifactId>some-artifact</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</plugin>
Many people feel there are already too many jars required for day to day Java development. If you feel this way, simply consume the main paranamer jar into your project's jar using the Maven2 shade plugin.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactId>artifact-shaded</shadedArtifactId>
<relocations>
<relocation>
<pattern>com.thoughtworks.paranamer</pattern>
<shadedPattern>com.yourcompany.yourapp.paranamer</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
The general answer to this question is that you should not ship something to third parties where they are going to hard-core your parameter names into their application. For you own in-house stuff, accessing parameter names is harmless. You should be able to ripple though the source code of even large applications, and change say "badSpeeldWord" to "badlySpelledWorld" if you need to.
In a later version we may store previous parameter names too. It won't be
be magic, you'll have to code the previous parameter names in your
source with a doclet tag. The lookup mechanism will change too.
It will likely leverage a doclet tag.
Paranamer will go away in the future when Java gets Reflection
access to Method parameter names. That should look like:
Method method = Foo.class.getMethod(....)
String[] parameterNames = method.getParameterNames();
But for now, we are thinking of the following directions :-
Release 2.2 and onwards have a debug release associated with them. These are compiled against the full ASM jar and require it as a transitive dependency. The non debug releases compile against a cut down fork of the bytecode reading ASM. This advice only affects the BytecodeReadingParanamer implementation.
Release 2.3 - Oct 19 2010
Release 2.2.1 - Jun 22 2010
Release 2.2 - Jan 2 2010 (all after this are JDK 5.0 and above only)
Release 2.1 - Sep 6 2009 (last version that is compatible with JDK 1.4)
Release 2.0 - Jul 10 2009
Release 1.5 - May 19 2009
Release 1.4 - May 9 2009
Release 1.3 - Feb 22 2009
Release 1.2 - Feb 2 2009
Release 1.1.7 - Jan 20 2009
Release 1.1.6 - Dec 22 2008
Release 1.1.5 - Aug 14 2008
Release 1.1.4 - Jun 19 2008
Release 1.1.3 - May 16 2008
Release 1.1.2 - Mar 16 2008
Release 1.1.1 - Nov 28 2007
Release 1.1 - Oct 14 2007
Release 1.0.1 - Jul 07 2007
Release 1.0 - Jul 01 2007
The unit tests for Paranamer illustrate some more way to use it: https://github.com/paul-hammant/paranamer/tree/master/paranamer/src/test/com/thoughtworks/paranamer
Developers, Maven 2 repository, lists, sites are detailed
here:
http://xircles.codehaus.org/projects/paranamer
Latest Released Jar file here (auto-download):
https://nexus.codehaus.org/service/local/artifact/maven/redirect?r=releases&g=com.thoughtworks.paranamer&a=paranamer&v=2.3