001/*
002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved.
003 * 
004 * http://www.izforge.com/izpack/
005 * http://developer.berlios.de/projects/izpack/
006 * 
007 * Copyright 2003 Jonathan Halliday
008 * 
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *     http://www.apache.org/licenses/LICENSE-2.0
014 *     
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 */
021
022package com.izforge.izpack.installer;
023
024import java.io.BufferedWriter;
025import java.io.File;
026import java.io.FileInputStream;
027import java.io.InputStream;
028import java.io.ObjectOutputStream;
029import java.io.OutputStreamWriter;
030import java.util.Iterator;
031import java.util.List;
032import java.util.TreeMap;
033import java.util.Vector;
034import java.util.zip.ZipEntry;
035import java.util.zip.ZipOutputStream;
036
037import net.n3.nanoxml.NonValidator;
038import net.n3.nanoxml.StdXMLBuilder;
039import net.n3.nanoxml.StdXMLParser;
040import net.n3.nanoxml.StdXMLReader;
041import net.n3.nanoxml.XMLElement;
042
043import com.izforge.izpack.ExecutableFile;
044import com.izforge.izpack.LocaleDatabase;
045import com.izforge.izpack.Panel;
046import com.izforge.izpack.util.Housekeeper;
047import com.izforge.izpack.util.OsConstraint;
048
049/**
050 * Runs the install process in text only (no GUI) mode.
051 * 
052 * @author Jonathan Halliday <jonathan.halliday@arjuna.com>
053 * @author Julien Ponge <julien@izforge.com>
054 * @author Johannes Lehtinen <johannes.lehtinen@iki.fi>
055 */
056public class AutomatedInstaller extends InstallerBase
057{
058
059    // there are panels which can be instantiated multiple times
060    // we therefore need to select the right XML section for each
061    // instance
062    private TreeMap panelInstanceCount;
063
064    /** The automated installation data. */
065    private AutomatedInstallData idata = new AutomatedInstallData();
066
067    /**
068     * Constructing an instance triggers the install.
069     * 
070     * @param inputFilename Name of the file containing the installation data.
071     * @exception Exception Description of the Exception
072     */
073    public AutomatedInstaller(String inputFilename) throws Exception
074    {
075        super();
076
077        File input = new File(inputFilename);
078
079        // Loads the installation data
080        loadInstallData(idata);
081
082        // Loads the xml data
083        idata.xmlData = getXMLData(input);
084
085        // Loads the langpack
086        idata.localeISO3 = idata.xmlData.getAttribute("langpack", "eng");
087        InputStream in = getClass().getResourceAsStream("/langpacks/" + idata.localeISO3 + ".xml");
088        idata.langpack = new LocaleDatabase(in);
089        idata.setVariable(ScriptParser.ISO3_LANG, idata.localeISO3);
090
091        // create the resource manager singleton
092        ResourceManager.create(idata);
093
094        // Load custom langpack if exist.
095        addCustomLangpack(idata);
096
097        this.panelInstanceCount = new TreeMap();
098
099        doInstall(idata);
100    }
101
102    /**
103     * Writes the uninstalldata.
104     * 
105     * Unfortunately, Java doesn't allow multiple inheritance, so <code>AutomatedInstaller</code>
106     * and <code>InstallerFrame</code> can't share this code ... :-/
107     * 
108     * TODO: We should try to fix this in the future.
109     */
110    private void writeUninstallData()
111    {
112        try
113        {
114            // We get the data
115            UninstallData udata = UninstallData.getInstance();
116            List files = udata.getFilesList();
117            ZipOutputStream outJar = idata.uninstallOutJar;
118
119            if (outJar == null) return;
120
121            System.out.println("[ Writing the uninstaller data ... ]");
122
123            // We write the files log
124            outJar.putNextEntry(new ZipEntry("install.log"));
125            BufferedWriter logWriter = new BufferedWriter(new OutputStreamWriter(outJar));
126            logWriter.write(idata.getInstallPath());
127            logWriter.newLine();
128            Iterator iter = files.iterator();
129            while (iter.hasNext())
130            {
131                logWriter.write((String) iter.next());
132                if (iter.hasNext()) logWriter.newLine();
133            }
134            logWriter.flush();
135            outJar.closeEntry();
136
137            // We write the uninstaller jar file log
138            outJar.putNextEntry(new ZipEntry("jarlocation.log"));
139            logWriter = new BufferedWriter(new OutputStreamWriter(outJar));
140            logWriter.write(udata.getUninstallerJarFilename());
141            logWriter.newLine();
142            logWriter.write(udata.getUninstallerPath());
143            logWriter.flush();
144            outJar.closeEntry();
145
146            // Write out executables to execute on uninstall
147            outJar.putNextEntry(new ZipEntry("executables"));
148            ObjectOutputStream execStream = new ObjectOutputStream(outJar);
149            iter = udata.getExecutablesList().iterator();
150            execStream.writeInt(udata.getExecutablesList().size());
151            while (iter.hasNext())
152            {
153                ExecutableFile file = (ExecutableFile) iter.next();
154                execStream.writeObject(file);
155            }
156            execStream.flush();
157            outJar.closeEntry();
158
159            // Cleanup
160            outJar.flush();
161            outJar.close();
162        }
163        catch (Exception err)
164        {
165            err.printStackTrace();
166        }
167    }
168
169    /**
170     * Runs the automated installation logic for each panel in turn.
171     * 
172     * @param installdata the installation data.
173     * @throws Exception
174     */
175    private void doInstall(AutomatedInstallData installdata) throws Exception
176    {
177        // TODO: i18n
178        System.out.println("[ Starting automated installation ]");
179
180        // walk the panels in order
181        Iterator panelsIterator = installdata.panelsOrder.iterator();
182        while (panelsIterator.hasNext())
183        {
184            Panel p = (Panel) panelsIterator.next();
185            String praefix = "com.izforge.izpack.panels.";
186            if (p.className.compareTo(".") > -1)
187            // Full qualified class name
188                praefix = "";
189            if (!OsConstraint.oneMatchesCurrentSystem(p.osConstraints)) continue;
190
191            String panelClassName = p.className;
192            String automationHelperClassName = praefix + panelClassName + "AutomationHelper";
193            Class automationHelperClass = null;
194            // determine if the panel supports automated install
195            try
196            {
197                automationHelperClass = Class.forName(automationHelperClassName);
198            }
199            catch (ClassNotFoundException e)
200            {
201                // this is OK - not all panels have/need automation support.
202                continue;
203            }
204
205            // instantiate the automation logic for the panel
206            PanelAutomation automationHelperInstance = null;
207            if (automationHelperClass != null)
208            {
209                try
210                {
211                    automationHelperInstance = (PanelAutomation) automationHelperClass
212                            .newInstance();
213                }
214                catch (Exception e)
215                {
216                    System.err.println("ERROR: no default constructor for "
217                            + automationHelperClassName + ", skipping...");
218                    continue;
219                }
220            }
221
222            // We get the panels root xml markup
223            Vector panelRoots = installdata.xmlData.getChildrenNamed(panelClassName);
224            int panelRootNo = 0;
225
226            if (this.panelInstanceCount.containsKey(panelClassName))
227            {
228                // get number of panel instance to process
229                panelRootNo = ((Integer) this.panelInstanceCount.get(panelClassName)).intValue();
230            }
231
232            XMLElement panelRoot = (XMLElement) panelRoots.elementAt(panelRootNo);
233
234            this.panelInstanceCount.put(panelClassName, new Integer(panelRootNo + 1));
235
236            // execute the installation logic for the current panel, if it has
237            // any:
238            if (automationHelperInstance != null)
239            {
240                try
241                {
242                    automationHelperInstance.runAutomated(installdata, panelRoot);
243                }
244                catch (Exception e)
245                {
246                    System.err.println("ERROR: automated installation failed for panel "
247                            + panelClassName);
248                    e.printStackTrace();
249                    continue;
250                }
251
252            }
253
254        }
255
256        // this does nothing if the uninstaller was not included
257        writeUninstallData();
258
259        System.out.println("[ Automated installation done ]");
260
261        // Bye
262        Housekeeper.getInstance().shutDown(0);
263    }
264
265    /**
266     * Loads the xml data for the automated mode.
267     * 
268     * @param input The file containing the installation data.
269     * @exception Exception thrown if there are problems reading the file.
270     */
271    public XMLElement getXMLData(File input) throws Exception
272    {
273        FileInputStream in = new FileInputStream(input);
274
275        // Initialises the parser
276        StdXMLParser parser = new StdXMLParser();
277        parser.setBuilder(new StdXMLBuilder());
278        parser.setReader(new StdXMLReader(in));
279        parser.setValidator(new NonValidator());
280
281        XMLElement rtn = (XMLElement) parser.parse();
282        in.close();
283
284        return rtn;
285    }
286}