KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > module > bridge > impl > BridgeImpl


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.apache.tools.ant.module.bridge.impl;
21
22 import java.beans.Introspector JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.io.PrintStream JavaDoc;
27 import java.lang.reflect.Constructor JavaDoc;
28 import java.lang.reflect.Field JavaDoc;
29 import java.lang.reflect.Method JavaDoc;
30 import java.lang.reflect.Modifier JavaDoc;
31 import java.net.URL JavaDoc;
32 import java.util.Collection JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.HashSet JavaDoc;
35 import java.util.LinkedHashSet JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Map JavaDoc;
38 import java.util.Set JavaDoc;
39 import java.util.Vector JavaDoc;
40 import java.util.WeakHashMap JavaDoc;
41 import java.util.logging.Level JavaDoc;
42 import java.util.logging.Logger JavaDoc;
43 import org.apache.tools.ant.BuildException;
44 import org.apache.tools.ant.DemuxOutputStream;
45 import org.apache.tools.ant.IntrospectionHelper;
46 import org.apache.tools.ant.Main;
47 import org.apache.tools.ant.Project;
48 import org.apache.tools.ant.ProjectHelper;
49 import org.apache.tools.ant.module.AntModule;
50 import org.apache.tools.ant.module.AntSettings;
51 import org.apache.tools.ant.module.api.IntrospectedInfo;
52 import org.apache.tools.ant.module.bridge.AntBridge;
53 import org.apache.tools.ant.module.bridge.BridgeInterface;
54 import org.apache.tools.ant.module.bridge.IntrospectionHelperProxy;
55 import org.apache.tools.ant.module.bridge.impl.NbBuildLogger;
56 import org.apache.tools.ant.module.bridge.impl.NbInputHandler;
57 import org.apache.tools.ant.types.EnumeratedAttribute;
58 import org.apache.tools.ant.types.Path;
59 import org.netbeans.api.progress.ProgressHandle;
60 import org.openide.ErrorManager;
61 import org.openide.awt.StatusDisplayer;
62 import org.openide.filesystems.FileObject;
63 import org.openide.filesystems.FileStateInvalidException;
64 import org.openide.filesystems.FileSystem;
65 import org.openide.filesystems.FileUtil;
66 import org.openide.util.Exceptions;
67 import org.openide.util.NbBundle;
68 import org.openide.util.NbCollections;
69 import org.openide.util.RequestProcessor;
70 import org.openide.windows.OutputWriter;
71
72 /**
73  * Implements the BridgeInterface using the current version of Ant.
74  * @author Jesse Glick
75  */

76 public class BridgeImpl implements BridgeInterface {
77     
78     /** Number of milliseconds to wait before forcibly halting a runaway process. */
79     private static final int STOP_TIMEOUT = 3000;
80     
81     private static boolean classpathInitialized = false;
82     
83     /**
84      * Index of loggers by active thread.
85      * @see #stop
86      */

87     private static final Map JavaDoc<Thread JavaDoc,NbBuildLogger> loggersByThread = new WeakHashMap JavaDoc<Thread JavaDoc,NbBuildLogger>();
88     
89     public BridgeImpl() {
90     }
91     
92     public String JavaDoc getAntVersion() {
93         try {
94             return Main.getAntVersion();
95         } catch (BuildException be) {
96             AntModule.err.notify(ErrorManager.INFORMATIONAL, be);
97             return NbBundle.getMessage(BridgeImpl.class, "LBL_ant_version_unknown");
98         }
99     }
100     
101     public boolean isAnt16() {
102         try {
103             Class.forName("org.apache.tools.ant.taskdefs.Antlib"); // NOI18N
104
return true;
105         } catch (ClassNotFoundException JavaDoc e) {
106             // Fine, 1.5
107
return false;
108         }
109     }
110     
111     public IntrospectionHelperProxy getIntrospectionHelper(Class JavaDoc clazz) {
112         return new IntrospectionHelperImpl(clazz);
113     }
114     
115     public boolean toBoolean(String JavaDoc val) {
116         return Project.toBoolean(val);
117     }
118     
119     public String JavaDoc[] getEnumeratedValues(Class JavaDoc c) {
120         if (EnumeratedAttribute.class.isAssignableFrom(c)) {
121             try {
122                 return ((EnumeratedAttribute)c.newInstance()).getValues();
123             } catch (Exception JavaDoc e) {
124                 AntModule.err.notify(ErrorManager.INFORMATIONAL, e);
125             }
126         } else if (Enum JavaDoc.class.isAssignableFrom(c)) { // Ant 1.7.0 (#41058)
127
try {
128                 Enum JavaDoc<?>[] vals = (Enum JavaDoc<?>[]) c.getMethod("values").invoke(null);
129                 String JavaDoc[] names = new String JavaDoc[vals.length];
130                 for (int i = 0; i < vals.length; i++) {
131                     names[i] = vals[i].name();
132                 }
133                 return names;
134             } catch (Exception JavaDoc x) {
135                 Exceptions.printStackTrace(x);
136             }
137         }
138         return null;
139     }
140     
141     public boolean run(File JavaDoc buildFile, List JavaDoc<String JavaDoc> targets, InputStream JavaDoc in, OutputWriter out, OutputWriter err,
142                        Map JavaDoc<String JavaDoc,String JavaDoc> properties, int verbosity, String JavaDoc displayName, Runnable JavaDoc interestingOutputCallback, ProgressHandle handle) {
143         if (!classpathInitialized) {
144             classpathInitialized = true;
145             // #46171: Ant expects this path to have itself and whatever else you loaded with it,
146
// or AntClassLoader.getResources will not be able to find anything in the Ant loader.
147
Path.systemClasspath = new Path(null, AntBridge.getMainClassPath());
148         }
149         
150         boolean ok = false;
151         
152         // Important for various other stuff.
153
final boolean ant16 = isAnt16();
154         
155         // Make sure "main Ant loader" is used as context loader for duration of the
156
// run. Otherwise some code, e.g. JAXP, will accidentally pick up NB classes,
157
// which can cause various undesirable effects.
158
ClassLoader JavaDoc oldCCL = Thread.currentThread().getContextClassLoader();
159         ClassLoader JavaDoc newCCL = Project.class.getClassLoader();
160         if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
161             AntModule.err.log("Fixing CCL: " + oldCCL + " -> " + newCCL);
162         }
163         Thread.currentThread().setContextClassLoader(newCCL);
164         AntBridge.fakeJavaClassPath();
165         try {
166         
167         final Project project;
168         
169         // first use the ProjectHelper to create the project object
170
// from the given build file.
171
final NbBuildLogger logger = new NbBuildLogger(buildFile, out, err, verbosity, displayName, interestingOutputCallback, handle);
172         Vector JavaDoc<String JavaDoc> targs;
173         try {
174             project = new Project();
175             project.addBuildListener(logger);
176             project.init();
177             try {
178                 addCustomDefs(project);
179             } catch (IOException JavaDoc e) {
180                 throw new BuildException(e);
181             }
182             project.setUserProperty("ant.file", buildFile.getAbsolutePath()); // NOI18N
183
// #14993:
184
project.setUserProperty("ant.version", Main.getAntVersion()); // NOI18N
185
File JavaDoc antHome = AntSettings.getAntHome();
186             if (antHome != null) {
187                 project.setUserProperty("ant.home", antHome.getAbsolutePath()); // NOI18N
188
}
189             for (Map.Entry JavaDoc<String JavaDoc,String JavaDoc> entry : properties.entrySet()) {
190                 project.setUserProperty(entry.getKey(), entry.getValue());
191             }
192             if (in != null && ant16) {
193                 try {
194                     Method JavaDoc m = Project.class.getMethod("setDefaultInputStream", InputStream JavaDoc.class); // NOI18N
195
m.invoke(project, in);
196                 } catch (Exception JavaDoc e) {
197                     AntModule.err.notify(ErrorManager.INFORMATIONAL, e);
198                 }
199             }
200             if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
201                 AntModule.err.log("CCL when configureProject is called: " + Thread.currentThread().getContextClassLoader());
202             }
203             ProjectHelper projhelper = ProjectHelper.getProjectHelper();
204             // Cf. Ant #32668 & #32216; ProjectHelper.configureProject undeprecated in 1.7
205
project.addReference("ant.projectHelper", projhelper); // NOI18N
206
projhelper.parse(project, buildFile);
207             
208             project.setInputHandler(new NbInputHandler(interestingOutputCallback));
209             
210             if (targets != null) {
211                 targs = new Vector JavaDoc<String JavaDoc>(targets);
212             } else {
213                 targs = new Vector JavaDoc<String JavaDoc>(1);
214                 targs.add(project.getDefaultTarget());
215             }
216             logger.setActualTargets(targets != null ? targets.toArray(new String JavaDoc[targets.size()]) : null);
217         }
218         catch (BuildException be) {
219             logger.buildInitializationFailed(be);
220             logger.shutdown();
221             if (in != null) {
222                 try {
223                     in.close();
224                 } catch (IOException JavaDoc e) {
225                     AntModule.err.notify(e);
226                 }
227             }
228             return false;
229         }
230         
231         project.fireBuildStarted();
232         
233         // Save & restore system output streams.
234
InputStream JavaDoc is = System.in;
235         if (in != null && ant16) {
236             try {
237                 Class JavaDoc<? extends InputStream JavaDoc> dis = Class.forName("org.apache.tools.ant.DemuxInputStream").asSubclass(InputStream JavaDoc.class); // NOI18N
238
Constructor JavaDoc<? extends InputStream JavaDoc> c = dis.getConstructor(Project.class);
239                 is = c.newInstance(project);
240             } catch (Exception JavaDoc e) {
241                 AntModule.err.notify(ErrorManager.INFORMATIONAL, e);
242             }
243         }
244         AntBridge.pushSystemInOutErr(is,
245                                      new PrintStream JavaDoc(new DemuxOutputStream(project, false)),
246                                      new PrintStream JavaDoc(new DemuxOutputStream(project, true)));
247
248         Thread JavaDoc currentThread = Thread.currentThread();
249         synchronized (loggersByThread) {
250             assert !loggersByThread.containsKey(currentThread);
251             loggersByThread.put(currentThread, logger);
252         }
253         try {
254             // Execute the configured project
255
//writer.println("#4"); // NOI18N
256
project.executeTargets(targs);
257             //writer.println("#5"); // NOI18N
258
project.fireBuildFinished(null);
259             ok = true;
260         } catch (Throwable JavaDoc t) {
261             // Really need to catch everything, else AntClassLoader.cleanup may
262
// not be called, resulting in a memory leak and/or locked JARs (#42431).
263
project.fireBuildFinished(t);
264         } finally {
265             AntBridge.restoreSystemInOutErr();
266             if (in != null) {
267                 try {
268                     in.close();
269                 } catch (IOException JavaDoc e) {
270                     AntModule.err.notify(e);
271                 }
272             }
273             synchronized (loggersByThread) {
274                 loggersByThread.remove(currentThread);
275             }
276         }
277         
278         // Now check to see if the Project defined any cool new custom tasks.
279
RequestProcessor.getDefault().post(new Runnable JavaDoc() {
280             public void run() {
281                 IntrospectedInfo custom = AntSettings.getCustomDefs();
282                 Map JavaDoc<String JavaDoc,Map JavaDoc<String JavaDoc,Class JavaDoc>> defs = new HashMap JavaDoc<String JavaDoc,Map JavaDoc<String JavaDoc,Class JavaDoc>>();
283                 defs.put("task", NbCollections.checkedMapByCopy(project.getTaskDefinitions(), String JavaDoc.class, Class JavaDoc.class, true));
284                 defs.put("type", NbCollections.checkedMapByCopy(project.getDataTypeDefinitions(), String JavaDoc.class, Class JavaDoc.class, true));
285                 custom.scanProject(defs);
286                 AntSettings.setCustomDefs(custom);
287                 logger.shutdown();
288                 // #85698: do not invoke multiple refreshes at once
289
refreshFilesystemsTask.schedule(0);
290                 gutProject(project);
291                 if (!ant16) {
292                     // #36393 - memory leak in Ant 1.5.
293
RequestProcessor.getDefault().post(new Runnable JavaDoc() {
294                         public void run() {
295                             hack36393();
296                         }
297                     });
298                 }
299             }
300         });
301         
302         } finally {
303             AntBridge.unfakeJavaClassPath();
304             if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
305                 AntModule.err.log("Restoring CCL: " + oldCCL);
306             }
307             Thread.currentThread().setContextClassLoader(oldCCL);
308         }
309         
310         return ok;
311     }
312
313     private static final RequestProcessor.Task refreshFilesystemsTask = RequestProcessor.getDefault().create(new Runnable JavaDoc() {
314         public void run() {
315             // #8993: also try to refresh masterfs...this is hackish...
316
// cf. also RefreshAllFilesystemsAction
317
for (FileSystem fs : getFileSystems()) {
318                 Logger.getLogger(BridgeImpl.class.getName()).log(Level.FINE, "Refreshing filesystem {0}", fs);
319                 fs.refresh(false);
320             }
321         }
322     });
323
324     public void stop(final Thread JavaDoc process) {
325         NbBuildLogger logger;
326         synchronized (loggersByThread) {
327             logger = loggersByThread.get(process);
328         }
329         if (logger != null) {
330             // Try stopping at a safe point.
331
StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(BridgeImpl.class, "MSG_stopping", logger.getDisplayNameNoLock()));
332             logger.stop();
333             // But if that doesn't do it, double-check later...
334
// Yes Thread.stop() is deprecated; that is why we try to avoid using it.
335
RequestProcessor.getDefault().create(new Runnable JavaDoc() {
336                 public void run () {
337                     forciblyStop(process);
338                 }
339             }).schedule(STOP_TIMEOUT);
340         } else {
341             // Try killing it now!
342
forciblyStop(process);
343         }
344     }
345     
346     private void forciblyStop(Thread JavaDoc process) {
347         if (process.isAlive()) {
348             StatusDisplayer.getDefault().setStatusText(NbBundle.getMessage(BridgeImpl.class, "MSG_halting"));
349             // XXX try using process.interrupt() first, then wait a bit longer
350
stopThread(process);
351         }
352     }
353
354     @SuppressWarnings JavaDoc("deprecation")
355     private static void stopThread(Thread JavaDoc process) {
356         process.stop();
357     }
358     
359     //copy - paste programming
360
//http://ant.netbeans.org/source/browse/ant/src-bridge/org/apache/tools/ant/module/bridge/impl/BridgeImpl.java.diff?r1=1.15&r2=1.16
361
//http:/java.netbeans.org/source/browse/java/javacore/src/org/netbeans/modules/javacore/Util.java
362
//http://core.netbeans.org/source/browse/core/ui/src/org/netbeans/core/ui/MenuWarmUpTask.java
363
//http://core.netbeans.org/source/browse/core/src/org/netbeans/core/actions/RefreshAllFilesystemsAction.java
364
//http://java.netbeans.org/source/browse/java/api/src/org/netbeans/api/java/classpath/ClassPath.java
365

366     private static FileSystem[] fileSystems;
367
368     private static FileSystem[] getFileSystems() {
369         if (fileSystems != null) {
370             return fileSystems;
371         }
372         File JavaDoc[] roots = File.listRoots();
373         Set JavaDoc<FileSystem> allRoots = new LinkedHashSet JavaDoc<FileSystem>();
374         assert roots != null && roots.length > 0 : "Could not list file roots"; // NOI18N
375

376         for (File JavaDoc root : roots) {
377             FileObject random = FileUtil.toFileObject(root);
378             if (random == null) continue;
379             
380             FileSystem fs;
381             try {
382                 fs = random.getFileSystem();
383                 allRoots.add(fs);
384                 
385                 /*Because there is MasterFileSystem impl. that provides conversion to FileObject for all File.listRoots
386                 (except floppy drives and empty CD). Then there is useless to convert all roots into FileObjects including
387                 net drives that might cause performance regression.
388                 */

389                 
390                 if (fs != null) {
391                     break;
392                 }
393             } catch (FileStateInvalidException e) {
394                 throw new AssertionError JavaDoc(e);
395             }
396         }
397         assert !allRoots.isEmpty() : "Could not get any filesystem"; // NOI18N
398
return fileSystems = allRoots.toArray(new FileSystem[allRoots.size()]);
399     }
400
401     private static void addCustomDefs(Project project) throws BuildException, IOException JavaDoc {
402         long start = System.currentTimeMillis();
403         if (AntBridge.getInterface().isAnt16()) {
404             Map JavaDoc<String JavaDoc,ClassLoader JavaDoc> antlibLoaders = AntBridge.getCustomDefClassLoaders();
405             for (Map.Entry JavaDoc<String JavaDoc,ClassLoader JavaDoc> entry : antlibLoaders.entrySet()) {
406                 String JavaDoc cnb = entry.getKey();
407                 ClassLoader JavaDoc l = entry.getValue();
408                 String JavaDoc resource = cnb.replace('.', '/') + "/antlib.xml"; // NOI18N
409
URL JavaDoc antlib = l.getResource(resource);
410                 if (antlib == null) {
411                     throw new IOException JavaDoc("Could not find " + antlib + " in ant/nblib/" + cnb.replace('.', '-') + ".jar"); // NOI18N
412
}
413                 // Once with no namespaces.
414
NbAntlib.process(project, antlib, null, l);
415                 // Once with.
416
String JavaDoc antlibUri = "antlib:" + cnb; // NOI18N
417
NbAntlib.process(project, antlib, antlibUri, l);
418             }
419         } else {
420             // For Ant 1.5, just dump in old-style defs in the simplest manner.
421
Map JavaDoc<String JavaDoc,Map JavaDoc<String JavaDoc,Class JavaDoc>> customDefs = AntBridge.getCustomDefsNoNamespace();
422             for (Map.Entry JavaDoc<String JavaDoc,Class JavaDoc> entry : customDefs.get("task").entrySet()) { // NOI18N
423
project.addTaskDefinition(entry.getKey(), entry.getValue());
424             }
425             for (Map.Entry JavaDoc<String JavaDoc,Class JavaDoc> entry : customDefs.get("type").entrySet()) { // NOI18N
426
project.addDataTypeDefinition(entry.getKey(), entry.getValue());
427             }
428         }
429         if (AntModule.err.isLoggable(ErrorManager.INFORMATIONAL)) {
430             AntModule.err.log("addCustomDefs took " + (System.currentTimeMillis() - start) + "msec");
431         }
432     }
433     
434     private static boolean doHack36393 = true;
435     /**
436      * Remove any outstanding ProcessDestroyer shutdown hooks.
437      * They should not be left in the JRE static area.
438      * Workaround for bug in Ant 1.5.x, fixed already in Ant 1.6.
439      */

440     private static void hack36393() {
441         if (!doHack36393) {
442             // Failed last time, skip this time.
443
return;
444         }
445         try {
446             Class JavaDoc shutdownC = Class.forName("java.lang.Shutdown"); // NOI18N
447
Class JavaDoc wrappedHookC = Class.forName("java.lang.Shutdown$WrappedHook"); // NOI18N
448
Field JavaDoc hooksF = shutdownC.getDeclaredField("hooks"); // NOI18N
449
hooksF.setAccessible(true);
450             Field JavaDoc hookF = wrappedHookC.getDeclaredField("hook"); // NOI18N
451
hookF.setAccessible(true);
452             Field JavaDoc lockF = shutdownC.getDeclaredField("lock"); // NOI18N
453
lockF.setAccessible(true);
454             Object JavaDoc lock = lockF.get(null);
455             Set JavaDoc<Thread JavaDoc> toRemove = new HashSet JavaDoc<Thread JavaDoc>();
456             synchronized (lock) {
457                 @SuppressWarnings JavaDoc("unchecked")
458                 Set JavaDoc<Object JavaDoc> hooks = (Set JavaDoc) hooksF.get(null);
459                 for (Object JavaDoc wrappedHook : hooks) {
460                     Thread JavaDoc hook = (Thread JavaDoc)hookF.get(wrappedHook);
461                     if (hook.getClass().getName().equals("org.apache.tools.ant.taskdefs.ProcessDestroyer")) { // NOI18N
462
// Don't remove it now - will get ConcurrentModificationException.
463
toRemove.add(hook);
464                     }
465                 }
466             }
467             for (Thread JavaDoc hook : toRemove) {
468                 if (!Runtime.getRuntime().removeShutdownHook(hook)) {
469                     throw new IllegalStateException JavaDoc("Hook was not really registered!"); // NOI18N
470
}
471                 AntModule.err.log("#36393: removing an unwanted ProcessDestroyer shutdown hook");
472                 // #36395: memory leak in ThreadGroup if the thread is not started.
473
hook.start();
474             }
475         } catch (Exception JavaDoc e) {
476             // Oh well.
477
AntModule.err.notify(ErrorManager.INFORMATIONAL, e);
478             doHack36393 = false;
479         }
480     }
481     
482     private static boolean doGutProject = !Boolean.getBoolean("org.apache.tools.ant.module.bridge.impl.BridgeImpl.doNotGutProject");
483     /**
484      * Try to break up as many references in a project as possible.
485      * Helpful to mitigate the effects of unsolved memory leaks: at
486      * least one project will not hold onto all subprojects, and a
487      * taskdef will not hold onto its siblings, etc.
488      */

489     private static void gutProject(Project p) {
490         if (!doGutProject) {
491             return;
492         }
493         // XXX should ideally try to wait for all other threads in this thread group
494
// to finish - see e.g. #51962 for example of what can happen otherwise.
495
try {
496             String JavaDoc s = p.getName();
497             AntModule.err.log("Gutting extra references in project \"" + s + "\"");
498             Field JavaDoc[] fs = Project.class.getDeclaredFields();
499             for (int i = 0; i < fs.length; i++) {
500                 if (Modifier.isStatic(fs[i].getModifiers())) {
501                     continue;
502                 }
503                 if (fs[i].getType().isPrimitive()) {
504                     continue;
505                 }
506                 fs[i].setAccessible(true);
507                 Object JavaDoc o = fs[i].get(p);
508                 try {
509                     if (o instanceof Collection JavaDoc) {
510                         ((Collection JavaDoc) o).clear();
511                         // #69727: do not null out the field (e.g. Project.listeners) in this case.
512
continue;
513                     } else if (o instanceof Map JavaDoc) {
514                         ((Map JavaDoc) o).clear();
515                         continue;
516                     }
517                 } catch (UnsupportedOperationException JavaDoc e) {
518                     // ignore
519
}
520                 if (Modifier.isFinal(fs[i].getModifiers())) {
521                     continue;
522                 }
523                 fs[i].set(p, null);
524             }
525             // #43113: IntrospectionHelper can hold strong refs to dynamically loaded classes
526
Field JavaDoc helpersF;
527             try {
528                 helpersF = IntrospectionHelper.class.getDeclaredField("helpers");
529             } catch (NoSuchFieldException JavaDoc x) { // Ant 1.7.0
530
helpersF = IntrospectionHelper.class.getDeclaredField("HELPERS");
531             }
532             helpersF.setAccessible(true);
533             Object JavaDoc helpersO = helpersF.get(null);
534             Map JavaDoc helpersM = (Map JavaDoc) helpersO;
535             helpersM.clear();
536             // #46532: java.beans.Introspector caches not cleared well in all cases.
537
Introspector.flushCaches();
538         } catch (Exception JavaDoc e) {
539             // Oh well.
540
AntModule.err.notify(ErrorManager.INFORMATIONAL, e);
541             doGutProject = false;
542         }
543     }
544     
545 }
546
Popular Tags