8161230: ClassLoader: add resource methods returning java.util.stream.Stream
Reviewed-by: psandoz, alanb, mchung, tvaleev
Contributed-by: Patrick Reinhart <patrick@reini.net>
--- a/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Fri Sep 09 14:54:29 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Fri Sep 09 14:54:41 2016 -0700
@@ -27,6 +27,7 @@
import java.io.InputStream;
import java.io.IOException;
+import java.io.UncheckedIOException;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -46,12 +47,16 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
import java.util.Stack;
import java.util.NoSuchElementException;
import java.util.Vector;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import jdk.internal.perf.PerfCounter;
import jdk.internal.module.ServicesCatalog;
@@ -1344,6 +1349,57 @@
}
/**
+ * Returns a stream whose elements are the URLs of all the resources with
+ * the given name. A resource is some data (images, audio, text, etc) that
+ * can be accessed by class code in a way that is independent of the
+ * location of the code.
+ *
+ * Resources in a named module are private to that module. This method does
+ * not find resources in named modules.
+ *
+ * <p> The name of a resource is a {@code /}-separated path name that
+ * identifies the resource.
+ *
+ * <p> The search order is described in the documentation for {@link
+ * #getResource(String)}.
+ *
+ * <p> The resources will be located when the returned stream is evaluated.
+ * If the evaluation results in an {@code IOException} then the I/O
+ * exception is wrapped in an {@link UncheckedIOException} that is then
+ * thrown.
+ *
+ * @apiNote When overriding this method it is recommended that an
+ * implementation ensures that any delegation is consistent with the {@link
+ * #getResource(java.lang.String) getResource(String)} method. This should
+ * ensure that the first element returned by the stream is the same
+ * resource that the {@code getResource(String)} method would return.
+ *
+ * @param name
+ * The resource name
+ *
+ * @return A stream of resource {@link java.net.URL URL} objects. If no
+ * resources could be found, the stream will be empty. Resources
+ * that the class loader doesn't have access to will not be in the
+ * stream.
+ *
+ * @see #findResources(String)
+ *
+ * @since 9
+ */
+ public Stream<URL> resources(String name) {
+ int characteristics = Spliterator.NONNULL | Spliterator.IMMUTABLE;
+ Supplier<Spliterator<URL>> si = () -> {
+ try {
+ return Spliterators.spliteratorUnknownSize(
+ getResources(name).asIterator(), characteristics);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ };
+ return StreamSupport.stream(si, characteristics, false);
+ }
+
+ /**
* Finds the resource with the given name. Class loader implementations
* should override this method to specify where to find resources.
*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ClassLoader/ResourcesStreamTest.java Fri Sep 09 14:54:41 2016 -0700
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016, 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.
+ *
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/*
+ * @test
+ * @bug 8161230
+ * @summary Test java.lang.ClassLoader.resources() method
+ *
+ * @build ResourcesStreamTest
+ * @run main ResourcesStreamTest
+ */
+public class ResourcesStreamTest {
+
+ public static void main(String[] args) throws Exception {
+ testSuccess();
+ testFailure();
+ }
+
+ public static void testSuccess() throws Exception {
+ // failing part first
+ try {
+ ClassLoader cl = new FailingClassLoader();
+ // should create the stream pipe
+ Stream<URL> stream = cl.resources("the name");
+ // expect function to throw an exception when calling the method
+ stream.forEach(System.out::println);
+ throw new Exception("expected UncheckedIOException not thrown");
+ } catch (UncheckedIOException uio) {
+ String causeMessage = uio.getCause().getMessage();
+ if (!"the name".equals(causeMessage))
+ throw new Exception("unexpected cause message: " + causeMessage);
+ }
+ }
+
+ public static void testFailure() throws Exception {
+ ClassLoader cl = new SuccessClassLoader();
+ long count = cl.resources("the name").count();
+ if (count != 1)
+ throw new Exception("expected resource is null or empty");
+
+ cl.resources("the name")
+ .filter(url -> "file:/somefile".equals(url.toExternalForm()))
+ .findFirst()
+ .orElseThrow(() -> new Exception("correct URL not found"));
+ }
+
+ public static class SuccessClassLoader extends ClassLoader {
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ URL url = new URL("file:/somefile");
+ return Collections.enumeration(Collections.singleton(url));
+ }
+ }
+
+ public static class FailingClassLoader extends ClassLoader {
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ throw new IOException(name);
+ }
+ }
+}