
com.jianggujin.modulelink.util.vfs.JDefaultVFS Maven / Gradle / Ivy
/**
* Copyright 2018 jianggujin (www.jianggujin.com).
*
* 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 com.jianggujin.modulelink.util.vfs;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
/**
* 默认实现{@link JVFS},适合大多数应用服务器
*
* @author jianggujin
*
*/
public class JDefaultVFS extends JVFS {
/** 表示JAR(zip)文件的魔法头 */
private static final byte[] JAR_MAGIC = { 'P', 'K', 3, 4 };
@Override
public boolean isValid() {
return true;
}
@Override
public List list(URL url, String path) throws IOException {
InputStream is = null;
try {
List resources = new ArrayList();
// 首先,尝试查找包含请求资源的JAR文件的URL,如果找到,然后将通过阅读JAR来列出子资源
URL jarUrl = findJarForResource(url);
if (jarUrl != null) {
is = jarUrl.openStream();
resources = listResources(new JarInputStream(is), path);
} else {
List children = new ArrayList();
try {
if (isJar(url)) {
// JBoss VFS有些版本可能提供一个Jar的流,即使URL引用的资源实际上并不是一个JAR
is = url.openStream();
JarInputStream jarInput = new JarInputStream(is);
for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null;) {
children.add(entry.getName());
}
jarInput.close();
} else {
/**
* 一些servlet容器允许从目录资源中读取,比如文本文件,每行列出一个子资源。
* 然而,仅仅通过读取它们来区分目录和文件资源是没有办法的。
* 为了解决这一问题,在读取每一行时,尝试通过类加载器作为当前资源的子元素查找它。
* 如果任何一行失败,我们假设当前资源不是目录。
*/
is = url.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
List lines = new ArrayList();
for (String line; (line = reader.readLine()) != null;) {
lines.add(line);
if (getResources(path + "/" + line).isEmpty()) {
lines.clear();
break;
}
}
if (!lines.isEmpty()) {
children.addAll(lines);
}
}
} catch (FileNotFoundException e) {
/*
* 文件的URL的openstream()调用可能会失败,这取决于servlet容器,因为目录无法打开阅读。如果出现这种情况,
* 则直接列出该目录。
*/
if ("file".equals(url.getProtocol())) {
File file = new File(url.getFile());
if (file.isDirectory()) {
children = Arrays.asList(file.list());
}
} else {
throw e;
}
}
// 在递归列出子资源时使用的URL前缀
String prefix = url.toExternalForm();
if (!prefix.endsWith("/")) {
prefix = prefix + "/";
}
// 遍历直系子女,添加文件到目录和检索
for (String child : children) {
String resourcePath = path + "/" + child;
resources.add(resourcePath);
URL childUrl = new URL(prefix + child);
resources.addAll(list(childUrl, resourcePath));
}
}
return resources;
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
}
}
}
}
/**
* 列出在给定的{@link JarInputStream}中与指定的路径开始的条目列表
*
* @param jar
* @param path
* @return
* @throws IOException
*/
protected List listResources(JarInputStream jar, String path) throws IOException {
// 在匹配名称时包括斜杠
if (!path.startsWith("/")) {
path = "/" + path;
}
if (!path.endsWith("/")) {
path = path + "/";
}
// 遍历条目并收集以请求路径开头的条目
List resources = new ArrayList();
for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
if (!entry.isDirectory()) {
// 如果缺少"/"则添加
String name = entry.getName();
if (!name.startsWith("/")) {
name = "/" + name;
}
// 检查文件名称
if (name.startsWith(path)) {
// 去除开头的"/"
resources.add(name.substring(1));
}
}
}
return resources;
}
/**
* 试图解构给定URL以找到包含URL引用的资源的JAR文件。也就是说,假设URL引用一个JAR条目,这个方法将返回一个URL,
* 该URL引用包含该条目的JAR文件。如果无法定位JAR,则此方法返回null。
*
* @param url
* @return
* @throws MalformedURLException
*/
protected URL findJarForResource(URL url) throws MalformedURLException {
// 如果URL的文件部分本身是一个URL,那么该URL可能指向JAR。
try {
for (;;) {
url = new URL(url.getFile());
}
} catch (MalformedURLException e) {
// 这将在某个时刻发生,并作为循环中的一个中断
}
// 寻找.jar扩展,然后删除其后内容
StringBuilder jarUrl = new StringBuilder(url.toExternalForm());
int index = jarUrl.lastIndexOf(".jar");
if (index >= 0) {
jarUrl.setLength(index + 4);
} else {
return null;
}
// 试着打开并测试
try {
URL testUrl = new URL(jarUrl.toString());
if (isJar(testUrl)) {
return testUrl;
} else {
// WebLogic修复: 检查URL文件是否存在于文件系统中
jarUrl.replace(0, jarUrl.length(), testUrl.getFile());
File file = new File(jarUrl.toString());
// 文件名可能是URL编码的
if (!file.exists()) {
try {
file = new File(URLEncoder.encode(jarUrl.toString(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported encoding? UTF-8? That's unpossible.");
}
}
if (file.exists()) {
testUrl = file.toURI().toURL();
if (isJar(testUrl)) {
return testUrl;
}
}
}
} catch (MalformedURLException e) {
}
return null;
}
/**
* 转换一个Java包名为可以被{@link ClassLoader#getResources(String)}使用的路径
*
* @param packageName
* @return
*/
protected String getPackagePath(String packageName) {
return packageName == null ? null : packageName.replace('.', '/');
}
/**
* 如果位于给定URL的资源是JAR文件,则返回true
*
* @param url
* @return
*/
protected boolean isJar(URL url) {
return isJar(url, new byte[JAR_MAGIC.length]);
}
/**
* 如果位于给定URL的资源是JAR文件,则返回true
*
* @param url
* @param buffer
* @return
*/
protected boolean isJar(URL url, byte[] buffer) {
InputStream is = null;
try {
is = url.openStream();
is.read(buffer, 0, JAR_MAGIC.length);
if (Arrays.equals(buffer, JAR_MAGIC)) {
return true;
}
} catch (Exception e) {
// 读取失败则表示不是一个Jar文件 Failure to read the stream means this is not a JAR
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
}
}
}
return false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy