From 95c0f1108fa601d5482019bd228db1d7cc437c3a Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 7 Oct 2020 15:02:50 +0200 Subject: [PATCH] Construct StringWriter instances with appropriate initial size Closes gh-25789 --- .../freemarker/FreeMarkerTemplateUtils.java | 4 +- .../springframework/util/FileCopyUtils.java | 20 ++++++---- .../org/springframework/util/StreamUtils.java | 37 +++++++++++-------- .../MappingJackson2MessageConverter.java | 12 +++--- .../MarshallingMessageConverter.java | 6 +-- .../MappingJackson2MessageConverter.java | 16 ++++---- .../MarshallingMessageConverter.java | 13 ++++--- 7 files changed, 60 insertions(+), 48 deletions(-) diff --git a/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerTemplateUtils.java b/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerTemplateUtils.java index 2c9fa7e478..e1ca06bf99 100644 --- a/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerTemplateUtils.java +++ b/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerTemplateUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,7 @@ public abstract class FreeMarkerTemplateUtils { public static String processTemplateIntoString(Template template, Object model) throws IOException, TemplateException { - StringWriter result = new StringWriter(); + StringWriter result = new StringWriter(1024); template.process(model, result); return result.toString(); } diff --git a/spring-core/src/main/java/org/springframework/util/FileCopyUtils.java b/spring-core/src/main/java/org/springframework/util/FileCopyUtils.java index 22d5767962..51753f3aa2 100644 --- a/spring-core/src/main/java/org/springframework/util/FileCopyUtils.java +++ b/spring-core/src/main/java/org/springframework/util/FileCopyUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,9 +40,13 @@ import java.io.Writer; * @author Juergen Hoeller * @since 06.10.2003 * @see StreamUtils + * @see FileSystemUtils */ public abstract class FileCopyUtils { + /** + * The default buffer size used when copying bytes. + */ public static final int BUFFER_SIZE = StreamUtils.BUFFER_SIZE; @@ -184,15 +188,15 @@ public abstract class FileCopyUtils { Assert.notNull(out, "No Writer specified"); try { - int byteCount = 0; + int charCount = 0; char[] buffer = new char[BUFFER_SIZE]; - int bytesRead = -1; - while ((bytesRead = in.read(buffer)) != -1) { - out.write(buffer, 0, bytesRead); - byteCount += bytesRead; + int charsRead; + while ((charsRead = in.read(buffer)) != -1) { + out.write(buffer, 0, charsRead); + charCount += charsRead; } out.flush(); - return byteCount; + return charCount; } finally { try { @@ -209,7 +213,7 @@ public abstract class FileCopyUtils { } /** - * Copy the contents of the given String to the given output Writer. + * Copy the contents of the given String to the given Writer. * Closes the writer when done. * @param in the String to copy from * @param out the Writer to copy to diff --git a/spring-core/src/main/java/org/springframework/util/StreamUtils.java b/spring-core/src/main/java/org/springframework/util/StreamUtils.java index 8023a6b660..6ce72cd283 100644 --- a/spring-core/src/main/java/org/springframework/util/StreamUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StreamUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,9 @@ import java.nio.charset.Charset; */ public abstract class StreamUtils { + /** + * The default buffer size used when copying bytes. + */ public static final int BUFFER_SIZE = 4096; private static final byte[] EMPTY_CONTENT = new byte[0]; @@ -50,7 +53,7 @@ public abstract class StreamUtils { /** * Copy the contents of the given InputStream into a new byte array. - * Leaves the stream open when done. + *

Leaves the stream open when done. * @param in the stream to copy from (may be {@code null} or empty) * @return the new byte array that has been copied to (possibly empty) * @throws IOException in case of I/O errors @@ -67,8 +70,9 @@ public abstract class StreamUtils { /** * Copy the contents of the given InputStream into a String. - * Leaves the stream open when done. + *

Leaves the stream open when done. * @param in the InputStream to copy from (may be {@code null} or empty) + * @param charset the {@link Charset} to use to decode the bytes * @return the String that has been copied to (possibly empty) * @throws IOException in case of I/O errors */ @@ -77,19 +81,19 @@ public abstract class StreamUtils { return ""; } - StringBuilder out = new StringBuilder(); + StringBuilder out = new StringBuilder(BUFFER_SIZE); InputStreamReader reader = new InputStreamReader(in, charset); char[] buffer = new char[BUFFER_SIZE]; - int bytesRead = -1; - while ((bytesRead = reader.read(buffer)) != -1) { - out.append(buffer, 0, bytesRead); + int charsRead; + while ((charsRead = reader.read(buffer)) != -1) { + out.append(buffer, 0, charsRead); } return out.toString(); } /** * Copy the contents of the given byte array to the given OutputStream. - * Leaves the stream open when done. + *

Leaves the stream open when done. * @param in the byte array to copy from * @param out the OutputStream to copy to * @throws IOException in case of I/O errors @@ -99,11 +103,12 @@ public abstract class StreamUtils { Assert.notNull(out, "No OutputStream specified"); out.write(in); + out.flush(); } /** - * Copy the contents of the given String to the given output OutputStream. - * Leaves the stream open when done. + * Copy the contents of the given String to the given OutputStream. + *

Leaves the stream open when done. * @param in the String to copy from * @param charset the Charset * @param out the OutputStream to copy to @@ -111,7 +116,7 @@ public abstract class StreamUtils { */ public static void copy(String in, Charset charset, OutputStream out) throws IOException { Assert.notNull(in, "No input String specified"); - Assert.notNull(charset, "No charset specified"); + Assert.notNull(charset, "No Charset specified"); Assert.notNull(out, "No OutputStream specified"); Writer writer = new OutputStreamWriter(out, charset); @@ -121,7 +126,7 @@ public abstract class StreamUtils { /** * Copy the contents of the given InputStream to the given OutputStream. - * Leaves both streams open when done. + *

Leaves both streams open when done. * @param in the InputStream to copy from * @param out the OutputStream to copy to * @return the number of bytes copied @@ -133,7 +138,7 @@ public abstract class StreamUtils { int byteCount = 0; byte[] buffer = new byte[BUFFER_SIZE]; - int bytesRead = -1; + int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); byteCount += bytesRead; @@ -165,7 +170,7 @@ public abstract class StreamUtils { } long bytesToCopy = end - start + 1; - byte[] buffer = new byte[StreamUtils.BUFFER_SIZE]; + byte[] buffer = new byte[(int) Math.min(StreamUtils.BUFFER_SIZE, bytesToCopy)]; while (bytesToCopy > 0) { int bytesRead = in.read(buffer); if (bytesRead == -1) { @@ -185,7 +190,7 @@ public abstract class StreamUtils { /** * Drain the remaining content of the given InputStream. - * Leaves the InputStream open when done. + *

Leaves the InputStream open when done. * @param in the InputStream to drain * @return the number of bytes read * @throws IOException in case of I/O errors @@ -255,7 +260,7 @@ public abstract class StreamUtils { @Override public void write(byte[] b, int off, int let) throws IOException { // It is critical that we override this method for performance - out.write(b, off, let); + this.out.write(b, off, let); } @Override diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java index 886e72cb0c..4b610643b2 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/MappingJackson2MessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -117,7 +117,7 @@ public class MappingJackson2MessageConverter implements SmartMessageConverter, B /** * Specify the encoding to use when converting to and from text-based * message body content. The default encoding will be "UTF-8". - *

When reading from a a text-based message, an encoding may have been + *

When reading from a text-based message, an encoding may have been * suggested through a special JMS property which will then be preferred * over the encoding set on this MessageConverter instance. * @see #setEncodingPropertyName @@ -283,13 +283,13 @@ public class MappingJackson2MessageConverter implements SmartMessageConverter, B * @return the resulting message * @throws JMSException if thrown by JMS methods * @throws IOException in case of I/O errors - * @see Session#createBytesMessage * @since 4.3 + * @see Session#createBytesMessage */ protected TextMessage mapToTextMessage(Object object, Session session, ObjectWriter objectWriter) throws JMSException, IOException { - StringWriter writer = new StringWriter(); + StringWriter writer = new StringWriter(1024); objectWriter.writeValue(writer, object); return session.createTextMessage(writer.toString()); } @@ -386,7 +386,7 @@ public class MappingJackson2MessageConverter implements SmartMessageConverter, B * sets the resulting value (either a mapped id or the raw Java class name) * into the configured type id message property. * @param object the payload object to set a type id for - * @param message the JMS Message to set the type id on + * @param message the JMS Message on which to set the type id property * @throws JMSException if thrown by JMS methods * @see #getJavaTypeForMessage(javax.jms.Message) * @see #setTypeIdPropertyName(String) @@ -482,7 +482,7 @@ public class MappingJackson2MessageConverter implements SmartMessageConverter, B *

The default implementation parses the configured type id property name * and consults the configured type id mapping. This can be overridden with * a different strategy, e.g. doing some heuristics based on message origin. - * @param message the JMS Message to set the type id on + * @param message the JMS Message from which to get the type id property * @throws JMSException if thrown by JMS methods * @see #setTypeIdOnMessage(Object, javax.jms.Message) * @see #setTypeIdPropertyName(String) diff --git a/spring-jms/src/main/java/org/springframework/jms/support/converter/MarshallingMessageConverter.java b/spring-jms/src/main/java/org/springframework/jms/support/converter/MarshallingMessageConverter.java index 0bd8dd8eac..0bfde85673 100644 --- a/spring-jms/src/main/java/org/springframework/jms/support/converter/MarshallingMessageConverter.java +++ b/spring-jms/src/main/java/org/springframework/jms/support/converter/MarshallingMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,8 +46,6 @@ import org.springframework.util.Assert; * @author Arjen Poutsma * @author Juergen Hoeller * @since 3.0 - * @see org.springframework.jms.core.JmsTemplate#convertAndSend - * @see org.springframework.jms.core.JmsTemplate#receiveAndConvert */ public class MarshallingMessageConverter implements MessageConverter, InitializingBean { @@ -210,7 +208,7 @@ public class MarshallingMessageConverter implements MessageConverter, Initializi protected TextMessage marshalToTextMessage(Object object, Session session, Marshaller marshaller) throws JMSException, IOException, XmlMappingException { - StringWriter writer = new StringWriter(); + StringWriter writer = new StringWriter(1024); Result result = new StreamResult(writer); marshaller.marshal(object, result); return session.createTextMessage(writer.toString()); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java index e3152c5aae..231252fd8c 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/MappingJackson2MessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -140,12 +140,13 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter { } } + @Override protected boolean canConvertFrom(Message message, Class targetClass) { if (targetClass == null || !supportsMimeType(message.getHeaders())) { return false; } - JavaType javaType = this.objectMapper.constructType(targetClass); + JavaType javaType = this.objectMapper.getTypeFactory().constructType(targetClass); AtomicReference causeRef = new AtomicReference(); if (this.objectMapper.canDeserialize(javaType, causeRef)) { return true; @@ -221,6 +222,7 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter { } } else { + // Assuming a text-based source payload if (view != null) { return this.objectMapper.readerWithView(view).forType(javaType).readValue(payload.toString()); } @@ -243,10 +245,9 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter { } Type genericParameterType = param.getNestedGenericParameterType(); Class contextClass = param.getContainingClass(); - Type type = getJavaType(genericParameterType, contextClass); - return this.objectMapper.getTypeFactory().constructType(type); + return getJavaType(genericParameterType, contextClass); } - return this.objectMapper.constructType(targetClass); + return this.objectMapper.getTypeFactory().constructType(targetClass); } private JavaType getJavaType(Type type, Class contextClass) { @@ -329,7 +330,8 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter { payload = out.toByteArray(); } else { - Writer writer = new StringWriter(); + // Assuming a text-based target payload + Writer writer = new StringWriter(1024); if (view != null) { this.objectMapper.writerWithView(view).writeValue(writer, payload); } @@ -387,7 +389,7 @@ public class MappingJackson2MessageConverter extends AbstractMessageConverter { * @return the JSON encoding to use (never {@code null}) */ protected JsonEncoding getJsonEncoding(MimeType contentType) { - if ((contentType != null) && (contentType.getCharset() != null)) { + if (contentType != null && contentType.getCharset() != null) { Charset charset = contentType.getCharset(); for (JsonEncoding encoding : JsonEncoding.values()) { if (charset.name().equals(encoding.getJavaName())) { diff --git a/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java b/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java index d95bedad8b..fb42c52308 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/converter/MarshallingMessageConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,6 +45,8 @@ import org.springframework.util.MimeType; * * @author Arjen Poutsma * @since 4.2 + * @see Marshaller + * @see Unmarshaller */ public class MarshallingMessageConverter extends AbstractMessageConverter { @@ -58,7 +60,8 @@ public class MarshallingMessageConverter extends AbstractMessageConverter { * {@link #setUnmarshaller(Unmarshaller)} to be invoked separately. */ public MarshallingMessageConverter() { - this(new MimeType("application", "xml"), new MimeType("text", "xml"), new MimeType("application", "*+xml")); + this(new MimeType("application", "xml"), new MimeType("text", "xml"), + new MimeType("application", "*+xml")); } /** @@ -154,7 +157,7 @@ public class MarshallingMessageConverter extends AbstractMessageConverter { return new StreamSource(new ByteArrayInputStream((byte[]) payload)); } else { - return new StreamSource(new StringReader((String) payload)); + return new StreamSource(new StringReader(payload.toString())); } } @@ -163,13 +166,13 @@ public class MarshallingMessageConverter extends AbstractMessageConverter { Assert.notNull(this.marshaller, "Property 'marshaller' is required"); try { if (byte[].class == getSerializedPayloadClass()) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); Result result = new StreamResult(out); this.marshaller.marshal(payload, result); payload = out.toByteArray(); } else { - Writer writer = new StringWriter(); + Writer writer = new StringWriter(1024); Result result = new StreamResult(writer); this.marshaller.marshal(payload, result); payload = writer.toString();