You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xmlbeans-cvs@xml.apache.org by pc...@apache.org on 2004/03/06 03:56:31 UTC
cvs commit: xml-xmlbeans/v2/jam/docs faq.html typedMetadata.html
pcal 2004/03/05 18:56:31
Added: v2/jam/docs faq.html typedMetadata.html
Log:
jam: doc updates
Revision Changes Path
1.1 xml-xmlbeans/v2/jam/docs/faq.html
Index: faq.html
===================================================================
<html>
<head>
<title>JAM FAQ</title>
<link href="jam.css" rel="stylesheet" type="text/css" />
</style>
</head>
<body>
<h2>JAM FAQ</h2>
<br /><br />
<h3>Why do I have to write all of these JAnnotationProxies? It seems like
such a pain.</h3>
<p>There are two ways this question can be answered; pick the answer you like best:
<ol>
<li><u>It really isn't that difficult, especially given the advantages
they provide, and it will soon be even easier</u>. Any decent IDE should be able to auto-generate stubs impls for a given 175 interface that you could quickly convert into a JAnnotationProxy. And when I have some extra time, I'm going to write a simple compiler that does exactly this from the command line.</li>
<li><u>You don't actually <i>have to</i> write them</u>. If you really,
really don't want to do write them and are willing to live without
strongly-typed metadata, JAM does provide a built-in alternative to
implementing JAnntationProxy yourself. Please refer to the javadocs on
UntypedJAnnotationProxy for details.
</li>
</ol>
</p>
<br /><br />
<h3>How do I get more control over how JAM maps my 175 Annotations to my JAnnotationProxy</h3>
<p>Typically, when implementing a JAnnotationProxy, you just extend JAnnotationProxyBase and add your properties. However, you are free to override
the base class' implementations of the methods on JAnnotationProxy so
as to customize how data gets set on the proxy.
</p>
<br /><br />
<h3>My program already supports some javadoc tags that don't map very nicely to my JAnnotationProxy. How can I continue to support those tags?</h3>
<p>You just need to take control of the mapping as described in the answer above.
</p>
<ol>
<li><u>It really isn't that difficult, especially given the advantages
they provide, and it will soon be even easier</u>. Any decent IDE should be able to auto-generate stubs impls for a given 175 interface that you could quickly convert into a JAnnotationProxy. And when I have some extra time, I'm going to write a simple compiler that does exactly this from the command line.</li>
<li><u>You don't actually <i>have to</i> write them</u>. If you really,
really don't want to do write them and are willing to live without
strongly-typed metadata, JAM does provide a built-in alternative to
implementing JAnntationProxy yourself. Please refer to the javadocs on
UntypedJAnnotationProxy for details.
</li>
</ol>
</p>
<br /><br />
<h3>Why can I get only one JAnnotation of a given type for each
class/method/field/whatever?</h3>
<p>
Sorry, but for better or worse, this is how JSR175 works. In a given
scope, you only get to have one annotation declaration of a given
annotation type. It only makes sense for JAM to do the same thing.
</p>
<br /><br />
<h3>So why doesn't JAM simply recreate the JVM's 'magic' and
synthesize implementations of 175 annotation types on the fly?</h3>
<p>That is something that has been considered. It certainly would not be
too hard to do, but it would introduce some restrictions that would negate
many of the advantages that JAM provides.</p>
<p>First and foremost, it would prevent your annotations from classloading
under under any pre-1.5 JRE. 175 annotation types all extend
<code>java.lang.annotation.Annotation</code>, and that class doesn't exist
in 1.4. Right off the bat, you would have locked your code into running
under 1.5.</p>
<p>Moreover, it's not entirely clear that writing your own implementation
of a JSR175 annotation interface is a valid thing to do. The spec doesn't
say anything about it, but it probably would strike some folks at
Sun as being at least slightly odd. I actually have done this with
1.5 beta 2 and javac makes no complaints, but it's not clear at this point
that this behavior will always be supported.</p>
<p>
Finally, the hot-codegenning the annotation type impls would also remove
another subtle advantage to the JAnnotationProxy approach, which is
that it allows annotation type authors to introduce behaviors into their
annotations. With plain JSR175, it is not possible, for example, to write
an annotation which performs a simple calculation, or contains any kind of
logic at all - it's all just dumb data. With JAnnotationProxies, you control
the class and you can make it do whatever you want. While this certainly
opens the door to people shooting themselves in the foot, I have never been
an advocate of protecting people from themselves.
</p>
<br /><br />
<h3>This JAnnotationProxy stuff sounds great and all, but there are some
cases where I really am going to need to be able to get at the 'real'
annotation object.</h3>
<p>
The JAnnotation interface does have two methods,
<code>getJavadocText()</code> and <code>getAnnotationObject</code> which
will give you the raw data if it is available. Use of these methods is
generally discouraged, since it means your code is making assumptions
about how the metadata is being represented and parsed. There can be no
guarantees that these methods will actually be able to return anything
to you. However, they are there if you really need them for some reason.
</p>
</body>
</html>
1.1 xml-xmlbeans/v2/jam/docs/typedMetadata.html
Index: typedMetadata.html
===================================================================
<html>
<head>
<title>Using JAM to Improve Developer Experience with JSR175</title>
<link href="jam.css" rel="stylesheet" type="text/css" />
</style>
</head>
<body>
<h2>Using JAM to Improve Developer Experience with JSR175</h2>
<h3>Overview</h3>
<p>
<a href='http://www.jcp.org/en/jsr/detail?id=175'>JSR175</a> is
a new metadata specification being rolled out in JDK1.5. It promises
to formalize the current ad-hoc javadoc tag-based systems by introducing
new language constructs and relfection APIs for viewing metadata. On
the whole, this is going to be a very good thing for the java
community.
</p>
<p>
However, JSR175 is also going to cause some difficulties for a particular
set of java developers who are trying to build particular kinds of tools.
This document provides a detailed description of what those difficulties
are and how the JAM API can help.
</p>
<h3>The Problem</h3>
<p>
One of the primary benefits promised by JSR175 is that it provides
a strongly-typed access model for java metadata. Gone will be the days
of parsing nastily-encoded javadoc tags, replaced by the convenience
of simple bean-like interfaces. Unfortunately, there are a few flaws in
how this vision has been realized.
</p>
<p>
The most obvious flaw is that it will only be possible in JDK1.5. Java
developers who wish to utilize 175 must convince their users to upgrade to
a new JRE. Historically, this has often been a very difficult thing to
do. Developers who are unable to convince their users to upgrade to 1.5
but wish to still take advantage of JSR175 are faced with the daunting
task of wedding older javadoc tag-based annotations with the newer 175
structures. The problem is compounded by the fact that Sun has
failed to articulate any migration strategy for such developers and users.
</p>
<p>
However, aside from JRE versioning problems, there is another, less-obvious
fly in the 175 ointment. This other problem will not evaporate as
soon as JRE 1.4.2 has gone the way of 1.0.2 - it is a problem fundamentally
woven into the JSR175 specification. It was the result of a conscious design
decision by the spec's authors, and it was made with good reasons
and the best of intentions. It is not a problem that will affect all
java developers, and perhaps not even very many of them. Nonetheless, for a
subset of java programmers trying to perform certain kinds of tasks, it
will remain a problem.
</p>
<p>
The problem is that these new, wonderful strongly-typed annotation
structures are interfaces, as opposed concrete classes.
</p>
<h3>Divergent APIs</h3>
<p>
Why is this a problem? To answer that, a quick perusal of the 1.5
JDK documentation is informative. Notice <code>java.lang.Class</code>,
you can get instances of <code>java.lang.annotation.Annotation</code>.
However, if you look at the 1.5 Javadoc Doclet API, you will see that
there is no way to do this; rather, it provides only untyped, 'by-name'
access to the annotation data.
</p>
<p>
Why is this? Remember that those Annotation types are really interfaces.
They don't have a constructor and you cannot instantiate them directly.
The 1.5 JVM has some magic to synthesize an implementation of the
annotation type on the fly at runtime and return an instance of it for you
to use. Unfortunately, the JVM doesn't share that magic with Javadoc,
or with anyone else, for that matter. As a result, there is no way to get
an instance of those annotation interfaces unless you are running in a VM
that has classloaded compiled versions of the annotated classes.
</p>
<p>
Now one may well ask: so what? Aren't you always running in a VM
that has classloaded compiled version of the annotated classes? Well,
this is true most of the time for most developers. However, there are some
developers who want to pre-process java souce before it is compiled.
Often this is because they want access to information that is lost
during java compilation, such as comments. This source analysis is
typically used in driving some kind code generation - xdoclet is a good
example of this. But more generally, this will be a problem for anyone
who is implementing or extending any kind of compilation technology which
directly reads JSR175-annotated java sources.
</p>
<p>
Sun's response (implicit in the 1.5 doclet API) seems to be that if you
are processing java sources in this way, not only are you stuck using a
completely different API for modelling java types, that API in turn forces
you to use an untyped access model for metadata on those types.
<p>
<p>
<u>This is unacceptable</u>, especially the last part. Strong typing
in java programming is almost always a Good Thing, and again, one of the
great promises of 175 is to bring strong typing to the world of metadata.
The failure to fully deliver on this promise to our cadre of compiler
and compiler extension authors may cause them to wonder what was so great
about JSR175 in the first place.
</p>
<p>
<h3>So what can JAM do about this?</h3>
<p>JAM's solution to this problem is simple: return control of the
metadata access model to the compiler authors. This is achieved by
allowing authors to create a bean which acts as a proxy to the JSR 175
annotation data; these beans are called <i>JAnnotationProxies</i>.</p>
<p>
The annotation data being proxied may in fact reside on an instance of
<code>java.lang.annotation.Annotation</code>, in the case where we are
looking at an annotation in a 1.5 VM. However, it may also simply
be an 175 annotation declaration in a java source file (remember,
again, that there we have no way to instantiate the 175 interface for
the annotation be declared). JAM does all the work to plug the
data into the JAnnotationProxy proxy. The compiler author in turn gets a
strongly-typed, consistent API metadata without having to worry about
where it's coming from.
</p>
<p>
Here is a simple example of what this looks like. Say we have defined
a 175 annotation type, like the following:
</p>
<pre>
package com.special;
public @interface SpecialId {
public int id() default 13;
}
</pre>
<p>
Our compiler author would then define a proxy to MyAnnotation that
would probably look something like this (except for the verbose
javadocs, which you can ignore unless you're looking for more detail):
</p>
<pre>
package com.special;
<!--
/**
* This class is defined by the author to act as a proxy to instances
* ot the SpecialId annotation. Though the author is largely free
* to do whatever they want in this class here, it is probably advisable
* to make it conform to the conventions and constraints
* associated with JSR 175 annotation types as much as possible, i.e.
* as if it actually implemented the 175 annotation interface.
* This means there are limits on the types of the bean properties
* and also that accessor methods are not prefixed with 'get'. A
* proxy should resemble the real thing as much as possible.
*
* The only really firm requirement is that the class be public,
* non-abstract, and have a public default constructor. It also is
* required that you provide setter methods that correspond to the type
* and names of the annotation properties so that JAM can use reflection
* to populate the JAnnotationProxy. (Actually, even this is not a really
* firm requirement if you're willing to do a little extra work - see
* the JAnnotationProxy javadocs for details).
*/
-->
public class SpecialIdProxy extends JAnnotationProxyBase {
public SpecialIdProxy() {} // a public no-arg constructor is required
public int id() { return mId;} // just like the 175 annotation method
public void setId(int id) { mId = id; } // this gets called by JAM
private int mId = 13; // note that this is same as default
}
</pre>
<p>
So far so redundant, right? Ok, let's write a very simple compiler-like
tool that wants to inspect a single java source file or java class, print out
it's comments (if it has any) and output the value of the SpecialId
annotation. That program would look something like this:
</p>
<pre>
package com.special;
public class SpecialIdViewer {
public static void main(String[] args)
{
// Verify the args are correct
if (args.length != 1) {
throw new Exception("must pass one java class name or "+
"relative source file path");
}
// Bootstrap into JAM. We need to create a JService that has either
// a class- or source- based representation of a class, depending
// on what they specified.
JServiceFactory factory = JServiceFactory.getInstance();
JServiceParams params = factory.createServiceParams();
// Establish a mapping between the 175 annotation type and our proxy
params.mapAnnotationProxy(SpecialIdProxy.class, "com.special.SpecialId");
// Decide whether they want to look at a source file or a class
if (args[0].endsWith(".java")) {
params.includeSourceFiles(new File("."),args[0]);
} else {
params.includeClass(args[0]);
}
// Ok, create our entry point into JAM
JService service = factory.createService(params);
// Whew! Ok, enough set up, let's get some real work done
// First, we get a JClass from the service that represents the
// class they specified. In this particular case, we know
// there can be exactly one, so just get it.
JClass clazz = service.getClasses[0];
// Now get the class doc and print it out if it's available
JComment comment = clazz.getComment();
System.out.println((comment != null) ? comment.getText():
"[comments are not available from classfiles]");
// Ok, get (a proxy to) the annotation
SpecialIdProxy annotation =
(SpecialIdProxy)c.getAnnotation(SpecialIdProxy.class);
// Note that at this point we have a typed structure for our metadata
// which we can use exactly like a real 175 annotation.
int specialId = annotation.id();
System.out.println("The special id is: "+specialId);
}
}
</pre>
<p>
We can now compile our SpecialIdViewer along with our SpecialId 175
annotation type and our SpecialIdProxy proxy. With that done, we might
now jar these things up and then send them off into the world so that
people can annotate their classes with SpecialIds and view them with our
SpecialIdViewer.
</p>
<p>Let's take a look at how that would work. Say we have a JRE 1.5 user
who has just downloaded our jar. They add our jar to their classpath
and then compile the following java source file they have written:
<pre>
package com.random.user;
import com.special.SpecialId;
/**
* This is some random user code.
* @SpecialId(id = 42)
*/
protected abstract class RandomUserCode {}
</pre>
<p>Now they run our SpecialIdViewer tool and see the following:</p>
<pre>
> java com.special.SpecialIdViewer com.random.user.RandomUserCode
[comments are not available from classfiles]
The special id is: 42
</pre>
<p>
Nothing too surprising there, but what exactly happened? When we asked
for the class' annotation of type SpecialIdProxy, JAM looked up the
SpecialId on the user class (because that's what we mapped
SpecialIdProxy to in the call to <code>mapAnnotationProxy()</code>),
created a new instance of SpecialIdProxy, copied the SpecialId data into
that proxy, and returned it to us so that we could print it out. It also
realized that it was unable to print out the class comments because it
only had the class, not the source.
</p>
<p>
At this point, you are probably still wondering why we went to all
of this trouble with this proxy business. Why didn't we just use the
175 SpecialId annotation type directly? To illustrate the advantage,
consider the same example again, except that this time the user
does not run <code>javac</code> on RandomUserCode.java and instead
provides our tool with only raw, uncompiled <code>.java</code> source code.
</p>
<pre>
> java com.special.SpecialIdViewer RandomUserCode.java
This is some random user code.
The special id is: 42
</pre>
<p>
Note JAM is able to give us the comment now, since this time we
have the source code. But otherwise, things basically worked just
the same as before. Nothing to surprising here, either.
</p>
<p>
But wait, isn't that itself surprising? Think for a minute about what you
would have to do in order to make this work without JAM (i.e. with
reflection and javadoc or something similar like xjavadoc or qdox.
At the point in our SpecialIdViewer where you decide whether you are
looking at a source file or a class file, you would have to fork your
codepath - on the one path, you would use the Reflection APIs, get your
175 SpecialId instance directly, and decide not to print out a comment.
On the other path, you would have to use javadoc's untyped API to get the
SpecialId value, and in this case you would be able to print out a comment.
In effect, you would have written almost all of your tool twice over.
</p>
<p>
In case you're still not surprised, consider what happens if our user
suddently decides to switch back to JRE 1.4. If they repeat the
previous example (using the source file), it still works just the
same. How is that possible, you ask? Well, first off, note that
SpecialIdViewer itself will still load just fine under 1.4 because
SpecialIdProxy has isolated it from any 1.5 dependencies. And because
we have the source file (RandomUserCode.java), JAM's java source parser is
able to parse the 1.5 annotations and populate the SpecialIdProxy without
requiring anything from 1.5.
</p>
<p>
That may be a neat party trick, but is it really useful? After all,
RandomUserCode is never going to compile or classload under 1.4.
Why someone want to do this? In truth, they probably won't. However,
they might want to do something similar but slightly different.
</p>
<h2>Processing Javadoc Tags</h2>
<p>
Take the case of a user who has not yet upgraded to JDK 1.5. They
simply refuse to upgrade, for the usual reasons users refuse to upgrade.
They still want to markup their classes with our SpecialIds, but because
they won't have JRE 1.5, they won't be able to use our JSR175 annotation
(SpecialId) to do it.</p>
<p>
The obvious choice in this case to let them declare their special ids
with javadoc tags, which have been the de facto standard for declaring
java metadata prior to JDK 1.5. Accordingly, our stubborn 1.4 user might
expect to be able to run SpecialIdViewer on the following 1.4 java
source file:
</p>
<pre>
package com.stubborn.user;
/**
* This is code from someone who simply will not upgrade to 1.4.
*
* @special id=79
*/
protected abstract class StubbornUserCode {}
</pre>
<p>
This seems reasonable enough. Unfortunately, though, that means we're going
to have to write a lot more code in our tool. Now we have to check for
javadoc tags, parse them if they are there, and then somehow glue
everything together in our code so that the behavior is the same. Right?
</p>
<p>
Wrong - fortunately, JAM can be a big help in this case as well. To support
this use case, we only need to add one new line (in bold) to our
SpecialIdViewer tool:
</p>
<pre>
package com.special;
public class SpecialIdViewer {
public static void main(String[] args)
{
...
// Establish a mapping between the 175 annotation type and our proxy
params.mapAnnotationProxy(SpecialIdProxy.class, "com.special.SpecialId");
// Also establish a mapping between the 'special tag and our proxy
<b>params.mapJavadocTagProxy(SpecialIdProxy.class, "special");</b>
...
}
</pre>
<p>
Now our stubborn 1.4 user runs our tool, and the desired output appears:
</p>
<pre>
> java com.special.SpecialIdViewer StubbornUserCode.java
This is code from someone who simply will not upgrade to 1.4.
The special id is: 79
</pre>
<p>
This works by exactly the same principle as our earlier example using
JRE 1.5. We have created a SpecialIdProxy through which we want to
view metadata. We now have told JAM that we want that class to be a
proxy for the 'special' javadoc tag (as well as the SpecialId 175
annotation type). When JAM is processes the java source, it therefore
knows that when it sees a 'special' tag, it should parse out it's values
and populate an instance of our SpecialIdProxy for us to use in our code.
</p>
<p>
So, here again we see how useful the annotation proxy is. Because we
have isolated our processing logic from the details of parsing metadata,
the same code works in all of the different scenarios we have seen: with
source files, with class files, with 175-style annotations, or with javadoc
annotations. You don't have to waste time worrying about how your
metadata and type information is going to get represented or parsed.
If you write against JAM, it just works.
</p>
<p>
<i>Still have questions? <a href='faq.html'>Try looking in FAQ.</a>
</p>
</body>
</html>
---------------------------------------------------------------------
To unsubscribe, e-mail: xmlbeans-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: xmlbeans-cvs-help@xml.apache.org