001: /*
002: * Copyright 2005 Paul Hinds
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, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: *
016: */
017: package org.tp23.antinstaller.taskdefs;
018:
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023:
024: import javax.xml.parsers.SAXParser;
025: import javax.xml.parsers.SAXParserFactory;
026:
027: import org.apache.tools.ant.BuildException;
028: import org.apache.tools.ant.Project;
029: import org.apache.tools.ant.taskdefs.Jar;
030: import org.apache.tools.ant.taskdefs.Manifest;
031: import org.apache.tools.ant.taskdefs.ManifestException;
032: import org.apache.tools.ant.types.FileSet;
033: import org.apache.tools.ant.types.ZipFileSet;
034: import org.apache.tools.zip.ZipOutputStream;
035: import org.tp23.antinstaller.renderer.swing.plaf.LookAndFeelFactory;
036: import org.tp23.antinstaller.runtime.ConfigurationLoader;
037: import org.xml.sax.InputSource;
038: import org.xml.sax.SAXException;
039: import org.xml.sax.SAXParseException;
040: import org.xml.sax.helpers.DefaultHandler;
041:
042: /**
043: * Creates a Installer archive.
044: *
045: *
046: * @ant.task category="packaging"
047: */
048: public class Installer extends Jar {
049:
050: public static final String NON_EXTRACTOR = "NonExtractor";
051: public static final String SELF_EXTRACTOR = "SelfExtractor";
052:
053: /** The extract type to use NonExtractor or SelfExtractor */
054: private String extractType;
055:
056: /** The AntInstall config file */
057: private File installConfig;
058:
059: /** The Ant build.xml to be used*/
060: private File buildFile;
061:
062: /** The extract image to be used*/
063: private File extractImage;
064:
065: /** The location of ant-installer.jar and ant-installer-ext.jar and possibly jgoodies-edited-1_2_2.jar */
066: private File antInstallLib;
067:
068: /** The location of xercesImpl.jar and xml-apis.jar
069: * This is not requried Xerces is optional and will increase the download size */
070: private File xercesLib;
071:
072: /** The location of ant.jar and ant-launcher.jar */
073: private File antLib;
074:
075: /** The AntInstaller Look And Feel to be used */
076: private String laf;
077:
078: /** The icon set to use */
079: private String icons;
080:
081: /** Stop the build if there is a known error in the config */
082: protected boolean failOnError = false;
083:
084: /** perform the validation */
085: protected boolean validateConfig = false;
086:
087: private boolean buildFileSet = false;
088: private boolean configFileSet = false;
089: /** Indicates that the build.xml and antinstall-config.xml have been added to the fileset already */
090: private boolean startCheckDuplicates = false;
091:
092: /** constructor */
093: public Installer() {
094: super ();
095: archiveType = "jar";
096: emptyBehavior = "create";
097: setEncoding("UTF8");
098: }
099:
100: protected void cleanUp() {
101: super .cleanUp();
102: }
103:
104: public void reset() {
105: super .reset();
106: extractType = null;
107: installConfig = null;
108: buildFile = null;
109: }
110:
111: /**
112: * @param installConfig The installConfig to set.
113: */
114: public void setInstallConfig(File installConfig) {
115: this .installConfig = installConfig;
116: if (!installConfig.exists()) {
117: throw new BuildException("AntInstall config: "
118: + installConfig + " does not exist.");
119: }
120: // Create a ZipFileSet for this file, and pass it up.
121: ZipFileSet fs = new ZipFileSet();
122: fs.setFile(installConfig);
123: fs.setFullpath("antinstall-config.xml");
124: super .addFileset(fs);
125: if (this .buildFile != null) {
126: startCheckDuplicates = true;
127: }
128: }
129:
130: /**
131: * @param buildFile The buildFile to set.
132: */
133: public void setBuildFile(File buildFile) {
134: this .buildFile = buildFile;
135: if (!buildFile.exists()) {
136: throw new BuildException("AntInstall build file: "
137: + buildFile + " does not exist.");
138: }
139: ZipFileSet fs = new ZipFileSet();
140: fs.setFile(buildFile);
141: fs.setFullpath("build.xml");
142: super .addFileset(fs);
143: if (this .installConfig != null) {
144: startCheckDuplicates = true;
145: }
146: }
147:
148: /**
149: * A side effect of this attribute is that the duplicate attribute is set to "preserve"
150: * @param extractImage The extract image to use in the SelfExtractor progress.
151: */
152: public void setExtractImage(File extractImage) {
153: this .extractImage = extractImage;
154: if (!extractImage.exists()) {
155: throw new BuildException("AntInstall extract image: "
156: + extractImage + " does not exist.");
157: } else {
158: log("adding extract image "
159: + extractImage.getAbsolutePath());
160: }
161: }
162:
163: /**
164: * @param icons The ocons pack to use for buttons.
165: */
166: public void setIcons(String icons) {
167: this .icons = icons;
168: // Create a ZipFileSet for this file, and pass it up.
169: File iconJar = new File(antInstallLib, "ai-icons-" + icons
170: + ".jar");
171: if (!iconJar.exists()) {
172: throw new BuildException("Missing icons: " + iconJar
173: + " does not exist.");
174: }
175: }
176:
177: /**
178: * @param extractType The extractType to set.
179: */
180: public void setExtractType(String extractType) {
181: this .extractType = extractType;
182: }
183:
184: public void setFailOnError(boolean fail) {
185: failOnError = fail;
186: }
187:
188: public void setValidateConfig(boolean validate) {
189: validateConfig = validate;
190: }
191:
192: /**
193: * The location of ant-installer.jar and possibly jgoodies-edited-1_2_2.jar
194: * @param antInstallLib The antInstallLib to set.
195: */
196: public void setAntInstallLib(File antInstallLib) {
197: this .antInstallLib = antInstallLib;
198: }
199:
200: /**
201: * The location of ant.jar and ant-launcher.jar
202: * @param antLib The antLib to set.
203: */
204: public void setAntLib(File antLib) {
205: this .antLib = antLib;
206: }
207:
208: /**
209: * @param xercesLib The xercesLib to set.
210: */
211: public void setXercesLib(File xercesLib) {
212: this .xercesLib = xercesLib;
213: }
214:
215: private void doFilesets() {
216: if (this .antLib != null) {
217: FileSet set = new FileSet();
218: set.setFile(new File(antLib, "ant.jar"));
219: set.setFile(new File(antLib, "ant-launcher.jar"));
220: addZipGroupFileset(set);
221: }
222: if (this .xercesLib != null) {
223: FileSet set = new FileSet();
224: set.setFile(new File(xercesLib, "xercesImpl.jar"));
225: set.setFile(new File(xercesLib, "xml-apis.jar"));
226: addZipGroupFileset(set);
227: }
228: if (this .antInstallLib != null) {
229: FileSet set = new FileSet();
230: if (extractImage != null) {
231: ZipFileSet fs = new ZipFileSet();
232: fs.setFile(extractImage);
233: fs.setFullpath("resources/extract-image.png");
234: super .addFileset(fs);
235: // nasty side effect to prevent duplicates so the correct image shows
236: Duplicate df = new Duplicate();
237: df.setValue("preserve");
238: super .setDuplicate(df);
239: }
240: set.setFile(new File(antInstallLib, "ant-installer.jar"));
241: addZipGroupFileset(set);
242: }
243: if (this .icons != null) {
244: FileSet set = new FileSet();
245: File iconJar = new File(antInstallLib, "ai-icons-" + icons
246: + ".jar");
247: set.setFile(iconJar);
248: addZipGroupFileset(set);
249: }
250: }
251:
252: /**
253: * Overrides the ZIP execute() method which creates filesets
254: */
255: public void execute() {
256: log(".-------------------------------.");
257: log("|-(o--~AntInstaller.sf.net~--o)-|");
258: log("`-----------------by-Paul-Hinds-ยด");
259: doFilesets();
260: if (validateConfig) {
261: validateConfig();
262: } else if (extractType.equals(SELF_EXTRACTOR)) {
263: // this reads the config just
264: // to extract the lookAndFeel attribute for the manifest at the moment
265: readConfig();
266: }
267: if (LookAndFeelFactory.isDefault(getLaf())) {
268: FileSet set = new FileSet();
269: set.setFile(new File(antInstallLib,
270: "jgoodies-edited-1_2_2.jar"));
271: addZipGroupFileset(set);
272: }
273:
274: super .execute();
275: }
276:
277: /**
278: * override of parent; validates configuration
279: * before initializing the output stream. The Manifest is set in the Jar superclass's
280: * method so Manifest modifications must be performed in this method
281: */
282: protected void initZipOutputStream(ZipOutputStream zOut)
283: throws IOException, BuildException {
284: // If no buildFile file is specified, it's an error.
285: if (buildFile == null && !isInUpdateMode()) {
286: throw new BuildException("buildFile attribute is required",
287: getLocation());
288: }
289: // If no installConfig file is specified, it's an error.
290: if (installConfig == null && !isInUpdateMode()) {
291: throw new BuildException(
292: "installConfig attribute is required",
293: getLocation());
294: }
295: try {
296: addConfiguredManifest(this .getManifest());
297: } catch (ManifestException me) {
298: throw new BuildException("Cant add AntInstaller Manifest",
299: me, getLocation());
300: }
301: super .initZipOutputStream(zOut);
302: }
303:
304: protected void zipFile(InputStream is, ZipOutputStream zOut,
305: String vPath, long lastModified, File fromArchive, int mode)
306: throws IOException {
307:
308: if (vPath.equalsIgnoreCase("antinstall-config.xml")) {
309: if (buildFileSet) {
310: log("Two antinstall-config.xml files in jar",
311: Project.MSG_WARN);
312: }
313: buildFileSet = true;
314: }
315: if (vPath.equalsIgnoreCase("build.xml")) {
316: if (configFileSet) {
317: log("Two build.xml files in jar", Project.MSG_WARN);
318: }
319: configFileSet = true;
320: }
321:
322: super .zipFile(is, zOut, vPath, lastModified, fromArchive, mode);
323: }
324:
325: /**
326: * This method is only valid after readConfig() or validateConfig() have been run
327: * @return Returns the Look And Feel class.
328: */
329: private String getLaf() {
330: if (laf == null) {
331: return LookAndFeelFactory.DEFAULT_LAF;
332: }
333: return laf;
334: }
335:
336: private Manifest getManifest() throws ManifestException {
337: if (extractType.equalsIgnoreCase(NON_EXTRACTOR)) {
338: return getNonExtractorManifest();
339: } else if (extractType.equalsIgnoreCase(SELF_EXTRACTOR)) {
340: return getSelfExtractorManifest();
341: } else {
342: throw new BuildException("Invalid extractType: "
343: + extractType);
344: }
345: }
346:
347: private Manifest getNonExtractorManifest() throws ManifestException {
348: return getCustomManifest("org.tp23.antinstaller.selfextract.NonExtractor");
349: }
350:
351: private Manifest getSelfExtractorManifest()
352: throws ManifestException {
353: return getCustomManifest("org.tp23.antinstaller.selfextract.SelfExtractor");
354: }
355:
356: private Manifest getCustomManifest(String mainClass)
357: throws ManifestException {
358: log("Creating MANIFEST.mf");
359: Manifest newManifest = new Manifest();
360: Manifest.Section mainSection = newManifest.getMainSection();
361: Manifest.Attribute attmc = new Manifest.Attribute();
362: attmc.setName("Main-Class");
363: attmc.setValue(mainClass);
364: mainSection.addAttributeAndCheck(attmc);
365: Manifest.Attribute attlaf = new Manifest.Attribute();
366: attlaf.setName("Look-And-Feel");
367: attlaf.setValue(getLaf());
368: mainSection.addAttributeAndCheck(attlaf);
369: return newManifest;
370:
371: }
372:
373: protected void validateConfig() {
374:
375: int ret = 1;
376: try {
377: log("validating config...");
378: ConfigurationLoader configurationLoader = new ConfigurationLoader();
379: configurationLoader.readConfig(installConfig
380: .getParentFile(), installConfig.getName());
381: ret = configurationLoader.validate();
382: laf = configurationLoader.getInstaller().getLookAndFeel();
383: if (ret != 0) {
384: err();
385: }
386: } catch (Exception ex) {
387: ex.printStackTrace();
388: err();
389: }
390:
391: try {
392: log("parsing included build.xml...");
393: InputSource xmlInp = new InputSource(new FileInputStream(
394: buildFile));
395: SAXParserFactory parserFactory = SAXParserFactory
396: .newInstance();
397: SAXParser parser = parserFactory.newSAXParser();
398: parser.parse(xmlInp, new DefaultHandler() {
399: public void error(SAXParseException e)
400: throws SAXException {
401: throw e;
402: }
403:
404: public void fatalError(SAXParseException e)
405: throws SAXException {
406: throw e;
407: }
408: });
409: log("build.xml is well formed");
410: } catch (Exception ex) {
411: ex.printStackTrace();
412: errNestedBuildXml();
413: }
414: }
415:
416: protected void readConfig() {
417:
418: try {
419: log("reading config...");
420: ConfigurationLoader configurationLoader = new ConfigurationLoader();
421: configurationLoader.readConfig(installConfig
422: .getParentFile(), installConfig.getName());
423: laf = configurationLoader.getInstaller().getLookAndFeel();
424: } catch (Exception ex) {
425: ex.printStackTrace();
426: err();
427: }
428: }
429:
430: /**
431: * error found on validation of the antinstall config
432: */
433: private void err() {
434: String errorMsg = "Error in config file:"
435: + installConfig.getAbsolutePath();
436: if (failOnError) {
437: throw new BuildException(errorMsg);
438: } else {
439: log(errorMsg, Project.MSG_ERR);
440: }
441: }
442:
443: /**
444: * error found in the build.xml used by ant installer (not the one
445: * running this task)
446: */
447: private void errNestedBuildXml() {
448: String errorMsg = "Error in included build file:"
449: + buildFile.getAbsolutePath();
450: if (failOnError) {
451: throw new BuildException(errorMsg);
452: } else {
453: log(errorMsg, Project.MSG_ERR);
454: }
455: }
456: }
|