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 }