All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.inftec.ju.util.JuUrl Maven / Gradle / Ivy

package ch.inftec.ju.util;

import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.inftec.ju.util.RegexUtil.Match;

public class JuUrl {
	private static Logger logger = LoggerFactory.getLogger(JuUrl.class);
	
	/**
	 * Gets the resource with the specified name, using the ClassLoader to
	 * resolve the resource.
	 * @param resourceName Absolute name, not starting with a '/'
	 * @return (First) found resource as URL or null if none was found
	 */
	public static URL resource(String resourceName) {
		return JuUrl.resource().get(resourceName);
	}
	
	/**
	 * Gets a resource URL relative to the specified class.
	 * @param resourceName Resource name
	 * @param relativeClass Class used to resolve the resource
	 * @return (First) found resource as URL or null if none was found
	 */
	public static URL resourceRelativeTo(String resourceName, Class relativeClass) {
		return JuUrl.resource().relativeTo(relativeClass).get(resourceName);
	}
	
	/**
	 * Gets an existing resource relative to a specified class, throwing an exception if no resource
	 * is found.
	 * @param resourceName Resource name
	 * @param relativeClass Class used to resolve the resource
	 * @return (First) found resource as URL
	 */
	public static URL existingResourceRelativeTo(String resourceName, Class relativeClass) {
		return JuUrl.resource().relativeTo(relativeClass).exceptionIfNone().get(resourceName);
	}
	
