0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU General
0007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
0008: * License("CDDL") (collectively, the "License"). You may not use this file except in
0009: * compliance with the License. You can obtain a copy of the License at
0010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
0011: * License for the specific language governing permissions and limitations under the
0012: * License. When distributing the software, include this License Header Notice in
0013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
0014: * designates this particular file as subject to the "Classpath" exception as
0015: * provided by Sun in the GPL Version 2 section of the License file that
0016: * accompanied this code. If applicable, add the following below the License Header,
0017: * with the fields enclosed by brackets [] replaced by your own identifying
0018: * information: "Portions Copyrighted [year] [name of copyright owner]"
0019: *
0020: * Contributor(s):
0021: *
0022: * The Original Software is NetBeans. The Initial Developer of the Original Software
0023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
0024: * Rights Reserved.
0025: *
0026: * If you wish your version of this file to be governed by only the CDDL or only the
0027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
0028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
0029: * you do not indicate a single choice of license, a recipient has the option to
0030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
0031: * to extend the choice of license to its licensees as provided above. However, if
0032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
0033: * the option applies only if the new code is made subject to such option by the
0034: * copyright holder.
0035: */
0036:
0037: package org.netbeans.installer;
0038:
0039: import com.apple.eawt.Application;
0040: import com.apple.eawt.ApplicationAdapter;
0041: import com.apple.eawt.ApplicationEvent;
0042: import java.io.File;
0043: import java.io.FileInputStream;
0044: import java.io.FileOutputStream;
0045: import java.io.IOException;
0046: import java.io.InputStream;
0047: import java.net.URL;
0048: import java.net.URLDecoder;
0049: import java.util.Arrays;
0050: import java.util.Enumeration;
0051: import java.util.Locale;
0052: import java.util.MissingResourceException;
0053: import java.util.Properties;
0054: import java.util.ResourceBundle;
0055: import java.util.TreeSet;
0056: import java.util.jar.Attributes;
0057: import java.util.jar.JarEntry;
0058: import java.util.jar.JarOutputStream;
0059: import java.util.jar.Manifest;
0060: import org.netbeans.installer.downloader.DownloadManager;
0061: import org.netbeans.installer.product.Registry;
0062: import org.netbeans.installer.utils.DateUtils;
0063: import org.netbeans.installer.utils.FileUtils;
0064: import org.netbeans.installer.utils.StreamUtils;
0065: import org.netbeans.installer.utils.SystemUtils;
0066: import org.netbeans.installer.utils.ErrorManager;
0067: import org.netbeans.installer.utils.LogManager;
0068: import org.netbeans.installer.utils.ResourceUtils;
0069: import org.netbeans.installer.utils.StringUtils;
0070: import org.netbeans.installer.utils.UiUtils;
0071: import org.netbeans.installer.utils.XMLUtils;
0072: import org.netbeans.installer.utils.exceptions.XMLException;
0073: import org.netbeans.installer.utils.helper.EngineResources;
0074: import org.netbeans.installer.utils.helper.ErrorLevel;
0075: import org.netbeans.installer.utils.helper.ExecutionMode;
0076: import org.netbeans.installer.utils.helper.FinishHandler;
0077: import org.netbeans.installer.utils.helper.UiMode;
0078: import org.netbeans.installer.utils.progress.Progress;
0079: import org.netbeans.installer.wizard.Wizard;
0080: import org.netbeans.installer.wizard.components.WizardComponent;
0081:
0082: /**
0083: * The main class of the NBI engine. It represents the installer and provides
0084: * methods to start the installation/maintenance process as well as to
0085: * finish/cancel/break the installation.
0086: *
0087: * @author Kirill Sorokin
0088: */
0089: public class Installer implements FinishHandler {
0090: /////////////////////////////////////////////////////////////////////////////////
0091: // Main
0092: /**
0093: * The main method. It creates an instance of {@link Installer} and calls
0094: * the {@link #start()} method, passing in the command line arguments.
0095: *
0096: * @param arguments The command line arguments
0097: */
0098: public static void main(String[] arguments) {
0099: new Installer(arguments).start();
0100: }
0101:
0102: /////////////////////////////////////////////////////////////////////////////////
0103: // Static
0104: private static Installer instance;
0105:
0106: /**
0107: * Returns an instance of <code>Installer</code>. If the instance does not
0108: * exist - it is created.
0109: *
0110: * @return An instance of <code>Installer</code>
0111: */
0112: public static synchronized Installer getInstance() {
0113: if (instance == null) {
0114: instance = new Installer(new String[0]);
0115: }
0116:
0117: return instance;
0118: }
0119:
0120: /////////////////////////////////////////////////////////////////////////////////
0121: // Instance
0122: private File localDirectory = new File(DEFAULT_LOCAL_DIRECTORY_PATH)
0123: .getAbsoluteFile();
0124:
0125: // Constructor //////////////////////////////////////////////////////////////////
0126: /**
0127: * The only private constructor - we need to hide the default one as
0128: * <code>Installer is a singleton.
0129: *
0130: * @param arguments Command-line parameters.
0131: */
0132: private Installer(String[] arguments) {
0133: LogManager.logEntry("initializing the installer engine"); // NOI18N
0134:
0135: // initialize the error manager
0136: ErrorManager.setFinishHandler(this );
0137: ErrorManager
0138: .setExceptionHandler(new ErrorManager.ExceptionHandler());
0139:
0140: // attach a handler for uncaught exceptions in the main thread
0141: Thread.currentThread().setUncaughtExceptionHandler(
0142: ErrorManager.getExceptionHandler());
0143:
0144: // check whether we can safely execute on the current platform, exit in
0145: // panic otherwise
0146: if (SystemUtils.getCurrentPlatform() == null) {
0147: ErrorManager.notifyCritical(ResourceUtils.getString(
0148: Installer.class, ERROR_UNSUPPORTED_PLATFORM_KEY));
0149: }
0150:
0151: instance = this ;
0152:
0153: // output all the possible target system information -- this may help to
0154: // devise the configuration differences in case of errors
0155: dumpSystemInfo();
0156:
0157: parseArguments(arguments);
0158: loadEngineProperties();
0159: setLocalDirectory();
0160:
0161: // once we have set the local directory (and therefore devised the log
0162: // file path) we can start logging safely
0163: LogManager.setLogFile(new File(localDirectory, LOG_FILE_NAME));
0164: LogManager.start();
0165:
0166: // initialize the download manager module
0167: final DownloadManager downloadManager = DownloadManager
0168: .getInstance();
0169: downloadManager.setLocalDirectory(localDirectory);
0170: downloadManager.setFinishHandler(this );
0171: downloadManager.init();
0172:
0173: // initialize the product registry module
0174: final Registry registry = Registry.getInstance();
0175: registry.setLocalDirectory(localDirectory);
0176: registry.setFinishHandler(this );
0177:
0178: // initialize the wizard module
0179: final Wizard wizard = Wizard.getInstance();
0180: wizard.setFinishHandler(this );
0181: wizard.getContext().put(registry);
0182:
0183: // create the lock file
0184: createLockFile();
0185:
0186: // perform some additional intiialization for Mac OS
0187: initializeMacOS();
0188:
0189: LogManager.logExit("... finished initializing the engine"); // NOI18N
0190: }
0191:
0192: // Life cycle control methods ///////////////////////////////////////////////////
0193: /**
0194: * Starts the installer.
0195: */
0196: public void start() {
0197: LogManager.logEntry("starting the installer"); // NOI18N
0198:
0199: Wizard.getInstance().open();
0200:
0201: LogManager.logExit("... finished starting the installer"); // NOI18N
0202: }
0203:
0204: /**
0205: * Cancels the installation. This method cancels the changes that were possibly
0206: * made to the components registry and exits with the cancel error code.
0207: *
0208: * This method is not logged.
0209: *
0210: * @see #finish()
0211: * @see #criticalExit()
0212: */
0213: public void cancel() {
0214: exitNormally(CANCEL_ERRORCODE);
0215: }
0216:
0217: /**
0218: * Finishes the installation. This method finalizes the changes made to the
0219: * components registry and exits with a normal error code.
0220: *
0221: * This method is not logged.
0222: *
0223: * @see #cancel()
0224: * @see #criticalExit()
0225: */
0226: public void finish() {
0227: int exitCode = NORMAL_ERRORCODE;
0228: final Object prop = System.getProperties().get(
0229: EXIT_CODE_PROPERTY);
0230: if (prop != null && prop instanceof Integer) {
0231: try {
0232: exitCode = ((Integer) prop).intValue();
0233: } catch (NumberFormatException e) {
0234: LogManager.log("... cannot parse exit code : " + prop,
0235: e);
0236: }
0237: }
0238: exitNormally(exitCode);
0239: }
0240:
0241: /**
0242: * Critically exists. No changes will be made to the components registry - it
0243: * will remain at the same state it was at the moment this method was called.
0244: *
0245: * This method is not logged.
0246: *
0247: * @see #cancel()
0248: * @see #finish()
0249: */
0250: public void criticalExit() {
0251: // exit immediately, as the system is apparently in a crashed state
0252: exitImmediately(CRITICAL_ERRORCODE);
0253: }
0254:
0255: public File getLocalDirectory() {
0256: return localDirectory;
0257: }
0258:
0259: // private //////////////////////////////////////////////////////////////////////
0260: private void exitNormally(int errorCode) {
0261: Wizard.getInstance().close();
0262: DownloadManager.getInstance().terminate();
0263: LogManager.stop();
0264:
0265: exitImmediately(errorCode);
0266: }
0267:
0268: private void exitImmediately(int errorCode) {
0269: if (Boolean.getBoolean(DONT_USE_SYSTEM_EXIT_PROPERTY)
0270: && (errorCode != CRITICAL_ERRORCODE)) {
0271: System.getProperties().put(EXIT_CODE_PROPERTY,
0272: new Integer(errorCode));
0273: } else {
0274: System.exit(errorCode);
0275: }
0276: }
0277:
0278: private void dumpSystemInfo() {
0279: LogManager.logEntry("dumping target system information"); // NOI18N
0280:
0281: LogManager.logIndent("system properties:"); // NOI18N
0282:
0283: for (Object key : new TreeSet<Object>(System.getProperties()
0284: .keySet())) {
0285: LogManager.log(key.toString() + " => " + // NOI18N
0286: System.getProperties().get(key).toString());
0287: }
0288:
0289: LogManager.unindent();
0290:
0291: LogManager.logExit("... end of target system information"); // NOI18N
0292: }
0293:
0294: private void loadEngineProperties() {
0295: LogManager.logEntry("loading engine properties"); // NOI18N
0296:
0297: try {
0298: LogManager.logIndent("loading engine properties");
0299:
0300: ResourceBundle bundle = ResourceBundle
0301: .getBundle(EngineResources.ENGINE_PROPERTIES_BUNDLE);
0302: Enumeration<String> keys = bundle.getKeys();
0303:
0304: while (keys.hasMoreElements()) {
0305: final String key = keys.nextElement();
0306: final String value = bundle.getString(key);
0307: LogManager.log("loading " + key + " => " + value); // NOI18N
0308: final String currentValue = System.getProperty(key);
0309: if (currentValue != null) {
0310: LogManager
0311: .log("... already defined, using existing value: "
0312: + currentValue); // NOI18N
0313: } else {
0314: System.setProperty(key, value);
0315: }
0316:
0317: }
0318: } catch (MissingResourceException e) {
0319: LogManager
0320: .log("... no engine properties file, skip loading engine properties");
0321: }
0322:
0323: LogManager.unindent();
0324:
0325: LogManager.logExit("... finished loading engine properties"); // NOI18N
0326: }
0327:
0328: /**
0329: * Parses the command line arguments passed to the installer. All unknown
0330: * arguments are ignored.
0331: *
0332: * @param arguments The command line arguments
0333: */
0334: private void parseArguments(String[] arguments) {
0335: LogManager.logEntry("parsing command-line arguments"); // NOI18N
0336:
0337: for (int i = 0; i < arguments.length; i++) {
0338: if (arguments[i].equalsIgnoreCase(LOOK_AND_FEEL_ARG)) {
0339: LogManager.logIndent(StringUtils.format(
0340: PARSING_ARGUMENT_STRING, LOOK_AND_FEEL_ARG));
0341:
0342: if (i < arguments.length - 1) {
0343: final String value = arguments[i + 1];
0344: System.setProperty(UiUtils.LAF_CLASS_NAME_PROPERTY,
0345: value);
0346:
0347: i = i + 1;
0348:
0349: LogManager.log("... class name: " + value); // NOI18N
0350: } else {
0351: final String message = ResourceUtils.getString(
0352: Installer.class,
0353: WARNING_BAD_LOOK_AND_FEEL_ARG_KEY,
0354: LOOK_AND_FEEL_ARG);
0355:
0356: ErrorManager.notifyWarning(message);
0357: }
0358:
0359: LogManager.unindent();
0360: continue;
0361: }
0362:
0363: if (arguments[i].equalsIgnoreCase(TARGET_ARG)) {
0364: LogManager.logIndent(StringUtils.format(
0365: PARSING_ARGUMENT_STRING, TARGET_ARG));
0366:
0367: if (i < arguments.length - 2) {
0368: final String uid = arguments[i + 1];
0369: final String version = arguments[i + 2];
0370:
0371: System
0372: .setProperty(
0373: Registry.TARGET_COMPONENT_UID_PROPERTY,
0374: uid);
0375: System.setProperty(
0376: Registry.TARGET_COMPONENT_VERSION_PROPERTY,
0377: version);
0378:
0379: i = i + 2;
0380:
0381: LogManager.log("target component:"); // NOI18N
0382: LogManager.log("... uid: " + uid); // NOI18N
0383: LogManager.log("... version: " + version); // NOI18N
0384: } else {
0385: ErrorManager.notifyWarning(ResourceUtils.getString(
0386: Installer.class,
0387: WARNING_BAD_TARGET_ARG_KEY, TARGET_ARG));
0388: }
0389:
0390: LogManager.unindent();
0391: continue;
0392: }
0393:
0394: if (arguments[i].equalsIgnoreCase(LOCALE_ARG)) {
0395: LogManager.logIndent(StringUtils.format(
0396: PARSING_ARGUMENT_STRING, LOCALE_ARG)); // NOI18N
0397:
0398: if (i < arguments.length - 1) {
0399: final String value = arguments[i + 1];
0400: final String[] valueParts = value
0401: .split(StringUtils.UNDERSCORE);
0402:
0403: final Locale targetLocale;
0404: switch (valueParts.length) {
0405: case 1:
0406: targetLocale = new Locale(valueParts[0]);
0407: break;
0408: case 2:
0409: targetLocale = new Locale(valueParts[0],
0410: valueParts[1]);
0411: break;
0412: case 3:
0413: targetLocale = new Locale(valueParts[0],
0414: valueParts[1], valueParts[2]);
0415: break;
0416: default:
0417: targetLocale = null;
0418:
0419: final String message = ResourceUtils.getString(
0420: Installer.class,
0421: WARNING_BAD_LOCALE_ARG_PARAM_KEY,
0422: LOCALE_ARG, value);
0423: ErrorManager.notifyWarning(message);
0424: }
0425:
0426: if (targetLocale != null) {
0427: Locale.setDefault(targetLocale);
0428:
0429: LogManager.log("... locale set to: "
0430: + targetLocale); // NOI18N
0431: } else {
0432: LogManager.log("... locale is not set, using "
0433: + // NOI18N
0434: "system default: "
0435: + Locale.getDefault()); // NOI18N
0436: }
0437:
0438: i = i + 1;
0439: } else {
0440: ErrorManager.notifyWarning(ResourceUtils.getString(
0441: Installer.class,
0442: WARNING_BAD_LOCALE_ARG_KEY, LOCALE_ARG));
0443: }
0444:
0445: LogManager.unindent();
0446: continue;
0447: }
0448:
0449: if (arguments[i].equalsIgnoreCase(STATE_ARG)) {
0450: LogManager.logIndent(StringUtils.format(
0451: PARSING_ARGUMENT_STRING, STATE_ARG)); // NOI18N
0452:
0453: if (i < arguments.length - 1) {
0454: String value = arguments[i + 1];
0455:
0456: File stateFile = new File(value).getAbsoluteFile();
0457: if (!stateFile.exists()) {
0458: ErrorManager.notifyWarning(ResourceUtils
0459: .getString(Installer.class,
0460: WARNING_MISSING_STATE_FILE_KEY,
0461: STATE_ARG, stateFile));
0462: } else {
0463: System
0464: .setProperty(
0465: Registry.SOURCE_STATE_FILE_PATH_PROPERTY,
0466: stateFile.getAbsolutePath());
0467: }
0468:
0469: i = i + 1;
0470: } else {
0471: ErrorManager.notifyWarning(ResourceUtils.getString(
0472: Installer.class,
0473: WARNING_BAD_STATE_FILE_ARG_KEY, STATE_ARG));
0474: }
0475:
0476: LogManager.unindent();
0477: continue;
0478: }
0479:
0480: if (arguments[i].equalsIgnoreCase(RECORD_ARG)) {
0481: LogManager.logIndent(StringUtils.format(
0482: PARSING_ARGUMENT_STRING, RECORD_ARG)); // NOI18N
0483:
0484: if (i < arguments.length - 1) {
0485: String value = arguments[i + 1];
0486:
0487: File stateFile = new File(value).getAbsoluteFile();
0488: if (stateFile.exists()) {
0489: ErrorManager
0490: .notifyWarning(ResourceUtils
0491: .getString(
0492: Installer.class,
0493: WARNING_TARGET_STATE_FILE_EXISTS_KEY,
0494: RECORD_ARG, stateFile));
0495: } else {
0496: System
0497: .setProperty(
0498: Registry.TARGET_STATE_FILE_PATH_PROPERTY,
0499: stateFile.getAbsolutePath());
0500: }
0501:
0502: i = i + 1;
0503: } else {
0504: ErrorManager.notifyWarning(ResourceUtils.getString(
0505: Installer.class,
0506: WARNING_BAD_TARGET_STATE_FILE_ARG_KEY,
0507: RECORD_ARG));
0508: }
0509:
0510: LogManager.unindent();
0511: continue;
0512: }
0513:
0514: if (arguments[i].equalsIgnoreCase(SILENT_ARG)) {
0515: LogManager.logIndent(StringUtils.format(
0516: PARSING_ARGUMENT_STRING, SILENT_ARG));
0517:
0518: UiMode.setCurrentUiMode(UiMode.SILENT);
0519:
0520: LogManager.unindent();
0521: continue;
0522: }
0523:
0524: if (arguments[i].equalsIgnoreCase(CREATE_BUNDLE_ARG)) {
0525: LogManager.logIndent(StringUtils.format(
0526: PARSING_ARGUMENT_STRING, CREATE_BUNDLE_ARG)); // NOI18N
0527:
0528: if (i < arguments.length - 1) {
0529: String value = arguments[i + 1];
0530:
0531: File targetFile = new File(value).getAbsoluteFile();
0532: if (targetFile.exists()) {
0533: ErrorManager.notifyWarning(ResourceUtils
0534: .getString(Installer.class,
0535: WARNING_BUNDLE_FILE_EXISTS_KEY,
0536: CREATE_BUNDLE_ARG, targetFile));
0537: } else {
0538: ExecutionMode
0539: .setCurrentExecutionMode(ExecutionMode.CREATE_BUNDLE);
0540: System.setProperty(
0541: Registry.CREATE_BUNDLE_PATH_PROPERTY,
0542: targetFile.getAbsolutePath());
0543: }
0544:
0545: i = i + 1;
0546: } else {
0547: ErrorManager.notifyWarning(ResourceUtils.getString(
0548: Installer.class,
0549: WARNING_BAD_CREATE_BUNDLE_ARG_KEY,
0550: CREATE_BUNDLE_ARG));
0551: }
0552:
0553: LogManager.unindent();
0554: continue;
0555: }
0556:
0557: if (arguments[i].equalsIgnoreCase(IGNORE_LOCK_ARG)) {
0558: LogManager.logIndent(StringUtils.format(
0559: PARSING_ARGUMENT_STRING, IGNORE_LOCK_ARG)); // NOI18N
0560:
0561: System.setProperty(IGNORE_LOCK_FILE_PROPERTY,
0562: UNARY_ARG_VALUE);
0563:
0564: LogManager.unindent();
0565: continue;
0566: }
0567:
0568: if (arguments[i].equalsIgnoreCase(USERDIR_ARG)) {
0569: LogManager.logIndent(StringUtils.format(
0570: PARSING_ARGUMENT_STRING, USERDIR_ARG)); // NOI18N
0571:
0572: if (i < arguments.length - 1) {
0573: String value = arguments[i + 1];
0574: File file = new File(value);
0575:
0576: System.setProperty(LOCAL_DIRECTORY_PATH_PROPERTY,
0577: file.getAbsolutePath());
0578:
0579: i = i + 1;
0580: } else {
0581: ErrorManager.notifyWarning(ResourceUtils.getString(
0582: Installer.class,
0583: WARNING_BAD_USERDIR_ARG_KEY, USERDIR_ARG));
0584: }
0585:
0586: LogManager.unindent();
0587: continue;
0588: }
0589:
0590: if (arguments[i].equalsIgnoreCase(PLATFORM_ARG)) {
0591: LogManager.logIndent(StringUtils.format(
0592: PARSING_ARGUMENT_STRING, PLATFORM_ARG)); // NOI18N
0593:
0594: if (i < arguments.length - 1) {
0595: String value = arguments[i + 1];
0596:
0597: System.setProperty(
0598: Registry.TARGET_PLATFORM_PROPERTY, value);
0599:
0600: i = i + 1;
0601: } else {
0602: ErrorManager
0603: .notifyWarning(ResourceUtils.getString(
0604: Installer.class,
0605: WARNING_BAD_PLATFORM_ARG_KEY,
0606: PLATFORM_ARG));
0607: }
0608:
0609: LogManager.unindent();
0610: continue;
0611: }
0612:
0613: if (arguments[i].equalsIgnoreCase(SUGGEST_INSTALL_ARG)) {
0614: LogManager.logIndent(StringUtils.format(
0615: PARSING_ARGUMENT_STRING, SUGGEST_INSTALL_ARG)); // NOI18N
0616:
0617: System.setProperty(Registry.SUGGEST_INSTALL_PROPERTY,
0618: UNARY_ARG_VALUE);
0619:
0620: LogManager.unindent();
0621: continue;
0622: }
0623:
0624: if (arguments[i].equalsIgnoreCase(SUGGEST_UNINSTALL_ARG)) {
0625: LogManager
0626: .logIndent(StringUtils.format(
0627: PARSING_ARGUMENT_STRING,
0628: SUGGEST_UNINSTALL_ARG)); // NOI18N
0629:
0630: System.setProperty(Registry.SUGGEST_UNINSTALL_PROPERTY,
0631: UNARY_ARG_VALUE);
0632:
0633: LogManager.unindent();
0634: continue;
0635: }
0636:
0637: if (arguments[i].equalsIgnoreCase(FORCE_INSTALL_ARG)) {
0638: LogManager.logIndent(StringUtils.format(
0639: PARSING_ARGUMENT_STRING, FORCE_INSTALL_ARG)); // NOI18N
0640:
0641: System.setProperty(Registry.FORCE_INSTALL_PROPERTY,
0642: UNARY_ARG_VALUE);
0643:
0644: LogManager.unindent();
0645: continue;
0646: }
0647:
0648: if (arguments[i].equalsIgnoreCase(FORCE_UNINSTALL_ARG)) {
0649: LogManager.logIndent(StringUtils.format(
0650: PARSING_ARGUMENT_STRING, FORCE_UNINSTALL_ARG)); // NOI18N
0651:
0652: System.setProperty(Registry.FORCE_UNINSTALL_PROPERTY,
0653: UNARY_ARG_VALUE);
0654:
0655: LogManager.unindent();
0656: continue;
0657: }
0658:
0659: if (arguments[i].equalsIgnoreCase(REGISTRY_ARG)) {
0660: LogManager.logIndent(StringUtils.format(
0661: PARSING_ARGUMENT_STRING, REGISTRY_ARG)); // NOI18N
0662:
0663: if (i < arguments.length - 1) {
0664: final String value = arguments[i + 1];
0665:
0666: final String existing = System
0667: .getProperty(Registry.REMOTE_PRODUCT_REGISTRIES_PROPERTY);
0668:
0669: if (existing == null) {
0670: System
0671: .setProperty(
0672: Registry.REMOTE_PRODUCT_REGISTRIES_PROPERTY,
0673: value);
0674: } else {
0675: if (!Arrays.asList(
0676: existing.split(StringUtils.LF))
0677: .contains(value)) {
0678: System
0679: .setProperty(
0680: Registry.REMOTE_PRODUCT_REGISTRIES_PROPERTY,
0681: existing + StringUtils.LF
0682: + value);
0683: }
0684: }
0685:
0686: i = i + 1;
0687: } else {
0688: ErrorManager
0689: .notifyWarning(ResourceUtils.getString(
0690: Installer.class,
0691: WARNING_BAD_REGISTRY_ARG_KEY,
0692: REGISTRY_ARG));
0693: }
0694:
0695: LogManager.unindent();
0696: continue;
0697: }
0698: if (arguments[i].equalsIgnoreCase(PROPERTIES_ARG)) {
0699: LogManager.logIndent(StringUtils.format(
0700: PARSING_ARGUMENT_STRING, PROPERTIES_ARG)); // NOI18N
0701:
0702: if (i < arguments.length - 1) {
0703: final String value = arguments[i + 1];
0704: final File propertiesFile = new File(value);
0705: InputStream is = null;
0706: try {
0707: is = new FileInputStream(propertiesFile);
0708: Properties props = new Properties();
0709: props.load(is);
0710: System.getProperties().putAll(props);
0711: } catch (IOException e) {
0712: LogManager.log(e);
0713: } finally {
0714: if (is != null) {
0715: try {
0716: is.close();
0717: } catch (IOException e) {
0718: LogManager.log(e);
0719: }
0720: }
0721: }
0722: i = i + 1;
0723: } else {
0724: ErrorManager.notifyWarning(ResourceUtils.getString(
0725: Installer.class,
0726: WARNING_BAD_PROPERTIES_ARG_KEY,
0727: PROPERTIES_ARG));
0728: }
0729:
0730: LogManager.unindent();
0731: continue;
0732: }
0733:
0734: if (arguments[i].equalsIgnoreCase(BUNDLE_PROPERTIES_ARG)) {
0735: LogManager
0736: .logIndent(StringUtils.format(
0737: PARSING_ARGUMENT_STRING,
0738: BUNDLE_PROPERTIES_ARG)); // NOI18N
0739:
0740: if (i < arguments.length - 1) {
0741: String value = arguments[i + 1];
0742: System.setProperty(BUNDLE_PROPERTIES_FILE_PROPERTY,
0743: value);
0744:
0745: i = i + 1;
0746: } else {
0747: ErrorManager.notifyWarning(ResourceUtils.getString(
0748: Installer.class,
0749: WARNING_BAD_BUNDLE_PROPERTIES_ARG_KEY,
0750: BUNDLE_PROPERTIES_ARG));
0751: }
0752:
0753: LogManager.unindent();
0754: continue;
0755: }
0756: if (arguments[i].equalsIgnoreCase(NO_SPACE_CHECK_ARG)) {
0757: LogManager.logIndent(StringUtils.format(
0758: PARSING_ARGUMENT_STRING, NO_SPACE_CHECK_ARG)); // NOI18N
0759:
0760: System.setProperty(SystemUtils.NO_SPACE_CHECK_PROPERTY,
0761: UNARY_ARG_VALUE);
0762:
0763: LogManager.unindent();
0764: continue;
0765: }
0766: }
0767: if (arguments.length == 0) {
0768: LogManager
0769: .log("... no command line arguments were specified"); // NOI18N
0770: }
0771:
0772: // validate arguments ///////////////////////////////////////////////////////
0773: /*
0774: Disabled since 28.02.2008 by Dmitry Lipin:
0775: I don`t see any reason for having that restiction at this moment:
0776: I can succesfully install/create bundle just with empty state file similar to
0777: Registry.DEFAULT_STATE_FILE_STUB_URI or even just have <state/> as the file contents
0778: */
0779: /*
0780: if (UiMode.getCurrentUiMode() != UiMode.DEFAULT_MODE) {
0781: if (System.getProperty(
0782: Registry.SOURCE_STATE_FILE_PATH_PROPERTY) == null) {
0783: UiMode.setCurrentUiMode(UiMode.DEFAULT_MODE);
0784: ErrorManager.notifyWarning(ResourceUtils.getString(
0785: Installer.class,
0786: WARNING_SILENT_WITHOUT_STATE_KEY,
0787: SILENT_ARG,
0788: STATE_ARG));
0789: }
0790: }
0791: */
0792: LogManager
0793: .logExit("... finished parsing command line arguments"); // NOI18N
0794: }
0795:
0796: private void setLocalDirectory() {
0797: LogManager.logIndent("initializing the local directory"); // NOI18N
0798:
0799: if (System.getProperty(LOCAL_DIRECTORY_PATH_PROPERTY) != null) {
0800: localDirectory = new File(System
0801: .getProperty(LOCAL_DIRECTORY_PATH_PROPERTY))
0802: .getAbsoluteFile();
0803: } else {
0804: LogManager.log("... custom local directory was " + // NOI18N
0805: "not specified, using the default"); // NOI18N
0806:
0807: localDirectory = new File(DEFAULT_LOCAL_DIRECTORY_PATH)
0808: .getAbsoluteFile();
0809: System.setProperty(LOCAL_DIRECTORY_PATH_PROPERTY,
0810: localDirectory.getAbsolutePath());
0811: }
0812:
0813: LogManager.log("... local directory: " + localDirectory); // NOI18N
0814:
0815: if (!localDirectory.exists()) {
0816: if (!localDirectory.mkdirs()) {
0817: ErrorManager.notifyCritical(ResourceUtils.getString(
0818: Installer.class,
0819: ERROR_CANNOT_CREATE_LOCAL_DIR_KEY,
0820: localDirectory));
0821: }
0822: } else if (localDirectory.isFile()) {
0823: ErrorManager.notifyCritical(ResourceUtils.getString(
0824: Installer.class, ERROR_LOCAL_DIR_IS_FILE_KEY,
0825: localDirectory));
0826: } else if (!localDirectory.canRead()) {
0827: ErrorManager.notifyCritical(ResourceUtils.getString(
0828: Installer.class,
0829: ERROR_NO_READ_PERMISSIONS_FOR_LOCAL_DIR_KEY,
0830: localDirectory));
0831: } else if (!localDirectory.canWrite()) {
0832: ErrorManager.notifyCritical(ResourceUtils.getString(
0833: Installer.class,
0834: ERROR_NO_WRITE_PERMISSIONS_FOR_LOCAL_DIR_KEY,
0835: localDirectory));
0836: }
0837:
0838: LogManager
0839: .logUnindent("... finished initializing local directory"); // NOI18N
0840: }
0841:
0842: private void createLockFile() {
0843: LogManager.logIndent("creating lock file"); // NOI18N
0844:
0845: if (System.getProperty(IGNORE_LOCK_FILE_PROPERTY) == null) {
0846: final File lock = new File(localDirectory, LOCK_FILE_NAME);
0847:
0848: if (lock.exists()) {
0849: LogManager.log("... lock file already exists"); // NOI18N
0850:
0851: final String dialogTitle = ResourceUtils.getString(
0852: Installer.class,
0853: LOCK_FILE_EXISTS_DIALOG_TITLE_KEY);
0854: final String dialogText = ResourceUtils.getString(
0855: Installer.class,
0856: LOCK_FILE_EXISTS_DIALOG_TEXT_KEY);
0857: if (!UiUtils.showYesNoDialog(dialogTitle, dialogText)) {
0858: cancel();
0859: }
0860: } else {
0861: try {
0862: lock.createNewFile();
0863: } catch (IOException e) {
0864: ErrorManager.notifyCritical(ResourceUtils
0865: .getString(Installer.class,
0866: ERROR_CANNOT_CREATE_LOCK_FILE_KEY),
0867: e);
0868: }
0869:
0870: LogManager.log("... created lock file: " + lock); // NOI18N
0871: }
0872:
0873: lock.deleteOnExit();
0874: } else {
0875: LogManager.log("... running with " + // NOI18N
0876: IGNORE_LOCK_ARG + ", skipping this step"); // NOI18N
0877: }
0878:
0879: LogManager.logUnindent("finished creating lock file"); // NOI18N
0880: }
0881:
0882: private void initializeMacOS() {
0883: if (SystemUtils.isMacOS()) {
0884: final Application application = Application
0885: .getApplication();
0886:
0887: application.removeAboutMenuItem();
0888: application.removePreferencesMenuItem();
0889:
0890: application
0891: .addApplicationListener(new ApplicationAdapter() {
0892: @Override
0893: public void handleQuit(ApplicationEvent event) {
0894: final String dialogTitle = ResourceUtils
0895: .getString(
0896: WizardComponent.class,
0897: WizardComponent.RESOURCE_CANCEL_DIALOG_TITLE);
0898: final String dialogText = ResourceUtils
0899: .getString(
0900: WizardComponent.class,
0901:
0902: WizardComponent.RESOURCE_CANCEL_DIALOG_TEXT);
0903:
0904: if (UiUtils.showYesNoDialog(dialogTitle,
0905: dialogText)) {
0906: cancel();
0907: }
0908: }
0909: });
0910: }
0911: }
0912:
0913: /**
0914: * Cache installer at NBI`s home directory.
0915: */
0916: public static File cacheInstallerEngine(Progress progress)
0917: throws IOException {
0918: final String propName = EngineResources.LOCAL_ENGINE_PATH_PROPERTY;
0919: File cachedEngine = null;
0920:
0921: if (System.getProperty(propName) == null) {
0922: cachedEngine = new File(System
0923: .getProperty(LOCAL_DIRECTORY_PATH_PROPERTY),
0924: "nbi-engine.jar");
0925: System
0926: .setProperty(propName, cachedEngine
0927: .getAbsolutePath());
0928: } else {
0929: cachedEngine = new File(System.getProperty(propName));
0930: }
0931:
0932: if (!FileUtils.exists(cachedEngine)) {
0933: cacheInstallerEngine(cachedEngine, progress);
0934: }
0935:
0936: return new File(System.getProperty(propName));
0937: }
0938:
0939: private static void cacheInstallerEngineJar(File dest,
0940: Progress progress) throws IOException {
0941: LogManager
0942: .log("... starting copying engine content to the new jar file");
0943: String[] entries = StringUtils
0944: .splitByLines(StreamUtils
0945: .readStream(ResourceUtils
0946: .getResource(EngineResources.ENGINE_CONTENTS_LIST)));
0947:
0948: JarOutputStream jos = null;
0949:
0950: try {
0951: Manifest mf = new Manifest();
0952: mf.getMainAttributes().put(Attributes.Name.MAIN_CLASS,
0953: Installer.class.getName());
0954: mf.getMainAttributes().put(
0955: Attributes.Name.MANIFEST_VERSION, "1.0");
0956: mf.getMainAttributes().put(Attributes.Name.CLASS_PATH, "");
0957:
0958: dest.getParentFile().mkdirs();
0959: jos = new JarOutputStream(new FileOutputStream(dest), mf);
0960: LogManager.log("... total entries : " + entries.length);
0961: for (int i = 0; i < entries.length; i++) {
0962: progress.setPercentage((i * 100) / entries.length);
0963: String name = entries[i];
0964: if (name.length() > 0) {
0965: String dataDir = EngineResources.DATA_DIRECTORY
0966: + StringUtils.FORWARD_SLASH;
0967: if (!name.startsWith(dataDir) || // all except "data/""
0968: name.equals(dataDir) || // "data/"
0969: name
0970: .matches(EngineResources.ENGINE_PROPERTIES_PATTERN)) {
0971: jos.putNextEntry(new JarEntry(name));
0972: if (!name.endsWith(StringUtils.FORWARD_SLASH)) {
0973: StreamUtils.transferData(ResourceUtils
0974: .getResource(name), jos);
0975: }
0976: }
0977: }
0978: }
0979: LogManager
0980: .log("... adding content list and some other stuff");
0981:
0982: jos.putNextEntry(new JarEntry(
0983: EngineResources.DATA_DIRECTORY
0984: + StringUtils.FORWARD_SLASH
0985: + "registry.xml"));
0986:
0987: XMLUtils.saveXMLDocument(Registry.getInstance()
0988: .getEmptyRegistryDocument(), jos);
0989:
0990: jos.putNextEntry(new JarEntry(
0991: EngineResources.ENGINE_CONTENTS_LIST));
0992: jos.write(StringUtils.asString(entries,
0993: SystemUtils.getLineSeparator()).getBytes());
0994: } catch (XMLException e) {
0995: IOException ex = new IOException();
0996: ex.initCause(e);
0997: throw ex;
0998: } finally {
0999: if (jos != null) {
1000: try {
1001: jos.close();
1002: } catch (IOException ex) {
1003: LogManager.log(ex);
1004: }
1005:
1006: }
1007: }
1008:
1009: LogManager.log("Installer Engine has been cached to " + dest);
1010: }
1011:
1012: public static void cacheInstallerEngine(File dest, Progress progress)
1013: throws IOException {
1014: LogManager
1015: .logIndent("cache engine data locally to run uninstall in the future");
1016:
1017: String filePrefix = "file:";
1018: String httpPrefix = "http://";
1019: String jarSep = "!/";
1020:
1021: String installerResource = Installer.class.getName().replace(
1022: ".", "/")
1023: + ".class";
1024: URL url = Installer.class.getClassLoader().getResource(
1025: installerResource);
1026: if (url == null) {
1027: throw new IOException(
1028: "No main Installer class in the engine");
1029: }
1030:
1031: LogManager.log(ErrorLevel.DEBUG,
1032: "NBI Engine URL for Installer.Class = " + url);
1033: LogManager.log(ErrorLevel.DEBUG, "URL Path = " + url.getPath());
1034:
1035: boolean needCache = true;
1036:
1037: if ("jar".equals(url.getProtocol())) {
1038: LogManager.log("... running engine as a .jar file");
1039: // we run engine from jar, not from .class
1040: String path = url.getPath();
1041: String jarLocation;
1042:
1043: if (path.startsWith(filePrefix)) {
1044: LogManager
1045: .log("... classloader says that jar file is on the disk");
1046: if (path.indexOf(jarSep) != -1) {
1047: jarLocation = path.substring(filePrefix.length(),
1048: path.indexOf(jarSep + installerResource));
1049: jarLocation = URLDecoder.decode(jarLocation,
1050: StringUtils.ENCODING_UTF8);
1051: File jarfile = new File(jarLocation);
1052: LogManager
1053: .log("... checking if it runs from cached engine");
1054: if (jarfile.getAbsolutePath().equals(
1055: dest.getAbsolutePath())) {
1056: needCache = false; // we already run cached version
1057: }
1058: LogManager.log("... " + !needCache);
1059: } else {
1060: throw new IOException("JAR path " + path
1061: + " doesn`t contaion jar-separator "
1062: + jarSep);
1063: }
1064: } else if (path.startsWith(httpPrefix)) {
1065: LogManager
1066: .log("... classloader says that jar file is on remote server");
1067: }
1068: } else {
1069: // a quick hack to allow caching engine when run from the IDE (i.e.
1070: // as a .class) - probably to be removed later. Or maybe not...
1071: LogManager.log("... running engine as a .class file");
1072: }
1073:
1074: if (needCache) {
1075: cacheInstallerEngineJar(dest, progress);
1076: }
1077:
1078: LogManager.logUnindent("... finished caching engine data");
1079: }
1080:
1081: /////////////////////////////////////////////////////////////////////////////////
1082: // Constants
1083:
1084: // errorcodes ///////////////////////////////////////////////////////////////////
1085: /** Errorcode to be used at normal exit */
1086: public static final int NORMAL_ERRORCODE = 0;
1087:
1088: /** Errorcode to be used when the installer is canceled */
1089: public static final int CANCEL_ERRORCODE = 1;
1090:
1091: /** Errorcode to be used when the installer exits because of a critical error */
1092: public static final int CRITICAL_ERRORCODE = 255;
1093:
1094: // command line arguments ///////////////////////////////////////////////////////
1095: public static final String TARGET_ARG = "--target"; // NOI18N
1096:
1097: public static final String LOOK_AND_FEEL_ARG = "--look-and-feel"; // NOI18N
1098:
1099: public static final String LOCALE_ARG = "--locale"; // NOI18N
1100:
1101: public static final String STATE_ARG = "--state"; // NOI18N
1102:
1103: public static final String RECORD_ARG = "--record"; // NOI18N
1104:
1105: public static final String SILENT_ARG = "--silent"; // NOI18N
1106:
1107: public static final String CREATE_BUNDLE_ARG = "--create-bundle"; // NOI18N
1108:
1109: public static final String IGNORE_LOCK_ARG = "--ignore-lock"; // NOI18N
1110:
1111: public static final String USERDIR_ARG = "--userdir"; // NOI18N
1112:
1113: public static final String PLATFORM_ARG = "--platform"; // NOI18N
1114:
1115: public static final String SUGGEST_INSTALL_ARG = "--suggest-install"; // NOI18N
1116:
1117: public static final String SUGGEST_UNINSTALL_ARG = "--suggest-uninstall"; // NOI18N
1118:
1119: public static final String FORCE_INSTALL_ARG = "--force-install"; // NOI18N
1120:
1121: public static final String FORCE_UNINSTALL_ARG = "--force-uninstall"; // NOI18N
1122:
1123: public static final String REGISTRY_ARG = "--registry"; // NOI18N
1124:
1125: public static final String PROPERTIES_ARG = "--properties"; // NOI18N
1126:
1127: public static final String BUNDLE_PROPERTIES_ARG = "--bundle-properties"; // NOI18N
1128:
1129: public static final String NO_SPACE_CHECK_ARG = "--nospacecheck"; // NOI18N
1130:
1131: public static final String UNARY_ARG_VALUE = "true"; // NOI18N
1132:
1133: public static final String PARSING_ARGUMENT_STRING = "parsing command line parameter \"{0}\"";//NOI18N
1134:
1135: // lock file ////////////////////////////////////////////////////////////////////
1136: public static final String LOCK_FILE_NAME = ".nbilock"; // NOI18N
1137:
1138: public static final String IGNORE_LOCK_FILE_PROPERTY = "nbi.ignore.lock.file"; // NOI18N
1139:
1140: // local working directory //////////////////////////////////////////////////////
1141: public static final String DEFAULT_LOCAL_DIRECTORY_PATH = System
1142: .getProperty("user.home")
1143: + File.separator + ".nbi";
1144:
1145: public static final String LOCAL_DIRECTORY_PATH_PROPERTY = "nbi.local.directory.path"; // NOI18N
1146:
1147: // miscellaneous ////////////////////////////////////////////////////////////////
1148: public static final String DONT_USE_SYSTEM_EXIT_PROPERTY = "nbi.dont.use.system.exit"; // NOI18N
1149:
1150: public static final String EXIT_CODE_PROPERTY = "nbi.exit.code"; // NOI18N
1151:
1152: public static final String BUNDLE_PROPERTIES_FILE_PROPERTY = "nbi.bundle.properties.file";//NOI18N
1153:
1154: public static final String LOG_FILE_NAME = "log/"
1155: + DateUtils.getTimestamp() + ".log";
1156:
1157: // resource bundle keys /////////////////////////////////////////////////////////
1158: private static final String ERROR_UNSUPPORTED_PLATFORM_KEY = "I.error.unsupported.platform"; // NOI18N
1159:
1160: private static final String ERROR_LOAD_ENGINE_PROPERTIES_KEY = "I.error.load.engine.properties"; // NOI18N
1161:
1162: private static final String WARNING_BAD_LOOK_AND_FEEL_ARG_KEY = "I.warning.bad.look.and.feel.arg"; // NOI18N
1163:
1164: private static final String WARNING_BAD_TARGET_ARG_KEY = "I.warning.bad.target.arg"; // NOI18N
1165:
1166: private static final String WARNING_BAD_LOCALE_ARG_PARAM_KEY = "I.warning.bad.locale.arg.param"; // NOI18N
1167:
1168: private static final String WARNING_BAD_LOCALE_ARG_KEY = "I.warning.bad.locale.arg"; // NOI18N
1169:
1170: private static final String WARNING_MISSING_STATE_FILE_KEY = "I.warning.missing.state.file"; // NOI18N
1171:
1172: private static final String WARNING_BAD_STATE_FILE_ARG_KEY = "I.warning.bag.state.file.arg"; // NOI18N
1173:
1174: private static final String WARNING_TARGET_STATE_FILE_EXISTS_KEY = "I.warning.target.state.file.exists"; // NOI18N
1175:
1176: private static final String WARNING_BAD_TARGET_STATE_FILE_ARG_KEY = "I.warning.bad.target.state.file.arg"; // NOI18N
1177:
1178: private static final String WARNING_BUNDLE_FILE_EXISTS_KEY = "I.warning.bundle.file.exists"; // NOI18N
1179:
1180: private static final String WARNING_BAD_CREATE_BUNDLE_ARG_KEY = "I.warning.bad.create.bundle.arg"; // NOI18N
1181:
1182: private static final String WARNING_BAD_USERDIR_ARG_KEY = "I.warning.bad.userdir.arg"; // NOI18N
1183:
1184: private static final String WARNING_BAD_PLATFORM_ARG_KEY = "I.warning.bad.platform.arg"; // NOI18N
1185:
1186: private static final String WARNING_BAD_REGISTRY_ARG_KEY = "I.warning.bad.registry.arg"; // NOI18N
1187:
1188: private static final String WARNING_BAD_PROPERTIES_ARG_KEY = "I.warning.bad.properties.arg"; // NOI18N
1189:
1190: private static final String WARNING_BAD_BUNDLE_PROPERTIES_ARG_KEY = "I.warning.bad.bundle.properties.arg"; // NOI18N
1191:
1192: private static final String WARNING_SILENT_WITHOUT_STATE_KEY = "I.warning.silent.without.state"; // NOI18N
1193:
1194: private static final String ERROR_CANNOT_CREATE_LOCAL_DIR_KEY = "I.error.cannot.create.local.dir"; // NOI18N
1195:
1196: private static final String ERROR_LOCAL_DIR_IS_FILE_KEY = "I.error.local.dir.is.file"; // NOI18N
1197:
1198: private static final String ERROR_NO_READ_PERMISSIONS_FOR_LOCAL_DIR_KEY = "I.error.no.read.permissions.for.local.dir"; // NOI18N
1199:
1200: private static final String ERROR_NO_WRITE_PERMISSIONS_FOR_LOCAL_DIR_KEY = "I.error.no.write.permissions.for.local.dir"; // NOI18N
1201:
1202: private static final String LOCK_FILE_EXISTS_DIALOG_TITLE_KEY = "I.lock.file.exists.dialog.title"; // NOI18N
1203:
1204: private static final String LOCK_FILE_EXISTS_DIALOG_TEXT_KEY = "I.lock.file.exists.dialog.text"; // NOI18N
1205:
1206: private static final String ERROR_CANNOT_CREATE_LOCK_FILE_KEY = "I.error.cannot.create.lock.file"; // NOI18N
1207:
1208: }
|