diff --git a/providers/multipart/pom.xml b/providers/multipart/pom.xml index 2d94ae74d18..4cf8d89ba98 100644 --- a/providers/multipart/pom.xml +++ b/providers/multipart/pom.xml @@ -70,6 +70,11 @@ ${project.version} test + + junit + junit + test + diff --git a/providers/multipart/src/main/java/org/jboss/resteasy/plugins/providers/multipart/HeaderFlushedOutputStream.java b/providers/multipart/src/main/java/org/jboss/resteasy/plugins/providers/multipart/HeaderFlushedOutputStream.java index f9187965628..7ba885faf29 100644 --- a/providers/multipart/src/main/java/org/jboss/resteasy/plugins/providers/multipart/HeaderFlushedOutputStream.java +++ b/providers/multipart/src/main/java/org/jboss/resteasy/plugins/providers/multipart/HeaderFlushedOutputStream.java @@ -11,8 +11,8 @@ * @version $Revision: 1 $ */ public class HeaderFlushedOutputStream extends OutputStream { - private MultivaluedMap headers; - private OutputStream stream; + private final MultivaluedMap headers; + private final OutputStream stream; private boolean headersFlushed = false; public HeaderFlushedOutputStream(final MultivaluedMap headers, diff --git a/providers/multipart/src/main/java/org/jboss/resteasy/plugins/providers/multipart/Mime4JWorkaround.java b/providers/multipart/src/main/java/org/jboss/resteasy/plugins/providers/multipart/Mime4JWorkaround.java index 0131d6c96a4..4486ceb6eb3 100644 --- a/providers/multipart/src/main/java/org/jboss/resteasy/plugins/providers/multipart/Mime4JWorkaround.java +++ b/providers/multipart/src/main/java/org/jboss/resteasy/plugins/providers/multipart/Mime4JWorkaround.java @@ -33,6 +33,8 @@ import org.apache.james.mime4j.storage.ThresholdStorageProvider; import org.apache.james.mime4j.stream.BodyDescriptorBuilder; import org.apache.james.mime4j.stream.MimeConfig; +import org.jboss.resteasy.plugins.providers.multipart.i18n.LogMessages; +import org.jboss.resteasy.spi.config.Configuration; import org.jboss.resteasy.spi.config.ConfigurationFactory; /** @@ -40,6 +42,9 @@ * Alter said code to use Mime4JWorkaroundBinaryEntityBuilder instead of EntityBuilder. */ public class Mime4JWorkaround { + static final String MEM_THRESHOLD_PROPERTY = "org.jboss.resteasy.plugins.providers.multipart.memoryThreshold"; + static final int DEFAULT_MEM_THRESHOLD = 1024; + /** * This is a rough copy of DefaultMessageBuilder.parseMessage() modified to use a Mime4JWorkaround as the contentHandler instead * of an EntityBuilder. @@ -64,7 +69,7 @@ public static Message parseMessage(InputStream is) throws IOException, MimeIOExc storageProvider = DefaultStorageProvider.getInstance(); } else { StorageProvider backend = new CustomTempFileStorageProvider(); - storageProvider = new ThresholdStorageProvider(backend, 1024); + storageProvider = new ThresholdStorageProvider(backend, getMemThreshold()); } BodyFactory bf = new StorageBodyFactory(storageProvider, mon); @@ -82,6 +87,25 @@ public static Message parseMessage(InputStream is) throws IOException, MimeIOExc } } + static int getMemThreshold() + { + try + { + Configuration cfg = ConfigurationFactory.getInstance().getConfiguration(); + int threshold = Integer.parseInt(cfg.getOptionalValue(MEM_THRESHOLD_PROPERTY, String.class).orElse( + Integer.toString(DEFAULT_MEM_THRESHOLD))); + if (threshold > -1) + { + return threshold; + } + LogMessages.LOGGER.debugf("Negative threshold, %s, specified. Using default value", threshold); + } + catch (Exception e) + { + LogMessages.LOGGER.debug("Exception caught parsing memory threshold. Using default value.", e); + } + return DEFAULT_MEM_THRESHOLD; + } /** * A custom TempFileStorageProvider that do no set deleteOnExit on temp files, diff --git a/providers/multipart/src/test/java/org/jboss/resteasy/plugins/providers/multipart/Mime4JWorkaroundTest.java b/providers/multipart/src/test/java/org/jboss/resteasy/plugins/providers/multipart/Mime4JWorkaroundTest.java new file mode 100644 index 00000000000..eaf4550e75a --- /dev/null +++ b/providers/multipart/src/test/java/org/jboss/resteasy/plugins/providers/multipart/Mime4JWorkaroundTest.java @@ -0,0 +1,79 @@ +package org.jboss.resteasy.plugins.providers.multipart; + +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class Mime4JWorkaroundTest { + + private static final Map preTestProperties = new HashMap<>(); + + @BeforeClass + public static void saveCurrentStateOfMemThresholdProperty() + { + if (System.getProperties().containsKey(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY)) + { + String value = System.getProperty(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY); + preTestProperties.put(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY, value); + } + } + + @AfterClass + public static void resetPropertiesToPreTestValues() + { + if (!preTestProperties.containsKey(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY)) + { + System.clearProperty(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY); + } + else + { + preTestProperties.forEach(System::setProperty); + } + } + + @Before + public void unsetMemThresholdProperty() + { + System.clearProperty(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY); + } + + @Test + public void testMemThresholdDefault() + { + assertEquals(Mime4JWorkaround.DEFAULT_MEM_THRESHOLD, Mime4JWorkaround.getMemThreshold()); + } + + @Test + public void testMemThresholdConfigProperty() + { + System.setProperty(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY, "2048"); + assertEquals(2048, Mime4JWorkaround.getMemThreshold()); + } + + @Test + public void testInvalidMemThresholdConfigPropertyReturnsDefault_NegativeNumber() + { + System.setProperty(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY, "-2048"); + assertEquals(Mime4JWorkaround.DEFAULT_MEM_THRESHOLD, Mime4JWorkaround.getMemThreshold()); + } + + @Test + public void testInvalidMemThresholdConfigPropertyReturnsDefault_DecimalNumber() + { + System.setProperty(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY, "2048.2"); + assertEquals(Mime4JWorkaround.DEFAULT_MEM_THRESHOLD, Mime4JWorkaround.getMemThreshold()); + } + + @Test + public void testInvalidMemThresholdConfigPropertyReturnsDefault_NotANumber() + { + System.setProperty(Mime4JWorkaround.MEM_THRESHOLD_PROPERTY, "Infinity"); + assertEquals(Mime4JWorkaround.DEFAULT_MEM_THRESHOLD, Mime4JWorkaround.getMemThreshold()); + } +} diff --git a/resteasy-client/src/main/java/org/jboss/resteasy/client/jaxrs/engines/ManualClosingApacheHttpClient43Engine.java b/resteasy-client/src/main/java/org/jboss/resteasy/client/jaxrs/engines/ManualClosingApacheHttpClient43Engine.java index 17d9a048859..c6ba83da5bc 100644 --- a/resteasy-client/src/main/java/org/jboss/resteasy/client/jaxrs/engines/ManualClosingApacheHttpClient43Engine.java +++ b/resteasy-client/src/main/java/org/jboss/resteasy/client/jaxrs/engines/ManualClosingApacheHttpClient43Engine.java @@ -54,6 +54,9 @@ public class ManualClosingApacheHttpClient43Engine implements ApacheHttpClientEngine { + static final String FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY = + "org.jboss.resteasy.client.jaxrs.engines.fileUploadInMemoryThreshold"; + /** * Used to build temp file prefix. */ @@ -123,39 +126,58 @@ public String run() throws Exception public ManualClosingApacheHttpClient43Engine() { - this.httpClient = createDefaultHttpClient(); - this.allowClosingHttpClient = true; + this(null, null, true, null); } public ManualClosingApacheHttpClient43Engine(final HttpHost defaultProxy) { - this.defaultProxy = defaultProxy; - this.httpClient = createDefaultHttpClient(); - this.allowClosingHttpClient = true; + this(null, null, true, defaultProxy); } public ManualClosingApacheHttpClient43Engine(final HttpClient httpClient) { - this.httpClient = httpClient; - this.allowClosingHttpClient = true; + this(httpClient, null, true, null); } public ManualClosingApacheHttpClient43Engine(final HttpClient httpClient, final boolean closeHttpClient) { - if (closeHttpClient && !(httpClient instanceof CloseableHttpClient)) + this(httpClient, null, closeHttpClient, null); + } + + public ManualClosingApacheHttpClient43Engine(final HttpClient httpClient, + final HttpContextProvider httpContextProvider) + { + this(httpClient, httpContextProvider, true, null); + } + + private ManualClosingApacheHttpClient43Engine(final HttpClient httpClient, + final HttpContextProvider httpContextProvider, final boolean closeHttpClient, final HttpHost defaultProxy) + { + this.httpClient = httpClient != null ? httpClient : createDefaultHttpClient(); + if (closeHttpClient && !(this.httpClient instanceof CloseableHttpClient)) { throw new IllegalArgumentException( "httpClient must be a CloseableHttpClient instance in order for allowing engine to close it!"); } - this.httpClient = httpClient; + this.httpContextProvider = httpContextProvider; this.allowClosingHttpClient = closeHttpClient; - } + this.defaultProxy = defaultProxy; - public ManualClosingApacheHttpClient43Engine(final HttpClient httpClient, final HttpContextProvider httpContextProvider) - { - this.httpClient = httpClient; - this.httpContextProvider = httpContextProvider; - this.allowClosingHttpClient = true; + try + { + int threshold = Integer.parseInt(ConfigurationFactory.getInstance().getConfiguration() + .getOptionalValue(FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY, String.class) + .orElse("1")); + if (threshold > -1) + { + this.fileUploadInMemoryThresholdLimit = threshold; + } + LogMessages.LOGGER.debugf("Negative threshold, %s, specified. Using default value", threshold); + } + catch (Exception e) + { + LogMessages.LOGGER.debug("Exception caught parsing memory threshold. Using default value.", e); + } } /** diff --git a/resteasy-client/src/test/java/org/jboss/resteasy/client/jaxrs/engines/ManualClosingApacheHttpClient43EngineTest.java b/resteasy-client/src/test/java/org/jboss/resteasy/client/jaxrs/engines/ManualClosingApacheHttpClient43EngineTest.java new file mode 100644 index 00000000000..17bb86935e6 --- /dev/null +++ b/resteasy-client/src/test/java/org/jboss/resteasy/client/jaxrs/engines/ManualClosingApacheHttpClient43EngineTest.java @@ -0,0 +1,79 @@ +package org.jboss.resteasy.client.jaxrs.engines; + +import static org.junit.Assert.assertEquals; +import static org.jboss.resteasy.client.jaxrs.engines.ManualClosingApacheHttpClient43Engine.FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ManualClosingApacheHttpClient43EngineTest { + + private static final Map preTestProperties = new HashMap<>(); + + @BeforeClass + public static void saveCurrentStateOfMemThresholdProperty() + { + if (System.getProperties().containsKey(FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY)) + { + String value = System.getProperty(FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY); + preTestProperties.put(FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY, value); + } + } + + @AfterClass + public static void resetPropertiesToPreTestValues() + { + if (!preTestProperties.containsKey(FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY)) + { + System.clearProperty(FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY); + } + else + { + preTestProperties.forEach(System::setProperty); + } + } + + @Before + public void unsetMemThresholdProperty() + { + System.clearProperty(ManualClosingApacheHttpClient43Engine.FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY); + } + @Test + public void testMemThresholdDefault() + { + assertEquals(1, new ManualClosingApacheHttpClient43Engine().getFileUploadInMemoryThresholdLimit()); + } + + @Test + public void testMemThresholdConfigProperty() + { + System.setProperty(ManualClosingApacheHttpClient43Engine.FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY, "8"); + assertEquals(8, new ManualClosingApacheHttpClient43Engine().getFileUploadInMemoryThresholdLimit()); + } + + @Test + public void testInvalidMemThresholdConfigPropertyReturnsDefault_NegativeNumber() + { + System.setProperty(ManualClosingApacheHttpClient43Engine.FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY, "-8"); + assertEquals(1, new ManualClosingApacheHttpClient43Engine().getFileUploadInMemoryThresholdLimit()); + } + + @Test + public void testInvalidMemThresholdConfigPropertyReturnsDefault_DecimalNumber() + { + System.setProperty(ManualClosingApacheHttpClient43Engine.FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY, "2048.2"); + assertEquals(1, new ManualClosingApacheHttpClient43Engine().getFileUploadInMemoryThresholdLimit()); + } + + @Test + public void testInvalidMemThresholdConfigPropertyReturnsDefault_NotANumber() + { + System.setProperty(ManualClosingApacheHttpClient43Engine.FILE_UPLOAD_IN_MEMORY_THRESHOLD_PROPERTY, "Infinity"); + assertEquals(1, new ManualClosingApacheHttpClient43Engine().getFileUploadInMemoryThresholdLimit()); + } +}