	/**
	 * Gets an existing resource relative to a specified class, throwing an exception if no resource is found.
	 * 

* The resourceName will be automatically prefixed with the relative classes simple name. Example: *

    *
  • resourceName: test.txt
  • *
  • relativeClass: com.test.TestClass
  • *
  • Actual resource name: TestClass_test.txt in directory com/test.
  • *
* * @param resourceName * @param relativeClass * @return */ public static URL existingResourceRelativeToAndPrefixed(String resourceName, Class relativeClass) { String actualResourceName = String.format("%s_%s", relativeClass.getSimpleName(), resourceName); return existingResourceRelativeTo(actualResourceName, relativeClass); } /** * Gets the resource with the specified name, making sure that there is only one resource with the name * and that it exists. * * @param resourceName * Resource name * @return URL to resource * @throws JuRuntimeException * If resource doesn't exist */ public static URL singleResource(String resourceName) { return JuUrl.resource().single().exceptionIfNone().get(resourceName); } /** * Converts the specified path to an URL, wrapping any exception into a * runtime exception. * @param p Path * @return Path as URL */ public static URL toUrl(Path p) { try { return p.toUri().toURL(); } catch (Exception ex) { throw new JuRuntimeException("Couldn't convert path '%s' to URL", ex, p); } } /** * Converts the specified URL to a Path instance. * @param url URL * @return Path instance */ public static Path toPath(URL url) { try { return Paths.get(url.toURI()); } catch (Exception ex) { throw new JuRuntimeException("Couldn't convert URL %s to Path", ex, url); } } /** * Gets a Path instance to an existing file. * @param path Path to file * @return Path instance * @throws JuRuntimeException If the file doesn't exist */ public static Path existingFile(String path) { return JuUrl.path().file().exists().get(path); } /** * Gets a Path instance to an existing folder. * @param path Path to folder * @return Path instance * @throws JuRuntimeException If the folder doesn't exist */ public static Path existingFolder(String path) { return JuUrl.path().directory().exists().get(path); } /** * Gets an URL to an existing resource. If none is found, an exception will be * thrown. *

* If multiple resources with the specified name/path exist, the first found is returned. * @param resourceName Resource name * @return URL to resource */ public static URL existingResource(String resourceName) { return JuUrl.resource().exceptionIfNone().get(resourceName); } /** *Gets a PathUrlBuilder that can be used to configure and perform path lookup. * @return PathUrlBuilder instance */ public static PathUrlBuilder path() { return new PathUrlBuilder(); } /** * Helper class to resolve paths. * @author Martin * */ public static class PathUrlBuilder { private boolean file; private boolean directory; private boolean exists; private String relativeToFirst; private String[] relativeToMore = new String[0]; /** * Sets the flag that the path must be a file. *

* Default is false which means that it needn't be a file (but CAN be). * @return This builder */ public PathUrlBuilder file() { this.file = true; return this; } /** * Sets the flag that the path must be a directory. *

* Defaults to false which means that it needn't be a directory (but CAN be). * @return This builder */ public PathUrlBuilder directory() { this.directory = true; return this; } /** * Sets the modified that the path must exist. If not, an exception will be thrown. * @return This builder */ public PathUrlBuilder exists() { this.exists = true; return this; } /** * Sets the relative paths to resolve the final path. * @param first First path part * @param more Optional additional path parts * @return This builder */ public PathUrlBuilder relativeTo(String first, String... more) { this.relativeToFirst = first; this.relativeToMore = more; return this; } /** * Gets the specified path, using the configuration of the builder. * @param path Path * @return Path instance. If exists is set, an exception will be thrown if the path doesn't exist */ public Path get(String path) { Path p = null; if (StringUtils.isEmpty(this.relativeToFirst)) { p = Paths.get(path); } else { String moreString[] = Arrays.copyOf(this.relativeToMore, this.relativeToMore.length + 1); moreString[moreString.length - 1] = path; p = Paths.get(this.relativeToFirst, moreString); } if (this.exists && !Files.exists(p)) { throw new JuRuntimeException("Path doesn't exist: %s (absolute: %s)", p, p.toAbsolutePath()); } if (this.file && !Files.isRegularFile(p)) { throw new JuRuntimeException("Path is not a file: %s (absolute: %s)", p, p.toAbsolutePath()); } if (this.directory && !Files.isDirectory(p)) { throw new JuRuntimeException("Path is not a directory: %s (absolute: %s)", p, p.toAbsolutePath()); } return p; } /** * Checks if the specified path is an existing file. * @param path Path to file * @return True if path is an existing file, false otherwise */ public boolean isExistingFile(Path path) { return Files.exists(path) && Files.isRegularFile(path); } /** * Checks if the specified path is an existing directory. * @param path Path to directory * @return True if path is an existing directory, false otherwise */ public boolean isExistingDirectory(Path path) { return Files.exists(path) && Files.isDirectory(path); } } /** * Gets a ResourceUrlBuilder that can be used to configure and perform resource lookup. * @return ResourceUrlBuilder instance */ public static ResourceUrlBuilder resource() { return new ResourceUrlBuilder(); } /** * Builder to lookup resources. * @author Martin * */ public static class ResourceUrlBuilder { private Class relativeClass; private boolean single = false; private boolean exceptionIfNone = false; static Boolean cacheVrfConversionFlag = null; static Boolean disableVfsForResourceLookup = null; /** * Resolves the resource relative to the specified class, using the method * Class.getResource. *

* If the resource starts with an '/', the path is absolute. * @param clazz Relative class * @return This builder */ public ResourceUrlBuilder relativeTo(Class clazz) { this.relativeClass = clazz; return this; } /** * Modifier for the get method to make sure that a single resource is found. *

* If multiple resources are found, an exception is thrown. *

* Default is false. * @return This builder */ public ResourceUrlBuilder single() { this.single = true; return this; } /** * Modified to throw an exception if no resource is found. *

* Default is false, i.e. null is returned if no resource is found * @retuen This builder */ public ResourceUrlBuilder exceptionIfNone() { this.exceptionIfNone = true; return this; } /** * Sets the exceptionIfNone flag. * @param exceptionIfNone If true, an exception is thrown if the resource doesn't exist * @return This builder */ public ResourceUrlBuilder exceptionIfNone(boolean exceptionIfNone) { this.exceptionIfNone = exceptionIfNone; return this; } /** * Gets a list of all resources on the classpath using the JuUrl classloader. *

* Paths are always absolute and must not start with a '/'. *

* Examples:

    *
  • log4j.xml
  • *
  • META-INF/persistence.xml
  • *
* @param resourceName Resource name * @return List of all resources with the specified name, as returned by the ClassLoader.getResources method. If none * are found, an empty list is returned */ public List getAll(String resourceName) { return this.getAll(resourceName, true); } /** * Helper method with package privacy to avoid stack overflows as JuPropertyChain is also evaluated using JuUrl. * @param resouceName Resource Name * @param considerVfsReplacement If false, VFS replacement isn't consided, i.e. the property defining it isn't evaluated * @return List of URLs */ List getAll(String resourceName, boolean considerVfsReplacement) { try { String resourcePrefix = ""; // If we have a relative class set, we'll need to specify the path explicitly... if (this.relativeClass != null) { String packageName = this.relativeClass.getPackage().getName(); resourcePrefix = packageName.replaceAll("\\.", "/") + "/"; // Note: This probably would fail on inner classes, but that shouldn't be a use case... } Enumeration resourcesEnum = Thread.currentThread().getContextClassLoader().getResources(resourcePrefix + resourceName); List resources = new ArrayList<>(); while (resourcesEnum.hasMoreElements()) { URL url = resourcesEnum.nextElement(); resources.add(considerVfsReplacement ? this.convertVfsResourceIfNecessary(url) : url); } return resources; } catch (Exception ex) { throw new JuRuntimeException("Couldn't lookup resource " + resourceName, ex); } } /** * Gets the resource with the specified name, taking the configuration of the * builder into account. * @param resourceName Resource name * @return URL or null if none was found and exceptionIfNone is not set. May also throw an exception * if multiples are found and single is set. */ public URL get(String resourceName) { return this.get(resourceName, true); } /** * Helper method with package privacy to avoid stack overflows as JuPropertyChain is also evaluated using JuUrl. * @param resouceName Resource Name * @param considerVfsReplacement If false, VFS replacement isn't consided, i.e. the property defining it isn't evaluated * @return URL */ URL get(String resourceName, boolean considerVfsReplacement) { URL url = null; if (this.relativeClass != null) { url = this.relativeClass.getResource(resourceName); } else { List urls = this.getAll(resourceName, false); if (this.single && urls.size() > 1) { XString xs = new XString("Found more than 1 resource with name %s:", resourceName); xs.increaseIndent(); for (URL u : urls) xs.addLine(u.toString()); throw new JuRuntimeException(xs.toString()); } else if (urls.size() > 0) { url = urls.get(0); } } if (url == null && this.exceptionIfNone) { throw new JuRuntimeException("Resource not found: %s", resourceName); } else { return considerVfsReplacement ? this.convertVfsResourceIfNecessary(url) : url; } } /** * Converts a VFS resource (as used in JBoss) to a regular jar:file resource if necessary. *

* For details, see http://stackoverflow.com/questions/20100390/how-to-turn-off-or-disable-vfs-file-loading-in-jboss-as7 *

* Note that VFS resource conversion makes only sense when developping locally and should be disabled for all server environments * (as we cannot access the resource the way we do here in this context anyway as the EAR is not deployed exploadedly). * * @param url * URL * @return Converted URL or same URL if no conversion was necessary */ private URL convertVfsResourceIfNecessary(URL url) { // No need to convert null URLs... if (url == null) return url; boolean disableVfs; synchronized(JuUrl.class) { if (cacheVrfConversionFlag == null) { cacheVrfConversionFlag = JuUtils.getJuPropertyChain().get("ju.ee.url.disableVfsForResourceLookup", Boolean.class); } if (!cacheVrfConversionFlag) { disableVfs = JuUtils.getJuPropertyChain().get("ju.ee.url.disableVfsForResourceLookup", Boolean.class); } else { if (disableVfsForResourceLookup == null) { disableVfsForResourceLookup = JuUtils.getJuPropertyChain().get("ju.ee.url.disableVfsForResourceLookup", Boolean.class); } disableVfs = disableVfsForResourceLookup; } } if (disableVfs) { String externalForm = url.toExternalForm(); if (externalForm.startsWith("vfs:")) { // VFS resource. // A VFS resource looks like vfs:/pathToJar/jarFile.jar/pathToResource // and needs to be converted to jar:file:/pathToJar/jarFile.jar!/pathToResource // As some resources are deployed in exploded form, we might be able to access the file directly: String newPath = externalForm.replaceFirst("vfs:", "file:"); // Try if file is accessible try { URL fileUrl = new URL(newPath); if (Files.exists(JuUrl.toPath(fileUrl))) { return fileUrl; } } catch (Exception ex) { logger.warn("Couldn't construct file URL for " + newPath); } // Now, we'll try to access the resource within a JAR. We'll still make sure that the JAR actually exists as an // accessible file. RegexUtil jarFile = new RegexUtil("(file:.+/([^/]+\\.jar))/"); Match[] matches = jarFile.getMatches(newPath); if (matches.length > 0) { Match lastMatch = matches[matches.length - 1]; String jarFilePath = lastMatch.getGroups()[0]; if (IOUtil.exists().file(jarFilePath)) { String jarFileName = lastMatch.getGroups()[1]; newPath = "jar:" + newPath.replaceAll(jarFileName + "/", jarFileName + "!/"); try { return new URL(newPath); } catch (Exception ex) { logger.warn("Couldn't convert vfs resource to jar:file resource for {}. Using old URL. Exception: {}", externalForm, ex.getMessage()); return url; } } else { return url; } } else { return url; } } else { return url; } } else { return url; } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy