org.netbeans.modules.autoupdate.services.Utilities Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.autoupdate.services;
import java.io.*;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.security.CodeSigner;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.PKIXParameters;
import java.security.cert.PKIXRevocationChecker;
import java.security.cert.PKIXRevocationChecker.Option;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import java.util.regex.Pattern;
import org.netbeans.Module;
import org.netbeans.ModuleManager;
import org.netbeans.api.autoupdate.UpdateElement;
import org.netbeans.api.autoupdate.UpdateManager;
import org.netbeans.api.autoupdate.UpdateUnit;
import org.netbeans.core.startup.AutomaticDependencies;
import org.netbeans.core.startup.Main;
import org.netbeans.core.startup.TopLogging;
import org.netbeans.modules.autoupdate.updateprovider.DummyModuleInfo;
import org.netbeans.modules.autoupdate.updateprovider.InstalledModuleProvider;
import org.netbeans.modules.autoupdate.updateprovider.UpdateItemImpl;
import org.netbeans.spi.autoupdate.KeyStoreProvider;
import org.netbeans.spi.autoupdate.KeyStoreProvider.TrustLevel;
import org.netbeans.spi.autoupdate.UpdateItem;
import org.netbeans.updater.ModuleDeactivator;
import org.netbeans.updater.ModuleUpdater;
import org.netbeans.updater.UpdateTracking;
import org.netbeans.updater.UpdaterDispatcher;
import org.openide.filesystems.FileUtil;
import org.openide.modules.*;
import org.openide.util.*;
import org.openide.xml.XMLUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
*
* @author Jiri Rechtacek, Radek Matous
*/
public class Utilities {
public static final String N_A = "N/A";
public static final String UNSIGNED = "UNSIGNED";
public static final String SIGNATURE_UNVERIFIED = "SIGNATURE_UNVERIFIED";
public static final String SIGNATURE_VERIFIED = "SIGNATURE_VERIFIED";
public static final String TRUSTED = "TRUSTED";
public static final String MODIFIED = "MODIFIED";
private Utilities() {}
public static final String UPDATE_DIR = "update"; // NOI18N
public static final String FILE_SEPARATOR = System.getProperty("file.separator");
public static final String DOWNLOAD_DIR = UPDATE_DIR + FILE_SEPARATOR + "download"; // NOI18N
public static final String RUNNING_DOWNLOAD_DIR = UPDATE_DIR + FILE_SEPARATOR + "download-in-progress"; // NOI18N
public static final String NBM_EXTENTSION = ".nbm";
public static final String JAR_EXTENSION = ".jar"; //OSGi bundle
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat ("yyyy/MM/dd"); // NOI18N
public static final String ATTR_ESSENTIAL = "AutoUpdate-Essential-Module";
private static final String PLUGIN_MANAGER_FIRST_CLASS_MODULES = "plugin.manager.first.class.modules"; // NOI18N
private static final String USER_KS_KEY = "userKS";
private static final String USER_KS_FILE_NAME = "user.ks";
private static final String KS_USER_PASSWORD = "open4user";
private static Lookup.Result keyStoreLookupResult;
private static Map> keystoreCache = Collections.synchronizedMap(new HashMap<>());
private static final Logger err = Logger.getLogger(Utilities.class.getName ());
public static Collection getKeyStore (TrustLevel trustLevel) {
if (keyStoreLookupResult == null) {
keyStoreLookupResult = Lookup.getDefault().lookupResult(KeyStoreProvider.class);
keyStoreLookupResult.addLookupListener(new KeyStoreProviderListener());
}
List result = keystoreCache.get(trustLevel);
if (result == null) {
Collection extends KeyStoreProvider> c = keyStoreLookupResult.allInstances();
if (c == null || c.isEmpty()) {
return Collections.emptyList();
}
List kss = new ArrayList<>();
for (KeyStoreProvider provider : c) {
if (provider.getTrustLevel() == trustLevel) {
KeyStore ks = provider.getKeyStore();
if (ks != null) {
kss.add(ks);
}
}
}
result = Collections.unmodifiableList(kss);
keystoreCache.put(trustLevel, result);
}
return result;
}
/**
*
* @param archiveCertPath
* @param trustedCertificates
* @return
*/
public static String verifyCertificates(CodeSigner archiveCertPath,
Collection trustedCertificates,
Set trustedCACertificates,
Collection validateCertificates,
Set validationCACertificates) {
assert archiveCertPath != null;
List extends Certificate> archiveCertificates = archiveCertPath.getSignerCertPath().getCertificates();
if(archiveCertificates.isEmpty()) {
return UNSIGNED;
}
// Case 1: We have direct trust into one of the certificates of the
// certificate chain
if(isChainTrusted(archiveCertificates, trustedCertificates)) {
return TRUSTED;
}
// Case 2: We trust the CA, that issued a certificate - do the
// normal CertPathValidation
if(verifyCACertificatePath(trustedCACertificates, archiveCertPath)) {
return TRUSTED;
}
// Case 3: We have a list of certificates, that we directly mark as
// valid
if(isChainTrusted(archiveCertificates, validateCertificates)) {
return SIGNATURE_VERIFIED;
}
// Case 4: We trust the CA to do validation
if (verifyCACertificatePath(validationCACertificates, archiveCertPath)) {
return SIGNATURE_VERIFIED;
}
// Case 5: File is not signed
return SIGNATURE_UNVERIFIED;
}
private static boolean verifyCACertificatePath(Set trustedCACertificates, CodeSigner archiveCertPath) {
if(trustedCACertificates.isEmpty()) {
return false;
}
try {
CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
PKIXParameters verificationParameters = new PKIXParameters(trustedCACertificates);
PKIXRevocationChecker rc = (PKIXRevocationChecker) cpv.getRevocationChecker();
rc.setOptions(EnumSet.of(Option.SOFT_FAIL));
verificationParameters.addCertPathChecker(rc);
if (archiveCertPath.getTimestamp() != null) {
cpv.validate(archiveCertPath.getSignerCertPath(), verificationParameters);
verificationParameters.setDate(archiveCertPath.getTimestamp().getTimestamp());
}
// validate raises a CertPathValidatorException if validation failed
cpv.validate(archiveCertPath.getSignerCertPath(), verificationParameters);
return true;
} catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException ex) {
// InvalidAlgorithmParameterException - Should not get here - trustAnchor cannot be null -> collection cannot be empty
// NoSuchAlgorithmException - Should not get here - "PKIX" is proper algorythm
err.log(Level.SEVERE, "Certificate verification failed - " + ex.getMessage(), ex);
//SIGNATURE_UNVERIFIED - result = 0;
} catch (CertPathValidatorException ex) {
// CertPath cannot be validated
err.log(Level.INFO, "Cannot validate certificate path - " + ex.getMessage(), ex);
//SIGNATURE_UNVERIFIED - result = 0;
}
return false;
}
private static boolean isChainTrusted(Collection extends Certificate> archiveCertificates, Collection extends Certificate> trustedCertificates) {
Collection c = new HashSet<>(trustedCertificates);
c.retainAll(archiveCertificates);
return ! c.isEmpty();
}
/**
* Establishes an order for verificication result strings. Only the values
*
* - {@code null}
* - {@link #UNSIGNED}
* - {@link #SIGNATURE_UNVERIFIED}
* - {@link #SIGNATURE_VERIFIED}
* - {@link #TRUSTED}
*
*/
public static final Comparator VERIFICATION_RESULT_COMPARATOR = new Comparator() {
@Override
public int compare(String o1, String o2) {
int i1 = mapVerificationResultToInt(o1);
int i2 = mapVerificationResultToInt(o2);
return i1 - i2;
}
};
private static int mapVerificationResultToInt(String input) {
if(input == null) {
return 0;
}
switch(input) {
case UNSIGNED: return 1;
case SIGNATURE_UNVERIFIED: return 2;
case SIGNATURE_VERIFIED: return 3;
case TRUSTED: return 4;
default: throw new IllegalArgumentException("Unmappable value: " + input);
}
}
public static Collection getCertificates (KeyStore keyStore) throws KeyStoreException {
Set certs = new HashSet<> ();
for (String alias: Collections.list (keyStore.aliases ())) {
Certificate[] certificateChain = keyStore.getCertificateChain(alias);
if (certificateChain != null) {
certs.addAll(Arrays.asList(certificateChain));
}
certs.add(keyStore.getCertificate(alias));
}
return certs;
}
public static Collection getTrustAnchor (KeyStore keyStore) throws KeyStoreException {
Set certs = new HashSet<> ();
for (String alias: Collections.list (keyStore.aliases ())) {
Certificate[] certificateChain = keyStore.getCertificateChain(alias);
if (certificateChain != null) {
for(Certificate cert: certificateChain) {
if(cert instanceof X509Certificate) {
certs.add(new TrustAnchor((X509Certificate) cert, null));
}
}
}
Certificate aliasCert = keyStore.getCertificate(alias);
if(aliasCert instanceof X509Certificate) {
certs.add(new TrustAnchor((X509Certificate) aliasCert, null));
}
}
return certs;
}
/**
* Get entries packed with pack200 in given NBM file.
*
* @param nbmFile the file to scan
* @return names of entries
* @throws IOException in case of I/O error
*/
static List getNbmPack200Entries(File nbmFile) throws IOException {
List pack200Entries = new ArrayList<>();
try (JarFile jf = new JarFile(nbmFile)) {
for (JarEntry entry : Collections.list(jf.entries())) {
if (entry.getName().endsWith(".pack.gz")) {
pack200Entries.add(entry.getName());
}
}
}
return pack200Entries;
}
/**
* Get the certpaths that were used to sign the NBM content.
*
* @param nbmFile file of nbm
* @return collection of CodeSigners, that were used to sign the non-signature
* entries of the NBM
* @throws IOException
* @throws SecurityException if JAR was tampered with or if the certificate
* chains are not consistent
*/
public static Collection getNbmCertificates (File nbmFile) throws IOException, SecurityException {
Set certs = null;
// Empty means only the MANIFEST.MF is present - special cased to be in
// line with established behaviour
boolean empty = true;
// The idea:
// - iterate over all JAR entries
// - read each entry (as required by the JarFile specificiation for verification)
// - extract the certificate paths, that were used to sign the
// entry
// - compare it with the previously read entries. If they are not
// identical, raise a SecurityException.
//
// Excluded from the above algorithm are:
// - directory entries
// - files that are part of the signature (each entry in META-INF, that
// ends with .SF, .DSA, .RSA or .EC
try (JarFile jf = new JarFile(nbmFile)) {
for (JarEntry entry : Collections.list(jf.entries())) {
verifyEntry(jf, entry);
if ((! entry.isDirectory()) && (! isSignatureEntry(entry))) {
if(! entry.getName().equals("META-INF/MANIFEST.MF")) {
empty = false;
}
Set entryCerts = new HashSet<>();
CodeSigner[] codeSigners = entry.getCodeSigners();
if (codeSigners != null) {
for (CodeSigner cs : entry.getCodeSigners()) {
entryCerts.add(cs);
}
}
if(certs == null) {
certs = entryCerts;
} else if (! certs.equals(entryCerts)) {
throw new SecurityException("Inconsistent certificate paths used for signing");
}
}
}
}
return empty ? null : certs;
}
private static final Pattern SIGNATURE_PATTERN = Pattern.compile("META-INF/([^/]+)\\.(SF|DSA|RSA|EC)");
private static boolean isSignatureEntry(JarEntry je) {
return SIGNATURE_PATTERN.matcher(je.getName()).matches();
}
/**
* @throws SecurityException
*/
@SuppressWarnings("empty-statement")
private static void verifyEntry (JarFile jf, JarEntry je) throws IOException, SecurityException {
InputStream is = null;
try {
is = jf.getInputStream (je);
byte[] buffer = new byte[8192];
while ((is.read (buffer, 0, buffer.length)) != -1);
} finally {
if (is != null) is.close ();
}
}
private static class KeyStoreProviderListener implements LookupListener {
private KeyStoreProviderListener () {
}
@Override
public void resultChanged (LookupEvent ev) {
keystoreCache.clear();
}
}
private static final String ATTR_NAME = "name"; // NOI18N
private static final String ATTR_SPEC_VERSION = "specification_version"; // NOI18N
private static final String ATTR_SIZE = "size"; // NOI18N
private static final String ATTR_NBM_NAME = "nbm_name"; // NOI18N
private static File getInstallLater(File root) {
File file = new File(root.getPath() + FILE_SEPARATOR + DOWNLOAD_DIR + FILE_SEPARATOR + ModuleUpdater.LATER_FILE_NAME);
return file;
}
public static void deleteAllDoLater() {
List clusters = UpdateTracking.clusters(true);
assert clusters != null : "Clusters cannot be empty."; // NOI18N
for (File cluster : clusters) {
for (File doLater : findDoLater (cluster)) {
doLater.delete ();
}
}
}
private static Collection findDoLater (File cluster) {
if (! cluster.exists ()) {
return Collections.emptySet ();
} else {
Collection res = new HashSet ();
if (getInstallLater (cluster).exists ()) {
res.add (getInstallLater (cluster));
}
if (ModuleDeactivator.getDeactivateLater (cluster).exists ()) {
res.add (ModuleDeactivator.getDeactivateLater (cluster));
}
return res;
}
}
public static void writeInstallLater (Map updates) {
// loop for all clusters and write if needed
List clusters = UpdateTracking.clusters(true);
assert clusters != null : "Clusters cannot be empty."; // NOI18N
for (File cluster : clusters) {
writeInstallLaterToCluster (cluster, updates);
}
}
private static void writeInstallLaterToCluster (File cluster, Map updates) {
Document document = XMLUtil.createDocument(UpdateTracking.ELEMENT_MODULES, null, null, null);
Element root = document.getDocumentElement();
if (updates.isEmpty ()) {
return ;
}
boolean isEmpty = true;
for (Map.Entry entry : updates.entrySet ()) {
UpdateElementImpl elementImpl = entry.getKey();
File c = entry.getValue();
// pass this module to given cluster ?
if (cluster.equals (c)) {
Element module = document.createElement(UpdateTracking.ELEMENT_MODULE);
module.setAttribute(UpdateTracking.ATTR_CODENAMEBASE, elementImpl.getCodeName());
module.setAttribute(ATTR_NAME, elementImpl.getDisplayName());
module.setAttribute(ATTR_SPEC_VERSION, elementImpl.getSpecificationVersion().toString());
module.setAttribute(ATTR_SIZE, Long.toString(elementImpl.getDownloadSize()));
module.setAttribute(ATTR_NBM_NAME, InstallSupportImpl.getDestination(cluster, elementImpl.getCodeName(), elementImpl.getInstallInfo().getDistribution()).getName());
root.appendChild( module );
isEmpty = false;
}
}
if (isEmpty) {
return ;
}
writeXMLDocumentToFile (document, getInstallLater (cluster));
}
private static void writeXMLDocumentToFile (Document doc, File dest) {
doc.getDocumentElement ().normalize ();
dest.getParentFile ().mkdirs ();
InputStream is = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream ();
OutputStream fos = null;
try {
try {
XMLUtil.write (doc, bos, "UTF-8"); // NOI18N
bos.close ();
fos = new FileOutputStream (dest);
is = new ByteArrayInputStream (bos.toByteArray ());
FileUtil.copy (is, fos);
} finally {
if (is != null) {
is.close ();
}
if (fos != null) {
fos.close ();
}
bos.close ();
}
} catch (java.io.FileNotFoundException fnfe) {
Exceptions.printStackTrace (fnfe);
} catch (java.io.IOException ioe) {
Exceptions.printStackTrace (ioe);
} finally {
try {
bos.close ();
} catch (IOException x) {
Exceptions.printStackTrace (x);
}
}
}
public static void writeDeactivateLater (Collection files) {
File userdir = InstallManager.getUserDir ();
assert userdir != null && userdir.exists (): "Userdir " + userdir + " found and exists."; // NOI18N
writeMarkedFilesToFile (files, ModuleDeactivator.getDeactivateLater (userdir));
}
public static void writeFileMarkedForDelete (Collection files) {
writeMarkedFilesToFile (files, ModuleDeactivator.getControlFileForMarkedForDelete (InstallManager.getUserDir ()));
}
public static void writeFileMarkedForDisable (Collection files) {
writeMarkedFilesToFile (files, ModuleDeactivator.getControlFileForMarkedForEnableDisable(InstallManager.getUserDir (), false));
}
public static void writeFileMarkedForEnable (Collection files) {
writeMarkedFilesToFile (files, ModuleDeactivator.getControlFileForMarkedForEnableDisable(InstallManager.getUserDir (), true));
}
private static void writeMarkedFilesToFile (Collection files, File dest) {
// don't forget for content written before
StringBuilder content = new StringBuilder();
if (dest.exists ()) {
content.append(ModuleDeactivator.readStringFromFile (dest));
}
for (File f : files) {
content.append(f.getAbsolutePath ());
content.append(UpdateTracking.PATH_SEPARATOR);
}
if (content.length () == 0) {
return ;
}
dest.getParentFile ().mkdirs ();
assert dest.getParentFile ().exists () && dest.getParentFile ().isDirectory () : "Parent of " + dest + " exists and is directory.";
InputStream is = null;
OutputStream fos = null;
try {
try {
fos = new FileOutputStream (dest);
is = new ByteArrayInputStream (content.toString().getBytes());
FileUtil.copy (is, fos);
} finally {
if (is != null) is.close();
if (fos != null) fos.close();
}
} catch (java.io.FileNotFoundException fnfe) {
Exceptions.printStackTrace(fnfe);
} catch (java.io.IOException ioe) {
Exceptions.printStackTrace(ioe);
}
}
public static void writeAdditionalInformation (Map updates) {
// loop for all clusters and write if needed
List clusters = UpdateTracking.clusters (true);
assert clusters != null : "Clusters cannot be empty."; // NOI18N
for (File cluster : clusters) {
writeAdditionalInformationToCluster (cluster, updates);
}
}
public static File locateUpdateTracking (ModuleInfo m) {
String fileNameToFind = UpdateTracking.TRACKING_FILE_NAME + '/' + m.getCodeNameBase ().replace ('.', '-') + ".xml"; // NOI18N
return InstalledFileLocator.getDefault ().locate (fileNameToFind, m.getCodeNameBase (), false);
}
public static String readSourceFromUpdateTracking (ModuleInfo m) {
String res = null;
File ut = locateUpdateTracking (m);
if (ut != null) {
Node n = getModuleConfiguration (ut);
if (n != null) {
Node attrOrigin = n.getAttributes ().getNamedItem (UpdateTracking.ATTR_ORIGIN);
assert attrOrigin != null : "ELEMENT_VERSION must contain ATTR_ORIGIN attribute.";
if (! (UpdateTracking.UPDATER_ORIGIN.equals (attrOrigin.getNodeValue ()) ||
UpdateTracking.INSTALLER_ORIGIN.equals (attrOrigin.getNodeValue ()))) {
// ignore default value
res = attrOrigin.getNodeValue ();
}
}
}
return res;
}
public static Date readInstallTimeFromUpdateTracking (ModuleInfo m) {
Date res = null;
String time = null;
File ut = locateUpdateTracking (m);
if (ut != null) {
Node n = getModuleConfiguration (ut);
if (n != null) {
Node attrInstallTime = n.getAttributes ().getNamedItem (UpdateTracking.ATTR_INSTALL);
assert attrInstallTime != null : "ELEMENT_VERSION must contain ATTR_INSTALL attribute.";
time = attrInstallTime.getNodeValue ();
}
}
if (time != null) {
try {
long lTime = Long.parseLong (time);
res = new Date (lTime);
} catch (NumberFormatException nfe) {
getLogger ().log (Level.INFO, nfe.getMessage (), nfe);
}
}
return res;
}
static void writeUpdateOfUpdaterJar (JarEntry updaterJarEntry, File zipFileWithUpdater, File targetCluster) throws IOException {
JarFile jf = new JarFile(zipFileWithUpdater);
String entryPath = updaterJarEntry.getName();
String entryName = entryPath.contains("/") ? entryPath.substring(entryPath.lastIndexOf("/") + 1) : entryPath;
File dest = new File (targetCluster, UpdaterDispatcher.UPDATE_DIR + // updater
UpdateTracking.FILE_SEPARATOR + UpdaterDispatcher.NEW_UPDATER_DIR + // new_updater
UpdateTracking.FILE_SEPARATOR + entryName
);
dest.getParentFile ().mkdirs ();
assert dest.getParentFile ().exists () && dest.getParentFile ().isDirectory () : "Parent of " + dest + " exists and is directory.";
InputStream is = null;
OutputStream fos = null;
try {
try {
fos = new FileOutputStream (dest);
is = jf.getInputStream (updaterJarEntry);
FileUtil.copy (is, fos);
} finally {
if (is != null) is.close();
if (fos != null) fos.close();
jf.close();
}
} catch (java.io.FileNotFoundException fnfe) {
getLogger ().log (Level.SEVERE, fnfe.getLocalizedMessage (), fnfe);
} catch (java.io.IOException ioe) {
getLogger ().log (Level.SEVERE, ioe.getLocalizedMessage (), ioe);
}
}
static void cleanUpdateOfUpdaterJar () {
// loop for all clusters and clean if needed
List clusters = UpdateTracking.clusters (true);
assert clusters != null : "Clusters cannot be empty."; // NOI18N
for (File cluster : clusters) {
File updaterDir = new File (cluster, UpdaterDispatcher.UPDATE_DIR + UpdateTracking.FILE_SEPARATOR + UpdaterDispatcher.NEW_UPDATER_DIR);
if (updaterDir.exists () && updaterDir.isDirectory ()) {
for (File f : updaterDir.listFiles ()) {
f.delete ();
}
updaterDir.delete ();
}
}
}
static Module toModule(UpdateUnit uUnit) {
return getModuleInstance(uUnit.getCodeName(), null); // XXX
}
public static Module toModule(String codeNameBase) {
ModuleInfo mi = ModuleCache.getInstance().find(codeNameBase);
if (mi instanceof Module) {
return (Module)mi;
}
return null;
}
public static Module toModule(String codeNameBase, SpecificationVersion specificationVersion) {
return getModuleInstance(codeNameBase, specificationVersion);
}
public static Module toModule (ModuleInfo info) {
Module m = getModuleInstance (info.getCodeNameBase(), info.getSpecificationVersion ());
if (m == null && info instanceof Module) {
m = (Module)info;
}
return m;
}
public static boolean isFixed (ModuleInfo info) {
Module m = toModule (info);
assert ! info.isEnabled () || m != null : "Module found for enabled " + info;
return m == null ? false : m.isFixed ();
}
public static boolean isValid (ModuleInfo info) {
Module m = toModule (info);
assert ! info.isEnabled () || m != null : "Module found for enabled " + info;
return m == null ? false : m.isValid ();
}
static UpdateUnit toUpdateUnit(Module m) {
return UpdateManagerImpl.getInstance().getUpdateUnit(m.getCodeNameBase());
}
static UpdateUnit toUpdateUnit(String codeNameBase) {
return UpdateManagerImpl.getInstance().getUpdateUnit(codeNameBase);
}
public static Set findRequiredUpdateElements (UpdateElement element,
Collection infos,
Set brokenDependencies,
boolean topAggressive,
Collection recommended) {
Set retval = new HashSet ();
switch (element.getUpdateUnit().getType ()) {
case KIT_MODULE :
case MODULE :
boolean avoidRecommended = recommended != null && ! recommended.isEmpty();
ModuleUpdateElementImpl el = (ModuleUpdateElementImpl) Trampoline.API.impl(element);
Set deps = new HashSet (el.getModuleInfo ().getDependencies ());
Set availableInfos = new HashSet (infos);
maybeAddImplicitHostDependency(element, deps);
int max_counter = el.getType().equals(UpdateManager.TYPE.KIT_MODULE) ? 2 : 1;
int counter = max_counter;
boolean aggressive = topAggressive && counter > 0;
Set all = new HashSet();
for (;;) {
Set newones = processDependencies(deps, retval, availableInfos, brokenDependencies, element, aggressive, recommended, avoidRecommended);
newones.removeAll(all);
//issue #247884 Autoupdate should force restart when a new module enables module which is a fragment of other module
for (Dependency dep : deps) {
UpdateUnit uu = toUpdateUnit(dep.getName());
if (uu != null && uu.getInstalled() != null) {
ModuleUpdateElementImpl em = (ModuleUpdateElementImpl) Trampoline.API.impl(uu.getInstalled());
// fragments which are installed, but not enabled yet, and have live host module will cause
// restart
if (em.getInstallInfo().getUpdateItemImpl().isFragment() && !uu.getInstalled().isEnabled()) {
String fh = em.getInstallInfo().getUpdateItemImpl().getFragmentHost();
Module m = Utilities.toModule(fh);
if (m != null && m.isEnabled()) {
el.getInstallInfo().getUpdateItemImpl().setNeedsRestart(true);
}
}
}
}
if (newones.isEmpty()) {
break;
}
all.addAll(newones);
deps = newones;
}
Set moreBroken = new HashSet ();
Set tmp = new HashSet (availableInfos);
Set more;
counter = max_counter;
aggressive = topAggressive && counter > 0;
while (retval.addAll (more = handleBackwardCompatability (tmp, moreBroken, aggressive))) {
if (! moreBroken.isEmpty ()) {
brokenDependencies.addAll (moreBroken);
break;
}
for (UpdateElement e : more) {
//infos.addAll (Trampoline.API.impl (el).getModuleInfos ());
tmp.add (((ModuleUpdateElementImpl) Trampoline.API.impl (e)).getModuleInfo ());
}
aggressive = aggressive && (counter--) > 0;
}
if (! moreBroken.isEmpty ()) {
brokenDependencies.addAll (moreBroken);
}
break;
case STANDALONE_MODULE :
case FEATURE :
FeatureUpdateElementImpl feature = (FeatureUpdateElementImpl) Trampoline.API.impl(element);
aggressive = topAggressive;
for (ModuleUpdateElementImpl module : feature.getContainedModuleElements ()) {
retval.addAll (findRequiredUpdateElements (module.getUpdateElement (), infos, brokenDependencies, aggressive, recommended));
}
break;
case CUSTOM_HANDLED_COMPONENT :
getLogger ().log (Level.INFO, "CUSTOM_HANDLED_COMPONENT doesn't care about required elements."); // XXX
break;
default:
assert false : "Not implement for type " + element.getUpdateUnit() + " of UpdateElement " + element;
}
return retval;
}
private static Reference
© 2015 - 2025 Weber Informatics LLC | Privacy Policy