From a6ff85df1e7beebc6ec6781cf538b282f7dd7f02 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Fri, 12 Dec 2025 16:26:22 +1100 Subject: [PATCH 01/14] Make backing FileItem available in FileItemWrap --- .../github/bordertech/wcomponents/file/FileItemWrap.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/file/FileItemWrap.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/file/FileItemWrap.java index e99802be5..4d53417e2 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/file/FileItemWrap.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/file/FileItemWrap.java @@ -26,6 +26,13 @@ public FileItemWrap(final FileItem item) { backing = item; } + /** + * @return the backing file item. + */ + public FileItem getBacking() { + return backing; + } + /** * The name of the file as supplied by the client. Depending on the client this may or may not include the full path * to the file. From f0ec781073826ed86d531571446290ef81762bc5 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Fri, 9 Jan 2026 13:19:37 +1100 Subject: [PATCH 02/14] Make stream handling more consistent --- .../wcomponents/WMultiFileWidget.java | 12 ++- .../wcomponents/servlet/ServletUtil.java | 61 ++++++++------- .../bordertech/wcomponents/util/FileUtil.java | 37 +++++----- .../wcomponents/util/HtmlSanitizerUtil.java | 2 + .../util/thumbnail/ThumbnailUtil.java | 74 ++++++++++--------- 5 files changed, 96 insertions(+), 90 deletions(-) diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WMultiFileWidget.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WMultiFileWidget.java index 6e22570d9..fcef054c3 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WMultiFileWidget.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WMultiFileWidget.java @@ -9,6 +9,7 @@ import com.github.bordertech.wcomponents.util.Util; import com.github.bordertech.wcomponents.util.thumbnail.ThumbnailUtil; import java.awt.Dimension; +import java.io.InputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; @@ -779,8 +780,7 @@ protected void doHandleThumbnailRequest(final FileWidgetUpload file) { * @param file the file to process */ protected void doHandleFileContentRequest(final FileWidgetUpload file) { - ContentEscape escape = new ContentEscape(file.getFile()); - throw escape; + throw new ContentEscape(file.getFile()); } /** @@ -788,15 +788,13 @@ protected void doHandleFileContentRequest(final FileWidgetUpload file) { * @return the thumbnail */ protected Image createThumbNail(final File file) { - Image image = null; - try { + try (InputStream stream = file.getInputStream()) { Dimension size = getThumbnailSize(); - image = ThumbnailUtil.createThumbnail(file.getInputStream(), file.getName(), size, file. - getMimeType()); + return ThumbnailUtil.createThumbnail(stream, file.getName(), size, file.getMimeType()); } catch (Exception e) { LOG.error("Could not generate thumbnail for file. " + e.getMessage(), e); + return null; } - return image; } /** diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/servlet/ServletUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/servlet/ServletUtil.java index 112633183..ee206439e 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/servlet/ServletUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/servlet/ServletUtil.java @@ -20,7 +20,6 @@ import com.github.bordertech.wcomponents.container.DataListInterceptor; import com.github.bordertech.wcomponents.container.DebugStructureInterceptor; import com.github.bordertech.wcomponents.container.FormInterceptor; -import com.github.bordertech.wcomponents.container.TemplateRenderInterceptor; import com.github.bordertech.wcomponents.container.InterceptorComponent; import com.github.bordertech.wcomponents.container.PageShellInterceptor; import com.github.bordertech.wcomponents.container.ResponseCacheInterceptor; @@ -31,6 +30,7 @@ import com.github.bordertech.wcomponents.container.SubordinateControlInterceptor; import com.github.bordertech.wcomponents.container.TargetableErrorInterceptor; import com.github.bordertech.wcomponents.container.TargetableInterceptor; +import com.github.bordertech.wcomponents.container.TemplateRenderInterceptor; import com.github.bordertech.wcomponents.container.TransformXMLInterceptor; import com.github.bordertech.wcomponents.container.UIContextDumpInterceptor; import com.github.bordertech.wcomponents.container.ValidateXMLInterceptor; @@ -230,37 +230,41 @@ public static void handleStaticResourceRequest(final HttpServletRequest request, } InputStream resourceStream = staticResource.getStream(); - if (resourceStream == null) { - LOG.warn( - "Static resource [" + staticRequest + "] not found. Stream for content is null."); - response.setStatus(HttpServletResponse.SC_NOT_FOUND); - return; - } - - int size = resourceStream.available(); - String fileName = WebUtilities.encodeForContentDispositionHeader(staticRequest. - substring(staticRequest - .lastIndexOf('/') + 1)); + try { + if (resourceStream == null) { + LOG.warn( + "Static resource [" + staticRequest + "] not found. Stream for content is null."); + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + return; + } - if (size > 0) { - response.setContentLength(size); - } + int size = resourceStream.available(); + String fileName = WebUtilities.encodeForContentDispositionHeader(staticRequest. + substring(staticRequest + .lastIndexOf('/') + 1)); - response.setContentType(WebUtilities.getContentType(staticRequest)); - response.setHeader("Cache-Control", CacheType.CONTENT_CACHE.getSettings()); + if (size > 0) { + response.setContentLength(size); + } - String param = request.getParameter(WContent.URL_CONTENT_MODE_PARAMETER_KEY); - if ("inline".equals(param)) { - response.setHeader("Content-Disposition", "inline; filename=" + fileName); - } else if ("attach".equals(param)) { - response.setHeader("Content-Disposition", "attachment; filename=" + fileName); - } else { - // added "filename=" to comply with https://tools.ietf.org/html/rfc6266 - response.setHeader("Content-Disposition", "filename=" + fileName); - } + response.setContentType(WebUtilities.getContentType(staticRequest)); + response.setHeader("Cache-Control", CacheType.CONTENT_CACHE.getSettings()); + + String param = request.getParameter(WContent.URL_CONTENT_MODE_PARAMETER_KEY); + if ("inline".equals(param)) { + response.setHeader("Content-Disposition", "inline; filename=" + fileName); + } else if ("attach".equals(param)) { + response.setHeader("Content-Disposition", "attachment; filename=" + fileName); + } else { + // added "filename=" to comply with https://tools.ietf.org/html/rfc6266 + response.setHeader("Content-Disposition", "filename=" + fileName); + } - if (!headersOnly) { - StreamUtil.copy(resourceStream, response.getOutputStream()); + if (!headersOnly) { + StreamUtil.copy(resourceStream, response.getOutputStream()); + } + } finally { + StreamUtil.safeClose(resourceStream); } } catch (IOException e) { LOG.warn("Could not process static resource [" + staticRequest + "]. ", e); @@ -650,6 +654,7 @@ public static void extractParameterMap(final HttpServletRequest request, final M /** * Find the value of a cookie on the request, by name. + * * @param request The request on which to check for the cookie. * @param name The name of the cookie we want the value of. * @return The value of the cookie, if present, otherwise null. diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java index c81cbee96..5db00b0e7 100644 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java @@ -3,6 +3,7 @@ import com.github.bordertech.wcomponents.file.File; import com.github.bordertech.wcomponents.file.FileItemWrap; import java.io.IOException; +import java.io.InputStream; import java.text.DecimalFormat; import java.util.List; import java.util.stream.Collectors; @@ -33,13 +34,11 @@ private FileUtil() { private static final Log LOG = LogFactory.getLog(FileUtil.class); /** - * Checks if the file item is one among the supplied file types. - * This first checks against file extensions, then against file mime types + * Checks if the file item is one among the supplied file types. This first checks against file extensions, then + * against file mime types * - * @param newFile the file to be checked, if null then return false - * otherwise validate - * @param fileTypes allowed file types, if null or empty return true, - * otherwise validate + * @param newFile the file to be checked, if null then return false otherwise validate + * @param fileTypes allowed file types, if null or empty return true, otherwise validate * @return {@code true} if either extension or mime-type match is successful */ public static boolean validateFileType(final FileItemWrap newFile, final List fileTypes) { @@ -53,12 +52,12 @@ public static boolean validateFileType(final FileItemWrap newFile, final List fileExts = fileTypes.stream() - .filter(fileType -> fileType.startsWith(".")) - .collect(Collectors.toList()); + .filter(fileType -> fileType.startsWith(".")) + .collect(Collectors.toList()); // filter mime types from fileTypes. final List fileMimes = fileTypes.stream() - .filter(fileType -> !fileExts.contains(fileType)) - .collect(Collectors.toList()); + .filter(fileType -> !fileExts.contains(fileType)) + .collect(Collectors.toList()); // First validate newFile against fileExts list // If extensions are supplied, then check if newFile has a name @@ -67,7 +66,7 @@ public static boolean validateFileType(final FileItemWrap newFile, final List fileExt.equals("." + split[1]))) { + && fileExts.stream().anyMatch(fileExt -> fileExt.equals("." + split[1]))) { return true; } } @@ -108,7 +107,9 @@ public static String getFileMimeType(final File file) { if (file.getMimeType() != null) { meta.set(TikaCoreProperties.CONTENT_TYPE_HINT, file.getMimeType()); } - return tika.detect(file.getInputStream(), meta); + try (InputStream stream = file.getInputStream()) { + return tika.detect(stream, meta); + } } catch (IOException ex) { LOG.error("Invalid file, name " + file.getName(), ex); } @@ -119,10 +120,8 @@ public static String getFileMimeType(final File file) { /** * Checks if the file item size is within the supplied max file size. * - * @param newFile the file to be checked, if null then return false - * otherwise validate - * @param maxFileSize max file size in bytes, if zero or negative return - * true, otherwise validate + * @param newFile the file to be checked, if null then return false otherwise validate + * @param maxFileSize max file size in bytes, if zero or negative return true, otherwise validate * @return {@code true} if file size is valid. */ public static boolean validateFileSize(final FileItemWrap newFile, final long maxFileSize) { @@ -164,8 +163,8 @@ public static String getInvalidFileTypeMessage(final List fileTypes) { return null; } return String.format(I18nUtilities.format(null, - InternalMessages.DEFAULT_VALIDATION_ERROR_FILE_WRONG_TYPE), - StringUtils.join(fileTypes.toArray(new Object[fileTypes.size()]), ",")); + InternalMessages.DEFAULT_VALIDATION_ERROR_FILE_WRONG_TYPE), + StringUtils.join(fileTypes.toArray(new Object[fileTypes.size()]), ",")); } /** @@ -176,6 +175,6 @@ public static String getInvalidFileTypeMessage(final List fileTypes) { */ public static String getInvalidFileSizeMessage(final long maxFileSize) { return String.format(I18nUtilities.format(null, InternalMessages.DEFAULT_VALIDATION_ERROR_FILE_WRONG_SIZE), - FileUtil.readableFileSize(maxFileSize)); + FileUtil.readableFileSize(maxFileSize)); } } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/HtmlSanitizerUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/HtmlSanitizerUtil.java index 885d26d1d..bf6e19639 100644 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/HtmlSanitizerUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/HtmlSanitizerUtil.java @@ -159,6 +159,8 @@ public static Policy createPolicy(final String resourceName) { return Policy.getInstance(resource); } catch (PolicyException ex) { throw new SystemException("Could not create AntiSamy Policy" + ex.getMessage(), ex); + } finally { + StreamUtil.safeClose(resource); } } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/thumbnail/ThumbnailUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/thumbnail/ThumbnailUtil.java index d84b1e55c..d8c4feab8 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/thumbnail/ThumbnailUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/thumbnail/ThumbnailUtil.java @@ -113,6 +113,11 @@ public final class ThumbnailUtil { */ private static final String IMAGE_JPEG_FORMAT = "jpeg"; + /** + * JPEG Mime Type. + */ + private static final String MIMETYPE_JPEG = "image/jpeg"; + /** * Don't allow this utility class to be constructed. */ @@ -136,19 +141,27 @@ public static com.github.bordertech.wcomponents.Image createThumbnail(final Inpu final Dimension scaledSize, final String mimeType) { final Dimension scale = scaledSize == null ? THUMBNAIL_SCALE_SIZE : scaledSize; - // Generate thumbnail for image files - if (is != null && mimeType != null && (mimeType.equals("image/jpeg") || mimeType.equals( - "image/bmp") - || mimeType.equals("image/png") || mimeType.equals("image/gif"))) { + // Attempt to generate thumbnail for image files + if (is != null && isImageMimeType(mimeType)) { byte[] bytes = createImageThumbnail(is, scale); if (bytes != null) { - return new BytesImage(bytes, "image/jpeg", "Thumbnail of " + name, null); + return new BytesImage(bytes, MIMETYPE_JPEG, "Thumbnail of " + name, null); } } // Use default thumbnail depending on mime type - com.github.bordertech.wcomponents.Image image = handleDefaultImage(mimeType, name, scale); - return image; + return handleDefaultImage(mimeType, name, scale); + } + + /** + * @param mimeType the mime type to check + * @return true if mime type is for an image + */ + private static boolean isImageMimeType(final String mimeType) { + return mimeType != null && (mimeType.equals(MIMETYPE_JPEG) + || mimeType.equals("image/bmp") + || mimeType.equals("image/png") + || mimeType.equals("image/gif")); } /** @@ -193,7 +206,7 @@ private static com.github.bordertech.wcomponents.Image handleDefaultImage(final // Scale to correct size ByteArrayInputStream byteIs = new ByteArrayInputStream(image.getBytes()); byte[] bytes = createImageThumbnail(byteIs, scale); - image = new BytesImage(bytes, "image/jpeg", "Thumbnail of " + name, null); + image = new BytesImage(bytes, MIMETYPE_JPEG, "Thumbnail of " + name, null); } return image; } @@ -207,32 +220,28 @@ private static com.github.bordertech.wcomponents.Image handleDefaultImage(final * @return a byte[] representing the JPEG thumb nail. */ private static byte[] createImageThumbnail(final InputStream is, final Dimension scaledSize) { + // Create buffered image from input stream BufferedImage image; - MemoryCacheImageInputStream mciis; - try { - mciis = new MemoryCacheImageInputStream(is); - image = ImageIO.read(mciis); + // ImageIO closes stream + image = ImageIO.read(new MemoryCacheImageInputStream(is)); + if (image == null) { + return null; + } } catch (Exception e) { LOG.warn("Unable to read input image", e); return null; } - if (image == null) { - return null; - } - + // Create scaled image try { - byte[] jpeg = createScaledJPEG(image, scaledSize); - return jpeg; + return createScaledJPEG(image, scaledSize); } catch (Exception e) { LOG.error("Error creating thumbnail from image", e); + return null; } finally { image.flush(); } - - return null; - } /** @@ -247,12 +256,10 @@ private static byte[] createImageThumbnail(final InputStream is, final Dimension private static byte[] createScaledJPEG(final Image image, final Dimension scaledSize) throws IOException { // Scale the image. - Image scaledImage = image.getScaledInstance(scaledSize.width, scaledSize.height, - Image.SCALE_SMOOTH); + Image scaledImage = image.getScaledInstance(scaledSize.width, scaledSize.height, Image.SCALE_SMOOTH); // Create a BufferedImage copy of the scaledImage. - BufferedImage bufferedImage = new BufferedImage(scaledImage.getWidth(null), scaledImage. - getHeight(null), + BufferedImage bufferedImage = new BufferedImage(scaledImage.getWidth(null), scaledImage.getHeight(null), BufferedImage.TYPE_INT_RGB); Graphics2D graphics = bufferedImage.createGraphics(); graphics.drawImage(scaledImage, 0, 0, null); @@ -260,17 +267,12 @@ private static byte[] createScaledJPEG(final Image image, final Dimension scaled graphics.dispose(); // Convert the scaled image to a JPEG byte array. - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - MemoryCacheImageOutputStream mciis = new MemoryCacheImageOutputStream(baos); - ImageIO.write(bufferedImage, IMAGE_JPEG_FORMAT, mciis); - mciis.flush(); - bufferedImage.flush(); - - byte[] jpeg = baos.toByteArray(); - mciis.close(); - - return jpeg; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); MemoryCacheImageOutputStream mciis = new MemoryCacheImageOutputStream(baos)) { + ImageIO.write(bufferedImage, IMAGE_JPEG_FORMAT, mciis); + mciis.flush(); + bufferedImage.flush(); + return baos.toByteArray(); + } } } From bcb7569f71ad53f0fbb6b1e3482ad7fe7374fed9 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Mon, 12 Jan 2026 16:44:44 +1100 Subject: [PATCH 03/14] Consistent use of try-with-resources when handling streams --- CHANGELOG.md | 2 + .../bordertech/wcomponents/ContentEscape.java | 14 +--- .../wcomponents/InternalResource.java | 8 +- .../bordertech/wcomponents/WebUtilities.java | 4 +- .../container/TransformXMLInterceptor.java | 7 +- .../wcomponents/monitor/UicStats.java | 28 +++---- .../wcomponents/servlet/ServletUtil.java | 73 +++++++++---------- .../template/PlainTextRendererImpl.java | 13 ++-- .../util/DefaultInternalConfiguration.java | 10 ++- .../wcomponents/util/HtmlSanitizerUtil.java | 14 ++-- .../wcomponents/util/SerializationUtil.java | 17 ++--- .../wcomponents/util/StreamUtil.java | 16 ++-- .../wcomponents/util/ThemeUtil.java | 6 +- .../util/thumbnail/ThumbnailUtil.java | 12 ++- .../SerializationPerformance_Test.java | 17 ++--- .../wcomponents/Serialization_Test.java | 32 ++++---- .../wcomponents/util/FileUtil_Test.java | 9 ++- 17 files changed, 124 insertions(+), 158 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e5f33022..08b2a42b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### API Changes ### Enhancements +* Consistent use of try-with-resources when handling streams + ### Bug Fixes ## 1.5.38 diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/ContentEscape.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/ContentEscape.java index 7706ef0b9..a64759a9e 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/ContentEscape.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/ContentEscape.java @@ -9,8 +9,7 @@ import org.apache.commons.logging.LogFactory; /** - * An Escape subclass that bypasses the usual request -> paint flow by directly producing the binary document - * content. + * An Escape subclass that bypasses the usual request -> paint flow by directly producing the binary document content. * * @author Martin Shevchenko * @since 1.0.0 @@ -81,28 +80,19 @@ public void escape() throws IOException { } if (contentAccess instanceof ContentStreamAccess) { - InputStream stream = null; - - try { - stream = ((ContentStreamAccess) contentAccess).getStream(); - + try (InputStream stream = ((ContentStreamAccess) contentAccess).getStream()) { if (stream == null) { throw new SystemException( "ContentAccess returned null stream, access=" + contentAccess); } - StreamUtil.copy(stream, response.getOutputStream()); - } finally { - StreamUtil.safeClose(stream); } } else { byte[] bytes = contentAccess.getBytes(); - if (bytes == null) { throw new SystemException( "ContentAccess returned null data, access=" + contentAccess); } - response.getOutputStream().write(bytes); } } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/InternalResource.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/InternalResource.java index 42bf2c7be..ef096226f 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/InternalResource.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/InternalResource.java @@ -56,17 +56,11 @@ public InternalResource(final String resourceName, final String description) { */ @Override public byte[] getBytes() { - InputStream stream = null; - - try { - stream = getClass().getResourceAsStream(resourceName); + try (InputStream stream = getClass().getResourceAsStream(resourceName)) { return StreamUtil.getBytes(stream); } catch (Exception e) { LOG.error("Failed to read resource: " + resourceName, e); - } finally { - StreamUtil.safeClose(stream); } - return EMPTY; } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WebUtilities.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WebUtilities.java index be49eda44..af28ca968 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WebUtilities.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WebUtilities.java @@ -752,8 +752,8 @@ public static String render(final Request request, final WComponent component) { component.preparePaint(request); try (PrintWriter writer = new PrintWriter(buffer)) { component.paint(new WebXmlRenderContext(writer)); + return buffer.toString(); } - return buffer.toString(); } finally { if (needsContext) { UIContextHolder.popContext(); @@ -807,8 +807,8 @@ public static String renderWithTransformToHTML(final Request request, final WCom chain.preparePaint(request); try (PrintWriter writer = new PrintWriter(buffer)) { chain.paint(new WebXmlRenderContext(writer)); + return buffer.toString(); } - return buffer.toString(); } finally { if (needsContext) { UIContextHolder.popContext(); diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/container/TransformXMLInterceptor.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/container/TransformXMLInterceptor.java index a193895a2..ddb04c9bc 100644 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/container/TransformXMLInterceptor.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/container/TransformXMLInterceptor.java @@ -182,15 +182,14 @@ public void paint(final RenderContext renderContext) { private void transform(final String xml, final UIContext uic, final PrintWriter writer) { Transformer transformer = newTransformer(); - Source inputXml; - try { - inputXml = new StreamSource(new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))); + try (InputStream inStream = new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) { + Source inputXml = new StreamSource(inStream); StreamResult result = new StreamResult(writer); if (debugRequested) { transformer.setParameter("isDebug", 1); } transformer.transform(inputXml, result); - } catch (TransformerException ex) { + } catch (TransformerException | IOException ex) { throw new SystemException("Could not transform xml", ex); } } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/monitor/UicStats.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/monitor/UicStats.java index 675eb91ba..7d29e96f1 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/monitor/UicStats.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/monitor/UicStats.java @@ -86,8 +86,7 @@ public Iterator getWCsAnalysed() { } /** - * Retrieves the map wchich contains all the WComponent instances that make up the WComponent tree starting from the - * given root component. + * Retrieves the map wchich contains all the WComponent instances that make up the WComponent tree starting from the given root component. * * @param root the root component. * @return the map of Stats for the components under the given root component. @@ -205,12 +204,8 @@ private Stat createStat(final WComponent comp) { * @return the serialized size of the given object, or -1 on error. */ private int getSerializationSize(final Object obj) { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos)) { oos.writeObject(obj); - oos.close(); - byte[] bytes = bos.toByteArray(); return bytes.length; } catch (IOException ex) { @@ -225,17 +220,18 @@ private int getSerializationSize(final Object obj) { */ private void addSerializationStat(final ComponentModel model, final Stat stat) { try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(model); - oos.close(); - - byte[] bytes = bos.toByteArray(); + // Calc size + byte[] bytes; + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(model); + bytes = bos.toByteArray(); + } stat.setSerializedSize(bytes.length); - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - ObjectInputStream ois = new ObjectInputStream(bis); - ois.readObject(); + // Check serializable + try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis)) { + ois.readObject(); + } stat.setModelState(Stat.MDL_SERIALIZABLE); } catch (Exception ex) { diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/servlet/ServletUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/servlet/ServletUtil.java index ee206439e..679a0bbd8 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/servlet/ServletUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/servlet/ServletUtil.java @@ -229,8 +229,7 @@ public static void handleStaticResourceRequest(final HttpServletRequest request, return; } - InputStream resourceStream = staticResource.getStream(); - try { + try (InputStream resourceStream = staticResource.getStream()) { if (resourceStream == null) { LOG.warn( "Static resource [" + staticRequest + "] not found. Stream for content is null."); @@ -263,8 +262,6 @@ public static void handleStaticResourceRequest(final HttpServletRequest request, if (!headersOnly) { StreamUtil.copy(resourceStream, response.getOutputStream()); } - } finally { - StreamUtil.safeClose(resourceStream); } } catch (IOException e) { LOG.warn("Could not process static resource [" + staticRequest + "]. ", e); @@ -316,34 +313,33 @@ public static void handleThemeResourceRequest(final HttpServletRequest req, return; } - InputStream resourceStream = null; + URL url = null; - try { - URL url = null; + // Check for project translation file + if (fileName.startsWith(THEME_TRANSLATION_RESOURCE_PREFIX)) { + String resourceFileName = fileName.substring(THEME_TRANSLATION_RESOURCE_PREFIX.length()); + url = ServletUtil.class.getResource(THEME_PROJECT_TRANSLATION_RESOURCE_PATH + resourceFileName); + } - // Check for project translation file - if (fileName.startsWith(THEME_TRANSLATION_RESOURCE_PREFIX)) { - String resourceFileName = fileName.substring(THEME_TRANSLATION_RESOURCE_PREFIX.length()); - url = ServletUtil.class.getResource(THEME_PROJECT_TRANSLATION_RESOURCE_PATH + resourceFileName); - } + // Load from the theme path + if (url == null) { + String resourceName = ThemeUtil.getThemeBase() + fileName; + url = ServletUtil.class.getResource(resourceName); + } - // Load from the theme path - if (url == null) { - String resourceName = ThemeUtil.getThemeBase() + fileName; - url = ServletUtil.class.getResource(resourceName); - } + if (url == null) { + resp.setStatus(HttpServletResponse.SC_NOT_FOUND); + return; + } - if (url == null) { - resp.setStatus(HttpServletResponse.SC_NOT_FOUND); - } else { - URLConnection connection = url.openConnection(); - resourceStream = connection.getInputStream(); - int size = resourceStream.available(); - if (size > 0) { - resp.setContentLength(size); - } + URLConnection connection = url.openConnection(); + try (InputStream resourceStream = connection.getInputStream()) { + int size = resourceStream.available(); + if (size > 0) { + resp.setContentLength(size); + } - /* + /* I have commented out the setting of the Content-Disposition on static theme resources because, well why is it there? If this needs to be reinstated please provide a thorough justification comment here so the reasons are clear. @@ -353,19 +349,16 @@ public static void handleThemeResourceRequest(final HttpServletRequest req, substring(fileName .lastIndexOf('/') + 1)); resp.setHeader("Content-Disposition", "filename=" + encodedName); // "filename=" to comply with https://tools.ietf.org/html/rfc6266 - */ - resp.setContentType(WebUtilities.getContentType(fileName)); - resp.setHeader("Cache-Control", CacheType.THEME_CACHE.getSettings()); - - resp.setHeader("Expires", "31536000"); - resp.setHeader("ETag", "\"" + WebUtilities.getProjectVersion() + "\""); - // resp.setHeader("Last-Modified", "Mon, 02 Jan 2015 01:00:00 GMT"); - long modified = connection.getLastModified(); - resp.setDateHeader("Last-Modified", modified); - StreamUtil.copy(resourceStream, resp.getOutputStream()); - } - } finally { - StreamUtil.safeClose(resourceStream); + */ + resp.setContentType(WebUtilities.getContentType(fileName)); + resp.setHeader("Cache-Control", CacheType.THEME_CACHE.getSettings()); + + resp.setHeader("Expires", "31536000"); + resp.setHeader("ETag", "\"" + WebUtilities.getProjectVersion() + "\""); + // resp.setHeader("Last-Modified", "Mon, 02 Jan 2015 01:00:00 GMT"); + long modified = connection.getLastModified(); + resp.setDateHeader("Last-Modified", modified); + StreamUtil.copy(resourceStream, resp.getOutputStream()); } } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/template/PlainTextRendererImpl.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/template/PlainTextRendererImpl.java index ec7cd2e9e..4d3b0b360 100644 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/template/PlainTextRendererImpl.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/template/PlainTextRendererImpl.java @@ -71,8 +71,6 @@ public void renderTemplate(final String templateName, final Map boolean xmlEncode = options.containsKey(XML_ENCODE); String cacheKey = templateName + "-" + xmlEncode; - InputStream stream = null; - // Caching Object value = options.get(USE_CACHE); boolean cache = (isCaching() && value == null) || (value != null && "true".equalsIgnoreCase(value.toString())); @@ -83,11 +81,12 @@ public void renderTemplate(final String templateName, final Map output = getCache().get(cacheKey); } if (output == null) { - stream = getClass().getResourceAsStream(name); - if (stream == null) { - throw new SystemException("Could not find plain text template [" + templateName + "]."); + try (InputStream stream = getClass().getResourceAsStream(name)) { + if (stream == null) { + throw new SystemException("Could not find plain text template [" + templateName + "]."); + } + output = new String(StreamUtil.getBytes(stream), StandardCharsets.UTF_8); } - output = new String(StreamUtil.getBytes(stream), StandardCharsets.UTF_8); if (xmlEncode) { output = WebUtilities.encode(output); } @@ -100,8 +99,6 @@ public void renderTemplate(final String templateName, final Map throw e; } catch (Exception e) { throw new SystemException("Problems with plain text template [" + templateName + "]. " + e.getMessage(), e); - } finally { - StreamUtil.safeClose(stream); } } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/DefaultInternalConfiguration.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/DefaultInternalConfiguration.java index e0f1ca9ae..571953393 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/DefaultInternalConfiguration.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/DefaultInternalConfiguration.java @@ -435,11 +435,11 @@ private void load(final String resourceName) { byte[] buff = contentsList.get(i); URL url = urlList.get(i); recordMessage("Loading from url " + url + "..."); - ByteArrayInputStream in = new ByteArrayInputStream(buff); - // Use the "IncludeProperties" to load properties into us one at a time.... IncludeProperties properties = new IncludeProperties(url.toString()); - properties.load(in); + try (ByteArrayInputStream in = new ByteArrayInputStream(buff)) { + properties.load(in); + } } File file = new File(resourceName); @@ -451,7 +451,9 @@ private void load(final String resourceName) { // Use the "IncludeProperties" to load properties into us, one at a time.... IncludeProperties properties = new IncludeProperties("file:" + filename(file)); - properties.load(new BufferedInputStream(new FileInputStream(file))); + try (InputStream fileStream = new FileInputStream(file); InputStream stream = new BufferedInputStream(fileStream)) { + properties.load(stream); + } } if (!found) { diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/HtmlSanitizerUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/HtmlSanitizerUtil.java index bf6e19639..9422607dc 100644 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/HtmlSanitizerUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/HtmlSanitizerUtil.java @@ -1,6 +1,7 @@ package com.github.bordertech.wcomponents.util; import com.github.bordertech.wcomponents.WebUtilities; +import java.io.IOException; import java.io.InputStream; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; @@ -151,16 +152,13 @@ public static Policy createPolicy(final String resourceName) { if (StringUtils.isBlank(resourceName)) { throw new SystemException("AntiSamy Policy resourceName cannot be null "); } - InputStream resource = HtmlSanitizerUtil.class.getClassLoader().getResourceAsStream(resourceName); - if (resource == null) { - throw new SystemException("Could not find AntiSamy Policy XML resource."); - } - try { + try (InputStream resource = HtmlSanitizerUtil.class.getClassLoader().getResourceAsStream(resourceName)) { + if (resource == null) { + throw new SystemException("Could not find AntiSamy Policy XML resource."); + } return Policy.getInstance(resource); - } catch (PolicyException ex) { + } catch (IOException | PolicyException ex) { throw new SystemException("Could not create AntiSamy Policy" + ex.getMessage(), ex); - } finally { - StreamUtil.safeClose(resource); } } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/SerializationUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/SerializationUtil.java index c6a8e42db..e04b9d1a8 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/SerializationUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/SerializationUtil.java @@ -26,16 +26,15 @@ private SerializationUtil() { */ public static Object pipe(final Object in) { try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream os = new ObjectOutputStream(bos); - os.writeObject(in); - os.close(); + byte[] bytes; + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream os = new ObjectOutputStream(bos)) { + os.writeObject(in); + bytes = bos.toByteArray(); + } - byte[] bytes = bos.toByteArray(); - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - ObjectInputStream is = new ObjectInputStream(bis); - Object out = is.readObject(); - return out; + try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream is = new ObjectInputStream(bis)) { + return is.readObject(); + } } catch (Exception ex) { throw new SystemException("Failed to pipe " + in, ex); } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/StreamUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/StreamUtil.java index 5c6bffc43..7b9e2521f 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/StreamUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/StreamUtil.java @@ -72,10 +72,10 @@ public static void copy(final InputStream in, final OutputStream out, final int * @throws IOException if there is an error reading from the stream. */ public static byte[] getBytes(final InputStream in) throws IOException { - ByteArrayOutputStream result = new ByteArrayOutputStream(); - copy(in, result); - result.close(); - return result.toByteArray(); + try (ByteArrayOutputStream result = new ByteArrayOutputStream()) { + copy(in, result); + return result.toByteArray(); + } } /** @@ -101,9 +101,9 @@ public static void safeClose(final Closeable stream) { * @throws IOException If there is an error reading from the is. */ public static byte[] streamToByteArray(final InputStream is) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - copy(is, baos); - byte[] bytes = baos.toByteArray(); - return bytes; + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + copy(is, baos); + return baos.toByteArray(); + } } } diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/ThemeUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/ThemeUtil.java index 10b4f41d9..812c9120d 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/ThemeUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/ThemeUtil.java @@ -55,12 +55,10 @@ public final class ThemeUtil { String resourceName = THEME_BASE + THEME_VERSION_FILE_NAME; // Get theme version property file (if in classpath) - InputStream resourceStream = null; Properties prop = new Properties(); String themeBuild = null; String themeWcVersion = null; - try { - resourceStream = ThemeUtil.class.getResourceAsStream(resourceName); + try (InputStream resourceStream = ThemeUtil.class.getResourceAsStream(resourceName)) { prop.load(resourceStream); // Theme build property themeBuild = prop.getProperty(THEME_BUILD_NUMBER_PARAM); @@ -68,8 +66,6 @@ public final class ThemeUtil { themeWcVersion = prop.getProperty(THEME_WC_BUILD_NUMBER_PARAM); } catch (Exception e) { LOG.warn("Could not load theme version properties file \"" + resourceName + "\""); - } finally { - StreamUtil.safeClose(resourceStream); } // If theme build not available, then use the wcomponents project version diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/thumbnail/ThumbnailUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/thumbnail/ThumbnailUtil.java index d8c4feab8..455c0aab7 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/thumbnail/ThumbnailUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/thumbnail/ThumbnailUtil.java @@ -204,9 +204,13 @@ private static com.github.bordertech.wcomponents.Image handleDefaultImage(final boolean sameWidth = scale.width == -1 || scale.width == THUMBNAIL_DEFAULT_SIZE.width; if (!sameHeight || !sameWidth) { // Scale to correct size - ByteArrayInputStream byteIs = new ByteArrayInputStream(image.getBytes()); - byte[] bytes = createImageThumbnail(byteIs, scale); - image = new BytesImage(bytes, MIMETYPE_JPEG, "Thumbnail of " + name, null); + try (ByteArrayInputStream byteIs = new ByteArrayInputStream(image.getBytes())) { + byte[] bytes = createImageThumbnail(byteIs, scale); + image = new BytesImage(bytes, MIMETYPE_JPEG, "Thumbnail of " + name, null); + } catch (IOException ex) { + image = null; + LOG.error("Error creating scaled thumbnail from image", ex); + } } return image; } @@ -223,7 +227,7 @@ private static byte[] createImageThumbnail(final InputStream is, final Dimension // Create buffered image from input stream BufferedImage image; try { - // ImageIO closes stream + // ImageIO closes cache stream image = ImageIO.read(new MemoryCacheImageInputStream(is)); if (image == null) { return null; diff --git a/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/SerializationPerformance_Test.java b/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/SerializationPerformance_Test.java index edf1a790f..1cd79b01b 100755 --- a/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/SerializationPerformance_Test.java +++ b/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/SerializationPerformance_Test.java @@ -23,8 +23,8 @@ import org.junit.experimental.categories.Category; /** - * Tests to check the performance of WComponent graph serialization. This test does not check for correct serialization - * - see {@link Serialization_Test} instead. + * Tests to check the performance of WComponent graph serialization. This test does not check for correct serialization - see + * {@link Serialization_Test} instead. * * @author Yiannis Paschalidis * @since 1.0.0 @@ -180,12 +180,10 @@ private int getUISize(final UIContext uic, final WComponent comp) throws IOExcep * @throws IOException an IO exception */ private static byte[] serialize(final Serializable obj) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(obj); - oos.close(); - - return bos.toByteArray(); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(obj); + return bos.toByteArray(); + } } /** @@ -288,8 +286,7 @@ private void findWComponents(final WComponent comp, final List resul } /** - * AllComponents instantiated with 10 repetitions. This needs to be created as a subclass as the UIRegistry uses the - * class name. + * AllComponents instantiated with 10 repetitions. This needs to be created as a subclass as the UIRegistry uses the class name. */ public static final class AllComponents10 extends AllComponents { diff --git a/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/Serialization_Test.java b/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/Serialization_Test.java index 9645d5add..d7e2fe1c9 100755 --- a/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/Serialization_Test.java +++ b/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/Serialization_Test.java @@ -7,8 +7,8 @@ import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import org.junit.Assert; import org.apache.commons.logging.LogFactory; +import org.junit.Assert; import org.junit.Test; /** @@ -129,22 +129,20 @@ private void assertSerializable(final Object obj) { */ private static Object pipe(final Object obj) { try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(obj); - oos.close(); - - byte[] bytes = bos.toByteArray(); - - FileOutputStream fos = new FileOutputStream("SerializeText.txt"); - fos.write(bytes); - fos.flush(); - fos.close(); - - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - ObjectInputStream ois = new ObjectInputStream(bis); - Object out = ois.readObject(); - return out; + byte[] bytes; + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(obj); + bytes = bos.toByteArray(); + } + + try (FileOutputStream fos = new FileOutputStream("SerializeText.txt")) { + fos.write(bytes); + fos.flush(); + } + + try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bis)) { + return ois.readObject(); + } } catch (Exception ex) { throw new SystemException("Failed to pipe " + obj, ex); } diff --git a/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/util/FileUtil_Test.java b/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/util/FileUtil_Test.java index a490fcc8f..bf3e88c3f 100644 --- a/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/util/FileUtil_Test.java +++ b/wcomponents-core/src/test/java/com/github/bordertech/wcomponents/util/FileUtil_Test.java @@ -150,11 +150,12 @@ private FileItem createFileItem(String fileResource) throws IOException { testFileContent[i] = (byte) (i & 0xff); } } else { - InputStream stream = getClass().getResourceAsStream(fileResource); - if (stream == null) { - throw new IOException("File resource not found: " + fileResource); + try (InputStream stream = getClass().getResourceAsStream(fileResource)) { + if (stream == null) { + throw new IOException("File resource not found: " + fileResource); + } + testFileContent = StreamUtil.getBytes(stream); } - testFileContent = StreamUtil.getBytes(stream); } MockFileItem fileItem = new MockFileItem(); fileItem.set(testFileContent); From c772d9c2ed22bf034a50decb6f8fb171d3199cf1 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Mon, 12 Jan 2026 17:05:06 +1100 Subject: [PATCH 04/14] Update examples to have consistent use of try-with-resources when handling streams --- .../wcomponents/examples/DynamicImage.java | 14 ++++++------- .../wcomponents/examples/TextImage.java | 18 +++++++--------- .../examples/WComponentRenderPerfTest.java | 19 ++++++++--------- .../wcomponents/examples/WImageExample.java | 12 ++++------- .../examples/picker/ExampleSection.java | 15 +------------ .../examples/theme/XsltTestComponent.java | 21 +------------------ .../examples/SimpleFileUpload_Test.java | 7 +++---- 7 files changed, 32 insertions(+), 74 deletions(-) diff --git a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/DynamicImage.java b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/DynamicImage.java index ecfa4f580..01edd5b5f 100755 --- a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/DynamicImage.java +++ b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/DynamicImage.java @@ -55,8 +55,7 @@ public String getMimeType() { } /** - * Retrieves the natural size of the image. If only one dimension is known, a negative value will be returned for - * the other dimension. + * Retrieves the natural size of the image. If only one dimension is known, a negative value will be returned for the other dimension. * * @return the image size, or null if unknown. */ @@ -98,12 +97,11 @@ public byte[] getBytes() { // Write the image to a byte array. Iterator writers = ImageIO.getImageWritersByMIMEType(getMimeType()); ImageWriter writer = writers.next(); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ImageOutputStream ios = ImageIO.createImageOutputStream(os); - writer.setOutput(ios); - writer.write(image); - - return os.toByteArray(); + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); ImageOutputStream ios = ImageIO.createImageOutputStream(os)) { + writer.setOutput(ios); + writer.write(image); + return os.toByteArray(); + } } catch (IOException ex) { LOG.error("Unable to generate client image.", ex); } diff --git a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/TextImage.java b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/TextImage.java index 5ca169898..934c51502 100755 --- a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/TextImage.java +++ b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/TextImage.java @@ -112,12 +112,11 @@ public TextImage(final String text, final Dimension size) { // Write the image to a byte array. Iterator writers = ImageIO.getImageWritersByMIMEType(MIME_TYPE); ImageWriter writer = writers.next(); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - ImageOutputStream ios = ImageIO.createImageOutputStream(os); - writer.setOutput(ios); - writer.write(image); - - imageBytes = os.toByteArray(); + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); ImageOutputStream ios = ImageIO.createImageOutputStream(os)) { + writer.setOutput(ios); + writer.write(image); + imageBytes = os.toByteArray(); + } } catch (IOException ex) { LOG.error("Unable to generate client image.", ex); } @@ -135,8 +134,7 @@ public String getMimeType() { } /** - * Retrieves the natural size of the image. If only one dimension is known, a negative value will be returned for - * the other dimension. + * Retrieves the natural size of the image. If only one dimension is known, a negative value will be returned for the other dimension. * * @return the image size, or null if unknown. */ @@ -146,8 +144,8 @@ public Dimension getSize() { } /** - * Sets the natural size of the image. If only one dimension is known, use a negative value for the other dimension. - * If the image size is unknown, set the size to null. + * Sets the natural size of the image. If only one dimension is known, use a negative value for the other dimension. If the image size is unknown, + * set the size to null. * * @param size the image size. */ diff --git a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/WComponentRenderPerfTest.java b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/WComponentRenderPerfTest.java index 77fa85b74..315bd3a2e 100755 --- a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/WComponentRenderPerfTest.java +++ b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/WComponentRenderPerfTest.java @@ -109,16 +109,15 @@ private static void launchTest(final String testName) throws IOException { testName }); - InputStream stdout = process.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(stdout)); - - // Pipe the input from the process to the logger - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - int index = line.indexOf(LINE_PREFIX); - - if (index != -1) { - line = line.substring(index + LINE_PREFIX.length()); - LOG.info(line); + try (InputStream stdout = process.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(stdout))) { + // Pipe the input from the process to the logger + for (String line = reader.readLine(); line != null; line = reader.readLine()) { + int index = line.indexOf(LINE_PREFIX); + + if (index != -1) { + line = line.substring(index + LINE_PREFIX.length()); + LOG.info(line); + } } } } diff --git a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/WImageExample.java b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/WImageExample.java index 7585e1f3b..bbc96d5f2 100755 --- a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/WImageExample.java +++ b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/WImageExample.java @@ -126,16 +126,12 @@ public static class ExampleImage implements Image { * @param resource the path to the image file. */ public ExampleImage(final String resource) { - InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream( - resource); - - if (in != null) { - try { + try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource)) { + if (in != null) { imageBytes = StreamUtil.getBytes(in); - in.close(); - } catch (IOException ex) { - LOG.error("Cannot load example image.", ex); } + } catch (IOException ex) { + LOG.error("Cannot load example image.", ex); } } diff --git a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/picker/ExampleSection.java b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/picker/ExampleSection.java index 4370dc87a..62dde9707 100755 --- a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/picker/ExampleSection.java +++ b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/picker/ExampleSection.java @@ -215,27 +215,14 @@ public void resetExample() { private static String getSource(final String className) { String sourceName = '/' + className.replace('.', '/') + ".java"; - InputStream stream = null; - - try { - stream = ExampleSection.class.getResourceAsStream(sourceName); - + try (InputStream stream = ExampleSection.class.getResourceAsStream(sourceName)) { if (stream != null) { byte[] sourceBytes = StreamUtil.getBytes(stream); - // we need to do some basic formatting of the source now. return new String(sourceBytes, "UTF-8"); } } catch (IOException e) { LOG.warn("Unable to read source code for class " + className, e); - } finally { - if (stream != null) { - try { - stream.close(); - } catch (IOException e) { - LOG.error("Error closing stream", e); - } - } } return null; diff --git a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/theme/XsltTestComponent.java b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/theme/XsltTestComponent.java index bd7f75ccb..aa7cbcb9f 100755 --- a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/theme/XsltTestComponent.java +++ b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/theme/XsltTestComponent.java @@ -95,33 +95,14 @@ private void runTransform() { */ private void transform(final File xsltFile, final File inputFile, final File outputFile) throws Exception { - FileReader xsltIn = null; - FileReader in = null; - FileWriter out = null; - - try { - xsltIn = new FileReader(xsltFile); + try (FileReader xsltIn = new FileReader(xsltFile); FileReader in = new FileReader(inputFile); FileWriter out = new FileWriter(outputFile)) { Source xsltSource = new StreamSource(xsltIn); TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(xsltSource); // transformer.setOutputProperty("disable-empty-element-collapsing", "true"); - - in = new FileReader(inputFile); Source source = new StreamSource(in); - out = new FileWriter(outputFile); StreamResult result = new StreamResult(out); transformer.transform(source, result); - } finally { - if (xsltIn != null) { - xsltIn.close(); - } - if (in != null) { - in.close(); - } - if (out != null) { - out.flush(); - out.close(); - } } } } diff --git a/wcomponents-examples/src/test/java/com/github/bordertech/wcomponents/examples/SimpleFileUpload_Test.java b/wcomponents-examples/src/test/java/com/github/bordertech/wcomponents/examples/SimpleFileUpload_Test.java index 18d1ec05b..592b7b16f 100755 --- a/wcomponents-examples/src/test/java/com/github/bordertech/wcomponents/examples/SimpleFileUpload_Test.java +++ b/wcomponents-examples/src/test/java/com/github/bordertech/wcomponents/examples/SimpleFileUpload_Test.java @@ -65,10 +65,9 @@ private static File createTempFile(final String content) throws IOException { File tempFile = File.createTempFile("SimpleFileUpload_Test", "tmp"); tempFile.deleteOnExit(); - OutputStream out = new FileOutputStream(tempFile); - out.write(content.getBytes()); - out.close(); - + try (OutputStream out = new FileOutputStream(tempFile)) { + out.write(content.getBytes()); + } return tempFile; } } From e176826d89849ec142c03194f1874ae3e0cc1e15 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Mon, 12 Jan 2026 17:06:25 +1100 Subject: [PATCH 05/14] Update LDE to have consistent use of try-with-resources when handling streams --- .../wcomponents/lde/LdeSessionUtil.java | 30 ++----------------- .../wcomponents/lde/PlainLauncher_Test.java | 10 +++++-- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/wcomponents-lde/src/main/java/com/github/bordertech/wcomponents/lde/LdeSessionUtil.java b/wcomponents-lde/src/main/java/com/github/bordertech/wcomponents/lde/LdeSessionUtil.java index f4106d6f9..fad7ccb3d 100755 --- a/wcomponents-lde/src/main/java/com/github/bordertech/wcomponents/lde/LdeSessionUtil.java +++ b/wcomponents-lde/src/main/java/com/github/bordertech/wcomponents/lde/LdeSessionUtil.java @@ -1,7 +1,6 @@ package com.github.bordertech.wcomponents.lde; import com.github.bordertech.wcomponents.util.ConfigurationProperties; -import com.github.bordertech.wcomponents.util.StreamUtil; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -47,16 +46,9 @@ private LdeSessionUtil() { */ public static void deserializeSessionAttributes(final HttpSession session) { File file = new File(SERIALIZE_SESSION_NAME); - FileInputStream fis = null; - ObjectInputStream ois = null; - if (file.canRead()) { - try { - fis = new FileInputStream(file); - ois = new ObjectInputStream(fis); - + try (FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis)) { List data = (List) ois.readObject(); - for (Iterator i = data.iterator(); i.hasNext();) { String key = (String) i.next(); Object value = i.next(); @@ -64,12 +56,6 @@ public static void deserializeSessionAttributes(final HttpSession session) { } } catch (Exception e) { LOG.error("Failed to read serialized session from " + file, e); - } finally { - if (ois != null) { - StreamUtil.safeClose(ois); - } else { - StreamUtil.safeClose(fis); - } } } else { LOG.warn("Unable to read serialized session from " + file); @@ -105,22 +91,10 @@ public static synchronized void serializeSessionAttributes(final HttpSession ses } // Write them to the file - FileOutputStream fos = null; - ObjectOutputStream oos = null; - - try { - fos = new FileOutputStream(file); - oos = new ObjectOutputStream(fos); - + try (FileOutputStream fos = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(fos)) { oos.writeObject(data); } catch (Exception e) { LOG.error("Failed to write serialized session to " + file, e); - } finally { - if (oos != null) { - StreamUtil.safeClose(oos); - } else { - StreamUtil.safeClose(fos); - } } } else { LOG.warn("Unable to write serialized session to " + file); diff --git a/wcomponents-lde/src/test/java/com/github/bordertech/wcomponents/lde/PlainLauncher_Test.java b/wcomponents-lde/src/test/java/com/github/bordertech/wcomponents/lde/PlainLauncher_Test.java index e0eda7c14..69370c3da 100755 --- a/wcomponents-lde/src/test/java/com/github/bordertech/wcomponents/lde/PlainLauncher_Test.java +++ b/wcomponents-lde/src/test/java/com/github/bordertech/wcomponents/lde/PlainLauncher_Test.java @@ -11,10 +11,11 @@ import com.github.bordertech.wcomponents.util.ConfigurationProperties; import com.github.bordertech.wcomponents.util.StreamUtil; import com.github.bordertech.wcomponents.util.mock.servlet.MockHttpServletRequest; +import java.io.InputStream; import java.net.URL; import java.net.URLConnection; -import org.junit.Assert; import org.junit.After; +import org.junit.Assert; import org.junit.Test; /** @@ -95,8 +96,11 @@ public void testServer() throws Exception { // Access the server and record the output URL url = new URL(launcher.getUrl()); URLConnection conn = url.openConnection(); - byte[] result = StreamUtil.getBytes(conn.getInputStream()); - String content = new String(result, "UTF-8"); + String content; + try (InputStream stream = conn.getInputStream()) { + byte[] result = StreamUtil.getBytes(stream); + content = new String(result, "UTF-8"); + } Assert.assertEquals("HandleRequest should have been called once", 1, MyTestApp.handleRequestCount); From 5b4a327495fc8191e7ce88ca0b55283f2b786721 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Mon, 12 Jan 2026 17:25:42 +1100 Subject: [PATCH 06/14] Updated AbstractRequest to remove deprecated methods uploadFileItems and readBytes (were protected static) --- CHANGELOG.md | 1 + .../wcomponents/AbstractRequest.java | 42 +------------------ 2 files changed, 2 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b2a42b9..13c00d7c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### API Changes ### Enhancements * Consistent use of try-with-resources when handling streams +* Updated AbstractRequest to remove deprecated methods uploadFileItems and readBytes (were protected static). Use StreamUtils instead. ### Bug Fixes diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/AbstractRequest.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/AbstractRequest.java index 3511feb72..5ed0ab8b6 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/AbstractRequest.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/AbstractRequest.java @@ -1,16 +1,11 @@ package com.github.bordertech.wcomponents; -import com.github.bordertech.wcomponents.servlet.ServletUtil; import com.github.bordertech.wcomponents.util.Config; import com.github.bordertech.wcomponents.util.Enumerator; -import com.github.bordertech.wcomponents.util.StreamUtil; import com.github.bordertech.wcomponents.util.Util; -import java.io.IOException; -import java.io.InputStream; import java.io.Serializable; import java.util.Enumeration; import java.util.HashMap; -import java.util.List; import java.util.Map; import org.apache.commons.fileupload.FileItem; @@ -69,7 +64,7 @@ public FileItem[] getFileItems(final String key) { result = deserialized.toArray(new FileItem[]{}); } } - */ + */ return result; } @@ -128,41 +123,6 @@ public boolean isLogout() { return logout; } - /** - *

- * {@link FileItem} classes (if attachements) will be kept as part of the request. The default behaviour of the file - * item is to store the upload in memory until it reaches a certain size, after which the content is streamed to a - * temp file.

- * - *

- * If, in the future, performance of uploads becomes a focus we can instead look into using the Jakarta Commons - * Streaming API. In this case, the content of the upload isn't stored anywhere. It will be up to the user to - * read/store the content of the stream.

- * - * @param fileItems a list of {@link FileItem}s corresponding to POSTed form data. - * @param parameters the map to store non-file request parameters in. - * @param files the map to store the uploaded file parameters in. - * @deprecated Use {@link ServletUtil#uploadFileItems(java.util.List, java.util.Map, java.util.Map)} instead. - */ - @Deprecated - protected static void uploadFileItems(final List fileItems, final Map parameters, - final Map files) { - ServletUtil.uploadFileItems(fileItems, parameters, files); - } - - /** - * Returns a byte array containing all the information contained in the given input stream. - * - * @param stream the input stream to read from. - * @return the stream contents as a byte array. - * @throws IOException if there is an error reading from the stream. - * @deprecated Use {@link StreamUtil#getBytes(java.io.InputStream)} instead. - */ - @Deprecated - protected static byte[] readBytes(final InputStream stream) throws IOException { - return StreamUtil.getBytes(stream); - } - /** * This method contains no logic. Subclasses which need to perform event handling logic (eg. * WPortletRequest) should override this method. From 40ec02fde48018848d8e1ab51ea68dca448a7a99 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Wed, 14 Jan 2026 14:06:20 +1000 Subject: [PATCH 07/14] Replaced org.apache.tika:tika library with org.overviewproject:mime-types in FileUtil to validate uploaded file mime types --- CHANGELOG.md | 1 + wcomponents-core/pom.xml | 17 ++------ .../wcomponents/WMultiFileWidget.java | 39 ++++++++++++++----- .../bordertech/wcomponents/util/FileUtil.java | 23 +++-------- .../wcomponents/util/FileUtil_Test.java | 11 ++---- 5 files changed, 44 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13c00d7c2..0e1e438b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Enhancements * Consistent use of try-with-resources when handling streams * Updated AbstractRequest to remove deprecated methods uploadFileItems and readBytes (were protected static). Use StreamUtils instead. +* Replaced org.apache.tika:tika library with org.overviewproject:mime-types in FileUtil to validate uploaded file mime types. ### Bug Fixes diff --git a/wcomponents-core/pom.xml b/wcomponents-core/pom.xml index 6bac339ba..19dd93a28 100755 --- a/wcomponents-core/pom.xml +++ b/wcomponents-core/pom.xml @@ -289,20 +289,9 @@ - org.apache.tika - tika-core - 2.9.4 - - - - org.slf4j - slf4j-api - - - commons-io - commons-io - - + org.overviewproject + mime-types + 2.0.0 diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WMultiFileWidget.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WMultiFileWidget.java index fcef054c3..c8d49514f 100755 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WMultiFileWidget.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/WMultiFileWidget.java @@ -739,16 +739,13 @@ protected void doHandleUploadRequest(final Request request) { // Wrap the file item FileItemWrap wrap = new FileItemWrap(items[0]); - // if fileType is supplied then validate it - if (hasFileTypes() && !FileUtil.validateFileType(wrap, getFileTypes())) { - String invalidMessage = FileUtil.getInvalidFileTypeMessage(getFileTypes()); - throw new SystemException(invalidMessage); + // Validate the file type + if (hasFileTypes()) { + doHandleUploadedFileTypeValidation(wrap); } - - // if fileSize is supplied then validate it - if (hasMaxFileSize() && !FileUtil.validateFileSize(wrap, getMaxFileSize())) { - String invalidMessage = FileUtil.getInvalidFileSizeMessage(getMaxFileSize()); - throw new SystemException(invalidMessage); + // Validate the file size + if (hasMaxFileSize()) { + doHandleUploadedFileSizeValidation(wrap); } FileWidgetUpload file = new FileWidgetUpload(fileId, wrap); @@ -759,6 +756,30 @@ protected void doHandleUploadRequest(final Request request) { setNewUpload(true); } + /** + * Perform file type validation on the uploaded file. + * + * @param wrap the file item to validate + */ + protected void doHandleUploadedFileTypeValidation(final FileItemWrap wrap) { + if (!FileUtil.validateFileType(wrap, getFileTypes())) { + String invalidMessage = FileUtil.getInvalidFileTypeMessage(getFileTypes()); + throw new SystemException(invalidMessage); + } + } + + /** + * Perform file size validation on the uploaded file. + * + * @param wrap the file item to validate + */ + protected void doHandleUploadedFileSizeValidation(final FileItemWrap wrap) { + if (!FileUtil.validateFileSize(wrap, getMaxFileSize())) { + String invalidMessage = FileUtil.getInvalidFileSizeMessage(getMaxFileSize()); + throw new SystemException(invalidMessage); + } + } + /** * Handle the thumb nail request. * diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java index 5db00b0e7..fe9a74435 100644 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java @@ -2,6 +2,7 @@ import com.github.bordertech.wcomponents.file.File; import com.github.bordertech.wcomponents.file.FileItemWrap; +import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.text.DecimalFormat; @@ -10,9 +11,8 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.tika.Tika; -import org.apache.tika.metadata.Metadata; -import org.apache.tika.metadata.TikaCoreProperties; +import org.overviewproject.mime_types.GetBytesException; +import org.overviewproject.mime_types.MimeTypeDetector; /** * Utility methods for {@link File}. @@ -97,20 +97,9 @@ public static boolean validateFileType(final FileItemWrap newFile, final List Date: Wed, 14 Jan 2026 15:38:45 +1000 Subject: [PATCH 08/14] Updated FileUtil to make file extension and mime type validation case insensitive --- CHANGELOG.md | 1 + .../bordertech/wcomponents/util/FileUtil.java | 21 +++++++++++------- .../wcomponents/util/FileUtil_Test.java | 22 +++++++++++++++++++ 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e1e438b5..2a2997cc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Consistent use of try-with-resources when handling streams * Updated AbstractRequest to remove deprecated methods uploadFileItems and readBytes (were protected static). Use StreamUtils instead. * Replaced org.apache.tika:tika library with org.overviewproject:mime-types in FileUtil to validate uploaded file mime types. +* Updated FileUtil to make file extension and mime type validation case insensitive. ### Bug Fixes diff --git a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java index fe9a74435..0afc420a8 100644 --- a/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java +++ b/wcomponents-core/src/main/java/com/github/bordertech/wcomponents/util/FileUtil.java @@ -51,36 +51,41 @@ public static boolean validateFileType(final FileItemWrap newFile, final List fileExts = fileTypes.stream() .filter(fileType -> fileType.startsWith(".")) .collect(Collectors.toList()); - // filter mime types from fileTypes. + // Filter mime types final List fileMimes = fileTypes.stream() .filter(fileType -> !fileExts.contains(fileType)) .collect(Collectors.toList()); // First validate newFile against fileExts list // If extensions are supplied, then check if newFile has a name - if (fileExts.size() > 0 && newFile.getName() != null) { + if (!fileExts.isEmpty() && newFile.getName() != null) { // Then see if newFile has an extension String[] split = newFile.getName().split(("\\.(?=[^\\.]+$)")); // If it exists, then check if it matches supplied extension(s) if (split.length == 2 - && fileExts.stream().anyMatch(fileExt -> fileExt.equals("." + split[1]))) { + && fileExts.stream().anyMatch(fileExt -> fileExt.equalsIgnoreCase("." + split[1]))) { return true; } } // If extension match is unsucessful, then move to fileMimes list - if (fileMimes.size() > 0) { + if (!fileMimes.isEmpty()) { final String mimeType = getFileMimeType(newFile); + if (mimeType == null) { + return false; + } LOG.debug("File mime-type is: " + mimeType); for (String fileMime : fileMimes) { - if (StringUtils.equals(mimeType, fileMime)) { + if (mimeType.equalsIgnoreCase(fileMime)) { return true; } if (fileMime.indexOf("*") == fileMime.length() - 1) { - fileMime = fileMime.substring(0, fileMime.length() - 1); - if (mimeType.indexOf(fileMime) == 0) { + String fileMimePrefix = fileMime.substring(0, fileMime.length() - 1).toLowerCase(); + String lcMimeType = mimeType.toLowerCase(); + if (lcMimeType.startsWith(fileMimePrefix)) { return true; } } @@ -93,7 +98,7 @@ public static boolean validateFileType(final FileItemWrap newFile, final List Date: Fri, 16 Jan 2026 13:33:21 +1000 Subject: [PATCH 09/14] Update project dependencies to latest versions --- CHANGELOG.md | 17 +++++++++++++++++ pom.xml | 4 ++-- wcomponents-core/pom.xml | 20 ++++++++++++++------ wcomponents-test-lib/pom.xml | 10 +++++----- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a2997cc1..6a961f4eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,23 @@ * Updated AbstractRequest to remove deprecated methods uploadFileItems and readBytes (were protected static). Use StreamUtils instead. * Replaced org.apache.tika:tika library with org.overviewproject:mime-types in FileUtil to validate uploaded file mime types. * Updated FileUtil to make file extension and mime type validation case insensitive. +* Updated the following dependencies: + * wcomponents-core: + * com.google.code.gson:gson from 2.13.1 to 2.13.2 + * org.apache.commons:commons-lang3 from 3.18.0 to 3.20.0 + * commons-io:commons-io from 2.19.0 to 2.21.0 + * com.google.errorprone:error_prone_annotations from 2.39.0 to 2.46.0 + * org.apache.httpcomponents.client5:httpclient5 from 5.5 to 5.6 + * org.apache.httpcomponents.core5:httpcore5 from 5.3.4 to 5.4 + * wcomponents-test-lib: + * io.github.bonigarcia:webdrivermanager from 6.1.0 to 6.3.3 + * org.apache.commons:commons-compress from 1.27.1 to 1.28.0 + * commons-codec:commons-codec from 1.18.0 to 1.20.0 + * com.google.guava:guava from 33.4.8-jre to 33.5.0-jre + * net.java.dev.jna:jna from 5.17.0 to 5.18.1 + * wcomponents-bundle: + * org.ehcache:ehcahce from 3.10.8 to 3.11.1 + * org.glassfish.jaxb:jaxb-runtime from 4.0.5 to 4.0.6 ### Bug Fixes diff --git a/pom.xml b/pom.xml index 14dd0afc3..a8313fcac 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ org.ehcache ehcache - 3.10.8 + 3.11.1 @@ -107,7 +107,7 @@ org.glassfish.jaxb jaxb-runtime - 4.0.5 + 4.0.6 diff --git a/wcomponents-core/pom.xml b/wcomponents-core/pom.xml index 19dd93a28..d5043b201 100755 --- a/wcomponents-core/pom.xml +++ b/wcomponents-core/pom.xml @@ -218,7 +218,7 @@ com.google.code.gson gson - 2.13.1 + 2.13.2 com.google.errorprone @@ -248,6 +248,14 @@ xerces xercesImpl + + commons-io + commons-io + + + org.apache.httpcomponents.core5 + httpcore5 + @@ -308,22 +316,22 @@ org.apache.commons commons-lang3 - 3.18.0 + 3.20.0 commons-io commons-io - 2.19.0 + 2.21.0 com.google.errorprone error_prone_annotations - 2.39.0 + 2.46.0 org.apache.httpcomponents.client5 httpclient5 - 5.5 + 5.6 org.slf4j @@ -334,7 +342,7 @@ org.apache.httpcomponents.core5 httpcore5 - 5.3.4 + 5.4 diff --git a/wcomponents-test-lib/pom.xml b/wcomponents-test-lib/pom.xml index 4bb9a2097..afab26971 100755 --- a/wcomponents-test-lib/pom.xml +++ b/wcomponents-test-lib/pom.xml @@ -79,7 +79,7 @@ io.github.bonigarcia webdrivermanager - 6.1.0 + 6.3.3 @@ -131,7 +131,7 @@ org.apache.commons commons-compress - 1.27.1 + 1.28.0 @@ -151,12 +151,12 @@ commons-codec commons-codec - 1.18.0 + 1.20.0 com.google.guava guava - 33.4.8-jre + 33.5.0-jre com.google.errorprone @@ -167,7 +167,7 @@ net.java.dev.jna jna - 5.17.0 + 5.18.1 From 930648d5594484b05b9f1ed3e124d9b59b2c56e3 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Fri, 16 Jan 2026 16:11:48 +1100 Subject: [PATCH 10/14] Update missed example with try-with-resources --- .../wcomponents/examples/theme/WMultiFileWidgetExample.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/theme/WMultiFileWidgetExample.java b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/theme/WMultiFileWidgetExample.java index f2d4df488..b8a9f5119 100755 --- a/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/theme/WMultiFileWidgetExample.java +++ b/wcomponents-examples/src/main/java/com/github/bordertech/wcomponents/examples/theme/WMultiFileWidgetExample.java @@ -204,9 +204,7 @@ private void appendFileDetails(final StringBuffer buf, final WMultiFileWidget fi for (FileWidgetUpload file : files) { String streamedSize; - try { - InputStream in = file.getFile().getInputStream(); - + try (InputStream in = file.getFile().getInputStream()) { int size = 0; while (in.read() >= 0) { size++; From deb1ed47fa861633756192c388e97da5131aefa6 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Fri, 16 Jan 2026 16:49:49 +1000 Subject: [PATCH 11/14] Add code-coverage module --- code-coverage/pom.xml | 69 +++++++++++++++++++++++++++++++++++++++++++ pom.xml | 1 + 2 files changed, 70 insertions(+) create mode 100644 code-coverage/pom.xml diff --git a/code-coverage/pom.xml b/code-coverage/pom.xml new file mode 100644 index 000000000..fa9f960c8 --- /dev/null +++ b/code-coverage/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + + com.github.bordertech.wcomponents + wcomponents-parent + 1.5.39-SNAPSHOT + ../pom.xml + + + code-coverage + code-coverage + + jar + + + + com.github.bordertech.wcomponents + wcomponents-core + ${project.version} + + + com.github.bordertech.wcomponents + wcomponents-examples + ${project.version} + + + com.github.bordertech.wcomponents + wcomponents-test-lib + ${project.version} + + + com.github.bordertech.wcomponents + wcomponents-lde + ${project.version} + + + + + + + + + org.jacoco + jacoco-maven-plugin + + + report-aggregate + test + + report-aggregate + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index a8313fcac..986b82860 100644 --- a/pom.xml +++ b/pom.xml @@ -217,6 +217,7 @@ wcomponents-theme wcomponents-xslt wcomponents-bundle + code-coverage From 07e726251c0fd016b23011ca720f0608f36688e8 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Fri, 16 Jan 2026 17:03:19 +1000 Subject: [PATCH 12/14] Add version to deploy plugin in code-coverage module --- code-coverage/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/code-coverage/pom.xml b/code-coverage/pom.xml index fa9f960c8..0cae18b87 100644 --- a/code-coverage/pom.xml +++ b/code-coverage/pom.xml @@ -58,6 +58,7 @@ org.apache.maven.plugins maven-deploy-plugin + 3.1.4 true From 71b2464decf35d043b57e0630ef957734a578aa9 Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Fri, 16 Jan 2026 17:28:21 +1000 Subject: [PATCH 13/14] Add wait for quality gate on sonar scan to fail build --- .github/workflows/github-actions-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-build.yml b/.github/workflows/github-actions-build.yml index 05d03519e..062f1ea42 100644 --- a/.github/workflows/github-actions-build.yml +++ b/.github/workflows/github-actions-build.yml @@ -48,7 +48,7 @@ jobs: echo "Sonar secure variables NOT available" else echo "Sonar secure variables ARE available" - mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey="bordertech-wcomponents" -Dsonar.organization="bordertech-github" -Dsonar.host.url="https://sonarcloud.io" + mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey="bordertech-wcomponents" -Dsonar.organization="bordertech-github" -Dsonar.host.url="https://sonarcloud.io" -Dsonar.qualitygate.wait=true fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From d6b53cad62557ee9fc6654c5fda08773dd3cde7d Mon Sep 17 00:00:00 2001 From: Jonathan Austin Date: Mon, 19 Jan 2026 15:29:54 +1000 Subject: [PATCH 14/14] EOL for new code-coverage pom.xml --- code-coverage/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code-coverage/pom.xml b/code-coverage/pom.xml index 0cae18b87..1e2fcf881 100644 --- a/code-coverage/pom.xml +++ b/code-coverage/pom.xml @@ -67,4 +67,4 @@ - \ No newline at end of file +