From c6ab714ff58b56255646410425eca754643f6bbf Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 22 Oct 2024 17:22:26 +0200 Subject: [PATCH 1/6] Only run one completion sampler --- .../server/protocol/TextDocumentServiceImpl.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java index 866a0825a464..74dbb155a7a9 100644 --- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java @@ -349,6 +349,7 @@ public void indexingComplete(Set indexedRoots) { private static final int INITIAL_COMPLETION_SAMPLING_DELAY = 1000; private static final int DEFAULT_COMPLETION_WARNING_LENGTH = 10_000; private static final RequestProcessor COMPLETION_SAMPLER_WORKER = new RequestProcessor("java-lsp-completion-sampler", 1, false, false); + private static final AtomicReference RUNNING_SAMPLER = new AtomicReference<>(); @Override @Messages({ @@ -366,11 +367,15 @@ public CompletableFuture, CompletionList>> completio if (!done.get()) { Sampler sampler = Sampler.createSampler("completion"); if (sampler != null) { - sampler.start(); - samplerRef.set(sampler); - samplingStart.set(System.currentTimeMillis()); - if (done.get()) { - sampler.stop(); + Sampler witnessSampler = RUNNING_SAMPLER.compareAndExchange(null, sampler); + + if (witnessSampler == null) { + sampler.start(); + samplerRef.set(sampler); + samplingStart.set(System.currentTimeMillis()); + if (done.get()) { + sampler.stop(); + } } } } @@ -494,6 +499,7 @@ public CompletableFuture, CompletionList>> completio done.set(true); Sampler sampler = samplerRef.get(); + RUNNING_SAMPLER.compareAndExchange(sampler, null); if (sampler != null) { long samplingTime = (System.currentTimeMillis() - completionStart); long minSamplingTime = Math.min(1_000, samplingWarningLength.get()); From 1a4bae9d54c22aacd221315aabd8cc614575a750 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Wed, 23 Oct 2024 06:38:54 +0200 Subject: [PATCH 2/6] Logs exception from TypeProvider.computeTypeNames but go on and ask other providers --- .../src/org/netbeans/modules/jumpto/type/GoToTypeAction.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ide/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java b/ide/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java index ac145bdbba1f..bbe39a3e4eae 100644 --- a/ide/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java +++ b/ide/jumpto/src/org/netbeans/modules/jumpto/type/GoToTypeAction.java @@ -613,6 +613,8 @@ private List getTypeNames(final String text, int[] ret final TypeProvider.Result result = TypeProviderAccessor.DEFAULT.createResult(items, message, context); provider.computeTypeNames(context, result); retry[0] = mergeRetryTimeOut(retry[0], TypeProviderAccessor.DEFAULT.getRetry(result)); + } catch (Exception ex) { + LOGGER.log(Level.SEVERE, "Provider ''" + provider.getDisplayName() + "'' yields an exception", ex); } finally { current = null; } From 78699aa2aaa3450c5c0889a7a393105231fd1050 Mon Sep 17 00:00:00 2001 From: Michael Bien Date: Thu, 24 Oct 2024 15:09:37 +0200 Subject: [PATCH 3/6] disable JDK 23 line-doc comments - codebase has many occurrences of '///' which were never meant to appear in javadoc this disables JDK 23+ "line doc comments" for now - javadoc CI step should run on JDK 23 --- .github/workflows/main.yml | 16 +++++++++++++++- nbbuild/javadoctools/template.xml | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a2d9863c1e71..0adda3250366 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -416,11 +416,25 @@ jobs: - name: Build nbms run: ant $OPTS build-nbms - # 13-14 min + # 13-14 min for javadoc; JDK version must be synced with nb-javac + - name: Set up JDK 23 for javadoc + if: env.test_javadoc == 'true' && success() + uses: actions/setup-java@v4 + with: + java-version: 23 + distribution: ${{ env.DEFAULT_JAVA_DISTRIBUTION }} + - name: Build javadoc if: env.test_javadoc == 'true' && success() run: ant $OPTS build-javadoc + - name: Set up JDK ${{ matrix.java }} + if: env.test_javadoc == 'true' && success() + uses: actions/setup-java@v4 + with: + java-version: ${{ matrix.java }} + distribution: ${{ env.DEFAULT_JAVA_DISTRIBUTION }} + # runs only in PRs if requested; ~18 min - name: Build all Tests if: env.test_tests == 'true' && github.event_name == 'pull_request' && success() diff --git a/nbbuild/javadoctools/template.xml b/nbbuild/javadoctools/template.xml index 8283e61e0a27..141954a4fec6 100644 --- a/nbbuild/javadoctools/template.xml +++ b/nbbuild/javadoctools/template.xml @@ -321,6 +321,9 @@ cause it to fail. ${javadoc.footer} + + From 64ce7e9b272ac2ea024b38bd750d27ed938a8574 Mon Sep 17 00:00:00 2001 From: Dusan Petrovic Date: Wed, 16 Oct 2024 14:55:02 +0200 Subject: [PATCH 4/6] Create secret rotation CronJob when running the app in the cluster --- .../oracle/assets/ConfigMapProvider.java | 15 +- .../CreateSecretRotationCronJobCommand.java | 303 ++++++++++++++++++ .../cloud/oracle/assets/KubernetesUtils.java | 16 + .../oracle/assets/RunInClusterAction.java | 15 +- 4 files changed, 330 insertions(+), 19 deletions(-) create mode 100644 enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CreateSecretRotationCronJobCommand.java diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/ConfigMapProvider.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/ConfigMapProvider.java index 99e7d84745c3..52a7190db05c 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/ConfigMapProvider.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/ConfigMapProvider.java @@ -64,8 +64,9 @@ public String getMicronautConfigFiles() { public void createConfigMap() { KubernetesUtils.runWithClient(cluster, client -> { - boolean configMapExist = checkIfConfigMapExist(client); - if (configMapExist) { + ConfigMapList cmList = client.configMaps().inNamespace(cluster.getNamespace()).list(); + ConfigMap configMap = (ConfigMap) KubernetesUtils.findResource(client, cmList, projectName); + if (configMap != null) { updateConfigMap(client); return; } @@ -87,16 +88,6 @@ public ConfigMapVolumeSource getVolumeSource() { .build(); } - private boolean checkIfConfigMapExist(KubernetesClient client) { - ConfigMapList cmList = client.configMaps().inNamespace(cluster.getNamespace()).list(); - for (ConfigMap cm : cmList.getItems()) { - if (projectName.equals(cm.getMetadata().getName())) { - return true; - } - } - return false; - } - private void updateConfigMap(KubernetesClient client) { Map applicationProperties = propertiesGenerator.getApplication(); Map bootstrapProperties = propertiesGenerator.getBootstrap(); diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CreateSecretRotationCronJobCommand.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CreateSecretRotationCronJobCommand.java new file mode 100644 index 000000000000..665d2de59270 --- /dev/null +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/CreateSecretRotationCronJobCommand.java @@ -0,0 +1,303 @@ +/* + * 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.cloud.oracle.assets; + +import io.fabric8.kubernetes.api.model.PodTemplateSpec; +import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; +import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.SecretList; +import io.fabric8.kubernetes.api.model.ServiceAccount; +import io.fabric8.kubernetes.api.model.ServiceAccountBuilder; +import io.fabric8.kubernetes.api.model.ServiceAccountList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import org.netbeans.spi.lsp.CommandProvider; +import org.openide.util.lookup.ServiceProvider; +import io.fabric8.kubernetes.api.model.batch.v1.CronJobList; +import io.fabric8.kubernetes.api.model.batch.v1.CronJob; +import io.fabric8.kubernetes.api.model.batch.v1.CronJobBuilder; +import io.fabric8.kubernetes.api.model.batch.v1.JobBuilder; +import io.fabric8.kubernetes.api.model.rbac.ClusterRole; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBinding; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBindingBuilder; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBindingList; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleBuilder; +import io.fabric8.kubernetes.api.model.rbac.ClusterRoleList; +import io.fabric8.kubernetes.client.KubernetesClient; +import java.util.Calendar; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; +import org.netbeans.modules.cloud.oracle.NotificationUtils; +import org.netbeans.modules.cloud.oracle.compute.ClusterItem; +import org.openide.util.NbBundle; + +/** + * + * @author Dusan Petrovic + */ +@NbBundle.Messages({ + "CronJobCreationError=Error while creating secret rotation CronJob" +}) +@ServiceProvider(service = CommandProvider.class) +public class CreateSecretRotationCronJobCommand implements CommandProvider { + + private static final String COMMAND_CREATE_CRONJOB = "nbls.cloud.assets.cluster.cronjob.create"; //NOI18N + private static final String SECRET_NAME = "docker-bearer-vscode-generated-ocirsecret"; //NOI18N + private static final String CRONJOB_NAME = "secret-rotation-cronjob"; //NOI18N + private static final String CLUSTER_ROLE_BINDING_NAME = "secret-manager-binding"; //NOI18N + private static final String CLUSTER_ROLE_NAME = "secret-manager"; //NOI18N + private static final String SERVICE_ACCOUNT_NAME = "create-secret-svc-account"; //NOI18N + private static final String BASE_IMAGE = "ghcr.io/oracle/oci-cli:latest"; //NOI18N + private static final String CONTAINER_NAME = "create-secret"; //NOI18N + private static final int WAITING_TIMEOUT = 60; + + private static final Set COMMANDS = new HashSet<>(Arrays.asList( + COMMAND_CREATE_CRONJOB + )); + + private ClusterItem cluster; + + @Override + public Set getCommands() { + return Collections.unmodifiableSet(COMMANDS); + } + + @Override + public CompletableFuture runCommand(String command, List arguments) { + return createSecretRotationCronJob(); + } + + public CompletableFuture createSecretRotationCronJob() { + CompletableFuture completableFuture = new CompletableFuture(); + this.cluster = CloudAssets.getDefault().getItem(ClusterItem.class); + KubernetesUtils.runWithClient(cluster, client -> { + try { + ServiceAccount serviceAccount = createServiceAccountIfNotExist(client); + createClusterRoleIfNotExist(client); + createClusterRoleBindingIfNotExist(client); + createCronJobIfNotExist(client, serviceAccount); + completableFuture.complete(null); + } catch(Exception ex) { + completableFuture.completeExceptionally(ex); + NotificationUtils.showErrorMessage(Bundle.CronJobCreationError()); + } + }); + return completableFuture; + } + + private void createCronJobIfNotExist(KubernetesClient client, ServiceAccount serviceAccount) { + CronJobList existingCronJobs = client.batch().v1().cronjobs().inNamespace(cluster.getNamespace()).list(); + CronJob cronJob = (CronJob) KubernetesUtils.findResource(client, existingCronJobs, CRONJOB_NAME); + if (cronJob != null) { + if (!secretExist(client)) { + invokeCronJob(client, cronJob); + } + return; + } + cronJob = new CronJobBuilder() + .withNewMetadata() + .withName(CRONJOB_NAME) + .withNamespace(cluster.getNamespace()) + .endMetadata() + .withNewSpec() + .withSchedule(getCronExpression()) + .withNewJobTemplate() + .withNewSpec() + .withBackoffLimit(0) + .withTemplate(cronJobPodTemplate(serviceAccount)) + .endSpec() + .endJobTemplate() + .endSpec() + .build(); + + client.batch().v1() + .cronjobs() + .inNamespace(cluster.getNamespace()) + .resource(cronJob) + .create(); + + invokeCronJob(client, cronJob); + } + + private boolean secretExist(KubernetesClient client) { + SecretList existingSecrets = client.secrets().inNamespace(cluster.getNamespace()).list(); + Secret secret = (Secret) KubernetesUtils.findResource(client, existingSecrets, SECRET_NAME); + return secret != null; + } + + private void invokeCronJob(KubernetesClient client, CronJob cronJob) { + client.batch().v1() + .jobs() + .inNamespace(cluster.getNamespace()) + .resource(new JobBuilder() + .withNewMetadata() + .withName("cronjob-invocation-" + UUID.randomUUID()) //NOI18N + .endMetadata() + .withSpec(cronJob.getSpec().getJobTemplate().getSpec()) + .build()) + .create(); + + waitForConditionWithTimeout(() -> { + return secretExist(client); + }, WAITING_TIMEOUT).join(); + } + + private CompletableFuture waitForConditionWithTimeout(Supplier condition, long timeout) { + CompletableFuture future = new CompletableFuture<>(); + + ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); + + ScheduledFuture checkTask = executor.scheduleAtFixedRate(() -> { + if (condition.get()) { + future.complete(null); + } + }, 0, 5, TimeUnit.SECONDS); + + executor.schedule(() -> { + if (!future.isDone()) { + future.completeExceptionally(new TimeoutException("Condition was not met within the timeout.")); //NOI18N + } + checkTask.cancel(true); + executor.shutdown(); + }, timeout, TimeUnit.SECONDS); + + return future; + } + + private String getCronExpression() { + Calendar calendar = Calendar.getInstance(); + int currentMinute = calendar.get(Calendar.MINUTE); + return currentMinute + " * * * *"; + } + + private PodTemplateSpec cronJobPodTemplate(ServiceAccount serviceAccount) { + return new PodTemplateSpecBuilder() + .withNewSpec() + .withHostNetwork(Boolean.TRUE) + .addNewContainer() + .withName(CONTAINER_NAME) + .withImage(BASE_IMAGE) + .addNewEnv() + .withName("OCI_CLI_AUTH") //NOI18N + .withValue("instance_principal") //NOI18N + .endEnv() + .withCommand("/bin/bash", "-c", createSecretCommand()) //NOI18N + .endContainer() + .withRestartPolicy("Never") //NOI18N + .withServiceAccountName(serviceAccount.getMetadata().getName()) + .endSpec() + .build(); + } + + private ServiceAccount createServiceAccountIfNotExist(KubernetesClient client) { + ServiceAccountList existingServiceAccounts = client.serviceAccounts().inNamespace(cluster.getNamespace()).list(); + ServiceAccount serviceAccount = (ServiceAccount) KubernetesUtils.findResource(client, existingServiceAccounts, SERVICE_ACCOUNT_NAME); + if (serviceAccount != null) { + return serviceAccount; + } + serviceAccount = new ServiceAccountBuilder() + .withNewMetadata() + .withName(SERVICE_ACCOUNT_NAME) + .endMetadata() + .build(); + + return client.serviceAccounts() + .inNamespace(cluster.getNamespace()) + .resource(serviceAccount) + .create(); + } + + private void createClusterRoleIfNotExist(KubernetesClient client) { + ClusterRoleList existingClusterRole = client.rbac().clusterRoles().list(); + ClusterRole clusterRole = (ClusterRole) KubernetesUtils.findResource(client, existingClusterRole, CLUSTER_ROLE_NAME); + if (clusterRole != null) { + return; + } + clusterRole = new ClusterRoleBuilder() + .withNewMetadata() + .withName(CLUSTER_ROLE_NAME) + .endMetadata() + .addNewRule() + .withApiGroups("") + .withResources("secrets") //NOI18N + .withVerbs("create", "get", "patch", "delete") //NOI18N + .endRule() + .build(); + + client.rbac().clusterRoles() + .resource(clusterRole) + .create(); + } + + private void createClusterRoleBindingIfNotExist(KubernetesClient client) { + ClusterRoleBindingList existingClusterRoleBinding = client.rbac().clusterRoleBindings().list(); + ClusterRoleBinding clusterRoleBinding = (ClusterRoleBinding) KubernetesUtils.findResource(client, existingClusterRoleBinding, CLUSTER_ROLE_BINDING_NAME); + if (clusterRoleBinding != null) { + return; + } + clusterRoleBinding = new ClusterRoleBindingBuilder() + .withNewMetadata() + .withName(CLUSTER_ROLE_BINDING_NAME) + .endMetadata() + .addNewSubject() + .withName(SERVICE_ACCOUNT_NAME) + .withKind("ServiceAccount") //NOI18N + .withNamespace(cluster.getNamespace()) + .endSubject() + .withNewRoleRef() + .withKind("ClusterRole") //NOI18N + .withName(CLUSTER_ROLE_NAME) + .withApiGroup("rbac.authorization.k8s.io") //NOI18N + .endRoleRef() + .build(); + + client.rbac().clusterRoleBindings() + .resource(clusterRoleBinding) + .create(); + } + + private String createSecretCommand() { + String repoEndpoint = cluster.getRegionCode() + ".ocir.io"; //NOI18N + return + "KUBECTL_VERSION=\"v1.27.4\"\n" + //NOI18N + "case \"$(uname -m)\" in\n" + //NOI18N + " x86_64) ARCHITECTURE=\"amd64\" ;;\n" + //NOI18N + " aarch64) ARCHITECTURE=\"arm64\" ;;\n" + //NOI18N + " *) ARCHITECTURE=\"Unknown architecture\" ;;\n" + //NOI18N + "esac\n" + //NOI18N + "KUBECTL_URL=\"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${ARCHITECTURE}/kubectl\"\n" + //NOI18N + "mkdir -p /tmp/bin\n" + //NOI18N + "curl -LO \"${KUBECTL_URL}\"\n" + //NOI18N + "chmod +x ./kubectl\n" + //NOI18N + "mv ./kubectl /tmp/bin/kubectl\n" + //NOI18N + "export PATH=$PATH:/tmp/bin\n" + //NOI18N + "TOKEN=$(oci raw-request --http-method GET --target-uri https://" + repoEndpoint + "/20180419/docker/token | jq -r '.data.token')\n" + //NOI18N + "kubectl create secret --save-config --dry-run=client docker-registry " + SECRET_NAME + " --docker-server=" + repoEndpoint + " --docker-username=BEARER_TOKEN --docker-password=\"$TOKEN\" -o yaml | kubectl apply -f - "; //NOI18N + } + +} diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/KubernetesUtils.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/KubernetesUtils.java index 5c88b56dd95c..37ac304999a1 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/KubernetesUtils.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/KubernetesUtils.java @@ -25,6 +25,11 @@ import com.oracle.bmc.http.client.Method; import com.oracle.bmc.http.client.jersey.JerseyHttpProvider; import com.oracle.bmc.http.signing.RequestSigningFilter; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.KubernetesResource; +import io.fabric8.kubernetes.api.model.KubernetesResourceList; +import io.fabric8.kubernetes.api.model.batch.v1.CronJob; +import io.fabric8.kubernetes.api.model.batch.v1.CronJobList; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; @@ -140,4 +145,15 @@ private static String getBearerToken(ClusterItem cluster) { throw new RuntimeException(ex); } } + + public static KubernetesResource findResource(KubernetesClient client, KubernetesResourceList existingResources, String resourceName) { + if (resourceName == null) return null; + + for (HasMetadata resource : existingResources.getItems()) { + if (resourceName.equals(resource.getMetadata().getName())) { + return resource; + } + } + return null; + } } diff --git a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RunInClusterAction.java b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RunInClusterAction.java index d0d480b6a796..c3762e9600fb 100644 --- a/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RunInClusterAction.java +++ b/enterprise/cloud.oracle/src/org/netbeans/modules/cloud/oracle/assets/RunInClusterAction.java @@ -56,7 +56,8 @@ @NbBundle.Messages({ "RunInCluster=Run in OKE Cluster", - "Deploying=Deploying project \"{0}\" to the cluster \"{1}\"" + "Deploying=Deploying project \"{0}\" to the cluster \"{1}\"", + "CreatingSecretRotationCronJob=Creating secret rotation CronJob" }) public class RunInClusterAction implements ActionListener { @@ -92,14 +93,14 @@ private void runInCluster() { } try { h.start(); + + CreateSecretRotationCronJobCommand srcc = new CreateSecretRotationCronJobCommand(); + h.progress(Bundle.CreatingSecretRotationCronJob()); + srcc.createSecretRotationCronJob().join(); + KubernetesUtils.runWithClient(cluster, client -> { - Deployment existingDeployment = null; DeploymentList dList = client.apps().deployments().inNamespace(cluster.getNamespace()).list(); - for (Deployment deployment : dList.getItems()) { - if (projectName.equals(deployment.getMetadata().getName())) { - existingDeployment = deployment; - } - } + Deployment existingDeployment = (Deployment) KubernetesUtils.findResource(client, dList, projectName); ConfigMapProvider configMapProvider = new ConfigMapProvider(projectName, cluster); configMapProvider.createConfigMap(); From 86df6fa6836e065be9cef5ac6164cea1887ae763 Mon Sep 17 00:00:00 2001 From: Michael Bien Date: Sat, 26 Oct 2024 20:08:18 +0200 Subject: [PATCH 5/6] output window: better warning color for FlatLaf light current default is often hard to read on white background --- .../src/org/netbeans/swing/laf/flatlaf/FlatLightLaf.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLightLaf.properties b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLightLaf.properties index 95606044b90f..0ba3f1707318 100644 --- a/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLightLaf.properties +++ b/platform/o.n.swing.laf.flatlaf/src/org/netbeans/swing/laf/flatlaf/FlatLightLaf.properties @@ -80,3 +80,4 @@ nb.quicksearch.border=1,1,1,1,@background # output nb.output.selectionBackground=#89BCED +nb.output.warning.foreground=#FF9900 From 513ed15e9bab81498c04a1aef067dc5a51089928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Bl=C3=A4sing?= Date: Fri, 1 Nov 2024 23:19:35 +0100 Subject: [PATCH 6/6] Support source archives with multiple modules The OpenJFX SDK and OpenJDK itself provide src.zip files, where the source is not located at the toplevel directory, but an intermediate level with one folder per module is used. Closes: #7888 --- .../libraries/J2SEVolumeCustomizer.form | 2 +- .../libraries/J2SEVolumeCustomizer.java | 25 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/java/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SEVolumeCustomizer.form b/java/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SEVolumeCustomizer.form index 0ef9a6b1c696..7b174977bb5f 100644 --- a/java/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SEVolumeCustomizer.form +++ b/java/java.j2seplatform/src/org/netbeans/modules/java/j2seplatform/libraries/J2SEVolumeCustomizer.form @@ -1,4 +1,4 @@ - +