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 keyword ) { 077 // jar:file:/実ディレクトリ!先頭クラス または、file:/実ディレクトリ または、/実ディレクトリ 078 079 String dir = filepath; // 5.5.2.6 (2012/05/25) findbugs対応 080 if( filepath.startsWith( "jar:" ) 081 || filepath.startsWith( "file:" ) 082 || filepath.charAt(0) == '/' ) { // 4.4.0.0 (2009/08/02) 083 int stAdrs = filepath.indexOf( '/' ); 084 if( filepath.charAt(stAdrs+2) == ':' ) { // 4.0.3.0 (2007/01/07) 085 stAdrs++; 086 } 087 int edAdrs = filepath.lastIndexOf( '!' ); 088 if( edAdrs < 0) { 089 edAdrs = filepath.length(); // 5.7.5.0 (2014/04/04) 最後の "/" はあってもなくてもよい。 090 } 091 dir = filepath.substring( stAdrs,edAdrs ); 092 } 093 094 File basefile = new File( dir ); 095 String baseFilename = basefile.getAbsolutePath() ; 096 baseLen = baseFilename.length() - keyword.length(); 097 findFilename( basefile ); 098 } 099 100 /** 101 * ファイルパスを ドット(.)に置き換え、.class を取り除いた形式(クラスの完全系)の文字列配列 102 * 103 * @return ファイルパスの文字列配列 104 */ 105 public String[] getFilenames() { 106 return list.toArray( new String[list.size()] ); 107 } 108 109 /** 110 * ファイルを再帰的に検索します。 111 * 指定のファイルオブジェクトが ファイルの場合は、.class であればListに追加し、 112 * .zip か .jar では、findJarFiles を呼び出します。 113 * ファイルでない場合は、配列を取り出し、自分自身を再帰的に呼び出します。 114 * 115 * @param file ファイル 116 */ 117 private void findFilename( final File file ) { 118 if( file.isFile() ) { 119 String name = file.getAbsolutePath(); 120 if( name.endsWith( SUFIX ) ) { 121 list.add( name.substring( baseLen,name.length()-SUFIX_LEN ).replace( File.separatorChar,'.' ) ); 122 } 123 else if( name.endsWith( ".jar" ) || name.endsWith( ".zip" ) ) { 124 findJarFiles( name ); 125 } 126 } 127 else { 128 File[] filelist = file.listFiles(); 129 for( int i=0; i<filelist.length; i++ ) { 130 findFilename( filelist[i] ); 131 } 132 } 133 } 134 135 /** 136 * jar/zipファイルを検索します。 137 * 圧縮ファイルでは、階層ではなく、Enumeration としてファイルを取り出します。 138 * 拡張子で判断して、Listに追加していきます。 139 * 140 * @og.rev 5.5.2.6 (2012/05/25) JarFile を、Closer#zipClose( ZipFile ) メソッドを利用して、close します。 141 * 142 * @param filename ファイル名 143 */ 144 private void findJarFiles( final String filename ) { 145 // 5.5.2.6 (2012/05/25) findbugs対応 146 JarFile jarFile = null; 147 try { 148 jarFile = new JarFile( filename ); 149 Enumeration<JarEntry> en = jarFile.entries() ; 150 while( en.hasMoreElements() ) { 151 JarEntry ent = en.nextElement(); 152 if( ! ent.isDirectory() ) { 153 String name = ent.getName(); 154 if( name.endsWith( SUFIX ) ) { 155 list.add( name.substring( 0,name.length()-SUFIX_LEN ).replace( '/','.' ) ); 156 } 157 } 158 } 159 } 160 catch( IOException ex ) { 161 String errMsg = "ファイル読み取りストリームに失敗しました。" 162 + " File=" + filename 163 + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正 164 throw new RuntimeException( errMsg,ex ); 165 } 166 // 5.5.2.6 (2012/05/25) findbugs対応 167 finally { 168 Closer.zipClose( jarFile ); 169 } 170 } 171 172 /** 173 * サンプルメイン 174 * ここでは、引数に通常のファイルではなく、クラスパスより取得します。 175 * 通常、取得されたファイル名は、クラスの完全系の文字列なので、クラスパスより取得 176 * している限り、そのまま オブジェクトを構築できることを意味します。 177 * 178 * @param args 引数 179 */ 180 public static void main( final String[] args ) { 181 try { 182 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 183 Enumeration<URL> enume = loader.getResources( args[0] ); // 4.3.3.6 (2008/11/15) Generics警告対応 184 while( enume.hasMoreElements() ) { 185 URL url = enume.nextElement(); // 4.3.3.6 (2008/11/15) Generics警告対応 186 // jar:file:/実ディレクトリ!先頭クラス または、file:/実ディレクトリ 187 System.out.println( "url=" + url.getFile() ); 188 189 FindClassFiles filenames = new FindClassFiles( url.getFile(),args[0] ); 190 String[] names = filenames.getFilenames(); 191 for( int i=0; i<names.length; i++ ) { 192 System.out.println( names[i] ); 193 } 194 } 195 } 196 catch( IOException ex ) { 197 String errMsg = "ファイル読み取りストリームに失敗しました。" 198 + ex.getMessage(); 199 throw new RuntimeException( errMsg,ex ); 200 } 201 } 202}