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