From c3a8a4933abb411d29b6d2de87aa3784a3e08c64 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 8 Aug 2010 22:53:52 +0000 Subject: [PATCH] added "contentLength()" method to Resource abstraction; URL-based Resource variants introspect "last-modified" and "content-length" response headers (SPR-5465); refined "exists()" check for UrlResource (HEAD request) and ClassPathResource (URL resolution) git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@3545 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../io/AbstractFileResolvingResource.java | 68 ++++++++++++++++++- .../core/io/AbstractResource.java | 15 +++- .../core/io/ClassPathResource.java | 17 +++++ .../org/springframework/core/io/Resource.java | 9 ++- .../springframework/util/ResourceUtils.java | 13 +++- 5 files changed, 116 insertions(+), 6 deletions(-) diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/org.springframework.core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index b1ed321aa31..84497b22b81 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/org.springframework.core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -18,8 +18,10 @@ package org.springframework.core.io; import java.io.File; import java.io.IOException; +import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; +import java.net.URLConnection; import org.springframework.util.ResourceUtils; @@ -81,6 +83,70 @@ public abstract class AbstractFileResolvingResource extends AbstractResource { } + @Override + public boolean exists() { + try { + URL url = getURL(); + if (ResourceUtils.isFileURL(url)) { + // Proceed with file system resolution... + return getFile().exists(); + } + else { + // Try a URL connection content-length header... + URLConnection con = url.openConnection(); + con.setUseCaches(false); + if (con instanceof HttpURLConnection) { + ((HttpURLConnection) con).setRequestMethod("HEAD"); + } + boolean doesExist = (con.getContentLength() >= 0); + if (!doesExist && con instanceof HttpURLConnection) { + ((HttpURLConnection) con).disconnect(); + } + return doesExist; + } + } + catch (IOException ex) { + return false; + } + } + + @Override + public int contentLength() throws IOException { + URL url = getURL(); + if (ResourceUtils.isFileURL(url)) { + // Proceed with file system resolution... + return super.contentLength(); + } + else { + // Try a URL connection content-length header... + URLConnection con = url.openConnection(); + con.setUseCaches(false); + if (con instanceof HttpURLConnection) { + ((HttpURLConnection) con).setRequestMethod("HEAD"); + } + return con.getContentLength(); + } + } + + @Override + public long lastModified() throws IOException { + URL url = getURL(); + if (ResourceUtils.isFileURL(url) || ResourceUtils.isJarURL(url)) { + // Proceed with file system resolution... + return super.lastModified(); + } + else { + // Try a URL connection last-modified header... + URLConnection con = url.openConnection(); + con.setUseCaches(false); + if (con instanceof HttpURLConnection) { + ((HttpURLConnection) con).setRequestMethod("HEAD"); + } + return con.getLastModified(); + } + } + + /** * Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime. */ diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/AbstractResource.java b/org.springframework.core/src/main/java/org/springframework/core/io/AbstractResource.java index eac9c813bed..6cd0acdd01f 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/io/AbstractResource.java +++ b/org.springframework.core/src/main/java/org/springframework/core/io/AbstractResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -107,6 +107,15 @@ public abstract class AbstractResource implements Resource { throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path"); } + /** + * This implementation checks the timestamp of the underlying File, + * if available. + * @see #getFile() + */ + public int contentLength() throws IOException { + return (int) getFile().length(); + } + /** * This implementation checks the timestamp of the underlying File, * if available. @@ -142,10 +151,10 @@ public abstract class AbstractResource implements Resource { /** * This implementation always throws IllegalStateException, - * assuming that the resource does not carry a filename. + * assuming that the resource does not have a filename. */ public String getFilename() throws IllegalStateException { - throw new IllegalStateException(getDescription() + " does not carry a filename"); + throw new IllegalStateException(getDescription() + " does not have a filename"); } diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/ClassPathResource.java b/org.springframework.core/src/main/java/org/springframework/core/io/ClassPathResource.java index c48d4edaa5e..3f34d32d1f1 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/io/ClassPathResource.java +++ b/org.springframework.core/src/main/java/org/springframework/core/io/ClassPathResource.java @@ -124,6 +124,23 @@ public class ClassPathResource extends AbstractFileResolvingResource { } + /** + * This implementation checks for the resolution of a resource URL. + * @see java.lang.ClassLoader#getResource(String) + * @see java.lang.Class#getResource(String) + */ + @Override + public boolean exists() { + URL url; + if (this.clazz != null) { + url = this.clazz.getResource(this.path); + } + else { + url = this.classLoader.getResource(this.path); + } + return (url != null); + } + /** * This implementation opens an InputStream for the given class path resource. * @see java.lang.ClassLoader#getResourceAsStream(String) diff --git a/org.springframework.core/src/main/java/org/springframework/core/io/Resource.java b/org.springframework.core/src/main/java/org/springframework/core/io/Resource.java index bd75d8900c9..f8ba779db40 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/io/Resource.java +++ b/org.springframework.core/src/main/java/org/springframework/core/io/Resource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2010 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. @@ -91,6 +91,13 @@ public interface Resource extends InputStreamSource { */ File getFile() throws IOException; + /** + * Determine the content length for this resource. + * @throws IOException if the resource cannot be resolved + * (in the file system or as some other known physical resource type) + */ + int contentLength() throws IOException; + /** * Determine the last-modified timestamp for this resource. * @throws IOException if the resource cannot be resolved diff --git a/org.springframework.core/src/main/java/org/springframework/util/ResourceUtils.java b/org.springframework.core/src/main/java/org/springframework/util/ResourceUtils.java index a037241fa20..e3097190292 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/ResourceUtils.java +++ b/org.springframework.core/src/main/java/org/springframework/util/ResourceUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 the original author or authors. + * Copyright 2002-2010 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. @@ -246,6 +246,17 @@ public abstract class ResourceUtils { return new File(resourceUri.getSchemeSpecificPart()); } + /** + * Determine whether the given URL points to a resource in the file system, + * that is, has protocol "file" or "vfs". + * @param url the URL to check + * @return whether the URL has been identified as a file system URL + */ + public static boolean isFileURL(URL url) { + String protocol = url.getProtocol(); + return (URL_PROTOCOL_FILE.equals(protocol) || protocol.startsWith(URL_PROTOCOL_VFS)); + } + /** * Determine whether the given URL points to a resource in a jar file, * that is, has protocol "jar", "zip", "wsjar" or "code-source".