sdedic closed pull request #1047: [Backport] Enhance AU to install missing
feature parts (PR-1044)
URL: https://github.com/apache/incubator-netbeans/pull/1047
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/Bundle.properties
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/Bundle.properties
index e9f19606cd..4457d9fa7d 100644
---
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/Bundle.properties
+++
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/Bundle.properties
@@ -75,7 +75,7 @@ CAP_Action=Executing Action
# {0} short name of the folder
MSG_BrokenProject={0} (broken)
MSG_BrokenActionInfo=Resolve Problems...
-
+MSG_BrokenAction_FeatureIncomplete=Feature {0} is incomplete: some module(s)
are missing: {1}
ConfigurationPanel.errorLabel.text=dummy
diff --git
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/ConfigurationPanel.java
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/ConfigurationPanel.java
index 6e95a6400a..ffa80d90a5 100644
---
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/ConfigurationPanel.java
+++
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/ConfigurationPanel.java
@@ -27,7 +27,7 @@
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
+import java.util.Map;
import java.util.concurrent.Callable;
import javax.swing.Box;
import javax.swing.BoxLayout;
@@ -47,7 +47,6 @@
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.modules.autoupdate.ui.api.PluginManager;
import org.openide.awt.Mnemonics;
-import org.openide.modules.SpecificationVersion;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor.Task;
@@ -70,7 +69,7 @@
public ConfigurationPanel(String displayName, final Callable<JComponent>
callable, FeatureInfo info) {
this(callable);
- setInfo(info, displayName, Collections.<UpdateElement>emptyList(),
Collections.<String>emptyList());
+ setInfo(info, displayName, Collections.<UpdateElement>emptyList(),
Collections.emptyList(), Collections.emptyMap(), false);
}
public ConfigurationPanel(final Callable<JComponent> callable) {
@@ -83,7 +82,8 @@ public ConfigurationPanel(final Callable<JComponent>
callable) {
}
public void setInfo(FeatureInfo info, String displayName,
Collection<UpdateElement> toInstall,
- Collection<String> missingModules) {
+ Collection<FeatureInfo.ExtraModuleInfo> missingModules,
+ Map<FeatureInfo.ExtraModuleInfo, FeatureInfo> extrasMap, boolean
required) {
this.featureInfo = info;
this.featureInstall = toInstall;
boolean activateNow = toInstall.isEmpty() && missingModules.isEmpty();
@@ -99,34 +99,36 @@ public void setInfo(FeatureInfo info, String displayName,
Collection<UpdateEleme
downloadLabel.setVisible(true);
activateButton.setVisible(true);
downloadButton.setVisible(true);
- SpecificationVersion jdk = new
SpecificationVersion(System.getProperty("java.specification.version"));
- String lblDownloadMsg;
-
- boolean required = false;
-
- if (info.getExtraModulesRecommendedMinJDK() != null &&
- jdk.compareTo(new
SpecificationVersion(info.getExtraModulesRecommendedMinJDK())) < 0) {
- required = true;
- }
- if (info.getExtraModulesRequiredText() != null &&
info.getExtraModulesRecommendedText() == null) {
- required = true;
+ StringBuilder sbDownload = new StringBuilder();
+
+ // collect descriptions from features contributing installed extras
+ for (FeatureInfo fi : extrasMap.values()) {
+ String s = required ?
+ fi.getExtraModulesRequiredText() :
+ fi.getExtraModulesRecommendedText();
+ if (s != null) {
+ if (sbDownload.length() > 0) {
+ sbDownload.append("\n");
+ }
+ sbDownload.append(s);
+ }
}
if (required) {
- lblDownloadMsg = info.getExtraModulesRequiredText();
activateButton.setEnabled(false);
} else {
- lblDownloadMsg = info.getExtraModulesRecommendedText();
activateButton.setEnabled(true);
}
+
+ String lblDownloadMsg = sbDownload.toString();
String list = "";
if (!missingModules.isEmpty()) {
StringBuilder sb = new StringBuilder();
- for (String s : missingModules) {
+ for (FeatureInfo.ExtraModuleInfo s : missingModules) {
if (sb.length() > 0) {
sb.append(", "); // NOI18N
}
- sb.append(s);
+ sb.append(s.displayName());
}
list = sb.toString();
if (required) {
@@ -139,7 +141,7 @@ public void setInfo(FeatureInfo info, String displayName,
Collection<UpdateEleme
} else {
downloadButton.setEnabled(true);
}
-
+
String lblActivateMsg =
NbBundle.getMessage(ConfigurationPanel.class, "LBL_EnableInfo", displayName);
String btnActivateMsg =
NbBundle.getMessage(ConfigurationPanel.class, "LBL_Enable");
String btnDownloadMsg =
NbBundle.getMessage(ConfigurationPanel.class, "LBL_Download");
diff --git
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FeatureInfo.java
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FeatureInfo.java
index c2f1c179c2..55d53b2294 100755
---
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FeatureInfo.java
+++
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FeatureInfo.java
@@ -26,17 +26,20 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
+import java.util.regex.Pattern;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathExpression;
import org.w3c.dom.Document;
import org.openide.modules.ModuleInfo;
+import org.openide.modules.SpecificationVersion;
import org.openide.util.Lookup;
/** Description of <em>Feature On Demand</em> capabilities and a
@@ -213,17 +216,59 @@ int isProject(FeatureProjectFactory.Data data) {
return Collections.unmodifiableSet(cnbs);
}
- public final Set<String> getExtraModules() {
- String cnbs = properties.getProperty("extra.modules"); // NOI18N
- TreeSet<String> s = new TreeSet<String>();
- if (cnbs != null) {
- s.addAll(Arrays.asList(cnbs.split(",")));
+ public final Set<ExtraModuleInfo> getExtraModules() {
+ Set<ExtraModuleInfo> s = new LinkedHashSet<>();
+ for (int i = 0; ; i++) {
+ String propName = i == 0 ? "extra.modules" : "extra.modules." + i;
// NOI18N
+ String cnbPattern = properties.getProperty(propName);
+ if (cnbPattern == null) {
+ break;
+ }
+ String rec = properties.getProperty(propName +
".recommended.min.jdk"); // NOI18N
+ s.add(new ExtraModuleInfo(cnbPattern, rec));
}
return s;
}
- public final String getExtraModulesRecommendedMinJDK() {
- return properties.getProperty("extra.modules.recommended.min.jdk"); //
NOI18N
+ static final class ExtraModuleInfo {
+ private final Pattern cnb;
+ final SpecificationVersion recMinJDK;
+
+ ExtraModuleInfo(String cnbPattern, String recommended) {
+ this.cnb = Pattern.compile(cnbPattern);
+ this.recMinJDK = recommended != null ? new
SpecificationVersion(recommended) : null;
+ }
+
+ String displayName() {
+ return cnb.pattern();
+ }
+
+ /**
+ * Returns the codename, if the pattern is actually a literal.
+ * Literal pattern should not contain any metacharacters, except '.'
and '/'.
+ * @return codename or {@code null}
+ */
+ String explicitCodebase() {
+ String pat = cnb.pattern();
+ for (int a = pat.length() - 1; a >= 0; a--) {
+ char c = pat.charAt(a);
+ if (!(Character.isAlphabetic(c) || Character.isDigit(c))) {
+ if (c != '.' && c != '/') {
+ return null;
+ }
+ }
+ }
+ return pat;
+ }
+
+ boolean matches(String cnb) {
+ return this.cnb.matcher(cnb).matches();
+ }
+
+ @Override
+ public String toString() {
+ return "ExtraModuleInfo{" + "cnb=" + cnb + ", recommended=" +
recMinJDK + '}';
+ }
}
public final String getExtraModulesRequiredText() {
diff --git
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FeatureProjectFactory.java
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FeatureProjectFactory.java
index 60caaa8822..35e77de331 100644
---
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FeatureProjectFactory.java
+++
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FeatureProjectFactory.java
@@ -29,6 +29,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
@@ -68,6 +69,7 @@
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataFolder;
import org.openide.nodes.FilterNode;
+import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor.Task;
import org.xml.sax.SAXException;
@@ -316,7 +318,7 @@ public void run() {
private final FeatureInfo[] additional;
private final Lookup lookup;
private ProjectState state;
- private String error;
+ private volatile String error;
private final ChangeListener weakL;
public FeatureNonProject(
@@ -379,9 +381,15 @@ final void switchToReal() {
}
}
}
-
+
private final class FeatureOpenHook extends ProjectOpenedHook
implements Runnable, ProgressMonitor {
+ /**
+ * The finder instance; valid only for the duration of the task,
+ * should be cleared after that.
+ */
+ private FindComponentModules finder;
+
@Override
protected void projectOpened() {
if (state == null) {
@@ -416,14 +424,45 @@ public void run() {
FeatureManager.logUI("ERGO_PROJECT_OPEN", info.clusterName);
error = null;
FindComponentModules findModules = new
FindComponentModules(info, additional);
- Collection<UpdateElement> toInstall =
findModules.getModulesForInstall ();
- Collection<UpdateElement> toEnable =
findModules.getModulesForEnable ();
- if (toInstall != null && ! toInstall.isEmpty ()) {
- ModulesInstaller installer = new
ModulesInstaller(toInstall, findModules, this);
- installer.getInstallTask ().waitFinished ();
- } else if (toEnable != null && ! toEnable.isEmpty ()) {
- ModulesActivator enabler = new ModulesActivator (toEnable,
findModules, this);
- enabler.getEnableTask ().waitFinished ();
+ synchronized (this) {
+ this.finder = findModules;
+ }
+ try {
+ Collection<UpdateElement> toInstall =
findModules.getModulesForInstall ();
+ Collection<UpdateElement> toEnable =
findModules.getModulesForEnable ();
+ if (!findModules.getIncompleteFeatures().isEmpty() &&
findModules.isDownloadRequired()) {
+ // ignore
+ Collection<FeatureInfo.ExtraModuleInfo> missingModules
= new LinkedHashSet<>(findModules.getMissingModules(info));
+ for (FeatureInfo i2 : additional) {
+
missingModules.addAll(findModules.getMissingModules(i2));
+ }
+ StringBuilder sb = new StringBuilder();
+ if (!missingModules.isEmpty()) {
+ for (FeatureInfo.ExtraModuleInfo s :
missingModules) {
+ if (sb.length() > 0) {
+ sb.append(", "); // NOI18N
+ }
+ sb.append(s.displayName());
+ }
+ }
+ error =
NbBundle.getMessage(FeatureProjectFactory.class,
+ "MSG_BrokenAction_FeatureIncomplete",
+
findModules.getIncompleteFeatures().iterator().next(),
+ sb.toString());
+ return;
+ }
+ if (toInstall != null && ! toInstall.isEmpty ()) {
+ ModulesInstaller installer = new
ModulesInstaller(toInstall, findModules, this);
+ installer.getInstallTask ().waitFinished ();
+ }
+ if (toEnable != null && ! toEnable.isEmpty () && error ==
null) {
+ ModulesActivator enabler = new ModulesActivator
(toEnable, findModules, this);
+ enabler.getEnableTask ().waitFinished ();
+ }
+ } finally {
+ synchronized (this) {
+ this.finder = null;
+ }
}
}
@@ -440,7 +479,11 @@ public void onEnable(ProgressHandle progressHandle) {
}
public void onError(String message) {
- error = message;
+ synchronized (this) {
+ if (finder.isDownloadRequired()) {
+ error = message;
+ }
+ }
}
} // end of FeatureOpenHook
} // end of FeatureNonProject
diff --git
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FindComponentModules.java
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FindComponentModules.java
index eea30cd302..85a0230ab5 100644
---
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FindComponentModules.java
+++
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FindComponentModules.java
@@ -20,10 +20,12 @@
package org.netbeans.modules.ide.ergonomics.fod;
import java.io.IOException;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
@@ -36,12 +38,14 @@
import java.util.prefs.Preferences;
import org.netbeans.api.autoupdate.InstallSupport;
import org.netbeans.api.autoupdate.OperationContainer;
+import org.netbeans.api.autoupdate.OperationContainer.OperationInfo;
import org.netbeans.api.autoupdate.OperationSupport;
import org.netbeans.api.autoupdate.UpdateElement;
import org.netbeans.api.autoupdate.UpdateManager;
import org.netbeans.api.autoupdate.UpdateUnit;
import org.netbeans.api.autoupdate.UpdateUnitProvider;
import org.netbeans.api.autoupdate.UpdateUnitProviderFactory;
+import org.openide.modules.SpecificationVersion;
import org.openide.util.Exceptions;
import org.openide.util.NbPreferences;
import org.openide.util.RequestProcessor;
@@ -65,13 +69,37 @@
/**
* Features, whose 'extra' modules were not found.
*/
- private Map<FeatureInfo, List<String>> incompleteFeatures = new
HashMap<>();
+ private Map<FeatureInfo, Collection<FeatureInfo.ExtraModuleInfo>>
incompleteFeatures = new HashMap<>();
+
+ /**
+ * Maps individual codebase names to owning feature infos. Built at the
start of findComponentModules
+ */
+ private Map<String, FeatureInfo> codebaseOwners = new HashMap<>();
+
+ /**
+ * Index to search through update units; reset every time an update is
attempted.
+ */
+ private Map<String, UpdateUnit> availUnits = null;
/**
* Errors encountered during updating component descriptions
*/
private List<IOException> updateErrors = new ArrayList<>();
+ /**
+ * ExtraInfos which need to be downloaded, if they are missing or not.
+ * Map to their owning FeatureInfo for descriptive labels
+ */
+ private Map<FeatureInfo.ExtraModuleInfo, FeatureInfo> extrasToDownload =
new HashMap<>();
+
+ /**
+ * Indicates that the user cannot proceed without a download.
+ * Accumulated during {@link
#getMissingModules(org.netbeans.modules.ide.ergonomics.fod.FeatureInfo)}
+ */
+ private boolean downloadRequired;
+
+ private final SpecificationVersion jdk = new
SpecificationVersion(System.getProperty("java.specification.version"));
+
public FindComponentModules(FeatureInfo info, FeatureInfo... additional) {
ArrayList<FeatureInfo> l = new ArrayList<FeatureInfo>();
l.add(info);
@@ -91,11 +119,11 @@ public FindComponentModules(FeatureInfo info,
FeatureInfo... additional) {
public Collection<UpdateElement> getModulesForInstall () {
findingTask.waitFinished();
- return forInstall;
+ return forInstall == null ? Collections.emptyList() : forInstall;
}
public Collection<UpdateElement> getModulesForEnable () {
findingTask.waitFinished();
- return forEnable;
+ return forEnable == null ? Collections.emptyList() : forEnable;
}
public Collection<FeatureInfo> getIncompleteFeatures() {
@@ -103,13 +131,13 @@ public FindComponentModules(FeatureInfo info,
FeatureInfo... additional) {
return incompleteFeatures.keySet();
}
- void markIncompleteFeature(FeatureInfo fi, String cnb) {
- incompleteFeatures.computeIfAbsent(fi, (f) -> new ArrayList<>())
- .add(cnb);
+ void markIncompleteFeature(FeatureInfo fi, FeatureInfo.ExtraModuleInfo
moduleInfo) {
+ incompleteFeatures.computeIfAbsent(fi, (f) -> new HashSet<>())
+ .add(moduleInfo);
}
- public Collection<String> getMissingModules(FeatureInfo info) {
- return incompleteFeatures.getOrDefault(info,
Collections.<String>emptyList());
+ public Collection<FeatureInfo.ExtraModuleInfo>
getMissingModules(FeatureInfo info) {
+ return incompleteFeatures.getOrDefault(info, Collections.emptyList());
}
public Collection<IOException> getUpdateErrors() {
@@ -207,11 +235,33 @@ public void run() {
findComponentModules ();
}
};
-
+
+ private void buildCodebaseIndex() {
+ for (FeatureInfo fi : FeatureManager.features()) {
+ for (String cnb : fi.getCodeNames()) {
+ codebaseOwners.put(cnb, fi);
+ }
+ }
+ }
+
+ private void buildUpdateUnitIndex() {
+ if (availUnits != null) {
+ return;
+ }
+ Collection<UpdateUnit> units = UpdateManager.getDefault
().getUpdateUnits (UpdateManager.TYPE.MODULE);
+ Map<String, UpdateUnit> uumap = new HashMap<>();
+ for (UpdateUnit u : units) {
+ uumap.put(u.getCodeName(), u);
+ }
+ availUnits = uumap;
+ }
+
private void findComponentModules () {
long start = System.currentTimeMillis();
Collection<UpdateUnit> units = null;
Collection<UpdateElement> elementsForInstall = null;
+ buildCodebaseIndex();
+
for (int[] refresh = { 2 }; refresh[0] > 0; ) {
if (units != null) {
List<UpdateUnitProvider> providers =
UpdateUnitProviderFactory.getDefault().getUpdateUnitProviders(true);
@@ -223,8 +273,9 @@ private void findComponentModules () {
Exceptions.attachSeverity(ex,
Level.INFO).printStackTrace();
}
}
-
+ availUnits = null;
}
+ buildUpdateUnitIndex();
units = UpdateManager.getDefault ().getUpdateUnits
(UpdateManager.TYPE.MODULE);
// install missing modules
elementsForInstall = getMissingModules(units, refresh);
@@ -236,6 +287,27 @@ private void findComponentModules () {
forEnable = getAllForEnable (elementsForEnable, units);
}
+ public Map<FeatureInfo.ExtraModuleInfo, FeatureInfo>
getExtrasToDownload() {
+ return extrasToDownload;
+ }
+
+ private void registerExtraDownloadable(FeatureInfo fi,
FeatureInfo.ExtraModuleInfo fmi) {
+ boolean required = false;
+ if (fmi.recMinJDK != null && jdk.compareTo(fmi.recMinJDK) < 0) {
+ required = true;
+ }
+ if (fi.getExtraModulesRequiredText() != null &&
fi.getExtraModulesRecommendedText() == null) {
+ required = true;
+ }
+ downloadRequired |= required;
+ extrasToDownload.put(fmi, fi);
+ }
+
+ public boolean isDownloadRequired() {
+ findingTask.waitFinished();
+ return downloadRequired;
+ }
+
private Collection<UpdateElement> getMissingModules(Collection<UpdateUnit>
allUnits, int[] refresh) {
Set<UpdateElement> res = new HashSet<UpdateElement> ();
for (UpdateUnit unit : allUnits) {
@@ -246,33 +318,104 @@ private void findComponentModules () {
}
}
boolean decr = true;
- for (FeatureInfo fi : this.infos) {
- Set<String> extraModules = fi.getExtraModules();
- FOUND: for (String cnb : extraModules) {
- for (UpdateUnit unit : allUnits) {
- if (cnb.equals(unit.getCodeName())) {
- if (unit.getInstalled() != null) {
- continue FOUND;
- } else {
- res.add(unit.getAvailableUpdates().get(0));
- continue FOUND;
+ Set<String> codebasesSeen = new HashSet<>();
+ OperationContainer<OperationSupport> ocForEnable =
OperationContainer.createForEnable();
+ OperationContainer<InstallSupport> ocForInstall =
OperationContainer.createForInstall();
+ boolean firstPass = true;
+
+ for (FeatureInfo startFi : infos) {
+ Deque<FeatureInfo> closure = new ArrayDeque<>();
+ closure.add(startFi);
+ codebasesSeen.add(startFi.getFeatureCodeNameBase());
+
+ while (!closure.isEmpty()) {
+ FeatureInfo fi = closure.poll();
+ Set<FeatureInfo.ExtraModuleInfo> extraModules =
fi.getExtraModules();
+ FOUND:
+ for (FeatureInfo.ExtraModuleInfo moduleInfo : extraModules) {
+ boolean found = false;
+ for (UpdateUnit unit : allUnits) {
+ if (moduleInfo.matches(unit.getCodeName())) {
+ if (unit.getInstalled() != null) {
+ // PENDING: if the cnb spec is a regexp that
matches MULTIPLE modules,
+ // then the wrong one (i.e. with broken deps)
can be matched.
+ continue FOUND;
+ } else if (!unit.getAvailableUpdates().isEmpty()) {
+ registerExtraDownloadable(fi, moduleInfo);
+ res.add(unit.getAvailableUpdates().get(0));
+ found = true;
+ }
}
}
+ if (found) {
+ continue FOUND;
+ }
+
+ // not found
+ if (decr) {
+ if (--refresh[0] > 0) {
+ // try to refresh
+ return res;
+ }
+ // decrement just once
+ decr = false;
+ }
+ // extra module(s) for the feature weren't found
+ markIncompleteFeature(fi, moduleInfo);
+ // mark the originating feature as incomplete, too
+ markIncompleteFeature(startFi, moduleInfo);
+ // and compute the overall 'required' status
+ registerExtraDownloadable(fi, moduleInfo);
}
-
- // not found
- if (decr) {
- if (--refresh[0] > 0) {
- // try to refresh
- return res;
+ if (firstPass) {
+ for (String cb : fi.getCodeNames()) {
+ UpdateUnit cbuu = availUnits.get(cb);
+ if (cbuu == null) {
+ // sorry
+ continue;
+ }
+ UpdateElement cbel = cbuu.getInstalled();
+ OperationInfo thisInfo;
+
+ if (cbel != null && ocForEnable.canBeAdded(cbuu,
cbel)) {
+ thisInfo = ocForEnable.add(cbuu, cbel);
+ } else {
+ if (cbuu.getAvailableUpdates().isEmpty()) {
+ if (decr) {
+ if (--refresh[0] > 0) {
+ // try to refresh
+ return res;
+ }
+ // decrement just once
+ decr = false;
+ }
+ continue;
+ }
+ UpdateElement ie =
cbuu.getAvailableUpdates().get(0);
+ // not installed
+ if (!ocForInstall.canBeAdded(cbuu, ie)) {
+ continue;
+ }
+ thisInfo = ocForInstall.add(cbuu, ie);
+
+ }
+ Collection<UpdateElement> deps =
thisInfo.getRequiredElements();
+ for (UpdateElement d : deps) {
+ UpdateUnit du = d.getUpdateUnit();
+ if (du.getInstalled() == null) {
+ res.add(d);
+ }
+ FeatureInfo depF =
this.codebaseOwners.get(du.getCodeName());
+ if (depF != null &&
codebasesSeen.add(depF.getFeatureCodeNameBase())) {
+ closure.add(depF);
+ }
+ }
}
- // decrement just once
- decr = false;
+ firstPass = false;
}
- // extra module(s) for the feature weren't found
- markIncompleteFeature(fi, cnb);
}
}
+
refresh[0] = 0;
return res;
}
@@ -288,6 +431,8 @@ private void findComponentModules () {
}
Set<UpdateElement> reqs = info.getRequiredElements ();
ocForInstall.add (reqs);
+ // PENDING: broken dependencies may mean the feature contains
a platform/java-specific
+ // module, but could be used even without that one.
Set<String> breaks = info.getBrokenDependencies ();
if (breaks.isEmpty ()) {
all.add (el);
diff --git
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FoDUpdateUnitProvider.java
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FoDUpdateUnitProvider.java
index 9c61c35db7..697e2aa62d 100644
---
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FoDUpdateUnitProvider.java
+++
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/fod/FoDUpdateUnitProvider.java
@@ -31,6 +31,7 @@
import org.netbeans.spi.autoupdate.UpdateProvider;
import org.openide.modules.Dependency;
import org.openide.modules.ModuleInfo;
+import org.openide.modules.SpecificationVersion;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
@@ -76,6 +77,11 @@ public CATEGORY getCategory() {
"org.netbeans.modules.java.source.nbjavac.test"// NOI18N
));
+ // protected because of tests obly
+ protected boolean acceptsModule(ModuleInfo mi) {
+ return !EXCLUDE_EXTRA_MODULES.contains(mi.getCodeNameBase());
+ }
+
@Override
public Map<String, UpdateItem> getUpdateItems () throws IOException {
Map<String, UpdateItem> res = new HashMap<String, UpdateItem> ();
@@ -85,8 +91,30 @@ public CATEGORY getCategory() {
if (!fi.isPresent()) {
continue;
}
+ Set<String> cnbs = new HashSet<>(fi.getCodeNames());
+ SpecificationVersion jdk = new
SpecificationVersion(System.getProperty("java.specification.version")); //
NOI18N
+ // add required extra modules
+ // extra module is required iff it provides ONLY
getExtraModulesRequiredText (but not recommended one), or
+ // the current JDK does not satisfy the minJDK requirement of the
extra module.
+ for (FeatureInfo.ExtraModuleInfo mi : fi.getExtraModules()) {
+ String m = mi.explicitCodebase();
+ if (m == null) {
+ continue;
+ }
+ boolean required = false;
+
+ if (mi.recMinJDK != null && jdk.compareTo(mi.recMinJDK) < 0) {
+ required = true;
+ }
+ if (fi.getExtraModulesRequiredText() != null &&
fi.getExtraModulesRecommendedText() == null) {
+ required = true;
+ }
+ if (required) {
+ cnbs.add(m);
+ }
+ }
if (registerFeature(
- all, fi.getCodeNames(),
+ all, cnbs,
fi.getFeatureCodeNameBase(), null, res, notYetProcessed
)) {
continue;
@@ -101,7 +129,7 @@ public CATEGORY getCategory() {
}
if (isIDECluster(mi)) {
baseIDE.add(mi.getCodeNameBase());
- } else if
(!EXCLUDE_EXTRA_MODULES.contains(mi.getCodeNameBase())) {
+ } else if (acceptsModule(mi)) {
extra.add(mi.getCodeNameBase());
}
}
diff --git
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/newproject/DescriptionStep.java
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/newproject/DescriptionStep.java
index d6b6168f28..c0a25a1fb0 100644
---
a/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/newproject/DescriptionStep.java
+++
b/ergonomics/ide.ergonomics/src/org/netbeans/modules/ide/ergonomics/newproject/DescriptionStep.java
@@ -163,11 +163,12 @@ final void presentModulesForActivation
(FindComponentModules f) {
}
final Collection<UpdateElement> toInstall =
f.getModulesForInstall();
final Collection<UpdateElement> elems = f.getModulesForEnable ();
- if ((elems != null && !elems.isEmpty ()) ||
f.getIncompleteFeatures().contains(info) || !f.getUpdateErrors().isEmpty()) {
+ if (!elems.isEmpty ()|| f.getIncompleteFeatures().contains(info)
|| !f.getUpdateErrors().isEmpty()) {
Collection<UpdateElement> visible = f.getVisibleUpdateElements
(elems);
final String name = ModulesInstaller.presentUpdateElements
(visible);
configPanel.setUpdateErrors(f.getUpdateErrors());
- configPanel.setInfo(info, name, toInstall,
f.getMissingModules(info));
+ configPanel.setInfo(info, name, toInstall,
f.getMissingModules(info),
+ f.getExtrasToDownload(), f.isDownloadRequired());
panel.replaceComponents(configPanel);
forEnable = elems;
fireChange ();
diff --git
a/ergonomics/ide.ergonomics/test/unit/src/org/netbeans/modules/ide/ergonomics/DynamicVerifyTest.java
b/ergonomics/ide.ergonomics/test/unit/src/org/netbeans/modules/ide/ergonomics/DynamicVerifyTest.java
index 49dd69094c..0a0b3f904d 100644
---
a/ergonomics/ide.ergonomics/test/unit/src/org/netbeans/modules/ide/ergonomics/DynamicVerifyTest.java
+++
b/ergonomics/ide.ergonomics/test/unit/src/org/netbeans/modules/ide/ergonomics/DynamicVerifyTest.java
@@ -29,6 +29,7 @@
import org.netbeans.modules.ide.ergonomics.fod.FoDUpdateUnitProvider;
import org.netbeans.spi.autoupdate.UpdateItem;
import org.netbeans.spi.project.ProjectFactory;
+import org.openide.modules.ModuleInfo;
import org.openide.util.Lookup;
/**
@@ -97,9 +98,18 @@ public static Test suite() {
}
public void testNoUserDefinedFeaturesInStandardBuild() throws Exception {
- FoDUpdateUnitProvider instance = new FoDUpdateUnitProvider();
+ // remove the javascript parser from the checklist: it has to be
user-installed
+ // through AU when HTML5 is enabled
+ FoDUpdateUnitProvider instance = new FoDUpdateUnitProvider() {
+ @Override
+ protected boolean acceptsModule(ModuleInfo mi) {
+ if (!super.acceptsModule(mi)) {
+ return false;
+ }
+ return
!"org.netbeans.libs.oracle.jsparser".equals(mi.getCodeNameBase());
+ }
+ };
Map<String, UpdateItem> items = instance.getUpdateItems();
-
assertNull("No user installed modules should be in standard build. If
this happens,\n" +
"like in case of
http://openide.netbeans.org/issues/show_bug.cgi?id=174052\n" +
"then you probably added new module and did not categorize it
properly,\n" +
diff --git a/platform/autoupdate.services/apichanges.xml
b/platform/autoupdate.services/apichanges.xml
index 3caf646527..9cee0f7f17 100644
--- a/platform/autoupdate.services/apichanges.xml
+++ b/platform/autoupdate.services/apichanges.xml
@@ -33,6 +33,20 @@
<!-- ACTUAL CHANGES BEGIN HERE: -->
<changes>
+ <change id="missing-elements">
+ <api name="general"/>
+ <summary>Report parts of a feature which is not installed
yet</summary>
+ <version major="1" minor="57"/>
+ <date day="30" month="11" year="2018"/>
+ <author login="sdedic"/>
+ <compatibility addition="yes" binary="compatible" deletion="no"
deprecation="no" semantic="compatible" source="compatible"/>
+ <description>
+ A distributed feature may list a module, which is only
available on some update center, in NB IDE case because of
+ licensing issues. This token must be reported so the as part
of the UpdateElement and Operation so the caller may
+ initiate the download operation.
+ </description>
+ <class package="org.netbeans.api.autoupdate"
name="OperationContainer"/>
+ </change>
<change id="visible-ancestor">
<api name="general"/>
<summary>Find a visible ancestor of UpdateUnit</summary>
diff --git a/platform/autoupdate.services/manifest.mf
b/platform/autoupdate.services/manifest.mf
index 7b7d89596c..ff82776089 100644
--- a/platform/autoupdate.services/manifest.mf
+++ b/platform/autoupdate.services/manifest.mf
@@ -1,7 +1,7 @@
Manifest-Version: 1.0
OpenIDE-Module: org.netbeans.modules.autoupdate.services
OpenIDE-Module-Localizing-Bundle:
org/netbeans/modules/autoupdate/services/resources/Bundle.properties
-OpenIDE-Module-Specification-Version: 1.55.1
+OpenIDE-Module-Specification-Version: 1.57.1
OpenIDE-Module-Layer:
org/netbeans/modules/autoupdate/services/resources/layer.xml
AutoUpdate-Show-In-Client: false
AutoUpdate-Essential-Module: true
diff --git a/platform/autoupdate.services/nbproject/project.properties
b/platform/autoupdate.services/nbproject/project.properties
index 1ad5c5e263..28a5a9b738 100644
--- a/platform/autoupdate.services/nbproject/project.properties
+++ b/platform/autoupdate.services/nbproject/project.properties
@@ -17,7 +17,7 @@
extra.module.files=modules/ext/updater.jar
is.autoload=true
javac.compilerargs=-Xlint -Xlint:-serial
-javac.source=1.7
+javac.source=1.8
javadoc.arch=${basedir}/arch.xml
javadoc.apichanges=${basedir}/apichanges.xml
test.unit.cp.extra=${basedir}/modules/ext/updater.jar
diff --git
a/platform/autoupdate.services/src/org/netbeans/api/autoupdate/OperationContainer.java
b/platform/autoupdate.services/src/org/netbeans/api/autoupdate/OperationContainer.java
index 996c951aa0..7f2299387c 100644
---
a/platform/autoupdate.services/src/org/netbeans/api/autoupdate/OperationContainer.java
+++
b/platform/autoupdate.services/src/org/netbeans/api/autoupdate/OperationContainer.java
@@ -376,6 +376,15 @@ public void removeAll() {
*/
public Set<String> getBrokenDependencies(){return
impl.getBrokenDependencies();}
+ /**
+ * Reports parts missing from the installation. Will return codenames
of required
+ * unknown modules (e.g. from catalogs not fetched yet). Note
differences to {@link #getBrokenDependencies()},
+ * which report also broken requirements for packages or java version
for specialized / optional modules.
+ * @return set of missing parts (modules). If nothing is missing,
returns empty set.
+ * @since 1.57
+ */
+ public Set<String> getMissingParts() { return impl.getMissingParts(); }
+
@Override
public String toString () {
return "OperationInfo: " + impl.getUpdateElement ().toString ();
// NOI18N
diff --git
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/FeatureUpdateElementImpl.java
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/FeatureUpdateElementImpl.java
index cc120ad8d8..bd86ea329b 100644
---
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/FeatureUpdateElementImpl.java
+++
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/FeatureUpdateElementImpl.java
@@ -21,6 +21,7 @@
import java.text.ParseException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
@@ -54,6 +55,11 @@
private String category;
private InstallInfo installInfo;
private static final Logger LOG = Logger.getLogger
(FeatureUpdateElementImpl.class.getName ());
+
+ /**
+ * Missing (necessary) dependencies, module codenames
+ */
+ private Set<String> missingElements;
private Set<ModuleUpdateElementImpl> moduleElementsImpl;
private Set<FeatureUpdateElementImpl> featureElementsImpl;
private UpdateManager.TYPE type;
@@ -61,6 +67,7 @@
public FeatureUpdateElementImpl (FeatureItem item, String providerName,
Set<ModuleUpdateElementImpl> moduleElementsImpl,
Set<FeatureUpdateElementImpl> featureElementsImpl,
+ Set<String> missing,
UpdateManager.TYPE type) {
super (item, providerName);
this.type = type;
@@ -80,6 +87,7 @@ public FeatureUpdateElementImpl (FeatureItem item, String
providerName,
if (category == null) {
category = NbBundle.getMessage (UpdateElementImpl.class,
"UpdateElementImpl_Feature_CategoryName");
}
+ missingElements = missing == null ? Collections.<String>emptySet() :
missing;
}
@Override
@@ -358,15 +366,24 @@ public int hashCode() {
public String toString() {
return "FeatureUpdateElementImpl[" + this.codeName + "/" +
this.specVersion + "]";
}
-
+
+ /**
+ * Returns unknown codenames
+ * @return unknown codenames or empty collection
+ */
+ public Set<String> getMissingElements() {
+ return missingElements;
+ }
+
public static class Agent extends FeatureUpdateElementImpl {
private Set<ModuleUpdateElementImpl> moduleElementsImpl;
private Set<FeatureUpdateElementImpl> featureElementsImpl;
+ private Set<String> missingImpl = Collections.emptySet();
private FeatureItem featureItem;
public Agent (FeatureItem item, String providerName,
UpdateManager.TYPE type) {
- super (item, providerName, null, null, type);
+ super (item, providerName, null, null, null, type);
this.featureItem = item;
}
@@ -383,6 +400,12 @@ public Agent (FeatureItem item, String providerName,
UpdateManager.TYPE type) {
assert featureElementsImpl != null : "FeatureUpdateElementImpl
depends on features " + featureElementsImpl;
return featureElementsImpl;
}
+
+ @Override
+ public Set<String> getMissingElements() {
+ initializeAgent();
+ return missingImpl;
+ }
private void initializeAgent() {
synchronized(this) {
@@ -478,6 +501,7 @@ private void initializeAgent() {
}
}
if (!dependenciesToModulesOrFeatures.isEmpty()) {
+ missingImpl = new HashSet<>(dependenciesToModulesOrFeatures);
LOG.log(Level.INFO, this.featureItem + " depends on
non-existing " + dependenciesToModulesOrFeatures);
}
return res;
diff --git
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/FeatureUpdateUnitImpl.java
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/FeatureUpdateUnitImpl.java
index ceb61c32da..2fc19eecbb 100644
---
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/FeatureUpdateUnitImpl.java
+++
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/FeatureUpdateUnitImpl.java
@@ -165,6 +165,7 @@ private void initializeFeature () {
installedFeatureElement.getSource (),
installedModules,
installedFeatures,
+ ((FeatureUpdateElementImpl) Trampoline.API.impl
(installedFeatureElement)).getMissingElements(),
featureImpl.getType ());
installedElement = Trampoline.API.createUpdateElement
(featureElementImpl);
featureElementImpl.setUpdateUnit
(installedFeatureElement.getUpdateUnit ());
@@ -186,6 +187,7 @@ private void initializeFeature () {
featureElements.get (0).getSource (),
availableModules,
availableFeatures,
+ null,
featureImpl.getType ());
updateElement = Trampoline.API.createUpdateElement
(featureElementImpl);
featureElementImpl.setUpdateUnit (featureElements.get
(0).getUpdateUnit ());
diff --git
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
index e70e04c99b..5098c28816 100644
---
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
+++
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/InstallSupportImpl.java
@@ -1222,6 +1222,8 @@ public void run() {
break;
}
}
+ // if something was (un)installed, we need to recompute
provides-requires info
+ UpdateManagerImpl.getInstance().flushComputedInfo();
}
}
diff --git
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationContainerImpl.java
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationContainerImpl.java
index 6cbe539b7a..a8977c0297 100644
---
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationContainerImpl.java
+++
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationContainerImpl.java
@@ -445,7 +445,7 @@ private OperationInfoImpl (UpdateUnit uUnit, UpdateElement
updateElement) {
}
return requiredElements;
}
-
+
public Set<String> getBrokenDependencies () {
if (! upToDate) {
brokenDeps = null;
@@ -453,14 +453,45 @@ private OperationInfoImpl (UpdateUnit uUnit,
UpdateElement updateElement) {
if (brokenDeps != null) {
return brokenDeps;
}
+ // shortcut, because OperationValidator.getRequiredElements is NOT
consistent with
+ // OperationValidator.getBrokenDependencies: while
getRequiredElements ignores breakages of
+ // feature contents, getBrokenDependencies wille verify them in
full, so feature's optional (platform-specific) module
+ // will report the whole feature as broken on activation or
install.
+ if (requiredElements != null) {
+ getRequiredElements();
+ if (brokenDeps != null) {
+ return brokenDeps;
+ }
+ }
List<ModuleInfo> moduleInfos = new ArrayList<ModuleInfo>();
+ Set<String> brokenContents = new HashSet<>();
for (OperationContainer.OperationInfo oii : listAll ()) {
UpdateElementImpl impl = Trampoline.API.impl
(oii.getUpdateElement ());
Collection<ModuleInfo> infos = impl.getModuleInfos ();
assert infos != null : "ModuleInfo for UpdateElement " +
oii.getUpdateElement () + " found.";
moduleInfos.addAll (infos);
+ if (impl.getType() == UpdateManager.TYPE.FEATURE) {
+ // add unknown tokens / codenames as broken content.
+
brokenContents.addAll(((FeatureUpdateElementImpl)impl).getMissingElements());
+ }
+ }
+ brokenContents.addAll(OperationValidator.getBrokenDependencies
(type, getUpdateElement (), moduleInfos));
+ return brokenContents;
+ }
+
+ /**
+ * Reports missing parts of the feature.
+ * @return missing/unknown directly contained codenames
+ */
+ public Set<String> getMissingParts() {
+ Set<String> brokenContents = new HashSet<>();
+ for (OperationContainer.OperationInfo oii : listAll ()) {
+ UpdateElementImpl impl = Trampoline.API.impl
(oii.getUpdateElement ());
+ if (impl instanceof FeatureUpdateElementImpl) {
+
brokenContents.addAll(((FeatureUpdateElementImpl)impl).getMissingElements());
+ }
}
- return OperationValidator.getBrokenDependencies (type,
getUpdateElement (), moduleInfos);
+ return brokenContents;
}
}
diff --git
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationValidator.java
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationValidator.java
index 9d1f0b08b8..12724f39c6 100644
---
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationValidator.java
+++
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/OperationValidator.java
@@ -25,7 +25,9 @@
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 static
org.netbeans.modules.autoupdate.services.Utilities.findRequiredUpdateElements;
import org.openide.modules.Dependency;
import org.openide.modules.ModuleInfo;
@@ -369,6 +371,7 @@ private boolean isValidOperationImpl (UpdateElementImpl
impl) {
}
}
List<UpdateElement> retval = new ArrayList<UpdateElement>();
+ Set<Dependency> brokenDeps = new HashSet<Dependency>();
if (mm != null) {
List<Module> toEnable = getModulesToEnable(mm, modules);
for (Module module : toEnable) {
@@ -379,10 +382,58 @@ private boolean isValidOperationImpl (UpdateElementImpl
impl) {
}
}
}
+ if (uElementImpl.getUpdateUnit() != null &&
+ uElementImpl.getType() == UpdateManager.TYPE.FEATURE) {
+ FeatureUpdateElementImpl ufi =
(FeatureUpdateElementImpl)uElementImpl;
+ for (ModuleUpdateElementImpl module :
ufi.getContainedModuleElements ()) {
+ Set<UpdateElement> els = findRequiredUpdateElements
(module.getUpdateElement (), moduleInfos, brokenDeps, true,
+ recommendedElements);
+ if (module.getUpdateUnit().getInstalled() == null) {
+ retval.add(module.getUpdateElement());
+ retval.addAll (els);
+ }
+ }
+ addMissingElements(ufi, brokenDependencies);
+ for (UpdateElement avail :
uElementImpl.getUpdateUnit().getAvailableUpdates()) {
+ UpdateElementImpl availImpl = Trampoline.API.impl (avail);
+ if (availImpl.getType() == UpdateManager.TYPE.FEATURE) {
+ // process container modules, which are known, but not
installed
+ // and are part of the feature
+ FeatureUpdateElementImpl feature =
(FeatureUpdateElementImpl)availImpl;
+ for (ModuleUpdateElementImpl module :
feature.getContainedModuleElements ()) {
+ if (module.getUpdateUnit().getInstalled() == null)
{
+ retval.add(module.getUpdateElement());
+ retval.addAll (
+ findRequiredUpdateElements
(module.getUpdateElement (), moduleInfos, brokenDeps, true,
+ recommendedElements));
+ }
+ }
+ // process contained modules, which are NOT known into
broken deps:
+ addMissingElements(feature, brokenDependencies);
+ }
+ }
+ for (FeatureUpdateElementImpl fei :
ufi.getDependingFeatures()) {
+
retval.addAll(getRequiredElementsImpl(fei.getUpdateElement(),
fei.getModuleInfos(),
+ brokenDependencies, recommendedElements));
+ }
+ // do IGNORE non-module dependencies from the feature: there
may be platform- or java-specific
+ // modules that have "broken" requirements.
+ for (Dependency d : brokenDeps) {
+ if (d.getType() == Dependency.TYPE_MODULE) {
+ brokenDependencies.add(d.toString());
+ }
+ }
+ }
return retval;
}
}
+ private static void addMissingElements(FeatureUpdateElementImpl feature,
Collection<String> broken) {
+ for (String s : feature.getMissingElements()) {
+ broken.add("module " + s); // NOI18N
+ }
+ }
+
private static class DisableValidator extends OperationValidator {
@Override
boolean isValidOperationImpl(UpdateUnit unit, UpdateElement uElement) {
diff --git
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateManagerImpl.java
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateManagerImpl.java
index 23c4d8b1e4..4ae351f35e 100644
---
a/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateManagerImpl.java
+++
b/platform/autoupdate.services/src/org/netbeans/modules/autoupdate/services/UpdateManagerImpl.java
@@ -67,6 +67,27 @@ public void clearCache () {
}
}
+ /**
+ * Flushes the cache, but does not flush the cache units themselves.
+ * This prevents re-creation of UpdateUnit instances; old instances were
+ * updated with info from the update step.
+ */
+ public void flushComputedInfo() {
+ Cache c;
+
+ synchronized(UpdateManagerImpl.Cache.class) {
+ Reference<Cache> cr = getCacheReference();
+ if (cr == null) {
+ return;
+ }
+ c = cr.get();
+ if (c == null) {
+ return;
+ }
+ }
+ c.clearMaps();
+ }
+
public static List<UpdateUnit> getUpdateUnits (UpdateProvider provider,
UpdateManager.TYPE... types) {
return filterUnitsByAskedTypes
(UpdateUnitFactory.getDefault().getUpdateUnits (provider).values (),
type2checkedList (types));
}
@@ -235,7 +256,7 @@ public UpdateUnitProvider getUpdateUnitProvider(String
source) {
}
private class Cache {
- private Map<String, UpdateUnit> units;
+ private final Map<String, UpdateUnit> units;
private Set<UpdateElement> availableEagers = null;
private Set<UpdateElement> installedEagers = null;
private Map<String, Collection<ModuleInfo>> token2installedProviders =
null;
@@ -245,35 +266,44 @@ public UpdateUnitProvider getUpdateUnitProvider(String
source) {
Cache() {
units = UpdateUnitFactory.getDefault ().getUpdateUnits ();
}
- public Set<UpdateElement> getAvailableEagers() {
+
+ synchronized void clearMaps() {
+ availableEagers = null;
+ installedEagers = null;
+ token2installedProviders = null;
+ token2availableProviders = null;
+ cluster2installedKits = null;
+ }
+
+ public synchronized Set<UpdateElement> getAvailableEagers() {
if (availableEagers == null) {
createMaps ();
}
assert availableEagers != null : "availableEagers initialized";
return availableEagers;
}
- public Set<UpdateElement> getInstalledEagers() {
+ public synchronized Set<UpdateElement> getInstalledEagers() {
if (installedEagers == null) {
createMaps ();
}
assert installedEagers != null : "installedEagers initialized";
return installedEagers;
}
- public Map<String, Collection<ModuleInfo>>
createMapToken2InstalledProviders () {
+ public synchronized Map<String, Collection<ModuleInfo>>
createMapToken2InstalledProviders () {
if (token2installedProviders == null) {
createMaps ();
}
assert token2installedProviders != null :
"token2installedProviders initialized";
return token2installedProviders;
}
- public Map<String, Collection<ModuleInfo>>
createMapToken2AvailableProviders () {
+ public synchronized Map<String, Collection<ModuleInfo>>
createMapToken2AvailableProviders () {
if (token2availableProviders == null) {
createMaps ();
}
assert token2availableProviders != null :
"token2availableProviders initialized";
return token2availableProviders;
}
- public Map<String, TreeSet<UpdateElement>>
createMapCluster2installedKits() {
+ public synchronized Map<String, TreeSet<UpdateElement>>
createMapCluster2installedKits() {
if (cluster2installedKits == null) {
createMaps();
}
diff --git
a/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/UpdateItem.java
b/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/UpdateItem.java
index 1ed5751465..2db1706594 100644
---
a/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/UpdateItem.java
+++
b/platform/autoupdate.services/src/org/netbeans/spi/autoupdate/UpdateItem.java
@@ -142,7 +142,9 @@ public static UpdateItem createModule (
/** Creates <code>UpdateItem</code> which represents <code>Feature</code>,
it's means group
* of NetBeans Modules. This <code>Feature</code> is handled in UI as
atomic item.
* UpdateItem is identify by <code>codeName</code> and
<code>specificationVersion<code>.
- *
+ * <p/>
+ * If some of the tokens in {@code dependencies} is not known, it will be
reported
+ * as a missing dependency.
*
* @param codeName code name of feature
* @param specificationVersion specification version of feature
@@ -151,6 +153,7 @@ public static UpdateItem createModule (
* @param description description
* @param category name of category
* @return UpdateItem
+ * @since 1.57 specified handling of unknown tokens
*/
public static UpdateItem createFeature (
String codeName,
diff --git
a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/RefreshItemsTest.java
b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/RefreshItemsTest.java
index 90075761e0..6ef0d4ab24 100644
---
a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/RefreshItemsTest.java
+++
b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/RefreshItemsTest.java
@@ -46,7 +46,7 @@ public void testRefreshItems () throws IOException {
populateCatalog(TestUtils.class.getResourceAsStream("data/updates-subset.xml"));
UpdateUnitProviderFactory.getDefault ().refreshProviders(null, true);
assertEquals(UpdateManager.getDefault().getUpdateUnits(UpdateManager.TYPE.MODULE).toString(),
- updateUnitsCount - 2,
UpdateManager.getDefault().getUpdateUnits(UpdateManager.TYPE.MODULE).size());
+ updateUnitsCount - 3,
UpdateManager.getDefault().getUpdateUnits(UpdateManager.TYPE.MODULE).size());
UpdateUnit toTestAgain = UpdateManagerImpl.getInstance
().getUpdateUnit ("org.yourorghere.refresh_providers_test");
assertNotNull ("Unit for org.yourorghere.refresh_providers_test
found.", toTestAgain);
diff --git
a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/RefreshProvidersTest.java
b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/RefreshProvidersTest.java
index acbbab4e30..eb8cca541d 100644
---
a/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/RefreshProvidersTest.java
+++
b/platform/autoupdate.services/test/unit/src/org/netbeans/api/autoupdate/RefreshProvidersTest.java
@@ -39,7 +39,7 @@ public void testRefreshProviders () throws IOException {
populateCatalog(TestUtils.class.getResourceAsStream("data/updates-subset.xml"));
UpdateUnitProviderFactory.getDefault ().refreshProviders(null, true);
assertEquals(UpdateManager.getDefault().getUpdateUnits(UpdateManager.TYPE.MODULE).toString(),
- updateUnitsCount - 2,
UpdateManager.getDefault().getUpdateUnits(UpdateManager.TYPE.MODULE).size());
+ updateUnitsCount - 3,
UpdateManager.getDefault().getUpdateUnits(UpdateManager.TYPE.MODULE).size());
}
}
diff --git a/platform/autoupdate.ui/nbproject/project.properties
b/platform/autoupdate.ui/nbproject/project.properties
index 3f1836a727..92b3463886 100644
--- a/platform/autoupdate.ui/nbproject/project.properties
+++ b/platform/autoupdate.ui/nbproject/project.properties
@@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.
javac.compilerargs=-Xlint:unchecked
-javac.source=1.7
+javac.source=1.8
test.config.xmlinst.includes=\
org/netbeans/test/autoupdate/xml/xml_0001.class
diff --git a/platform/autoupdate.ui/nbproject/project.xml
b/platform/autoupdate.ui/nbproject/project.xml
index d1d0e739e0..8615fb5fb9 100644
--- a/platform/autoupdate.ui/nbproject/project.xml
+++ b/platform/autoupdate.ui/nbproject/project.xml
@@ -56,7 +56,7 @@
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
- <specification-version>1.39</specification-version>
+ <specification-version>1.57</specification-version>
</run-dependency>
</dependency>
<dependency>
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/PluginManagerUI.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/PluginManagerUI.java
index fc771b8dae..9c8ca8efef 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/PluginManagerUI.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/PluginManagerUI.java
@@ -62,7 +62,7 @@
private final Object initLock = new Object();
private Object helpInstance = null;
- private static RequestProcessor.Task runningTask;
+ private static volatile RequestProcessor.Task runningTask;
private static Collection<Runnable> runOnCancel = null;
private boolean wasSettings = false;
public static final int INDEX_OF_UPDATES_TAB = 0;
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UnitTab.form
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UnitTab.form
index d524a5314f..39b3827bb1 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UnitTab.form
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UnitTab.form
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="UTF-8" ?>
-
<!--
Licensed to the Apache Software Foundation (ASF) under one
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UnitTab.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UnitTab.java
index f5b9aebbfe..d35c2b2fe1 100644
--- a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UnitTab.java
+++ b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/UnitTab.java
@@ -36,12 +36,16 @@
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
+import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.PreferenceChangeEvent;
@@ -77,6 +81,7 @@
import org.netbeans.api.progress.ProgressHandleFactory;
import
org.netbeans.modules.autoupdate.ui.wizards.OperationWizardModel.OperationType;
import org.openide.awt.Mnemonics;
+import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
@@ -1577,25 +1582,82 @@ public void tableDataChanged(Collection<Unit> units) {
}
setEnabled(false);
}
-
- @Override
- public void performerImpl() {
- final int row = getSelectedRow ();
- final Map<String, Boolean> state =
UnitCategoryTableModel.captureState (model.getUnits ());
+
+ boolean finished = false;
+ Map<String, Boolean> state;
+
+ void clearContainers() {
+ Containers.forEnable().removeAll();
+ Containers.forDisable().removeAll();
+ Containers.forAvailable().removeAll();
+ Containers.forUninstall ().removeAll();
+ }
+
+ /**
+ * Forces refresh of UI data. Replans work to EDT, but waits for the
result.
+ * First waits until the download/install task completes, so that UI
models
+ * for checking items are not suspended by the pending operation.
+ * <p/>
+ * Will <b>clean all containers</b> and populate them again from the
UI.
+ * @return newly populated Container
+ */
+ OperationContainer refreshData() {
+ if (!SwingUtilities.isEventDispatchThread()) {
+ final OperationContainer[] c = new OperationContainer[1];
+ RequestProcessor.Task runningTask =
PluginManagerUI.getRunningTask();
+ if (runningTask != null) {
+ runningTask.waitFinished();
+ }
+ try {
+ SwingUtilities.invokeAndWait(() -> {
+ c[0] = refreshData();
+ });
+ } catch (InterruptedException | InvocationTargetException ex) {
+ OperationContainer x = Containers.forEnable();
+ x.removeAll();
+ }
+ return c[0];
+ }
+ refreshState ();
+ try {
+ clearContainers();
+ fireUpdataUnitChange();
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ if(!finished) {
+ UnitCategoryTableModel.restoreState (model.getUnits (), state,
model.isMarkedAsDefault ());
+ }
+ return prepareContainer();
+ }
+
+ OperationContainer prepareContainer() {
OperationContainer<OperationSupport> c = Containers.forEnable();
+ Collection<Unit> units = new ArrayList<>();
for (Unit u : model.getUnits()) {
if (u.isMarked() && isEnabled(u)) {
- c.add(u.updateUnit, u.getRelevantElement());
+ units.add(u);
}
}
+ c.removeAll();
+ for (Unit u : units) {
+ c.add(u.updateUnit, u.getRelevantElement());
+ }
+ return c;
+ }
+
+ @Override
+ public void performerImpl() {
+ final int row = getSelectedRow ();
+ state = UnitCategoryTableModel.captureState (model.getUnits ());
UninstallUnitWizard wizard = new UninstallUnitWizard ();
- boolean finished = false;
+ finished = false;
+ // unused, but hold the reference on stack.
+ OperationContainer c = prepareContainer();
try {
- finished = wizard.invokeWizard(true);
+ finished = wizard.invokeWizard(true, this::refreshData);
} finally {
- Containers.forEnable().removeAll();
- Containers.forDisable().removeAll();
- Containers.forUninstall ().removeAll();
+ clearContainers();
if (finished) {
for (Unit u : model.getMarkedUnits()) {
u.setMarked(false);
@@ -1616,7 +1678,7 @@ protected boolean isEnabled (Unit u) {
if ((u != null) && (u instanceof Unit.Installed)) {
Unit.Installed i = (Unit.Installed)u;
if (!i.getRelevantElement ().isEnabled ()) {
- retval = Unit.Installed.isOperationAllowed (u.updateUnit,
u.getRelevantElement (), Containers.forEnable ());
+ retval = Unit.Installed.isOperationAllowed (u.updateUnit,
u.getRelevantElement (), Containers.forEnable ());
}
}
return retval;
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/Utilities.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/Utilities.java
index 563abd4cfe..a604af3fbb 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/Utilities.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/Utilities.java
@@ -23,6 +23,7 @@
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
+import java.nio.file.Files;
import java.text.Collator;
import java.text.SimpleDateFormat;
import java.util.*;
@@ -38,6 +39,7 @@
import org.netbeans.modules.autoupdate.ui.actions.ShowNotifications;
import org.openide.awt.HtmlBrowser;
import org.openide.awt.Mnemonics;
+import org.openide.modules.Places;
import org.openide.util.*;
/**
@@ -771,5 +773,26 @@ private static File getPlatformDir () {
private static Preferences getPreferences () {
return NbPreferences.forModule (Utilities.class);
}
+
+ /**
+ * Hacky way how to determine if the AU catalog providers have built their
+ * caches. Checks just for the default provider cache filenames. Used for
decision
+ * that user should perform check for updates to get plugin portal
contents.
+ *
+ * @return true, if caches are present.
+ */
+ public static boolean hasBuiltDefaultCaches() {
+ File cacheDir = Places.getCacheSubdirectory("catalogcache");
+ if (!cacheDir.exists()) {
+ return false;
+ }
+ try {
+ return Files.list(cacheDir.toPath()).anyMatch((p) ->
+ p.getName(p.getNameCount() -
1).toString().endsWith("-update-provider")
+ );
+ } catch (IOException ex) {
+ return false;
+ }
+ }
}
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties
index 2c74d29169..e0f4999c77 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/Bundle.properties
@@ -163,3 +163,5 @@
OperationDescriptionStep_AffectedPlugin= {0}<
InstallStep_DownloadProblem=There were some problems while storing
{0}.\nCause: {1}
InstallStep_DownloadProblem_SomePlugins=selected plugins
LicenseApprovalPanel.lbPlugins.text=Plugins:
+OperationDescriptionStep_NoteCachesNotBuilt=<br><hr><br>\
+ <b>Note:</b> Missing modules may be published on Update Centers. Please
run "<b>Check for Newest</b>" from the "<b>Available Plugins</b>" tab.
\ No newline at end of file
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallStep.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallStep.java
index 1797b9541f..3b3ff814a1 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallStep.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallStep.java
@@ -41,6 +41,8 @@
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
+import javax.swing.event.AncestorEvent;
+import javax.swing.event.AncestorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TreeSelectionEvent;
@@ -83,7 +85,7 @@
*
* @author Jiri Rechtacek
*/
-public class InstallStep implements
WizardDescriptor.FinishablePanel<WizardDescriptor> {
+class InstallStep implements
WizardDescriptor.FinishablePanel<WizardDescriptor> {
private OperationPanel panel;
private PanelBodyContainer component;
private InstallUnitWizardModel model = null;
@@ -128,6 +130,15 @@
private final boolean allowRunInBackground;
private final boolean runInBackground;
+ /**
+ * If true, will trigger delayed modification of the wizard,
+ * see {@link #delayedModifyEnabled}. The flag is set from within
+ * successful installation, but the actual modification should be done
+ * only after installation task completes, so that access to the
+ * model is not blocked by a pending operation
+ */
+ boolean delayedModifyEnabled = true;
+
/** Creates a new instance of OperationDescriptionStep */
public InstallStep (InstallUnitWizardModel model) {
this (model, false);
@@ -156,7 +167,24 @@ public boolean isFinishPanel() {
@Override
public PanelBodyContainer getComponent() {
if (component == null) {
+ delayedModifyEnabled = false;
panel = new OperationPanel(allowRunInBackground, runInBackground);
+
+ // Disable 'install' button right after the panel appears.
+ panel.addAncestorListener(new AncestorListener() {
+ public void ancestorAdded(AncestorEvent event) {
+ model.modifyOptionsForInstall(wd);
+ }
+
+ @Override
+ public void ancestorRemoved(AncestorEvent event) {
+ }
+
+ @Override
+ public void ancestorMoved(AncestorEvent event) {
+ }
+
+ });
panel.addPropertyChangeListener (new PropertyChangeListener () {
@Override
public void propertyChange (PropertyChangeEvent evt) {
@@ -167,6 +195,10 @@ public void propertyChange (PropertyChangeEvent evt) {
it.waitFinished ();
} finally {
PluginManagerUI.unregisterRunningTask ();
+ // wait for the task to finish, THEN refresh the
model
+ // and THEN modify the dialog's appearance
+ model.performRefresh();
+ delayedModifyOptions();
}
} else if (OperationPanel.RUN_IN_BACKGROUND.equals
(evt.getPropertyName ())) {
setRunInBackground ();
@@ -190,8 +222,7 @@ public void run () {
@SuppressWarnings("null")
private void doDownloadAndVerificationAndInstall () {
-
- OperationContainer<InstallSupport> installContainer =
model.getBaseContainer();
+ OperationContainer<InstallSupport> installContainer =
model.getInstallContainer();
final InstallSupport support = installContainer.getSupport();
assert support != null : "Operation failed: OperationSupport cannot be
null because OperationContainer "
+ "contains elements: " + installContainer.listAll() + " and
invalid elements " + installContainer.listInvalid() + "\ncontainer: " +
installContainer;
@@ -201,8 +232,8 @@ private void doDownloadAndVerificationAndInstall () {
log.log(Level.WARNING, "Operation failed: OperationSupport was
null because OperationContainer "
+ "either does not contain any elements: {0} or contains
invalid elements {1}",
new Object[]{
- model.getBaseContainer().listAll(),
- model.getBaseContainer().listInvalid()});
+ model.getInstallContainer().listAll(),
+ model.getInstallContainer().listInvalid()});
return ;
}
@@ -222,7 +253,8 @@ private void doDownloadAndVerificationAndInstall () {
}
}
if (! canceled) {
- fireChange ();
+ // delay the fire untilt he task completes
+
org.netbeans.modules.autoupdate.ui.actions.Installer.RP.post(this::fireChange);
}
}
@@ -315,7 +347,7 @@ public boolean cancel () {
return handleCancel ();
}
});
- totalUnits = model.getBaseContainer ().listAll ().size ();
+ totalUnits = model.getInstallContainer ().listAll ().size ();
processedUnits = 0;
detailLabel.addPropertyChangeListener (TEXT_PROPERTY, new
PropertyChangeListener () {
@Override
@@ -434,7 +466,7 @@ public boolean cancel () {
return true;
}
});
- totalUnits = model.getBaseContainer ().listAll ().size ();
+ totalUnits = model.getInstallContainer ().listAll ().size ();
processedUnits = 0;
if (indeterminateProgress) {
detailLabel.addPropertyChangeListener (TEXT_PROPERTY, new
PropertyChangeListener () {
@@ -620,7 +652,7 @@ private Restarter handleInstall(Installer i, final
InstallSupport support) {
handle = systemHandle;
} else {
spareHandle = ProgressHandleFactory.createHandle (getBundle
("InstallStep_Install_InstallingPlugins"));
- totalUnits = model.getBaseContainer ().listAll ().size ();
+ totalUnits = model.getInstallContainer ().listAll ().size ();
processedUnits = 0;
if (indeterminateProgress) {
detailLabel.addPropertyChangeListener (TEXT_PROPERTY, new
PropertyChangeListener () {
@@ -668,12 +700,25 @@ public void propertyChange (PropertyChangeEvent evt) {
return r;
}
+ /**
+ * Performs delayed update of wizard buttons. Does nothing if the operation
+ * does not complete successfuly.
+ */
+ private void delayedModifyOptions() {
+ if (delayedModifyEnabled) {
+ model.modifyOptionsForEndInstall (wd);
+ }
+ }
+
private void presentInstallDone () {
if (canceled) {
log.fine("Quit presentInstallDone() because an previous
installation was canceled.");
return ;
}
- model.modifyOptionsForDoClose (wd);
+ // The buttons should not be changed right now, because the UI could
need to access the model
+ // which is partially obscured by a running install Task. The buttons
will be changed once
+ // the Task completes in RequestProcessor.
+ delayedModifyEnabled = true;
if (installException == null) {
component.setHeadAndContent (getBundle (HEAD_INSTALL_DONE),
getBundle (CONTENT_INSTALL_DONE));
panel.setBody (getBundle ("InstallStep_InstallDone_Text"),
@@ -817,8 +862,8 @@ public void storeSettings (WizardDescriptor wd) {
}
} else if (restarter != null) {
if (support == null) {
- assert model.getBaseContainer().listAll() == null :
"storeSettings failed. OperationSupport is null because OperationContainer " +
- "contains no elements: " + model.getBaseContainer
().listAll ();
+ assert model.getInstallContainer().listAll() == null :
"storeSettings failed. OperationSupport is null because OperationContainer " +
+ "contains no elements: " + model.getInstallContainer
().listAll ();
return ;
}
if (panel.restartNow ()) {
@@ -927,7 +972,7 @@ private UpdateElement findCulprit(String codeName) {
if (codeName == null || codeName.isEmpty()) {
return null;
}
- for (OperationInfo<InstallSupport> info :
model.getBaseContainer().listAll()) {
+ for (OperationInfo<InstallSupport> info :
model.getInstallContainer().listAll()) {
if (codeName.equals(info.getUpdateElement().getCodeName())) {
return info.getUpdateElement();
}
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallUnitWizardIterator.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallUnitWizardIterator.java
index 9942145d31..9fc9f110e9 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallUnitWizardIterator.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallUnitWizardIterator.java
@@ -19,29 +19,13 @@
package org.netbeans.modules.autoupdate.ui.wizards;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.NoSuchElementException;
-import javax.swing.event.ChangeListener;
-import org.openide.WizardDescriptor;
import org.openide.util.NbBundle;
/**
*
* @author Jiri Rechtacek
*/
-public final class InstallUnitWizardIterator implements
WizardDescriptor.Iterator<WizardDescriptor> {
-
- private int index;
- private final List<WizardDescriptor.Panel<WizardDescriptor>> panels = new
ArrayList<WizardDescriptor.Panel<WizardDescriptor>> ();
- private InstallUnitWizardModel installModel;
- private WizardDescriptor.Panel<WizardDescriptor> licenseApprovalStep =
null;
- private WizardDescriptor.Panel<WizardDescriptor> customHandleStep = null;
- private WizardDescriptor.Panel<WizardDescriptor> installStep = null;
- private boolean isCompact = false;
- private boolean clearLazyUnits = false;
- private final boolean allowRunInBackground;
- private final boolean runInBackground;
+final class InstallUnitWizardIterator extends InstallableIteratorBase {
public InstallUnitWizardIterator (InstallUnitWizardModel model) {
this (model, false);
@@ -56,12 +40,8 @@ public InstallUnitWizardIterator (InstallUnitWizardModel
model, boolean clearLaz
}
public InstallUnitWizardIterator (InstallUnitWizardModel model, boolean
clearLazyUnits, boolean allowRunInBackground, boolean runInBackground) {
- this.installModel = model;
- this.clearLazyUnits = clearLazyUnits;
- this.allowRunInBackground = allowRunInBackground;
- this.runInBackground = runInBackground;
+ super(model, clearLazyUnits, allowRunInBackground, runInBackground);
createPanels ();
- index = 0;
}
public InstallUnitWizardModel getModel () {
@@ -72,18 +52,7 @@ public InstallUnitWizardModel getModel () {
private void createPanels () {
assert panels != null && panels.isEmpty() : "Panels are still empty";
panels.add (new OperationDescriptionStep (installModel));
- licenseApprovalStep = new LicenseApprovalStep (installModel);
- panels.add (licenseApprovalStep);
- customHandleStep = new CustomHandleStep (installModel);
- panels.add (customHandleStep);
- installStep = new InstallStep (installModel, clearLazyUnits,
allowRunInBackground, runInBackground);
- panels.add (installStep);
- }
-
- @Override
- public WizardDescriptor.Panel<WizardDescriptor> current () {
- assert panels != null;
- return panels.get (index);
+ insertPanels(1);
}
@Override
@@ -91,58 +60,9 @@ public String name () {
return NbBundle.getMessage (InstallUnitWizardIterator.class,
"InstallUnitWizard_Title");
}
- @Override
- public boolean hasNext () {
- compactPanels ();
- return index < panels.size () - 1;
- }
-
@Override
public boolean hasPrevious () {
- compactPanels ();
- return index > 0 && ! (current () instanceof InstallStep || current ()
instanceof CustomHandleStep);
- }
-
- @Override
- public void nextPanel () {
- compactPanels ();
- if (!hasNext ()) {
- throw new NoSuchElementException ();
- }
- index++;
- }
-
- @Override
- public void previousPanel () {
- compactPanels ();
- if (!hasPrevious ()) {
- throw new NoSuchElementException ();
- }
- index--;
- }
-
- // If nothing unusual changes in the middle of the wizard, simply:
- @Override
- public void addChangeListener (ChangeListener l) {}
- @Override
- public void removeChangeListener (ChangeListener l) {}
-
- private void compactPanels () {
- if (isCompact) {
- return ;
- }
-
- boolean allLicensesTouched = getModel().allLicensesTouched();
- if (allLicensesTouched && getModel ().allLicensesApproved ()) {
- panels.remove (licenseApprovalStep);
- }
- if (! getModel ().hasCustomComponents ()) {
- panels.remove (customHandleStep);
- }
- if (! getModel ().hasStandardComponents ()) {
- panels.remove (installStep);
- }
- isCompact = allLicensesTouched;
+ return super.hasPrevious() && ! (current () instanceof InstallStep ||
current () instanceof CustomHandleStep);
}
}
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallUnitWizardModel.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallUnitWizardModel.java
index b6c07fed4b..4631bf61e4 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallUnitWizardModel.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallUnitWizardModel.java
@@ -34,7 +34,7 @@
*
* @author Jiri Rechtacek
*/
-public final class InstallUnitWizardModel extends OperationWizardModel {
+public class InstallUnitWizardModel extends OperationWizardModel {
private Installer installer = null;
private OperationType doOperation;
private static Set<String> approvedLicences = new HashSet<String> ();
@@ -67,6 +67,11 @@ public OperationType getOperation () {
", invalid: " + c.listInvalid() + "]: but support is " +
c.getSupport();
return c;
}
+
+ @Override
+ OperationContainer<InstallSupport> getInstallContainer() {
+ return getBaseContainer();
+ }
private OperationContainer<InstallSupport> getBaseContainerImpl() {
if (updateContainer != null) {
@@ -179,5 +184,4 @@ public PluginManagerUI getPluginManager () {
public void setPluginManager (PluginManagerUI manager) {
this.manager = manager;
}
-
}
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallableIteratorBase.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallableIteratorBase.java
new file mode 100644
index 0000000000..a08b979194
--- /dev/null
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/InstallableIteratorBase.java
@@ -0,0 +1,150 @@
+/*
+ * 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.ui.wizards;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import javax.swing.event.ChangeListener;
+import org.openide.WizardDescriptor;
+
+/**
+ * Common base for AU wizard iterators, does basic panel handling.
+ *
+ * @author sdedic
+ */
+abstract class InstallableIteratorBase implements
WizardDescriptor.Iterator<WizardDescriptor> {
+ protected final List<WizardDescriptor.Panel<WizardDescriptor>> panels =
new ArrayList<WizardDescriptor.Panel<WizardDescriptor>> ();
+ protected final InstallUnitWizardModel installModel;
+
+ protected int index;
+ private WizardDescriptor.Panel<WizardDescriptor> licenseApprovalStep =
null;
+ private WizardDescriptor.Panel<WizardDescriptor> customHandleStep = null;
+ private WizardDescriptor.Panel<WizardDescriptor> installStep = null;
+ private boolean isCompact = false;
+ private boolean clearLazyUnits = false;
+ private final boolean allowRunInBackground;
+ private final boolean runInBackground;
+
+ public InstallableIteratorBase (InstallUnitWizardModel model) {
+ this (model, false);
+ }
+
+ public InstallableIteratorBase (InstallUnitWizardModel model, boolean
clearLazyUnits) {
+ this(model, clearLazyUnits, true, true);
+ }
+
+ public InstallableIteratorBase (InstallUnitWizardModel model, boolean
clearLazyUnits, boolean allowRunInBackground) {
+ this(model, clearLazyUnits, allowRunInBackground, false);
+ }
+
+ public InstallableIteratorBase (InstallUnitWizardModel model, boolean
clearLazyUnits, boolean allowRunInBackground, boolean runInBackground) {
+ this.installModel = model;
+ this.clearLazyUnits = clearLazyUnits;
+ this.allowRunInBackground = allowRunInBackground;
+ this.runInBackground = runInBackground;
+ index = 0;
+ }
+
+ public InstallUnitWizardModel getModel () {
+ assert installModel != null;
+ return installModel;
+ }
+
+ @Override
+ public WizardDescriptor.Panel<WizardDescriptor> current () {
+ assert panels != null;
+ return panels.get (index);
+ }
+
+ @Override
+ public boolean hasNext () {
+ compactPanels ();
+ return index < panels.size () - 1;
+ }
+
+ @Override
+ public void nextPanel () {
+ compactPanels ();
+ if (!hasNext ()) {
+ throw new NoSuchElementException ();
+ }
+ index++;
+ }
+
+ @Override
+ public void previousPanel () {
+ compactPanels ();
+ if (!hasPrevious()) {
+ throw new NoSuchElementException ();
+ }
+ index--;
+ }
+
+ @Override
+ public boolean hasPrevious () {
+ compactPanels ();
+ return index > 0;
+ }
+
+ // If nothing unusual changes in the middle of the wizard, simply:
+ @Override
+ public void addChangeListener (ChangeListener l) {}
+ @Override
+ public void removeChangeListener (ChangeListener l) {}
+
+ /**
+ * Inserts install-related panels at a specific index
+ * @param index
+ */
+ protected void insertPanels(int index) {
+ licenseApprovalStep = new LicenseApprovalStep (installModel);
+ panels.add (index++, licenseApprovalStep);
+ customHandleStep = new CustomHandleStep (installModel);
+ panels.add (index++, customHandleStep);
+ installStep = new InstallStep (installModel, clearLazyUnits,
allowRunInBackground, runInBackground);
+ panels.add (index++, installStep);
+ }
+
+ /**
+ * Removes previously registered install panels
+ */
+ protected void removeInstallPanels() {
+ panels.remove(licenseApprovalStep);
+ panels.remove(installStep);
+ panels.remove(customHandleStep);
+ }
+
+ protected void compactPanels () {
+ if (isCompact) {
+ return ;
+ }
+ boolean allLicensesTouched = getModel().allLicensesTouched();
+ if (allLicensesTouched && getModel ().allLicensesApproved ()) {
+ panels.remove (licenseApprovalStep);
+ }
+ if (! getModel ().hasCustomComponents ()) {
+ panels.remove (customHandleStep);
+ }
+ if (! getModel ().hasStandardComponents ()) {
+ panels.remove (installStep);
+ }
+ isCompact = allLicensesTouched;
+ }
+}
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/LicenseApprovalStep.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/LicenseApprovalStep.java
index 2cd5376376..d3cc2905c4 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/LicenseApprovalStep.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/LicenseApprovalStep.java
@@ -43,7 +43,7 @@
*
* @author Jiri Rechtacek
*/
-public class LicenseApprovalStep implements
WizardDescriptor.FinishablePanel<WizardDescriptor> {
+class LicenseApprovalStep implements
WizardDescriptor.FinishablePanel<WizardDescriptor> {
private LicenseApprovalPanel panel;
private PanelBodyContainer component;
private InstallUnitWizardModel model = null;
@@ -70,7 +70,8 @@ public Component getComponent() {
component = new PanelBodyContainer (getBundle (HEAD), getBundle
(CONTENT), tmp);
component.setPreferredSize
(OperationWizardModel.PREFFERED_DIMENSION);
if (wd != null) {
- model.modifyOptionsForDoOperation (wd);
+ // have "Activate" text during 'enable' phase.
+ model.modifyOptionsForDoOperation (wd, 2);
}
component.setWaitingState (true);
appendLoadingLazy ();
@@ -111,7 +112,8 @@ public HelpCtx getHelp() {
public void readSettings (WizardDescriptor wd) {
this.wd = wd;
if (panel != null) {
- model.modifyOptionsForDoOperation (wd);
+ // have "Activate" text during 'enable' phase.
+ model.modifyOptionsForDoOperation (wd, 2);
}
}
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/OperationDescriptionStep.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/OperationDescriptionStep.java
index d080896785..4169f6abcf 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/OperationDescriptionStep.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/OperationDescriptionStep.java
@@ -41,6 +41,7 @@
import org.netbeans.api.autoupdate.UpdateManager;
import org.netbeans.api.autoupdate.UpdateUnit;
import org.netbeans.modules.autoupdate.ui.Containers;
+import org.netbeans.modules.autoupdate.ui.Utilities;
import org.netbeans.modules.autoupdate.ui.actions.AutoupdateCheckScheduler;
import org.netbeans.modules.autoupdate.ui.actions.Installer;
import
org.netbeans.modules.autoupdate.ui.wizards.LazyInstallUnitWizardIterator.LazyUnit;
@@ -75,17 +76,23 @@
private static final String DEPENDENCIES_TITLE_UNINSTALL =
"UninstallDependenciesResolutionStep_Table_Title";
private static final String DEPENDENCIES_TITLE_ACTIVATE =
"OperationDescriptionStep_TableInstall_Title";
private static final String DEPENDENCIES_TITLE_DEACTIVATE =
"UninstallDependenciesResolutionStep_Table_Title";
- private PanelBodyContainer component;
+
+ private volatile PanelBodyContainer component;
private OperationWizardModel model = null;
private boolean readyToGo = false;
private final List<ChangeListener> listeners = new
ArrayList<ChangeListener> ();
private RequestProcessor.Task lazyDependingTask = null;
+ WizardDescriptor lastWD;
/** Creates a new instance of OperationDescriptionStep */
public OperationDescriptionStep (OperationWizardModel model) {
this.model = model;
}
+ void reset() {
+ component = null;
+ }
+
@Override
public Component getComponent() {
if (component == null) {
@@ -154,7 +161,7 @@ public Component getComponent() {
}
return component;
}
-
+
private void appendDependingLazy (final String tableTitle, final String
dependenciesTitle) {
lazyDependingTask = Installer.RP.post (new Runnable () {
@Override
@@ -200,6 +207,9 @@ public void run () {
);
component.setBody (p);
component.setWaitingState (false);
+ if (lastWD != null) {
+ updateButtons(lastWD);
+ }
fireChange ();
}
});
@@ -237,6 +247,10 @@ static String prepareBrokenDependenciesForShow
(OperationWizardModel model) {
if (moreBroken) {
s += getBundle (OperationWizardModel.MORE_BROKEN_PLUGINS);
}
+ // present a note directing user to the Check for Updates action
+ if (!model.getMissingModules().isEmpty() &&
!Utilities.hasBuiltDefaultCaches()) {
+ s += getBundle ("OperationDescriptionStep_NoteCachesNotBuilt");
+ }
return s.trim ();
}
@@ -495,9 +509,19 @@ public HelpCtx getHelp() {
@Override
public void readSettings(WizardDescriptor wd) {
+ updateButtons(wd);
+ }
+
+ /**
+ * Updates wizard buttons as appropriate for the phase.
+ * @param wd
+ */
+ private void updateButtons(WizardDescriptor wd) {
+ lastWD = wd;
boolean doOperation = ! (model instanceof InstallUnitWizardModel);
if (doOperation) {
- model.modifyOptionsForDoOperation (wd);
+ // will
+ model.modifyOptionsForDoOperation (wd, 0);
} else {
model.modifyOptionsForStartWizard (wd);
}
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/OperationWizardModel.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/OperationWizardModel.java
index d68e9cd48c..27ec514e5e 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/OperationWizardModel.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/OperationWizardModel.java
@@ -30,10 +30,12 @@
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
+import org.netbeans.api.autoupdate.InstallSupport;
import org.netbeans.api.autoupdate.OperationContainer;
import org.netbeans.api.autoupdate.OperationContainer.OperationInfo;
import org.netbeans.api.autoupdate.OperationException;
@@ -45,6 +47,8 @@
import org.netbeans.modules.autoupdate.ui.Utilities;
import org.openide.WizardDescriptor;
import org.openide.awt.Mnemonics;
+import org.openide.modules.Dependency;
+import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
/**
@@ -66,7 +70,25 @@
static String MORE_BROKEN_PLUGINS =
"OperationWizardModel_MoreBrokenPlugins"; // NOI18N
private TreeMap<String, Set<UpdateElement>> dep2plugins = null;
abstract OperationType getOperation ();
+ private Callable<OperationContainer> refreshCallable;
+ private Set<String> missingModules = new HashSet<>();
+
+ /**
+ * Basic container for the operation. This container represents the
operation itself.
+ * @return container for the operation
+ */
abstract OperationContainer getBaseContainer ();
+
+ /**
+ * Install container, if part of the operation.
+ * If the operation does not use the install step, returns {@code null}.
+ *
+ * @return install container or {@code null}
+ */
+ OperationContainer getInstallContainer() {
+ return null;
+ }
+
abstract OperationContainer<OperationSupport> getCustomHandledContainer ();
public static enum OperationType {
@@ -117,6 +139,15 @@ public boolean hasRequiredUpdateElements () {
} else {
continue;
}
+ } else if (brokenDep.toLowerCase().startsWith("module
")) { // NOI18N
+ // Special handling for modules - if the module is
missing (there's no UpdateUnit for it),
+ String modName = brokenDep.substring(7).trim();
+ Set<Dependency> deps =
Dependency.create(Dependency.TYPE_MODULE, modName);
+ for (Dependency d : deps) {
+ if
(!UpdateManager.getDefault().getUpdateUnits().stream().anyMatch((m) ->
m.getCodeName().equals(d.getName()))) {
+ missingModules.add(d.getName());
+ }
+ }
}
if (dep2plugins.get (brokenDep) == null) {
dep2plugins.put (brokenDep, new
HashSet<UpdateElement> ());
@@ -171,6 +202,15 @@ public boolean hasStandardComponents () {
return ! getBaseContainer ().listAll ().isEmpty ();
}
+ /**
+ * Set of unknown modules. May mean that the AU caches are not populated
+ * with all the update centers.
+ * @return missing module codenames
+ */
+ public Set<String> getMissingModules() {
+ return missingModules;
+ }
+
public Set<UpdateElement> getCustomHandledComponents () {
if (customHandledElements == null) {
customHandledElements = new HashSet<UpdateElement> ();
@@ -319,6 +359,15 @@ public void run () {
});
}
+ /**
+ * Will modify the wizard at the end of the install operation.
+ * The default will delegate to {@link
#modifyOptionsForDoClose(org.openide.WizardDescriptor)}.
+ * @param wd
+ */
+ public void modifyOptionsForEndInstall(WizardDescriptor wd) {
+ modifyOptionsForDoClose (wd, false);
+ }
+
// XXX Hack in WizardDescriptor
public void modifyOptionsForDoClose (final WizardDescriptor wd, final
boolean canCancel) {
recognizeButtons (wd);
@@ -351,8 +400,8 @@ public void modifyOptionsForStartWizard (WizardDescriptor
wd) {
}
public void modifyOptionsForContinue (final WizardDescriptor wd, boolean
canFinish) {
+ recognizeButtons (wd);
if (canFinish) {
- recognizeButtons (wd);
final JButton b = getOriginalFinish (wd);
Mnemonics.setLocalizedText (b, getBundle
("InstallUnitWizardModel_Buttons_Close"));
SwingUtilities.invokeLater (new Runnable () {
@@ -362,15 +411,73 @@ public void run () {
}
});
} else {
- recognizeButtons (wd);
removeFinish (wd);
Mnemonics.setLocalizedText (getOriginalNext (wd),
NbBundle.getMessage (InstallUnitWizardModel.class,
"InstallUnitWizardModel_Buttons_MnemonicNext", getBundle
("InstallUnitWizardModel_Buttons_Next")));
}
}
+ /**
+ * Brings back cancel, and sets it as a closing option.
+ * Should work properly if the Cancel was completely removed, or just
replaced using
+ * {@link #modifyOptionsForDisabledCancel}.
+ *
+ * @param wd wizard descriptor to modify
+ */
+ public void modifyOptionsContinueWithCancel(final WizardDescriptor wd) {
+ JButton b = getOriginalNext (wd);
+ JButton c = getOriginalCancel(wd);
+ Object[] opts = wd.getOptions();
+ final Object[] arr;
+ final Object[] closingArr = new Object[] { c };
+ if (!Arrays.asList(opts).contains(c)) {
+ List newOpts = new ArrayList<>(Arrays.asList(opts));
+ Object o = wd.getProperty("OperationWizardModel_disabledCancel");
+ // replace previously disabled cancel
+ int n = o == null ? -1 : newOpts.indexOf(o);
+ if (n > -1) {
+ newOpts.set(n, c);
+ } else {
+ // fallback: find 'next' and place cancel next to it
+ n = newOpts.indexOf(b);
+ if (n == -1) {
+ n = newOpts.size();
+ }
+ newOpts.add(n, c);
+ }
+ arr = newOpts.toArray();
+ } else {
+ arr = opts;
+ }
+ SwingUtilities.invokeLater (new Runnable () {
+ int cnt;
+ @Override
+ public void run () {
+ b.requestFocus();
+ if (cnt++ > 0) {
+ return;
+ }
+
+ b.setDefaultCapable(true);
+ wd.setOptions (arr);
+ wd.setClosingOptions(closingArr);
+ SwingUtilities.invokeLater(this);
+ }
+ });
+ }
+
+ /**
+ * Changes option suitably for "install" phase. Will disable next button
+ * (install is running).
+ * @param wd wizar ddescriptor
+ */
+ public void modifyOptionsForInstall(WizardDescriptor wd) {
+ recognizeButtons (wd);
+ getOriginalNext (wd).setEnabled(false);
+ }
+
// XXX Hack in WizardDescriptor
- public void modifyOptionsForDoOperation (WizardDescriptor wd) {
+ public void modifyOptionsForDoOperation (WizardDescriptor wd, int
panelType) {
recognizeButtons (wd);
removeFinish (wd);
switch (getOperation ()) {
@@ -391,6 +498,22 @@ public void modifyOptionsForDoOperation (WizardDescriptor
wd) {
Mnemonics.setLocalizedText (getOriginalNext (wd), getBundle
("UninstallUnitWizardModel_Buttons_Uninstall"));
break;
case ENABLE :
+ if (hasComponentsToInstall()) {
+ // modifications for the nested install during the enable
operation
+ switch (panelType) {
+ case 1:
+ Mnemonics.setLocalizedText (getOriginalNext (wd),
getBundle ("InstallUnitWizardModel_Buttons_Install"));
+ break;
+ case 2:
+ Mnemonics.setLocalizedText (getOriginalNext (wd),
getBundle ("UninstallUnitWizardModel_Buttons_TurnOn"));
+ break;
+ default:
+ Mnemonics.setLocalizedText (getOriginalNext (wd),
NbBundle.getMessage (InstallUnitWizardModel.class,
+ "InstallUnitWizardModel_Buttons_MnemonicNext",
getBundle ("InstallUnitWizardModel_Buttons_Next")));
+ break;
+ }
+ break;
+ }
Mnemonics.setLocalizedText (getOriginalNext (wd), getBundle
("UninstallUnitWizardModel_Buttons_TurnOn"));
break;
case DISABLE :
@@ -399,6 +522,7 @@ public void modifyOptionsForDoOperation (WizardDescriptor
wd) {
default:
assert false : "Unknown operationType " + getOperation ();
}
+ getOriginalNext (wd).setEnabled(true);
}
// XXX Hack in WizardDescriptor
@@ -418,6 +542,7 @@ public void modifyOptionsForDisabledCancel (final
WizardDescriptor wd) {
JButton b = (JButton) o;
if (b.equals (getOriginalCancel (wd))) {
JButton disabledCancel = new JButton (b.getText ());
+ wd.putProperty("OperationWizardModel_disabledCancel",
disabledCancel);
disabledCancel.setEnabled (false);
newOptionsL.add (disabledCancel);
} else {
@@ -438,7 +563,7 @@ public void doCleanup (boolean cancel) throws
OperationException {
getCustomHandledContainer ().removeAll ();
}
- private void recognizeButtons (WizardDescriptor wd) {
+ public void recognizeButtons (WizardDescriptor wd) {
if (! reconized) {
Object [] options = wd.getOptions ();
assert options != null : "options: " + options;
@@ -490,6 +615,7 @@ public void run () {
private void addRequiredElements (Set<UpdateElement> elems) {
OperationContainer baseContainer = getBaseContainer();
OperationContainer customContainer = getCustomHandledContainer();
+ OperationContainer installContainer = getInstallContainer();
for (UpdateElement el : elems) {
if (el == null || el.getUpdateUnit () == null) {
Logger.getLogger (OperationWizardModel.class.getName ()).log
(Level.INFO, "UpdateElement " + el + " cannot be null"
@@ -498,8 +624,10 @@ private void addRequiredElements (Set<UpdateElement>
elems) {
}
if (UpdateManager.TYPE.CUSTOM_HANDLED_COMPONENT ==
el.getUpdateUnit ().getType ()) {
customContainer.add (el);
- } else {
+ } else if (baseContainer.canBeAdded(el.getUpdateUnit(), el)) {
baseContainer.add (el);
+ } else if (installContainer != null &&
installContainer.canBeAdded(el.getUpdateUnit(), el)) {
+ installContainer.add(el);
}
}
}
@@ -507,4 +635,53 @@ private void addRequiredElements (Set<UpdateElement>
elems) {
private String getBundle (String key) {
return NbBundle.getMessage (InstallUnitWizardModel.class, key);
}
+
+ /**
+ * Determines if there are modules to install before the main enable
operation
+ * may be successful.
+ * @return modules to install
+ */
+ public boolean hasComponentsToInstall() {
+ OperationContainer<InstallSupport> oc = getInstallContainer();
+ if (oc == null) {
+ return false;
+ }
+ return !oc.listAll().isEmpty();
+ }
+
+ public void setRefreshCallable(Callable<OperationContainer>
refreshCallable) {
+ this.refreshCallable = refreshCallable;
+ }
+
+ /**
+ * Will trigger refresh of this model and possibly UI. If the
+ * {@link #refreshCallable} is set, the method will call it to obtain
+ * a new {@link OperationContainer} contents to initialize the model.
+ * {@link #refresh} will be called with the new container.
+ */
+ protected void performRefresh() {
+ requiredElements = null;
+ dep2plugins = null;
+ allElements = null;
+ customHandledElements = null;
+ primaryElements = null;
+
+ if (refreshCallable != null) {
+ try {
+ refresh(refreshCallable.call());
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+ }
+
+ /**
+ * Subclasses should override this method to perform a data refresh.
+ * The method is called from {@link #performRefresh} to reinitialize the
+ * wizard model, e.g. after module set/state change during the wizard
+ *
+ * @param cont the new operation container
+ */
+ protected void refresh(OperationContainer cont) {
+ }
}
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizard.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizard.java
index af4c7aae95..db5b3ce76e 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizard.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizard.java
@@ -22,8 +22,10 @@
import org.netbeans.modules.autoupdate.ui.*;
import java.awt.Dialog;
import java.text.MessageFormat;
+import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.netbeans.api.autoupdate.OperationContainer;
import org.openide.DialogDisplayer;
import org.openide.WizardDescriptor;
import org.openide.util.NbBundle;
@@ -40,14 +42,25 @@
public UninstallUnitWizard () {}
public boolean invokeWizard () {
- return invokeWizardImpl (true, null);
+ return invokeWizardImpl (true, null, null);
}
public boolean invokeWizard (boolean doEnable) {
- return invokeWizardImpl (null, doEnable ? Boolean.TRUE :
Boolean.FALSE);
+ return invokeWizardImpl (null, doEnable ? Boolean.TRUE :
Boolean.FALSE, null);
}
- private boolean invokeWizardImpl (Boolean doUninstall, Boolean doEnable) {
+ /**
+ * Invokes the wizard with the ability to call back and refresh the model
and UI
+ *
+ * @param doEnable if true will represent enable operation
+ * @param refresher non-{@code null} will be provided to the model to
refresh.
+ * @return whether the wizard was cancelled
+ */
+ public boolean invokeWizard (boolean doEnable,
Callable<OperationContainer> refresher) {
+ return invokeWizardImpl (null, doEnable ? Boolean.TRUE :
Boolean.FALSE, refresher);
+ }
+
+ private boolean invokeWizardImpl (Boolean doUninstall, Boolean doEnable,
Callable<OperationContainer> refresher) {
assert doUninstall != null || doEnable != null : "At least one action
is enabled";
assert ! (doUninstall != null && doEnable != null) : "Only once action
is enabled";
assert doUninstall == null || Containers.forUninstall () != null :
"The OperationContainer<OperationSupport> forUninstall must exist!";
@@ -56,6 +69,7 @@ private boolean invokeWizardImpl (Boolean doUninstall,
Boolean doEnable) {
UninstallUnitWizardModel model = new UninstallUnitWizardModel
(doUninstall != null
? OperationWizardModel.OperationType.UNINSTALL : doEnable ?
OperationWizardModel.OperationType.ENABLE :
OperationWizardModel.OperationType.DISABLE);
+ model.setRefreshCallable(refresher);
WizardDescriptor.Iterator<WizardDescriptor> iterator = new
UninstallUnitWizardIterator (model);
WizardDescriptor wizardDescriptor = new WizardDescriptor (iterator);
wizardDescriptor.setModal (true);
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizardIterator.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizardIterator.java
index d44d9c3461..7e40584c71 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizardIterator.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizardIterator.java
@@ -19,9 +19,7 @@
package org.netbeans.modules.autoupdate.ui.wizards;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.NoSuchElementException;
+import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openide.WizardDescriptor;
import org.openide.util.NbBundle;
@@ -30,65 +28,87 @@
*
* @author Jiri Rechtacek
*/
-public final class UninstallUnitWizardIterator implements
WizardDescriptor.Iterator<WizardDescriptor> {
+public final class UninstallUnitWizardIterator extends InstallableIteratorBase
implements ChangeListener {
- private int index;
- private List<WizardDescriptor.Panel<WizardDescriptor>> panels = new
ArrayList<WizardDescriptor.Panel<WizardDescriptor>> ();
private UninstallUnitWizardModel uninstallModel;
+ private OperationDescriptionStep descStep;
+ private boolean installAdded;
public UninstallUnitWizardIterator (UninstallUnitWizardModel model) {
+ super(model.createInstallModel(), false, false, false);
uninstallModel = model;
createPanels ();
index = 0;
}
- public UninstallUnitWizardModel getModel () {
- assert uninstallModel != null;
- return uninstallModel;
- }
-
private void createPanels () {
assert panels != null && panels.isEmpty() : "Panels are still empty";
- panels.add (new OperationDescriptionStep (uninstallModel));
+ descStep = new OperationDescriptionStep (uninstallModel);
+ panels.add (descStep);
if (uninstallModel.hasCustomComponents ()) {
panels.add (new CustomHandleStep (uninstallModel));
}
panels.add (new UninstallStep (uninstallModel));
+ // will react on the change to the final presentation
+ descStep.addChangeListener(this);
}
public WizardDescriptor.Panel<WizardDescriptor> current () {
assert panels != null;
return panels.get (index);
}
+
+ @Override
+ public void nextPanel() {
+ if (current() instanceof InstallStep) {
+ index = 0;
+ // must reset the data in descStep
+ descStep.reset();
+ removeInstallPanels();
+ } else {
+ super.nextPanel();
+ }
+ }
public String name () {
return NbBundle.getMessage (UninstallUnitWizard.class,
"UninstallUnitWizard_Title");
}
- public boolean hasNext () {
- return index < panels.size () - 1;
- }
-
public boolean hasPrevious () {
- return index > 0 && ! (current () instanceof UninstallStep);
- }
-
- public void nextPanel () {
- if (!hasNext ()) {
- throw new NoSuchElementException ();
+ // disable Back on 'confirmation' screen
+ if (current() instanceof UninstallStep) {
+ return false;
}
- index++;
- }
-
- public void previousPanel () {
- if (!hasPrevious ()) {
- throw new NoSuchElementException ();
+ // disable Back during installation, no turning back to licenses
+ if (current() instanceof InstallStep) {
+ return false;
}
- index--;
+ return super.hasPrevious();
}
// If nothing unusual changes in the middle of the wizard, simply:
public void addChangeListener (ChangeListener l) {}
public void removeChangeListener (ChangeListener l) {}
+
+ @Override
+ public void stateChanged(ChangeEvent e) {
+ if (e.getSource() != descStep) {
+ return;
+ }
+ // if the model has initialized and says we have components to install,
+ // add the intermediate install panels to the wizard
+ if (uninstallModel.hasComponentsToInstall() && !installAdded) {
+ installAdded = true;
+ insertPanels(1);
+ }
+ }
+
+ protected void compactPanels() {
+ // block compacting on further panels since it may damage the
+ // current() panel after installation is complete
+ if (index == 0) {
+ super.compactPanels();
+ }
+ }
}
diff --git
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizardModel.java
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizardModel.java
index eee47b79df..2328f9fb21 100644
---
a/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizardModel.java
+++
b/platform/autoupdate.ui/src/org/netbeans/modules/autoupdate/ui/wizards/UninstallUnitWizardModel.java
@@ -19,9 +19,11 @@
package org.netbeans.modules.autoupdate.ui.wizards;
+import org.netbeans.api.autoupdate.InstallSupport;
import org.netbeans.api.autoupdate.OperationContainer;
import org.netbeans.api.autoupdate.OperationSupport;
import org.netbeans.modules.autoupdate.ui.Containers;
+import org.openide.WizardDescriptor;
/**
*
@@ -29,9 +31,21 @@
*/
public class UninstallUnitWizardModel extends OperationWizardModel {
private OperationType operationType;
- private OperationContainer container;
+ /**
+ * The enable/disable/uninstall operation
+ */
+ private OperationContainer container;
+ /**
+ * The nested install operation
+ */
+ private OperationContainer installContainer;
private OperationContainer<OperationSupport> customContainer;
+ /**
+ * Supplemental model that serves for implied install process
+ */
+ private InstallUnitWizardModel nestedInstall;
+
/**
@param doAction if is null it means doUninstall, false means doDisable,
true means doEnable
*/
@@ -44,6 +58,7 @@ public UninstallUnitWizardModel (OperationType doOperation) {
break;
case ENABLE :
this.container = Containers.forEnable ();
+ this.installContainer = Containers.forAvailable();
break;
case DISABLE :
this.container = Containers.forDisable ();
@@ -54,6 +69,39 @@ public UninstallUnitWizardModel (OperationType doOperation) {
assert container!=null ;
}
+
+ @Override
+ public void recognizeButtons(WizardDescriptor wd) {
+ super.recognizeButtons(wd);
+ if (nestedInstall != null) {
+ nestedInstall.recognizeButtons(wd);
+ }
+ }
+
+ /**
+ * Creates a delegating Install model. The model should satisfy 'install'
+ * steps and delegate to this model and/or override necessary operations
+ * @return
+ */
+ public InstallUnitWizardModel createInstallModel() {
+ if (nestedInstall == null) {
+ nestedInstall = new InstallUnitWizardModel(OperationType.INSTALL,
getInstallContainer()) {
+ @Override
+ public void modifyOptionsForEndInstall(WizardDescriptor wd) {
+ modifyOptionsForContinue(wd, false);
+ modifyOptionsContinueWithCancel(wd);
+ }
+
+ @Override
+ protected void performRefresh() {
+ UninstallUnitWizardModel.this.performRefresh();
+ }
+
+
+ };
+ }
+ return nestedInstall;
+ }
public OperationType getOperation () {
return operationType;
@@ -63,8 +111,28 @@ public OperationContainer getBaseContainer () {
return container;
}
+ @Override
+ public OperationContainer<InstallSupport> getInstallContainer() {
+ return installContainer;
+ }
+
+ @Override
public OperationContainer<OperationSupport> getCustomHandledContainer () {
return customContainer;
}
+ /**
+ * Resets the containers. Will reset the main container and install
container.
+ * @param c the new container
+ */
+ @Override
+ protected void refresh(OperationContainer c) {
+ if (container != c) {
+ this.container = c;
+ }
+ if (installContainer != null) {
+ installContainer.removeAll();
+ }
+ }
+
}
diff --git a/platform/o.n.bootstrap/src/org/netbeans/ModuleManager.java
b/platform/o.n.bootstrap/src/org/netbeans/ModuleManager.java
index 4db13c7a00..c9c4dd93e8 100644
--- a/platform/o.n.bootstrap/src/org/netbeans/ModuleManager.java
+++ b/platform/o.n.bootstrap/src/org/netbeans/ModuleManager.java
@@ -1668,12 +1668,24 @@ public void disable(Set<Module> modules) throws
IllegalArgumentException {
}
}
+ /**
+ * Determines if enabling compat modules is disruptive. Compat modules are
often
+ * fragment modules augmenting regular ones. If the host module is already
enabled / loaded,
+ * the compat module cannot back-patch already existing classes, and IDE
restart is needed.
+ *
+ * @param modules initial set of modules
+ * @return true, if the operation requires a restart
+ * @throws IllegalArgumentException
+ */
public boolean hasToEnableCompatModules(Set<Module> modules) throws
IllegalArgumentException {
List<Module> toEnable = simulateEnable(modules);
for (Module m : toEnable) {
String fragmentHostCodeName = m.getFragmentHostCodeName();
if (fragmentHostCodeName != null &&
!fragmentHostCodeName.isEmpty()) {
- return true;
+ Module fragHost = get(fragmentHostCodeName);
+ if (fragHost != null && fragHost.isEnabled()) {
+ return true;
+ }
}
}
return false;
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists