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

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