001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.util; 017 018import java.io.IOException; 019import java.io.File; 020 021import java.util.List; 022import java.util.ArrayList; 023import java.util.Enumeration; 024import java.util.jar.JarFile; 025import java.util.jar.JarEntry; 026import java.net.URL; 027 028/** 029 * このクラスは、指定のディレクトリパスから .class ファイルを検索するクラスです。 030 * 検索パスは、実ファイルと、zipファイルの内部、jar ファイルの内部も含みます。 031 * 検索結果は、.class を取り除き、ファイルパスを、すべてドット(.)に変換した形式にします。 032 * これは、ほとんどクラスのフルパス文字列に相当します。 033 * ここで取得されたファイル名より、実クラスオブジェクトの作成が可能になります。 034 * 035 * このクラスの main メソッドは、クラスパスから指定の名前を持つクラス以下のディレクトリより 036 * ファイルを検索します。通常、このクラスの使い方として、取得したクラスファイル名(文字列) 037 * から、引数なしコンストラクタを呼び出して、実オブジェクトを生成させるので、通常のフォルダ 038 * から検索するより、クラスパス内から検索するペースが多いため、サンプルをそのように設定 039 * しています。 040 * 041 * @og.rev 4.0.0.0 (2004/12/31) 新規作成 042 * @og.group 初期化 043 * 044 * @version 4.0 045 * @author Kazuhiko Hasegawa 046 * @since JDK5.0, 047 */ 048public final class FindClassFiles { 049 private final List<String> list = new ArrayList<String>(); 050 private final int baseLen; 051 052 private static final String SUFIX = ".class" ; 053 private static final int SUFIX_LEN = SUFIX.length(); 054 055 /** 056 * 検索パスを指定して構築する、コンストラクタです。 057 * ここで見つかったパス以下の classファイル(拡張子は小文字で、.class )を検索します。 058 * このファイル名は ファイルパスを ドット(.)に置き換え、.class を取り除いた格納しておきます。 059 * 060 * ※ Tomcat8.0.3 では、ClassLoader の getResources(String)で取得するURL名が、 061 * /C:/opengionV6/uap/webapps/gf/WEB-INF/classes/org/opengion/plugin/ 062 * の形式で、最後の "/" を取る為、filepath.length() - 1 処理していましたが、 063 * Tomcat8.0.5 では、/C:/opengionV6/uap/webapps/gf/WEB-INF/classes/org/opengion/plugin 064 * の形式で、最後の "/" がなくなっています。 065 * 最後の "/" があってもなくても、new File(String) でディレクトリのオブジェクトを 066 * 作成できるため、filepath.length() に変更します。 067 * 068 * @og.rev 4.0.3.0 (2007/01/07) UNIXパス検索時の、ファイルパスの取得方法の不具合対応 069 * @og.rev 5.0.0.0 (2009/08/03) UNIXパス検索時の、ファイルパスの取得方法の不具合対応 070 * @og.rev 5.0.0.0 (2009/08/03) UNIXパス検索時の、ファイルパスの取得方法の不具合対応 071 * @og.rev 5.7.5.0 (2014/04/04) ファイル名の取得方法の修正 072 * 073 * @param filepath 対象となるファイル群を検索する、ファイルパス 074 * @param keyword 検索対象ファイルのキーワード 075 */ 076// public FindClassFiles( final String filepath,final String prefix ) { 077 public FindClassFiles( final String filepath,final String keyword ) { 078 // jar:file:/実ディレクトリ!先頭クラス または、file:/実ディレクトリ または、/実ディレクトリ 079 080// String dir = null; 081 String dir = filepath; // 5.5.2.6 (2012/05/25) findbugs対応 082 if( filepath.startsWith( "jar:" ) || filepath.startsWith( "file:" ) 083// || ( filepath.charAt(0) == '/' && filepath.charAt(2) == ':' ) ) { 084 || ( filepath.charAt(0) == '/' ) ) { // 4.4.0.0 (2009/08/02) 085 int stAdrs = filepath.indexOf( '/' ); 086 if( filepath.charAt(stAdrs+2) == ':' ) { // 4.0.3.0 (2007/01/07) 087 stAdrs++; 088 } 089 int edAdrs = filepath.lastIndexOf( '!' ); 090 if( edAdrs < 0) { 091// edAdrs = filepath.length() - 1; 092 edAdrs = filepath.length(); // 5.7.5.0 (2014/04/04) 最後の "/" はあってもなくてもよい。 093 } 094 dir = filepath.substring( stAdrs,edAdrs ); 095 } 096 097 File basefile = new File( dir ); 098 String baseFilename = basefile.getAbsolutePath() ; 099// baseLen = baseFilename.length() - prefix.length(); 100 baseLen = baseFilename.length() - keyword.length(); 101 findFilename( basefile ); 102 } 103 104 /** 105 * ファイルパスを ドット(.)に置き換え、.class を取り除いた形式(クラスの完全系)の文字列配列 106 * 107 * @return ファイルパスの文字列配列 108 */ 109 public String[] getFilenames() { 110 return list.toArray( new String[list.size()] ); 111 } 112 113 /** 114 * ファイルを再帰的に検索します。 115 * 指定のファイルオブジェクトが ファイルの場合は、.class であればListに追加し、 116 * .zip か .jar では、findJarFiles を呼び出します。 117 * ファイルでない場合は、配列を取り出し、自分自身を再帰的に呼び出します。 118 * 119 * @param file ファイル 120 */ 121 private void findFilename( final File file ) { 122 if( file.isFile() ) { 123 String name = file.getAbsolutePath(); 124 if( name.endsWith( SUFIX ) ) { 125 list.add( name.substring( baseLen,name.length()-SUFIX_LEN ).replace( File.separatorChar,'.' ) ); 126 } 127 else if( name.endsWith( ".jar" ) || name.endsWith( ".zip" ) ) { 128 findJarFiles( name ); 129 } 130 } 131 else { 132 File[] filelist = file.listFiles(); 133 for( int i=0; i<filelist.length; i++ ) { 134 findFilename( filelist[i] ); 135 } 136 } 137 } 138 139 /** 140 * jar/zipファイルを検索します。 141 * 圧縮ファイルでは、階層ではなく、Enumeration としてファイルを取り出します。 142 * 拡張子で判断して、Listに追加していきます。 143 * 144 * @og.rev 5.5.2.6 (2012/05/25) JarFile を、Closer#zipClose( ZipFile ) メソッドを利用して、close します。 145 * 146 * @param filename ファイル名 147 */ 148 private void findJarFiles( final String filename ) { 149 // 5.5.2.6 (2012/05/25) findbugs対応 150 JarFile jarFile = null; 151 try { 152// JarFile jarFile = new JarFile( filename ); 153 jarFile = new JarFile( filename ); 154 Enumeration<JarEntry> en = jarFile.entries() ; 155 while( en.hasMoreElements() ) { 156 JarEntry ent = en.nextElement(); 157 if( ! ent.isDirectory() ) { 158 String name = ent.getName(); 159 if( name.endsWith( SUFIX ) ) { 160 list.add( name.substring( 0,name.length()-SUFIX_LEN ).replace( '/','.' ) ); 161 } 162 } 163 } 164 } 165 catch( IOException ex ) { 166 String errMsg = "ファイル読み取りストリームに失敗しました。" 167 + " File=" + filename 168 + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正 169 throw new RuntimeException( errMsg,ex ); 170 } 171 // 5.5.2.6 (2012/05/25) findbugs対応 172 finally { 173 Closer.zipClose( jarFile ); 174 } 175 } 176 177 /** 178 * サンプルメイン 179 * ここでは、引数に通常のファイルではなく、クラスパスより取得します。 180 * 通常、取得されたファイル名は、クラスの完全系の文字列なので、クラスパスより取得 181 * している限り、そのまま オブジェクトを構築できることを意味します。 182 * 183 * @param args 引数 184 */ 185 public static void main( final String[] args ) { 186 try { 187 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 188 Enumeration<URL> enume = loader.getResources( args[0] ); // 4.3.3.6 (2008/11/15) Generics警告対応 189 while( enume.hasMoreElements() ) { 190 URL url = enume.nextElement(); // 4.3.3.6 (2008/11/15) Generics警告対応 191 // jar:file:/実ディレクトリ!先頭クラス または、file:/実ディレクトリ 192 System.out.println( "url=" + url.getFile() ); 193 194 FindClassFiles filenames = new FindClassFiles( url.getFile(),args[0] ); 195 String[] names = filenames.getFilenames(); 196 for( int i=0; i<names.length; i++ ) { 197 System.out.println( names[i] ); 198 } 199 } 200 } 201 catch( IOException ex ) { 202 String errMsg = "ファイル読み取りストリームに失敗しました。" 203 + ex.getMessage(); 204 throw new RuntimeException( errMsg,ex ); 205 } 206 } 207}