blob: 2d082178013b249309464f1357c57623e28c08a6 [file] [log] [blame]
// ***************************************************************************************************************************
// * 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.juneau.utils;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import org.apache.juneau.internal.*;
/**
* Class for retrieving and caching resource files from the classpath.
*/
public final class ClasspathResourceManager {
// Maps resource names+locales to found resources.
private final ConcurrentHashMap<ResourceKey,byte[]> byteCache;
private final ConcurrentHashMap<ResourceKey,String> stringCache;
private final Class<?> baseClass;
private final ClasspathResourceFinder resourceFinder;
private final boolean useCache;
/**
* Constructor.
*
* @param baseClass The default class to use for retrieving resources from the classpath.
* @param resourceFinder The resource finder implementation.
* @param useCache If <jk>true</jk>, retrieved resources are stored in an in-memory cache for fast lookup.
*/
public ClasspathResourceManager(Class<?> baseClass, ClasspathResourceFinder resourceFinder, boolean useCache) {
this.baseClass = baseClass;
this.resourceFinder = resourceFinder;
this.useCache = useCache;
if (useCache) {
this.byteCache = new ConcurrentHashMap<>();
this.stringCache = new ConcurrentHashMap<>();
} else {
this.byteCache = null;
this.stringCache = null;
}
}
/**
* Constructor.
*
* <p>
* Uses default {@link ClasspathResourceFinderBasic} for finding resources.
*
* @param baseClass The default class to use for retrieving resources from the classpath.
*/
public ClasspathResourceManager(Class<?> baseClass) {
this(baseClass, new ClasspathResourceFinderBasic(), false);
}
/**
* Finds the resource with the given name.
*
* @param name Name of the desired resource.
* @return An input stream to the object, or <jk>null</jk> if the resource could not be found.
* @throws IOException Thrown by underlying stream.
*/
public InputStream getStream(String name) throws IOException {
return getStream(name, null);
}
/**
* Finds the resource with the given name for the specified locale and returns it as an input stream.
*
* @param name Name of the desired resource.
* @param locale The locale. Can be <jk>null</jk>.
* @return An input stream to the object, or <jk>null</jk> if the resource could not be found.
* @throws IOException Thrown by underlying stream.
*/
public InputStream getStream(String name, Locale locale) throws IOException {
return getStream(baseClass, name, locale);
}
/**
* Finds the resource with the given name for the specified locale and returns it as an input stream.
*
* @param baseClass
* Overrides the default class to use for retrieving the classpath resource.
* <br>If <jk>null</jk>, uses the base class passed in through the constructor of this class.
* @param name Name of the desired resource.
* @param locale The locale. Can be <jk>null</jk>.
* @return An input stream to the object, or <jk>null</jk> if the resource could not be found.
* @throws IOException Thrown by underlying stream.
*/
public InputStream getStream(Class<?> baseClass, String name, Locale locale) throws IOException {
if (baseClass == null)
baseClass = this.baseClass;
if (! useCache)
return resourceFinder.findResource(baseClass, name, locale);
ResourceKey key = new ResourceKey(name, locale);
byte[] r = byteCache.get(key);
if (r == null) {
try (InputStream is = resourceFinder.findResource(baseClass, name, locale)) {
if (is != null)
byteCache.putIfAbsent(key, IOUtils.readBytes(is, 1024));
}
}
r = byteCache.get(key);
return r == null ? null : new ByteArrayInputStream(r);
}
/**
* Finds the resource with the given name and converts it to a simple string.
*
* @param name Name of the desired resource.
* @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
* @throws IOException Thrown by underlying stream.
*/
public String getString(String name) throws IOException {
return getString(baseClass, name, null);
}
/**
* Finds the resource with the given name and converts it to a simple string.
*
* @param baseClass
* Overrides the default class to use for retrieving the classpath resource.
* <br>If <jk>null</jk>, uses the base class passed in through the constructor of this class.
* @param name Name of the desired resource.
* @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
* @throws IOException Thrown by underlying stream.
*/
public String getString(Class<?> baseClass, String name) throws IOException {
return getString(baseClass, name, null);
}
/**
* Finds the resource with the given name and converts it to a simple string.
*
* @param name Name of the desired resource.
* @param locale The locale. Can be <jk>null</jk>.
* @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
* @throws IOException Thrown by underlying stream.
*/
public String getString(String name, Locale locale) throws IOException {
return getString(baseClass, name, locale);
}
/**
* Finds the resource with the given name and converts it to a simple string.
*
* @param baseClass
* Overrides the default class to use for retrieving the classpath resource.
* <br>If <jk>null</jk>, uses the base class passed in through the constructor of this class.
* @param name Name of the desired resource.
* @param locale The locale. Can be <jk>null</jk>.
* @return The resource converted to a string, or <jk>null</jk> if the resource could not be found.
* @throws IOException Thrown by underlying stream.
*/
public String getString(Class<?> baseClass, String name, Locale locale) throws IOException {
if (baseClass == null)
baseClass = this.baseClass;
if (! useCache) {
try (InputStream is = resourceFinder.findResource(baseClass, name, locale)) {
return IOUtils.read(is, IOUtils.UTF8);
}
}
ResourceKey key = new ResourceKey(name, locale);
String r = stringCache.get(key);
if (r == null) {
try (InputStream is = resourceFinder.findResource(baseClass, name, locale)) {
if (is != null)
stringCache.putIfAbsent(key, IOUtils.read(is, IOUtils.UTF8));
}
}
return stringCache.get(key);
}
private class ResourceKey {
final String name;
final Locale locale;
ResourceKey(String name, Locale locale) {
this.name = name;
this.locale = locale;
}
@Override
public int hashCode() {
return name.hashCode() + (locale == null ? 0 : locale.hashCode());
}
@Override
public boolean equals(Object o) {
if (o == null)
return false;
ResourceKey ok = (ResourceKey)o;
return ObjectUtils.equals(name, ok.name) && ObjectUtils.equals(locale, ok.locale);
}
}
}