Sorry, forget to Cc core-libs on my response to Peter’s questions

> Begin forwarded message:
> 
> From: Steve Drach <[email protected]>
> Subject: Re: RFR 8163798: Add a versionedStream method to JarFile
> Date: August 26, 2016 at 9:44:39 AM PDT
> To: Peter Levart <[email protected]>
> 
> Hi Peter,
> 
>> Javadoc for the new method mentions public and non-public classes:
>> 
>> "@{code JarEntries}
>>    * containing non-public classes in the
>>    * versioned directory that corresponds to the version returned by
>>    * {@link JarFile#getVersion()} are included in the stream because they are
>>    * typically accessed by the public classes."
>> 
>> Why?
> 
> The public “interface” for a multi-release jar is the set of public or 
> protected classes in the base (or root) section of the jar.  By definition, 
> no new (i.e. without a corresponding base class) public or protected classes 
> can be contained in a versioned directory.  Versioned directories can contain 
> new package-private classes.  In practice these package-private classes are 
> dependencies for the public/protected classes in
> in the same versioned directory.
> 
>> The logic of multi-release jar files is indifferent towards the access 
>> attributes of class files. It never parses the class file to interpret the 
>> access attributes of the containing class.
> 
> I know.  I thought about doing that but rejected it as too heavyweight and 
> just decided to use the heuristic that real class files have the suffix 
> “.class” and that if there was not a corresponding base class, then the class 
> has to be package-private according to the “rules” of multi-release jar 
> files.  The jar tool enforces these rules by actually parsing the class files.
> 
>> The class-level javadoc of JarFile does mention "public classes", but I view 
>> this just as a recommendation how they should be constructed.
>> 
>> The logic of multi-release jar files is indifferent towards the types of 
>> resources contained in the jar entries too, as far as I can see. Your logic 
>> in versionedStream() is not. The proposed new method is the only place in 
>> JarFile and ZipFile where the check for ".class" suffix of the entry name is 
>> performed. Why this special treatement of class files?
> 
> I hope the explanation above is sufficient.  The versionedStream looks at the 
> jar file in an inverted or backwards way.  In the usual case, a
> multi-release jar file entry is obtained by 
> JarFile.getJarEntry(base-entry-name).  In this case if the jar file was 
> opened for version 10, we’d not be able to see or get package-private classes 
> in the version 9 directory.  Oh, one thing I should mention, Mr. Jar’s can be 
> sparsely populated in the versioned directories, So if a version 10 public 
> class was not in the version 10 directory, but was in the version 9 
> directory, then the version 9 one would be returned.  
> 
>> I also don't understand why the versioned stream should contain the 
>> untranslated base versioned directories. For example, the following entries 
>> for the v10 stream in the test:
>> 
>> "META-INF/versions/9/"
>> "META-INF/versions/10/“
> 
> I just did it because they are returned by JarFile::stream.  I’m ambivalent 
> about it, so I wanted to see the comments if any.
> 
>> 
>> All other resources and sub-directories in the META-INF/versions/XX/... 
>> directories are "normalized" (meaning that the versioned prefix is stripped 
>> from the names in the returned entries). Normalizing above directories would 
>> translate them to "root" directory, which is never included as an entry of a 
>> normal jar or zip file. So I think they could simply be skipped in the 
>> resulting versioned stream. Couldn't they?
> 
> Yes.
> 
>> 
>> Considering all of the above, I think versionedStream() method could be much 
>> simpler. For example, something like the following:
>> 
>> 
>>   public Stream<JarEntry> versionedStream() {
>>       return
>>           !isMultiRelease()
>>           ? stream()
>>           : stream()
>>               .flatMap(je -> {
>>                   String name = je.getName();
>>                   if (name.startsWith(META_INF_VERSIONS)) {
>>                       // versioned entry
>>                       if (name.length() > META_INF_VERSIONS.length()) {
>>                           // potentially real entry
>>                           String vStr = 
>> name.substring(META_INF_VERSIONS.length());
>>                           int offset = vStr.indexOf('/');
>>                           int version = 0;
>>                           if (offset >= 0) try {
>>                               version = Integer.parseInt(vStr.substring(0, 
>> offset));
>>                           } catch (NumberFormatException e) { /* ignore */ }
>>                           if (version <= 0) {
>>                               throw new RuntimeException(
>>                                   "Can't obtain version from multi-release 
>> jar entry: "
>>                                   + name);
>>                           }
>>                           return (version <= versionMajor && vStr.length() > 
>> offset + 1)
>>                                  // normalized name of real entry
>>                                  ? Stream.of(vStr.substring(offset + 1))
>>                                  // version > versionMajor or
>>                                  // META-INF/versions/<version>/ dir - 
>> ignored
>>                                  : Stream.empty();
>>                       } else {
>>                           // META-INF/versions/ dir - ignored
>>                           return Stream.empty();
>>                       }
>>                   } else { // base entry
>>                       return Stream.of(name);
>>                   }
>>               })
>>               .distinct()
>>               .map(this::getJarEntry);
>>   }
>> 
>> 
>> What do you think?
> 
> It’s certainly more sophisticated than the way I did it. If we could 
> constrain the getJarEntry only to base names we might be able to handle the 
> package-private classes too.  I need to think about it a bit.
> 
> Steve
> 
>> 
>> Regards, Peter
>> 
>> 
>> On 08/26/2016 07:16 AM, Tagir Valeev wrote:
>>> Hello!
>>> 
>>> Small nitpick:
>>> 
>>> versionsMap.keySet().forEach(v -> {
>>>    Stream<String> names = versionsMap.get(v).stream().map(nm -> nm.name);
>>>    if (v == versionMajor) {
>>>        // add all entries of the version we are interested in
>>>        finalNames.addAll(names.collect(Collectors.toSet()));
>>>    } else {
>>>        // add resource entries found in lower versioned directories
>>>        finalNames.addAll(names.filter(nm -> nm.endsWith(".class") ? false
>>> : true)
>>>                .collect(Collectors.toSet()));
>>>    }
>>> });
>>> 
>>> I suggest to replace with
>>> 
>>> versionsMap.forEach((v, list) -> {
>>>  Stream<String> names = list.stream().map(nm -> nm.name);
>>>  if (v != versionMajor) {
>>>    names = names.filter(nm -> !nm.endsWith(".class"));
>>>  }
>>>  names.forEach(finalNames::add);
>>> });
>>> 
>>> This is cleaner and somewhat faster to use Map.forEach as you don't need to
>>> lookup every map entry.
>>> 
>>> Also I don't see why "nm -> nm.endsWith(".class") ? false : true" could be
>>> better than
>>> "nm -> !nm.endsWith(".class")". Probably a matter of style though.
>>> 
>>> Finally there's no need to collect into intermediate set just to add this
>>> set into finalNames.
>>> Instead you can drain the stream directly to finalNames via forEach.
>>> 
>>> Probably it should be explicitly noted in spec that the resulting stream is
>>> unordered (or at least may
>>> be unordered) as it's created from the Set.
>>> 
>>> With best regards,
>>> Tagir Valeev
>>> 
>>> On Fri, Aug 26, 2016 at 2:30 AM, Steve Drach <[email protected]> wrote:
>>> 
>>>> Hi,
>>>> 
>>>> Please review this changeset that adds a versionedStream method to JarFile.
>>>> 
>>>> webrev: http://cr.openjdk.java.net/~sdrach/8163798/webrev.00/ <
>>>> http://cr.openjdk.java.net/~sdrach/8163798/webrev.00/>
>>>> issue: https://bugs.openjdk.java.net/browse/JDK-8163798 <
>>>> https://bugs.openjdk.java.net/browse/JDK-8163798>
>>>> 
>>>> Thanks
>>>> Steve
>>>> 
>>>> 
>>>> 
>> 
> 

Reply via email to