Annotation scanning with java 9 multi version jars


bitonti@...
 

Hi,

I don't see an application of MR to EAR, but EARs don't package classes directly, so that seems natural.  (But are there any resource loading cases for EAR files?)

Reasons that I'm finding that suggest that WAR should support MR is that (1) the extension seems straightforward; and (2) the use of MR for other module types is direct, meaning, MR enablement may be likely for other module types (3) having fragment JARs support MR but having the WAR itself not seems exceptional behavior.

In any case, if fragment JARS *do* support MR, we still have the main question: What are the rules for scanning annotations?

My simplest answer is that since JavaEE/servlet annotations are an alternate to metadata which can be specified in a descriptor, and descriptors live in WEB-INF (or META-INF), and those are single versioned, then, JavaEE/servlet annotations should be single versioned.  Then, all annotations must be present in base class versions, and no alteration is permitted (or is visible) in MR version specific classes.

I presume that this applies to not only descriptor type annotations, but also to Servlet Container Initializer (SCI) related annotations.

Despite this rule, there might be a problem, in that annotation processing can involve class relationships (superclasses and interfaces).  Would superclasses or interfaces be allowed to vary for annotated classes across MR versions?

Thx!
Thomas Bitonti


Greg Wilkins
 


I agree with Mark, I don't believe that MR behaviour should necessarily apply to WAR and EAR files.

Sure if you give war file to a java9 ClassLoader, it will happily version WEB-INF/web.xml.  However it is not a correct thing to do to give a war file to a java9 ClassLoader, as it will not be able to load any classes from WEB-INF/classes (versioned or not).   So while a war is a jar, it is not a classloadable jar.

Also the precedent set by the java9 ClassLoader itself is that meta-data is not versioned - ie all of META-INF is excluded from versioning.  It's thus reasonable to say that MR considerations should not automagically apply to WEB-INF either.

Furthermore, I see no reason for a war file to support MR in any way other than WEB-INF/lib may contain MR jars.  Versioning other resources in a war makes no sense as any selection of resourced based on version surely should be the client version not the server version?

regards


On 15 January 2018 at 13:17, Mark Thomas <markt@...> wrote:
On 09/01/18 15:18, bitonti@... wrote:
> Hi,
>
> Looking over the JEP238 and JavaDoc, the focus is on resource lookup.  I
> don't see any behavior which distinguishes a class type resource from a
> non-class type resource.  Once a resource is available in a JAR it never
> goes away.  Once the resource of a member class is available at a MR
> version, it remains available at all higher MR versions.
>
> From a metadata perspective, I'm seeing mixed directions.  Metadata
> which is provided beneath META-INF is fixed across all MR versions. 
> But, module descriptors can be MR version specific.  Module "uses"
> clauses can be MR version specific.  Then, does that imply a need to
> handle different class paths for JavaEE modules -- which contradicts
> that META-INF metadata is fixed?
>
> From an annotation scanning perspective, the best I've determined is
> that some annotations will be independent of MR version (probably
> anything that can be mapped back to module descriptors, which live in
> META-INF, and which is MR version independent), while other annotations
> are tied to class details and will vary across MR versions (for example,
> @Trivial, which WebSphere uses for dynamic trace injection.)  The
> question that I have is whether any JavaEE active annotations would be
> of the runtime type, meaning, allowed to vary by MR version.
>
> I presume that WAR and RAR files, as specializations of JAR files,
> automatically acquire the MR JAR behavior.

My starting position is the opposite.

The summary in JEP 238 starts:
"Extend the JAR file format to allow multiple, Java-release-specific
versions of class files to coexist in a single archive."

That says to me, multi-release features are intended only for classes.

Secondly, WAR files do not implement all the features of JARs. See the
glossary of the Servlet spec for some explicit examples.

Therefore, unless the behaviour is explicitly called out in the Servlet
spec my starting position is that a WAR file does not support it.

Mark

> For WAR files there seem to
> be questions about how exactly to map base resources to the MR version
> specific locations, e.g., should "WEB-INF/classes" be discarded.  (A
> simplistic extension of MR JAR function to WAR files would not be aware
> of "WEB-INF" as a distinguished directory and would carry that into the
> mapped MR version specific locations.)
>
> Thx!
> Thomas Bitonti
> IBM - WebSphere Application Server
>







--
Greg Wilkins <gregw@...> CTO http://webtide.com


Mark Thomas
 

On 09/01/18 15:18, bitonti@... wrote:
Hi,

Looking over the JEP238 and JavaDoc, the focus is on resource lookup.  I
don't see any behavior which distinguishes a class type resource from a
non-class type resource.  Once a resource is available in a JAR it never
goes away.  Once the resource of a member class is available at a MR
version, it remains available at all higher MR versions.

From a metadata perspective, I'm seeing mixed directions.  Metadata
which is provided beneath META-INF is fixed across all MR versions. 
But, module descriptors can be MR version specific.  Module "uses"
clauses can be MR version specific.  Then, does that imply a need to
handle different class paths for JavaEE modules -- which contradicts
that META-INF metadata is fixed?

From an annotation scanning perspective, the best I've determined is
that some annotations will be independent of MR version (probably
anything that can be mapped back to module descriptors, which live in
META-INF, and which is MR version independent), while other annotations
are tied to class details and will vary across MR versions (for example,
@Trivial, which WebSphere uses for dynamic trace injection.)  The
question that I have is whether any JavaEE active annotations would be
of the runtime type, meaning, allowed to vary by MR version.

I presume that WAR and RAR files, as specializations of JAR files,
automatically acquire the MR JAR behavior.
My starting position is the opposite.

The summary in JEP 238 starts:
"Extend the JAR file format to allow multiple, Java-release-specific
versions of class files to coexist in a single archive."

That says to me, multi-release features are intended only for classes.

Secondly, WAR files do not implement all the features of JARs. See the
glossary of the Servlet spec for some explicit examples.

Therefore, unless the behaviour is explicitly called out in the Servlet
spec my starting position is that a WAR file does not support it.

Mark

For WAR files there seem to
be questions about how exactly to map base resources to the MR version
specific locations, e.g., should "WEB-INF/classes" be discarded.  (A
simplistic extension of MR JAR function to WAR files would not be aware
of "WEB-INF" as a distinguished directory and would carry that into the
mapped MR version specific locations.)

Thx!
Thomas Bitonti
IBM - WebSphere Application Server


bitonti@...
 

Hi,

Looking over the JEP238 and JavaDoc, the focus is on resource lookup.  I don't see any behavior which distinguishes a class type resource from a non-class type resource.  Once a resource is available in a JAR it never goes away.  Once the resource of a member class is available at a MR version, it remains available at all higher MR versions.

From a metadata perspective, I'm seeing mixed directions.  Metadata which is provided beneath META-INF is fixed across all MR versions.  But, module descriptors can be MR version specific.  Module "uses" clauses can be MR version specific.  Then, does that imply a need to handle different class paths for JavaEE modules -- which contradicts that META-INF metadata is fixed?

From an annotation scanning perspective, the best I've determined is that some annotations will be independent of MR version (probably anything that can be mapped back to module descriptors, which live in META-INF, and which is MR version independent), while other annotations are tied to class details and will vary across MR versions (for example, @Trivial, which WebSphere uses for dynamic trace injection.)  The question that I have is whether any JavaEE active annotations would be of the runtime type, meaning, allowed to vary by MR version.

I presume that WAR and RAR files, as specializations of JAR files, automatically acquire the MR JAR behavior.  For WAR files there seem to be questions about how exactly to map base resources to the MR version specific locations, e.g., should "WEB-INF/classes" be discarded.  (A simplistic extension of MR JAR function to WAR files would not be aware of "WEB-INF" as a distinguished directory and would carry that into the mapped MR version specific locations.)

Thx!
Thomas Bitonti
IBM - WebSphere Application Server


Greg Wilkins
 


All,

I've had a conversations regarding this issue on jigsaw-dev and core-libs-dev. While there was some acknowledgement that the JarFile API is incomplete regarding support for Multi Release jars, there was not much enthusiasm taking on the responsibility of filtering out non applicable classes.

Specifically I proposed:
the stream needs to handle inner classes and only include them
if their matching outerclass is available at the same version. So for
example a base Foo$Bar.class will only be included if the stream includes a
base Foo.class, and it will not be included if the Foo.class is version 9
or above. Likewise a version 9 Foo$Bar.class will only be included in the
stream if the stream also includes a version 9 Foo.class, and will not be
included if the stream has a version 10 or above Foo.class

and provided a proposed implementation: 

to which the response from Alan Bateman at oracle was:

I don't think this should be pushed down to the JarFile API. The JarFile
API provides the base API for accessing JAR files and should not be
concerned with the semantics or relationship between entries. I agree
that annotation scanning tools and libraries need to do additional work
to deal with orphaned or menacing inner classes in a MR JAR but it's not
too different to arranging a class path with a JAR file containing the
"classes for JDK 9" ahead of a JAR file containing the version of the
library that runs on JDK 8. I do think that further checks could be done
by the `jar` tool to identify issues at packaging time.

So if that view prevails, then it is up to the application specification and container developers to agree on a semantic of what classes will be scanned, else we will have a major portability problem.   There were several responses in the discussion there that indicated that such problems are already being seen in the wild as libraries are being released using this features in advance of the tools being able to cope.

So it would be really great if we could come up with an agreement on how to apply interpret the "semantic relationships" within a MR Jar.      

I think the main driver for my proposal are that: it avoids the need to analyse the class files to determine relationships and applicability; b) it is a likely default layout for MR jars;  c) it has defensible semantics.

However, I think it more important that there is an agreed portable interpretation, rather than any one interpretation, so happy to consider other proposals.

cheers








On 14 September 2017 at 03:28, Mark Thomas <markt@...> wrote:
On 13/09/2017 01:02, Greg Wilkins wrote:
>
>
> All,
>
> We are a bit concerned with providing reasonable support for annotation
> scanning in java 9 multi version jars.

Likewise.

