Resource.isFile() and JAF MediaTypeFactory

Issue: SPR-14484
This commit is contained in:
Juergen Hoeller
2016-07-19 18:53:31 +02:00
parent 7287baefeb
commit 8bb34bc962
22 changed files with 270 additions and 262 deletions

View File

@@ -43,8 +43,7 @@ import org.springframework.util.comparator.CompoundComparator;
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 3.0
* @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.1.1">HTTP 1.1: Semantics
* and Content, section 3.1.1.1</a>
* @see <a href="http://tools.ietf.org/html/rfc7231#section-3.1.1.1">HTTP 1.1: Semantics and Content, section 3.1.1.1</a>
*/
public class MediaType extends MimeType implements Serializable {
@@ -450,15 +449,18 @@ public class MediaType extends MimeType implements Serializable {
* Re-create the given mime types as media types.
* @since 5.0
*/
public static List<MediaType> toMediaTypes(List<MimeType> mimeTypes) {
return mimeTypes.stream().map(MediaType::toMediaType).collect(Collectors.toList());
public static List<MediaType> asMediaTypes(List<MimeType> mimeTypes) {
return mimeTypes.stream().map(MediaType::asMediaType).collect(Collectors.toList());
}
/**
* Re-create the given mime type as a media type.
* @since 5.0
*/
public static MediaType toMediaType(MimeType mimeType) {
public static MediaType asMediaType(MimeType mimeType) {
if (mimeType instanceof MediaType) {
return (MediaType) mimeType;
}
return new MediaType(mimeType.getType(), mimeType.getSubtype(), mimeType.getParameters());
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2002-2016 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.http;
import java.io.IOException;
import java.io.InputStream;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
/**
* A factory delegate for resolving {@link MediaType} objects
* from {@link Resource} handles or filenames.
*
* <p>This implementation is based on the Java Activation Framework,
* sharing the MIME type definitions with Spring's JavaMail support.
* However, JAF is an implementation detail and not leaking out.
*
* @author Juergen Hoeller
* @since 5.0
*/
public class MediaTypeFactory {
private static final FileTypeMap fileTypeMap;
static {
fileTypeMap = loadFileTypeMapFromContextSupportModule();
}
private static FileTypeMap loadFileTypeMapFromContextSupportModule() {
// See if we can find the extended mime.types from the context-support module...
Resource mappingLocation = new ClassPathResource("org/springframework/mail/javamail/mime.types");
if (mappingLocation.exists()) {
InputStream inputStream = null;
try {
inputStream = mappingLocation.getInputStream();
return new MimetypesFileTypeMap(inputStream);
}
catch (IOException ex) {
// ignore
}
finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException ex) {
// ignore
}
}
}
}
return FileTypeMap.getDefaultFileTypeMap();
}
/**
* Determine a media type for the given resource, if possible.
* @param resource the resource to introspect
* @return the corresponding media type, or {@code null} if none found
*/
public static MediaType getMediaType(Resource resource) {
String filename = resource.getFilename();
return (filename != null ? getMediaType(filename) : null);
}
/**
* Determine a media type for the given file name, if possible.
* @param filename the file name plus extension
* @return the corresponding media type, or {@code null} if none found
*/
public static MediaType getMediaType(String filename) {
String mediaType = fileTypeMap.getContentType(filename);
return (StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null);
}
}

View File

@@ -19,19 +19,16 @@ package org.springframework.http.converter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
/**
* Implementation of {@link HttpMessageConverter} that can read and write {@link Resource Resources}
@@ -82,7 +79,7 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
@Override
protected MediaType getDefaultContentType(Resource resource) {
if (jafPresent) {
return ActivationMediaTypeFactory.getMediaType(resource);
return MediaTypeFactory.getMediaType(resource);
}
else {
return MediaType.APPLICATION_OCTET_STREAM;
@@ -131,54 +128,4 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
}
}
/**
* Inner class to avoid a hard-coded JAF dependency.
*/
private static class ActivationMediaTypeFactory {
private static final FileTypeMap fileTypeMap;
static {
fileTypeMap = loadFileTypeMapFromContextSupportModule();
}
private static FileTypeMap loadFileTypeMapFromContextSupportModule() {
// See if we can find the extended mime.types from the context-support module...
Resource mappingLocation = new ClassPathResource("org/springframework/mail/javamail/mime.types");
if (mappingLocation.exists()) {
InputStream inputStream = null;
try {
inputStream = mappingLocation.getInputStream();
return new MimetypesFileTypeMap(inputStream);
}
catch (IOException ex) {
// ignore
}
finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException ex) {
// ignore
}
}
}
}
return FileTypeMap.getDefaultFileTypeMap();
}
public static MediaType getMediaType(Resource resource) {
String filename = resource.getFilename();
if (filename != null) {
String mediaType = fileTypeMap.getContentType(filename);
if (StringUtils.hasText(mediaType)) {
return MediaType.parseMediaType(mediaType);
}
}
return null;
}
}
}

