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 *
16 */
17package org.tp23.antinstaller.taskdefs;
18
19import java.io.File;
20import java.io.FileInputStream;
21import java.io.IOException;
22import java.io.InputStream;
23
24import javax.xml.parsers.SAXParser;
25import javax.xml.parsers.SAXParserFactory;
26
27import org.apache.tools.ant.BuildException;
28import org.apache.tools.ant.Project;
29import org.apache.tools.ant.taskdefs.Jar;
30import org.apache.tools.ant.taskdefs.Manifest;
31import org.apache.tools.ant.taskdefs.ManifestException;
32import org.apache.tools.ant.types.FileSet;
33import org.apache.tools.ant.types.ZipFileSet;
34import org.apache.tools.zip.ZipOutputStream;
35import org.tp23.antinstaller.renderer.swing.plaf.LookAndFeelFactory;
36import org.tp23.antinstaller.runtime.ConfigurationLoader;
37import org.xml.sax.InputSource;
38import org.xml.sax.SAXException;
39import org.xml.sax.SAXParseException;
40import org.xml.sax.helpers.DefaultHandler;
41
42/**
43 * Creates a Installer archive.
44 * 
45 *
46 * @ant.task category="packaging"
47 */
48public class Installer extends Jar {
49
50    public static final String NON_EXTRACTOR = "NonExtractor";
51    public static final String SELF_EXTRACTOR = "SelfExtractor";
52    
53    /** The extract type to use NonExtractor or SelfExtractor */
54    private String extractType;
55
56    /** The AntInstall config file */
57    private File installConfig;
58
59    /** The Ant build.xml to be used*/
60    private File buildFile;
61
62    /** The location of ant-installer.jar and ant-installer-ext.jar and possibly jgoodies-edited-1_2_2.jar */
63    private File antInstallLib;
64
65    /** The location of xercesImpl.jar and xml-apis.jar 
66     * This is not requried Xerces is optional and will increase the download size */
67    private File xercesLib;
68
69    /** The location of ant.jar and ant-launcher.jar */
70    private File antLib;
71
72    /** The AntInstaller Look And Feel to be used */
73    private String laf;
74    
75    /** The icon set to use */
76    private String icons;
77
78    /** Stop the build if there is a known error in the config */
79    protected boolean failOnError = false;
80    
81    /** perform the validation */
82    protected boolean validateConfig = false;
83    
84    private boolean buildFileSet = false;
85    private boolean configFileSet = false;
86    /** Indicates that the build.xml and antinstall-config.xml have been added to the fileset already */
87    private boolean startCheckDuplicates = false; 
88
89    /** constructor */
90    public Installer() {
91        super();
92        archiveType = "jar";
93        emptyBehavior = "create";
94        setEncoding("UTF8");
95    }
96 
97    protected void cleanUp() {
98        super.cleanUp();
99    }
00
01    public void reset() {
02        super.reset();
03        extractType = null;
04        installConfig = null;
05        buildFile = null;
06    }
07
08    /**
09     * @param installConfig The installConfig to set.
10     */
11    public void setInstallConfig(File installConfig) {
12        this.installConfig = installConfig;
13        if (!installConfig.exists()) {
14            throw new BuildException("AntInstall config: "
15                                     + installConfig
16                                     + " does not exist.");
17        }
18        // Create a ZipFileSet for this file, and pass it up.
19        ZipFileSet fs = new ZipFileSet();
20        fs.setFile(installConfig);
21        fs.setFullpath("antinstall-config.xml");
22        super.addFileset(fs);
23        if(this.buildFile != null) {
24            startCheckDuplicates = true;
25        }
26    }
27    /**
28     * @param buildFile The buildFile to set.
29     */
30    public void setBuildFile(File buildFile) {
31        this.buildFile = buildFile;
32        if (!buildFile.exists()) {
33            throw new BuildException("AntInstall build file: "
34                                     + buildFile
35                                     + " does not exist.");
36        }
37        // Create a ZipFileSet for this file, and pass it up.
38        ZipFileSet fs = new ZipFileSet();
39        fs.setFile(buildFile);
40        fs.setFullpath("build.xml");
41        super.addFileset(fs);
42        if(this.installConfig != null) {
43            startCheckDuplicates = true;
44        }
45    }
46    /**
47     * @param icons The ocons pack to use for buttons.
48     */
49    public void setIcons(String icons) {
50        this.icons = icons;
51        // Create a ZipFileSet for this file, and pass it up.
52        File iconJar = new File(antInstallLib, "ai-icons-" + icons + ".jar");
53        if (!iconJar.exists()) {
54            throw new BuildException("Missing icons: "
55                                     + iconJar
56                                     + " does not exist.");
57        }
58        FileSet set = new FileSet();
59        set.setFile(iconJar);
60        addZipGroupFileset(set);
61
62    }
63    /**
64     * @param extractType The extractType to set.
65     */
66    public void setExtractType(String extractType) {
67        this.extractType = extractType;
68    }
69    
70    public void setFailOnError(boolean fail) {
71        failOnError = fail;
72    }
73    
74    public void setValidateConfig(boolean validate) {
75        validateConfig = validate;
76    }
77
78    /**
79     * The location of ant-installer.jar and possibly jgoodies-edited-1_2_2.jar
80     * @param antInstallLib The antInstallLib to set.
81     */
82    public void setAntInstallLib(File antInstallLib) {
83        this.antInstallLib = antInstallLib;
84        FileSet set = new FileSet();
85        set.setFile(new File(antInstallLib, "ant-installer.jar"));
86        addZipGroupFileset(set);
87    }
88    /**
89     * The location of ant.jar and ant-launcher.jar
90     * @param antLib The antLib to set.
91     */
92    public void setAntLib(File antLib) {
93        this.antLib = antLib;
94        FileSet set = new FileSet();
95        set.setFile(new File(antLib, "ant.jar"));
96        set.setFile(new File(antLib, "ant-launcher.jar"));
97        addZipGroupFileset(set);
98    }
99    /**
00     * @param xercesLib The xercesLib to set.
01     */
02    public void setXercesLib(File xercesLib) {
03        this.xercesLib = xercesLib;
04        FileSet set = new FileSet();
05        set.setFile(new File(xercesLib, "xercesImpl.jar"));
06        set.setFile(new File(xercesLib, "xml-apis.jar"));
07        addZipGroupFileset(set);
08    }
09
10    /**
11     * Overrides the ZIP execute() method which creates filesets
12     */
13    public void execute(){
14        log(".-------------------------------.");
15        log("|-(o--~AntInstaller.sf.net~--o)-|");
16        log("`-----------------by-Paul-Hinds-ยด");
17        
18        if(validateConfig) {
19            validateConfig();
20        } else if(extractType.equals(SELF_EXTRACTOR)){
21            // this reads the config just 
22            // to extract the lookAndFeel attribute for the manifest at the moment
23            readConfig();
24        }
25        if( LookAndFeelFactory.isDefault(getLaf()) ){
26            FileSet set = new FileSet();
27            set.setFile(new File(antInstallLib, "jgoodies-edited-1_2_2.jar"));
28            addZipGroupFileset(set);
29        }
30
31        super.execute();
32    }
33
34    /**
35     * override of  parent; validates configuration
36     * before initializing the output stream.  The Manifest is set in the Jar superclass's
37     * method so Manifest modifications must be performed in this method
38     */
39    protected void initZipOutputStream(ZipOutputStream zOut)
40        throws IOException, BuildException {
41        // If no buildFile file is specified, it's an error.
42        if (buildFile == null && !isInUpdateMode()) {
43            throw new BuildException("buildFile attribute is required", getLocation());
44        }
45        // If no installConfig file is specified, it's an error.
46        if (installConfig == null && !isInUpdateMode()) {
47            throw new BuildException("installConfig attribute is required", getLocation());
48        }
49        try{ 
50            addConfiguredManifest(this.getManifest());
51        }
52        catch(ManifestException me){
53            throw new BuildException("Cant add AntInstaller Manifest", me, getLocation());
54        }        
55        super.initZipOutputStream(zOut);
56    }
57    
58    protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath,
59            long lastModified, File fromArchive, int mode)
60            throws IOException {
61        
62        if(vPath.equalsIgnoreCase("antinstall-config.xml")) {
63            if(buildFileSet) {
64                log("Two antinstall-config.xml files in jar", Project.MSG_WARN);
65            }
66            buildFileSet = true;
67        }
68        if(vPath.equalsIgnoreCase("build.xml")) {
69            if(configFileSet) {
70                log("Two build.xml files in jar", Project.MSG_WARN);
71            }
72            configFileSet = true;
73        }
74
75        super.zipFile(is, zOut, vPath, lastModified, fromArchive, mode);
76    }
77
78    /**
79     * This method is only valid after readConfig() or validateConfig() have been run
80     * @return Returns the Look And Feel class.
81     */
82    private String getLaf() {
83        if(laf == null){
84            return LookAndFeelFactory.DEFAULT_LAF;
85        }
86        return laf;
87    }
88
89    private Manifest getManifest() throws ManifestException{
90        if(extractType.equalsIgnoreCase(NON_EXTRACTOR)){
91            return getNonExtractorManifest();
92        }
93        else if(extractType.equalsIgnoreCase(SELF_EXTRACTOR)){
94            return getSelfExtractorManifest();
95        }
96        else {
97            throw new BuildException("Invalid extractType: " + extractType);
98        }
99    }
00    
01    private Manifest getNonExtractorManifest() throws ManifestException{
02        return getCustomManifest("org.tp23.antinstaller.selfextract.NonExtractor");
03    }
04    private Manifest getSelfExtractorManifest() throws ManifestException{
05        return getCustomManifest("org.tp23.antinstaller.selfextract.SelfExtractor");
06    }
07    private Manifest getCustomManifest(String mainClass) throws ManifestException{
08        log("Creating MANIFEST.mf");
09        Manifest newManifest = new Manifest();
10        Manifest.Section mainSection = newManifest.getMainSection();
11        Manifest.Attribute attmc = new Manifest.Attribute();
12        attmc.setName("Main-Class");
13        attmc.setValue(mainClass);
14        mainSection.addAttributeAndCheck(attmc);
15        Manifest.Attribute attlaf = new Manifest.Attribute();
16        attlaf.setName("Look-And-Feel");
17        attlaf.setValue(getLaf());
18        mainSection.addAttributeAndCheck(attlaf);
19        return newManifest;
20        
21    }
22    
23    protected void validateConfig(){
24        
25        int ret = 1;
26        try {
27            log("validating config...");
28            ConfigurationLoader configurationLoader = new ConfigurationLoader();
29            configurationLoader.readConfig(installConfig.getParentFile(), installConfig.getName());
30            ret = configurationLoader.validate();
31            laf = configurationLoader.getInstaller().getLookAndFeel();
32            if(ret != 0){
33                err();
34            }
35        }
36        catch (Exception ex) {
37            ex.printStackTrace();
38            err();
39        }
40        
41        try{
42            log("parsing included build.xml...");
43            InputSource xmlInp = new InputSource(new FileInputStream(buildFile));
44            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
45            SAXParser parser = parserFactory.newSAXParser();
46            parser.parse(xmlInp, new DefaultHandler(){
47                public void error(SAXParseException e) throws SAXException{
48                    throw e;
49                }
50                public void fatalError(SAXParseException e) throws SAXException{
51                    throw e;
52                }
53            } );
54            log("build.xml is well formed");
55        }
56        catch (Exception ex) {
57            ex.printStackTrace();
58            errNestedBuildXml();
59        }
60    }
61
62    protected void readConfig(){
63        
64        try {
65            log("reading config...");
66            ConfigurationLoader configurationLoader = new ConfigurationLoader();
67            configurationLoader.readConfig(installConfig.getParentFile(), installConfig.getName());
68            laf = configurationLoader.getInstaller().getLookAndFeel();
69        }
70        catch (Exception ex) {
71            ex.printStackTrace();
72            err();
73        }
74    }
75    
76    /**
77     * error found on validation of the antinstall config
78     */
79    private void err(){
80        String errorMsg = "Error in config file:" + installConfig.getAbsolutePath();
81        if (failOnError) {
82            throw new BuildException(errorMsg);
83        } else {
84            log(errorMsg, Project.MSG_ERR);
85        }
86    }
87    /**
88     * error found in the build.xml used by ant installer (not the one
89     * running this task)
90     */
91    private void errNestedBuildXml(){
92        String errorMsg = "Error in included build file:" + buildFile.getAbsolutePath();
93        if (failOnError) {
94            throw new BuildException(errorMsg);
95        } else {
96            log(errorMsg, Project.MSG_ERR);
97        }
98    }
99}
00