diff --git a/engines/base/src/main/java/org/alfresco/transform/base/probes/ProbeTransform.java b/engines/base/src/main/java/org/alfresco/transform/base/probes/ProbeTransform.java index ecd8f605e..bc9e36f55 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/probes/ProbeTransform.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/probes/ProbeTransform.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * Copyright (C) 2005 - 2026 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -41,6 +41,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.UnaryOperator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -114,6 +115,14 @@ public long getMaxTime() public ProbeTransform(String sourceFilename, String sourceMimetype, String targetMimetype, Map transformOptions, long expectedLength, long plusOrMinus, int livenessPercent, long maxTransforms, long maxTransformSeconds, long livenessTransformPeriodSeconds) + { + this(sourceFilename, sourceMimetype, targetMimetype, transformOptions, expectedLength, plusOrMinus, + livenessPercent, maxTransforms, maxTransformSeconds, livenessTransformPeriodSeconds, System::getenv); + } + + ProbeTransform(String sourceFilename, String sourceMimetype, String targetMimetype, Map transformOptions, + long expectedLength, long plusOrMinus, int livenessPercent, long maxTransforms, long maxTransformSeconds, + long livenessTransformPeriodSeconds, UnaryOperator envReader) { this.sourceFilename = sourceFilename; this.sourceMimetype = sourceMimetype; @@ -122,29 +131,54 @@ public ProbeTransform(String sourceFilename, String sourceMimetype, String targe minExpectedLength = Math.max(0, expectedLength - plusOrMinus); maxExpectedLength = expectedLength + plusOrMinus; - this.livenessPercent = (int) getPositiveLongEnv("livenessPercent", livenessPercent); - maxTransformCount = getPositiveLongEnv("maxTransforms", maxTransforms); - maxTransformTime = getPositiveLongEnv("maxTransformSeconds", maxTransformSeconds) * 1000; + this.livenessPercent = (int) getPositiveLongEnv("livenessPercent", livenessPercent, envReader); + maxTransformCount = getNonNegativeLongEnv("maxTransforms", maxTransforms, envReader); + maxTransformTime = getNonNegativeLongEnv("maxTransformSeconds", maxTransformSeconds, envReader) * 1000; livenessTransformPeriod = getPositiveLongEnv("livenessTransformPeriodSeconds", - livenessTransformPeriodSeconds) * 1000; - livenessTransformEnabled = getBooleanEnvVar("livenessTransformEnabled", false); + livenessTransformPeriodSeconds, envReader) * 1000; + livenessTransformEnabled = getBooleanEnvVar("livenessTransformEnabled", false, envReader); } - private boolean getBooleanEnvVar(final String name, final boolean defaultValue) + private boolean getBooleanEnvVar(final String name, final boolean defaultValue, UnaryOperator envReader) { try { - return Boolean.parseBoolean(System.getenv(name)); + return Boolean.parseBoolean(envReader.apply(name)); } catch (Exception ignore) - {} + { + logger.warn("Probe: {} environment variable value is not a valid boolean, using default {}", name, defaultValue); + } return defaultValue; } - private long getPositiveLongEnv(String name, long defaultValue) + private long getNonNegativeLongEnv(String name, long defaultValue, UnaryOperator envReader) + { + String env = envReader.apply(name); + if (env != null) + { + try + { + long l = Long.parseLong(env); + if (l >= 0) + { + logger.trace("Probe: {}={}", name, l); + return l; + } + } + catch (NumberFormatException ignore) + { + logger.warn("Probe: {} environment variable value is not a valid number, using default {}", name, defaultValue); + } + } + logger.trace("Probe: {}={}", name, defaultValue); + return defaultValue; + } + + private long getPositiveLongEnv(String name, long defaultValue, UnaryOperator envReader) { long l = -1; - String env = System.getenv(name); + String env = envReader.apply(name); if (env != null) { try @@ -152,7 +186,9 @@ private long getPositiveLongEnv(String name, long defaultValue) l = Long.parseLong(env); } catch (NumberFormatException ignore) - {} + { + logger.warn("Probe: {} environment variable value is not a valid number, using default {}", name, defaultValue); + } } if (l <= 0) { diff --git a/engines/base/src/test/java/org/alfresco/transform/base/probes/ProbeTransformTest.java b/engines/base/src/test/java/org/alfresco/transform/base/probes/ProbeTransformTest.java new file mode 100644 index 000000000..f044836d4 --- /dev/null +++ b/engines/base/src/test/java/org/alfresco/transform/base/probes/ProbeTransformTest.java @@ -0,0 +1,139 @@ +/* + * #%L + * Alfresco Transform Core + * %% + * Copyright (C) 2005 - 2026 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.transform.base.probes; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.UnaryOperator; + +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import org.alfresco.transform.exceptions.TransformException; + +class ProbeTransformTest +{ + private static final long DEFAULT_MAX_TRANSFORMS = 1024; + private static final long DEFAULT_MAX_TRANSFORM_SECONDS = 120; + + private ProbeTransform createProbe(long maxTransforms, long maxTransformSeconds) + { + return createProbe(maxTransforms, maxTransformSeconds, name -> null); + } + + private ProbeTransform createProbe(long maxTransforms, long maxTransformSeconds, UnaryOperator envReader) + { + return new ProbeTransform("probe.html", "text/html", "text/plain", Map.of(), + 107, 30, 150, maxTransforms, maxTransformSeconds, 600, envReader); + } + + private void setTransformCount(ProbeTransform probe, long count) + { + ((AtomicLong) ReflectionTestUtils.getField(probe, "transformCount")).set(count); + } + + @Test + void whenMaxTransformsIsZeroTransformCountCheckIsDisabled() + { + ProbeTransform probe = createProbe(0, DEFAULT_MAX_TRANSFORM_SECONDS); + setTransformCount(probe, Long.MAX_VALUE); + + assertDoesNotThrow(() -> probe.doTransformOrNothing(true, null)); + } + + @Test + void whenMaxTransformsIsPositiveTransformCountCheckIsEnforced() + { + ProbeTransform probe = createProbe(5, DEFAULT_MAX_TRANSFORM_SECONDS); + setTransformCount(probe, 6); + + assertThrows(TransformException.class, () -> probe.doTransformOrNothing(true, null)); + } + + @Test + void whenMaxTransformsEnvVarIsZeroItIsAcceptedAndDisablesCheck() + { + ProbeTransform probe = createProbe(DEFAULT_MAX_TRANSFORMS, DEFAULT_MAX_TRANSFORM_SECONDS, + name -> "maxTransforms".equals(name) ? "0" : null); + setTransformCount(probe, Long.MAX_VALUE); + + assertDoesNotThrow(() -> probe.doTransformOrNothing(true, null)); + } + + @Test + void whenMaxTransformsEnvVarIsNegativeItFallsBackToDefault() + { + // env var "-1" is invalid, so default (5) is used and the check is enforced + ProbeTransform probe = createProbe(5, DEFAULT_MAX_TRANSFORM_SECONDS, + name -> "maxTransforms".equals(name) ? "-1" : null); + setTransformCount(probe, 6); + + assertThrows(TransformException.class, () -> probe.doTransformOrNothing(true, null)); + } + + @Test + void whenMaxTransformSecondsIsZeroTimeCheckIsDisabled() + { + ProbeTransform probe = createProbe(DEFAULT_MAX_TRANSFORMS, 0); + probe.recordTransformTime(Long.MAX_VALUE); + + assertDoesNotThrow(() -> probe.doTransformOrNothing(true, null)); + } + + @Test + void whenMaxTransformSecondsIsPositiveTimeCheckIsEnforced() + { + ProbeTransform probe = createProbe(DEFAULT_MAX_TRANSFORMS, 1); + probe.recordTransformTime(2000); + + assertThrows(TransformException.class, () -> probe.doTransformOrNothing(true, null)); + } + + @Test + void whenMaxTransformSecondsEnvVarIsZeroItIsAcceptedAndDisablesCheck() + { + ProbeTransform probe = createProbe(DEFAULT_MAX_TRANSFORMS, DEFAULT_MAX_TRANSFORM_SECONDS, + name -> "maxTransformSeconds".equals(name) ? "0" : null); + probe.recordTransformTime(Long.MAX_VALUE); + + assertDoesNotThrow(() -> probe.doTransformOrNothing(true, null)); + } + + @Test + void whenMaxTransformSecondsEnvVarIsNegativeItFallsBackToDefault() + { + // env var "-1" is invalid, so default (1 second) is used and the check is enforced + ProbeTransform probe = createProbe(DEFAULT_MAX_TRANSFORMS, 1, + name -> "maxTransformSeconds".equals(name) ? "-1" : null); + probe.recordTransformTime(2000); + + assertThrows(TransformException.class, () -> probe.doTransformOrNothing(true, null)); + } +}