> We are already seeing these jars in our java 8 deployments (from some
> logging frameworks), so we are having to update our scanning to ignore
> the versioned classes (as they can't be java 8).
>
> However, we want to update our scanning to support running on java 9,
> but just cannot work out a good algorithm for doing so, specially when
> confronted with inner classes.
>
> A multi versioned jar might contain something like:
>
>   * org/example/Foo.class
>   * org/example/Foo$Bar.class
>   * META-INF/versions/9/org/example/Foo.class
>
> So it is clear that there is a java 9 version of Foo.  But what is
> unclear is the inner class Foo$Bar?  Is that only used by the base Foo
> version? or does the java 9 version also use the Foo$Bar inner class,
> but it didn't use any java 9 features, so the base version is able to be
> used??
>
> So it looks like we are going to need to analyse the actual Foo class
> version used to see if Foo$Bar is referenced and only then scan Foo$Bar
> for annotations (and recursive analysis for Foo$Bar$Bob class )!     It
> means that given the index only of a jar, it is impossible to come up
> with the list of classes that will be provided by that jar for a given
> JVM!    The only alternative would be if there was an enforced
> convention that any versioned class would also version all of it's inner
> classes - which may be a reasonable assumption given that they would be
> compiled together, but we see nothing in the specifications that force a
> jar to be assembled that way.
>
> Is this something the other containers are assuming (ie if there is  no
> META-INF/versions/9/org/example/Foo$Bar.class, then the inner class is
> not used by the versioned outer class and thus do not scan
> org/example/Foo$Bar.class)?
>
> Or are we going to have to engage in code analysis just to determine
> which classes we need to analyse for annotations?

We (Tomcat) haven't had any reports of problems in this area yet. It is
on the list of things to worry about (and thanks for setting out the
problems so clearly) but we haven't looked at a solution yet.

The convention that any versioned class would also version all of it's
inner classes sounds like a reasonable solution to me.

Have you asked about this on jigsaw-dev@... ?

Cheers,

Mark







--
Greg Wilkins <gregw@...> CTO http://webtide.com


Mark Thomas
 

On 13/09/2017 01:02, Greg Wilkins wrote:


All,

We are a bit concerned with providing reasonable support for annotation
scanning in java 9 multi version jars.
Likewise.

We are already seeing these jars in our java 8 deployments (from some
logging frameworks), so we are having to update our scanning to ignore
the versioned classes (as they can't be java 8).

However, we want to update our scanning to support running on java 9,
but just cannot work out a good algorithm for doing so, specially when
confronted with inner classes.

A multi versioned jar might contain something like:

* org/example/Foo.class
* org/example/Foo$Bar.class
* META-INF/versions/9/org/example/Foo.class

So it is clear that there is a java 9 version of Foo. But what is
unclear is the inner class Foo$Bar? Is that only used by the base Foo
version? or does the java 9 version also use the Foo$Bar inner class,
but it didn't use any java 9 features, so the base version is able to be
used??

So it looks like we are going to need to analyse the actual Foo class
version used to see if Foo$Bar is referenced and only then scan Foo$Bar
for annotations (and recursive analysis for Foo$Bar$Bob class )! It
means that given the index only of a jar, it is impossible to come up
with the list of classes that will be provided by that jar for a given
JVM! The only alternative would be if there was an enforced
convention that any versioned class would also version all of it's inner
classes - which may be a reasonable assumption given that they would be
compiled together, but we see nothing in the specifications that force a
jar to be assembled that way.

Is this something the other containers are assuming (ie if there is no
META-INF/versions/9/org/example/Foo$Bar.class, then the inner class is
not used by the versioned outer class and thus do not scan
org/example/Foo$Bar.class)?

Or are we going to have to engage in code analysis just to determine
which classes we need to analyse for annotations?
We (Tomcat) haven't had any reports of problems in this area yet. It is
on the list of things to worry about (and thanks for setting out the
problems so clearly) but we haven't looked at a solution yet.

The convention that any versioned class would also version all of it's
inner classes sounds like a reasonable solution to me.

Have you asked about this on jigsaw-dev@... ?

Cheers,

Mark


Greg Wilkins
 



All,

We are a bit concerned with providing reasonable support for annotation scanning in java 9 multi version jars.

We are already seeing these jars in our java 8 deployments (from some logging frameworks), so we are having to update our scanning to ignore the versioned classes (as they can't be java 8).

However, we want to update our scanning to support running on java 9, but just cannot work out a good algorithm for doing so, specially when confronted with inner classes.

A multi versioned jar might contain something like:
  • org/example/Foo.class
  • org/example/Foo$Bar.class
  • META-INF/versions/9/org/example/Foo.class
So it is clear that there is a java 9 version of Foo.  But what is unclear is the inner class Foo$Bar?  Is that only used by the base Foo version? or does the java 9 version also use the Foo$Bar inner class, but it didn't use any java 9 features, so the base version is able to be used??

So it looks like we are going to need to analyse the actual Foo class version used to see if Foo$Bar is referenced and only then scan Foo$Bar for annotations (and recursive analysis for Foo$Bar$Bob class )!     It means that given the index only of a jar, it is impossible to come up with the list of classes that will be provided by that jar for a given JVM!    The only alternative would be if there was an enforced convention that any versioned class would also version all of it's inner classes - which may be a reasonable assumption given that they would be compiled together, but we see nothing in the specifications that force a jar to be assembled that way.

Is this something the other containers are assuming (ie if there is  no META-INF/versions/9/org/example/Foo$Bar.class, then the inner class is not used by the versioned outer class and thus do not scan org/example/Foo$Bar.class)?

Or are we going to have to engage in code analysis just to determine which classes we need to analyse for annotations?

regards

--
Greg Wilkins <gregw@...> CTO http://webtide.com