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 */ 016 package org.opengion.fukurou.util; 017 018 import java.io.File; 019 import java.io.StringWriter; 020 import java.net.MalformedURLException; 021 import java.net.URL; 022 import java.net.URLClassLoader; 023 import java.util.Arrays; 024 import java.util.Map; 025 import java.util.WeakHashMap; 026 027 import javax.tools.JavaCompiler; 028 import javax.tools.StandardJavaFileManager; 029 import javax.tools.ToolProvider; 030 import javax.tools.JavaCompiler.CompilationTask; 031 032 /** 033 * AutoCompile機è?ã€HotDeploy機è?を実ç¾ã™ã‚‹ãŸã‚ã®ã‚¯ãƒ©ã‚¹ãƒãƒ¼ãƒ??ã§ã™ã? 034 * 035 * AutoCompile機è?ã¯ã€ã‚¯ãƒ©ã‚¹ã®å‹•的コンパイルを行ã„ã¾ã™ã? 036 * AutoCompile機è?を有効ã«ã™ã‚‹ã«ã¯ã€ã‚³ãƒ³ã‚¹ãƒˆãƒ©ã‚¯ã‚¿ã§ä¸Žãˆã‚‰ã‚Œã‚‹HybsLoaderConfigオブジェクトã§ã€? 037 * AutoCompileフラグをtrueã«ã—ã¦ãŠãå¿?¦ãŒã‚りã¾ã™ã? 038 * 039 * HotDeploy機è?ã¯ã€ã‚¯ãƒ©ã‚¹ã®å‹•çš„ãƒãƒ¼ãƒ‰ã‚’行ã„ã¾ã™ã? 040 * HotDeploy機è?を有効ã«ã™ã‚‹ã«ã¯ã€ã‚³ãƒ³ã‚¹ãƒˆãƒ©ã‚¯ã‚¿ã§ä¸Žãˆã‚‰ã‚Œã‚‹HybsLoaderConfigオブジェクトã§ã€? 041 * HotDeployフラグをtrueã«ã—ã¦ãŠãå¿?¦ãŒã‚りã¾ã™ã? 042 * 043 * (1)クラスã®å‹•的コンパイル 044 * {@link #loadClass(String)}メソãƒ?ƒ‰ãŒå‘¼ã°ã‚ŒãŸå ´åˆã«ã€ã‚½ãƒ¼ã‚¹ãƒ?‚£ãƒ¬ã‚¯ãƒˆã‚ˆã‚Šã?対象ã¨ãªã‚‹ã‚½ãƒ¼ã‚¹ãƒ•ァイルã‚? 045 * 検索ã—ã?クラスã®ã‚³ãƒ³ãƒ‘イルを行ã„ã¾ã™ã? 046 * コンパイルãŒè¡Œã‚れるæ¡ä»¶ã¯ã€ã?クラスファイルãŒå˜åœ¨ã—ãªã??ã¾ãŸã?「クラスファイルã®ã‚¿ã‚¤ãƒ?‚¹ã‚¿ãƒ³ãƒ—ãŒã‚½ãƒ¼ã‚¹ãƒ•ァイルよりå¤ã??ã§ã™ã? 047 * 048 * コンパイルを行ã†ã«ã¯ã€JDKã«å«ã¾ã‚Œã‚‹tools.jarãŒå˜åœ¨ã—ã¦ã?‚‹å¿?¦ãŒã‚りã¾ã™ã? 049 * tools.jarãŒè¦‹ã¤ã‹ã‚‰ãªã??åˆã?エラーã¨ãªã‚Šã¾ã™ã? 050 * 051 * ã¾ãŸã?コンパイルã®ã‚¿ã‚¹ã‚¯ã®ã‚¯ãƒ©ã‚¹(オブジェクãƒ?ã¯ã€JVMã®ã‚·ã‚¹ãƒ?ƒ クラスãƒãƒ¼ãƒ??上ã?クラスã«å˜åœ¨ã—ã¦ã?¾ã™ã? 052 * ã“ã?ãŸã‚ã€ã‚µãƒ¼ãƒ–レãƒ?ƒˆã‚³ãƒ³ãƒ?ƒŠã§ã€??常èªã¿è¾¼ã¾ã‚Œã‚‹WEB-INF/classes,WEB-INF/lib以下ã?クラスファイルもã? 053 * ãã?ã¾ã¾ã§ã¯å‚ç?ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã€? 054 * ã“れらã?クラスをå‚ç…§ã™ã‚‹å ´åˆã?ã€HybsLoaderConfigオブジェクトã«å¯¾ã—ã¦ã‚¯ãƒ©ã‚¹ãƒ‘スをè¨å®šã—ã¦ãŠãå¿?¦ãŒã‚りã¾ã™ã? 055 * 056 * (2)クラスãƒãƒ¼ãƒ? 057 * クラスã®å‹•çš„ãƒãƒ¼ãƒ‰ã?ã€ã‚¯ãƒ©ã‚¹ãƒãƒ¼ãƒ??ã®å…¥ã‚Œæ›¿ãˆã«ã‚ˆã£ã¦å®Ÿç¾ã—ã¦ã?¾ã™ã? 058 * HotDeploy機è?を有効ã«ã—ãŸå ´åˆã?èªã¿è¾¼ã‚?‚¯ãƒ©ã‚¹å˜ä½ã«URLClassLoaderを生æˆã—ã¦ã?¾ã™ã? 059 * クラスãƒãƒ¼ãƒ‰ã‚’行ã†éš›ã«ã€URLClassLoaderã‚’æ–°ã—ã生æ?ã™ã‚‹ã“ã¨ã§ã€ã‚¯ãƒ©ã‚¹ã®å†ãƒãƒ¼ãƒ‰ã‚’行ã£ã¦ã?¾ã™ã? 060 * ã¤ã¾ã‚Šã?HotDeployã«ã‚ˆã‚Šèªã¿è¾¼ã¾ã‚Œã‚‹ãれãžã‚Œã®ã‚¯ãƒ©ã‚¹ã¯ã€ãŠäº’ã„ã«ç‹¬ç«‹ã—ã?平行ãªä½ç½®ã«å˜åœ¨ã™ã‚‹)関係㫠061 * ãªã‚Šã¾ã™ã? 062 * ã“ã?ãŸã‚ã€ã‚ã‚‹HotDeployã«ã‚ˆã‚Šãƒãƒ¼ãƒ‰ã•れãŸã‚¯ãƒ©ã‚¹Aã‹ã‚‰ã€åŒã˜ãHotDeployã«ã‚ˆã‚Šãƒãƒ¼ãƒ‰ã•れãŸã‚¯ãƒ©ã‚¹Bを直接å‚ç? 063 * ã™ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã€? 064 * ã“ã?å ´åˆã?ã€ã‚¯ãƒ©ã‚¹Bã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェースをé™çš?ªã‚¯ãƒ©ã‚¹ãƒãƒ¼ãƒ??(クラスAã‹ã‚‰å‚ç?ã§ãã‚‹ä½ç½®)ã«é…ç½®ã™ã‚‹ã“ã¨ã§ã€ã‚¯ãƒ©ã‚¹B 065 * ã®ã‚ªãƒ–ジェクトã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã? 066 * 067 * @og.rev 5.1.1.0 (2009/12/01) æ–°è¦ä½œæ? 068 * @og.group æ¥å‹™ãƒã‚¸ãƒ?‚¯ 069 * 070 * @version 5.0 071 * @author Hiroki Nakamura 072 * @since JDK1.6, 073 */ 074 public class HybsLoader { 075 private static final String CR = System.getProperty("line.separator"); 076 077 // HotDeploy機è?を使用ã—ãªã??åˆã?URLClossLoaderã®ã‚ャãƒ?‚·ãƒ¥ã‚ー 078 private static final String CONST_LOADER_KEY = "CONST_LOADER_KEY"; 079 080 private static final JavaCompiler COMPILER = ToolProvider.getSystemJavaCompiler(); 081 private static final StandardJavaFileManager FILE_MANAGER = COMPILER.getStandardFileManager(null, null, null); 082 083 private final Map<String, HybsURLClassLoader> loaderMap = new WeakHashMap<String, HybsURLClassLoader>(); 084 private final Map<String, String> clsNameMap = new WeakHashMap<String, String>(); 085 private final String srcDir; 086 private final String classDir; 087 private final boolean isHotDeploy; 088 private final boolean isAutoCompile; 089 private final String classPath; 090 091 /** 092 * HybsLoaderOptionを使用ã—ã¦HybsLoaderオブジェクトを生æ?ã—ã¾ã™ã? 093 * 094 * @param option HybsLoaderを構築ã™ã‚‹ãŸã‚ã?è¨å®šæƒ…å ± 095 */ 096 public HybsLoader( final HybsLoaderConfig option ) { 097 srcDir = option.getSrcDir(); 098 classDir = option.getClassDir(); 099 isHotDeploy = option.isHotDeploy(); 100 isAutoCompile = option.isAutoCompile(); 101 classPath = option.getClassPath(); 102 } 103 104 /** 105 * æŒ?®šã•れãŸã‚¯ãƒ©ã‚¹åã?クラスをãƒãƒ¼ãƒ‰ã—ã¾ã™ã? 106 * クラスåã«ã¤ã?¦ã¯ã€ã‚¯ãƒ©ã‚¹è‡ªèº«ã®åç§°ã®ã¿ã‚’指定ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã? 107 * (パッケージåã‚’å«ã‚ãŸå®Œå?ãªå½¢ã®ã‚¯ãƒ©ã‚¹åã‚’æŒ?®šã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã? 108 * 109 * @param clsNm クラスå? 110 * 111 * @return クラス 112 */ 113 public Class<?> load( final String clsNm ) { 114 String clsName = getQualifiedName( clsNm ); 115 if( isAutoCompile ) { 116 compileClass( clsName ); 117 } 118 Class<?> cls = loadClass( clsName ); 119 120 return cls; 121 } 122 123 /** 124 * æŒ?®šã•れãŸã‚¯ãƒ©ã‚¹åã?クラスをãƒãƒ¼ãƒ‰ã—ã€ãƒ‡ãƒ•ォルトコンストラクターを使用ã—㦠125 * インスタンスを生æˆã—ã¾ã™ã? 126 * 127 * @og.rev 5.1.8.0 (2010/07/01) Exceptionã®ã‚¨ãƒ©ãƒ¼ãƒ¡ãƒ?‚»ãƒ¼ã‚¸ã®ä¿®æ£(状態ã?出åŠ? 128 * 129 * @param clsName クラスå?Qualified Name) 130 * 131 * @return インスタンス 132 */ 133 public Object newInstance( final String clsName ) { 134 Class<?> cls = load( clsName ); 135 Object obj = null; 136 try { 137 obj = cls.newInstance(); 138 } 139 catch( InstantiationException ex ) { 140 // throw new RuntimeException( "インスタンスã®ç”Ÿæ?ã«å¤±æ•—ã—ã¾ã—ãŸ" ); 141 String errMsg = "インスタンスã®ç”Ÿæ?ã«å¤±æ•—ã—ã¾ã—ãŸã€?" + clsName + "]" ; 142 throw new RuntimeException( errMsg , ex ); 143 } 144 catch( IllegalAccessException ex ) { 145 // throw new RuntimeException( "ã‚¢ã‚¯ã‚»ã‚¹ãŒæ‹’å¦ã•れã¾ã—ãŸ" ); 146 String errMsg = "ã‚¢ã‚¯ã‚»ã‚¹ãŒæ‹’å¦ã•れã¾ã—ãŸã€?" + clsName + "]" ; 147 throw new RuntimeException( errMsg , ex ); 148 } 149 return obj; 150 } 151 152 /** 153 * クラスåより完å?クラスåを検索ã—ã¾ã™ã? 154 * 155 * @og.rev 5.2.1.0 (2010/10/01) クラスファイルãŒå˜åœ¨ã™ã‚‹å ´åˆã?ã€ã‚¨ãƒ©ãƒ¼ã«ã—ãªã?? 156 * 157 * @param clsNm クラスå? 158 * 159 * @return 完å?クラスå? 160 */ 161 private String getQualifiedName( final String clsNm ) { 162 String clsName = null; 163 if( clsNm.indexOf( '.' ) >= 0 ) { 164 clsName = clsNm; 165 } 166 else { 167 synchronized( clsNameMap ) { 168 clsName = clsNameMap.get( clsNm ); 169 if( clsName == null ) { 170 clsName = findFile( "", clsNm ); 171 } 172 if( clsName == null ) { 173 clsName = findFileByCls( "", clsNm ); 174 } 175 clsNameMap.put( clsNm, clsName ); 176 } 177 if( clsName == null ) { 178 throw new RuntimeException( "ソースファイルãŒå˜åœ¨ã—ã¾ã›ã‚“。ファイル=[" + clsNm + "]" ); 179 } 180 } 181 return clsName; 182 } 183 184 /** 185 * クラスåã«å¯¾å¿œã™ã‚‹Javaファイルをå?帰çš?«æ¤œç´¢ã—ã¾ã™ã? 186 * 187 * @param path 既定パス 188 * @param nm クラスå? 189 * 190 * @return 完å?クラスå? 191 */ 192 private String findFile( final String path, final String nm ) { 193 String tmpSrcPath = srcDir + path; 194 File[] files = (new File( tmpSrcPath )).listFiles(); 195 if( files != null && files.length > 0 ) { 196 for( int i=0; i<files.length; i++ ) { 197 if ( files[i].isDirectory() ) { 198 String rtn = findFile( path + files[i].getName() + File.separator, nm ); 199 if( rtn != null && rtn.length() > 0 ) { 200 return rtn; 201 } 202 } 203 else if( ( nm + ".java" ).equals( files[i].getName() ) ) { 204 return path.replace( File.separatorChar, '.' ) + nm; 205 } 206 } 207 } 208 return null; 209 } 210 211 /** 212 * クラスåã«å¯¾å¿œã™ã‚‹Javaファイルをå?帰çš?«æ¤œç´¢ã—ã¾ã™ã? 213 * 214 * @og.rev 5.2.1.0 (2010/10/01) クラスファイルãŒå˜åœ¨ã™ã‚‹å ´åˆã?ã€ã‚¨ãƒ©ãƒ¼ã«ã—ãªã?? 215 * 216 * @param path 既定パス 217 * @param nm クラスå? 218 * 219 * @return 完å?クラスå? 220 */ 221 private String findFileByCls( final String path, final String nm ) { 222 String tmpSrcPath = classDir + path; 223 File[] files = (new File( tmpSrcPath )).listFiles(); 224 if( files != null && files.length > 0 ) { 225 for( int i=0; i<files.length; i++ ) { 226 if ( files[i].isDirectory() ) { 227 String rtn = findFile( path + files[i].getName() + File.separator, nm ); 228 if( rtn != null && rtn.length() > 0 ) { 229 return rtn; 230 } 231 } 232 else if( ( nm + ".class" ).equals( files[i].getName() ) ) { 233 return path.replace( File.separatorChar, '.' ) + nm; 234 } 235 } 236 } 237 return null; 238 } 239 240 /** 241 * クラスをコンパイルã—ã¾ã™ã? 242 * 243 * @og.rev 5.1.8.0 (2010/07/01) ソースファイルã®ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‰ã?ã€UTF-8ã«ã™ã‚‹ã€? 244 * @og.rev 5.2.1.0 (2010/10/01) クラスファイルãŒå˜åœ¨ã™ã‚‹å ´åˆã?ã€ã‚¨ãƒ©ãƒ¼ã«ã—ãªã?? 245 * 246 * @param clsNm クラスå? 247 */ 248 private void compileClass( final String clsNm ) { 249 if( COMPILER == null ) { 250 throw new RuntimeException( "コンパイラクラスãŒå®šç¾©ã•れã¦ã?¾ã›ã‚“。tools.jarãŒå˜åœ¨ã—ãªã?¯èƒ½æ€§ãŒã‚りã¾ã? ); 251 } 252 253 String srcFqn = srcDir + clsNm.replace( ".", File.separator ) + ".java"; 254 File srcFile = new File( srcFqn ); 255 String classFqn = classDir + clsNm.replace( ".", File.separator ) + ".class"; 256 File clsFile = new File ( classFqn ); 257 258 // クラスファイルãŒå˜åœ¨ã™ã‚‹å ´åˆã?ã€ã‚¨ãƒ©ãƒ¼ã«ã—ãªã?? 259 if( !srcFile.exists() ) { 260 if( clsFile.exists() ) { 261 return; 262 } 263 throw new RuntimeException( "ソースファイルãŒå˜åœ¨ã—ã¾ã›ã‚“。ファイル=[" + srcFqn + "]" ); 264 } 265 266 if( clsFile.exists() && srcFile.lastModified() <= clsFile.lastModified() ) { 267 return; 268 } 269 270 if( !clsFile.getParentFile().exists() ) { 271 if( !clsFile.getParentFile().mkdirs() ) { 272 throw new RuntimeException( "ãƒ?‚£ãƒ¬ã‚¯ãƒˆãƒªãŒä½œæ?ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ファイル=[" + clsFile + "]" ); 273 } 274 } 275 276 StringWriter sw = new StringWriter(); 277 File[] sourceFiles = { new File( srcFqn ) }; 278 // 5.1.8.0 (2010/07/01) ソースファイルã®ã‚¨ãƒ³ã‚³ãƒ¼ãƒ‰ã?ã€UTF-8ã«ã™ã‚‹ã€? 279 // String[] cpOpts = new String[]{ "-d", classDir, "-classpath", classPath }; 280 String[] cpOpts = new String[]{ "-d", classDir, "-classpath", classPath, "-encoding", "UTF-8" }; 281 282 CompilationTask task = COMPILER.getTask(sw, FILE_MANAGER, null, Arrays 283 .asList(cpOpts), null, FILE_MANAGER 284 .getJavaFileObjects(sourceFiles)); 285 286 boolean isOk = false; 287 // lockã—ã¦ãŠã‹ãªã?¨ã€java.lang.IllegalStateExceptionãŒç™ºç”Ÿã™ã‚‹ã“ã¨ãŒã‚ã‚? 288 synchronized( this ) { 289 isOk = task.call(); 290 } 291 if( !isOk ) { 292 throw new RuntimeException( "コンパイルã«å¤±æ•—ã—ã¾ã—ãŸã€? + CR + sw.toString() ); 293 } 294 } 295 296 /** 297 * クラスをãƒãƒ¼ãƒ‰ã—ã¾ã™ã? 298 * 299 * @param clsNm クラスå? 300 * 301 * @return ãƒãƒ¼ãƒ‰ã—ãŸã‚¯ãƒ©ã‚¹ã‚ªãƒ–ジェクãƒ? 302 */ 303 private Class<?> loadClass( final String clsNm ) { 304 String key = isHotDeploy ? clsNm : CONST_LOADER_KEY; 305 306 String classFqn = classDir + clsNm.replace( ".", File.separator ) + ".class"; 307 File clsFile = new File( classFqn ); 308 if( !clsFile.exists() ) { 309 throw new RuntimeException( "クラスファイルãŒå˜åœ¨ã—ã¾ã›ã‚“。ファイル=[" + classFqn + "]" ); 310 } 311 long lastClassModifyTime = clsFile.lastModified(); 312 313 HybsURLClassLoader loader = null; 314 synchronized( loaderMap ) { 315 loader = loaderMap.get( key ); 316 if( loader == null || lastClassModifyTime > loader.getCreationTime() ) { 317 try { 318 loader = new HybsURLClassLoader( new URL[] { ( new File( classDir ).toURI().toURL() ) }, this.getClass().getClassLoader() ); 319 } 320 catch( MalformedURLException ex ) { 321 throw new RuntimeException( "クラスãƒãƒ¼ãƒ‰ã?URL変æ›ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ファイル=[" + classFqn + "]", ex ); 322 } 323 loaderMap.put( key, loader ); 324 } 325 } 326 327 Class<?> cls; 328 try { 329 cls = loader.loadClass( clsNm ); 330 } 331 catch( ClassNotFoundException ex ) { 332 // throw new RuntimeException( "クラスãŒå˜åœ¨ã—ã¾ã›ã‚“。ファイル=[" + classFqn + "]" ); 333 String errMsg = "クラスãŒå˜åœ¨ã—ã¾ã›ã‚“。ファイル=[" + classFqn + "]" ; 334 throw new RuntimeException( errMsg , ex ); 335 } 336 return cls; 337 } 338 339 /** 340 * URLClassLoaderã‚’æ‹¡å¼µã—ã?クラスãƒãƒ¼ãƒ??ã®ç”Ÿæ?時間を管ç?§ãるよã†ã«ã—ã¦ã?¾ã™ã? 341 */ 342 private static class HybsURLClassLoader { 343 final URLClassLoader loader; 344 final long creationTime; 345 346 HybsURLClassLoader( final URL[] urls, final ClassLoader clsLd ) { 347 loader = new URLClassLoader( urls, clsLd ); 348 creationTime = System.currentTimeMillis(); 349 } 350 351 HybsURLClassLoader( final URL[] urls ) { 352 this( urls, null ); 353 } 354 355 Class<?> loadClass( final String clsName ) throws ClassNotFoundException { 356 return loader.loadClass( clsName ); 357 } 358 359 long getCreationTime() { 360 return creationTime; 361 } 362 } 363 }