blob: e0083d38ce65db5d5ef28cf4297d4f25f5286735 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.java.disco;
import eu.hansolo.jdktools.Architecture;
import io.foojay.api.discoclient.pkg.Pkg;
import eu.hansolo.jdktools.PackageType;
import io.foojay.api.discoclient.pkg.Distribution;
import eu.hansolo.jdktools.OperatingSystem;
import eu.hansolo.jdktools.versioning.VersionNumber;
import eu.hansolo.jdktools.ArchiveType;
import eu.hansolo.jdktools.Latest;
import io.foojay.api.discoclient.pkg.MajorVersion;
import eu.hansolo.jdktools.TermOfSupport;
import io.foojay.api.discoclient.util.Helper;
import static org.netbeans.modules.java.disco.OS.getOperatingSystem;
import static org.netbeans.modules.java.disco.SwingWorker2.submit;
import java.awt.CardLayout;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeEvent;
import org.checkerframework.checker.guieffect.qual.UIEffect;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
@NbBundle.Messages({
"SelectPackage.quick=Quick",
"SelectPackage.advanced=Advanced",
"SelectPackage.componentName=Connect to OpenJDK Discovery Service",
"SelectPackage.loadingError=Could not load list. Please check network access or try again later."
})
@SuppressWarnings("initialization")
public class SelectPackagePanel extends FirstPanel {
private static final Logger log = Logger.getLogger(SelectPackagePanel.class.getName());
public static final String PROP_VALIDITY_CHANGED = "panelValidityChanged";
private final Client discoClient;
@UIEffect
public static SelectPackagePanel create() {
SelectPackagePanel f = new SelectPackagePanel();
f.init();
return f;
}
private final QuickPanel quickPanel;
private final FooAdvancedPanel advancedPanel;
@SuppressWarnings("initialization")
@UIEffect
private SelectPackagePanel() {
// Setup disco client
discoClient = Client.getInstance();
quickPanel = new QuickPanel(this);
advancedPanel = new FooAdvancedPanel();
//please wait message
((CardLayout) getLayout()).first(this);
tabs.add(Bundle.SelectPackage_quick(), quickPanel);
tabs.add(Bundle.SelectPackage_advanced(), advancedPanel);
tabs.addChangeListener((ChangeEvent e) -> {
SelectPackagePanel.this.fireValidityChange();
if (tabs.getSelectedComponent() == quickPanel) {
quickPanel.switchFocus(advancedPanel.getSelectedDistribution(),
advancedPanel.getSelectedVersion());
} else {
advancedPanel.switchFocus(quickPanel.getSelectedDistribution(),
quickPanel.getSelectedVersion());
}
});
}
@UIEffect
private void init() {
setName(Bundle.SelectPackage_componentName());
}
private boolean initialLoad = false; //track the async load in addNotify
@Override
@UIEffect
public void addNotify() {
super.addNotify();
class Result {
final List<Integer> versionNumbers;
final Map<Integer, TermOfSupport> versionNumberSupport;
final List<Distribution> distributions;
final int current;
public Result(List<Integer> versionNumbers, Map<Integer, TermOfSupport> versionNumberSupport, List<Distribution> distributions, int current) {
this.versionNumbers = versionNumbers;
this.versionNumberSupport = versionNumberSupport;
this.distributions = distributions;
this.current = current;
}
}
if (initialLoad)
return;
initialLoad = true;
//loading stuff when ui shown
submit(() -> {
int minVersion = 6;
int maxVersion = discoClient.getLatestSts(true).getAsInt();
int current = discoClient.getLatestSts(false).getAsInt();
// limit to LTS + current
Map<Integer, TermOfSupport> maintainedVersions = discoClient.getAllMaintainedMajorVersions().stream()
.filter(v -> v.getAsInt() >= minVersion && v.getAsInt() <= current) // defensive filter, the API returned an EA JDK as released
.filter(v -> v.getAsInt() == current || v.getTermOfSupport() == TermOfSupport.LTS)
.collect(Collectors.toMap(MajorVersion::getAsInt, MajorVersion::getTermOfSupport));
List<Integer> versionNumbers = IntStream.range(minVersion, maxVersion+1).boxed().collect(Collectors.toList());
List<Distribution> distros = discoClient.getDistributions();
return new Result(versionNumbers, maintainedVersions, distros, current);
}).then((c) -> {
//hide 'please wait' message, show tabs
((CardLayout) getLayout()).next(SelectPackagePanel.this);
Distribution defaultDist = discoClient.getDistribution(DiscoPlatformInstall.defaultDistribution()).orElse(null);
advancedPanel.updateDistributions(c.distributions);
advancedPanel.setVersions(c.versionNumbers, c.versionNumberSupport, c.current);
quickPanel.updateDistributions(c.distributions, defaultDist);
quickPanel.setVersions(c.versionNumbers, c.versionNumberSupport, c.current);
quickPanel.initFocus();
fireValidityChange();
}).handle(ex -> {
loadingLabel.setText(Bundle.SelectPackage_loadingError());
initialLoad = false;
long currentTimeMillisStart = System.currentTimeMillis();
//check connectivity
submit(() -> {
String body = Helper.get("https://www.example.com").body();
return body != null && !"".equals(body);
}).then(isOnline -> {
long now = System.currentTimeMillis();
//if we are online, but still got an error, let's show it to the user if our ping didn't take forever
if (isOnline && (now - currentTimeMillisStart <= 300)) {
Exceptions.printStackTrace(ex);
} else {
log.log(Level.INFO, "Could not load initial list", ex);
}
}).handle(ex2 -> {
//the ping itself got an error, log everything
log.log(Level.INFO, "Could not load initial list", ex);
log.log(Level.INFO, "Could not check network connectivity", ex2);
})
.execute();
}).execute();
}
void fireValidityChange() {
firePropertyChange(PROP_VALIDITY_CHANGED, null, null);
}
class FooAdvancedPanel extends AdvancedPanel {
FooAdvancedPanel() {
ListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.addListSelectionListener(e -> {
SelectPackagePanel.this.fireValidityChange();
});
}
@UIEffect
@Override
protected void updateData(Distribution distribution, Integer featureVersion, Architecture architecture, Latest latest, PackageType bundleType, boolean ea) {
if (distribution == null || featureVersion == null) {
return;
}
OperatingSystem operatingSystem = getOperatingSystem();
ArchiveType extension = ArchiveType.NONE;
Boolean fx = false;
this.setEnabled(false);
submit(() -> {
List<Pkg> bundles = discoClient.getPkgs(distribution, new VersionNumber(featureVersion), latest, operatingSystem, architecture, extension, bundleType, ea, fx);
return bundles;
}).then(this::setPackages)
//TODO: Show something to user, offer reload, auto-reload in N seconds?
.handle(Exceptions::printStackTrace)
.execute();
}
@Override
protected void updateDistributions(List<Distribution> distros) {
super.updateDistributions(distros);
}
@UIEffect
private void setPackages(List<Pkg> bundles) {
SelectPackagePanel.this.setEnabled(true);
tableModel.setBundles(bundles);
}
}
@UIEffect
public @Nullable PkgSelection getSelectedPackage() {
if (!tabs.isVisible()) {
return null;
}
Pkg pkg = null;
switch (tabs.getSelectedIndex()) {
case 0:
pkg = quickPanel.getSelectedPackage();
break;
case 1:
pkg = advancedPanel.getSelectedPackage();
break;
default:
throw new IllegalStateException();
}
return pkg == null ? null : PkgSelection.of(pkg);
}
}