11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.tools.jar; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.lang.module.InvalidModuleDescriptorException; 32 import java.lang.module.ModuleDescriptor; 33 import java.lang.module.ModuleDescriptor.Exports; 34 import java.lang.module.InvalidModuleDescriptorException; 35 import java.lang.module.ModuleDescriptor.Opens; 36 import java.lang.module.ModuleDescriptor.Provides; 37 import java.lang.module.ModuleDescriptor.Requires; 38 import java.util.Collections; 39 import java.util.Comparator; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.function.Consumer; 46 import java.util.jar.JarEntry; 47 import java.util.jar.JarFile; 48 import java.util.zip.ZipEntry; 49 50 import static java.util.jar.JarFile.MANIFEST_NAME; 51 import static sun.tools.jar.Main.VERSIONS_DIR; 52 import static sun.tools.jar.Main.VERSIONS_DIR_LENGTH; 53 import static sun.tools.jar.Main.MODULE_INFO; 54 import static sun.tools.jar.Main.getMsg; 55 import static sun.tools.jar.Main.formatMsg; 56 import static sun.tools.jar.Main.formatMsg2; 57 import static sun.tools.jar.Main.toBinaryName; 58 import static sun.tools.jar.Main.isModuleInfoEntry; 59 60 final class Validator { 61 private final static boolean DEBUG = Boolean.getBoolean("jar.debug"); 62 private final Map<String,FingerPrint> fps = new HashMap<>(); 63 private final Main main; 64 private final JarFile jf; 65 private int oldVersion = -1; 66 private String currentTopLevelName; 67 private boolean isValid = true; 68 private Set<String> concealedPkgs = Collections.emptySet(); 69 private ModuleDescriptor md; 70 private String mdName; 71 72 private Validator(Main main, JarFile jf) { 73 this.main = main; 74 this.jf = jf; 75 checkModuleDescriptor(MODULE_INFO); 76 } 77 78 static boolean validate(Main main, JarFile jf) throws IOException { 79 return new Validator(main, jf).validate(); 80 } 81 82 private boolean validate() { 83 try { 84 jf.stream() 85 .filter(e -> !e.isDirectory() && 86 !e.getName().equals(MANIFEST_NAME)) 87 .sorted(ENTRY_COMPARATOR) 88 .forEachOrdered(e -> validate(e)); 89 return isValid; 90 } catch (InvalidJarException e) { 91 error(formatMsg("error.validator.bad.entry.name", e.getMessage())); 92 } 93 return false; 94 } 95 96 private static class InvalidJarException extends RuntimeException { 97 private static final long serialVersionUID = -3642329147299217726L; 98 InvalidJarException(String msg) { 99 super(msg); 100 } 101 } 102 103 // sort base entries before versioned entries, and sort entry classes with 104 // nested classes so that the top level class appears before the associated 105 // nested class 106 static Comparator<String> ENTRYNAME_COMPARATOR = (s1, s2) -> { 107 108 if (s1.equals(s2)) return 0; 109 boolean b1 = s1.startsWith(VERSIONS_DIR); 110 boolean b2 = s2.startsWith(VERSIONS_DIR); 111 if (b1 && !b2) return 1; 112 if (!b1 && b2) return -1; 113 int n = 0; // starting char for String compare 114 if (b1 && b2) { 115 // normally strings would be sorted so "10" goes before "9", but 116 // version number strings need to be sorted numerically 117 n = VERSIONS_DIR.length(); // skip the common prefix 118 int i1 = s1.indexOf('/', n); 119 int i2 = s2.indexOf('/', n); 120 if (i1 == -1) throw new InvalidJarException(s1); 121 if (i2 == -1) throw new InvalidJarException(s2); 122 // shorter version numbers go first 123 if (i1 != i2) return i1 - i2; 124 // otherwise, handle equal length numbers below 125 } 126 int l1 = s1.length(); 127 int l2 = s2.length(); 128 int lim = Math.min(l1, l2); 129 for (int k = n; k < lim; k++) { 130 char c1 = s1.charAt(k); 131 char c2 = s2.charAt(k); 132 if (c1 != c2) { 133 // change natural ordering so '.' comes before '$' 134 // i.e. top level classes come before nested classes 135 if (c1 == '$' && c2 == '.') return 1; 136 if (c1 == '.' && c2 == '$') return -1; 137 return c1 - c2; 138 } 139 } 140 return l1 - l2; 141 }; 142 143 static Comparator<ZipEntry> ENTRY_COMPARATOR = 144 Comparator.comparing(ZipEntry::getName, ENTRYNAME_COMPARATOR); 145 146 /* 147 * Validator has state and assumes entries provided to accept are ordered 148 * from base entries first and then through the versioned entries in 149 * ascending version order. Also, to find isolated nested classes, 150 * classes must be ordered so that the top level class is before the associated 151 * nested class(es). 152 */ 153 public void validate(JarEntry je) { 154 String entryName = je.getName(); 155 156 // directories are always accepted 157 if (entryName.endsWith("/")) { 158 debug("%s is a directory", entryName); 159 return; 160 } 161 162 // validate the versioned module-info 163 if (isModuleInfoEntry(entryName)) { 164 if (!entryName.equals(mdName)) 165 checkModuleDescriptor(entryName); 166 return; 167 } 168 169 // figure out the version and basename from the JarEntry 170 int version; 171 String basename; 172 String versionStr = null;; 173 if (entryName.startsWith(VERSIONS_DIR)) { 174 int n = entryName.indexOf("/", VERSIONS_DIR_LENGTH); 175 if (n == -1) { 176 error(formatMsg("error.validator.version.notnumber", entryName)); 177 isValid = false; 178 return; 179 } 180 versionStr = entryName.substring(VERSIONS_DIR_LENGTH, n); 181 try { 182 version = Integer.parseInt(versionStr); 183 } catch (NumberFormatException x) { 184 error(formatMsg("error.validator.version.notnumber", entryName)); 185 isValid = false; 186 return; 187 } 188 if (n == entryName.length()) { 189 error(formatMsg("error.validator.entryname.tooshort", entryName)); 190 isValid = false; 191 return; 192 } 193 basename = entryName.substring(n + 1); 194 } else { 195 version = 0; 196 basename = entryName; 197 } 198 debug("\n===================\nversion %d %s", version, entryName); 199 200 if (oldVersion != version) { 201 oldVersion = version; 202 currentTopLevelName = null; 203 if (md == null && versionStr != null) { 204 // don't have a base module-info.class yet, try to see if 205 // a versioned one exists 206 checkModuleDescriptor(VERSIONS_DIR + versionStr + "/" + MODULE_INFO); 207 } 208 } 209 210 // analyze the entry, keeping key attributes 211 FingerPrint fp; 212 try (InputStream is = jf.getInputStream(je)) { 213 fp = new FingerPrint(basename, is.readAllBytes()); 214 } catch (IOException x) { 215 error(x.getMessage()); 216 isValid = false; 217 return; 218 } 219 String internalName = fp.name(); 220 221 // process a base entry paying attention to nested classes 222 if (version == 0) { 223 debug("base entry found"); 224 if (fp.isNestedClass()) { 225 debug("nested class found"); 226 if (fp.topLevelName().equals(currentTopLevelName)) { 227 fps.put(internalName, fp); 228 return; 229 } 230 error(formatMsg("error.validator.isolated.nested.class", entryName)); 231 isValid = false; 232 return; 233 } 234 // top level class or resource entry 235 if (fp.isClass()) { 236 currentTopLevelName = fp.topLevelName(); 237 if (!checkInternalName(entryName, basename, internalName)) { 238 isValid = false; 239 return; 240 } 241 } 242 fps.put(internalName, fp); 243 return; 244 } 245 246 // process a versioned entry, look for previous entry with same name 247 FingerPrint matchFp = fps.get(internalName); 248 debug("looking for match"); 249 if (matchFp == null) { 250 debug("no match found"); 251 if (fp.isClass()) { 252 if (fp.isNestedClass()) { 253 if (!checkNestedClass(version, entryName, internalName, fp)) { 254 isValid = false; 255 } 256 return; 257 } 258 if (fp.isPublicClass()) { 259 if (!isConcealed(internalName)) { 260 error(Main.formatMsg("error.validator.new.public.class", entryName)); 261 isValid = false; 262 return; 263 } 264 warn(formatMsg("warn.validator.concealed.public.class", entryName)); 265 debug("%s is a public class entry in a concealed package", entryName); 266 } 267 debug("%s is a non-public class entry", entryName); 268 fps.put(internalName, fp); 269 currentTopLevelName = fp.topLevelName(); 270 return; 271 } 272 debug("%s is a resource entry"); 273 fps.put(internalName, fp); 274 return; 275 } 276 debug("match found"); 277 278 // are the two classes/resources identical? 279 if (fp.isIdentical(matchFp)) { 280 warn(formatMsg("warn.validator.identical.entry", entryName)); 281 return; // it's okay, just takes up room 282 } 283 debug("sha1 not equal -- different bytes"); 284 285 // ok, not identical, check for compatible class version and api 286 if (fp.isClass()) { 287 if (fp.isNestedClass()) { 288 if (!checkNestedClass(version, entryName, internalName, fp)) { 289 isValid = false; 290 } 291 return; 292 } 293 debug("%s is a class entry", entryName); 294 if (!fp.isCompatibleVersion(matchFp)) { 295 error(formatMsg("error.validator.incompatible.class.version", entryName)); 296 isValid = false; 297 return; 298 } 299 if (!fp.isSameAPI(matchFp)) { 300 error(formatMsg("error.validator.different.api", entryName)); 301 isValid = false; 302 return; 303 } 304 if (!checkInternalName(entryName, basename, internalName)) { 305 isValid = false; 306 return; 307 } 308 debug("fingerprints same -- same api"); 309 fps.put(internalName, fp); 310 currentTopLevelName = fp.topLevelName(); 311 return; 312 } 313 debug("%s is a resource", entryName); 314 315 warn(formatMsg("warn.validator.resources.with.same.name", entryName)); 316 fps.put(internalName, fp); 317 return; 318 } 319 320 /** 321 * Checks whether or not the given versioned module descriptor's attributes 322 * are valid when compared against the root/base module descriptor. 323 * 324 * A versioned module descriptor must be identical to the root/base module 325 * descriptor, with two exceptions: 326 * - A versioned descriptor can have different non-public `requires` 327 * clauses of platform ( `java.*` and `jdk.*` ) modules, and 328 * - A versioned descriptor can have different `uses` clauses, even of 329 * service types defined outside of the platform modules. 330 */ 331 private void checkModuleDescriptor(String miName) { 332 ZipEntry je = jf.getEntry(miName); 333 if (je != null) { 334 try (InputStream jis = jf.getInputStream(je)) { 335 ModuleDescriptor md = ModuleDescriptor.read(jis); 336 // Initialize the base md if it's not yet. A "base" md can be either the 337 // root module-info.class or the first versioned module-info.class 338 ModuleDescriptor base = this.md; 339 340 if (base == null) { 341 concealedPkgs = new HashSet<>(md.packages()); 342 md.exports().stream().map(Exports::source).forEach(concealedPkgs::remove); 343 md.opens().stream().map(Opens::source).forEach(concealedPkgs::remove); 344 // must have the implementation class of the services it 'provides'. 345 if (md.provides().stream().map(Provides::providers) 346 .flatMap(List::stream) 347 .filter(p -> jf.getEntry(toBinaryName(p)) == null) 348 .peek(p -> error(formatMsg("error.missing.provider", p))) 349 .count() != 0) { 350 isValid = false; 351 return; 352 } 353 this.md = md; 354 this.mdName = miName; 355 return; 356 } 357 358 if (!base.name().equals(md.name())) { 359 error(getMsg("error.validator.info.name.notequal")); 360 isValid = false; 361 } 362 if (!base.requires().equals(md.requires())) { 363 Set<Requires> baseRequires = base.requires(); 364 for (Requires r : md.requires()) { 365 if (baseRequires.contains(r)) 366 continue; 367 if (r.modifiers().contains(Requires.Modifier.TRANSITIVE)) { 368 error(getMsg("error.validator.info.requires.transitive")); 369 isValid = false; 370 } else if (!isPlatformModule(r.name())) { 371 error(getMsg("error.validator.info.requires.added")); 372 isValid = false; 373 } 374 } 375 for (Requires r : baseRequires) { 376 Set<Requires> mdRequires = md.requires(); 377 if (mdRequires.contains(r)) 378 continue; 379 if (!isPlatformModule(r.name())) { 380 error(getMsg("error.validator.info.requires.dropped")); 381 isValid = false; 382 } 383 } 384 } 385 if (!base.exports().equals(md.exports())) { 386 error(getMsg("error.validator.info.exports.notequal")); 387 isValid = false; 388 } 389 if (!base.opens().equals(md.opens())) { 390 error(getMsg("error.validator.info.opens.notequal")); 391 isValid = false; 392 } 393 if (!base.provides().equals(md.provides())) { 394 error(getMsg("error.validator.info.provides.notequal")); 395 isValid = false; 396 } 397 if (!base.mainClass().equals(md.mainClass())) { 398 error(formatMsg("error.validator.info.manclass.notequal", je.getName())); 399 isValid = false; 400 } 401 if (!base.version().equals(md.version())) { 402 error(formatMsg("error.validator.info.version.notequal", je.getName())); 403 isValid = false; 404 } 405 } catch (Exception x) { 406 error(x.getMessage() + " : " + miName); 407 this.isValid = false; 408 } 409 } 410 } 411 412 private static boolean isPlatformModule(String name) { 413 return name.startsWith("java.") || name.startsWith("jdk."); 414 } 415 416 private boolean checkInternalName(String entryName, String basename, String internalName) { 417 String className = className(basename); 418 if (internalName.equals(className)) { 419 return true; 420 } 421 error(formatMsg2("error.validator.names.mismatch", 422 entryName, internalName.replace("/", "."))); 423 return false; 424 } 425 426 private boolean checkNestedClass(int version, String entryName, String internalName, FingerPrint fp) { 427 debug("%s is a nested class entry in top level class %s", entryName, fp.topLevelName()); 428 if (fp.topLevelName().equals(currentTopLevelName)) { 429 debug("%s (top level class) was accepted", fp.topLevelName()); 430 fps.put(internalName, fp); 431 return true; 432 } 433 debug("top level class was not accepted"); 434 error(formatMsg("error.validator.isolated.nested.class", entryName)); 435 return false; 436 } 437 438 private String className(String entryName) { 439 return entryName.endsWith(".class") ? entryName.substring(0, entryName.length() - 6) : null; 440 } 441 442 private boolean isConcealed(String internalName) { 443 if (concealedPkgs.isEmpty()) { 444 return false; 445 } 446 int idx = internalName.lastIndexOf('/'); 447 String pkgName = idx != -1 ? internalName.substring(0, idx).replace('/', '.') : ""; 448 return concealedPkgs.contains(pkgName); 449 } 450 451 private void debug(String fmt, Object... args) { 452 if (DEBUG) System.err.format(fmt, args); 453 } 454 455 private void error(String msg) { 456 main.error(msg); 457 } 458 459 private void warn(String msg) { 460 main.warn(msg); 461 } 462 463 } | 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.tools.jar; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.lang.module.ModuleDescriptor; 32 import java.lang.module.ModuleDescriptor.Exports; 33 import java.lang.module.ModuleDescriptor.Opens; 34 import java.lang.module.ModuleDescriptor.Provides; 35 import java.lang.module.ModuleDescriptor.Requires; 36 import java.util.Collections; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Set; 42 import java.util.TreeMap; 43 import java.util.function.Function; 44 import java.util.stream.Collectors; 45 import java.util.zip.ZipEntry; 46 import java.util.zip.ZipFile; 47 48 import static java.util.jar.JarFile.MANIFEST_NAME; 49 import static sun.tools.jar.Main.VERSIONS_DIR; 50 import static sun.tools.jar.Main.VERSIONS_DIR_LENGTH; 51 import static sun.tools.jar.Main.MODULE_INFO; 52 import static sun.tools.jar.Main.getMsg; 53 import static sun.tools.jar.Main.formatMsg; 54 import static sun.tools.jar.Main.formatMsg2; 55 import static sun.tools.jar.Main.toBinaryName; 56 57 final class Validator { 58 59 private final Map<String,FingerPrint> classes = new HashMap<>(); 60 private final Main main; 61 private final ZipFile zf; 62 private boolean isValid = true; 63 private Set<String> concealedPkgs = Collections.emptySet(); 64 private ModuleDescriptor md; 65 private String mdName; 66 67 private Validator(Main main, ZipFile zf) { 68 this.main = main; 69 this.zf = zf; 70 checkModuleDescriptor(MODULE_INFO); 71 } 72 73 static boolean validate(Main main, ZipFile zf) throws IOException { 74 return new Validator(main, zf).validate(); 75 } 76 77 private boolean validate() { 78 try { 79 zf.stream() 80 .filter(e -> e.getName().endsWith(".class")) 81 .map(this::getFingerPrint) 82 .filter(FingerPrint::isClass) // skip any non-class entry 83 .collect(Collectors.groupingBy( 84 FingerPrint::mrversion, 85 TreeMap::new, 86 Collectors.toMap(FingerPrint::className, 87 Function.identity(), 88 this::sameNameFingerPrint))) 89 .forEach((version, entries) -> { 90 if (version == 0) 91 validateBase(entries); 92 else 93 validateVersioned(entries); 94 }); 95 } catch (InvalidJarException e) { 96 errorAndInvalid(e.getMessage()); 97 } 98 return isValid; 99 } 100 101 static class InvalidJarException extends RuntimeException { 102 private static final long serialVersionUID = -3642329147299217726L; 103 InvalidJarException(String msg) { 104 super(msg); 105 } 106 } 107 108 private FingerPrint sameNameFingerPrint(FingerPrint fp1, FingerPrint fp2) { 109 checkClassName(fp1); 110 checkClassName(fp2); 111 // entries/classes with same name, return fp2 for now ? 112 return fp2; 113 } 114 115 private FingerPrint getFingerPrint(ZipEntry ze) { 116 // figure out the version and basename from the ZipEntry 117 String ename = ze.getName(); 118 String bname = ename; 119 int version = 0; 120 121 if (ename.startsWith(VERSIONS_DIR)) { 122 int n = ename.indexOf("/", VERSIONS_DIR_LENGTH); 123 if (n == -1) { 124 throw new InvalidJarException( 125 formatMsg("error.validator.version.notnumber", ename)); 126 } 127 try { 128 version = Integer.parseInt(ename, VERSIONS_DIR_LENGTH, n, 10); 129 } catch (NumberFormatException x) { 130 throw new InvalidJarException( 131 formatMsg("error.validator.version.notnumber", ename)); 132 } 133 if (n == ename.length()) { 134 throw new InvalidJarException( 135 formatMsg("error.validator.entryname.tooshort", ename)); 136 } 137 bname = ename.substring(n + 1); 138 } 139 140 // return the cooresponding fingerprint entry 141 try (InputStream is = zf.getInputStream(ze)) { 142 return new FingerPrint(bname, ename, version, is.readAllBytes()); 143 } catch (IOException x) { 144 throw new InvalidJarException(x.getMessage()); 145 } 146 } 147 148 /* 149 * Validates (a) if there is any isolated nested class, and (b) if the 150 * class name in class file (by asm) matches the entry's basename. 151 */ 152 public void validateBase(Map<String, FingerPrint> fps) { 153 fps.values().forEach( fp -> { 154 if (!checkClassName(fp)) { 155 isValid = false; 156 return; 157 } 158 if (fp.isNestedClass()) { 159 if (!checkNestedClass(fp, fps)) { 160 isValid = false; 161 } 162 } 163 classes.put(fp.className(), fp); 164 }); 165 } 166 167 public void validateVersioned(Map<String, FingerPrint> fps) { 168 169 fps.values().forEach( fp -> { 170 171 // validate the versioned module-info 172 if (MODULE_INFO.equals(fp.basename())) { 173 checkModuleDescriptor(fp.entryName()); 174 return; 175 } 176 // process a versioned entry, look for previous entry with same name 177 FingerPrint matchFp = classes.get(fp.className()); 178 if (matchFp == null) { 179 // no match found 180 if (fp.isNestedClass()) { 181 if (!checkNestedClass(fp, fps)) { 182 isValid = false; 183 } 184 return; 185 } 186 if (fp.isPublicClass()) { 187 if (!isConcealed(fp.className())) { 188 errorAndInvalid(formatMsg("error.validator.new.public.class", 189 fp.entryName())); 190 return; 191 } 192 // entry is a public class entry in a concealed package 193 warn(formatMsg("warn.validator.concealed.public.class", 194 fp.entryName())); 195 } 196 classes.put(fp.className(), fp); 197 return; 198 } 199 200 // are the two classes/resources identical? 201 if (fp.isIdentical(matchFp)) { 202 warn(formatMsg("warn.validator.identical.entry", fp.entryName())); 203 return; // it's okay, just takes up room 204 } 205 206 // ok, not identical, check for compatible class version and api 207 if (fp.isNestedClass()) { 208 if (!checkNestedClass(fp, fps)) { 209 isValid = false; 210 } 211 return; // fall through, need check nested public class?? 212 } 213 if (!fp.isCompatibleVersion(matchFp)) { 214 errorAndInvalid(formatMsg("error.validator.incompatible.class.version", 215 fp.entryName())); 216 return; 217 } 218 if (!fp.isSameAPI(matchFp)) { 219 errorAndInvalid(formatMsg("error.validator.different.api", 220 fp.entryName())); 221 return; 222 } 223 if (!checkClassName(fp)) { 224 isValid = false; 225 return; 226 } 227 classes.put(fp.className(), fp); 228 229 return; 230 }); 231 } 232 233 /* 234 * Checks whether or not the given versioned module descriptor's attributes 235 * are valid when compared against the root/base module descriptor. 236 * 237 * A versioned module descriptor must be identical to the root/base module 238 * descriptor, with two exceptions: 239 * - A versioned descriptor can have different non-public `requires` 240 * clauses of platform ( `java.*` and `jdk.*` ) modules, and 241 * - A versioned descriptor can have different `uses` clauses, even of 242 * service types defined outside of the platform modules. 243 */ 244 private void checkModuleDescriptor(String miName) { 245 ZipEntry ze = zf.getEntry(miName); 246 if (ze != null) { 247 try (InputStream jis = zf.getInputStream(ze)) { 248 ModuleDescriptor md = ModuleDescriptor.read(jis); 249 // Initialize the base md if it's not yet. A "base" md can be either the 250 // root module-info.class or the first versioned module-info.class 251 ModuleDescriptor base = this.md; 252 253 if (base == null) { 254 concealedPkgs = new HashSet<>(md.packages()); 255 md.exports().stream().map(Exports::source).forEach(concealedPkgs::remove); 256 md.opens().stream().map(Opens::source).forEach(concealedPkgs::remove); 257 // must have the implementation class of the services it 'provides'. 258 if (md.provides().stream().map(Provides::providers) 259 .flatMap(List::stream) 260 .filter(p -> zf.getEntry(toBinaryName(p)) == null) 261 .peek(p -> error(formatMsg("error.missing.provider", p))) 262 .count() != 0) { 263 isValid = false; 264 return; 265 } 266 this.md = md; 267 this.mdName = miName; 268 return; 269 } 270 271 if (!base.name().equals(md.name())) { 272 errorAndInvalid(getMsg("error.validator.info.name.notequal")); 273 } 274 if (!base.requires().equals(md.requires())) { 275 Set<Requires> baseRequires = base.requires(); 276 for (Requires r : md.requires()) { 277 if (baseRequires.contains(r)) 278 continue; 279 if (r.modifiers().contains(Requires.Modifier.TRANSITIVE)) { 280 errorAndInvalid(getMsg("error.validator.info.requires.transitive")); 281 } else if (!isPlatformModule(r.name())) { 282 errorAndInvalid(getMsg("error.validator.info.requires.added")); 283 } 284 } 285 for (Requires r : baseRequires) { 286 Set<Requires> mdRequires = md.requires(); 287 if (mdRequires.contains(r)) 288 continue; 289 if (!isPlatformModule(r.name())) { 290 errorAndInvalid(getMsg("error.validator.info.requires.dropped")); 291 } 292 } 293 } 294 if (!base.exports().equals(md.exports())) { 295 errorAndInvalid(getMsg("error.validator.info.exports.notequal")); 296 } 297 if (!base.opens().equals(md.opens())) { 298 errorAndInvalid(getMsg("error.validator.info.opens.notequal")); 299 } 300 if (!base.provides().equals(md.provides())) { 301 errorAndInvalid(getMsg("error.validator.info.provides.notequal")); 302 } 303 if (!base.mainClass().equals(md.mainClass())) { 304 errorAndInvalid(formatMsg("error.validator.info.manclass.notequal", 305 ze.getName())); 306 } 307 if (!base.version().equals(md.version())) { 308 errorAndInvalid(formatMsg("error.validator.info.version.notequal", 309 ze.getName())); 310 } 311 } catch (Exception x) { 312 errorAndInvalid(x.getMessage() + " : " + miName); 313 } 314 } 315 } 316 317 private boolean checkClassName(FingerPrint fp) { 318 if (fp.className().equals(className(fp.basename()))) { 319 return true; 320 } 321 error(formatMsg2("error.validator.names.mismatch", 322 fp.entryName(), fp.className().replace("/", "."))); 323 return false; 324 } 325 326 private boolean checkNestedClass(FingerPrint fp, Map<String, FingerPrint> outerClasses) { 327 if (outerClasses.containsKey(fp.outerClassName())) { 328 return true; 329 } 330 // outer class was not available 331 error(formatMsg("error.validator.isolated.nested.class", fp.entryName())); 332 return false; 333 } 334 335 private boolean isConcealed(String className) { 336 if (concealedPkgs.isEmpty()) { 337 return false; 338 } 339 int idx = className.lastIndexOf('/'); 340 String pkgName = idx != -1 ? className.substring(0, idx).replace('/', '.') : ""; 341 return concealedPkgs.contains(pkgName); 342 } 343 344 private static boolean isPlatformModule(String name) { 345 return name.startsWith("java.") || name.startsWith("jdk."); 346 } 347 348 private static String className(String entryName) { 349 return entryName.endsWith(".class") ? entryName.substring(0, entryName.length() - 6) : null; 350 } 351 352 private void error(String msg) { 353 main.error(msg); 354 } 355 356 private void errorAndInvalid(String msg) { 357 main.error(msg); 358 isValid = false; 359 } 360 361 private void warn(String msg) { 362 main.warn(msg); 363 } 364 } |