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// import org.opengion.fukurou.system.Closer; // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 029import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 030 031/** 032 * このクラスは、指定のディレクトリパスから .class ファイルを検索するクラスです。 033 * 検索パスは、実ファイルと、zipファイルの内部、jar ファイルの内部も含みます。 034 * 検索結果は、.class を取り除き、ファイルパスを、すべてドット(.)に変換した形式にします。 035 * これは、ほとんどクラスのフルパス文字列に相当します。 036 * ここで取得されたファイル名より、実クラスオブジェクトの作成が可能になります。 037 * 038 * このクラスの main メソッドは、クラスパスから指定の名前を持つクラス以下のディレクトリより 039 * ファイルを検索します。通常、このクラスの使い方として、取得したクラスファイル名(文字列) 040 * から、引数なしコンストラクタを呼び出して、実オブジェクトを生成させるので、通常のフォルダ 041 * から検索するより、クラスパス内から検索するペースが多いため、サンプルをそのように設定 042 * しています。 043 * 044 * @og.rev 4.0.0.0 (2004/12/31) 新規作成 045 * @og.group 初期化 046 * 047 * @version 4.0 048 * @author Kazuhiko Hasegawa 049 * @since JDK5.0, 050 */ 051public final class FindClassFiles { 052 private final List<String> list = new ArrayList<>(); 053 private final int baseLen; 054 055 private static final String SUFIX = ".class" ; 056 private static final int SUFIX_LEN = SUFIX.length(); 057 058 /** 059 * 検索パスを指定して構築する、コンストラクタです。 060 * ここで見つかったパス以下の classファイル(拡張子は小文字で、.class )を検索します。 061 * このファイル名は ファイルパスを ドット(.)に置き換え、.class を取り除いた格納しておきます。 062 * 063 * ※ Tomcat8.0.3 では、ClassLoader の getResources(String)で取得するURL名が、 064 * /C:/opengionV6/uap/webapps/gf/WEB-INF/classes/org/opengion/plugin/ 065 * の形式で、最後の "/" を取る為、filepath.length() - 1 処理していましたが、 066 * Tomcat8.0.5 では、/C:/opengionV6/uap/webapps/gf/WEB-INF/classes/org/opengion/plugin 067 * の形式で、最後の "/" がなくなっています。 068 * 最後の "/" があってもなくても、new File(String) でディレクトリのオブジェクトを 069 * 作成できるため、filepath.length() に変更します。 070 * 071 * @og.rev 4.0.3.0 (2007/01/07) UNIXパス検索時の、ファイルパスの取得方法の不具合対応 072 * @og.rev 5.0.0.0 (2009/08/03) UNIXパス検索時の、ファイルパスの取得方法の不具合対応 073 * @og.rev 5.0.0.0 (2009/08/03) UNIXパス検索時の、ファイルパスの取得方法の不具合対応 074 * @og.rev 5.7.5.0 (2014/04/04) ファイル名の取得方法の修正 075 * 076 * @param filepath 対象となるファイル群を検索する、ファイルパス 077 * @param keyword 検索対象ファイルのキーワード 078 */ 079 public FindClassFiles( final String filepath,final String keyword ) { 080 // jar:file:/実ディレクトリ!先頭クラス または、file:/実ディレクトリ または、/実ディレクトリ 081 082 String dir = filepath; // 5.5.2.6 (2012/05/25) findbugs対応 083 if( filepath.startsWith( "jar:" ) 084 || filepath.startsWith( "file:" ) 085 || filepath.charAt(0) == '/' ) { // 4.4.0.0 (2009/08/02) 086 int stAdrs = filepath.indexOf( '/' ); 087 if( filepath.charAt(stAdrs+2) == ':' ) { // 4.0.3.0 (2007/01/07) 088 stAdrs++; 089 } 090 int edAdrs = filepath.lastIndexOf( '!' ); 091 if( edAdrs < 0) { 092 edAdrs = filepath.length(); // 5.7.5.0 (2014/04/04) 最後の "/" はあってもなくてもよい。 093 } 094 dir = filepath.substring( stAdrs,edAdrs ); 095 } 096 097 final File basefile = new File( dir ); 098 final String baseFilename = basefile.getAbsolutePath() ; 099 baseLen = baseFilename.length() - keyword.length(); 100 findFilename( basefile ); 101 } 102 103 /** 104 * ファイルパスを ドット(.)に置き換え、.class を取り除いた形式(クラスの完全系)の文字列配列。 105 * 106 * @return ファイルパスの文字列配列 107 * @og.rtnNotNull 108 */ 109 public String[] getFilenames() { 110// return list.toArray( new String[list.size()] ); 111 return list.toArray( new String[0] ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 OptimizableToArrayCall 対応 112 } 113 114 /** 115 * ファイルを再帰的に検索します。 116 * 指定のファイルオブジェクトが ファイルの場合は、.class であればListに追加し、 117 * .zip か .jar では、findJarFiles を呼び出します。 118 * ファイルでない場合は、配列を取り出し、自分自身を再帰的に呼び出します。 119 * 120 * @og.rev 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値のnullチェックを追加。 121 * 122 * @param file ファイル 123 */ 124 private void findFilename( final File file ) { 125 if( file.isFile() ) { 126 final String name = file.getAbsolutePath(); 127 if( name.endsWith( SUFIX ) ) { 128 list.add( name.substring( baseLen,name.length()-SUFIX_LEN ).replace( File.separatorChar,'.' ) ); 129 } 130 else if( name.endsWith( ".jar" ) || name.endsWith( ".zip" ) ) { 131 findJarFiles( name ); 132 } 133 } 134 else { 135 final File[] filelist = file.listFiles(); 136 // 6.3.9.0 (2015/11/06) null になっている可能性があるメソッドの戻り値のnullチェックを追加。 137 if( filelist != null ) { 138 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 139// for( int i=0; i<filelist.length; i++ ) { 140// findFilename( filelist[i] ); 141// } 142 for( final File tmp : filelist ) { 143 findFilename( tmp ); 144 } 145 } 146 } 147 } 148 149 /** 150 * jar/zipファイルを検索します。 151 * 圧縮ファイルでは、階層ではなく、Enumeration としてファイルを取り出します。 152 * 拡張子で判断して、Listに追加していきます。 153 * 154 * @og.rev 5.5.2.6 (2012/05/25) JarFile を、Closer#zipClose( ZipFile ) メソッドを利用して、close します。 155 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 156 * 157 * @param filename ファイル名 158 */ 159 private void findJarFiles( final String filename ) { 160 // 5.5.2.6 (2012/05/25) findbugs対応 161 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 162// JarFile jarFile = null; 163// try { 164// jarFile = new JarFile( filename ); 165 try ( JarFile jarFile = new JarFile( filename ) ) { 166 final Enumeration<JarEntry> en = jarFile.entries() ; 167 while( en.hasMoreElements() ) { 168 final JarEntry ent = en.nextElement(); 169 if( ! ent.isDirectory() ) { 170 final String name = ent.getName(); 171 if( name.endsWith( SUFIX ) ) { 172 list.add( name.substring( 0,name.length()-SUFIX_LEN ).replace( '/','.' ) ); 173 } 174 } 175 } 176 } 177 catch( final IOException ex ) { 178 final String errMsg = "ファイル読み取りストリームに失敗しました。" 179 + " File=" + filename 180 + ex.getMessage(); // 5.1.8.0 (2010/07/01) errMsg 修正 181 throw new OgRuntimeException( errMsg,ex ); 182 } 183 // 5.5.2.6 (2012/05/25) findbugs対応 184// finally { 185// Closer.zipClose( jarFile ); 186// } 187 } 188 189 /** 190 * サンプルメイン 191 * ここでは、引数に通常のファイルではなく、クラスパスより取得します。 192 * 通常、取得されたファイル名は、クラスの完全系の文字列なので、クラスパスより取得 193 * している限り、そのまま オブジェクトを構築できることを意味します。 194 * 195 * @og.rev 6.8.5.1 (2018/01/15) ファイル名は、##バージョン番号を変換しておく必要がある。 196 * 197 * @param args 引数 198 */ 199 public static void main( final String[] args ) { 200 try { 201 final ClassLoader loader = Thread.currentThread().getContextClassLoader(); 202 final Enumeration<URL> enume = loader.getResources( args[0] ); // 4.3.3.6 (2008/11/15) Generics警告対応 203 while( enume.hasMoreElements() ) { 204 final URL url = enume.nextElement(); // 4.3.3.6 (2008/11/15) Generics警告対応 205 // jar:file:/実ディレクトリ!先頭クラス または、file:/実ディレクトリ 206 final String dir = url.getFile().replaceAll( "%23%23","##" ); // 6.8.5.1 (2018/01/15) 207// System.out.println( "url=" + url.getFile() ); 208 System.out.println( "url=" + dir ); // 6.8.5.1 (2018/01/15) 209 210// final FindClassFiles filenames = new FindClassFiles( url.getFile(),args[0] ); 211 final FindClassFiles filenames = new FindClassFiles( dir,args[0] ); // 6.8.5.1 (2018/01/15) 212 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 213// final String[] names = filenames.getFilenames(); 214// for( int i=0; i<names.length; i++ ) { 215// System.out.println( names[i] ); 216// } 217 for( final String name : filenames.getFilenames() ) { 218 System.out.println( name ); 219 } 220 } 221 } 222 catch( final IOException ex ) { 223 final String errMsg = "ファイル読み取りストリームに失敗しました。" 224 + ex.getMessage(); 225 throw new OgRuntimeException( errMsg,ex ); 226 } 227 } 228}