View File

@@ -28,8 +28,8 @@ import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpInputMessage;
/**
* Implementation of the {@link HttpMessageReader} interface that delegates to
* a {@link Decoder}.
* Implementation of the {@link HttpMessageReader} interface that delegates
* to a {@link Decoder}.
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
@@ -49,9 +49,8 @@ public class DecoderHttpMessageReader<T> implements HttpMessageReader<T> {
*/
public DecoderHttpMessageReader(Decoder<T> decoder) {
this.decoder = decoder;
this.readableMediaTypes = decoder != null ?
MediaType.toMediaTypes(decoder.getDecodableMimeTypes()) :
Collections.emptyList();
this.readableMediaTypes = (decoder != null ?
MediaType.asMediaTypes(decoder.getDecodableMimeTypes()) : Collections.emptyList());
}

View File

@@ -32,8 +32,8 @@ import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
/**
* Implementation of the {@link HttpMessageWriter} interface that delegates to
* an {@link Encoder}.
* Implementation of the {@link HttpMessageWriter} interface that delegates
* to an {@link Encoder}.
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
@@ -53,9 +53,8 @@ public class EncoderHttpMessageWriter<T> implements HttpMessageWriter<T> {
*/
public EncoderHttpMessageWriter(Encoder<T> encoder) {
this.encoder = encoder;
this.writableMediaTypes = encoder != null ?
MediaType.toMediaTypes(encoder.getEncodableMimeTypes()) :
Collections.emptyList();
this.writableMediaTypes = (encoder != null ?
MediaType.asMediaTypes(encoder.getEncodableMimeTypes()) : Collections.emptyList());
}

View File

