Repository: incubator-tamaya Updated Branches: refs/heads/master 2dd0c13e5 -> c48c5794e
TAMAYA-43: Implemented ant pattern based resource lookup for files and classpath, about 20k compiled size overall. TAMAYA-43: Removed Resource abstraction in favor of URL. Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/commit/ae66299e Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/tree/ae66299e Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/diff/ae66299e Branch: refs/heads/master Commit: ae66299e25b41167008021ffe95cad236f6e2bd3 Parents: 2dd0c13 Author: anatole <anat...@apache.org> Authored: Wed Jan 7 22:49:39 2015 +0100 Committer: anatole <anat...@apache.org> Committed: Sun Jan 11 01:40:57 2015 +0100 ---------------------------------------------------------------------- .../tamaya/format/ConfigurationFormat.java | 10 +- .../apache/tamaya/format/PropertiesFormat.java | 15 +- .../tamaya/format/PropertiesXmlFormat.java | 15 +- ...AbstractPathBasedPropertySourceProvider.java | 7 +- .../AbstractResourcePropertySourceProvider.java | 17 +- .../org/apache/tamaya/resource/Resource.java | 115 -------- .../tamaya/resource/ResourceResolver.java | 25 +- .../resource/internal/ClassPathResource.java | 187 ------------- .../resource/internal/ClasspathCollector.java | 264 +++++++++++++++++++ .../internal/DefaultResourceResolver.java | 61 ++++- .../tamaya/resource/internal/FileCollector.java | 157 +++++++++++ .../tamaya/resource/internal/FileResource.java | 172 ------------ .../resource/internal/InputStreamResource.java | 117 -------- .../tamaya/resource/internal/Locator.java | 150 +++++++++++ .../tamaya/resource/internal/UrlResource.java | 140 ---------- 15 files changed, 671 insertions(+), 781 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/formats/src/main/java/org/apache/tamaya/format/ConfigurationFormat.java ---------------------------------------------------------------------- diff --git a/modules/formats/src/main/java/org/apache/tamaya/format/ConfigurationFormat.java b/modules/formats/src/main/java/org/apache/tamaya/format/ConfigurationFormat.java index 4a1e06f..924fa7c 100644 --- a/modules/formats/src/main/java/org/apache/tamaya/format/ConfigurationFormat.java +++ b/modules/formats/src/main/java/org/apache/tamaya/format/ConfigurationFormat.java @@ -21,9 +21,8 @@ package org.apache.tamaya.format; import org.apache.tamaya.spi.PropertySource; import java.io.IOException; -import java.io.InputStream; +import java.net.URL; import java.util.Collection; -import java.util.function.Supplier; /** * Implementations current this class encapsulate the mechanism how to read a @@ -44,12 +43,11 @@ public interface ConfigurationFormat { * ladder case multiple PropertySources can be returned, each one with its own ordinal and the corresponding * entries. * - * @param sourceName name to be used for constructing a useful name for the created - * {@link org.apache.tamaya.spi.PropertySource} instances. - * @param streamSupplier the resource represented by a supplier of InputStream, not null + * @param url the url to read the configuration data from (could be a file, a remote location, a classpath + * resource or something else. * @return the corresponding {@link org.apache.tamaya.spi.PropertySource} instances, never {@code null}. */ - Collection<PropertySource> readConfiguration(String sourceName, Supplier<InputStream> streamSupplier) + Collection<PropertySource> readConfiguration(URL url) throws IOException; } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesFormat.java ---------------------------------------------------------------------- diff --git a/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesFormat.java b/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesFormat.java index 34c9e21..a49e492 100644 --- a/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesFormat.java +++ b/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesFormat.java @@ -21,6 +21,7 @@ package org.apache.tamaya.format; import org.apache.tamaya.spi.PropertySource; import java.io.InputStream; +import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -28,7 +29,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; -import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -70,10 +70,15 @@ public class PropertiesFormat implements ConfigurationFormat { @SuppressWarnings("unchecked") @Override - public Collection<PropertySource> readConfiguration(String sourceName, Supplier<InputStream> streamSupplier) { - final String name = "Properties(" + Objects.requireNonNull(sourceName) + ')'; + public Collection<PropertySource> readConfiguration(URL url) { + final String name; + if (Objects.requireNonNull(url).getQuery() == null) { + name = "Properties(" + Objects.requireNonNull(url).toString() + ')'; + } else { + name = Objects.requireNonNull(url).getQuery(); + } List<PropertySource> propertySources = new ArrayList<>(); - try (InputStream is = streamSupplier.get()) { + try (InputStream is = url.openStream()) { if (is != null) { final Properties p = new Properties(); p.load(is); @@ -101,7 +106,7 @@ public class PropertiesFormat implements ConfigurationFormat { return propertySources; } } catch (Exception e) { - LOG.log(Level.FINEST, e, () -> "Failed to read config from resource: " + sourceName); + LOG.log(Level.FINEST, e, () -> "Failed to read config from resource: " + url); } return Collections.emptyList(); } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesXmlFormat.java ---------------------------------------------------------------------- diff --git a/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesXmlFormat.java b/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesXmlFormat.java index 8b3468b..ddfe723 100644 --- a/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesXmlFormat.java +++ b/modules/formats/src/main/java/org/apache/tamaya/format/PropertiesXmlFormat.java @@ -21,6 +21,7 @@ package org.apache.tamaya.format; import org.apache.tamaya.spi.PropertySource; import java.io.InputStream; +import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -28,7 +29,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Properties; -import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -71,10 +71,15 @@ public class PropertiesXmlFormat implements ConfigurationFormat { @SuppressWarnings("unchecked") @Override - public Collection<PropertySource> readConfiguration(String source, Supplier<InputStream> streamSupplier) { - final String name = "XML-Properties:" + Objects.requireNonNull(source) + ')'; + public Collection<PropertySource> readConfiguration(URL url) { + final String name; + if (Objects.requireNonNull(url).getQuery() == null) { + name = "XML-Properties(" + Objects.requireNonNull(url).toString() + ')'; + } else { + name = Objects.requireNonNull(url).getQuery(); + } List<PropertySource> propertySources = new ArrayList<>(); - try (InputStream is = streamSupplier.get()) { + try (InputStream is = url.openStream()) { if (is != null) { final Properties p = new Properties(); p.loadFromXML(is); @@ -102,7 +107,7 @@ public class PropertiesXmlFormat implements ConfigurationFormat { return propertySources; } } catch (Exception e) { - LOG.log(Level.FINEST, e, () -> "Failed to read config from resource: " + source); + LOG.log(Level.FINEST, e, () -> "Failed to read config from resource: " + url); } return Collections.emptyList(); } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractPathBasedPropertySourceProvider.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractPathBasedPropertySourceProvider.java b/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractPathBasedPropertySourceProvider.java index c4c5651..58acba3 100644 --- a/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractPathBasedPropertySourceProvider.java +++ b/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractPathBasedPropertySourceProvider.java @@ -23,6 +23,7 @@ import org.apache.tamaya.spi.PropertySource; import org.apache.tamaya.spi.PropertySourceProvider; import org.apache.tamaya.spi.ServiceContext; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -75,13 +76,13 @@ public abstract class AbstractPathBasedPropertySourceProvider implements Propert public Collection<PropertySource> getPropertySources() { List<PropertySource> propertySources = new ArrayList<>(); paths.forEach((path) -> { - for (Resource res : ServiceContext.getInstance().getService(ResourceResolver.class).get().getResources(path)) { + for (URL res : ServiceContext.getInstance().getService(ResourceResolver.class).get().getResources(path)) { try { for (ConfigurationFormat format : configFormats) { - propertySources.addAll(format.readConfiguration(sourceName, res)); + propertySources.addAll(format.readConfiguration(res)); } } catch (Exception e) { - LOG.log(Level.WARNING, "Failed to add resource based config: " + res.getName(), e); + LOG.log(Level.WARNING, "Failed to add resource based config: " + res, e); } } }); http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractResourcePropertySourceProvider.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractResourcePropertySourceProvider.java b/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractResourcePropertySourceProvider.java index 6edba5b..f1ec885 100644 --- a/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractResourcePropertySourceProvider.java +++ b/modules/resources/src/main/java/org/apache/tamaya/resource/AbstractResourcePropertySourceProvider.java @@ -22,6 +22,7 @@ import org.apache.tamaya.format.ConfigurationFormat; import org.apache.tamaya.spi.PropertySource; import org.apache.tamaya.spi.PropertySourceProvider; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -39,25 +40,25 @@ public abstract class AbstractResourcePropertySourceProvider implements Property /** The supported formats. */ private List<ConfigurationFormat> formats = new ArrayList<>(); /** The resource. */ - private Resource resource; + private URL resource; /** The source name used for creating the PropertySource names. */ private String sourceName; /** * Creates a new instance. - * @param resource the {@link Resource}, not null. + * @param resource the {@link URL}, not null. * @param formats the supported formats, not empty. */ - public AbstractResourcePropertySourceProvider(String sourceName, Resource resource, ConfigurationFormat... formats) { + public AbstractResourcePropertySourceProvider(String sourceName, URL resource, ConfigurationFormat... formats) { this(sourceName, resource, Arrays.asList(formats)); } /** * Creates a new instance. - * @param resource the {@link Resource}, not null. + * @param resource the {@link URL}, not null. * @param formats the supported formats, not empty. */ - public AbstractResourcePropertySourceProvider(String sourceName, Resource resource, List<ConfigurationFormat> formats) { + public AbstractResourcePropertySourceProvider(String sourceName, URL resource, List<ConfigurationFormat> formats) { this.resource = Objects.requireNonNull(resource); this.sourceName = Objects.requireNonNull(sourceName); if(formats.size()==0){ @@ -72,7 +73,7 @@ public abstract class AbstractResourcePropertySourceProvider implements Property * * @return the underlying resource, never null. */ - public Resource getResource() { + public URL getResource() { return this.resource; } @@ -90,9 +91,9 @@ public abstract class AbstractResourcePropertySourceProvider implements Property List<PropertySource> propertySources = new ArrayList<>(); for (ConfigurationFormat format : formats) { try { - propertySources.addAll(format.readConfiguration(sourceName, resource)); + propertySources.addAll(format.readConfiguration(resource)); } catch (Exception e) { - LOG.info(() -> "Format was not matching: " + format + " for resource: " + resource.getName()); + LOG.info(() -> "Format was not matching: " + format + " for resource: " + resource); } } return propertySources; http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/Resource.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/Resource.java b/modules/resources/src/main/java/org/apache/tamaya/resource/Resource.java deleted file mode 100644 index 39e34e9..0000000 --- a/modules/resources/src/main/java/org/apache/tamaya/resource/Resource.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.tamaya.resource; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.Objects; -import java.util.function.Supplier; - -/** - * Interface for an abstract resource. The effective resource implementation can be completely arbitrary. - * By default files, classpath format and URLs are supported, but alternate implementations are possible. - * - * @see #get() - * @see #toURI() - */ -public interface Resource extends Supplier<InputStream> { - - /** - * Return whether this resource actually exists. Depending on the resource this can delegate to - * {@link java.io.File#exists()} or whatever may be appropriate to check accessibility of the resource. - */ - default boolean exists() { - // Try to open a file first, if that fails try to open the stream... - try { - return new File(toURI()).exists(); - } catch (IOException ex) { - // Fallback - try { - InputStream is = get(); - is.close(); - return true; - } catch (Exception e) { - // ignore, just return false for non existing - return false; - } - } - } - - /** - * Checks whether the resource is accessible, meaning {@link #get()} should return a InputStream for reading the - * resource's content. - * - * @see #get() - */ - default boolean isAccessible() { - return true; - } - - /** - * Returns the resource as an URI. - * - * @throws IOException if the resource cannot be resolved as URI. - */ - URI toURI() throws IOException; - - /** - * Determines the length for this resource. - * - * @throws IOException if the resource is not readable. - */ - default long length() throws IOException { - try(InputStream is = this.get();) { - Objects.requireNonNull(is, "resource not available"); - long length = 0; - byte[] buf = new byte[256]; - int bytesRead; - while ((bytesRead = is.read(buf)) > 0) { - length += bytesRead; - } - return length; - } - } - - /** - * Determine the last-modified timestamp for a resource, as UTC ms timestamp - * - * @throws IOException if the resource is not accessible. - */ - default long lastModified() throws IOException{ - return new File(toURI()).lastModified(); - } - - /** - * Get a name for the resource. The name should be identifying the resource and also - * never change, so it must be eligible for hashcode/equals implementations. - */ - default String getName() { - try { - return toURI().toString(); - } catch (Exception e) { - return toString(); - } - } - - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java b/modules/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java index 7901ca6..31856cf 100644 --- a/modules/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java +++ b/modules/resources/src/main/java/org/apache/tamaya/resource/ResourceResolver.java @@ -18,6 +18,7 @@ */ package org.apache.tamaya.resource; +import java.net.URL; import java.util.Arrays; import java.util.Collection; @@ -28,16 +29,16 @@ import java.util.Collection; public interface ResourceResolver { /** - * Resolves resource expressions to a list of {@link Resource}s. Hereby + * Resolves resource expressions to a list of {@link URL}s. Hereby * the ordering of format matches the input of the resolved expressions. Nevertheless be aware that * there is no determined ordering of format located within a classloader. * * @param expressions the expressions to be resolved, not empty. - * @return the corresponding collection of current {@link Resource}s found, never + * @return the corresponding collection of current {@link URL}s found, never * null. * . */ - default Collection<Resource> getResources(Collection<String> expressions) { + default Collection<URL> getResources(Collection<String> expressions) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = getClass().getClassLoader(); @@ -46,45 +47,45 @@ public interface ResourceResolver { } /** - * Resolves resource expressions to a list of {@link Resource}s. Hereby + * Resolves resource expressions to a list of {@link URL}s. Hereby * the ordering of format matches the input of the resolved expressions. Nevertheless be aware that * there is no determined ordering of format located within a classloader. * * @param expressions the expressions to be resolved, not empty. - * @return the corresponding collection of current {@link Resource}s found, never + * @return the corresponding collection of current {@link URL}s found, never * null. * . */ - default Collection<Resource> getResources(String... expressions) { + default Collection<URL> getResources(String... expressions) { return getResources(Arrays.asList(expressions)); } /** - * Resolves resource expressions to a list of {@link Resource}s, considerubg + * Resolves resource expressions to a list of {@link URL}s, considerubg * the given classloader for classloader dependent format. Hereby * the ordering of format matches the input of the resolved expressions. Nevertheless be aware that * there is no determined ordering of format located within a classloader. * * @param expressions the expressions to be resolved, not empty. - * @return the corresponding collection of current {@link Resource}s found, never + * @return the corresponding collection of current {@link URL}s found, never * null. * . */ - default Collection<Resource> getResources(ClassLoader classLoader, String... expressions){ + default Collection<URL> getResources(ClassLoader classLoader, String... expressions){ return getResources(classLoader, Arrays.asList(expressions)); } /** - * Resolves resource expressions to a list of {@link Resource}s, considerubg + * Resolves resource expressions to a list of {@link URL}s, considerubg * the given classloader for classloader dependent format. Hereby * the ordering of format matches the input of the resolved expressions. Nevertheless be aware that * there is no determined ordering of format located within a classloader. * * @param expressions the expressions to be resolved, not empty. - * @return the corresponding collection of current {@link Resource}s found, never + * @return the corresponding collection of current {@link URL}s found, never * null. * . */ - Collection<Resource> getResources(ClassLoader classLoader, Collection<String> expressions); + Collection<URL> getResources(ClassLoader classLoader, Collection<String> expressions); } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClassPathResource.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClassPathResource.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClassPathResource.java deleted file mode 100644 index b901164..0000000 --- a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClassPathResource.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.tamaya.resource.internal; - -import org.apache.tamaya.resource.Resource; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Implementation of {@link Resource} to be loaded from the classpath. - */ -public class ClassPathResource implements Resource { - - private static final Logger LOG = Logger.getLogger(ClassPathResource.class.getName()); - - private final String path; - - private ClassLoader classLoader; - - - /** - * Create a new resource using the current context class loader. - * - * @param path the resource path, not null - * @see java.lang.ClassLoader#getResourceAsStream(String) - */ - public ClassPathResource(String path) { - this(path, (ClassLoader) null); - } - - /** - * Create a new resource using the given class loader. - * - * @param path the resource path, not null - * @param classLoader the class loader to load the resource with, - * or {@code null} for the current context class loader - * @see ClassLoader#getResourceAsStream(String) - */ - public ClassPathResource(String path, ClassLoader classLoader) { - Objects.requireNonNull(path, "Path null"); - if (path.startsWith("/")) { - path = path.substring(1); - } - this.path = path.trim(); - if (classLoader == null) { - classLoader = Thread.currentThread().getContextClassLoader(); - } - if (classLoader == null) { - classLoader = getClass().getClassLoader(); - } - this.classLoader = classLoader; - } - - /** - * @return the path for this resource. - */ - public final String getPath() { - return this.path; - } - - /** - * @return the ClassLoader that this resource will be accessed from. - */ - public final ClassLoader getClassLoader() { - return this.classLoader; - } - - - /** - * Checks if the given resource is resolvable from the configured classloader. - * - * @see java.lang.ClassLoader#getResource(String) - */ - @Override - public boolean exists() { - return (resolveURL() != null); - } - - /** - * Resolves a URL for the underlying class path resource. - * - * @return the resolved URL, or {@code null} - */ - protected URL resolveURL() { - return this.classLoader.getResource(this.path); - } - - /** - * This implementation opens an InputStream for the given class path resource. - * - * @see java.lang.ClassLoader#getResourceAsStream(String) - * @see java.lang.Class#getResourceAsStream(String) - */ - @Override - public InputStream get() { - try { - InputStream is = this.classLoader.getResourceAsStream(this.path); - if (is == null) { - throw new IOException(getName() + " does not exist"); - } - return is; - } catch (IOException e) { - LOG.log(Level.INFO, "Failed to open classpath resource: " + path, e); - return null; - } - } - - @Override - public URI toURI() throws IOException { - try { - return resolveURL().toURI(); - } catch (URISyntaxException e) { - throw new IOException(e); - } - } - - @Override - public long lastModified() throws IOException { - return 0; - } - - /** - * This implementation returns the name current the file that this class path - * resource refers to. - */ - @Override - public String getName() { - return "classpath:" + path; - } - - /** - * This implementation returns a description that includes the class path location. - */ - @Override - public String toString() { - return "ClassPathResource[" + path + ']'; - } - - /** - * This implementation compares the underlying class path locations. - */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (obj instanceof ClassPathResource) { - ClassPathResource otherRes = (ClassPathResource) obj; - return (this.path.equals(otherRes.path) && - Objects.equals(this.classLoader, otherRes.classLoader)); - } - return false; - } - - /** - * This implementation returns the hash code current the underlying - * class path location. - */ - @Override - public int hashCode() { - return getName().hashCode(); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClasspathCollector.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClasspathCollector.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClasspathCollector.java new file mode 100644 index 0000000..3f2093b --- /dev/null +++ b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClasspathCollector.java @@ -0,0 +1,264 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tamaya.resource.internal; + +import java.io.File; +import java.io.IOException; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.List; +import java.util.Objects; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Collector that searches files based on ant styled patterns. For example the following patterns would be matched: + * <pre> + * classpath:javax/annotations/* + * javax?/annotations/**/*.class + * org/apache/tamaya/**/tamayaconfig.properties + * </pre> + */ +public class ClasspathCollector { + + /** + * JAR protocol. + */ + public static final String PROTOCOL_JAR = "jar"; + + /** + * Separator between JAR file URL and the internal jar file path. + */ + public static final String JAR_URL_SEPARATOR = "!/"; + + /** + * ZIP protocol. + */ + public static final String PROTOCOL_ZIP = "zip"; + + /** + * ZIP protocol for a JBoss jar file entry: "vfszip". + */ + public static final String PROTOCOL_VFSZIP = "vfszip"; + + /** + * URL protocol for an WebSphere jar file: "wsjar". + */ + public static final String PROTOCOL_WSJAR = "wsjar"; + + /** + * URL protocol for an entry from an OC4J jar. + */ + public static final String PROTOCOL_CODE_SOURCE = "code-source"; + + /** + * The logger used. + */ + private static final Logger LOG = Logger.getLogger(ClasspathCollector.class.getName()); + + /** + * The classloader used to load the resources. + */ + private ClassLoader classLoader; + + /** + * Creates a new instance. + * + * @param classLoader the class loader to be used, not null. + */ + public ClasspathCollector(ClassLoader classLoader) { + this.classLoader = Objects.requireNonNull(classLoader); + } + + /** + * Collect all classpath resources given the expression. + * + * @param expression the expression, not null. + * @return the resources found. + */ + public Collection<URL> collectFiles(String expression) { + if (expression.startsWith("classpath:")) { + expression = expression.substring("classpath:".length()); + } + if (expression.startsWith("/")) { + expression = expression.substring(1); + } + Locator locator = Locator.of(expression); + List<URL> result = new ArrayList<>(); + String rootPath = locator.getRootPath(); + try { + Enumeration<URL> rootResources = this.classLoader.getResources(rootPath); + while (rootResources.hasMoreElements()) { + URL resource = rootResources.nextElement(); + try { + if (isJarFile(resource)) { + result.addAll(doFindPathMatchingJarResources(resource, locator.getSubPath())); + } else { + File file = getFile(resource); + result.addAll(FileCollector.traverseAndSelectFromChildren(file, locator.getSubPathTokens(), 0)); + } + } catch (Exception e) { + LOG.log(Level.SEVERE, "Error locating resources for: " + expression, e); + } + } + } catch (IOException e) { + LOG.log(Level.SEVERE, "Error locating resources for: " + expression, e); + } + return result; + } + + + /** + * Find all resources in jar files that match the given location pattern + * via the Ant-style PathMatcher. + * + * @param rootDirResource the root directory as Resource + * @param subPattern the sub pattern to match (below the root directory) + * @return the Set of matching Resource instances + * @throws java.io.IOException in case of I/O errors + * @see java.net.JarURLConnection + */ + protected Collection<URL> doFindPathMatchingJarResources(URL rootDirResource, String subPattern) + throws IOException, URISyntaxException { + subPattern = subPattern.replace("*", ".*").replace("?", ".?").replace(".*.*", ".*"); + URLConnection con = rootDirResource.toURI().toURL().openConnection(); + JarFile jarFile; + boolean newJarFile = false; + String jarFileUrl; + String rootEntryPath; + + if (con instanceof JarURLConnection) { + // Should usually be the case for traditional JAR files. + JarURLConnection jarCon = (JarURLConnection) con; + jarCon.setUseCaches(false); + jarFile = jarCon.getJarFile(); + jarFileUrl = jarCon.getJarFileURL().toExternalForm(); + JarEntry jarEntry = jarCon.getJarEntry(); + rootEntryPath = (jarEntry != null ? jarEntry.getName() : ""); + } else { + // No JarURLConnection -> need to resort to URL file parsing. + // We'll assume URLs of the format "jar:path!/entry", with the protocol + // being arbitrary as long as following the entry format. + // We'll also handle paths with and without leading "file:" prefix. + String urlFile = rootDirResource.toURI().toURL().getFile(); + int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR); + jarFileUrl = urlFile.substring(0, separatorIndex); + if (jarFileUrl.startsWith("file:")) { + jarFileUrl = jarFileUrl.substring("file:".length()); + } + jarFile = new JarFile(jarFileUrl); + newJarFile = true; + jarFileUrl = "file:" + jarFileUrl; + rootEntryPath = urlFile.substring(separatorIndex + JAR_URL_SEPARATOR.length()); + } + + try { + if (LOG.isLoggable(Level.FINEST)) { + LOG.finest("Looking for matching resources in jar file [" + jarFileUrl + "]"); + } + if (!rootEntryPath.isEmpty() && !rootEntryPath.endsWith("/")) { + // Root entry path must end with slash for correct matching + rootEntryPath = rootEntryPath + '/'; + } + Collection<URL> result = new ArrayList<>(10); + for (Enumeration entries = jarFile.entries(); entries.hasMoreElements(); ) { + JarEntry entry = (JarEntry) entries.nextElement(); + String entryPath = entry.getName(); + if (entryPath.startsWith(rootEntryPath)) { + String relativePath = entryPath.substring(rootEntryPath.length()); + if (relativePath.matches(subPattern)) { + result.add(createRelativeFrom(rootDirResource, relativePath)); + } + } + } + return result; + } finally { + // Close jar file, but only if freshly obtained - + // not from JarURLConnection, which might cache the file reference. + if (newJarFile) { + jarFile.close(); + } + } + } + + /** + * Creates a new URL based on the given root path and the relative path to be added. + * + * @param url the root, not null + * @param relativePath the relative path to be added, not null + * @return the new URL instance + * @throws MalformedURLException + */ + private URL createRelativeFrom(URL url, String relativePath) + throws MalformedURLException { + String rootDirResource = url.toExternalForm(); + if (relativePath.startsWith("/")) { + relativePath = relativePath.substring(1); + } + if (!rootDirResource.endsWith("/")) { + rootDirResource = rootDirResource + '/'; + } + return new URL(rootDirResource + relativePath); + } + + + /** + * Small check if a given URL is a jar file URL. + * + * @param url the URL to check, not null. + * @return true if the URL has one of the following protocols: jar, zip, vfszip, wsjar, code-source. + */ + private boolean isJarFile(URL url) { + String protocol = Objects.requireNonNull(url).getProtocol(); + return (PROTOCOL_JAR.equals(protocol) || + PROTOCOL_ZIP.equals(protocol) || + PROTOCOL_VFSZIP.equals(protocol) || + PROTOCOL_WSJAR.equals(protocol) || + (PROTOCOL_CODE_SOURCE.equals(protocol) && url.getPath().indexOf(JAR_URL_SEPARATOR) != -1)); + } + + /** + * Creates a file from an URL. + * + * @param resourceUrl the url, not null. + * @return a new file instance. The instance still may not exist. if the url's protocol is not 'file', {@code null} + * is returned. + */ + private File getFile(URL resourceUrl) { + Objects.requireNonNull(resourceUrl, "Resource URL must not be null"); + if (!"file".equals(resourceUrl.getProtocol())) { + return null; + } + try { + return new File(resourceUrl.toURI().getSchemeSpecificPart()); + } catch (Exception ex) { + // Fallback for URLs that are not valid URIs (should hardly ever happen). + return new File(resourceUrl.getFile()); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java index 9fd6fc4..c90e53a 100644 --- a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java +++ b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/DefaultResourceResolver.java @@ -18,7 +18,6 @@ */ package org.apache.tamaya.resource.internal; -import org.apache.tamaya.resource.Resource; import org.apache.tamaya.resource.ResourceResolver; import javax.annotation.Priority; @@ -39,10 +38,10 @@ public class DefaultResourceResolver implements ResourceResolver { private static final Logger LOG = Logger.getLogger(DefaultResourceResolver.class.getName()); @Override - public List<Resource> getResources(ClassLoader classLoader, Collection<String> expressions) { - List<Resource> resources = new ArrayList<>(); + public List<URL> getResources(ClassLoader classLoader, Collection<String> expressions) { + List<URL> resources = new ArrayList<>(); for (String expression : expressions) { - if (tryClassPath(classLoader, expression, resources) || tryFile(expression, resources) || + if (tryPath(classLoader, expression, resources) || tryClassPath(classLoader, expression, resources) || tryFile(expression, resources) || tryURL(expression, resources)) { continue; } @@ -51,12 +50,41 @@ public class DefaultResourceResolver implements ResourceResolver { return resources; } - private boolean tryClassPath(ClassLoader classLoader, String expression, List<Resource> resources) { + /** + * Tries to evaluate the location passed by Ant path matching. + * @param classLoader the class loader to use + * @param expression the path expression + * @param resources the resources for adding the results + * @return true, if the expression could be resolved. + */ + private boolean tryPath(ClassLoader classLoader, String expression, List<URL> resources) { + try { + // 1: try file path + Collection<URL> found = FileCollector.collectFiles(expression); + if (found.isEmpty()) { + found = new ClasspathCollector(classLoader).collectFiles(expression); + } + resources.addAll(found); + return !found.isEmpty(); + } catch (Exception e) { + LOG.finest(() -> "Failed to load resource from CP: " + expression); + } + return false; + } + + /** + * Tries to evaluate the location passed by loading from the classloader. + * @param classLoader the class loader to use + * @param expression the path expression + * @param resources the resources for adding the results + * @return true, if the expression could be resolved. + */ + private boolean tryClassPath(ClassLoader classLoader, String expression, List<URL> resources) { try { Enumeration<URL> urls = classLoader.getResources(expression); while (urls.hasMoreElements()) { URL url = urls.nextElement(); - resources.add(new UrlResource(url)); + resources.add(url); } return !resources.isEmpty(); } catch (Exception e) { @@ -65,11 +93,17 @@ public class DefaultResourceResolver implements ResourceResolver { return false; } - private boolean tryFile(String expression, List<Resource> resources) { + /** + * Tries to evaluate the location passed by lokking up a file. + * @param expression the path expression + * @param resources the resources for adding the results + * @return true, if the expression could be resolved. + */ + private boolean tryFile(String expression, List<URL> resources) { try { File file = new File(expression); if (file.exists()) { - resources.add(new FileResource(file)); + resources.add(file.toURI().toURL()); return true; } } catch (Exception e) { @@ -78,16 +112,21 @@ public class DefaultResourceResolver implements ResourceResolver { return false; } - private boolean tryURL(String expression, List<Resource> resources) { + /** + * Tries to interpret the expression as URL. + * @param expression the path expression + * @param resources the resources for adding the results + * @return true, if the expression could be resolved. + */ + private boolean tryURL(String expression, List<URL> resources) { try { URL url = new URL(expression); - resources.add(new UrlResource(url)); + resources.add(url); return true; } catch (Exception e) { LOG.finest(() -> "Failed to load resource from file: " + expression); } return false; - } } http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/internal/FileCollector.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/FileCollector.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/FileCollector.java new file mode 100644 index 0000000..d506e11 --- /dev/null +++ b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/FileCollector.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tamaya.resource.internal; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; + +/** + * Collector that searches files based on ant styled patterns. For example the following patterns would be matched: + * <pre> + * file:C:/temp/*.txt + * file:C:\**\*.ini + * C:\Programs\**/*.ini + * /user/home/A*b101_?.pid + * /var/logs/**/*.log + * </pre> + */ +public class FileCollector { + + public static final String FILE_PREFIX = "file:"; + + private FileCollector() { + } + + private static final Logger LOG = Logger.getLogger(FileCollector.class.getName()); + + public static Collection<URL> collectFiles(String expression) { + expression = expression.replace("\\", "/"); + Locator locator = Locator.of(expression); + List<URL> result = new ArrayList<>(); + String rootPath = locator.getRootPath(); + if (rootPath.startsWith(FILE_PREFIX)) { + rootPath = rootPath.substring(FILE_PREFIX.length()); + } + File file = new File(rootPath); + if (file.exists()) { + List<String> subTokens = locator.getSubPathTokens(); + result.addAll(traverseAndSelectFromChildren(file, subTokens, 0)); + } + return result; + } + + static Collection<URL> traverseAndSelectFromChildren(File dir, List<String> subTokens, int tokenIndex) { + if (tokenIndex >= subTokens.size() || dir.isFile()) { + return Collections.emptyList(); + } + List<URL> result = new ArrayList<>(); + String token = subTokens.get(tokenIndex); + if (token.equals("**")) { + result.addAll(traverseAndSelectFromChildren(dir, getSubExpression(subTokens, tokenIndex + 1))); + } else { + token = token.replace("*", ".*"); + File[] files = dir.listFiles(); + if (tokenIndex == subTokens.size() - 1) { + // select files! + for (File f : files) { + if (f.isFile() && f.getName().matches(token)) { + result.add(getURL(f)); + } + } + } else { + // check directory pattern + for (File f : files) { + if (f.isDirectory() && f.getName().matches(token)) { + result.addAll(traverseAndSelectFromChildren(f, subTokens, tokenIndex + 1)); + } + } + } + } + return result; + } + + static Collection<URL> traverseAndSelectFromChildren(File file, String subExpression) { + List<URL> result = new ArrayList<>(); + for (File childFile : file.listFiles()) { + if (childFile.isFile()) { + if (childFile.getName().matches(subExpression)) { + try { + result.add(getURL(childFile)); + } catch (Exception e) { + LOG.warning(() -> "File not convertible to URL: " + childFile); + } + } + } else if (childFile.isDirectory()) { + result.addAll(traverseAndSelectFromChildren(childFile, subExpression)); + } + } + return result; + } + + private static boolean matchesFile(File childFile, List<String> subTokens, int tokenIndex) { + if (tokenIndex < (subTokens.size() - 1)) { + // not all tokens consumed, so no match! + return false; + } + String tokenToMatch = subTokens.get(tokenIndex); + tokenToMatch = tokenToMatch.replace("*", ".*"); + return childFile.getName().matches(tokenToMatch); + } + + /** + * Get an URL from a file. + * + * @param file the file, not null. + * @return the URL, never null. + * @throws java.lang.IllegalStateException if it fails to create the URL + */ + private static URL getURL(File file) { + Objects.requireNonNull(file); + try { + return file.toURI().toURL(); + } catch (Exception e) { + throw new IllegalStateException("Failed to create URL from file: " + file); + } + } + + /** + * Constructs a sub expression, using the tokens from {@code subTokens} starting at index {@code startIndex}. + * + * @param subTokens the token list, not null + * @param startIndex the start index from where tokens should be taken to produce the path. + * @return the constructed path, never null. + */ + private static String getSubExpression(List<String> subTokens, int startIndex) { + StringBuilder b = new StringBuilder(); + for (int i = startIndex; i < subTokens.size(); i++) { + b.append(subTokens.get(i)); + b.append('/'); + } + if (b.length() > 0) { + b.setLength(b.length() - 1); + } + return b.toString().replaceAll("\\*", ".*").replaceAll("\\?", ".?"); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/internal/FileResource.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/FileResource.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/FileResource.java deleted file mode 100644 index c5f521a..0000000 --- a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/FileResource.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.tamaya.resource.internal; - -import org.apache.tamaya.resource.Resource; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Implementation of {@link Resource} to be loaded from a file. - * - * @see java.io.File - */ -public class FileResource implements Resource { - - private static final Logger LOG = Logger.getLogger(FileResource.class.getName()); - - private final File file; - - /** - * Creates a new instance. - * - * @param file a File, not null. - */ - public FileResource(File file) { - this.file = Objects.requireNonNull(file, "File must not be null"); - } - - /** - * Crreates a new instance. - * - * @param filePath a file path - */ - public FileResource(String filePath) { - Objects.requireNonNull(filePath, "Path must not be null"); - this.file = new File(filePath); - } - - - /** - * Get the file path for this resource. - */ - public final String getPath() { - return this.file.getPath(); - } - - - /** - * This implementation returns whether the underlying file exists. - * - * @see java.io.File#exists() - */ - @Override - public boolean exists() { - return this.file.exists(); - } - - /** - * This implementation checks whether the underlying file is marked as readable - * (and corresponds to an actual file with content, not to a directory). - * - * @see java.io.File#canRead() - * @see java.io.File#isDirectory() - */ - @Override - public boolean isAccessible() { - return (this.file.canRead() && !this.file.isDirectory()); - } - - /** - * This implementation opens a FileInputStream for the underlying file. - * - * @see java.io.FileInputStream - */ - @Override - public InputStream get() { - try { - return new FileInputStream(this.file); - } catch (Exception e) { - LOG.log(Level.INFO, "Failed to open file: " + file.getAbsolutePath(), e); - return null; - } - } - - /** - * This implementation returns a URI for the underlying file. - * - * @see java.io.File#toURI() - */ - @Override - public URI toURI() throws IOException { - return this.file.toURI(); - } - - /** - * Returns the underlying File's length. - */ - @Override - public long length() throws IOException { - return this.file.length(); - } - - @Override - public long lastModified() throws IOException { - return file.lastModified(); - } - - /** - * Returns the name of the current file. - * - * @see java.io.File#getName() - */ - @Override - public String getName() { - return this.file.getName(); - } - - /** - * Returns a description that includes the absolute - * path of the current file. - * - * @see java.io.File#getAbsolutePath() - */ - @Override - public String toString() { - return "File [" + this.file.getAbsolutePath() + "]"; - } - - - // implementation current WritableResource - - /** - * Compares the underlying Files. - */ - @Override - public boolean equals(Object obj) { - return (obj == this || - (obj instanceof FileResource && this.file.equals(((FileResource) obj).file))); - } - - /** - * Returns hash code current the underlying File reference. - */ - @Override - public int hashCode() { - return this.file.hashCode(); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/internal/InputStreamResource.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/InputStreamResource.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/InputStreamResource.java deleted file mode 100644 index d4aa673..0000000 --- a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/InputStreamResource.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.tamaya.resource.internal; - -import org.apache.tamaya.resource.Resource; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.Objects; - -/** - * Simple Resource encapsulating an InputStream. - */ -public class InputStreamResource implements Resource { - - /** The InputStream. */ - private final InputStream inputStream; - /** The read flag. */ - private boolean read = false; - /** The name of the resource. */ - private String name; - - - /** - * Create a new InputStreamResource. - * - * @param inputStream the InputStream to use - */ - public InputStreamResource(InputStream inputStream) { - this(inputStream, "InputStream:"); - } - - /** - * Create a new InputStreamResource. - * - * @param inputStream the InputStream to use - * @param name where the InputStream comes from - */ - public InputStreamResource(InputStream inputStream, String name) { - this.inputStream = Objects.requireNonNull(inputStream); - this.name = (name != null ? name : "InputStream"); - } - - - /** - * This implementation always returns {@code true}. - */ - @Override - public boolean exists() { - return true; - } - - @Override - public URI toURI() throws IOException { - throw new IOException("URI not available."); - } - - @Override - public long lastModified() throws IOException { - throw new IOException("lastModified not available."); - } - - /** - * Accesses the input stream. Hereby the input stream can only accessed once. - */ - @Override - public InputStream get() { - if (this.read) { - throw new IllegalStateException("InputStream can only be read once!"); - } - this.read = true; - return this.inputStream; - } - - /** - * This implementation returns the passed-in description, if any. - */ - public String toString() { - return this.name != null ? this.name : super.toString(); - } - - - /** - * Compares the underlying InputStream. - */ - @Override - public boolean equals(Object obj) { - return (obj == this || - (obj instanceof InputStreamResource && ((InputStreamResource) obj).inputStream.equals(this.inputStream))); - } - - /** - * This implementation returns the hash code current the underlying InputStream. - */ - @Override - public int hashCode() { - return this.inputStream.hashCode(); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/internal/Locator.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/Locator.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/Locator.java new file mode 100644 index 0000000..8d9e08a --- /dev/null +++ b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/Locator.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.tamaya.resource.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Small helper class that manages the path parts of a location expression. + */ +final class Locator { + /** + * The tokenized location expression. + */ + private List<String> tokens; + + /** + * Creates a new instances based on the tokenized expression. + * + * @param tokens the tokenized expression, not null. + */ + Locator(List<String> tokens) { + this.tokens = tokens; + } + + /** + * Creates a new instance of the corresponding expression. + * + * @param expression the location expression, not null. + * @return the tokenized instance. + */ + public static Locator of(String expression) { + return new Locator(Arrays.asList(expression.split("/")).stream().filter((s) -> !s.isEmpty()).collect(Collectors.toList())); + } + + /** + * Access the root path, which is the location expression, before any wildcards or placeholders are used. + * It is used as the entry point into the file system or for accessing base classpath resources, before + * further analysis on the file or jar filesystem can be performed. + * + * @return the root path, never null. + */ + public String getRootPath() { + StringBuilder builder = new StringBuilder(); + for (String token : tokens) { + if (containsPlaceholder(token)) { + break; + } else { + builder.append(token); + builder.append('/'); + } + } + if (builder.length() > 0) { + builder.setLength(builder.length() - 1); + } + return builder.toString(); + } + + /** + * Return the sub expression path, which contains the second part of the expression, starting with a placeholder + * or wildcard token. + * + * @return the sub expression part, never null. + */ + public String getSubPath() { + StringBuilder builder = new StringBuilder(); + for (String token : getSubPathTokens()) { + builder.append(token); + builder.append('/'); + } + if (builder.length() > 0) { + builder.setLength(builder.length() - 1); + } + return builder.toString(); + } + + /** + * This method returns the single tokenized form of the sub expression. + * + * @return the tokenized version of the sub path. + * @see #getSubPath() + */ + public List<String> getSubPathTokens() { + List<String> subTokens = new ArrayList<>(); + for (String token : tokens) { + if (!containsPlaceholder(token)) { + continue; + } else { + subTokens.add(token); + } + } + return subTokens; + } + + /** + * Access the full reconstructed path. In most cases this should match the original expression. + * + * @return the full expression path, never null. + */ + public String getPath() { + StringBuilder builder = new StringBuilder(); + for (String token : tokens) { + builder.append(token); + builder.append('/'); + } + if (builder.length() > 0) { + builder.setLength(builder.length() - 1); + } + return builder.toString(); + } + + /** + * Short method that checks for '*' and '?' chars. + * + * @param token the token to check, not null + * @return true, if it contains wildcard characters. + */ + private boolean containsPlaceholder(String token) { + return token.contains("*") || token.contains("?"); + } + + /** + * Return the expressions' path. + * + * @return the locator path. + */ + @Override + public String toString() { + return "Locator: " + getPath(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/ae66299e/modules/resources/src/main/java/org/apache/tamaya/resource/internal/UrlResource.java ---------------------------------------------------------------------- diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/UrlResource.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/UrlResource.java deleted file mode 100644 index 6522cf5..0000000 --- a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/UrlResource.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.tamaya.resource.internal; - -import org.apache.tamaya.resource.Resource; - -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLConnection; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Implementation of a resource based on a {@code java.net.URL}. - */ -public class UrlResource implements Resource { - - private static final Logger LOG = Logger.getLogger(UrlResource.class.getName()); - - /** - * Original URL, used for actual access. - */ - private final URL url; - - /** - * Create a new instance based on the given URL. - * - * @param url a URL - */ - public UrlResource(URL url) { - this.url = Objects.requireNonNull(url, "URL null"); - } - - /** - * Create a new URLResource based on a URL path. - * - * @param path a URL path - * @throws MalformedURLException if the given URL path is not valid - * @see java.net.URL#URL(String) - */ - public UrlResource(String path) throws MalformedURLException { - Objects.requireNonNull(path, "Path must not be null"); - this.url = new URL(path); - } - - - /** - * This implementation opens an InputStream for the given URL. - * - * @see java.net.URL#openConnection() - * @see java.net.URLConnection#setUseCaches(boolean) - * @see java.net.URLConnection#getInputStream() - */ - @Override - public InputStream get() { - URLConnection con = null; - try { - con = this.url.openConnection(); - useCachesIfNecessary(con); - return con.getInputStream(); - } catch (IOException e) { - if (con instanceof HttpURLConnection) { - ((HttpURLConnection) con).disconnect(); - } - LOG.log(Level.INFO, "Failed to open URL: " + url, e); - return null; - } - } - - @Override - public URI toURI() throws IOException { - try { - return this.url.toURI(); - } catch (URISyntaxException e) { - throw new IOException("Failed to create URI from " + url); - } - } - - @Override - public String getName() { - return this.url.toString(); - } - - @Override - public String toString() { - return "URL [" + this.url + "]"; - } - - /** - * Compares the underlying URL references. - */ - @Override - public boolean equals(Object obj) { - return (obj == this || - (obj instanceof UrlResource && this.url.equals(((UrlResource) obj).url))); - } - - /** - * This implementation returns the hash code current the underlying URL reference. - */ - @Override - public int hashCode() { - return this.url.hashCode(); - } - - /** - * Set the {@link URLConnection#setUseCaches "useCaches"} flag on the - * given connection, preferring {@code false} but leaving the - * flag at {@code true} for JNLP based format. - * - * @param con the URLConnection to set the flag on - */ - private void useCachesIfNecessary(URLConnection con) { - con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP")); - } - -} -