diff --git a/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java b/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java index 6807928b2c25a..417eb89264d51 100644 --- a/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java +++ b/src/java.base/share/classes/jdk/internal/jimage/BasicImageReader.java @@ -24,9 +24,7 @@ */ package jdk.internal.jimage; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; @@ -39,6 +37,8 @@ import java.security.PrivilegedAction; import java.util.Objects; import java.util.stream.IntStream; + +import jdk.internal.jimage.BasicImageReader.ImageError.Reason; import jdk.internal.jimage.decompressor.Decompressor; /** @@ -144,10 +144,12 @@ public Void run() { if (channel.read(headerBuffer, 0L) == headerSize) { headerBuffer.rewind(); } else { - throw new IOException("\"" + name + "\" is not an image file"); + throw new ImageError(Reason.INVALID_JIMAGE, + "\"" + name + "\" is not an image file"); } } else if (headerBuffer.capacity() < headerSize) { - throw new IOException("\"" + name + "\" is not an image file"); + throw new ImageError(Reason.INVALID_JIMAGE, + "\"" + name + "\" is not an image file"); } // Interpret the image file header @@ -164,7 +166,8 @@ public Void run() { // Interpret the image index if (memoryMap.capacity() < indexSize) { - throw new IOException("The image file \"" + name + "\" is corrupted"); + throw new ImageError(Reason.CORRUPT_JIMAGE, + "The image file \"" + name + "\" is corrupted"); } redirect = intBuffer(memoryMap, header.getRedirectOffset(), header.getRedirectSize()); offsets = intBuffer(memoryMap, header.getOffsetsOffset(), header.getOffsetsSize()); @@ -191,14 +194,16 @@ private ImageHeader readHeader(IntBuffer buffer) throws IOException { ImageHeader result = ImageHeader.readFrom(buffer); if (result.getMagic() != ImageHeader.MAGIC) { - throw new IOException("\"" + name + "\" is not an image file"); + throw new ImageError(Reason.INVALID_JIMAGE, + "\"" + name + "\" is not an image file"); } if (result.getMajorVersion() != ImageHeader.MAJOR_VERSION || - result.getMinorVersion() != ImageHeader.MINOR_VERSION) { - throw new IOException("The image file \"" + name + "\" is not " + - "the correct version. Major: " + result.getMajorVersion() + - ". Minor: " + result.getMinorVersion()); + result.getMinorVersion() != ImageHeader.MINOR_VERSION) { + throw new ImageError(Reason.BAD_VERSION, + "The image file \"" + name + "\" is not the correct version.\n" + + "Major: " + result.getMajorVersion() + + ". Minor: " + result.getMinorVersion()); } return result; @@ -457,10 +462,31 @@ public ByteBuffer getResourceBuffer(ImageLocation loc) { return null; } - public InputStream getResourceStream(ImageLocation loc) { - Objects.requireNonNull(loc); - byte[] bytes = getResource(loc); + /** + * Specialized {@link IOException} thrown during construction to provide a + * semantic reason for failure and allow better user-facing error messages. + */ + public final static class ImageError extends IOException { + private static final long serialVersionUID = 6002259582237888214L; + + public enum Reason { + /** The file being opened does not appear to be a jimage file. */ + INVALID_JIMAGE, + /** The jimage file being opened is corrupted. */ + CORRUPT_JIMAGE, + /** The jimage file being opened has the wrong version. */ + BAD_VERSION, + } + + private final Reason reason; - return new ByteArrayInputStream(bytes); + public ImageError(Reason reason, String message) { + super(message); + this.reason = reason; + } + + public Reason getReason() { + return reason; + } } } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java b/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java index df2aca02d6888..3cd2cb0677f41 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java +++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java @@ -30,6 +30,7 @@ import java.io.PrintWriter; import java.nio.file.FileSystem; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.PathMatcher; import java.util.ArrayList; import java.util.Arrays; @@ -44,6 +45,7 @@ import java.lang.classfile.MethodModel; import jdk.internal.jimage.BasicImageReader; +import jdk.internal.jimage.BasicImageReader.ImageError.Reason; import jdk.internal.jimage.ImageHeader; import jdk.internal.jimage.ImageLocation; import jdk.tools.jlink.internal.ImageResourcesTree; @@ -435,6 +437,12 @@ private void iterate(JImageAction jimageAction, } } } catch (IOException ioe) { + // Handle specific errors for which better advice can be given. + if (ioe instanceof BasicImageReader.ImageError err + && err.getReason() == Reason.BAD_VERSION) { + throw TASK_HELPER.newBadArgs("err.bad.version", file); + } + // Non-specific error during processing. throw TASK_HELPER.newBadArgs("err.invalid.jimage", file, ioe.getMessage()); } } diff --git a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage.properties b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage.properties index 3042823130ce2..243de6dc2040c 100644 --- a/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage.properties +++ b/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage.properties @@ -31,20 +31,20 @@ main.usage=\ Usage: {0} jimage...\n\ \n\ \ extract - Extract all jimage entries and place in a directory specified\n\ -\ by the --dir= (default='.') option.\n\ +\ by the --dir= (default=''.'') option.\n\ \n\ \ info - Prints detailed information contained in the jimage header.\n\ \n\ \ list - Prints the names of all the entries in the jimage. When used with\n\ \ --verbose, list will also print entry size and offset attributes.\n\ \n\ -\ verify - Reports on any .class entries that don't verify as classes.\n\ +\ verify - Reports on any .class entries that don''t verify as classes.\n\ \n\ Possible options include: main.usage.extract=\ \ extract - Extract all jimage entries and place in a directory specified\n\ -\ by the --dir= (default='.') option. +\ by the --dir= (default=''.'') option. main.usage.info=\ \ info - Prints detailed information contained in the jimage header. @@ -54,7 +54,7 @@ main.usage.list=\ \ --verbose, list will also print entry size and offset attributes. main.usage.verify=\ -\ verify - Reports errors on any .class entries that don't verify as classes. +\ verify - Reports errors on any .class entries that don''t verify as classes. error.prefix=Error: warn.prefix=Warning: @@ -101,3 +101,5 @@ err.no.jimage=no jimage provided err.option.unsupported={0} not supported: {1} err.unknown.option=unknown option: {0} err.cannot.create.dir=cannot create directory {0} +err.bad.version=Unable to open {0}: mismatched file and tool version\n\ +Use ''/bin/jimage'' for the JDK associated with this jimage file.