blob: 7c05dd2e377c152f8bbed227c77841b9fc1e020b [file] [log] [blame]
/*
* 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.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.Security;
import java.security.cert.CertPath;
import java.security.cert.CertPathValidator;
import java.security.cert.CertPathValidatorException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXCertPathValidatorResult;
import java.security.cert.PKIXParameters;
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 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.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<KeyStoreProvider> result;
private static final Logger err = Logger.getLogger(Utilities.class.getName ());
public static Collection<KeyStore> getKeyStore () {
if (result == null) {
result = Lookup.getDefault ().lookupResult (KeyStoreProvider.class);
result.addLookupListener (new KeyStoreProviderListener ());
}
Collection<? extends KeyStoreProvider> c = result.allInstances ();
if (c == null || c.isEmpty ()) {
return Collections.emptyList ();
}
List<KeyStore> kss = new ArrayList ();
for (KeyStoreProvider provider : c) {
KeyStore ks = provider.getKeyStore ();
if (ks != null) {
kss.add (ks);
}
}
return kss;
}
public static String verifyCertificates(Collection<Certificate> archiveCertificates, Collection<Certificate> trustedCertificates) {
if (archiveCertificates == null) {
return N_A;
}
if (!archiveCertificates.isEmpty()) {
Collection<Certificate> c = new HashSet(trustedCertificates);
c.retainAll(archiveCertificates);
if (c.isEmpty()) {
Map<Principal, X509Certificate> certSubjectsMap = new HashMap();
Set<Principal> certIssuersSet = new HashSet();
for (Certificate cert : archiveCertificates) {
if (cert != null) {
X509Certificate x509Cert = (X509Certificate) cert;
certSubjectsMap.put(x509Cert.getSubjectDN(), x509Cert);
if (x509Cert.getIssuerDN() != null) {
certIssuersSet.add(x509Cert.getIssuerDN());
}
}
}
Map<X509Certificate, X509Certificate> candidates = new HashMap();
for (Principal p : certSubjectsMap.keySet()) {
// cert chain may not be ordered - trust anchor could before certificate itself
if (certIssuersSet.contains(p)) {
continue;
}
X509Certificate cert = certSubjectsMap.get(p);
Principal tap = cert.getIssuerDN();
if (tap != null) {
X509Certificate tempTrustAnchor = certSubjectsMap.get(tap);
if (tempTrustAnchor != null) {
candidates.put(cert, tempTrustAnchor);
}
}
}
// TRUSTED = 2
// SIGNATURE_VERIFIED = 1
// SIGNATURE_UNVERIFIED = 0
int res = 0;
for (X509Certificate cert : candidates.keySet()) {
X509Certificate trustCert = candidates.get(cert);
PKIXCertPathValidatorResult validResult = null;
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
List certList = new ArrayList();
certList.add(cert);
CertPath cp = cf.generateCertPath(certList);
TrustAnchor trustAnchor = new TrustAnchor(trustCert, null);
PKIXParameters params = new PKIXParameters(Collections.singleton(trustAnchor));
params.setRevocationEnabled(true);
Security.setProperty("ocsp.enable", "true");
System.setProperty("com.sun.security.enableCRLDP", "true"); // CRL fallback
CertPathValidator cpv = CertPathValidator.getInstance("PKIX");
validResult = (PKIXCertPathValidatorResult) cpv.validate(cp, params);
} catch (CertificateException | InvalidAlgorithmParameterException | NoSuchAlgorithmException ex) {
// CertificateException - Should not get here - "X.509" is proper certificate type
// 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;
} catch (SecurityException ex) {
// When jar/nbm correctly signed, but content modified
err.log(Level.INFO, "The content of the jar/nbm has been modified - " + ex.getMessage(), ex);
return MODIFIED;
}
if (validResult != null) {
String certDNName = cert.getSubjectDN().getName();
if (certDNName.contains("CN=\"Oracle America, Inc.\"")
&& (certDNName.contains("OU=Software Engineering") || certDNName.contains("OU=Code Signing Bureau"))) {
res = 2;
break;
} else {
res = 1;
}
}
}
switch (res) {
case 2:
return TRUSTED;
case 1:
return SIGNATURE_VERIFIED;
default:
return SIGNATURE_UNVERIFIED;
}
} else {
// signed by trusted certificate stored in user's keystore od ide.ks
return TRUSTED;
}
}
return UNSIGNED;
}
public static Collection<Certificate> getCertificates (KeyStore keyStore) throws KeyStoreException {
Set<Certificate> certs = new HashSet<Certificate> ();
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<Certificate> getNbmCertificates (File nbmFile) throws IOException {
Set<Certificate> certs = new HashSet<Certificate>();
JarFile jf = new JarFile(nbmFile);
boolean empty = true;
try {
for (JarEntry entry : Collections.list(jf.entries())) {
verifyEntry(jf, entry);
if (!entry.getName().startsWith("META-INF/")) {
empty = false;
if (entry.getCertificates() != null) {
certs.addAll(Arrays.asList(entry.getCertificates()));
}
}
}
} finally {
jf.close();
}
return empty ? null : certs;
}
/**
* @throws SecurityException
*/
@SuppressWarnings("empty-statement")
private static void verifyEntry (JarFile jf, JarEntry je) throws IOException {
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 ();
}
}
static private class KeyStoreProviderListener implements LookupListener {
private KeyStoreProviderListener () {
}
@Override
public void resultChanged (LookupEvent ev) {
result = null;
}
}
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<File> 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<File> findDoLater (File cluster) {
if (! cluster.exists ()) {
return Collections.emptySet ();
} else {
Collection<File> res = new HashSet<File> ();
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<UpdateElementImpl, File> updates) {
// loop for all clusters and write if needed
List<File> 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<UpdateElementImpl, File> updates) {
Document document = XMLUtil.createDocument(UpdateTracking.ELEMENT_MODULES, null, null, null);
Element root = document.getDocumentElement();
if (updates.isEmpty ()) {
return ;
}
boolean isEmpty = true;
for (UpdateElementImpl elementImpl : updates.keySet ()) {
File c = updates.get(elementImpl);
// 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<File> 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<File> files) {
writeMarkedFilesToFile (files, ModuleDeactivator.getControlFileForMarkedForDelete (InstallManager.getUserDir ()));
}
public static void writeFileMarkedForDisable (Collection<File> files) {
writeMarkedFilesToFile (files, ModuleDeactivator.getControlFileForMarkedForDisable (InstallManager.getUserDir ()));
}
private static void writeMarkedFilesToFile (Collection<File> 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<UpdateElementImpl, File> updates) {
// loop for all clusters and write if needed
List<File> 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<File> 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<UpdateElement> findRequiredUpdateElements (UpdateElement element,
Collection<ModuleInfo> infos,
Set<Dependency> brokenDependencies,
boolean topAggressive,
Collection<UpdateElement> recommended) {
Set<UpdateElement> retval = new HashSet<UpdateElement> ();
switch (element.getUpdateUnit().getType ()) {
case KIT_MODULE :
case MODULE :
boolean avoidRecommended = recommended != null && ! recommended.isEmpty();
ModuleUpdateElementImpl el = (ModuleUpdateElementImpl) Trampoline.API.impl(element);
Set<Dependency> deps = new HashSet<Dependency> (el.getModuleInfo ().getDependencies ());
Set<ModuleInfo> availableInfos = new HashSet<ModuleInfo> (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<Dependency> all = new HashSet<Dependency>();
for (;;) {
Set<Dependency> 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<Dependency> moreBroken = new HashSet<Dependency> ();
Set<ModuleInfo> tmp = new HashSet<ModuleInfo> (availableInfos);
Set<UpdateElement> 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<Map<ModuleInfo, Set<UpdateElement>>> cachedInfo2RequestedReference = null;
private static Set<UpdateElement> handleBackwardCompatability4ModuleInfo (ModuleInfo mi, Set<ModuleInfo> forInstall, Set<Dependency> brokenDependencies, boolean aggressive) {
if (cachedInfo2RequestedReference != null && cachedInfo2RequestedReference.get() != null) {
Set<UpdateElement> requested = cachedInfo2RequestedReference.get().get(mi);
if (requested != null) {
return requested;
}
}
UpdateUnit u = UpdateManagerImpl.getInstance().getUpdateUnit(mi.getCodeNameBase());
Set<UpdateElement> moreRequested = new HashSet<UpdateElement>();
// invalid codenamebase (in unit tests)
if (u == null) {
return moreRequested;
}
// not installed, not need to handle backward compatability
UpdateElement i = u.getInstalled();
if (i == null) {
return moreRequested;
}
// Dependency.TYPE_MODULE
Collection<Dependency> dependencies = new HashSet<Dependency>();
dependencies.addAll(Dependency.create(Dependency.TYPE_MODULE, mi.getCodeName()));
SortedSet<String> newTokens = new TreeSet<String>(Arrays.asList(mi.getProvides()));
SortedSet<String> oldTokens = new TreeSet<String>(Arrays.asList(((ModuleUpdateElementImpl) Trampoline.API.impl(i)).getModuleInfo().getProvides()));
oldTokens.removeAll(newTokens);
// handle diff
for (String tok : oldTokens) {
// don't care about provider of platform dependency here
if (tok.startsWith("org.openide.modules.os") || tok.startsWith("org.openide.modules.jre")) { // NOI18N
continue;
}
dependencies.addAll(Dependency.create(Dependency.TYPE_REQUIRES, tok));
dependencies.addAll(Dependency.create(Dependency.TYPE_NEEDS, tok));
}
for (Dependency d : dependencies) {
DependencyAggregator deco = DependencyAggregator.getAggregator(d);
int type = d.getType();
String name = d.getName();
Collection<ModuleInfo> dependings = deco.getDependening();
synchronized (dependings) {
for (ModuleInfo depMI : dependings) {
Module depM = getModuleInstance(depMI.getCodeNameBase(), depMI.getSpecificationVersion());
if (depM == null) {
continue;
}
if (!depM.getProblems().isEmpty()) {
// skip this module because it has own problems already
continue;
}
for (Dependency toTry : depM.getDependencies()) {
// check only relevant deps
if (type == toTry.getType() && name.equals(toTry.getName())
&& !DependencyChecker.checkDependencyModule(toTry, mi)) {
UpdateUnit tryUU = UpdateManagerImpl.getInstance().getUpdateUnit(depM.getCodeNameBase());
if (!tryUU.getAvailableUpdates().isEmpty()) {
UpdateElement tryUE = tryUU.getAvailableUpdates().get(0);
ModuleInfo tryUpdated = ((ModuleUpdateElementImpl) Trampoline.API.impl(tryUE)).getModuleInfo();
Set<Dependency> deps = new HashSet<Dependency>(tryUpdated.getDependencies());
Set<ModuleInfo> availableInfos = new HashSet<ModuleInfo>(forInstall);
Set<Dependency> newones;
maybeAddImplicitHostDependency(tryUE, deps);
while (!(newones = processDependencies(deps, moreRequested, availableInfos, brokenDependencies, tryUE, aggressive, null, false)).isEmpty()) {
deps = newones;
}
moreRequested.add(tryUE);
}
}
}
}
}
}
if (cachedInfo2RequestedReference == null || cachedInfo2RequestedReference.get() == null) {
cachedInfo2RequestedReference = new WeakReference<Map<ModuleInfo, Set<UpdateElement>>>
(new HashMap<ModuleInfo, Set<UpdateElement>>());
}
cachedInfo2RequestedReference.get().put(mi, moreRequested);
return moreRequested;
}
private static Reference<Set<ModuleInfo>> cachedInfosReference = null;
private static Reference<Set<UpdateElement>> cachedResultReference = null;
private static Set<UpdateElement> handleBackwardCompatability (Set<ModuleInfo> forInstall, Set<Dependency> brokenDependencies, boolean aggressive) {
if (cachedInfosReference != null) {
Set<ModuleInfo> cir = cachedInfosReference.get();
if (cir != null && cir.equals(forInstall) ) {
if (cachedResultReference != null) {
Set<UpdateElement> crr = cachedResultReference.get();
if (crr != null) {
return crr;
}
}
}
}
cachedInfosReference = new WeakReference<Set<ModuleInfo>>(forInstall);
err.finest("calling handleBackwardCompatability(size: " + forInstall.size() + ")");
Set<UpdateElement> moreRequested = new HashSet<UpdateElement> ();
// backward compatibility
for (ModuleInfo mi : forInstall) {
moreRequested.addAll(handleBackwardCompatability4ModuleInfo(mi, forInstall, brokenDependencies, aggressive));
}
cachedResultReference = new WeakReference<Set<UpdateElement>>(moreRequested);
return moreRequested;
}
private static boolean maybeAddImplicitHostDependency(UpdateElement el, Set<Dependency> deps) {
// check fragment, add implicit dependencies
UpdateElementImpl elImpl = Trampoline.API.impl(el);
UpdateItemImpl uiImpl = elImpl.getInstallInfo().getUpdateItemImpl();
if (!uiImpl.isFragment()) {
return false;
}
String fhost = uiImpl.getFragmentHost();
Collection<Dependency> hostDep = Dependency.create(Dependency.TYPE_MODULE, fhost);
err.fine(hostDep.toString());
return deps.addAll(hostDep);
}
private static Set<Dependency> processDependencies (final Set<Dependency> original,
Set<UpdateElement> retval,
Set<ModuleInfo> availableInfos,
Set<Dependency> brokenDependencies,
UpdateElement el,
boolean agressive,
Collection<UpdateElement> recommended,
boolean avoidRecommended) {
Set<Dependency> res = new HashSet<Dependency> ();
AutomaticDependencies.Report rep = AutomaticDependencies.getDefault().refineDependenciesAndReport(el.getCodeName(), original);
if (rep.isModified()) {
err.fine(rep.toString());
}
for (Dependency dep : original) {
if (Dependency.TYPE_RECOMMENDS == dep.getType() && avoidRecommended) {
continue;
}
Collection<UpdateElement> requestedElements = handleDependency (el, dep, availableInfos, brokenDependencies, agressive);
if (requestedElements != null) {
if (Dependency.TYPE_RECOMMENDS == dep.getType()) {
if (recommended != null) {
recommended.addAll(requestedElements);
}
}
for (UpdateElement req : requestedElements) {
ModuleUpdateElementImpl reqM = (ModuleUpdateElementImpl) Trampoline.API.impl (req);
availableInfos.add (reqM.getModuleInfo ());
retval.add (req);
res.addAll (reqM.getModuleInfo ().getDependencies ());
maybeAddImplicitHostDependency(req, res);
}
}
}
res.removeAll (original);
return res;
}
public static Collection<UpdateElement> handleDependency (UpdateElement el,
Dependency dep,
Collection<ModuleInfo> availableInfos,
Set<Dependency> brokenDependencies,
boolean beAggressive) {
Collection<UpdateElement> requested = new HashSet<UpdateElement> ();
switch (dep.getType ()) {
case Dependency.TYPE_JAVA :
if (! DependencyChecker.matchDependencyJava (dep)) {
brokenDependencies.add (dep);
}
break;
case Dependency.TYPE_PACKAGE :
if (! DependencyChecker.matchPackageDependency (dep)) {
brokenDependencies.add (dep);
}
break;
case Dependency.TYPE_MODULE :
Collection<UpdateUnit> reqUnits = DependencyAggregator.getRequested (dep);
assert reqUnits == null || reqUnits.isEmpty() || reqUnits.size() == 1 : dep + " returns null, empty or only once module, but returns " + reqUnits;
boolean matched = false;
UpdateUnit u = reqUnits == null || reqUnits.isEmpty() ? null : reqUnits.iterator().next();
if (u != null) {
boolean aggressive = beAggressive;
if (aggressive && (isFirstClassModule(el) || u.getType() == UpdateManager.TYPE.KIT_MODULE)) {
aggressive = false;
}
// follow aggressive updates strategy
// if new module update is available, promote it even though installed one suites all the dependendencies
if (u.getInstalled() != null) {
UpdateElementImpl reqElImpl = Trampoline.API.impl(u.getInstalled());
matched = DependencyChecker.checkDependencyModule(dep, ((ModuleUpdateElementImpl) reqElImpl).getModuleInfo());
}
if (!matched) {
for (ModuleInfo m : availableInfos) {
if (DependencyChecker.checkDependencyModule(dep, m)) {
matched = true;
break;
}
}
}
if (aggressive || !matched) {
UpdateElement reqEl = u.getAvailableUpdates().isEmpty() ? null : u.getAvailableUpdates().get(0);
if (reqEl != null) {
UpdateElementImpl reqElImpl = Trampoline.API.impl(reqEl);
ModuleUpdateElementImpl reqModuleImpl = (ModuleUpdateElementImpl) reqElImpl;
ModuleInfo info = reqModuleImpl.getModuleInfo();
if (DependencyChecker.checkDependencyModule(dep, info)) {
if (!availableInfos.contains(info)) {
requested.add(reqEl);
matched = true;
}
}
}
}
} else {
for (ModuleInfo m : availableInfos) {
if (DependencyChecker.checkDependencyModule(dep, m)) {
matched = true;
break;
}
}
}
if (!matched) {
brokenDependencies.add(dep);
}
break;
case Dependency.TYPE_REQUIRES :
case Dependency.TYPE_NEEDS :
case Dependency.TYPE_RECOMMENDS :
if (DummyModuleInfo.TOKEN_MODULE_FORMAT1.equals (dep.getName ()) ||
DummyModuleInfo.TOKEN_MODULE_FORMAT2.equals (dep.getName ())) {
// these tokens you can ignore here
break;
}
Collection<UpdateUnit> requestedUnits = DependencyAggregator.getRequested (dep);
boolean passed = false;
if (requestedUnits == null || requestedUnits.isEmpty()) {
// look on availableInfos as well
for (ModuleInfo m : availableInfos) {
if (Arrays.asList (m.getProvides ()).contains (dep.getName ())) {
passed = true;
break;
}
}
} else {
passed = true;
for (UpdateUnit uu : requestedUnits) {
if (! uu.getAvailableUpdates ().isEmpty ()) {
requested.add(uu.getAvailableUpdates ().get (0));
}
}
}
if (! passed && Dependency.TYPE_RECOMMENDS != dep.getType ()) {
brokenDependencies.add (dep);
}
break;
}
return requested;
}
static Set<String> getBrokenDependencies (UpdateElement element, List<ModuleInfo> infos) {
assert element != null : "UpdateElement cannot be null";
Set<Dependency> brokenDependencies = new HashSet<Dependency> ();
// create init collection of brokenDependencies
Utilities.findRequiredUpdateElements (element, infos, brokenDependencies, false, new HashSet<UpdateElement>());
// backward compatibility
for (ModuleInfo mi : infos) {
UpdateUnit u = UpdateManagerImpl.getInstance ().getUpdateUnit (mi.getCodeNameBase ());
// invalid codenamebase (in unit tests)
if (u == null) {
continue;
}
// not installed, not need to handle backward compatability
UpdateElement i = u.getInstalled ();
if (i == null) {
continue;
}
// maybe newer version is processed as a update
if (! u.getAvailableUpdates ().isEmpty ()) {
UpdateElement ue = u.getAvailableUpdates ().get (0);
ModuleInfo newerMI = ((ModuleUpdateElementImpl) Trampoline.API.impl (ue)).getModuleInfo ();
if (infos.contains (newerMI)) {
continue;
}
}
// Dependency.TYPE_MODULE
for (Dependency d : Dependency.create (Dependency.TYPE_MODULE, mi.getCodeName ())) {
DependencyAggregator deco = DependencyAggregator.getAggregator (d);
Collection<ModuleInfo> dependings = deco.getDependening();
synchronized (dependings) {
for (ModuleInfo depMI : dependings) {
//Module depM = Utilities.toModule (depMI);
Module depM = getModuleInstance(depMI.getCodeNameBase(), depMI.getSpecificationVersion());
if (depM == null) {
continue;
}
if (! depM.getProblems ().isEmpty ()) {
// skip this module because it has own problems already
continue;
}
for (Dependency toTry : depM.getDependencies ()) {
// check only relevant deps
if (deco.equals (DependencyAggregator.getAggregator (toTry)) &&
! DependencyChecker.checkDependencyModule (toTry, mi)) {
brokenDependencies.add (toTry);
}
}
}
}
}
// Dependency.TYPE_REQUIRES
// Dependency.TYPE_NEEDS
SortedSet<String> newTokens = new TreeSet<String> (Arrays.asList (mi.getProvides ()));
SortedSet<String> oldTokens = new TreeSet<String> (Arrays.asList (((ModuleUpdateElementImpl) Trampoline.API.impl (i)).getModuleInfo ().getProvides ()));
oldTokens.removeAll (newTokens);
// handle diff
for (String tok : oldTokens) {
Collection<Dependency> deps = new HashSet<Dependency> (Dependency.create (Dependency.TYPE_REQUIRES, tok));
deps.addAll (Dependency.create (Dependency.TYPE_NEEDS, tok));
for (Dependency d : deps) {
DependencyAggregator deco = DependencyAggregator.getAggregator (d);
Collection<ModuleInfo> dependings = deco.getDependening();
synchronized (dependings) {
for (ModuleInfo depMI : dependings) {
//Module depM = Utilities.toModule (depMI);
Module depM = getModuleInstance(depMI.getCodeNameBase(), depMI.getSpecificationVersion());
if (depM == null) {
continue;
}
if (! depM.getProblems ().isEmpty ()) {
// skip this module because it has own problems already
continue;
}
for (Dependency toTry : depM.getDependencies ()) {
// check only relevant deps
if (deco.equals (DependencyAggregator.getAggregator (toTry))) {
brokenDependencies.add (toTry);
}
}
}
}
}
}
}
Set<String> retval = new HashSet<String> (brokenDependencies.size ());
for (Dependency dep : brokenDependencies) {
retval.add (dep.toString ());
}
return retval;
}
static Set<String> getBrokenDependenciesInInstalledModules (UpdateElement element) {
assert element != null : "UpdateElement cannot be null";
Set<Dependency> deps = new HashSet<Dependency> ();
for (ModuleInfo m : getModuleInfos (Collections.singleton (element))) {
deps.addAll (DependencyChecker.findBrokenDependenciesTransitive (m,
InstalledModuleProvider.getInstalledModules ().values (),
new HashSet<ModuleInfo> ()));
}
Set<String> retval = new HashSet<String> ();
for (Dependency dep : deps) {
retval.add (dep.toString ());
}
return retval;
}
private static List<ModuleInfo> getModuleInfos (Collection<UpdateElement> elements) {
List<ModuleInfo> infos = new ArrayList<ModuleInfo> (elements.size ());
for (UpdateElement el : elements) {
if (el.getUpdateUnit () != null && el.getUpdateUnit ().isPending ()) {
// cannot depend of UpdateElement in pending state
continue;
}
UpdateElementImpl impl = Trampoline.API.impl (el);
infos.addAll (impl.getModuleInfos ());
}
return infos;
}
private static Module getModuleInstance(String codeNameBase, SpecificationVersion specificationVersion) {
ModuleInfo mi = ModuleCache.getInstance().find(codeNameBase);
if (mi instanceof Module) {
Module m = (Module)mi;
if (specificationVersion == null) {
err.log(Level.FINE, "no module {0} for null version", m);
return m;
} else {
SpecificationVersion version = m.getSpecificationVersion();
if (version == null) {
err.log(Level.FINER, "No version for {0}", m);
return null;
}
final int res = version.compareTo(specificationVersion);
err.log(Level.FINER, "Comparing versions: {0}.compareTo({1}) = {2}", new Object[]{version, specificationVersion, res});
return res >= 0 ? m : null;
}
}
return null;
}
public static boolean isAutomaticallyEnabled(String codeNameBase) {
Module m = getModuleInstance(codeNameBase, null);
return m != null ? (m.isAutoload() || m.isEager() || m.isFixed()) : false;
}
public static ModuleInfo takeModuleInfo (UpdateElement el) {
UpdateElementImpl impl = Trampoline.API.impl (el);
assert impl instanceof ModuleUpdateElementImpl;
return ((ModuleUpdateElementImpl) impl).getModuleInfo ();
}
private static String productVersion = null;
public static String getProductVersion () {
if (productVersion == null) {
String buildNumber = System.getProperty ("netbeans.buildnumber"); // NOI18N
productVersion = NbBundle.getMessage (TopLogging.class, "currentVersion", buildNumber); // NOI18N
}
return productVersion;
}
private static Node getModuleConfiguration (File moduleUpdateTracking) {
Document document;
InputStream is;
try {
is = new BufferedInputStream (new FileInputStream (moduleUpdateTracking));
InputSource xmlInputSource = new InputSource (is);
document = XMLUtil.parse (xmlInputSource, false, false, null, org.openide.xml.EntityCatalog.getDefault ());
is.close ();
} catch (SAXException saxe) {
getLogger ().log (Level.INFO, "SAXException when reading " + moduleUpdateTracking, saxe);
return null;
} catch (IOException ioe) {
getLogger ().log (Level.INFO, "IOException when reading " + moduleUpdateTracking, ioe);
return null;
}
assert document.getDocumentElement () != null : "File " + moduleUpdateTracking + " must contain <module> element.";
if (document.getDocumentElement () == null) {
return null;
}
return getModuleElement (document.getDocumentElement ());
}
private static Node getModuleElement (Element element) {
Node lastElement = null;
assert UpdateTracking.ELEMENT_MODULE.equals (element.getTagName ()) : "The root element is: " + UpdateTracking.ELEMENT_MODULE + " but was: " + element.getTagName ();
NodeList listModuleVersions = element.getElementsByTagName (UpdateTracking.ELEMENT_VERSION);
for (int i = 0; i < listModuleVersions.getLength (); i++) {
lastElement = getModuleLastVersion (listModuleVersions.item (i));
if (lastElement != null) {
break;
}
}
return lastElement;
}
private static Node getModuleLastVersion (Node version) {
Node attrLast = version.getAttributes ().getNamedItem (UpdateTracking.ATTR_LAST);
assert attrLast != null : "ELEMENT_VERSION must contain ATTR_LAST attribute.";
if (Boolean.valueOf (attrLast.getNodeValue ())) {
return version;
} else {
return null;
}
}
private static File getAdditionalInformation (File root) {
File file = new File (root.getPath () + FILE_SEPARATOR + DOWNLOAD_DIR +
FILE_SEPARATOR + UpdateTracking.ADDITIONAL_INFO_FILE_NAME);
return file;
}
private static void writeAdditionalInformationToCluster (File cluster, Map<UpdateElementImpl, File> updates) {
if (updates.isEmpty ()) {
return ;
}
Document document = XMLUtil.createDocument (UpdateTracking.ELEMENT_ADDITIONAL, null, null, null);
Element root = document.getDocumentElement ();
boolean isEmpty = true;
for (UpdateElementImpl impl : updates.keySet ()) {
File c = updates.get (impl);
// pass this module to given cluster ?
if (cluster.equals (c)) {
Element module = document.createElement (UpdateTracking.ELEMENT_ADDITIONAL_MODULE);
module.setAttribute(ATTR_NBM_NAME,
InstallSupportImpl.getDestination (cluster, impl.getCodeName(), impl.getInstallInfo().getDistribution()).getName ());
module.setAttribute (UpdateTracking.ATTR_ADDITIONAL_SOURCE, impl.getSource ());
root.appendChild( module );
isEmpty = false;
}
}
if (isEmpty) {
return ;
}
writeXMLDocumentToFile (document, getAdditionalInformation (cluster));
}
public static UpdateItem createUpdateItem (UpdateItemImpl impl) {
assert Trampoline.SPI != null;
return Trampoline.SPI.createUpdateItem (impl);
}
public static UpdateItemImpl getUpdateItemImpl (UpdateItem item) {
assert Trampoline.SPI != null;
return Trampoline.SPI.impl (item);
}
public static boolean canDisable (Module m) {
return m != null && m.isEnabled () && ! isEssentialModule (m) && ! m.isAutoload () && ! m.isEager ();
}
public static boolean canEnable (Module m) {
return m != null && !m.isEnabled () && ! m.isAutoload () && ! m.isEager ();
}
@SuppressWarnings("null")
public static boolean isElementInstalled (UpdateElement el) {
assert el != null : "Invalid call isElementInstalled with null parameter.";
if (el == null) {
return false;
}
return el.equals (el.getUpdateUnit ().getInstalled ());
}
public static boolean isKitModule (ModuleInfo mi) {
return Main.getModuleSystem().isShowInAutoUpdateClient(mi);
}
public static boolean isEssentialModule (ModuleInfo mi) {
Object o = mi.getAttribute (ATTR_ESSENTIAL);
return isFixed (mi) || (o != null && Boolean.parseBoolean (o.toString ()));
}
public static boolean isFirstClassModule (UpdateElement ue) {
String codeName = ue.getCodeName();
String names = System.getProperty (PLUGIN_MANAGER_FIRST_CLASS_MODULES);
if (names == null || names.length () == 0) {
UpdateElementImpl ueImpl = Trampoline.API.impl(ue);
return ueImpl.isPreferredUpdate();
} else {
StringTokenizer en = new StringTokenizer(names, ","); // NOI18N
while (en.hasMoreTokens()) {
if (en.nextToken().trim().equals(codeName)) {
return true;
}
}
}
return false;
}
private static Logger getLogger () {
return err;
}
/** Finds modules depending on given module.
* @param m a module to start from; may be enabled or not, but must be owned by this manager
* @return a set (possibly empty) of modules managed by this manager, never including m
*/
public static Set<Module> findRequiredModules (Module m, ModuleManager mm, Map<Module, Set<Module>> m2reqs) {
Set<Module> res;
if (m2reqs != null) {
res = m2reqs.get (m);
if (res == null) {
res = mm.getModuleInterdependencies(m, false, false, true);
m2reqs.put (m, res);
}
} else {
res = mm.getModuleInterdependencies(m, false, false, true);
}
return res;
}
/** Finds for modules given module depends upon.
* @param m a module to start from; may be enabled or not, but must be owned by this manager
* @return a set (possibly empty) of modules managed by this manager, never including m
*/
public static Set<Module> findDependingModules (Module m, ModuleManager mm, Map<Module, Set<Module>> m2deps) {
Set<Module> res;
if (m2deps != null) {
res = m2deps.get (m);
if (res == null) {
res = filterDependingOnOtherProvider(m, mm.getModuleInterdependencies(m, true, false, true));
m2deps.put (m, res);
}
} else {
res = filterDependingOnOtherProvider(m, mm.getModuleInterdependencies(m, true, false, true));
}
return res;
}
private static Set<Module> filterDependingOnOtherProvider(Module m, Set<Module> modules) {
Set<Module> alive = new HashSet<Module> ();
for (String token : m.getProvides()) {
for (Module depM : modules) {
for (Dependency dep : depM.getDependencies()) {
if (dep.getType() == Dependency.TYPE_REQUIRES || dep.getType() == Dependency.TYPE_NEEDS) {
if (token.equals(dep.getName())) {
// check other installed providers
assert UpdateManagerImpl.getInstance().getInstalledProviders(token).contains(m) :
"Provides of token " + token + " " + UpdateManagerImpl.getInstance().getInstalledProviders(token) +
" contains " + m;
if (UpdateManagerImpl.getInstance().getInstalledProviders(token).size() > 1) {
alive.add(depM);
}
}
}
}
}
}
modules.removeAll(alive);
return modules;
}
public static String formatDate(Date date) {
synchronized(DATE_FORMAT) {
return DATE_FORMAT.format(date);
}
}
public static Date parseDate(String date) throws ParseException {
synchronized(DATE_FORMAT) {
return DATE_FORMAT.parse(date);
}
}
@SuppressWarnings("null")
public static boolean canWriteInCluster (File cluster) {
assert cluster != null : "dir cannot be null";
if (cluster == null) {
return false;
}
if (cluster.exists () && cluster.isDirectory ()) {
File dir4test;
File update = new File (cluster, UPDATE_DIR);
File download = new File (cluster, DOWNLOAD_DIR);
if (download.exists ()) {
dir4test = download;
} else if (update.exists ()) {
dir4test = update;
} else {
dir4test = cluster;
}
// workaround the bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4420020
if (dir4test.canWrite () && dir4test.canRead ()) {
boolean canWrite = canWrite (dir4test);
getLogger ().log (Level.FINE, "Can write into {0}? {1}", new Object[]{dir4test, canWrite});
return canWrite;
} else {
getLogger ().log (Level.FINE, "Can write into {0}? {1}", new Object[]{dir4test, dir4test.canWrite ()});
return dir4test.canWrite ();
}
}
cluster.mkdirs ();
getLogger ().log (Level.FINE, "Can write into new cluster {0}? {1}", new Object[]{cluster, cluster.canWrite ()});
return cluster.canWrite ();
}
public static boolean canWrite (File f) {
// workaround the bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4420020
if (org.openide.util.Utilities.isWindows ()) {
if (f.isFile ()) {
FileWriter fw = null;
try {
fw = new FileWriter (f, true);
getLogger ().log (Level.FINE, "{0} has write permission", f);
} catch (IOException ioe) {
// just check of write permission
getLogger ().log (Level.FINE, f + " has no write permission", ioe);
return false;
} finally {
try {
if (fw != null) {
fw.close ();
}
} catch (IOException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
}
}
return true;
} else {
try {
File dummy = File.createTempFile ("dummy", null, f);
dummy.delete ();
getLogger ().log (Level.FINE, "{0} has write permission", f);
} catch (IOException ioe) {
getLogger ().log (Level.FINE, f + " has no write permission", ioe);
return false;
}
return true;
}
} else {
return f.canWrite ();
}
}
public static KeyStore loadKeyStore () {
String fileName = getPreferences ().get (USER_KS_KEY, null);
if (fileName == null) {
return null;
} else {
InputStream is = null;
KeyStore ks = null;
try {
File f = new File (getCacheDirectory (), fileName);
if (! f.exists ()) {
return null;
}
is = new BufferedInputStream (new FileInputStream (f));
ks = KeyStore.getInstance (KeyStore.getDefaultType ());
ks.load (is, KS_USER_PASSWORD.toCharArray ());
} catch (IOException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
} catch (NoSuchAlgorithmException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
} catch (CertificateException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
} catch (KeyStoreException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
} finally {
try {
if (is != null) {
is.close ();
}
} catch (IOException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
}
}
return ks;
}
}
private static void storeKeyStore (KeyStore ks) {
OutputStream os = null;
try {
File f = new File (getCacheDirectory (), USER_KS_FILE_NAME);
os = new BufferedOutputStream (new FileOutputStream (f));
ks.store (os, KS_USER_PASSWORD.toCharArray ());
getPreferences ().put (USER_KS_KEY, USER_KS_FILE_NAME);
} catch (KeyStoreException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
} catch (IOException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
} catch (NoSuchAlgorithmException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
} catch (CertificateException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
} finally {
try {
if (os != null) {
os.close ();
}
} catch (IOException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
}
}
}
public static void addCertificates (Collection<Certificate> certs) {
KeyStore ks = loadKeyStore ();
if (ks == null) {
try {
ks = KeyStore.getInstance (KeyStore.getDefaultType ());
ks.load (null, KS_USER_PASSWORD.toCharArray ());
} catch (IOException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
return ;
} catch (NoSuchAlgorithmException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
return ;
} catch (CertificateException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
return ;
} catch (KeyStoreException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
return ;
}
}
for (Certificate c : certs) {
try {
// don't add certificate twice
if (ks.getCertificateAlias (c) != null) {
continue;
}
// Find free alias name
String alias = null;
for (int i = 0; i < 9999; i++) {
alias = "genAlias" + i; // NOI18N
if (! ks.containsAlias (alias)) {
break;
}
}
if (alias == null) {
getLogger ().log (Level.INFO, "Too many certificates with {0}", c);
}
ks.setCertificateEntry (alias, c);
} catch (KeyStoreException ex) {
getLogger ().log (Level.INFO, ex.getLocalizedMessage (), ex);
}
}
storeKeyStore (ks);
}
public static void writeFirstClassModule(String moduleCodeName) {
if (moduleCodeName == null) {
getPreferences().put(PLUGIN_MANAGER_FIRST_CLASS_MODULES, "");
return ;
}
String names = getPreferences().get(PLUGIN_MANAGER_FIRST_CLASS_MODULES, "");
names = names.isEmpty() ? moduleCodeName : names + "," + moduleCodeName;
getPreferences().put(PLUGIN_MANAGER_FIRST_CLASS_MODULES, names);
}
private static File getCacheDirectory () {
return Places.getCacheSubdirectory("catalogcache");
}
private static Preferences getPreferences() {
return NbPreferences.root ().node ("/org/netbeans/modules/autoupdate"); // NOI18N
}
}