net.hasor.cobble.loader.providers.ClassPathResourceLoader Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2008-2009 the original author or authors.
*
* Licensed 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 net.hasor.cobble.loader.providers;
import net.hasor.cobble.ClassUtils;
import net.hasor.cobble.ExceptionUtils;
import net.hasor.cobble.StringUtils;
import net.hasor.cobble.SystemUtils;
import net.hasor.cobble.function.ESupplier;
import net.hasor.cobble.io.FileUtils;
import net.hasor.cobble.loader.Scanner;
import net.hasor.cobble.loader.*;
import net.hasor.cobble.logging.Logger;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.util.*;
import java.util.function.Predicate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
/**
* 用于创建一个可以从 classpath 中获取资源的 ResourceLoader
* @version : 2021-10-10
* @author 赵永春 ([email protected])
*/
public class ClassPathResourceLoader extends AbstractResourceLoader {
private static final Logger logger = Logger.getLogger(ClassPathResourceLoader.class);
public static ResourceLoader INSTANCE = new ClassPathResourceLoader();
private final ClassLoader classLoader;
private final List classpath;
private final String[] ZIP_TYPES = new String[] { ".jar", ".JAR", ".zip", ".ZIP" };
public ClassPathResourceLoader() {
super(Thread.currentThread().getContextClassLoader());
this.classLoader = Thread.currentThread().getContextClassLoader();
this.classpath = getClassPath(this.classLoader);
}
public ClassPathResourceLoader(ClassLoader parent) {
super(parent);
this.classLoader = Objects.requireNonNull(parent);
this.classpath = getClassPath(this.classLoader);
}
public Class> getClass(String className) throws ClassNotFoundException {
return this.classLoader.loadClass(className);
}
/** 获取ClassPath路径 */
protected List getClassPath(ClassLoader classLoader) {
return ClassUtils.getClassPath(classLoader);
}
private String formatResourcePath(String resourcePath) {
resourcePath = resourcePath.replaceAll("/{2}", "/");
if (resourcePath.charAt(0) == '/') {
resourcePath = resourcePath.substring(1);
}
return resourcePath;
}
@Override
public URL getResource(String resource) throws IOException {
return this.classLoader.getResource(formatResourcePath(resource));
}
@Override
public InputStream getResourceAsStream(String resource) {
return this.classLoader.getResourceAsStream(formatResourcePath(resource));
}
@Override
public long getResourceSize(String resource) throws IOException {
return -1;
}
@Override
public List getResources(String resource) throws IOException {
resource = formatResourcePath(resource);
List urls = new ArrayList<>();
Enumeration urlEnumeration = this.classLoader.getResources(resource);
while (urlEnumeration.hasMoreElements()) {
URL url = urlEnumeration.nextElement();
if (url != null) {
urls.add(url);
}
}
return urls;
}
@Override
public List getResourcesAsStream(String resource) throws IOException {
resource = formatResourcePath(resource);
List ins = new ArrayList<>();
Enumeration urlEnumeration = this.classLoader.getResources(resource);
while (urlEnumeration.hasMoreElements()) {
URL url = urlEnumeration.nextElement();
InputStream in = (url != null) ? url.openStream() : null;
if (in != null) {
ins.add(in);
}
}
return ins;
}
@Override
public boolean exist(String resource) {
return this.classLoader.getResource(formatResourcePath(resource)) != null;
}
@Override
public List scanResources(MatchType matchType, Predicate matcher, Scanner scanner, String[] scanPaths) throws IOException {
final Predicate[] testJar = buildPredicate(matchType, scanPaths, ZipEntry::getName);
final Predicate[] testDir = buildPredicate(matchType, scanPaths, s -> s);
List result = new ArrayList<>();
for (URL url : this.classpath) {
try {
if (!matcher.test(url.toURI())) {
continue;
}
} catch (URISyntaxException e) {
logger.error("cannot convert URL to URI jar (" + url + ") " + e.getMessage(), e);
throw ExceptionUtils.toRuntime(e);
}
String protocol = url.getProtocol();
if (protocol.equals("file")) {
File contextDir = FileUtils.toFile(url);
if (contextDir == null || !contextDir.exists()) {
continue;
}
if (contextDir.isDirectory()) {
dirScan(result, contextDir, contextDir, testDir, scanner, false);
} else {
String jarName = contextDir.getName();
if (!StringUtils.endsWithAny(jarName, ZIP_TYPES)) {
continue;
}
try {
JarFile jarFile = new net.hasor.cobble.loader.jar.JarFile(contextDir);
jarScan(result, jarFile, testJar, scanner, false);
} catch (Exception e) {
logger.error("cannot open jar (" + jarName + ") " + e.getMessage(), e);
}
}
} else if (protocol.equals("jar")) {
JarURLConnection jarc = (JarURLConnection) url.openConnection();
JarFile jarFile = jarc.getJarFile();
jarScan(result, jarFile, testJar, scanner, false);
}
}
return result;
}
@Override
public T scanOneResource(MatchType matchType, Predicate matcher, Scanner scanner, String[] scanPaths) throws IOException {
List result = new ArrayList<>();
Predicate[] testJar = buildPredicate(matchType, scanPaths, ZipEntry::getName);
Predicate[] testDir = buildPredicate(matchType, scanPaths, s -> s);
for (URL url : this.classpath) {
try {
if (!matcher.test(url.toURI())) {
continue;
}
} catch (URISyntaxException e) {
logger.error("cannot convert URL to URI jar (" + url + ") " + e.getMessage(), e);
throw ExceptionUtils.toRuntime(e);
}
String protocol = url.getProtocol();
if (protocol.equals("file")) {
File contextDir = FileUtils.toFile(url);
if (contextDir == null || !contextDir.exists()) {
continue;
}
if (contextDir.isDirectory()) {
dirScan(result, contextDir, contextDir, testDir, scanner, true);
} else {
String jarName = contextDir.getName();
if (!StringUtils.endsWithAny(jarName, ZIP_TYPES)) {
continue;
}
try {
JarFile jarFile = new net.hasor.cobble.loader.jar.JarFile(contextDir);
jarScan(result, jarFile, testJar, scanner, true);
} catch (Exception e) {
logger.error("cannot open jar (" + jarName + ") " + e.getMessage(), e);
}
}
} else if (protocol.equals("jar")) {
JarURLConnection jarc = (JarURLConnection) url.openConnection();
JarFile jarFile = jarc.getJarFile();
jarScan(result, jarFile, testJar, scanner, true);
}
if (!result.isEmpty()) {
return result.get(0);
}
}
return null;
}
private static void jarScan(List result, JarFile jarFile, Predicate[] jarTest, Scanner scanner, boolean matchOnce) throws IOException {
URL content = new File(jarFile.getName()).toURI().toURL();
Iterator zipEntry = jarFile.stream().iterator();
while (zipEntry.hasNext()) {
JarEntry entry = zipEntry.next();
if (!jarTestFound(entry, jarTest)) {
continue;
}
try {
URI uri = new URL("jar:" + content + "!/" + entry.getName()).toURI();
ESupplier inputStream = () -> jarFile.getInputStream(entry);
T res = scanner.found(new ScanEvent(entry.getName(), entry.getSize(), uri, inputStream));
if (res != null) {
result.add(res);
}
} catch (URISyntaxException e) {
if (logger.isDebugEnabled()) {
logger.warn("scanJarFile :" + e.getMessage(), e);
} else {
logger.debug("scanJarFile :" + e.getMessage());
}
}
if (matchOnce && !result.isEmpty()) {
return;
}
}
}
private static boolean jarTestFound(JarEntry entry, Predicate[] jarTest) {
if (jarTest == null || jarTest.length == 0) {
return true;
} else {
for (Predicate predicate : jarTest) {
if (predicate.test(entry)) {
return true;
}
}
return false;
}
}
private static void dirScan(List result, File contextDir, File curFile, Predicate[] testDir, Scanner scanner, boolean matchOnce) throws IOException {
if (matchOnce && !result.isEmpty()) {
return;
}
File[] listFiles = curFile.listFiles();
if (listFiles == null) {
return;
}
for (File fileItem : listFiles) {
if (matchOnce && !result.isEmpty()) {
return;
}
if (fileItem.isHidden() || !fileItem.exists()) {
continue;
}
if (fileItem.isDirectory()) {
dirScan(result, contextDir, fileItem, testDir, scanner, matchOnce);
continue;
}
String resourceName = fileItem.getAbsolutePath().substring(contextDir.getAbsolutePath().length() + 1);
if (SystemUtils.isWindows()) {
resourceName = StringUtils.replace(resourceName, File.separator, "/");
}
if (!dirTestFound(resourceName, testDir)) {
continue;
}
ESupplier inputStream = () -> {
if (fileItem.canRead()) {
return Files.newInputStream(fileItem.toPath());
} else {
throw new IOException("file cannot be read :" + fileItem);
}
};
T res = scanner.found(new ScanEvent(resourceName, fileItem.length(), fileItem.toURI(), inputStream));
if (res != null) {
result.add(res);
}
}
}
private static boolean dirTestFound(String entry, Predicate[] dirTest) {
if (dirTest == null || dirTest.length == 0) {
return true;
} else {
for (Predicate predicate : dirTest) {
if (predicate.test(entry)) {
return true;
}
}
return false;
}
}
}