1 /* 
2  * Copyright 2005 Paul Hinds
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.tp23.antinstaller.runtime.exe;
17
18import java.io.File;
19import java.io.FileOutputStream;
20import java.io.IOException;
21import java.io.InputStream;
22import java.io.PrintStream;
23import java.net.MalformedURLException;
24import java.net.URL;
25import java.net.URLClassLoader;
26import java.util.Iterator;
27import java.util.Map;
28import java.util.Properties;
29
30import org.apache.tools.ant.BuildException;
31import org.apache.tools.ant.BuildListener;
32import org.apache.tools.ant.DefaultLogger;
33import org.apache.tools.ant.Diagnostics;
34import org.apache.tools.ant.Project;
35import org.apache.tools.ant.ProjectHelper;
36import org.apache.tools.ant.input.DefaultInputHandler;
37import org.apache.tools.ant.launch.Locator;
38import org.tp23.antinstaller.InstallException;
39import org.tp23.antinstaller.InstallerContext;
40import org.tp23.antinstaller.antmod.ProjectHelper3;
41import org.tp23.antinstaller.selfextract.NonExtractor;
42import org.tp23.antinstaller.selfextract.SelfExtractor;
43/**
44 *
45 * <p>This AntRunner runs Ant builds directly from a Jar without having to extract
46 * the build.xml to temporary space.</p>
47 * <p> </p>
48 * <p>Copyright: Copyright (c) 2004</p>
49 * <p>Company: tp23</p>
50 * @author Paul Hinds
51 * @version $Id: AntProjectFilter.java,v 1.10 2007/01/28 08:44:39 teknopaul Exp $
52 */
53public class AntProjectFilter implements ExecuteFilter{
54    
55    //TODO certain jars are added from Ant default directories that are probably not needed
56
57    private static String antVersion = null;
58    
59    /** The Ant Home property - from default Launcher  */
60    public static final String ANTHOME_PROPERTY = "ant.home";
61
62    /** The location of a per-user library directory - from default Launcher */
63    public static final String USER_LIBDIR = ".ant/lib";
64
65    public AntProjectFilter() {
66    }
67
68    /**
69     * run Ant
70     *
71     * @param ctx InstallerContext
72     * @throws InstallException
73     * @todo Implement this org.tp23.antinstaller.runtime.AntRunner method
74     */
75    public void exec(InstallerContext ctx) throws InstallException {
76        if(ctx.getInstaller().isVerbose()) {
77            ctx.log("Starting Ant Project");
78        }
79
80        try {
81            
82            Project project = new Project();
83            appendClassPath();
84            setAntHome(ctx);
85            project.setCoreLoader(this.getClass().getClassLoader());
86            
87            DefaultLogger antLogger = new DefaultLogger();
88            antLogger.setOutputPrintStream(ctx.getAntOutputRenderer().getOut());
89            antLogger.setErrorPrintStream(ctx.getAntOutputRenderer().getErr());
90            antLogger.setMessageOutputLevel(Project.MSG_INFO);
91            BuildListener bl = ctx.getBuildListener();
92            if(bl != null){
93                project.addBuildListener(bl);
94            }
95            project.addBuildListener(antLogger);
96
97            /*
98             * Log useful ant task output to the log file to help debugging
99             * and for customer support
00             */
01            final String logFileName = ctx.getLogger().getFileName();
02            if( logFileName != null && logFileName.length() > 0 )
03            {
04                PrintStream stream = new PrintStream( new FileOutputStream(logFileName, true), true);
05                DefaultLogger antFileLogger = new DefaultLogger();
06                antFileLogger.setOutputPrintStream( stream );
07                antFileLogger.setErrorPrintStream( stream );
08                int logLevel = (ctx.getInstaller().isVerbose())
09                                    ? Project.MSG_VERBOSE
10                                    : Project.MSG_INFO;
11                antFileLogger.setMessageOutputLevel( logLevel );
12
13                project.addBuildListener( antFileLogger );
14            }
15
16            // irrelevant really but might help someone on a command line
17            project.setInputHandler(new DefaultInputHandler());
18            project.fireBuildStarted();
19            
20            project.init();
21            project.setUserProperty("ant.version", getAntVersion());
22
23
24            // add properties
25            // N.B. properties are not loaded from the file it exists for debugging installers
26            String arg;
27            String value;
28            Map properties = ctx.getInstaller().getResultContainer().getAllProperties();
29            Iterator iter = properties.keySet().iterator();
30            while (iter.hasNext()) {
31                arg = (String) iter.next();
32                value = (String) properties.get(arg);
33                project.setUserProperty(arg, value);
34            }
35            
36            // From here we immitate Main
37            try {
38                Diagnostics.validateVersion();
39            } catch (Throwable exc) {
40                // minimal messages for the benefit of the command line install
41                System.err.println("Version error:" + exc.getClass() + "," + exc.getMessage());
42                return;
43            }
44             
45            ProjectHelper helper = new ProjectHelper3();
46            project.addReference("ant.projectHelper", helper);
47            
48            File buildXml = new File(ctx.getFileRoot(), ctx.getAntBuildFile());
49            if(buildXml.exists()){
50                helper.parse(project, buildXml);
51                project.setUserProperty("ant.file",buildXml.getAbsolutePath());
52            } else {
53                URL buildIS = this.getClass().getResource("/" + ctx.getAntBuildFile());
54                helper.parse(project, buildIS);
55                project.setUserProperty("ant.file", buildIS.toExternalForm());
56            }
57            
58            File enclosingJar = SelfExtractor.getEnclosingJar(this);
59            project.setUserProperty(NonExtractor.ANTINSTALLER_JAR_PROPERTY , enclosingJar.getAbsolutePath());
60            System.out.println(NonExtractor.ANTINSTALLER_JAR_PROPERTY + enclosingJar.getAbsolutePath());
61
62            //what is this !?! project.setKeepGoingMode(keepGoingMode);
63
64            project.setBaseDir(ctx.getFileRoot());
65
66            project.executeTargets(ctx.getInstaller().getTargets(ctx));
67            project.fireBuildFinished(null);
68            ctx.setInstallSucceded(true);
69            ctx.log("Ant finished");
70        }
71        catch (Throwable e) {
72            throw new InstallException("Error running the install, " + e.getMessage(), e);
73        }
74        finally {
75            ctx.getRunner().antFinished();
76        }
77    }
78
79
80    public static synchronized String getAntVersion() throws BuildException {
81         if (antVersion == null) {
82             try {
83                 Properties props = new Properties();
84                 InputStream in =
85                     AntProjectFilter.class.getResourceAsStream("/org/apache/tools/ant/version.txt");
86                 props.load(in);
87                 in.close();
88
89                 StringBuffer msg = new StringBuffer();
90                 msg.append("Apache Ant version ");
91                 msg.append(props.getProperty("VERSION"));
92                 msg.append(" compiled on ");
93                 msg.append(props.getProperty("DATE"));
94                 antVersion = msg.toString();
95             } catch (IOException ioe) {
96                 throw new BuildException("Could not load the version information:"
97                                          + ioe.getMessage());
98             } catch (NullPointerException npe) {
99                 throw new BuildException("Could not load the version information.");
00             }
01         }
02         return antVersion;
03     }
04    
05    /**
06     * Append extra Ant jars to the classpath the original classpath 
07     * is not removed incase the installer is launched from a script
08     *
09     */
10    private static void appendClassPath(){
11        try {
12            // now update the class.path property
13            StringBuffer baseClassPath
14                = new StringBuffer(System.getProperty("java.class.path"));
15            if (baseClassPath.charAt(baseClassPath.length() - 1)
16                == File.pathSeparatorChar) {
17                baseClassPath.setLength(baseClassPath.length() - 1);
18            }
19            URL[] jars = getLibPaths();
20            for (int i = 0; i < jars.length; ++i) {
21                baseClassPath.append(File.pathSeparatorChar);
22                baseClassPath.append(Locator.fromURI(jars[i].toString()));
23            }
24
25            System.setProperty("java.class.path", baseClassPath.toString());
26
27            URLClassLoader loader = new URLClassLoader(jars);
28            Thread.currentThread().setContextClassLoader(loader);
29        }
30        catch (MalformedURLException e) {
31            // swallow exception, normally all resources are already loaded
32            System.err.println("Invalid Jar path");
33        }
34    }
35    
36    
37    /**
38     * Ant home is not a requirement but can exist prior to loading
39     * the default Ant mechanism of using the current Jars parent
40     * is consipicuously absent, do not rely on ANT_HOME out side of a 
41     * controlled environment (e.g. a normal install)
42     */
43    private static void setAntHome(InstallerContext ctx){
44        String antHomeProperty = System.getProperty(ANTHOME_PROPERTY);
45        if(antHomeProperty==null){
46            System.setProperty(ANTHOME_PROPERTY, ctx.getFileRoot().getAbsolutePath());
47        }
48    }
49    
50    /**
51     * To maintain compatability with previous verisons currently the only
52     * Ant command line argument supported is the -lib parameter with the value
53     * "antlib"
54     * @TODO this should probably be removed
55     * @throws MalformedURLException
56     */
57    private static URL[] getLibPaths() throws MalformedURLException{
58        
59        // add all Jars from the ./antlib directory at the time of the build
60        // this is NOT based on ANT_HOME
61        URL[] libJars = Locator.getLocationURLs(new File("antlib"));
62
63        // add all the Jars from ~/.ant/lib
64        // this is probably irrelevant for a normal install
65        URL[] userJars = Locator.getLocationURLs(new File(USER_LIBDIR));
66
67        // Now try and find JAVA_HOME
68        File toolsJar = Locator.getToolsJar();
69
70        int jarsLength = libJars.length + userJars.length + (toolsJar!=null?1:0);
71        URL[] allJars = new URL[jarsLength];
72        int i = 0;
73        if(toolsJar != null){
74            allJars[i++] = toolsJar.toURL();
75        }
76        if(libJars.length != 0){
77            System.arraycopy(libJars, 0, allJars, i, libJars.length);
78            i += libJars.length;
79        }
80        if(userJars.length != 0){
81            System.arraycopy(userJars, 0, allJars, i, userJars.length);
82            //i+=userJars.length;
83            //assert(allJars.length=i-1);
84        }
85        return libJars;
86    }
87    
88
89}
90