43 * Adds aliasing to ZipFileSystem to support multi-release jar files. An alias map 44 * is created by {@link JarFileSystem#createVersionedLinks(int)}. The map is then 45 * consulted when an entry is looked up in {@link JarFileSystem#getInode(byte[])} 46 * to determine if the entry has a corresponding versioned entry. If so, the 47 * versioned entry is returned. 48 * 49 * @author Steve Drach 50 */ 51 class JarFileSystem extends ZipFileSystem { 52 // lookup needs to be initialized because isMultiReleaseJar is called before createVersionedLinks 53 private Function<byte[], byte[]> lookup = path -> path; 54 55 @Override 56 IndexNode getInode(byte[] path) { 57 // check for an alias to a versioned entry 58 return super.getInode(lookup.apply(path)); 59 } 60 61 JarFileSystem(ZipFileSystemProvider provider, Path zfpath, Map<String,?> env) throws IOException { 62 super(provider, zfpath, env); 63 if (isMultiReleaseJar()) { 64 int version; 65 Object o = env.get("multi-release"); 66 if (o instanceof String) { 67 String s = (String)o; 68 if (s.equals("runtime")) { 69 version = Runtime.version().feature(); 70 } else { 71 version = Integer.parseInt(s); 72 } 73 } else if (o instanceof Integer) { 74 version = (Integer)o; 75 } else if (o instanceof Version) { 76 version = ((Version)o).feature(); 77 } else { 78 throw new IllegalArgumentException("env parameter must be String, Integer, " 79 + "or Version"); 80 } 81 createVersionedLinks(version < 0 ? 0 : version); 82 setReadOnly(); 83 } 84 } 85 86 private boolean isMultiReleaseJar() throws IOException { 87 try (InputStream is = newInputStream(getBytes("/META-INF/MANIFEST.MF"))) { 88 String multiRelease = new Manifest(is).getMainAttributes() 89 .getValue(Attributes.Name.MULTI_RELEASE); 90 return "true".equalsIgnoreCase(multiRelease); 91 } catch (NoSuchFileException x) { 92 return false; 93 } 94 } 95 96 /** 97 * create a map of aliases for versioned entries, for example: 98 * version/PackagePrivate.class -> META-INF/versions/9/version/PackagePrivate.class 99 * version/PackagePrivate.java -> META-INF/versions/9/version/PackagePrivate.java 100 * version/Version.class -> META-INF/versions/10/version/Version.class 101 * version/Version.java -> META-INF/versions/10/version/Version.java 102 * 103 * then wrap the map in a function that getEntry can use to override root 104 * entry lookup for entries that have corresponding versioned entries 105 */ | 43 * Adds aliasing to ZipFileSystem to support multi-release jar files. An alias map 44 * is created by {@link JarFileSystem#createVersionedLinks(int)}. The map is then 45 * consulted when an entry is looked up in {@link JarFileSystem#getInode(byte[])} 46 * to determine if the entry has a corresponding versioned entry. If so, the 47 * versioned entry is returned. 48 * 49 * @author Steve Drach 50 */ 51 class JarFileSystem extends ZipFileSystem { 52 // lookup needs to be initialized because isMultiReleaseJar is called before createVersionedLinks 53 private Function<byte[], byte[]> lookup = path -> path; 54 55 @Override 56 IndexNode getInode(byte[] path) { 57 // check for an alias to a versioned entry 58 return super.getInode(lookup.apply(path)); 59 } 60 61 JarFileSystem(ZipFileSystemProvider provider, Path zfpath, Map<String,?> env) throws IOException { 62 super(provider, zfpath, env); 63 Object o = getRuntimeVersion(env); 64 if (isMultiReleaseJar() && (o != null)) { 65 int version; 66 if (o instanceof String) { 67 String s = (String)o; 68 if (s.equals("runtime")) { 69 version = Runtime.version().feature(); 70 } else if (s.matches("^[1-9][0-9]*$")) { 71 version = Version.parse(s).feature(); 72 } else { 73 throw new IllegalArgumentException("Invalid runtime version"); 74 } 75 } else if (o instanceof Integer) { 76 version = Version.parse(((Integer)o).toString()).feature(); 77 } else if (o instanceof Version) { 78 version = ((Version)o).feature(); 79 } else { 80 throw new IllegalArgumentException("env parameter must be String, Integer, " 81 + "or Version"); 82 } 83 createVersionedLinks(version < 0 ? 0 : version); 84 setReadOnly(); 85 } 86 } 87 88 /** 89 * Utility method to get the release version for a multi-release JAR. It 90 * first checks the documented property {@code releaseVersion} and if not 91 * found checks the original property {@code multi-release} 92 * @param env ZIP FS map 93 * @return release version or null if it is not specified 94 */ 95 private Object getRuntimeVersion(Map<String, ?> env) { 96 Object o = null; 97 if (env.containsKey(ZipFileSystemProvider.PROPERTY_RELEASE_VERSION)) { 98 o = env.get(ZipFileSystemProvider.PROPERTY_RELEASE_VERSION); 99 } else { 100 o = env.get(ZipFileSystemProvider.PROPERTY_MULTI_RELEASE); 101 } 102 return o; 103 } 104 105 private boolean isMultiReleaseJar() throws IOException { 106 try (InputStream is = newInputStream(getBytes("/META-INF/MANIFEST.MF"))) { 107 String multiRelease = new Manifest(is).getMainAttributes() 108 .getValue(Attributes.Name.MULTI_RELEASE); 109 return "true".equalsIgnoreCase(multiRelease); 110 } catch (NoSuchFileException x) { 111 return false; 112 } 113 } 114 115 /** 116 * create a map of aliases for versioned entries, for example: 117 * version/PackagePrivate.class -> META-INF/versions/9/version/PackagePrivate.class 118 * version/PackagePrivate.java -> META-INF/versions/9/version/PackagePrivate.java 119 * version/Version.class -> META-INF/versions/10/version/Version.class 120 * version/Version.java -> META-INF/versions/10/version/Version.java 121 * 122 * then wrap the map in a function that getEntry can use to override root 123 * entry lookup for entries that have corresponding versioned entries 124 */ |