diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index 88f466077d..6848d25870 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -89,6 +89,9 @@ import javax.annotation.Nullable; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FileUtils; + /** * Various utility methods that don't have more proper home. * @@ -181,43 +184,54 @@ public class Util { } /** - * Loads the contents of a file into a string. + * Reads the entire contents of the text file at logfile into a + * string using the {@link Charset#defaultCharset() default charset} for + * decoding. If no such file exists, an empty string is returned. + * @param logfile The text file to read in its entirety. + * @return The entire text content of logfile. + * @throws IOException If an error occurs while reading the file. + * @deprecated call {@link #loadFile(java.io.File, java.nio.charset.Charset)} + * instead to specify the charset to use for decoding (preferably + * {@link java.nio.charset.StandardCharsets#UTF_8}). */ @Nonnull + @Deprecated public static String loadFile(@Nonnull File logfile) throws IOException { return loadFile(logfile, Charset.defaultCharset()); } + /** + * Reads the entire contents of the text file at logfile into a + * string using charset for decoding. If no such file exists, + * an empty string is returned. + * @param logfile The text file to read in its entirety. + * @param charset The charset to use for decoding the bytes in logfile. + * @return The entire text content of logfile. + * @throws IOException If an error occurs while reading the file. + */ @Nonnull public static String loadFile(@Nonnull File logfile, @Nonnull Charset charset) throws IOException { - if(!logfile.exists()) - return ""; - - StringBuilder str = new StringBuilder((int)logfile.length()); - - // We're not using Files.newBufferedReader() here because there is a - // difference in how an InputStreamReader constructed from a Charset and - // the reader returned by Files.newBufferedReader() handle malformed and - // unmappable byte sequences for the specified encoding; the latter is - // more picky and will throw a CharacterCodingException. See: - // https://issues.jenkins-ci.org/browse/JENKINS-49060?focusedCommentId=325989&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-325989 + // Note: Until charset handling is resolved (e.g. by implementing + // https://issues.jenkins-ci.org/browse/JENKINS-48923 ), this method + // must be able to handle character encoding errors. As reported at + // https://issues.jenkins-ci.org/browse/JENKINS-49112 Run.getLog() calls + // loadFile() to fully read the generated log file. This file might + // contain unmappable and/or malformed byte sequences. We need to make + // sure that in such cases, no CharacterCodingException is thrown. // - // As reported at https://issues.jenkins-ci.org/browse/JENKINS-49112 - // Run.getLog() calls loadFile() to fully read the generated log file. - // Until charset handling is resolved (e.g. by implementing - // https://issues.jenkins-ci.org/browse/JENKINS-48923 ), malformed - // bytes will need to be tolerated. - try (InputStream rawIn = Files.newInputStream(fileToPath(logfile)); - Reader r = new BufferedReader(new InputStreamReader(rawIn, charset))) { - char[] buf = new char[1024]; - int len; - while ((len = r.read(buf, 0, buf.length)) > 0) - str.append(buf, 0, len); + // One approach that cannot be used is to call Files.newBufferedReader() + // because there is a difference in how an InputStreamReader constructed + // from a Charset and the reader returned by Files.newBufferedReader() + // handle malformed and unmappable byte sequences for the specified + // encoding; the latter is more picky and will throw an exception. + // See: https://issues.jenkins-ci.org/browse/JENKINS-49060?focusedCommentId=325989&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-325989 + try { + return FileUtils.readFileToString(logfile, charset); + } catch (FileNotFoundException e) { + return ""; } catch (Exception e) { - throw new IOException("Failed to fully read " + logfile + " using charset " + charset.name(), e); + throw new IOException("Failed to fully read " + logfile, e); } - - return str.toString(); } /**