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.product.components;
0038:
0039: import java.io.File;
0040: import java.io.IOException;
0041: import java.net.URI;
0042: import java.util.ArrayList;
0043: import java.util.List;
0044: import java.util.Map;
0045: import org.netbeans.installer.Installer;
0046: import org.netbeans.installer.product.Registry;
0047: import org.netbeans.installer.product.RegistryNode;
0048: import org.netbeans.installer.product.dependencies.Conflict;
0049: import org.netbeans.installer.product.dependencies.InstallAfter;
0050: import org.netbeans.installer.product.dependencies.Requirement;
0051: import org.netbeans.installer.utils.ResourceUtils;
0052: import org.netbeans.installer.utils.helper.DependencyType;
0053: import org.netbeans.installer.utils.helper.DetailedStatus;
0054: import org.netbeans.installer.utils.helper.RemovalMode;
0055: import org.netbeans.installer.utils.helper.Status;
0056: import org.netbeans.installer.utils.FileProxy;
0057: import org.netbeans.installer.utils.ErrorManager;
0058: import org.netbeans.installer.utils.XMLUtils;
0059: import org.netbeans.installer.utils.exceptions.NativeException;
0060: import org.netbeans.installer.utils.FileUtils;
0061: import org.netbeans.installer.utils.LogManager;
0062: import org.netbeans.installer.utils.exceptions.XMLException;
0063: import org.netbeans.installer.utils.helper.FileEntry;
0064: import org.netbeans.installer.utils.helper.FilesList;
0065: import org.netbeans.installer.utils.helper.NbiClassLoader;
0066: import org.netbeans.installer.utils.exceptions.DownloadException;
0067: import org.netbeans.installer.utils.exceptions.FinalizationException;
0068: import org.netbeans.installer.utils.exceptions.InitializationException;
0069: import org.netbeans.installer.utils.exceptions.InstallationException;
0070: import org.netbeans.installer.utils.exceptions.UninstallationException;
0071: import org.netbeans.installer.utils.helper.Platform;
0072: import org.netbeans.installer.utils.StringUtils;
0073: import org.netbeans.installer.utils.SystemUtils;
0074: import org.netbeans.installer.utils.exceptions.ParseException;
0075: import org.netbeans.installer.utils.helper.ApplicationDescriptor;
0076: import org.netbeans.installer.utils.helper.Version;
0077: import org.netbeans.installer.utils.helper.ExtendedUri;
0078: import org.netbeans.installer.utils.helper.Dependency;
0079: import org.netbeans.installer.utils.helper.Text;
0080: import org.netbeans.installer.utils.progress.CompositeProgress;
0081: import org.netbeans.installer.utils.progress.Progress;
0082: import org.netbeans.installer.utils.system.UnixNativeUtils;
0083: import org.netbeans.installer.wizard.components.WizardComponent;
0084: import org.w3c.dom.Document;
0085: import org.w3c.dom.Element;
0086:
0087: /**
0088: *
0089: * @author Kirill Sorokin
0090: */
0091: public final class Product extends RegistryNode {
0092: /////////////////////////////////////////////////////////////////////////////////
0093: // Instance
0094: private Version version;
0095: private List<Platform> supportedPlatforms;
0096:
0097: private Status initialStatus;
0098: private Status currentStatus;
0099:
0100: private List<ExtendedUri> logicUris;
0101: private List<ExtendedUri> dataUris;
0102:
0103: private List<String> features;
0104:
0105: private long requiredDiskSpace;
0106:
0107: private List<Dependency> dependencies;
0108:
0109: private NbiClassLoader classLoader;
0110: private ProductConfigurationLogic configurationLogic;
0111:
0112: private Throwable installationError;
0113: private List<Throwable> installationWarnings;
0114:
0115: private Throwable uninstallationError;
0116: private List<Throwable> uninstallationWarnings;
0117:
0118: private FilesList installedFiles;
0119:
0120: private InstallationPhase installationPhase;
0121:
0122: // constructor //////////////////////////////////////////////////////////////////
0123: public Product() {
0124: supportedPlatforms = new ArrayList<Platform>();
0125: logicUris = new ArrayList<ExtendedUri>();
0126: dataUris = new ArrayList<ExtendedUri>();
0127: dependencies = new ArrayList<Dependency>();
0128: }
0129:
0130: // essential functionality //////////////////////////////////////////////////////
0131: public void install(final Progress progress)
0132: throws InstallationException {
0133: final CompositeProgress totalProgress = new CompositeProgress();
0134: final CompositeProgress unjarProgress = new CompositeProgress();
0135: final Progress logicProgress = new Progress();
0136:
0137: // initialization phase ////////////////////////////////////////////////
0138: installationPhase = InstallationPhase.INITIALIZATION;
0139:
0140: // load the component's configuration logic (it should be already
0141: // there, but we need to be sure)
0142: try {
0143: getLogic();
0144: } catch (InitializationException e) {
0145: throw new InstallationException(ResourceUtils.getString(
0146: Product.class, ERROR_CANNOT_INITIALIZE_PRODUCT_KEY,
0147: getDisplayName()), e);
0148: }
0149:
0150: totalProgress.addChild(unjarProgress, Progress.COMPLETE
0151: - configurationLogic.getLogicPercentage());
0152: totalProgress.addChild(logicProgress, configurationLogic
0153: .getLogicPercentage());
0154: totalProgress.synchronizeTo(progress);
0155: totalProgress.synchronizeDetails(true);
0156:
0157: // check whether the installation location was set, if it's not we
0158: // cannot continue
0159: if (getInstallationLocation() == null) {
0160: throw new InstallationException(ResourceUtils.getString(
0161: Product.class,
0162: ERROR_INSTALLATION_LOCATION_NOT_SET_KEY,
0163: getDisplayName()));
0164: } else if (getInstallationLocation().equals(
0165: new File(StringUtils.EMPTY_STRING))) {
0166: throw new InstallationException(ResourceUtils.getString(
0167: Product.class,
0168: ERROR_INSTALLATION_LOCATION_SET_EMPTY_KEY,
0169: getDisplayName()));
0170: }
0171:
0172: // initialize the local cache directory
0173: final File cache = getLocalCache();
0174: if (!cache.exists()) {
0175: if (!cache.mkdirs()) {
0176: throw new InstallationException(
0177: ResourceUtils
0178: .getString(
0179: Product.class,
0180: ERROR_CANNOT_CREATE_PRODUCT_CACHE_DIR_KEY,
0181: cache, getDisplayName()));
0182: }
0183: } else if (!cache.isDirectory()) {
0184: throw new InstallationException(ResourceUtils.getString(
0185: Product.class, ERROR_CACHE_NOT_DIRECTORY_KEY,
0186: cache, getDisplayName()));
0187: }
0188:
0189: // initialize the files list
0190: installedFiles = new FilesList();
0191:
0192: // check for cancel status
0193: if (progress.isCanceled())
0194: return;
0195:
0196: // extraction phase /////////////////////////////////////////////////////////
0197: installationPhase = InstallationPhase.EXTRACTION;
0198:
0199: totalProgress.setTitle(StringUtils.format(
0200: MESSAGE_INSTALLATION_STRING, getDisplayName()));
0201:
0202: final File contentsDir = new File(getInstallationLocation(),
0203: "Contents");
0204: final File macosDir = new File(contentsDir, "MacOS");
0205: final File resourcesDir = new File(contentsDir, "Resources");
0206: final File infoplist = new File(contentsDir, "Info.plist");
0207:
0208: // if we're running on macos x and the configuraion logic tells us that the
0209: // product should be automatically wrapped, we first create the required
0210: // directories structure and then extract the product
0211: if (SystemUtils.isMacOS() && configurationLogic.wrapForMacOs()) {
0212: setProperty(APPLICATION_LOCATION_PROPERTY,
0213: getInstallationLocation().getAbsolutePath());
0214: setInstallationLocation(new File(resourcesDir,
0215: getInstallationLocation().getName().replaceAll(
0216: "\\.app$", StringUtils.EMPTY_STRING)));
0217:
0218: final UnixNativeUtils utils = (UnixNativeUtils) SystemUtils
0219: .getNativeUtils();
0220:
0221: try {
0222: installedFiles.add(FileUtils.mkdirs(contentsDir));
0223: installedFiles.add(FileUtils.mkdirs(resourcesDir));
0224: installedFiles.add(FileUtils.mkdirs(macosDir));
0225:
0226: final String executableName = "executable"; //NOI18N
0227:
0228: installedFiles.add(utils.createSymLink(new File(
0229: macosDir, executableName), new File(
0230: getInstallationLocation(), configurationLogic
0231: .getExecutable())));
0232:
0233: final String iconName = "icon.icns"; //NOI18N
0234:
0235: installedFiles.add(utils.createSymLink(new File(
0236: resourcesDir, iconName), new File(
0237: getInstallationLocation(), configurationLogic
0238: .getIcon())));
0239:
0240: installedFiles.add(FileUtils.writeFile(infoplist,
0241: StringUtils.format(INFO_PLIST_STUB,
0242: getDisplayName(), getVersion()
0243: .toString(), getVersion()
0244: .toMinor(), executableName,
0245: iconName)));
0246: } catch (IOException e) {
0247: throw new InstallationException(ResourceUtils
0248: .getString(Product.class,
0249: ERROR_CANNOT_WRAP_FOR_MACOS_KEY), e);
0250: }
0251: }
0252:
0253: // extract each of the defined installation data files
0254: unjarProgress
0255: .setPercentage(Progress.COMPLETE % dataUris.size());
0256: unjarProgress.synchronizeDetails(true);
0257: for (ExtendedUri uri : dataUris) {
0258: final Progress currentProgress = new Progress();
0259: unjarProgress.addChild(currentProgress, Progress.COMPLETE
0260: / dataUris.size());
0261:
0262: // get the uri of the current data file
0263: final URI dataUri = uri.getLocal();
0264: if (dataUri == null) {
0265: throw new InstallationException(ResourceUtils
0266: .getString(Product.class,
0267: ERROR_DATA_NOT_CACHED_KEY,
0268: getDisplayName()));
0269: }
0270:
0271: // convert it to a file and do some additional checks
0272: final File dataFile = new File(uri.getLocal());
0273: if (!dataFile.exists()) {
0274: throw new InstallationException(ResourceUtils
0275: .getString(Product.class,
0276: ERROR_DATA_NOT_CACHED_KEY,
0277: getDisplayName()));
0278: }
0279:
0280: // exract it and add the files to the installed files list
0281: try {
0282: installedFiles.add(FileUtils.unjar(dataFile,
0283: getInstallationLocation(), currentProgress));
0284: } catch (IOException e) {
0285: if (e.getMessage().equals("Not enough space")) {
0286: throw new InstallationException(ResourceUtils
0287: .getString(Product.class,
0288: ERROR_NOT_ENOUGH_SPACE_KEY), e);
0289: }
0290: throw new InstallationException(ResourceUtils
0291: .getString(Product.class,
0292: ERROR_CANNOT_EXTRACT_DATA_KEY,
0293: getDisplayName()), e);
0294: } catch (XMLException e) {
0295: throw new InstallationException(ResourceUtils
0296: .getString(Product.class,
0297: ERROR_CANNOT_EXTRACT_DATA_KEY,
0298: getDisplayName()), e);
0299: }
0300:
0301: // finally remove the data file
0302: try {
0303: FileProxy.getInstance().deleteFile(uri);
0304: } catch (IOException e) {
0305: throw new InstallationException(ResourceUtils
0306: .getString(Product.class,
0307: ERROR_CANNOT_CLEAR_DATA_CACHE_KEY,
0308: dataFile), e);
0309: }
0310: }
0311:
0312: // create legal/docs artifacts
0313: progress.setDetail(StringUtils
0314: .format(MESSAGE_LEGAL_ARTIFACTS_STRING));
0315: try {
0316: saveLegalArtifacts();
0317: } catch (IOException e) {
0318: addInstallationWarning(e);
0319: }
0320:
0321: // check for cancel status
0322: if (progress.isCanceled())
0323: return;
0324:
0325: // custom configuration phase ///////////////////////////////////////////////
0326: installationPhase = InstallationPhase.CUSTOM_LOGIC;
0327:
0328: totalProgress.setTitle(StringUtils.format(
0329: MESSAGE_CONFIGURATION_STRING, getDisplayName()));
0330:
0331: // run custom configuration logic
0332: progress
0333: .setDetail(StringUtils.format(MESSAGE_RUN_LOGIC_STRING));
0334: configurationLogic.install(logicProgress);
0335: logicProgress.setPercentage(Progress.COMPLETE);
0336: progress.setDetail(StringUtils.EMPTY_STRING);
0337:
0338: // check for cancel status
0339: if (progress.isCanceled())
0340: return;
0341:
0342: // finalization phase ///////////////////////////////////////////////////////
0343: installationPhase = InstallationPhase.FINALIZATION;
0344:
0345: // register the component in the system install manager
0346: if (configurationLogic.registerInSystem()) {
0347: try {
0348: progress.setDetail(StringUtils
0349: .format(MESSAGE_SYSTEM_REGISTRATION_STRING));
0350: SystemUtils
0351: .addComponentToSystemInstallManager(getApplicationDescriptor());
0352: } catch (NativeException e) {
0353: throw new InstallationException(ResourceUtils
0354: .getString(Product.class,
0355: ERROR_SYSTEM_INTEGRATION_FAILER_KEY,
0356: getDisplayName()), e);
0357: }
0358: }
0359:
0360: // save the installed files list
0361: progress.setDetail(StringUtils
0362: .format(MESSAGE_SAVE_INSTALL_FILES_LIST_STRING));
0363: try {
0364: installedFiles.saveXmlGz(getInstalledFilesList());
0365: } catch (XMLException e) {
0366: throw new InstallationException(ResourceUtils.getString(
0367: Product.class, ERROR_CANNOT_SAVE_FILES_LIST_KEY), e);
0368: }
0369:
0370: installationPhase = InstallationPhase.COMPLETE;
0371: progress.setPercentage(Progress.COMPLETE);
0372: setStatus(Status.INSTALLED);
0373: }
0374:
0375: public void rollback(final Progress progress)
0376: throws UninstallationException {
0377: final CompositeProgress totalProgress = new CompositeProgress();
0378: final Progress logicProgress = new Progress();
0379: final Progress eraseProgress = new Progress();
0380:
0381: // initialization ///////////////////////////////////////////////////////////
0382:
0383: // load the component's configuration logic (it should be already
0384: // there, but we need to be sure)
0385: try {
0386: getLogic();
0387: } catch (InitializationException e) {
0388: throw new UninstallationException(ResourceUtils.getString(
0389: Product.class, ERROR_CANNOT_INITIALIZE_PRODUCT_KEY,
0390: getDisplayName()), e);
0391: }
0392:
0393: int logicChunk = (int) (progress.getPercentage() * ((float) configurationLogic
0394: .getLogicPercentage() / (float) Progress.COMPLETE));
0395: int eraseChunk = (int) (progress.getPercentage() * (1. - ((float) configurationLogic
0396: .getLogicPercentage() / (float) Progress.COMPLETE)));
0397:
0398: totalProgress.setPercentage(Progress.COMPLETE - logicChunk
0399: - eraseChunk);
0400: totalProgress.addChild(logicProgress, logicChunk);
0401: totalProgress.addChild(eraseProgress, eraseChunk);
0402: totalProgress.synchronizeDetails(true);
0403: totalProgress.reverseSynchronizeTo(progress);
0404:
0405: // rollback /////////////////////////////////////////////////////////////////
0406:
0407: // the starting point is chosen depending on the stage at which the
0408: // installation process was canceled, or failed; note that we intentionally
0409: // fall through all these cases, as they should be executed exactly in this
0410: // order and the only unclear point is where to start
0411: switch (installationPhase) {
0412: case COMPLETE:
0413: case FINALIZATION:
0414: try {
0415: FileUtils.deleteFile(getInstalledFilesList());
0416: } catch (IOException e) {
0417: ErrorManager.notifyWarning(ResourceUtils.getString(
0418: Product.class,
0419: ERROR_CANNOT_DELETE_FILES_LIST_KEY), e);
0420: }
0421:
0422: if (configurationLogic.registerInSystem()) {
0423: try {
0424: SystemUtils
0425: .removeComponentFromSystemInstallManager(getApplicationDescriptor());
0426: } catch (NativeException e) {
0427: ErrorManager.notifyWarning(ResourceUtils.getString(
0428: Product.class,
0429: ERROR_CANNOT_REMOVE_FROM_SYSTEM_KEY,
0430: getDisplayName()), e);
0431: }
0432: }
0433:
0434: case CUSTOM_LOGIC:
0435: configurationLogic.uninstall(logicProgress);
0436:
0437: case EXTRACTION:
0438: logicProgress.setPercentage(Progress.COMPLETE);
0439:
0440: // remove installation files
0441: int total = installedFiles.getSize();
0442: int current = 0;
0443:
0444: for (FileEntry entry : installedFiles) {
0445: current++;
0446:
0447: File file = entry.getFile();
0448:
0449: eraseProgress.setDetail(StringUtils.format(
0450: MESSAGE_DELETE_STRING, file));
0451: eraseProgress.setPercentage(Progress.COMPLETE * current
0452: / total);
0453:
0454: try {
0455: FileUtils.deleteFile(file);
0456: } catch (IOException e) {
0457: ErrorManager.notifyWarning(ResourceUtils
0458: .getString(Product.class,
0459: ERROR_CANNOT_DELETE_FILE_KEY), e);
0460: }
0461: }
0462:
0463: case INITIALIZATION:
0464: eraseProgress.setPercentage(Progress.COMPLETE);
0465: // for initialization we don't need to do anything
0466:
0467: default:
0468: // default, nothing should be done here
0469: }
0470: }
0471:
0472: public void uninstall(final Progress progress)
0473: throws UninstallationException {
0474: final CompositeProgress totalProgress = new CompositeProgress();
0475: final Progress logicProgress = new Progress();
0476: final Progress eraseProgress = new Progress();
0477:
0478: // initialization phase /////////////////////////////////////////////////////
0479:
0480: // load the component's configuration logic (it should be already
0481: // there, but we need to be sure)
0482: try {
0483: getLogic();
0484: } catch (InitializationException e) {
0485: throw new UninstallationException(ResourceUtils.getString(
0486: Product.class, ERROR_CANNOT_INITIALIZE_PRODUCT_KEY,
0487: getDisplayName()), e);
0488: }
0489:
0490: totalProgress.addChild(logicProgress, configurationLogic
0491: .getLogicPercentage());
0492: totalProgress.addChild(eraseProgress, Progress.COMPLETE
0493: - configurationLogic.getLogicPercentage());
0494: totalProgress.synchronizeTo(progress);
0495: totalProgress.synchronizeDetails(true);
0496:
0497: // load the installed files list
0498: try {
0499: installedFiles = new FilesList()
0500: .loadXmlGz(getInstalledFilesList());
0501: } catch (XMLException e) {
0502: throw new UninstallationException(ResourceUtils.getString(
0503: Product.class, ERROR_CANNOT_GET_FILES_LIST_KEY), e);
0504: }
0505:
0506: // custom logic phase ///////////////////////////////////////////////////////
0507: progress.setTitle(StringUtils.format(
0508: MESSAGE_UNCONFIGURATION_STRING, getDisplayName()));
0509:
0510: // run custom unconfiguration logic
0511: configurationLogic.uninstall(logicProgress);
0512: logicProgress.setPercentage(Progress.COMPLETE);
0513: progress.setDetail(StringUtils.EMPTY_STRING);
0514:
0515: // files deletion phase /////////////////////////////////////////////////////
0516: progress.setTitle(StringUtils.format(
0517: MESSAGE_UNINSTALLATION_STRING, getDisplayName()));
0518:
0519: // remove installation files
0520: if (configurationLogic.getRemovalMode() == RemovalMode.ALL) {
0521: try {
0522: File startPoint = getInstallationLocation();
0523: if (SystemUtils.isMacOS()
0524: && configurationLogic.wrapForMacOs()) {
0525: startPoint = startPoint.getParentFile()
0526: .getParentFile().getParentFile();
0527: }
0528: FileUtils.deleteFile(startPoint, true, eraseProgress);
0529: } catch (IOException e) {
0530: addUninstallationWarning(new UninstallationException(
0531: ResourceUtils.getString(Product.class,
0532: ERROR_CANNOT_DELETE_FILE_KEY), e));
0533: }
0534: } else {
0535: try {
0536: FileUtils.deleteFiles(installedFiles, eraseProgress);
0537: } catch (IOException e) {
0538: addUninstallationWarning(new UninstallationException(
0539: ResourceUtils.getString(Product.class,
0540: ERROR_CANNOT_DELETE_FILE_KEY), e));
0541: }
0542: }
0543:
0544: // remove the component from the native install manager
0545: if (configurationLogic.registerInSystem()) {
0546: try {
0547: SystemUtils
0548: .removeComponentFromSystemInstallManager(getApplicationDescriptor());
0549: } catch (NativeException e) {
0550: addUninstallationWarning(new UninstallationException(
0551: ResourceUtils.getString(Product.class,
0552: ERROR_CANNOT_REMOVE_FROM_SYSTEM_KEY,
0553: getDisplayName()), e));
0554: }
0555: }
0556:
0557: progress.setDetail(StringUtils.EMPTY_STRING);
0558: // remove the files list
0559: try {
0560: FileUtils.deleteFile(getInstalledFilesList());
0561: } catch (IOException e) {
0562: addUninstallationWarning(new UninstallationException(
0563: ResourceUtils.getString(Product.class,
0564: ERROR_CANNOT_DELETE_FILES_LIST_KEY), e));
0565: }
0566:
0567: progress.setPercentage(Progress.COMPLETE);
0568: setStatus(Status.NOT_INSTALLED);
0569: }
0570:
0571: // configuration logic //////////////////////////////////////////////////////////
0572: public List<ExtendedUri> getLogicUris() {
0573: return logicUris;
0574: }
0575:
0576: public void downloadLogic(final Progress progress)
0577: throws DownloadException {
0578: final CompositeProgress overallProgress = new CompositeProgress();
0579:
0580: final int percentageChunk = Progress.COMPLETE
0581: / logicUris.size();
0582: final int percentageLeak = Progress.COMPLETE % logicUris.size();
0583:
0584: overallProgress.setPercentage(percentageLeak);
0585: overallProgress.synchronizeTo(progress);
0586: overallProgress.synchronizeDetails(true);
0587:
0588: for (ExtendedUri uri : logicUris) {
0589: final Progress currentProgress = new Progress();
0590: overallProgress.addChild(currentProgress, percentageChunk);
0591:
0592: final File cache = FileProxy.getInstance().getFile(
0593: uri.getRemote(), currentProgress);
0594: uri.setLocal(cache.toURI());
0595: }
0596: }
0597:
0598: public boolean isLogicDownloaded() {
0599: for (ExtendedUri uri : logicUris) {
0600: if (uri.getLocal() == null) {
0601: return false;
0602: }
0603: }
0604:
0605: return true;
0606: }
0607:
0608: public ProductConfigurationLogic getLogic()
0609: throws InitializationException {
0610: if (configurationLogic != null) {
0611: return configurationLogic;
0612: }
0613:
0614: if (!isLogicDownloaded()) {
0615: throw new InitializationException(ResourceUtils.getString(
0616: Product.class, ERROR_LOGIC_NOT_YET_DOWNLOADED_KEY,
0617: getDisplayName()));
0618: }
0619:
0620: try {
0621: String classname = null;
0622: for (ExtendedUri uri : logicUris) {
0623: classname = FileUtils.getJarAttribute(new File(uri
0624: .getLocal()), MANIFEST_LOGIC_CLASS);
0625:
0626: if (classname != null) {
0627: break;
0628: }
0629: }
0630:
0631: classLoader = new NbiClassLoader(logicUris);
0632:
0633: configurationLogic = (ProductConfigurationLogic) classLoader
0634: .loadClass(classname).newInstance();
0635: configurationLogic.setProduct(this );
0636:
0637: return configurationLogic;
0638: } catch (IOException e) {
0639: throw new InitializationException(ResourceUtils.getString(
0640: Product.class, ERROR_CANNOT_LOAD_LOGIC_KEY,
0641: getDisplayName()), e);
0642: } catch (ClassNotFoundException e) {
0643: throw new InitializationException(ResourceUtils.getString(
0644: Product.class, ERROR_CANNOT_LOAD_LOGIC_KEY,
0645: getDisplayName()), e);
0646: } catch (InstantiationException e) {
0647: throw new InitializationException(ResourceUtils.getString(
0648: Product.class, ERROR_CANNOT_LOAD_LOGIC_KEY,
0649: getDisplayName()), e);
0650: } catch (IllegalAccessException e) {
0651: throw new InitializationException(ResourceUtils.getString(
0652: Product.class, ERROR_CANNOT_LOAD_LOGIC_KEY,
0653: getDisplayName()), e);
0654: }
0655: }
0656:
0657: // installation data ////////////////////////////////////////////////////////////
0658: public List<ExtendedUri> getDataUris() {
0659: return dataUris;
0660: }
0661:
0662: public void downloadData(final Progress progress)
0663: throws DownloadException {
0664: final CompositeProgress overallProgress = new CompositeProgress();
0665:
0666: final int percentageChunk = Progress.COMPLETE / dataUris.size();
0667: final int percentageLeak = Progress.COMPLETE % dataUris.size();
0668:
0669: overallProgress.setPercentage(percentageLeak);
0670: overallProgress.synchronizeTo(progress);
0671: overallProgress.synchronizeDetails(true);
0672:
0673: for (ExtendedUri uri : dataUris) {
0674: final Progress currentProgress = new Progress();
0675: overallProgress.addChild(currentProgress, percentageChunk);
0676:
0677: final File cache = FileProxy.getInstance().getFile(
0678: uri.getRemote(), currentProgress);
0679: uri.setLocal(cache.toURI());
0680: }
0681: }
0682:
0683: public boolean isDataDownloaded() {
0684: for (ExtendedUri uri : dataUris) {
0685: if (uri.getLocal() == null) {
0686: return false;
0687: }
0688: }
0689:
0690: return true;
0691: }
0692:
0693: // wizard ///////////////////////////////////////////////////////////////////////
0694: public List<WizardComponent> getWizardComponents() {
0695: try {
0696: return getLogic().getWizardComponents();
0697: } catch (InitializationException e) {
0698: ErrorManager.notifyError(ResourceUtils.getString(
0699: Product.class,
0700: ERROR_CANNOT_GET_WIZARD_COMPONENTS_KEY), e);
0701: }
0702:
0703: return null;
0704: }
0705:
0706: // status ///////////////////////////////////////////////////////////////////////
0707: public Status getStatus() {
0708: return currentStatus;
0709: }
0710:
0711: public void setStatus(final Status status) {
0712: if (initialStatus == null) {
0713: initialStatus = status;
0714: }
0715:
0716: currentStatus = status;
0717: }
0718:
0719: public boolean hasStatusChanged() {
0720: return currentStatus != initialStatus;
0721: }
0722:
0723: public DetailedStatus getDetailedStatus() {
0724: if (getStatus() == Status.INSTALLED) {
0725: if (getUninstallationError() != null) {
0726: return DetailedStatus.FAILED_TO_UNINSTALL;
0727: }
0728: if (hasStatusChanged()
0729: && (getInstallationWarnings() != null)) {
0730: return DetailedStatus.INSTALLED_WITH_WARNINGS;
0731: }
0732: if (hasStatusChanged()) {
0733: return DetailedStatus.INSTALLED_SUCCESSFULLY;
0734: }
0735: }
0736:
0737: if (getStatus() == Status.NOT_INSTALLED) {
0738: if (getInstallationError() != null) {
0739: return DetailedStatus.FAILED_TO_INSTALL;
0740: }
0741: if (hasStatusChanged()
0742: && (getUninstallationWarnings() != null)) {
0743: return DetailedStatus.UNINSTALLED_WITH_WARNINGS;
0744: }
0745: if (hasStatusChanged()) {
0746: return DetailedStatus.UNINSTALLED_SUCCESSFULLY;
0747: }
0748: }
0749:
0750: return null;
0751: }
0752:
0753: // dependencies /////////////////////////////////////////////////////////////////
0754: public List<Dependency> getDependencies() {
0755: return dependencies;
0756: }
0757:
0758: @Deprecated
0759: public List<Dependency> getDependencies(
0760: final DependencyType... types) {
0761: Class[] classes = new Class[types.length];
0762: for (int i = 0; i < types.length; i++) {
0763: classes[i] = toDependencyClass(types[i]);
0764: }
0765: return getDependencies(classes);
0766: }
0767:
0768: @Deprecated
0769: private Class<? extends Dependency> toDependencyClass(
0770: DependencyType type) {
0771: switch (type) {
0772: case REQUIREMENT:
0773: return Requirement.class;
0774: case CONFLICT:
0775: return Conflict.class;
0776: case INSTALL_AFTER:
0777: return InstallAfter.class;
0778: default:
0779: return null;
0780: }
0781: }
0782:
0783: public List<Dependency> getDependencies(Class... dependencyClasses) {
0784: final List<Dependency> filtered = new ArrayList<Dependency>();
0785:
0786: for (Dependency dependency : dependencies) {
0787: for (Class clazz : dependencyClasses) {
0788: //if (clazz.isInstance(dependency)) {
0789: if (clazz.isInstance(dependency)) {
0790: filtered.add(dependency);
0791: break;
0792: }
0793: }
0794: }
0795:
0796: return filtered;
0797: }
0798:
0799: public boolean satisfies(final Dependency dependency) {
0800: return dependency.satisfies(this );
0801: }
0802:
0803: public List<Dependency> getDependencyByUid(String dependentUid) {
0804: final List<Dependency> filtered = new ArrayList<Dependency>();
0805:
0806: for (Dependency dependency : dependencies) {
0807: if (dependency.getUid().equals(dependentUid)) {
0808: filtered.add(dependency);
0809: }
0810: }
0811:
0812: return filtered;
0813: }
0814:
0815: // system requirements //////////////////////////////////////////////////////////
0816: public long getRequiredDiskSpace() {
0817: return requiredDiskSpace;
0818: }
0819:
0820: // install-time error/warnings //////////////////////////////////////////////////
0821: public Throwable getInstallationError() {
0822: return installationError;
0823: }
0824:
0825: public void setInstallationError(final Throwable error) {
0826: installationError = error;
0827: }
0828:
0829: public List<Throwable> getInstallationWarnings() {
0830: return installationWarnings;
0831: }
0832:
0833: public void addInstallationWarning(final Throwable warning) {
0834: if (installationWarnings == null) {
0835: installationWarnings = new ArrayList<Throwable>();
0836: }
0837:
0838: installationWarnings.add(warning);
0839: }
0840:
0841: // uninstall-time error/warnings ////////////////////////////////////////////////
0842: public Throwable getUninstallationError() {
0843: return uninstallationError;
0844: }
0845:
0846: public void setUninstallationError(final Throwable error) {
0847: uninstallationError = error;
0848: }
0849:
0850: public List<Throwable> getUninstallationWarnings() {
0851: return uninstallationWarnings;
0852: }
0853:
0854: public void addUninstallationWarning(final Throwable warning) {
0855: if (uninstallationWarnings == null) {
0856: uninstallationWarnings = new ArrayList<Throwable>();
0857: }
0858:
0859: uninstallationWarnings.add(warning);
0860: }
0861:
0862: // node <-> dom /////////////////////////////////////////////////////////////////
0863: protected String getTagName() {
0864: return PRODUCT_TAG_NAME;
0865: }
0866:
0867: public Element saveToDom(final Element element)
0868: throws FinalizationException {
0869: super .saveToDom(element);
0870:
0871: final Document document = element.getOwnerDocument();
0872:
0873: element.setAttribute(VERSION_TAG_NAME, version.toString());
0874: element.setAttribute(PLATFORMS_TAG_NAME, StringUtils.asString(
0875: supportedPlatforms, StringUtils.SPACE));
0876: element.setAttribute(STATUS_TAG_NAME, currentStatus.toString());
0877: element.setAttribute(FEATURES_TAG_NAME, StringUtils.asString(
0878: features, StringUtils.SPACE));
0879:
0880: element.appendChild(XMLUtils.saveExtendedUrisList(logicUris,
0881: document.createElement(CONFIGURATION_LOGIC_TAG_NAME)));//NOI18N
0882:
0883: element.appendChild(XMLUtils.saveExtendedUrisList(dataUris,
0884: document.createElement(INSTALLATION_DATA_TAG_NAME)));//NOI18N
0885:
0886: final Element systemRequirementsElement = document
0887: .createElement(SYSTEM_REQUIREMENTS_TAG_NAME);//NOI18N
0888:
0889: final Element diskSpaceElement = document
0890: .createElement(DISK_SPACE_TAG_NAME);//NOI18N
0891: diskSpaceElement.setTextContent(Long
0892: .toString(requiredDiskSpace));
0893: systemRequirementsElement.appendChild(diskSpaceElement);
0894:
0895: element.appendChild(systemRequirementsElement);
0896:
0897: if (dependencies.size() > 0) {
0898: element.appendChild(XMLUtils.saveDependencies(dependencies,
0899: document.createElement(DEPENDENCIES_TAG_NAME)));//NOI18N
0900: }
0901:
0902: return element;
0903: }
0904:
0905: public Product loadFromDom(final Element element)
0906: throws InitializationException {
0907:
0908: super .loadFromDom(element);
0909:
0910: Element child;
0911:
0912: try {
0913: version = Version.getVersion(element
0914: .getAttribute(VERSION_TAG_NAME));
0915: supportedPlatforms = StringUtils.parsePlatforms(element
0916: .getAttribute(PLATFORMS_TAG_NAME));
0917:
0918: initialStatus = StringUtils.parseStatus(element
0919: .getAttribute(STATUS_TAG_NAME));
0920: currentStatus = initialStatus;
0921:
0922: features = StringUtils
0923: .asList(element.getAttribute(FEATURES_TAG_NAME),
0924: StringUtils.SPACE);
0925:
0926: logicUris.addAll(XMLUtils.parseExtendedUrisList(XMLUtils
0927: .getChild(element, CONFIGURATION_LOGIC_TAG_NAME)));
0928:
0929: dataUris.addAll(XMLUtils.parseExtendedUrisList(XMLUtils
0930: .getChild(element, INSTALLATION_DATA_TAG_NAME)));
0931:
0932: requiredDiskSpace = Long.parseLong(XMLUtils.getChild(
0933: element,
0934: SYSTEM_REQUIREMENTS_TAG_NAME + "/"
0935: + DISK_SPACE_TAG_NAME).getTextContent());
0936:
0937: child = XMLUtils.getChild(element, DEPENDENCIES_TAG_NAME);
0938: if (child != null) {
0939: dependencies.addAll(XMLUtils.parseDependencies(child));
0940: }
0941: } catch (ParseException e) {
0942: throw new InitializationException(ResourceUtils.getString(
0943: Product.class, ERROR_CANNOT_LOAD_PRODUCT_KEY,
0944: getDisplayName()), e);
0945: }
0946:
0947: return this ;
0948: }
0949:
0950: // essential getters/setters ////////////////////////////////////////////////////
0951: public Version getVersion() {
0952: return version;
0953: }
0954:
0955: public List<Platform> getPlatforms() {
0956: return supportedPlatforms;
0957: }
0958:
0959: public List<String> getFeatures() {
0960: return features;
0961: }
0962:
0963: public File getInstallationLocation() {
0964: final String path = SystemUtils.resolveString(
0965: getProperty(INSTALLATION_LOCATION_PROPERTY),
0966: getClassLoader());
0967:
0968: return path == null ? null : new File(path);
0969: }
0970:
0971: public void setInstallationLocation(final File location) {
0972: setProperty(INSTALLATION_LOCATION_PROPERTY, location
0973: .getAbsolutePath());
0974: }
0975:
0976: public File getLocalCache() {
0977: return new File(Registry.getInstance().getLocalProductCache(),
0978: uid + File.separator + version);
0979: }
0980:
0981: public FilesList getInstalledFiles() {
0982: return installedFiles;
0983: }
0984:
0985: public File getInstalledFilesList() {
0986: return new File(getLocalCache(), INSTALLED_FILES_LIST_FILE_NAME);
0987: }
0988:
0989: public ClassLoader getClassLoader() {
0990: return classLoader;
0991: }
0992:
0993: public long getDownloadSize() {
0994: long downloadSize = 0;
0995:
0996: for (ExtendedUri uri : logicUris) {
0997: downloadSize += uri.getSize();
0998: }
0999: for (ExtendedUri uri : dataUris) {
1000: downloadSize += uri.getSize();
1001: }
1002:
1003: return downloadSize;
1004: }
1005:
1006: private ApplicationDescriptor getApplicationDescriptor() {
1007: final String key = "nbi-" + uid + "-" + version;
1008: final String displayName = configurationLogic
1009: .getSystemDisplayName();
1010: final String icon;
1011: if (configurationLogic.getIcon() != null) {
1012: icon = new File(getInstallationLocation(),
1013: configurationLogic.getIcon()).getAbsolutePath();
1014: } else {
1015: icon = null;
1016: }
1017:
1018: String installLocation = getInstallationLocation()
1019: .getAbsolutePath();
1020: if (SystemUtils.isMacOS() && configurationLogic.wrapForMacOs()) {
1021: final String applicationLocation = getProperty(APPLICATION_LOCATION_PROPERTY);
1022:
1023: if (applicationLocation != null) {
1024: installLocation = applicationLocation;
1025: }
1026: }
1027:
1028: final String[] modifyCommand = new String[] {
1029: Installer.TARGET_ARG, uid, version.toString() };
1030:
1031: final String[] uninstallCommand = new String[] {
1032: Installer.TARGET_ARG, uid, version.toString(),
1033: Installer.FORCE_UNINSTALL_ARG };
1034:
1035: if (configurationLogic.allowModifyMode()) {
1036: return new ApplicationDescriptor(key, displayName, icon,
1037: installLocation, uninstallCommand, modifyCommand);
1038: } else {
1039: return new ApplicationDescriptor(key, displayName, icon,
1040: installLocation, uninstallCommand, null);
1041: }
1042: }
1043:
1044: // miscellanea //////////////////////////////////////////////////////////////////
1045: public boolean isCompatibleWith(final Platform platform) {
1046: for (Platform compatiblePlatform : supportedPlatforms) {
1047: if (compatiblePlatform.isCompatibleWith(platform)) {
1048: return true;
1049: }
1050: }
1051:
1052: return false;
1053: }
1054:
1055: private void saveLegalArtifacts() throws IOException {
1056: final Text license = configurationLogic.getLicense();
1057: if (license != null) {
1058: final File file = new File(getInstallationLocation(),
1059: "LICENSE-" + uid
1060: + license.getContentType().getExtension());
1061:
1062: FileUtils.writeFile(file, license.getText());
1063: installedFiles.add(file);
1064: }
1065:
1066: final Map<String, Text> thirdPartyLicenses = configurationLogic
1067: .getThirdPartyLicenses();
1068: if (thirdPartyLicenses != null) {
1069: final File file = new File(getInstallationLocation(),
1070: "THIRDPARTYLICENSES-" + uid + ".txt");
1071:
1072: for (String title : thirdPartyLicenses.keySet()) {
1073: FileUtils
1074: .appendFile(
1075: file,
1076: "%% The following software may be included in this product: "
1077: + title
1078: + ";\n"
1079: + "Use of any of this software is governed by the terms of the license below:\n\n");
1080: FileUtils.appendFile(file, thirdPartyLicenses
1081: .get(title).getText()
1082: + "\n\n");
1083: }
1084:
1085: installedFiles.add(file);
1086: }
1087:
1088: final Text thirdPartyLicense = configurationLogic
1089: .getThirdPartyLicense();
1090: if (thirdPartyLicense != null) {
1091: final File file = new File(getInstallationLocation(),
1092: "THIRDPARTYLICENSE-"
1093: + uid
1094: + thirdPartyLicense.getContentType()
1095: .getExtension());
1096:
1097: FileUtils.writeFile(file, thirdPartyLicense.getText());
1098: installedFiles.add(file);
1099: }
1100:
1101: final Text releaseNotes = configurationLogic.getReleaseNotes();
1102: if (releaseNotes != null) {
1103: final File file = new File(getInstallationLocation(),
1104: "RELEASENOTES-"
1105: + uid
1106: + releaseNotes.getContentType()
1107: .getExtension());
1108:
1109: FileUtils.writeFile(file, releaseNotes.getText());
1110: installedFiles.add(file);
1111: }
1112:
1113: final Text readme = configurationLogic.getReadme();
1114: if (readme != null) {
1115: final File file = new File(getInstallationLocation(),
1116: "README-" + uid
1117: + readme.getContentType().getExtension());
1118:
1119: FileUtils.writeFile(file, readme.getText());
1120: installedFiles.add(file);
1121: }
1122:
1123: final Text distributionReadme = configurationLogic
1124: .getDistributionReadme();
1125: if (distributionReadme != null) {
1126: final File file = new File(getInstallationLocation(),
1127: "DISTRIBUTION-"
1128: + uid
1129: + distributionReadme.getContentType()
1130: .getExtension());
1131:
1132: FileUtils.writeFile(file, distributionReadme.getText());
1133: installedFiles.add(file);
1134: }
1135: }
1136:
1137: /////////////////////////////////////////////////////////////////////////////////
1138: // Inner Classes
1139: private static enum InstallationPhase {
1140: INITIALIZATION, EXTRACTION, CUSTOM_LOGIC, FINALIZATION, COMPLETE;
1141: }
1142:
1143: /////////////////////////////////////////////////////////////////////////////////
1144: // Constants
1145: public static final String INSTALLATION_LOCATION_PROPERTY = "installation.location"; // NOI18N
1146:
1147: public static final String INSTALLED_FILES_LIST_FILE_NAME = "installed-files.xml.gz"; // NOI18N
1148:
1149: public static final String MANIFEST_LOGIC_CLASS = "Configuration-Logic-Class"; // NOI18N
1150:
1151: public static final String INFO_PLIST_STUB = FileUtils.INFO_PLIST_STUB;
1152:
1153: private static final String APPLICATION_LOCATION_PROPERTY = "application.location";
1154:
1155: private static final String VERSION_TAG_NAME = "version";//NOI18N
1156: private static final String PLATFORMS_TAG_NAME = "platforms";//NOI18N
1157: private static final String STATUS_TAG_NAME = "status";//NOI18N
1158: private static final String FEATURES_TAG_NAME = "features";//NOI18N
1159: private static final String CONFIGURATION_LOGIC_TAG_NAME = "configuration-logic";//NOI18N
1160: private static final String INSTALLATION_DATA_TAG_NAME = "installation-data";//NOI18N
1161: private static final String DEPENDENCIES_TAG_NAME = "dependencies";//NOI18N
1162: private static final String PRODUCT_TAG_NAME = "product";//NOI18N
1163: private static final String SYSTEM_REQUIREMENTS_TAG_NAME = "system-requirements";//NOI18N
1164: private static final String DISK_SPACE_TAG_NAME = "disk-space";//NOI18N
1165:
1166: private static final String ERROR_CANNOT_INITIALIZE_PRODUCT_KEY = "P.error.cannot.initialize.product";//NOI18N
1167: private static final String ERROR_CANNOT_LOAD_LOGIC_KEY = "P.error.cannot.load.logic";//NOI18N
1168: private static final String ERROR_INSTALLATION_LOCATION_NOT_SET_KEY = "P.error.installdir.not.set";//NOI18N
1169: private static final String ERROR_INSTALLATION_LOCATION_SET_EMPTY_KEY = "P.error.installdir.set.empty";//NOI18N
1170: private static final String ERROR_CANNOT_CREATE_PRODUCT_CACHE_DIR_KEY = "P.error.cannot.create.cache.dir";//NOI18N
1171: private static final String ERROR_CACHE_NOT_DIRECTORY_KEY = "P.error.local.cache.not.dir";//NOI18N
1172: private static final String ERROR_CANNOT_LOAD_PRODUCT_KEY = "P.error.cannot.load.product";//NOI18N
1173: private static final String ERROR_LOGIC_NOT_YET_DOWNLOADED_KEY = "P.error.logic.not.yet.downloaded";//NOI18N
1174: private static final String ERROR_DATA_NOT_CACHED_KEY = "P.error.installation.data.not.cached";//NOI18N
1175: private static final String ERROR_CANNOT_EXTRACT_DATA_KEY = "P.error.cannot.extract.data";//NOI18N
1176: private static final String ERROR_CANNOT_SAVE_FILES_LIST_KEY = "P.error.cannot.save.files.list";//NOI18N
1177: private static final String ERROR_CANNOT_GET_WIZARD_COMPONENTS_KEY = "P.error.cannot.get.wizard.components";//NOI18N
1178: private static final String ERROR_CANNOT_WRAP_FOR_MACOS_KEY = "P.error.cannot.wrap.for.macos";//NOI18N
1179: private static final String ERROR_CANNOT_GET_FILES_LIST_KEY = "P.error.cannot.get.files.list";//NOI18N
1180: private static final String ERROR_CANNOT_DELETE_FILES_LIST_KEY = "P.error.cannot.delete.files.list";//NOI18N
1181: private static final String ERROR_CANNOT_DELETE_FILE_KEY = "P.error.cannot.delete.file";//NOI18N
1182: private static final String ERROR_CANNOT_REMOVE_FROM_SYSTEM_KEY = "P.error.cannot.remove.from.system";//NOI18N
1183: private static final String ERROR_NOT_ENOUGH_SPACE_KEY = "P.error.not.enough.space";//NOI18N
1184: private static final String ERROR_CANNOT_CLEAR_DATA_CACHE_KEY = "P.error.cannot.clear.cache";//NOI18N
1185: private static final String ERROR_SYSTEM_INTEGRATION_FAILER_KEY = "P.error.system.integartion.failed";//NOI18N
1186:
1187: private static final String MESSAGE_INSTALLATION_STRING = ResourceUtils
1188: .getString(Product.class, "P.message.installation");//NOI18N
1189: private static final String MESSAGE_UNINSTALLATION_STRING = ResourceUtils
1190: .getString(Product.class, "P.message.uninstallation");//NOI18N
1191: private static final String MESSAGE_CONFIGURATION_STRING = ResourceUtils
1192: .getString(Product.class, "P.message.configuration");//NOI18N
1193: private static final String MESSAGE_UNCONFIGURATION_STRING = ResourceUtils
1194: .getString(Product.class, "P.message.unconfiguration");//NOI18N
1195: private static final String MESSAGE_LEGAL_ARTIFACTS_STRING = ResourceUtils
1196: .getString(Product.class, "P.message.legal.artifacts");//NOI18N
1197: private static final String MESSAGE_RUN_LOGIC_STRING = ResourceUtils
1198: .getString(Product.class, "P.message.run.logic");//NOI18N
1199: private static final String MESSAGE_SYSTEM_REGISTRATION_STRING = ResourceUtils
1200: .getString(Product.class, "P.message.system.registration");//NOI18N
1201: private static final String MESSAGE_SAVE_INSTALL_FILES_LIST_STRING = ResourceUtils
1202: .getString(Product.class,
1203: "P.message.save.installation.files.list");//NOI18N
1204: private static final String MESSAGE_DELETE_STRING = ResourceUtils
1205: .getString(Product.class, "P.message.delete");//NOI18N
1206: }
|