8129632: (fs) Files.probeContentType returns null on Mac OS X
Summary: Append a Uniform Type Identifier-based FileType Detector for Mac OS X
Reviewed-by: alanb
--- a/jdk/make/lib/NioLibraries.gmk Tue Jun 30 08:51:16 2015 -0700
+++ b/jdk/make/lib/NioLibraries.gmk Tue Jun 30 10:47:54 2015 -0700
@@ -81,7 +81,8 @@
LDFLAGS_SUFFIX_windows := jvm.lib ws2_32.lib $(WIN_JAVA_LIB) \
$(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libnet/net.lib \
advapi32.lib, \
- LDFLAGS_SUFFIX_macosx := -ljava -lnet -pthread -framework CoreFoundation, \
+ LDFLAGS_SUFFIX_macosx := -ljava -lnet -pthread \
+ -framework CoreFoundation -framework CoreServices, \
LDFLAGS_SUFFIX :=, \
VERSIONINFO_RESOURCE := $(GLOBAL_VERSION_INFO_RESOURCE), \
RC_FLAGS := $(RC_FLAGS) \
--- a/jdk/src/java.base/macosx/classes/sun/nio/fs/MacOSXFileSystemProvider.java Tue Jun 30 08:51:16 2015 -0700
+++ b/jdk/src/java.base/macosx/classes/sun/nio/fs/MacOSXFileSystemProvider.java Tue Jun 30 10:47:54 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,8 @@
FileTypeDetector getFileTypeDetector() {
Path userMimeTypes = Paths.get(AccessController.doPrivileged(
new GetPropertyAction("user.home")), ".mime.types");
- return new MimeTypesFileTypeDetector(userMimeTypes);
+
+ return chain(new MimeTypesFileTypeDetector(userMimeTypes),
+ new UTIFileTypeDetector());
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/macosx/classes/sun/nio/fs/UTIFileTypeDetector.java Tue Jun 30 10:47:54 2015 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * File type detector that uses a file extension to look up its MIME type
+ * via the Apple Uniform Type Identifier interfaces.
+ */
+class UTIFileTypeDetector extends AbstractFileTypeDetector {
+ UTIFileTypeDetector() {
+ super();
+ }
+
+ private native String probe0(String fileExtension) throws IOException;
+
+ @Override
+ protected String implProbeContentType(Path path) throws IOException {
+ Path fn = path.getFileName();
+ if (fn == null)
+ return null; // no file name
+
+ String ext = getExtension(fn.toString());
+ if (ext.isEmpty())
+ return null; // no extension
+
+ return probe0(ext);
+ }
+
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<>() {
+ @Override
+ public Void run() {
+ System.loadLibrary("nio");
+ return null;
+ }
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/macosx/native/libnio/fs/UTIFileTypeDetector.c Tue Jun 30 10:47:54 2015 -0700
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreServices/CoreServices.h>
+
+/**
+ * Creates a CF string from the given Java string.
+ * If javaString is NULL, NULL is returned.
+ * If a memory error occurs, and OutOfMemoryError is thrown and
+ * NULL is returned.
+ */
+static CFStringRef toCFString(JNIEnv *env, jstring javaString)
+{
+ if (javaString == NULL) {
+ return NULL;
+ } else {
+ CFStringRef result = NULL;
+ jsize length = (*env)->GetStringLength(env, javaString);
+ const jchar *chars = (*env)->GetStringChars(env, javaString, NULL);
+ if (chars == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "toCFString failed");
+ return NULL;
+ }
+ result = CFStringCreateWithCharacters(NULL, (const UniChar *)chars,
+ length);
+ (*env)->ReleaseStringChars(env, javaString, chars);
+ if (result == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "toCFString failed");
+ return NULL;
+ }
+ return result;
+ }
+}
+
+/**
+ * Creates a Java string from the given CF string.
+ * If cfString is NULL, NULL is returned.
+ * If a memory error occurs, and OutOfMemoryError is thrown and
+ * NULL is returned.
+ */
+static jstring toJavaString(JNIEnv *env, CFStringRef cfString)
+{
+ if (cfString == NULL) {
+ return NULL;
+ } else {
+ jstring javaString = NULL;
+
+ CFIndex length = CFStringGetLength(cfString);
+ const UniChar *constchars = CFStringGetCharactersPtr(cfString);
+ if (constchars) {
+ javaString = (*env)->NewString(env, constchars, length);
+ } else {
+ UniChar *chars = malloc(length * sizeof(UniChar));
+ if (chars == NULL) {
+ JNU_ThrowOutOfMemoryError(env, "toJavaString failed");
+ return NULL;
+ }
+ CFStringGetCharacters(cfString, CFRangeMake(0, length), chars);
+ javaString = (*env)->NewString(env, chars, length);
+ free(chars);
+ }
+ return javaString;
+ }
+}
+
+/**
+ * Returns the content type corresponding to the supplied file extension.
+ * The mapping is determined using Uniform Type Identifiers (UTIs). If
+ * the file extension parameter is NULL, a CFString cannot be created
+ * from the file extension parameter, there is no UTI corresponding to
+ * the file extension, the UTI cannot supply a MIME type for the file
+ * extension, or a Java string cannot be created, then NULL is returned;
+ * otherwise the MIME type string is returned.
+ */
+JNIEXPORT jstring JNICALL
+Java_sun_nio_fs_UTIFileTypeDetector_probe0(JNIEnv* env, jobject ftd,
+ jstring ext)
+{
+ jstring result = NULL;
+
+ CFStringRef extension = toCFString(env, ext);
+ if (extension != NULL) {
+ CFStringRef uti =
+ UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension,
+ extension, NULL);
+ CFRelease(extension);
+
+ if (uti != NULL) {
+ CFStringRef mimeType =
+ UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType);
+ CFRelease(uti);
+
+ if (mimeType != NULL) {
+ result = toJavaString(env, mimeType);
+ CFRelease(mimeType);
+ }
+ }
+ }
+
+ return result;
+}
--- a/jdk/src/java.base/share/classes/sun/nio/fs/AbstractFileTypeDetector.java Tue Jun 30 08:51:16 2015 -0700
+++ b/jdk/src/java.base/share/classes/sun/nio/fs/AbstractFileTypeDetector.java Tue Jun 30 10:47:54 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -42,6 +42,27 @@
}
/**
+ * Returns the extension of a file name, specifically the portion of the
+ * parameter string after the first dot. If the parameter is {@code null},
+ * empty, does not contain a dot, or the dot is the last character, then an
+ * empty string is returned, otherwise the characters after the dot are
+ * returned.
+ *
+ * @param name A file name
+ * @return The characters after the first dot or an empty string.
+ */
+ protected final String getExtension(String name) {
+ String ext = "";
+ if (name != null && !name.isEmpty()) {
+ int dot = name.indexOf('.');
+ if ((dot >= 0) && (dot < name.length() - 1)) {
+ ext = name.substring(dot + 1);
+ }
+ }
+ return ext;
+ }
+
+ /**
* Invokes the appropriate probe method to guess a file's content type,
* and checks that the content type's syntax is valid.
*/
--- a/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java Tue Jun 30 08:51:16 2015 -0700
+++ b/jdk/src/java.base/unix/classes/sun/nio/fs/MimeTypesFileTypeDetector.java Tue Jun 30 10:47:54 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -83,18 +83,6 @@
return mimeType;
}
- // Get the extension of a file name.
- private static String getExtension(String name) {
- String ext = "";
- if (name != null && !name.isEmpty()) {
- int dot = name.indexOf('.');
- if ((dot >= 0) && (dot < name.length() - 1)) {
- ext = name.substring(dot + 1);
- }
- }
- return ext;
- }
-
/**
* Parse the mime types file, and store the type-extension mappings into
* mimeTypeMap. The mime types file is not loaded until the first probe
--- a/jdk/test/java/nio/file/Files/probeContentType/Basic.java Tue Jun 30 08:51:16 2015 -0700
+++ b/jdk/test/java/nio/file/Files/probeContentType/Basic.java Tue Jun 30 10:47:54 2015 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,7 +22,7 @@
*/
/* @test
- * @bug 4313887
+ * @bug 4313887 8129632
* @summary Unit test for probeContentType method
* @library ../..
* @build Basic SimpleFileTypeDetector
@@ -33,9 +33,9 @@
import java.io.*;
/**
- * Uses Files.probeContentType to probe html file and custom file type.
+ * Uses Files.probeContentType to probe html file, custom file type, and minimal
+ * set of file extension to content type mappings.
*/
-
public class Basic {
static Path createHtmlFile() throws IOException {
@@ -51,6 +51,39 @@
return Files.createTempFile("red", ".grape");
}
+ static void checkContentTypes(String[] extensions, String[] expectedTypes)
+ throws IOException {
+ if (extensions.length != expectedTypes.length) {
+ throw new IllegalArgumentException("Parameter array lengths differ");
+ }
+
+ int failures = 0;
+ for (int i = 0; i < extensions.length; i++) {
+ String extension = extensions[i];
+ Path file = Files.createTempFile("foo", "." + extension);
+ try {
+ String type = Files.probeContentType(file);
+ if (type == null) {
+ System.err.println("Content type of " + extension
+ + " cannot be determined");
+ failures++;
+ } else {
+ if (!type.equals(expectedTypes[i])) {
+ System.err.println("Content type: " + type
+ + "; expected: " + expectedTypes[i]);
+ failures++;
+ }
+ }
+ } finally {
+ Files.delete(file);
+ }
+ }
+
+ if (failures > 0) {
+ throw new RuntimeException("Test failed!");
+ }
+ }
+
public static void main(String[] args) throws IOException {
// exercise default file type detector
@@ -79,5 +112,17 @@
Files.delete(file);
}
+ // Verify that common file extensions are mapped to the correct content
+ // types on Mac OS X only which has consistent Uniform Type Identifiers.
+ if (System.getProperty("os.name").contains("OS X")) {
+ String[] extensions = new String[]{
+ "jpg", "mp3", "mp4", "pdf", "png"
+ };
+ String[] expectedTypes = new String[]{
+ "image/jpeg", "audio/mpeg", "video/mp4", "application/pdf",
+ "image/png"
+ };
+ checkContentTypes(extensions, expectedTypes);
+ }
}
}