Skip to content

Commit 7f43f02

Browse files
committed
FormHttpMessageConverter uses a delegate class for JavaMail-based MIME encoding (isolating the JavaMail API dependency)
Issue: SPR-12108
1 parent cfd9fd6 commit 7f43f02

File tree

1 file changed

+41
-33
lines changed

1 file changed

+41
-33
lines changed

spring-web/src/main/java/org/springframework/http/converter/FormHttpMessageConverter.java

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.List;
2929
import java.util.Map;
3030
import java.util.Random;
31+
import javax.mail.internet.MimeUtility;
3132

3233
import org.springframework.core.io.Resource;
3334
import org.springframework.http.HttpEntity;
@@ -41,8 +42,6 @@
4142
import org.springframework.util.StreamUtils;
4243
import org.springframework.util.StringUtils;
4344

44-
import javax.mail.internet.MimeUtility;
45-
4645
/**
4746
* Implementation of {@link HttpMessageConverter} to read and write 'normal' HTML
4847
* forms and also to write (but not read) multipart data (e.g. file uploads).
@@ -79,8 +78,9 @@
7978
* <p>Some methods in this class were inspired by {@code org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity}.
8079
*
8180
* @author Arjen Poutsma
82-
* @see MultiValueMap
81+
* @author Rossen Stoyanchev
8382
* @since 3.0
83+
* @see MultiValueMap
8484
*/
8585
public class FormHttpMessageConverter implements HttpMessageConverter<MultiValueMap<String, ?>> {
8686

@@ -128,6 +128,7 @@ public void setCharset(Charset charset) {
128128
* and relies on {@code MimeUtility} from "javax.mail".
129129
* <p>If not set file names will be encoded as US-ASCII.
130130
* @param multipartCharset the charset to use
131+
* @since 4.1.1
131132
* @see <a href="http://en.wikipedia.org/wiki/MIME#Encoded-Word">Encoded-Word</a>
132133
*/
133134
public void setMultipartCharset(Charset multipartCharset) {
@@ -150,7 +151,7 @@ public List<MediaType> getSupportedMediaTypes() {
150151
* Set the message body converters to use. These converters are used to
151152
* convert objects to MIME parts.
152153
*/
153-
public final void setPartConverters(List<HttpMessageConverter<?>> partConverters) {
154+
public void setPartConverters(List<HttpMessageConverter<?>> partConverters) {
154155
Assert.notEmpty(partConverters, "'partConverters' must not be empty");
155156
this.partConverters = partConverters;
156157
}
@@ -159,8 +160,8 @@ public final void setPartConverters(List<HttpMessageConverter<?>> partConverters
159160
* Add a message body converter. Such a converters is used to convert objects
160161
* to MIME parts.
161162
*/
162-
public final void addPartConverter(HttpMessageConverter<?> partConverter) {
163-
Assert.notNull(partConverter, "'partConverter' must not be NULL");
163+
public void addPartConverter(HttpMessageConverter<?> partConverter) {
164+
Assert.notNull(partConverter, "'partConverter' must not be null");
164165
this.partConverters.add(partConverter);
165166
}
166167

@@ -174,9 +175,8 @@ public boolean canRead(Class<?> clazz, MediaType mediaType) {
174175
return true;
175176
}
176177
for (MediaType supportedMediaType : getSupportedMediaTypes()) {
177-
// we can't read multipart
178-
if (!supportedMediaType.equals(MediaType.MULTIPART_FORM_DATA) &&
179-
supportedMediaType.includes(mediaType)) {
178+
// We can't read multipart....
179+
if (!supportedMediaType.equals(MediaType.MULTIPART_FORM_DATA) && supportedMediaType.includes(mediaType)) {
180180
return true;
181181
}
182182
}
@@ -204,13 +204,11 @@ public MultiValueMap<String, String> read(Class<? extends MultiValueMap<String,
204204
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
205205

206206
MediaType contentType = inputMessage.getHeaders().getContentType();
207-
Charset charset = contentType.getCharSet() != null ? contentType.getCharSet() : this.charset;
207+
Charset charset = (contentType.getCharSet() != null ? contentType.getCharSet() : this.charset);
208208
String body = StreamUtils.copyToString(inputMessage.getBody(), charset);
209209

210210
String[] pairs = StringUtils.tokenizeToStringArray(body, "&");
211-
212211
MultiValueMap<String, String> result = new LinkedMultiValueMap<String, String>(pairs.length);
213-
214212
for (String pair : pairs) {
215213
int idx = pair.indexOf('=');
216214
if (idx == -1) {
@@ -391,14 +389,8 @@ protected String getFilename(Object part) {
391389
if (part instanceof Resource) {
392390
Resource resource = (Resource) part;
393391
String filename = resource.getFilename();
394-
if (multipartCharset != null) {
395-
try {
396-
filename = MimeUtility.encodeText(filename, multipartCharset.name(), null);
397-
}
398-
catch (UnsupportedEncodingException e) {
399-
// should not happen
400-
throw new IllegalStateException(e);
401-
}
392+
if (this.multipartCharset != null) {
393+
filename = MimeDelegate.encode(filename, this.multipartCharset.name());
402394
}
403395
return filename;
404396
}
@@ -414,25 +406,25 @@ protected String getFilename(Object part) {
414406
*/
415407
private class MultipartHttpOutputMessage implements HttpOutputMessage {
416408

417-
private final HttpHeaders headers = new HttpHeaders();
409+
private final OutputStream outputStream;
418410

419-
private final OutputStream os;
411+
private final HttpHeaders headers = new HttpHeaders();
420412

421413
private boolean headersWritten = false;
422414

423-
public MultipartHttpOutputMessage(OutputStream os) {
424-
this.os = os;
415+
public MultipartHttpOutputMessage(OutputStream outputStream) {
416+
this.outputStream = outputStream;
425417
}
426418

427419
@Override
428420
public HttpHeaders getHeaders() {
429-
return headersWritten ? HttpHeaders.readOnlyHttpHeaders(headers) : this.headers;
421+
return (this.headersWritten ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
430422
}
431423

432424
@Override
433425
public OutputStream getBody() throws IOException {
434426
writeHeaders();
435-
return this.os;
427+
return this.outputStream;
436428
}
437429

438430
private void writeHeaders() throws IOException {
@@ -441,14 +433,14 @@ private void writeHeaders() throws IOException {
441433
byte[] headerName = getAsciiBytes(entry.getKey());
442434
for (String headerValueString : entry.getValue()) {
443435
byte[] headerValue = getAsciiBytes(headerValueString);
444-
os.write(headerName);
445-
os.write(':');
446-
os.write(' ');
447-
os.write(headerValue);
448-
writeNewLine(os);
436+
this.outputStream.write(headerName);
437+
this.outputStream.write(':');
438+
this.outputStream.write(' ');
439+
this.outputStream.write(headerValue);
440+
writeNewLine(this.outputStream);
449441
}
450442
}
451-
writeNewLine(os);
443+
writeNewLine(this.outputStream);
452444
this.headersWritten = true;
453445
}
454446
}
@@ -458,7 +450,23 @@ private byte[] getAsciiBytes(String name) {
458450
return name.getBytes("US-ASCII");
459451
}
460452
catch (UnsupportedEncodingException ex) {
461-
// should not happen, US-ASCII is always supported
453+
// Should not happen - US-ASCII is always supported.
454+
throw new IllegalStateException(ex);
455+
}
456+
}
457+
}
458+
459+
460+
/**
461+
* Inner class to avoid a hard dependency on the JavaMail API.
462+
*/
463+
private static class MimeDelegate {
464+
465+
public static String encode(String value, String charset) {
466+
try {
467+
return MimeUtility.encodeText(value, charset, null);
468+
}
469+
catch (UnsupportedEncodingException ex) {
462470
throw new IllegalStateException(ex);
463471
}
464472
}

0 commit comments

Comments
 (0)