Issue #1797 Multi Release Jar javadoc
This commit is contained in:
parent
5de895af88
commit
6b7bbbe66a
|
@ -20,44 +20,132 @@ package org.eclipse.jetty.util;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* <p>Utility class to create a stream of Multi Release {@link JarEntry}s</p>
|
||||
* <p>This is the java 8 version of this class.
|
||||
* A java 9 version of this class is included as a Multi Release class in the
|
||||
* jetty-util jar, that uses java 9 APIs to correctly handle Multi Release jars.</p>
|
||||
* <p>Utility class to handle a Multi Release Jar file</p>
|
||||
*/
|
||||
public class MultiReleaseJarFile
|
||||
{
|
||||
private static final String META_INF_VERSIONS = "META-INF/versions/";
|
||||
|
||||
public static JarFile open(File file) throws IOException
|
||||
private final JarFile jarFile;
|
||||
private final int majorVersion;
|
||||
private final boolean multiRelease;
|
||||
|
||||
/* Map to hold unversioned name to VersionedJarEntry */
|
||||
private final Map<String,VersionedJarEntry> entries;
|
||||
|
||||
/**
|
||||
* Construct a multi release jar file for the current JVM version, ignoring directories.
|
||||
* @param file The file to open
|
||||
*/
|
||||
public MultiReleaseJarFile(File file) throws IOException
|
||||
{
|
||||
return new JarFile(file);
|
||||
this(file,JavaVersion.VERSION.getMajor(),false);
|
||||
}
|
||||
|
||||
public static Stream<VersionedJarEntry> streamVersioned(JarFile jf)
|
||||
/**
|
||||
* Construct a multi release jar file
|
||||
* @param file The file to open
|
||||
* @param majorVersion The major JVM version to apply when selecting a version.
|
||||
* @param includeDirectories true if any directory entries should not be ignored
|
||||
* @throws IOException if the jar file cannot be read
|
||||
*/
|
||||
public MultiReleaseJarFile(File file, int majorVersion, boolean includeDirectories) throws IOException
|
||||
{
|
||||
return jf.stream()
|
||||
.map(VersionedJarEntry::new);
|
||||
if (file==null || !file.exists() || !file.canRead() || file.isDirectory())
|
||||
throw new IllegalArgumentException("bad jar file: "+file);
|
||||
|
||||
jarFile = new JarFile(file,true,JarFile.OPEN_READ);
|
||||
this.majorVersion = majorVersion;
|
||||
|
||||
Manifest manifest = jarFile.getManifest();
|
||||
if (manifest==null)
|
||||
multiRelease = false;
|
||||
else
|
||||
multiRelease = Boolean.valueOf(String.valueOf(manifest.getMainAttributes().getValue("Multi-Release")));
|
||||
|
||||
Map<String,VersionedJarEntry> map = new TreeMap<>();
|
||||
jarFile.stream()
|
||||
.map(VersionedJarEntry::new)
|
||||
.filter(e->(includeDirectories||!e.isDirectory()) && e.isApplicable())
|
||||
.forEach(e->map.compute(e.name, (k, v) -> v==null || v.isReplacedBy(e) ? e : v));
|
||||
|
||||
for (Iterator<Map.Entry<String,VersionedJarEntry>> i = map.entrySet().iterator();i.hasNext();)
|
||||
{
|
||||
Map.Entry<String,VersionedJarEntry> e = i.next();
|
||||
VersionedJarEntry entry = e.getValue();
|
||||
|
||||
if (entry.inner)
|
||||
{
|
||||
VersionedJarEntry outer = map.get(entry.outer);
|
||||
|
||||
if (entry.outer==null || outer.version!= entry.version)
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
|
||||
entries = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
public static Stream<JarEntry> stream(JarFile jf)
|
||||
/**
|
||||
* @return true IFF the jar is a multi release jar
|
||||
*/
|
||||
public boolean isMultiRelease()
|
||||
{
|
||||
// Java 8 version of this class, ignores all versioned entries.
|
||||
return streamVersioned(jf)
|
||||
.filter(e->!e.isVersioned())
|
||||
.map(e->e.resolve(jf));
|
||||
return multiRelease;
|
||||
}
|
||||
|
||||
public static class VersionedJarEntry
|
||||
/**
|
||||
* @return The major version applied to this jar for the purposes of selecting entries
|
||||
*/
|
||||
public int getVersion()
|
||||
{
|
||||
return majorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A stream of versioned entries from the jar, excluded any that are not applicable
|
||||
*/
|
||||
public Stream<VersionedJarEntry> stream()
|
||||
{
|
||||
return entries.values().stream();
|
||||
}
|
||||
|
||||
/** Get a versioned resource entry by name
|
||||
* @param name The unversioned name of the resource
|
||||
* @return The versioned entry of the resource
|
||||
*/
|
||||
public VersionedJarEntry getEntry(String name)
|
||||
{
|
||||
return entries.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[%b,%d]",jarFile.getName(),isMultiRelease(),getVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* A versioned Jar entry
|
||||
*/
|
||||
public class VersionedJarEntry
|
||||
{
|
||||
final JarEntry entry;
|
||||
final String name;
|
||||
final int version;
|
||||
final boolean inner;
|
||||
final String outer;
|
||||
|
||||
VersionedJarEntry(JarEntry entry)
|
||||
{
|
||||
|
@ -65,17 +153,18 @@ public class MultiReleaseJarFile
|
|||
String name = entry.getName();
|
||||
if (name.startsWith(META_INF_VERSIONS))
|
||||
{
|
||||
v=-1;
|
||||
v = -1;
|
||||
int index = name.indexOf('/', META_INF_VERSIONS.length());
|
||||
if (index >= 0 && index < name.length())
|
||||
if (index > META_INF_VERSIONS.length() && index < name.length())
|
||||
{
|
||||
try
|
||||
{
|
||||
v = Integer.parseInt(name.substring(META_INF_VERSIONS.length(), index), 10);
|
||||
v = TypeUtil.parseInt(name, META_INF_VERSIONS.length(), index - META_INF_VERSIONS.length(), 10);
|
||||
name = name.substring(index + 1);
|
||||
}
|
||||
catch (NumberFormatException x)
|
||||
{
|
||||
throw new RuntimeException("illegal version in "+jarFile,x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,27 +172,79 @@ public class MultiReleaseJarFile
|
|||
this.entry = entry;
|
||||
this.name = name;
|
||||
this.version = v;
|
||||
this.inner = name.contains("$") && name.toLowerCase().endsWith(".class");
|
||||
this.outer = inner ? name.substring(0, name.indexOf('$')) + name.substring(name.length() - 6, name.length()) : null;
|
||||
}
|
||||
|
||||
public int version()
|
||||
/**
|
||||
* @return the unversioned name of the resource
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The name of the resource within the jar, which could be versioned
|
||||
*/
|
||||
public String getNameInJar()
|
||||
{
|
||||
return entry.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The version of the resource or 0 for a base version
|
||||
*/
|
||||
public int getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return True iff the entry is not from the base version
|
||||
*/
|
||||
public boolean isVersioned()
|
||||
{
|
||||
return version > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return True iff the entry is a directory
|
||||
*/
|
||||
public boolean isDirectory()
|
||||
{
|
||||
return entry.isDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An input stream of the content of the versioned entry.
|
||||
* @throws IOException if something goes wrong!
|
||||
*/
|
||||
public InputStream getInputStream() throws IOException
|
||||
{
|
||||
return jarFile.getInputStream(entry);
|
||||
}
|
||||
|
||||
boolean isApplicable()
|
||||
{
|
||||
if (multiRelease)
|
||||
return this.version>=0 && this.version <= majorVersion && name.length()>0;
|
||||
return this.version==0;
|
||||
}
|
||||
|
||||
boolean isReplacedBy(VersionedJarEntry entry)
|
||||
{
|
||||
if (isDirectory())
|
||||
return entry.version==0;
|
||||
return this.name.equals(entry.name) && entry.version>version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return entry.toString() + (version==0?"[base]":("["+version+"]"));
|
||||
}
|
||||
|
||||
public JarEntry resolve(JarFile jf)
|
||||
{
|
||||
return entry;
|
||||
return String.format("%s->%s[%d]",name,entry.getName(),version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue