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;
0038:
0039: import java.io.File;
0040: import java.io.IOException;
0041: import java.util.ArrayList;
0042: import java.util.HashSet;
0043: import java.util.LinkedList;
0044: import java.util.List;
0045: import java.util.Properties;
0046: import java.util.Queue;
0047: import java.util.Set;
0048: import javax.xml.XMLConstants;
0049: import javax.xml.parsers.DocumentBuilder;
0050: import javax.xml.parsers.DocumentBuilderFactory;
0051: import javax.xml.parsers.ParserConfigurationException;
0052: import javax.xml.validation.Schema;
0053: import javax.xml.validation.SchemaFactory;
0054: import org.netbeans.installer.product.components.Group;
0055: import org.netbeans.installer.product.components.Product;
0056: import org.netbeans.installer.product.dependencies.Conflict;
0057: import org.netbeans.installer.product.dependencies.InstallAfter;
0058: import org.netbeans.installer.product.dependencies.Requirement;
0059: import org.netbeans.installer.product.filters.OrFilter;
0060: import org.netbeans.installer.product.filters.ProductFilter;
0061: import org.netbeans.installer.product.filters.GroupFilter;
0062: import org.netbeans.installer.product.filters.RegistryFilter;
0063: import org.netbeans.installer.product.filters.TrueFilter;
0064: import org.netbeans.installer.utils.ResourceUtils;
0065: import org.netbeans.installer.utils.UiUtils;
0066: import org.netbeans.installer.utils.helper.DetailedStatus;
0067: import org.netbeans.installer.utils.helper.ExtendedUri;
0068: import org.netbeans.installer.utils.helper.Status;
0069: import org.netbeans.installer.utils.ErrorManager;
0070: import org.netbeans.installer.utils.FileProxy;
0071: import org.netbeans.installer.utils.FileUtils;
0072: import org.netbeans.installer.utils.helper.Dependency;
0073: import org.netbeans.installer.utils.helper.ErrorLevel;
0074: import org.netbeans.installer.utils.StringUtils;
0075: import org.netbeans.installer.utils.SystemUtils;
0076: import org.netbeans.installer.utils.XMLUtils;
0077: import org.netbeans.installer.utils.exceptions.DownloadException;
0078: import org.netbeans.installer.utils.exceptions.FinalizationException;
0079: import org.netbeans.installer.utils.exceptions.InitializationException;
0080: import org.netbeans.installer.utils.exceptions.ParseException;
0081: import org.netbeans.installer.utils.LogManager;
0082: import org.netbeans.installer.utils.helper.Platform;
0083: import org.netbeans.installer.utils.exceptions.XMLException;
0084: import org.netbeans.installer.utils.helper.ExecutionMode;
0085: import org.netbeans.installer.utils.helper.Feature;
0086: import org.netbeans.installer.utils.helper.FinishHandler;
0087: import org.netbeans.installer.utils.helper.NbiProperties;
0088: import org.netbeans.installer.utils.helper.PropertyContainer;
0089: import org.netbeans.installer.utils.helper.Version;
0090: import org.netbeans.installer.utils.progress.CompositeProgress;
0091: import org.netbeans.installer.utils.progress.Progress;
0092: import org.w3c.dom.Document;
0093: import org.w3c.dom.Element;
0094: import org.xml.sax.SAXException;
0095:
0096: /**
0097: *
0098: * @author Kirill Sorokin
0099: */
0100: public class Registry implements PropertyContainer {
0101: /////////////////////////////////////////////////////////////////////////////////
0102: // Static
0103: private static Registry instance;
0104:
0105: public static synchronized Registry getInstance() {
0106: if (instance == null) {
0107: instance = new Registry();
0108: }
0109:
0110: return instance;
0111: }
0112:
0113: /////////////////////////////////////////////////////////////////////////////////
0114: // Instance
0115: private File localDirectory;
0116: private File localRegistryFile;
0117: private File localProductCache;
0118:
0119: private String localRegistryStubUri;
0120: private String bundledRegistryUri;
0121: private List<String> remoteRegistryUris;
0122: private String registrySchemaUri;
0123:
0124: private String stateFileSchemaUri;
0125: private String stateFileStubUri;
0126:
0127: private List<Feature> features;
0128:
0129: private List<ExtendedUri> includes;
0130:
0131: private RegistryNode registryRoot;
0132: private NbiProperties properties;
0133: private Platform targetPlatform;
0134:
0135: private FinishHandler finishHandler;
0136:
0137: // constructors /////////////////////////////////////////////////////////////////
0138: public Registry() {
0139: localRegistryStubUri = DEFAULT_LOCAL_PRODUCT_REGISTRY_STUB_URI;
0140: bundledRegistryUri = DEFAULT_BUNDLED_PRODUCT_REGISTRY_URI;
0141: remoteRegistryUris = new ArrayList<String>();
0142: registrySchemaUri = DEFAULT_PRODUCT_REGISTRY_SCHEMA_URI;
0143:
0144: stateFileSchemaUri = DEFAULT_STATE_FILE_SCHEMA_URI;
0145: stateFileStubUri = DEFAULT_STATE_FILE_STUB_URI;
0146:
0147: features = new LinkedList<Feature>();
0148:
0149: includes = new LinkedList<ExtendedUri>();
0150:
0151: registryRoot = new Group();
0152: registryRoot.setRegistryType(RegistryType.LOCAL);
0153:
0154: properties = new NbiProperties();
0155: targetPlatform = SystemUtils.getCurrentPlatform();
0156: }
0157:
0158: // initialization/finalization //////////////////////////////////////////////////
0159: public void setLocalDirectory(final File localDirectory) {
0160: this .localDirectory = localDirectory;
0161:
0162: localProductCache = new File(localDirectory,
0163: DEFAULT_LOCAL_PRODUCT_CACHE_DIRECTORY_NAME);
0164: localRegistryFile = new File(localDirectory,
0165: DEFAULT_LOCAL_REGISTRY_FILE_NAME);
0166: }
0167:
0168: public void setFinishHandler(final FinishHandler finishHandler) {
0169: this .finishHandler = finishHandler;
0170: }
0171:
0172: public void setTargetPlatform(final Platform targetPlatform) {
0173: this .targetPlatform = targetPlatform;
0174: }
0175:
0176: public void initializeRegistry(final Progress progress)
0177: throws InitializationException {
0178: LogManager.logEntry("initializing product registry");
0179:
0180: setRegistryProperties();
0181:
0182: final CompositeProgress compositeProgress = new CompositeProgress();
0183: Progress childProgress;
0184:
0185: int percentageChunk = Progress.COMPLETE
0186: / (remoteRegistryUris.size() + 2);
0187: int percentageLeak = Progress.COMPLETE
0188: % (remoteRegistryUris.size() + 2);
0189:
0190: compositeProgress.synchronizeTo(progress);
0191: compositeProgress.synchronizeDetails(true);
0192:
0193: childProgress = new Progress();
0194: compositeProgress.addChild(childProgress, percentageChunk
0195: + percentageLeak);
0196: compositeProgress.setTitle(ResourceUtils.getString(
0197: Registry.class, LOADING_LOCAL_REGISTRY_KEY,
0198: localRegistryFile));
0199:
0200: try {
0201: loadProductRegistry(localRegistryFile.toURI().toString(),
0202: childProgress, RegistryType.LOCAL, false);
0203: } catch (InitializationException e) {
0204: if (!UiUtils.showYesNoDialog(ResourceUtils.getString(
0205: Registry.class,
0206: ERROR_LOADING_LOCAL_REGISTRY_TITLE_KEY),
0207: ResourceUtils.getString(Registry.class,
0208: ERROR_LOADING_LOCAL_REGISTRY_MESSAGE_KEY,
0209: localRegistryFile))) {
0210: finishHandler.criticalExit();
0211: } else {
0212: LogManager.log(ErrorLevel.ERROR, e);
0213: }
0214: }
0215:
0216: childProgress = new Progress();
0217: compositeProgress.addChild(childProgress, percentageChunk);
0218: compositeProgress.setTitle(ResourceUtils.getString(
0219: Registry.class, LOADING_BUNDLED_REGISTRY_KEY,
0220: bundledRegistryUri));
0221:
0222: try {
0223: loadProductRegistry(bundledRegistryUri, childProgress,
0224: RegistryType.BUNDLED, true);
0225: } catch (InitializationException e) {
0226: if (!UiUtils.showYesNoDialog(ResourceUtils.getString(
0227: Registry.class,
0228: ERROR_LOADING_BUNDLED_REGISTRY_TITLE_KEY),
0229: ResourceUtils.getString(Registry.class,
0230: ERROR_LOADING_BUNDLED_REGISTRY_MESSAGE_KEY,
0231: bundledRegistryUri))) {
0232: finishHandler.criticalExit();
0233: } else {
0234: LogManager.log(ErrorLevel.ERROR, e);
0235: }
0236: }
0237:
0238: for (String remoteRegistryURI : remoteRegistryUris) {
0239: childProgress = new Progress();
0240: compositeProgress.addChild(childProgress, percentageChunk);
0241: compositeProgress.setTitle(ResourceUtils.getString(
0242: Registry.class, LOADING_REMOTE_REGISTRY_KEY,
0243: remoteRegistryURI));
0244:
0245: try {
0246: loadProductRegistry(remoteRegistryURI, childProgress,
0247: RegistryType.REMOTE, true);
0248: } catch (InitializationException e) {
0249: if (!UiUtils
0250: .showYesNoDialog(
0251: ResourceUtils
0252: .getString(Registry.class,
0253: ERROR_LOADING_REMOTE_REGISTRY_TITLE_KEY),
0254: ResourceUtils
0255: .getString(
0256: Registry.class,
0257: ERROR_LOADING_REMOTE_REGISTRY_MESSAGE_KEY,
0258: remoteRegistryURI))) {
0259: finishHandler.criticalExit();
0260: } else {
0261: LogManager.log(ErrorLevel.ERROR, e);
0262: }
0263: }
0264: }
0265:
0266: validateDependencies();
0267:
0268: if (System.getProperty(SOURCE_STATE_FILE_PATH_PROPERTY) != null) {
0269: loadStateFile(new File(System
0270: .getProperty(SOURCE_STATE_FILE_PATH_PROPERTY)),
0271: new Progress());
0272: }
0273:
0274: applyRegistryFilters();
0275: changeStatuses();
0276:
0277: LogManager
0278: .logExit("... product registry initialization complete");
0279: }
0280:
0281: public void finalizeRegistry(final Progress progress)
0282: throws FinalizationException {
0283: LogManager.logEntry("finalizing product registry");
0284:
0285: progress.setPercentage(Progress.START);
0286:
0287: // remove installation data for all the products, if it still exists (should
0288: // be removed right upon installation); we only remove the files if the
0289: // local uri is different from the remote one and is not contained in the
0290: // list of alternate uris -- as we could be useing a locally located remote
0291: // registry
0292: LogManager
0293: .log("... removing remaining installation data for all the products");
0294: for (Product product : getProducts()) {
0295: for (ExtendedUri uri : product.getDataUris()) {
0296: try {
0297: FileProxy.getInstance().deleteFile(uri);
0298: } catch (IOException e) {
0299: ErrorManager.notifyWarning(ResourceUtils.getString(
0300: Registry.class,
0301: ERROR_CANNOT_DELETE_DATA_KEY), e);
0302: }
0303: }
0304: }
0305: LogManager.log("... save local registry if necessary");
0306: // save the local registry if we're executing in normal mode (i.e. not
0307: // creating a bundle)
0308: if (ExecutionMode.getCurrentExecutionMode() == ExecutionMode.NORMAL) {
0309: progress.setTitle(ResourceUtils.getString(Registry.class,
0310: SAVE_LOCAL_REGISTRY_TITLE_KEY));
0311: progress.setDetail(ResourceUtils.getString(Registry.class,
0312: SAVE_LOCAL_REGISTRY_DETAIL_KEY, localRegistryFile));
0313: LogManager.log("... save registry to file "
0314: + localRegistryFile);
0315: saveProductRegistry(localRegistryFile, new ProductFilter(
0316: Status.INSTALLED), false, // we don't need any includes in the local registry,
0317: true, // but the registry's properties would be nice,
0318: false); // and the features list is not required either
0319: }
0320:
0321: // save the state file if it is required (i.e. --record command line option
0322: // was specified)
0323: LogManager.log("... save state file if necessary");
0324: if (System.getProperty(TARGET_STATE_FILE_PATH_PROPERTY) != null) {
0325: File stateFile = new File(System
0326: .getProperty(TARGET_STATE_FILE_PATH_PROPERTY));
0327: LogManager.log("... save state file to " + stateFile);
0328: saveStateFile(stateFile, new Progress());
0329: }
0330:
0331: progress.setPercentage(Progress.COMPLETE);
0332:
0333: LogManager.logExit("finalizing product registry");
0334: }
0335:
0336: private void setRegistryProperties() throws InitializationException {
0337: LogManager.logEntry("initializing product registry properties");
0338:
0339: /////////////////////////////////////////////////////////////////////////////
0340: LogManager
0341: .logIndent("initializing local product cache directory");
0342: if (System.getProperty(LOCAL_PRODUCT_CACHE_DIRECTORY_PROPERTY) != null) {
0343: localProductCache = new File(
0344: localDirectory,
0345: System
0346: .getProperty(LOCAL_PRODUCT_CACHE_DIRECTORY_PROPERTY));
0347: }
0348: if (!localProductCache.exists()) {
0349: if (!localProductCache.mkdirs()) {
0350: throw new InitializationException(ResourceUtils
0351: .getString(Registry.class,
0352: ERROR_CANNOT_CREATE_CACHE_DIR_KEY,
0353: localProductCache));
0354: }
0355: } else if (localProductCache.isFile()) {
0356: throw new InitializationException(ResourceUtils.getString(
0357: Registry.class, ERROR_CACHE_IS_FILE_KEY,
0358: localProductCache));
0359: } else if (!localProductCache.canRead()) {
0360: throw new InitializationException(ResourceUtils.getString(
0361: Registry.class, ERROR_CANNOT_READ_CACHE_KEY,
0362: localProductCache));
0363: } else if (!localProductCache.canWrite()) {
0364: throw new InitializationException(ResourceUtils.getString(
0365: Registry.class, ERROR_CANNOT_WRITE_CACHE_KEY,
0366: localProductCache));
0367: }
0368: LogManager.logUnindent("... " + localProductCache);
0369:
0370: /////////////////////////////////////////////////////////////////////////////
0371: LogManager.log("initializing local product registry file");
0372: if (System.getProperty(LOCAL_PRODUCT_REGISTRY_PROPERTY) != null) {
0373: localRegistryFile = new File(localDirectory, System
0374: .getProperty(LOCAL_PRODUCT_REGISTRY_PROPERTY));
0375: }
0376:
0377: if (!localRegistryFile.exists()) {
0378: try {
0379: FileUtils.copyFile(FileProxy.getInstance().getFile(
0380: localRegistryStubUri, true), localRegistryFile);
0381: } catch (DownloadException e) {
0382: throw new InitializationException(ResourceUtils
0383: .getString(Registry.class,
0384: ERROR_CANNOT_CREATE_REGISTRY_KEY), e);
0385: } catch (IOException e) {
0386: throw new InitializationException(ResourceUtils
0387: .getString(Registry.class,
0388: ERROR_CANNOT_CREATE_REGISTRY_KEY), e);
0389: }
0390: } else if (localRegistryFile.isDirectory()) {
0391: throw new InitializationException(ResourceUtils.getString(
0392: Registry.class, ERROR_REGISTRY_IS_DIRECTORY_KEY,
0393: localRegistryFile));
0394: } else if (!localRegistryFile.canRead()) {
0395: throw new InitializationException(ResourceUtils.getString(
0396: Registry.class, ERROR_CANNOT_READ_CACHE_KEY,
0397: localRegistryFile));
0398: } else if (!localRegistryFile.canWrite()) {
0399: throw new InitializationException(ResourceUtils.getString(
0400: Registry.class, ERROR_CANNOT_WRITE_REGISTRY_KEY,
0401: localRegistryFile));
0402: }
0403: LogManager.log(" ... " + localRegistryFile);
0404:
0405: /////////////////////////////////////////////////////////////////////////////
0406: LogManager
0407: .log(" initializing local product registry stub uri");
0408: if (System.getProperty(LOCAL_PRODUCT_REGISTRY_STUB_PROPERTY) != null) {
0409: localRegistryStubUri = System
0410: .getProperty(LOCAL_PRODUCT_REGISTRY_STUB_PROPERTY);
0411: }
0412: LogManager.log(" ... " + localRegistryStubUri);
0413:
0414: /////////////////////////////////////////////////////////////////////////////
0415: LogManager.log(" initializing bundled product registry uri");
0416: if (System.getProperty(BUNDLED_PRODUCT_REGISTRY_URI_PROPERTY) != null) {
0417: bundledRegistryUri = System
0418: .getProperty(BUNDLED_PRODUCT_REGISTRY_URI_PROPERTY);
0419: }
0420: LogManager.log(" ... " + bundledRegistryUri);
0421:
0422: /////////////////////////////////////////////////////////////////////////////
0423: LogManager.log(" initializing product registry schema uri");
0424: if (System.getProperty(PRODUCT_REGISTRY_SCHEMA_URI_PROPERTY) != null) {
0425: registrySchemaUri = System
0426: .getProperty(PRODUCT_REGISTRY_SCHEMA_URI_PROPERTY);
0427: }
0428: LogManager.log(" ... " + registrySchemaUri);
0429:
0430: /////////////////////////////////////////////////////////////////////////////
0431: LogManager
0432: .log(" initializing remote product registries uris");
0433: if (System.getProperty(REMOTE_PRODUCT_REGISTRIES_PROPERTY) != null) {
0434: for (String remoteRegistryURI : System.getProperty(
0435: REMOTE_PRODUCT_REGISTRIES_PROPERTY).split(
0436: StringUtils.LF)) {
0437: remoteRegistryUris.add(remoteRegistryURI);
0438: }
0439: }
0440: for (String string : remoteRegistryUris) {
0441: LogManager.log(" ... " + string);
0442: }
0443:
0444: /////////////////////////////////////////////////////////////////////////////
0445: LogManager.log(" initializing state file schema uri");
0446: if (System.getProperty(STATE_FILE_SCHEMA_URI_PROPERTY) != null) {
0447: stateFileSchemaUri = System
0448: .getProperty(STATE_FILE_SCHEMA_URI_PROPERTY);
0449: }
0450: LogManager.log(" ... " + stateFileSchemaUri);
0451:
0452: /////////////////////////////////////////////////////////////////////////////
0453: LogManager.log(" initializing default state file uri");
0454: if (System.getProperty(STATE_FILE_STUB_PROPERTY) != null) {
0455: stateFileStubUri = System
0456: .getProperty(STATE_FILE_STUB_PROPERTY);
0457: }
0458: LogManager.log(" ... " + stateFileStubUri);
0459:
0460: /////////////////////////////////////////////////////////////////////////////
0461: LogManager.log(" initializing target platform");
0462: if (System.getProperty(TARGET_PLATFORM_PROPERTY) != null) {
0463: final String platformString = System
0464: .getProperty(TARGET_PLATFORM_PROPERTY);
0465: try {
0466: targetPlatform = StringUtils
0467: .parsePlatform(platformString);
0468: } catch (ParseException e) {
0469: throw new InitializationException(ResourceUtils
0470: .getString(Registry.class,
0471: ERROR_CANNOT_PARSE_PLATFORM_KEY,
0472: platformString), e);
0473:
0474: }
0475: }
0476: LogManager.log(" ... " + targetPlatform);
0477: LogManager.logExit("initializing product registry properties");
0478: }
0479:
0480: private void validateDependencies() throws InitializationException {
0481: for (Product product : getProducts()) {
0482: validateRequirements(product);
0483: validateConflicts(product);
0484: validateInstallAfters(product);
0485: }
0486: }
0487:
0488: private void applyRegistryFilters() {
0489: // if a target component was specified, hide everything except:
0490: // * the target itself
0491: // * products, whose requirement(s) the target satisfies
0492: // * ancestors of any of the above
0493: if ((System.getProperty(TARGET_COMPONENT_UID_PROPERTY) != null)
0494: && (System
0495: .getProperty(TARGET_COMPONENT_VERSION_PROPERTY) != null)) {
0496: final String uid = System
0497: .getProperty(TARGET_COMPONENT_UID_PROPERTY);
0498: final Version version = Version.getVersion(System
0499: .getProperty(TARGET_COMPONENT_VERSION_PROPERTY));
0500:
0501: final Product target = getProduct(uid, version);
0502:
0503: if (target != null) {
0504: final List<Product> dependents = getInavoidableDependents(target);
0505:
0506: for (Product product : getProducts()) {
0507: if (!target.equals(product)
0508: && !dependents.contains(product)
0509: && !product.isAncestor(target)
0510: && !product.isAncestor(dependents)) {
0511: product.setVisible(false);
0512: }
0513: }
0514: } else {
0515: if (!UiUtils.showYesNoDialog(ResourceUtils.getString(
0516: Registry.class,
0517: ERROR_MISSING_TARGET_COMPONENT_TITLE_KEY),
0518: ResourceUtils.getString(Registry.class,
0519: ERROR_MISSING_TARGET_COMPONENT_MSG_KEY,
0520: uid, version))) {
0521: finishHandler.cancel();
0522: } else {
0523: for (Product product : getProducts()) {
0524: product.setVisible(false);
0525: }
0526: }
0527: }
0528: }
0529:
0530: // hide products that do not support the current platform
0531: for (Product product : getProducts()) {
0532: boolean compatible = false;
0533:
0534: for (Platform productPlatform : product.getPlatforms()) {
0535: if (targetPlatform.isCompatibleWith(productPlatform)) {
0536: compatible = true;
0537: break;
0538: }
0539: }
0540:
0541: if (!compatible) {
0542: product.setVisible(false);
0543: }
0544: }
0545:
0546: // hide empty groups
0547: for (Group group : getGroups()) {
0548: if (group.isEmpty()) {
0549: group.setVisible(false);
0550: }
0551: }
0552: }
0553:
0554: private void changeStatuses() {
0555: if (Boolean.getBoolean(SUGGEST_INSTALL_PROPERTY)
0556: || Boolean.getBoolean(FORCE_INSTALL_PROPERTY)) {
0557: for (Product product : getProducts(Status.NOT_INSTALLED)) {
0558: // we should not change the status of components that are not
0559: // visible (were filtered out either at build time or runtime), as
0560: // this may cause unexpected results - these components are not
0561: // expected to be dealt with
0562: if (product.isVisible()) {
0563: product.setStatus(Status.TO_BE_INSTALLED);
0564: }
0565: }
0566: }
0567:
0568: if (Boolean.getBoolean(SUGGEST_UNINSTALL_PROPERTY)
0569: || Boolean.getBoolean(FORCE_UNINSTALL_PROPERTY)) {
0570: for (Product product : getProducts(Status.INSTALLED)) {
0571: // we should not change the status of components that are not
0572: // visible (were filtered out either at build time or runtime), as
0573: // this may cause unexpected results - these components are not
0574: // expected to be dealt with
0575: if (product.isVisible()) {
0576: product.setStatus(Status.TO_BE_UNINSTALLED);
0577: }
0578: }
0579: }
0580: }
0581:
0582: // validation ///////////////////////////////////////////////////////////////////
0583: private void validateRequirements(final Product product)
0584: throws InitializationException {
0585: validateRequirements(product, new LinkedList<Product>());
0586: }
0587:
0588: private void validateRequirements(final Product product,
0589: final List<Product> prohibitedList)
0590: throws InitializationException {
0591: for (Dependency requirement : product
0592: .getDependencies(Requirement.class)) {
0593: // get the list of products that satisfy the requirement
0594: final List<Product> requirees = queryProducts(new ProductFilter(
0595: requirement.getUid(),
0596: requirement.getVersionLower(), requirement
0597: .getVersionUpper(), targetPlatform));
0598:
0599: // if there are no products that satisfy the requirement, the registry
0600: // is inconsistent
0601: if (requirees.size() == 0) {
0602: String sourceId = product.getUid() + "/"
0603: + product.getVersion();
0604: String requirementId = requirement.getUid()
0605: + "/"
0606: + requirement.getVersionLower()
0607: + " - "
0608: + requirement.getVersionUpper()
0609: + (requirement.getVersionResolved() != null ? " ["
0610: + requirement.getVersionResolved()
0611: + "]"
0612: : "");
0613:
0614: throw new InitializationException(ResourceUtils
0615: .getString(Registry.class,
0616: ERROR_REQUIREMENT_KEY, sourceId,
0617: requirementId));
0618: }
0619:
0620: // iterate over the list of satisfying products, and check whether they
0621: // define a dependency that is satisfied wither by the current product
0622: // or by any product in the prohibited list; if it is, we have a cyclic
0623: // dependency which is faulty - throw an exception
0624: for (Product requiree : requirees) {
0625: for (Dependency dependency : requiree.getDependencies()) {
0626: if (product.satisfies(dependency)) {
0627: throw new InitializationException(ResourceUtils
0628: .getString(Registry.class,
0629: ERROR_CYCLIC_DEPENDENCY_KEY,
0630: product.getUid(), dependency
0631: .getUid()));
0632:
0633: }
0634:
0635: for (Product prohibited : prohibitedList) {
0636: if (prohibited.satisfies(dependency)) {
0637: throw new InitializationException(
0638: ResourceUtils
0639: .getString(
0640: Registry.class,
0641: ERROR_CYCLIC_DEPENDENCY_KEY,
0642: prohibited.getUid(),
0643: dependency.getUid()));
0644: }
0645: }
0646: }
0647:
0648: // if the requiree's dependencies are ok, we need to check whether,
0649: // the products that satisfy its requirements are ok as well
0650: final List<Product> newProhibitedList = new LinkedList<Product>();
0651: newProhibitedList.addAll(prohibitedList);
0652: newProhibitedList.add(product);
0653:
0654: validateRequirements(requiree, newProhibitedList);
0655: }
0656: }
0657: }
0658:
0659: private void validateConflicts(final Product product)
0660: throws InitializationException {
0661: for (Dependency requirement : product
0662: .getDependencies(Requirement.class)) {
0663: // get the list of products that satisfy the requirement
0664: final List<Product> requirees = queryProducts(new ProductFilter(
0665: requirement.getUid(),
0666: requirement.getVersionLower(), requirement
0667: .getVersionUpper(), targetPlatform));
0668:
0669: for (Dependency conflict : product
0670: .getDependencies(Conflict.class)) {
0671: // get the list of products that satisfy the conflict
0672: final List<Product> conflictees = queryProducts(new ProductFilter(
0673: conflict.getUid(), conflict.getVersionLower(),
0674: conflict.getVersionUpper(), targetPlatform));
0675:
0676: if (SystemUtils.intersects(requirees, conflictees)) {
0677: throw new InitializationException(
0678: "A requiree is also a conflictee.");
0679: }
0680: }
0681: }
0682: }
0683:
0684: private void validateInstallAfters(final Product product)
0685: throws InitializationException {
0686: validateInstallAfters(product, new LinkedList<Product>());
0687: }
0688:
0689: private void validateInstallAfters(final Product product,
0690: final List<Product> prohibitedList)
0691: throws InitializationException {
0692: for (Dependency installafter : product
0693: .getDependencies(InstallAfter.class)) {
0694: // get the list of products that satisfy the install-after dependency
0695: final List<Product> dependees = queryProducts(new ProductFilter(
0696: installafter.getUid(), targetPlatform));
0697:
0698: // iterate over the list of satisfying products, and check whether they
0699: // define a requirement or install-efter dependency that is satisfied
0700: // either by the current product or by any product in the prohibited
0701: // list; if it is, we have a cyclic dependency which is faulty - throw
0702: // an exception
0703: for (Product requiree : dependees) {
0704: for (Dependency dependency : requiree.getDependencies(
0705: Requirement.class, InstallAfter.class)) {
0706: if (product.satisfies(dependency)) {
0707: throw new InitializationException(ResourceUtils
0708: .getString(Registry.class,
0709: ERROR_CYCLIC_DEPENDENCY_KEY,
0710: product.getUid(), dependency
0711: .getUid()));
0712: }
0713:
0714: for (Product prohibited : prohibitedList) {
0715: if (prohibited.satisfies(dependency)) {
0716: throw new InitializationException(
0717: ResourceUtils
0718: .getString(
0719: Registry.class,
0720: ERROR_CYCLIC_DEPENDENCY_KEY,
0721: prohibited.getUid(),
0722: dependency.getUid()));
0723: }
0724: }
0725: }
0726:
0727: // if the requiree's dependencies are ok, we need to check whether
0728: // the products that satisfy its requirements are ok as well
0729: final List<Product> newProhibitedList = new LinkedList<Product>();
0730: newProhibitedList.addAll(prohibitedList);
0731: newProhibitedList.add(product);
0732:
0733: validateRequirements(requiree, newProhibitedList);
0734: }
0735:
0736: }
0737: }
0738:
0739: /**
0740: * Returns the list of products for which the given product is the only one,
0741: * that satisfies the requirement. In other words the returned products define
0742: * at least one requirement that is directly or indirectly satisfied by this
0743: * particular product and not by any other products.
0744: *
0745: * <p>
0746: * Product's status is also taken into account, i.e. if the dependent product
0747: * is installed, a not installed product cannot be considered as satisfying the
0748: * requirement.
0749: *
0750: * @param product Product for which the dependents chain should be constructed.
0751: * @return The list of products for which the given product is the only one
0752: * satisfying their requirements.
0753: */
0754: public List<Product> getInavoidableDependents(final Product product) {
0755: final Set<Product> dependents = new HashSet<Product>();
0756:
0757: for (Product candidate : getProducts()) {
0758: for (Dependency requirement : candidate
0759: .getDependencies(Requirement.class)) {
0760: final List<Product> requirees = getProducts(requirement);
0761:
0762: // if the candidate product is installed, then not installed
0763: // products cannot be counted as satisfying the requirement
0764: if (candidate.getStatus() == Status.INSTALLED) {
0765: for (int i = 0; i < requirees.size(); i++) {
0766: if (requirees.get(i).getStatus() != Status.INSTALLED) {
0767: requirees.remove(i);
0768: }
0769: }
0770: }
0771:
0772: // if the requirees size is 0 then we're in trouble, but this method
0773: // should not be concerned about this stuff -- it's the
0774: // reponsibility of the requirement validating methods
0775:
0776: // if the list of requirees contains only one element and this
0777: // element equals to the given product -- the candidate should be
0778: // included in the list of inavoidable dependents; additionally we
0779: // need to checks for indirect requirements, i.e. run this method
0780: // recursively on the dependent
0781: if ((requirees.size() == 1)
0782: && requirees.get(0).equals(product)) {
0783: dependents.add(candidate);
0784: dependents
0785: .addAll(getInavoidableDependents(candidate));
0786: }
0787: }
0788: }
0789:
0790: return new ArrayList<Product>(dependents);
0791: }
0792:
0793: private void validateInstallations() throws InitializationException {
0794: for (Product product : getProducts()) {
0795: if (product.getStatus() == Status.INSTALLED) {
0796: final String message = product.getLogic()
0797: .validateInstallation();
0798:
0799: if (message != null) {
0800: final List<Product> inavoidableDependents = getInavoidableDependents(product);
0801:
0802: boolean result = UiUtils
0803: .showYesNoDialog(
0804: ResourceUtils.getString(
0805: Registry.class,
0806: ERROR_VALIDATION_TITLE_KEY),
0807: ResourceUtils
0808: .getString(
0809: Registry.class,
0810: ERROR_VALIDATION_MSG_KEY,
0811: product
0812: .getDisplayName(),
0813: message,
0814: product
0815: .getDisplayName(),
0816: StringUtils
0817: .asString(inavoidableDependents)));
0818:
0819: if (result) {
0820: product.setStatus(Status.NOT_INSTALLED);
0821: product.getParent().removeChild(product);
0822: for (Product dependent : inavoidableDependents) {
0823: dependent.setStatus(Status.NOT_INSTALLED);
0824: dependent.getParent()
0825: .removeChild(dependent);
0826: }
0827: } else {
0828: finishHandler.criticalExit();
0829: }
0830: }
0831: }
0832: }
0833: }
0834:
0835: // registry <-> dom <-> xml operations //////////////////////////////////////////
0836: public void loadProductRegistry(final File file)
0837: throws InitializationException {
0838: loadProductRegistry(file.toURI().toString());
0839: }
0840:
0841: public void loadProductRegistry(final String uri)
0842: throws InitializationException {
0843: loadProductRegistry(uri, new Progress(), RegistryType.REMOTE,
0844: false);
0845: }
0846:
0847: public void loadProductRegistry(final String uri,
0848: final Progress progress, final RegistryType registryType,
0849: final boolean loadIncludes) throws InitializationException {
0850: try {
0851: final Element registryElement = loadRegistryDocument(uri)
0852: .getDocumentElement();
0853:
0854: // load the includes
0855: final Element includesElement = XMLUtils.getChild(
0856: registryElement, "includes");
0857:
0858: if (includesElement != null) {
0859: includes.addAll(XMLUtils
0860: .parseExtendedUrisList(includesElement));
0861:
0862: if (loadIncludes) {
0863: for (ExtendedUri includeUri : includes) {
0864: loadProductRegistry(includeUri.getRemote()
0865: .toString(), new Progress(),
0866: RegistryType.REMOTE, true);
0867: }
0868: }
0869: }
0870:
0871: // load the properties
0872: final Element propertiesElement = XMLUtils.getChild(
0873: registryElement, "properties");
0874:
0875: if (propertiesElement != null) {
0876: final NbiProperties map = XMLUtils
0877: .parseNbiProperties(propertiesElement);
0878: for (Object name : map.keySet()) {
0879: if (!properties.containsKey(name)) {
0880: properties.put(name, map.get(name));
0881: }
0882: }
0883: }
0884:
0885: // load the features list
0886: final Element featuresElement = XMLUtils.getChild(
0887: registryElement, "features");
0888:
0889: if (featuresElement != null) {
0890: for (Feature feature : XMLUtils
0891: .parseFeaturesList(featuresElement)) {
0892: boolean shouldAdd = true;
0893:
0894: int i;
0895: for (i = 0; i < features.size(); i++) {
0896: if (features.get(i).getId().equals(
0897: feature.getId())) {
0898: shouldAdd = false;
0899: break;
0900: }
0901: if (features.get(i).getOffset() > feature
0902: .getOffset()) {
0903: break;
0904: }
0905: }
0906:
0907: if (shouldAdd) {
0908: features.add(i, feature);
0909: }
0910: }
0911: }
0912:
0913: // load the components
0914: loadRegistryComponents(registryRoot, registryElement,
0915: registryType);
0916:
0917: validateInstallations();
0918:
0919: progress.setPercentage(Progress.COMPLETE);
0920: } catch (ParseException e) {
0921: throw new InitializationException(ResourceUtils.getString(
0922: Registry.class, ERROR_CANNOT_LOAD_REGISTRY_KEY), e);
0923: } catch (XMLException e) {
0924: throw new InitializationException(ResourceUtils.getString(
0925: Registry.class, ERROR_CANNOT_LOAD_REGISTRY_KEY), e);
0926: }
0927: }
0928:
0929: public void saveProductRegistry(final File file,
0930: final RegistryFilter filter, final boolean saveIncludes,
0931: final boolean saveProperties, final boolean saveFeatures)
0932: throws FinalizationException {
0933: try {
0934: LogManager.logEntry("saving product registry file");
0935: LogManager.log("... getting registry document");
0936: Document document = getRegistryDocument(filter,
0937: saveIncludes, saveProperties, saveFeatures);
0938: LogManager.log("... saving registry document to file "
0939: + file);
0940: XMLUtils.saveXMLDocument(document, file);
0941: LogManager.log("... saving XML file succesfully finished");
0942: } catch (XMLException e) {
0943: throw new FinalizationException(ResourceUtils.getString(
0944: Registry.class, ERROR_REGISTRY_FINALIZATION), e);
0945: } finally {
0946: LogManager.logExit("... saving product registry done");
0947: }
0948: }
0949:
0950: public Document getEmptyRegistryDocument() throws XMLException {
0951: return loadRegistryDocument(localRegistryStubUri);
0952: }
0953:
0954: public Document getRegistryDocument(final RegistryFilter filter,
0955: final boolean saveIncludes, final boolean saveProperties,
0956: final boolean saveFeatures) throws XMLException,
0957: FinalizationException {
0958: final Document document = getEmptyRegistryDocument();
0959: final Element documentElement = document.getDocumentElement();
0960:
0961: if ((includes.size() > 0) && saveIncludes) {
0962: documentElement.appendChild(XMLUtils.saveExtendedUrisList(
0963: includes, document.createElement("includes")));
0964: }
0965:
0966: if ((properties.size() > 0) && saveProperties) {
0967: documentElement.appendChild(XMLUtils.saveNbiProperties(
0968: properties, document.createElement("properties")));
0969: }
0970:
0971: if ((features.size() > 0) && saveFeatures) {
0972: documentElement.appendChild(XMLUtils.saveFeaturesList(
0973: features, document.createElement("features")));
0974: }
0975:
0976: final Element componentsElement = registryRoot
0977: .saveChildrenToDom(document, filter);
0978: if (componentsElement != null) {
0979: documentElement.appendChild(componentsElement);
0980: }
0981:
0982: return document;
0983: }
0984:
0985: public Document loadRegistryDocument(final String uri)
0986: throws XMLException {
0987: try {
0988: final File schemaFile = FileProxy.getInstance().getFile(
0989: registrySchemaUri, true);
0990: final File registryFile = FileProxy.getInstance().getFile(
0991: uri, true);
0992:
0993: final Schema schema = SchemaFactory.newInstance(
0994: XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
0995: schemaFile);
0996:
0997: final DocumentBuilderFactory factory = DocumentBuilderFactory
0998: .newInstance();
0999: try {
1000: factory.setSchema(schema);
1001: } catch (UnsupportedOperationException e) {
1002: // if the parser does not support schemas, let it be -- we can do
1003: // without it anyway -- just log it and proceed
1004: ErrorManager.notifyDebug(ResourceUtils.getString(
1005: Registry.class,
1006: ERROR_PARSER_DO_NOT_CUPPORT_SCHEMAS_KEY,
1007: factory.getClass()), e);
1008: }
1009: factory.setNamespaceAware(true);
1010:
1011: final DocumentBuilder builder = factory
1012: .newDocumentBuilder();
1013:
1014: return builder.parse(registryFile);
1015: } catch (DownloadException e) {
1016: throw new XMLException(ResourceUtils.getString(
1017: Registry.class, ERROR_REGISTRY_DOCUMENT_LOADING), e);
1018: } catch (ParserConfigurationException e) {
1019: throw new XMLException(ResourceUtils.getString(
1020: Registry.class, ERROR_REGISTRY_DOCUMENT_LOADING), e);
1021: } catch (SAXException e) {
1022: throw new XMLException(ResourceUtils.getString(
1023: Registry.class, ERROR_REGISTRY_DOCUMENT_LOADING), e);
1024: } catch (IOException e) {
1025: throw new XMLException(ResourceUtils.getString(
1026: Registry.class, ERROR_REGISTRY_DOCUMENT_LOADING), e);
1027: }
1028: }
1029:
1030: private void loadRegistryComponents(final RegistryNode parentNode,
1031: final Element parentElement, final RegistryType registryType)
1032: throws InitializationException {
1033: final Element element = XMLUtils.getChild(parentElement,
1034: "components");
1035:
1036: if (element != null) {
1037: for (Element child : XMLUtils.getChildren(element)) {
1038: if (child.getNodeName().equals("product")) {
1039: final Product product = new Product()
1040: .loadFromDom(child);
1041:
1042: // find the existing products which have the same uid/version
1043: // and whose platforms itersect with the platforms of the
1044: // currently loaded component (i.e. at least one of the platforms
1045: // in the existing product is compatible a platform in the
1046: // currept product's set and vice versa)
1047: final List<Product> existing = getProducts(product
1048: .getUid(), product.getVersion(), product
1049: .getPlatforms());
1050:
1051: product.setRegistryType(registryType);
1052:
1053: if (existing.size() == 0) {
1054: parentNode.addChild(product);
1055: loadRegistryComponents(product, child,
1056: registryType);
1057: } else {
1058: loadRegistryComponents(existing.get(0), child,
1059: registryType);
1060: }
1061: }
1062:
1063: if (child.getNodeName().equals("group")) {
1064: final Group group = new Group().loadFromDom(child);
1065: final Group existing = getGroup(group.getUid());
1066:
1067: group.setRegistryType(registryType);
1068:
1069: if (existing == null) {
1070: parentNode.addChild(group);
1071: loadRegistryComponents(group, child,
1072: registryType);
1073: } else {
1074: loadRegistryComponents(existing, child,
1075: registryType);
1076: }
1077: }
1078: }
1079: }
1080: }
1081:
1082: // basic queries ////////////////////////////////////////////////////////////////
1083: public List<RegistryNode> query(final RegistryFilter filter) {
1084: List<RegistryNode> matches = new LinkedList<RegistryNode>();
1085: Queue<RegistryNode> queue = new LinkedList<RegistryNode>();
1086:
1087: queue.offer(registryRoot);
1088: while (queue.peek() != null) {
1089: final RegistryNode node = queue.poll();
1090:
1091: if (filter.accept(node)) {
1092: matches.add(node);
1093: }
1094:
1095: for (RegistryNode child : node.getChildren()) {
1096: queue.offer(child);
1097: }
1098: }
1099:
1100: return matches;
1101: }
1102:
1103: public List<Product> queryProducts(final RegistryFilter filter) {
1104: List<Product> components = new ArrayList<Product>();
1105:
1106: for (RegistryNode node : query(filter)) {
1107: if (node instanceof Product) {
1108: components.add((Product) node);
1109: }
1110: }
1111:
1112: return components;
1113: }
1114:
1115: public List<Group> queryGroups(final RegistryFilter filter) {
1116: List<Group> groups = new ArrayList<Group>();
1117:
1118: for (RegistryNode node : query(filter)) {
1119: if (node instanceof Group) {
1120: groups.add((Group) node);
1121: }
1122: }
1123:
1124: return groups;
1125: }
1126:
1127: public List<RegistryNode> getNodes() {
1128: return query(TrueFilter.INSTANCE);
1129: }
1130:
1131: public List<RegistryNode> getNodes(final RegistryType registryType) {
1132: final List<RegistryNode> filtered = new LinkedList<RegistryNode>();
1133:
1134: for (RegistryNode node : getNodes()) {
1135: if (node.getRegistryType() == registryType) {
1136: filtered.add(node);
1137: }
1138: }
1139:
1140: return filtered;
1141: }
1142:
1143: // products queries /////////////////////////////////////////////////////////////
1144: public List<Product> getProducts() {
1145: return queryProducts(TrueFilter.INSTANCE);
1146: }
1147:
1148: public List<Product> getProducts(final Platform platform) {
1149: return queryProducts(new ProductFilter(platform));
1150: }
1151:
1152: public List<Product> getProducts(final String uid) {
1153: return queryProducts(new ProductFilter(uid, targetPlatform));
1154: }
1155:
1156: public List<Product> getProducts(final String uid,
1157: final Version lower, final Version upper) {
1158: return queryProducts(new ProductFilter(uid, lower, upper,
1159: targetPlatform));
1160: }
1161:
1162: public List<Product> getProducts(final String uid,
1163: final Version version, final Platform platform) {
1164: return queryProducts(new ProductFilter(uid, version, platform));
1165: }
1166:
1167: public List<Product> getProducts(final String uid,
1168: final Version version, final List<Platform> platforms) {
1169: return queryProducts(new ProductFilter(uid, version, platforms));
1170: }
1171:
1172: public List<Product> getProducts(final Dependency dependency) {
1173: if (dependency instanceof Requirement) {
1174: if (dependency.getVersionResolved() != null) {
1175: return queryProducts(new ProductFilter(dependency
1176: .getUid(), dependency.getVersionResolved(),
1177: dependency.getVersionResolved(), targetPlatform));
1178: }
1179: }
1180: if (dependency instanceof Requirement
1181: || dependency instanceof Conflict) {
1182: return queryProducts(new ProductFilter(dependency.getUid(),
1183: dependency.getVersionLower(), dependency
1184: .getVersionUpper(), targetPlatform));
1185: }
1186: if (dependency instanceof InstallAfter) {
1187: return queryProducts(new ProductFilter(dependency.getUid(),
1188: targetPlatform));
1189: }
1190: ErrorManager.notifyCritical(ResourceUtils.getString(
1191: Registry.class, ERROR_UNKNOWN_DEPENDENCY_KEY,
1192: dependency.getClass().toString()));
1193:
1194: // the only way for us to reach this spot is to get to 'default:' in the
1195: // switch, but ErrorManager.notifyCritical() will cause a System.exit(),
1196: // so the below line is present only for successful compilation
1197: return null;
1198: }
1199:
1200: public List<Product> getProducts(final Status status) {
1201: return queryProducts(new ProductFilter(status, targetPlatform));
1202: }
1203:
1204: public List<Product> getProducts(final DetailedStatus detailedStatus) {
1205: return queryProducts(new ProductFilter(detailedStatus,
1206: targetPlatform));
1207: }
1208:
1209: public List<Product> getProducts(final Feature feature) {
1210: return queryProducts(new ProductFilter(feature, targetPlatform));
1211: }
1212:
1213: public Product getProduct(final String uid, final Version version) {
1214: final List<Product> candidates = getProducts(uid, version,
1215: targetPlatform);
1216:
1217: return (candidates.size() > 0) ? candidates.get(0) : null;
1218: }
1219:
1220: // groups queries ///////////////////////////////////////////////////////////////
1221: public List<Group> getGroups() {
1222: return queryGroups(TrueFilter.INSTANCE);
1223: }
1224:
1225: public Group getGroup(final String uid) {
1226: List<Group> candidates = queryGroups(new GroupFilter(uid));
1227:
1228: return (candidates.size() > 0) ? candidates.get(0) : null;
1229: }
1230:
1231: // installation order related queries ///////////////////////////////////////////
1232: public List<Product> getProductsToInstall() {
1233: final List<Product> products = new LinkedList<Product>();
1234:
1235: Product product;
1236: while ((product = getNextComponentToInstall(products)) != null) {
1237: products.add(product);
1238: }
1239:
1240: return products;
1241: }
1242:
1243: public List<Product> getProductsToUninstall() {
1244: final List<Product> products = new ArrayList<Product>();
1245:
1246: Product product;
1247: while ((product = getNextComponentToUninstall(products)) != null) {
1248: products.add(product);
1249: }
1250:
1251: return products;
1252: }
1253:
1254: private Product getNextComponentToInstall(
1255: final List<Product> currentList) {
1256: for (Product product : getProducts()) {
1257: if ((product.getStatus() == Status.TO_BE_INSTALLED)
1258: && !currentList.contains(product)
1259: && checkDependenciesForInstall(product)) {
1260: boolean productIsGood = true;
1261:
1262: // all products satisfying the requirement and install-after
1263: // dependencies which are planned for installation should be already
1264: // present in the list
1265: for (Dependency dependency : product.getDependencies(
1266: Requirement.class, InstallAfter.class)) {
1267: for (Product dependee : getProducts(dependency)) {
1268: if ((dependee.getStatus() == Status.TO_BE_INSTALLED)
1269: && !currentList.contains(dependee)) {
1270: productIsGood = false;
1271: }
1272: }
1273: }
1274:
1275: if (productIsGood) {
1276: return product;
1277: }
1278: }
1279: }
1280:
1281: return null;
1282: }
1283:
1284: private Product getNextComponentToUninstall(
1285: final List<Product> currentList) {
1286: for (Product product : getProducts()) {
1287: if ((product.getStatus() == Status.TO_BE_UNINSTALLED)
1288: && !currentList.contains(product)
1289: && checkDependenciesForUninstall()) {
1290: boolean productIsGood = true;
1291:
1292: for (Product dependent : getProducts()) {
1293: if ((dependent.getStatus() != Status.NOT_INSTALLED)
1294: && !currentList.contains(dependent)
1295: && satisfiesRequirement(product, dependent)) {
1296: productIsGood = false;
1297: break;
1298: }
1299: }
1300:
1301: if (productIsGood) {
1302: return product;
1303: }
1304:
1305: }
1306: }
1307: return null;
1308: }
1309:
1310: // products /////////////////////////////////////////////////////////////////////
1311: public boolean satisfiesRequirement(final Product candidate,
1312: final Product product) {
1313: for (Dependency requirement : product
1314: .getDependencies(Requirement.class)) {
1315: final List<Product> requirees = getProducts(requirement);
1316:
1317: for (Product requiree : requirees) {
1318: if (candidate.equals(requiree)
1319: || satisfiesRequirement(candidate, requiree)) {
1320: return true;
1321: }
1322: }
1323: }
1324:
1325: return false;
1326: }
1327:
1328: public boolean checkDependenciesForInstall(final Product product) {
1329: for (Dependency requirement : product
1330: .getDependencies(Requirement.class)) {
1331: final List<Product> requirees = getProducts(requirement);
1332: boolean satisfied = false;
1333:
1334: for (Product requiree : requirees) {
1335: if ((requiree.getStatus() == Status.INSTALLED)
1336: || (requiree.getStatus() == Status.TO_BE_INSTALLED)) {
1337: satisfied = true;
1338: break;
1339: }
1340: }
1341:
1342: if (!satisfied)
1343: return false;
1344: }
1345:
1346: for (Dependency conflict : product
1347: .getDependencies(Conflict.class)) {
1348: final List<Product> conflictees = getProducts(conflict);
1349: boolean satisfied = true;
1350:
1351: for (Product conflictee : conflictees) {
1352: if ((conflictee.getStatus() == Status.INSTALLED)
1353: || (conflictee.getStatus() == Status.TO_BE_INSTALLED)) {
1354: satisfied = false;
1355: break;
1356: }
1357: }
1358:
1359: if (!satisfied)
1360: return false;
1361: }
1362:
1363: return true;
1364: }
1365:
1366: public boolean checkDependenciesForUninstall() {
1367: for (Product product : getProducts()) {
1368: if ((product.getStatus() == Status.INSTALLED)
1369: || (product.getStatus() == Status.TO_BE_INSTALLED)) {
1370: for (Dependency requirement : product
1371: .getDependencies(Requirement.class)) {
1372: final List<Product> requirees = getProducts(requirement);
1373:
1374: for (Product requiree : requirees) {
1375: if (requiree.getStatus() == Status.INSTALLED) {
1376: return true;
1377: }
1378: }
1379: }
1380: }
1381: }
1382:
1383: return true;
1384: }
1385:
1386: // properties ///////////////////////////////////////////////////////////////////
1387: public Properties getProperties() {
1388: return properties;
1389: }
1390:
1391: public String getProperty(final String name) {
1392: return properties.getProperty(name);
1393: }
1394:
1395: public void setProperty(final String name, final String value) {
1396: properties.setProperty(name, value);
1397: }
1398:
1399: // state file methods ///////////////////////////////////////////////////////////
1400: public void loadStateFile(final File stateFile,
1401: final Progress progress) throws InitializationException {
1402: try {
1403: LogManager.log("loading state file from "
1404: + stateFile.getAbsolutePath());
1405:
1406: LogManager.log("parsing xml file...");
1407: final File schemaFile = FileProxy.getInstance().getFile(
1408: stateFileSchemaUri, true);
1409:
1410: final Schema schema = SchemaFactory.newInstance(
1411: XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
1412: schemaFile);
1413:
1414: final DocumentBuilderFactory factory = DocumentBuilderFactory
1415: .newInstance();
1416: try {
1417: factory.setSchema(schema);
1418: } catch (UnsupportedOperationException e) {
1419: // if the parser does not support schemas, let it be -- we can do
1420: // without it anyway -- just log it and proceed
1421: ErrorManager.notifyDebug(ResourceUtils.getString(
1422: Registry.class,
1423: ERROR_PARSER_DO_NOT_CUPPORT_SCHEMAS_KEY,
1424: factory.getClass()), e);
1425: }
1426: factory.setNamespaceAware(true);
1427:
1428: final Document document = factory.newDocumentBuilder()
1429: .parse(stateFile);
1430: LogManager.log("...complete");
1431:
1432: final Element element = document.getDocumentElement();
1433:
1434: // get the total number of components in this state file, we need this to
1435: // be able to properly update the progress
1436: int productsNumber = XMLUtils.countDescendants(element,
1437: "product");
1438:
1439: // we should get the percentage per component and we reserce one area for
1440: // registry-wide properties
1441: int percentageChunk = Progress.COMPLETE
1442: / (productsNumber + 1);
1443: int percentageLeak = Progress.COMPLETE
1444: % (productsNumber + 1);
1445:
1446: LogManager.log(" parsing registry properties...");
1447:
1448: final Element propertiesElement = XMLUtils.getChild(
1449: element, "properties");
1450: if (propertiesElement != null) {
1451: progress.setDetail(ResourceUtils
1452: .getString(Registry.class,
1453: LOADING_REGISTRY_PROPERTIES_KEY));
1454: properties.putAll(XMLUtils
1455: .parseNbiProperties(propertiesElement));
1456: }
1457:
1458: LogManager.log(" ...complete");
1459: progress.addPercentage(percentageChunk + percentageLeak);
1460:
1461: LogManager.log(ErrorLevel.DEBUG,
1462: " parsing components...");
1463:
1464: final Element productsElement = XMLUtils.getChild(element,
1465: "components");
1466: if (productsElement != null) {
1467: for (Element productElement : XMLUtils
1468: .getChildren(productsElement)) {
1469: final String uid = productElement
1470: .getAttribute("uid");
1471: final Version version = Version
1472: .getVersion(productElement
1473: .getAttribute("version"));
1474: final List<Platform> platforms = StringUtils
1475: .parsePlatforms(productElement
1476: .getAttribute("platform"));
1477:
1478: LogManager.log(" parsing component uid="
1479: + uid + ", version=" + version);
1480: progress.setDetail(ResourceUtils.getString(
1481: Registry.class, LOADING_COMPONENT_KEY, uid,
1482: version));
1483:
1484: boolean compatible = false;
1485:
1486: for (Platform productPlatform : platforms) {
1487: if (targetPlatform
1488: .isCompatibleWith(productPlatform)) {
1489: compatible = true;
1490: break;
1491: }
1492: }
1493:
1494: if (compatible) {
1495: final Product product = getProduct(uid, version);
1496:
1497: if (product != null) {
1498: final Status status = StringUtils
1499: .parseStatus(productElement
1500: .getAttribute("status"));
1501: switch (status) {
1502: case NOT_INSTALLED:
1503: continue;
1504: case TO_BE_INSTALLED:
1505: if (product.getStatus() != Status.INSTALLED) {
1506: product.setStatus(status);
1507: } else {
1508: continue;
1509: }
1510: break;
1511: case INSTALLED:
1512: continue;
1513: case TO_BE_UNINSTALLED:
1514: if (product.getStatus() != Status.NOT_INSTALLED) {
1515: product.setStatus(status);
1516: } else {
1517: continue;
1518: }
1519: break;
1520: }
1521:
1522: final Element productPropertiesElement = XMLUtils
1523: .getChild(productElement,
1524: "properties");
1525: if (productPropertiesElement != null) {
1526: product
1527: .getProperties()
1528: .putAll(
1529: XMLUtils
1530: .parseNbiProperties(productPropertiesElement));
1531: }
1532: }
1533: }
1534: }
1535: }
1536: LogManager.log(ErrorLevel.DEBUG, " ...complete");
1537: } catch (DownloadException e) {
1538: throw new InitializationException(ResourceUtils.getString(
1539: Registry.class, ERROR_LOADING_COMPONENTS), e);
1540: } catch (ParserConfigurationException e) {
1541: throw new InitializationException(ResourceUtils.getString(
1542: Registry.class, ERROR_LOADING_COMPONENTS), e);
1543: } catch (SAXException e) {
1544: throw new InitializationException(ResourceUtils.getString(
1545: Registry.class, ERROR_LOADING_COMPONENTS), e);
1546: } catch (IOException e) {
1547: throw new InitializationException(ResourceUtils.getString(
1548: Registry.class, ERROR_LOADING_COMPONENTS), e);
1549: } catch (ParseException e) {
1550: throw new InitializationException(ResourceUtils.getString(
1551: Registry.class, ERROR_LOADING_COMPONENTS), e);
1552: }
1553: }
1554:
1555: public void saveStateFile(final File stateFile,
1556: final Progress progress) throws FinalizationException {
1557: try {
1558: final File schemaFile = FileProxy.getInstance().getFile(
1559: stateFileSchemaUri, true);
1560: final File stubFile = FileProxy.getInstance().getFile(
1561: stateFileStubUri, true);
1562:
1563: final Schema schema = SchemaFactory.newInstance(
1564: XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
1565: schemaFile);
1566:
1567: final DocumentBuilderFactory factory = DocumentBuilderFactory
1568: .newInstance();
1569: try {
1570: factory.setSchema(schema);
1571: } catch (UnsupportedOperationException e) {
1572: // if the parser does not support schemas, let it be -- we can do
1573: // without it anyway -- just log it and proceed
1574: ErrorManager.notifyDebug(ResourceUtils.getString(
1575: Registry.class,
1576: ERROR_PARSER_DO_NOT_CUPPORT_SCHEMAS_KEY,
1577: factory.getClass()), e);
1578: }
1579: factory.setNamespaceAware(true);
1580:
1581: final Document document = factory.newDocumentBuilder()
1582: .parse(stubFile);
1583:
1584: final Element documentElement = document
1585: .getDocumentElement();
1586:
1587: if (properties.size() > 0) {
1588: Element propertiesNode = document
1589: .createElement("properties");
1590:
1591: for (Object key : properties.keySet()) {
1592: String name = (String) key;
1593:
1594: Element propertyNode = document
1595: .createElement("property");
1596:
1597: propertyNode.setAttribute("name", name);
1598: propertyNode.setTextContent(properties
1599: .getProperty(name));
1600:
1601: propertiesNode.appendChild(propertyNode);
1602: }
1603:
1604: documentElement.appendChild(propertiesNode);
1605: }
1606:
1607: List<Product> products = queryProducts(new OrFilter(
1608: new ProductFilter(Status.INSTALLED, targetPlatform),
1609: new ProductFilter(Status.NOT_INSTALLED,
1610: targetPlatform)));
1611: if (products.size() > 0) {
1612: final Element productsNode = document
1613: .createElement("components");
1614:
1615: for (Product component : products) {
1616: final Element productNode = document
1617: .createElement("product");
1618:
1619: productNode.setAttribute("uid", component.getUid());
1620: productNode.setAttribute("version", component
1621: .getVersion().toString());
1622: productNode.setAttribute("platform", StringUtils
1623: .asString(component.getPlatforms(), " "));
1624:
1625: switch (component.getStatus()) {
1626: case INSTALLED:
1627: productNode.setAttribute("status",
1628: Status.TO_BE_INSTALLED.toString());
1629: break;
1630: case NOT_INSTALLED:
1631: productNode.setAttribute("status",
1632: Status.TO_BE_UNINSTALLED.toString());
1633: break;
1634: default:
1635: continue;
1636: }
1637:
1638: if (component.getProperties().size() > 0) {
1639: Element propertiesNode = document
1640: .createElement("properties");
1641: Properties props = new Properties();
1642: props.putAll(component.getProperties());
1643: for (Object key : props.keySet()) {
1644: String name = (String) key;
1645:
1646: Element propertyNode = document
1647: .createElement("property");
1648:
1649: propertyNode.setAttribute("name", name);
1650: propertyNode.setTextContent(props
1651: .getProperty(name));
1652:
1653: propertiesNode.appendChild(propertyNode);
1654: }
1655: productNode.appendChild(propertiesNode);
1656: }
1657:
1658: productsNode.appendChild(productNode);
1659: }
1660:
1661: documentElement.appendChild(productsNode);
1662: }
1663: FileUtils.mkdirs(stateFile.getParentFile());
1664: XMLUtils.saveXMLDocument(document, stateFile);
1665: } catch (DownloadException e) {
1666: throw new FinalizationException(ResourceUtils.getString(
1667: Registry.class, ERROR_REGISTRY_FINALIZATION), e);
1668: } catch (ParserConfigurationException e) {
1669: throw new FinalizationException(ResourceUtils.getString(
1670: Registry.class, ERROR_REGISTRY_FINALIZATION), e);
1671: } catch (SAXException e) {
1672: throw new FinalizationException(ResourceUtils.getString(
1673: Registry.class, ERROR_REGISTRY_FINALIZATION), e);
1674: } catch (IOException e) {
1675: throw new FinalizationException(ResourceUtils.getString(
1676: Registry.class, ERROR_REGISTRY_FINALIZATION), e);
1677: } catch (XMLException e) {
1678: throw new FinalizationException(ResourceUtils.getString(
1679: Registry.class, ERROR_REGISTRY_FINALIZATION), e);
1680: }
1681: }
1682:
1683: // miscellanea //////////////////////////////////////////////////////////////////
1684: public File getLocalProductCache() {
1685: return localProductCache;
1686: }
1687:
1688: public RegistryNode getRegistryRoot() {
1689: return registryRoot;
1690: }
1691:
1692: public boolean hasInstalledChildren(final RegistryNode parentNode) {
1693: for (RegistryNode child : parentNode.getChildren()) {
1694: if (child instanceof Product) {
1695: Product component = (Product) child;
1696: if (component.getStatus() == Status.INSTALLED) {
1697: return true;
1698: }
1699: }
1700:
1701: if (hasInstalledChildren(child)) {
1702: return true;
1703: }
1704: }
1705:
1706: return false;
1707: }
1708:
1709: public Platform getTargetPlatform() {
1710: return targetPlatform;
1711: }
1712:
1713: /////////////////////////////////////////////////////////////////////////////////
1714: // Constants
1715: public static final String DEFAULT_LOCAL_PRODUCT_CACHE_DIRECTORY_NAME = "product-cache";
1716:
1717: public static final String LOCAL_PRODUCT_CACHE_DIRECTORY_PROPERTY = "nbi.product.local.cache.directory.name";
1718:
1719: public static final String DEFAULT_LOCAL_REGISTRY_FILE_NAME = "registry.xml";
1720:
1721: public static final String LOCAL_PRODUCT_REGISTRY_PROPERTY = "nbi.product.local.registry.file.name";
1722:
1723: public static final String DEFAULT_LOCAL_PRODUCT_REGISTRY_STUB_URI = FileProxy.RESOURCE_SCHEME_PREFIX
1724: + "org/netbeans/installer/product/default-registry.xml";
1725:
1726: public static final String LOCAL_PRODUCT_REGISTRY_STUB_PROPERTY = "nbi.product.local.registry.stub";
1727:
1728: public static final String DEFAULT_BUNDLED_PRODUCT_REGISTRY_URI = FileProxy.RESOURCE_SCHEME_PREFIX
1729: + "data/registry.xml";
1730:
1731: public static final String BUNDLED_PRODUCT_REGISTRY_URI_PROPERTY = "nbi.product.bundled.registry.uri";
1732:
1733: public static final String DEFAULT_PRODUCT_REGISTRY_SCHEMA_URI = FileProxy.RESOURCE_SCHEME_PREFIX
1734: + "org/netbeans/installer/product/registry.xsd";
1735:
1736: public static final String PRODUCT_REGISTRY_SCHEMA_URI_PROPERTY = "nbi.product.registry.schema.uri";
1737:
1738: public static final String REMOTE_PRODUCT_REGISTRIES_PROPERTY = "nbi.product.remote.registries";
1739:
1740: public static final String TARGET_COMPONENT_UID_PROPERTY = "nbi.product.target.component.uid";
1741:
1742: public static final String TARGET_COMPONENT_VERSION_PROPERTY = "nbi.product.target.component.version";
1743:
1744: public static final String SOURCE_STATE_FILE_PATH_PROPERTY = "nbi.product.source.state.file.path";
1745:
1746: public static final String TARGET_STATE_FILE_PATH_PROPERTY = "nbi.product.target.state.file.path";
1747:
1748: public static final String STATE_FILE_SCHEMA_URI_PROPERTY = "nbi.state.file.schema.uri";
1749:
1750: public static final String DEFAULT_STATE_FILE_SCHEMA_URI = FileProxy.RESOURCE_SCHEME_PREFIX
1751: + "org/netbeans/installer/product/state-file.xsd";
1752:
1753: public static final String DEFAULT_STATE_FILE_STUB_URI = FileProxy.RESOURCE_SCHEME_PREFIX
1754: + "org/netbeans/installer/product/default-state-file.xml";
1755:
1756: public static final String STATE_FILE_STUB_PROPERTY = "nbi.state.file.stub";
1757:
1758: public static final String TARGET_PLATFORM_PROPERTY = "nbi.target.platform";
1759:
1760: public static final String SUGGEST_INSTALL_PROPERTY = "nbi.product.suggest.install";
1761:
1762: public static final String SUGGEST_UNINSTALL_PROPERTY = "nbi.product.suggest.uninstall";
1763:
1764: public static final String FORCE_INSTALL_PROPERTY = "nbi.product.force.install";
1765:
1766: public static final String FORCE_UNINSTALL_PROPERTY = "nbi.product.force.uninstall";
1767:
1768: public static final String CREATE_BUNDLE_PATH_PROPERTY = "nbi.create.bundle.path";
1769:
1770: public static final String LAZY_LOAD_ICONS_PROPERTY = "nbi.product.lazy.load.icons";
1771:
1772: private static final String LOADING_LOCAL_REGISTRY_KEY = "R.loading.local.registry"; //NOI18N
1773: private static final String ERROR_LOADING_LOCAL_REGISTRY_TITLE_KEY = "R.error.loading.local.registry.failed.title";//NOI18N
1774: private static final String ERROR_LOADING_LOCAL_REGISTRY_MESSAGE_KEY = "R.error.loading.local.registry.failed.msg";//NOI18N
1775:
1776: private static final String LOADING_BUNDLED_REGISTRY_KEY = "R.loading.bundled.registry"; //NOI18N
1777: private static final String ERROR_LOADING_BUNDLED_REGISTRY_TITLE_KEY = "R.error.loading.bundled.registry.failed.title";//NOI18N
1778: private static final String ERROR_LOADING_BUNDLED_REGISTRY_MESSAGE_KEY = "R.error.loading.bundled.registry.failed.msg";//NOI18N
1779:
1780: private static final String LOADING_REMOTE_REGISTRY_KEY = "R.loading.remote.registry"; //NOI18N
1781: private static final String ERROR_LOADING_REMOTE_REGISTRY_TITLE_KEY = "R.error.loading.remote.registry.failed.title";//NOI18N
1782: private static final String ERROR_LOADING_REMOTE_REGISTRY_MESSAGE_KEY = "R.error.loading.remote.registry.failed.msg"; //NOI18N
1783: private static final String ERROR_CANNOT_DELETE_DATA_KEY = "R.error.cannot.delete.data"; //NOI18N
1784: private static final String SAVE_LOCAL_REGISTRY_TITLE_KEY = "R.save.local.registry.title";//NOI18N
1785: private static final String SAVE_LOCAL_REGISTRY_DETAIL_KEY = "R.save.local.registry.detail"; //NOI18N
1786: private static final String ERROR_MISSING_TARGET_COMPONENT_TITLE_KEY = "R.error.missing.target.component.title"; //NOI18N
1787: private static final String ERROR_MISSING_TARGET_COMPONENT_MSG_KEY = "R.error.missing.target.component.msg"; //NOI18N
1788: private static final String ERROR_VALIDATION_TITLE_KEY = "R.error.validation.title";//NOI18N
1789: private static final String ERROR_VALIDATION_MSG_KEY = "R.error.validation.msg";//NOI18N
1790: private static final String ERROR_REQUIREMENT_KEY = "R.error.matching.requirement";//NOI18N
1791: private static final String ERROR_CYCLIC_DEPENDENCY_KEY = "R.error.cyclic.dependency";//NOI18N
1792: private static final String LOADING_REGISTRY_PROPERTIES_KEY = "R.loading.registry.properties";//NOI18N
1793: private static final String LOADING_COMPONENT_KEY = "R.loading.component";//NOI18N
1794: private static final String ERROR_LOADING_COMPONENTS = "R.error.loading.components";//NOI18N
1795: private static final String ERROR_REGISTRY_FINALIZATION = "R.error.registry.finalization";//NOI18N
1796: private static final String ERROR_REGISTRY_DOCUMENT_LOADING = "R.error.registry.document.loading";//NOI18N
1797: private static final String ERROR_CANNOT_CREATE_CACHE_DIR_KEY = "R.error.cannot.create.cache.directory";//NOI18N
1798: private static final String ERROR_CACHE_IS_FILE_KEY = "R.error.cache.is.file";//NOI18N
1799: private static final String ERROR_CANNOT_READ_CACHE_KEY = "R.error.cannot.read.cache";//NOI18N
1800: private static final String ERROR_CANNOT_WRITE_CACHE_KEY = "R.error.cannot.write.cache";//NOI18N
1801: private static final String ERROR_CANNOT_CREATE_REGISTRY_KEY = "R.error.cannot.create.registry";//NOI18N
1802: private static final String ERROR_REGISTRY_IS_DIRECTORY_KEY = "R.error.registry.is.dir";//NOI18N
1803: private static final String ERROR_CANNOT_READ_REGISTRY_KEY = "R.error.cannot.read.registry";//NOI18N
1804: private static final String ERROR_CANNOT_WRITE_REGISTRY_KEY = "R.error.cannot.write.registry";//NOI18N
1805: private static final String ERROR_CANNOT_PARSE_PLATFORM_KEY = "R.error.cannot.parse.platform";//NOI18N
1806: private static final String ERROR_CANNOT_LOAD_REGISTRY_KEY = "R.error.cannot.load.registry";//NOI18N
1807: private static final String ERROR_PARSER_DO_NOT_CUPPORT_SCHEMAS_KEY = "R.error.parser.not.support.schemas";//NOI18N
1808: private static final String ERROR_UNKNOWN_DEPENDENCY_KEY = "R.error.unknown.dependency";//NOI18N
1809:
1810: }
|