001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.xbean.finder.archive; 018 019import static java.util.Arrays.asList; 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.HashMap; 024import java.util.Map; 025import java.util.jar.Attributes; 026import java.util.jar.Manifest; 027 028// helper to share the multijar release logic in a single place and avoid to impl it in all archives 029public class MJarSupport { 030 private static final boolean SUPPORT_MJAR = asList("true", "force") 031 .contains(System.getProperty("jdk.util.jar.enableMultiRelease", "true")); 032 private static final int MJAR_VERSION = findMJarVersion(); 033 034 private static int findMJarVersion() { 035 if (!SUPPORT_MJAR) { 036 return -1; 037 } 038 final int version = major(System.getProperty("java.version")); 039 final int jarVersion = major(System.getProperty("jdk.util.jar.version")); 040 if (jarVersion > 0) { 041 return Math.min(version, jarVersion); 042 } 043 return Math.min(7/*unexpected but just in case*/, version); 044 } 045 046 private static int major(final String version) { 047 if (version == null) { 048 return -1; 049 } 050 final String[] parts = version.split("\\."); 051 try { 052 final int i = Integer.parseInt(parts[0]); 053 if (i == 1 && parts.length > 1) { 054 return Integer.parseInt(parts[1]); 055 } 056 return i; 057 } catch (final NumberFormatException nfe) { 058 // unexpected 059 return -1; 060 } 061 } 062 063 private boolean mjar; 064 private final Map<String, Clazz> classes = new HashMap<String, Clazz>(); 065 066 public boolean isMjar() { 067 return mjar; 068 } 069 070 public Map<String, Clazz> getClasses() { 071 return classes; 072 } 073 074 public void load(final InputStream is) throws IOException { 075 if (!SUPPORT_MJAR) { 076 return; 077 } 078 load(new Manifest(is)); 079 } 080 081 public void load(final Manifest manifest) { 082 if (!SUPPORT_MJAR) { 083 return; 084 } 085 final Attributes mainAttributes = manifest.getMainAttributes(); 086 if (mainAttributes != null) { 087 mjar = Boolean.parseBoolean(mainAttributes.getValue("Multi-Release")); 088 } 089 } 090 091 // for exploded dirs since jars are handled by the JVM 092 public void visit(final String name) { 093 String normalized = name.replace('/', '.'); 094 if (normalized.startsWith("/")) { 095 normalized = normalized.substring(1); 096 } 097 if (normalized.startsWith("META-INF.versions.")) { 098 final String version = normalized.substring("META-INF.versions.".length()); 099 final int nextSep = version.indexOf('.'); 100 if (nextSep < 0) { 101 return; 102 } 103 final String vStr = version.substring(0, nextSep); 104 final int major; 105 try { 106 if ((major = Integer.parseInt(vStr)) > MJAR_VERSION) { 107 return; 108 } 109 } catch (final NumberFormatException nfe) { 110 return; 111 } 112 if (nextSep < version.length()) { 113 final String cname = version.substring(nextSep + 1); 114 final Clazz existing = classes.get(cname); 115 if (existing == null || existing.version < major) { 116 classes.put(cname, new Clazz(name + (!version.endsWith(".class") ? ".class" : ""), major)); 117 } 118 } 119 } 120 } 121 122 public static class Clazz { 123 private final String path; 124 private final int version; 125 126 private Clazz(final String path, final int version) { 127 this.path = path; 128 this.version = version; 129 } 130 131 public String getPath() { 132 return path; 133 } 134 } 135}