@@ -31,10 +31,9 @@ import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.ZeroCopyHttpOutputMessage;
import org.springframework.util.MimeTypeUtils;
import org.springframework.util.ResourceUtils;
/**
* Implementation of {@link HttpMessageWriter} that can write
@@ -67,8 +66,7 @@ public class ResourceHttpMessageWriter extends EncoderHttpMessageWriter<Resource
concatMap(resource -> {
HttpHeaders headers = outputMessage.getHeaders();
addHeaders(headers, resource, contentType);
return writeContent(resource, type, contentType, outputMessage);
return writeContent(resource, type, outputMessage);
}));
}
@@ -76,8 +74,7 @@ public class ResourceHttpMessageWriter extends EncoderHttpMessageWriter<Resource
if (headers.getContentType() == null) {
if (contentType == null || !contentType.isConcrete() ||
MediaType.APPLICATION_OCTET_STREAM.equals(contentType)) {
contentType = MimeTypeUtils.getMimeType(resource.getFilename()).
map(MediaType::toMediaType).
contentType = Optional.ofNullable(MediaTypeFactory.getMediaType(resource)).
orElse(MediaType.APPLICATION_OCTET_STREAM);
}
headers.setContentType(contentType);
@@ -87,9 +84,7 @@ public class ResourceHttpMessageWriter extends EncoderHttpMessageWriter<Resource
}
}
private Mono<Void> writeContent(Resource resource, ResolvableType type,
MediaType contentType, ReactiveHttpOutputMessage outputMessage) {
private Mono<Void> writeContent(Resource resource, ResolvableType type, ReactiveHttpOutputMessage outputMessage) {
if (outputMessage instanceof ZeroCopyHttpOutputMessage) {
Optional<File> file = getFile(resource);
if (file.isPresent()) {
@@ -119,11 +114,11 @@ public class ResourceHttpMessageWriter extends EncoderHttpMessageWriter<Resource
}
private static Optional<File> getFile(Resource resource) {
if (ResourceUtils.hasFile(resource)) {
if (resource.isFile()) {
try {
return Optional.of(resource.getFile());
}
catch (IOException ignored) {
catch (IOException ex) {
// should not happen
}
}

View File

@@ -16,20 +16,16 @@
package org.springframework.web.accept;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Map;
import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
@@ -128,7 +124,7 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
throws HttpMediaTypeNotAcceptableException {
if (this.useJaf && JAF_PRESENT) {
MediaType mediaType = JafMediaTypeFactory.getMediaType("file." + extension);
MediaType mediaType = MediaTypeFactory.getMediaType("file." + extension);
if (mediaType != null && !MediaType.APPLICATION_OCTET_STREAM.equals(mediaType)) {
return mediaType;
}
@@ -157,7 +153,7 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
mediaType = lookupMediaType(extension);
}
if (mediaType == null && JAF_PRESENT) {
mediaType = JafMediaTypeFactory.getMediaType(filename);
mediaType = MediaTypeFactory.getMediaType(filename);
}
if (MediaType.APPLICATION_OCTET_STREAM.equals(mediaType)) {
mediaType = null;
@@ -165,56 +161,4 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
return mediaType;
}
/**
* Inner class to avoid hard-coded dependency on JAF.
*/
private static class JafMediaTypeFactory {
private static final FileTypeMap fileTypeMap;
static {
fileTypeMap = initFileTypeMap();
}
/**
* Find extended mime.types from the spring-context-support module.
*/
private static FileTypeMap initFileTypeMap() {
Resource resource = new ClassPathResource("org/springframework/mail/javamail/mime.types");
if (resource.exists()) {
if (logger.isTraceEnabled()) {
logger.trace("Loading JAF FileTypeMap from " + resource);
}
InputStream inputStream = null;
try {
inputStream = resource.getInputStream();
return new MimetypesFileTypeMap(inputStream);
}
catch (IOException ex) {
// ignore
}
finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException ex) {
// ignore
}
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Loading default Java Activation Framework FileTypeMap");
}
return FileTypeMap.getDefaultFileTypeMap();
}
public static MediaType getMediaType(String filename) {
String mediaType = fileTypeMap.getContentType(filename);
return (StringUtils.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2016 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.
@@ -77,6 +77,7 @@ public class ServletContextResource extends AbstractFileResolvingResource implem
this.path = pathToUse;
}
/**
* Return the ServletContext for this resource.
*/
@@ -91,7 +92,6 @@ public class ServletContextResource extends AbstractFileResolvingResource implem
return this.path;
}
/**
* This implementation checks {@code ServletContext.getResource}.
* @see javax.servlet.ServletContext#getResource(String)
@@ -129,6 +129,22 @@ public class ServletContextResource extends AbstractFileResolvingResource implem
}
}
@Override
public boolean isFile() {
try {
URL url = this.servletContext.getResource(this.path);
if (url != null && ResourceUtils.isFileURL(url)) {
return true;
}
else {
return (this.servletContext.getRealPath(this.path) != null);
}
}
catch (MalformedURLException ex) {
return false;
}
}
/**
* This implementation delegates to {@code ServletContext.getResourceAsStream},
* but throws a FileNotFoundException if no resource found.