Remove CF client and boot dash plugins and related tests

This commit is contained in:
aboyko
2023-03-07 18:46:46 -05:00
parent 9fe72953ca
commit 9185656e02
273 changed files with 27 additions and 33927 deletions

View File

@@ -1,2 +1,2 @@
mvn clean package -Pe426 -Psnapshot -Dsigning.skip=true -Dhttpclient.retry-max=20 -Dmaven.test.skip=true -Declipse.p2.mirrors=false -Dtycho.localArtifacts=ignore -Dorg.eclipse.equinox.p2.transport.ecf.retry=5 -Dskip.osx.signing=true -Dskip.win.signing=true -Dskip.osx.notarizing=true
mvn clean package -Pe427 -Psnapshot -Dsigning.skip=true -Dhttpclient.retry-max=20 -Dmaven.test.skip=true -Declipse.p2.mirrors=false -Dtycho.localArtifacts=ignore -Dorg.eclipse.equinox.p2.transport.ecf.retry=5 -Dskip.osx.signing=true -Dskip.win.signing=true -Dskip.osx.notarizing=true

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.springframework.boot.ide.product.e418</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -55,14 +55,12 @@
<module>../eclipse-extensions/org.springframework.ide.eclipse.boot.validation</module>
<module>../eclipse-extensions/org.springframework.ide.eclipse.boot.wizard</module>
<module>../eclipse-extensions/org.springframework.ide.eclipse.boot.dash.azure</module>
<module>../eclipse-extensions/org.springframework.ide.eclipse.boot.dash.cf</module>
<module>../eclipse-extensions/org.springframework.ide.eclipse.boot.dash.docker</module>
<module>../eclipse-extensions/org.springframework.ide.eclipse.boot.dash.feature</module>
<module>../eclipse-extensions/org.springframework.ide.eclipse.docker.client</module>
<module>../eclipse-extensions/org.springframework.ide.eclipse.maven.pom</module>
<module>../eclipse-extensions/org.springframework.ide.eclipse.xml.namespaces</module>
<module>../eclipse-extensions/org.springframework.ide.eclipse.xml.namespaces.feature</module>
<module>../eclipse-extensions/org.springsource.ide.eclipse.commons.cloudfoundry.client.v2</module>
</modules>
<properties>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src/"/>
<classpathentry exported="true" kind="lib" path="lib/spring-boot-loader-tools-1.2.3.RELEASE.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.springframework.ide.eclipse.boot.dash.cf</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -1,8 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8

View File

@@ -1,59 +0,0 @@
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=true
sp_cleanup.add_missing_deprecated_annotations=true
sp_cleanup.add_missing_methods=false
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
sp_cleanup.add_missing_override_annotations_interface_methods=true
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_functional_interfaces=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.correct_indentation=false
sp_cleanup.format_source_code=false
sp_cleanup.format_source_code_changes_only=false
sp_cleanup.insert_inferred_type_arguments=false
sp_cleanup.make_local_variable_final=true
sp_cleanup.make_parameters_final=false
sp_cleanup.make_private_fields_final=true
sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=false
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=true
sp_cleanup.organize_imports=false
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
sp_cleanup.remove_private_constructors=true
sp_cleanup.remove_redundant_type_arguments=false
sp_cleanup.remove_trailing_whitespaces=true
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_casts=false
sp_cleanup.remove_unnecessary_nls_tags=false
sp_cleanup.remove_unused_imports=false
sp_cleanup.remove_unused_local_variables=false
sp_cleanup.remove_unused_private_fields=true
sp_cleanup.remove_unused_private_members=false
sp_cleanup.remove_unused_private_methods=true
sp_cleanup.remove_unused_private_types=true
sp_cleanup.sort_members=false
sp_cleanup.sort_members_all=false
sp_cleanup.use_anonymous_class_creation=false
sp_cleanup.use_blocks=false
sp_cleanup.use_blocks_only_for_return_and_throw=false
sp_cleanup.use_lambda=true
sp_cleanup.use_parentheses_in_expressions=false
sp_cleanup.use_this_for_non_static_field_access=false
sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
sp_cleanup.use_this_for_non_static_method_access=false
sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true

View File

@@ -1,2 +0,0 @@
boot.validation.initialized=true
eclipse.preferences.version=1

View File

@@ -1,79 +0,0 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.springframework.ide.eclipse.boot.dash.cf;singleton:=true
Bundle-Version: 4.18.0.qualifier
Bundle-Vendor: VMware, Inc
Bundle-Name: Spring Boot Dash CF Support
Require-Bundle: org.eclipse.ui,
org.eclipse.ui.ide,
org.springframework.ide.eclipse.boot.launch,
org.springframework.ide.eclipse.boot,
org.springframework.ide.eclipse.boot.dash,
org.springsource.ide.eclipse.commons.livexp,
com.google.guava;bundle-version="15.0.0",
org.springframework.ide.eclipse.boot.wizard,
io.projectreactor.reactor-core,
org.reactivestreams.reactive-streams,
org.eclipse.core.resources,
org.eclipse.debug.core,
org.eclipse.jdt.core,
org.springsource.ide.eclipse.commons.ui,
org.eclipse.ui.views.properties.tabbed,
org.eclipse.jdt.launching,
org.eclipse.debug.ui,
org.eclipse.jdt.debug.ui,
org.springsource.ide.eclipse.commons.core,
org.eclipse.ui.console,
javax.ws.rs;bundle-version="2.0.1",
org.springframework.ide.eclipse.beans.ui.live,
org.eclipse.compare,
org.eclipse.core.filebuffers,
org.eclipse.text,
org.springsource.ide.eclipse.commons.frameworks.core,
org.apache.httpcomponents.httpclient,
org.apache.httpcomponents.httpcore,
org.yaml.snakeyaml,
org.apache.commons.lang3,
org.springframework.ide.eclipse.editor.support,
org.eclipse.jface.text,
org.springsource.ide.eclipse.commons.cloudfoundry.client.v2,
org.eclipse.core.net,
org.eclipse.equinox.security,
org.eclipse.jdt.ui,
org.eclipse.ui.genericeditor,
org.eclipse.ui.editors,
com.hierynomus.sshj;bundle-version="0.24.0",
com.jcraft.jzlib;bundle-version="1.1.3",
org.bouncycastle.bcpkix;bundle-version="1.59.0",
org.bouncycastle.bcprov;bundle-version="1.59.0",
org.springsource.ide.eclipse.commons.boot.ls,
org.springframework.tooling.cloudfoundry.manifest.ls,
org.springframework.tooling.ls.eclipse.commons
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-Activator: org.springframework.ide.eclipse.boot.dash.cf.BootDashCfPlugin
Bundle-ActivationPolicy: lazy
Export-Package: org.springframework.ide.eclipse.boot.dash.cf,
org.springframework.ide.eclipse.boot.dash.cf.actions,
org.springframework.ide.eclipse.boot.dash.cf.client,
org.springframework.ide.eclipse.boot.dash.cf.client.v2,
org.springframework.ide.eclipse.boot.dash.cf.debug,
org.springframework.ide.eclipse.boot.dash.cf.deployment,
org.springframework.ide.eclipse.boot.dash.cf.dialogs,
org.springframework.ide.eclipse.boot.dash.cf.jmxtunnel,
org.springframework.ide.eclipse.boot.dash.cf.model,
org.springframework.ide.eclipse.boot.dash.cf.packaging,
org.springframework.ide.eclipse.boot.dash.cf.routes,
org.springframework.ide.eclipse.boot.dash.cf.runtarget,
org.springframework.ide.eclipse.boot.dash.cf.ui
Bundle-ClassPath: .,
lib/spring-boot-loader-tools-1.2.3.RELEASE.jar
Automatic-Module-Name: org.springframework.ide.eclipse.boot.dash.cf
Import-Package: org.eclipse.core.runtime,
org.eclipse.core.runtime.jobs,
org.eclipse.core.runtime.preferences,
org.eclipse.osgi.util,
org.osgi.framework,
com.google.gson,
com.google.gson.annotations,
com.google.gson.reflect,
com.google.gson.stream

View File

@@ -1,7 +0,0 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.xml,\
icons/,\
lib/spring-boot-loader-tools-1.2.3.RELEASE.jar

Binary file not shown.

Before

Width:  |  Height:  |  Size: 697 B

View File

@@ -1,89 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.springframework.ide.eclipse.boot.dash.injections">
<injection class="org.springframework.ide.eclipse.boot.dash.cf.BootDashInjections"/>
</extension>
<extension
point="org.eclipse.compare.contentMergeViewers">
<contentTypeBinding
contentMergeViewerId="org.springframework.ide.eclipse.boot.dash.cf.yamlMergeViewer"
contentTypeId="org.springframework.ide.eclipse.manifest.yml">
</contentTypeBinding>
<viewer
class="org.springframework.ide.eclipse.boot.dash.cloudfoundry.deployment.YamlMergeViewerCreator"
extensions="yml"
id="org.springframework.ide.eclipse.boot.dash.cf.yamlMergeViewer"
label="YAML Text Merge">
</viewer>
</extension>
<extension point="org.eclipse.debug.core.launchConfigurationTypes">
<launchConfigurationType
id="org.springframework.ide.eclipse.boot.dash.ssh.tunnel.launch"
name="Boot Dash SSH Debug Tunnel"
delegate="org.springframework.ide.eclipse.boot.dash.cf.debug.SshDebugLaunchConfigurationDelegate"
modes="debug"
sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer">
</launchConfigurationType>
</extension>
<extension
point="org.eclipse.ui.views.properties.tabbed.propertySections">
<propertySections
contributorId="org.springframework.ide.eclipse.boot.dash.propertyContributor">
<propertySection
class="org.springframework.ide.eclipse.boot.dash.cf.views.properties.CloudFoundryAppPropertiesSection"
id="org.springframework.ide.eclipse.boot.dash.section.CloudFoundryGeneral"
tab="org.springframework.ide.eclipse.boot.dash.tab.General">
<input
type="org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement">
</input>
</propertySection>
<propertySection
class="org.springframework.ide.eclipse.boot.dash.cf.views.properties.CloudServiceGeneralPropertiesSection"
id="org.springframework.ide.eclipse.boot.dash.section.CloudServiceSection"
tab="org.springframework.ide.eclipse.boot.dash.tab.General">
<input
type="org.springframework.ide.eclipse.boot.dash.cf.model.CloudServiceInstanceDashElement">
</input>
</propertySection>
</propertySections>
</extension>
<extension
point="org.eclipse.debug.ui.launchConfigurationTypeImages">
<launchConfigurationTypeImage
icon="platform:/plugin/org.springframework.ide.eclipse.boot/resources/icons/boot-devtools-icon.png"
configTypeID="org.springframework.ide.eclipse.boot.dash.ssh.tunnel.launch"
id="org.springframework.ide.eclipse.boot.dash.ssh.tunnel.launch.image">
</launchConfigurationTypeImage>
</extension>
<extension point="org.eclipse.debug.ui.launchConfigurationTabGroups">
<launchConfigurationTabGroup
type="org.springframework.ide.eclipse.boot.dash.ssh.tunnel.launch"
class="org.springframework.ide.eclipse.boot.dash.cf.debug.SshDebugLaunchConfigurationTabGroup"
id="org.springframework.ide.eclipse.boot.dash.cf.debug.SshDebugLaunchConfigurationTabGroup">
</launchConfigurationTabGroup>
</extension>
<extension
point="org.eclipse.ui.genericeditor.reconcilers">
<reconciler
class="org.springframework.ide.eclipse.boot.dash.cf.deployment.ApplicationNameReconciler"
contentType="org.springframework.ide.eclipse.manifest.yml">
<enabledWhen>
<with
variable="editor">
<test
property="org.springframework.ide.eclipse.boot.EmbeddedEditor.context"
value="deployment-properties-dialog">
</test>
</with>
</enabledWhen>
</reconciler>
</extension>
</plugin>

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot.ide</groupId>
<artifactId>org.springframework.boot.ide</artifactId>
<version>4.18.0-SNAPSHOT</version>
<relativePath>../../eclipse-distribution/pom.xml</relativePath>
</parent>
<groupId>org.springframework.ide.eclipse</groupId>
<artifactId>org.springframework.ide.eclipse.boot.dash.cf</artifactId>
<packaging>eclipse-plugin</packaging>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-source-plugin</artifactId>
<version>${tycho-version}</version>
<executions>
<execution>
<id>plugin-source</id>
<phase>generate-sources</phase>
<goals>
<goal>plugin-source</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-p2-plugin</artifactId>
<version>${tycho-version}</version>
<executions>
<execution>
<id>second-generate-p2-metadata</id>
<goals>
<goal>p2-metadata</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,34 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Phil Webb - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf;
import java.util.concurrent.CompletableFuture;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
public class BootDashCfPlugin extends AbstractUIPlugin {
private static final String PLUGIN_ID = "org.springframework.ide.eclipse.boot.dash.cf";
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
CompletableFuture.runAsync(() -> BootDashTargetInfoSynchronizer.start());
}
@Override
public void stop(BundleContext context) throws Exception {
BootDashTargetInfoSynchronizer.stop();
super.stop(context);
}
}

View File

@@ -1,62 +0,0 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf;
import org.springframework.ide.eclipse.boot.dash.api.RunTargetType;
import org.springframework.ide.eclipse.boot.dash.cf.actions.CfBootDashActions;
import org.springframework.ide.eclipse.boot.dash.cf.client.CloudFoundryClientFactory;
import org.springframework.ide.eclipse.boot.dash.cf.client.v2.DefaultCloudFoundryClientFactoryV2;
import org.springframework.ide.eclipse.boot.dash.cf.debug.DebugSupport;
import org.springframework.ide.eclipse.boot.dash.cf.debug.SshDebugSupport;
import org.springframework.ide.eclipse.boot.dash.cf.debug.SshTunnelFactory;
import org.springframework.ide.eclipse.boot.dash.cf.debug.SshTunnelImpl;
import org.springframework.ide.eclipse.boot.dash.cf.jmxtunnel.CloudFoundryRemoteBootAppsDataContributor;
import org.springframework.ide.eclipse.boot.dash.cf.jmxtunnel.JmxSshTunnelManager;
import org.springframework.ide.eclipse.boot.dash.cf.labels.BootDashCfLabels;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTargetType;
import org.springframework.ide.eclipse.boot.dash.cf.ui.CfUserInteractions;
import org.springframework.ide.eclipse.boot.dash.cf.ui.DefaultCfUserInteractions;
import org.springframework.ide.eclipse.boot.dash.di.EclipseBeanLoader.Contribution;
import org.springframework.ide.eclipse.boot.dash.di.SimpleDIContext;
import org.springframework.ide.eclipse.boot.dash.labels.BootDashLabels;
import org.springframework.ide.eclipse.boot.dash.model.DefaultBootDashModelContext;
import org.springframework.ide.eclipse.boot.dash.views.BootDashActions;
import org.springsource.ide.eclipse.commons.boot.ls.remoteapps.RemoteBootAppsDataHolder;
/**
* Contributes bean definitions to {@link DefaultBootDashModelContext}
*/
public class BootDashInjections implements Contribution {
@Override
public void applyBeanDefinitions(SimpleDIContext context) throws Exception {
//TargetType
context.def(RunTargetType.class, CloudFoundryRunTargetType::new);
//Auto sync of cf deployed apps with JMX over SSH tunnel with boot ls (for live hover/data features).
context.def(RemoteBootAppsDataHolder.Contributor.class, CloudFoundryRemoteBootAppsDataContributor::new);
//UI actions
context.defInstance(BootDashActions.Factory.class, CfBootDashActions.factory);
//BootDashLabels
context.defInstance(BootDashLabels.Contribution.class, BootDashCfLabels.jmxDecoration);
//cf internal
context.def(CfUserInteractions.class, DefaultCfUserInteractions::new);
context.defInstance(DebugSupport.class, SshDebugSupport.INSTANCE);
context.defInstance(SshTunnelFactory.class, SshTunnelImpl::new);
context.defInstance(JmxSshTunnelManager.class, new JmxSshTunnelManager());
context.defInstance(CloudFoundryClientFactory.class, DefaultCloudFoundryClientFactoryV2.INSTANCE);
}
}

View File

@@ -1,160 +0,0 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.client.ClientRequests;
import org.springframework.ide.eclipse.boot.dash.cf.client.v2.DefaultClientRequestsV2;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CfTargetsInfo;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CfTargetsInfo.Target;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CfTargetsInfo.TargetDiagnosticMessages;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryTargetProperties;
import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel;
import org.springframework.ide.eclipse.boot.dash.model.RunTarget;
import org.springframework.tooling.cloudfoundry.manifest.ls.CloudFoundryManifestLanguageServer;
import org.springsource.ide.eclipse.commons.livexp.core.ValueListener;
import com.google.common.collect.ImmutableSet;
/**
* Helper methods to attach listener to the BootDash model and keep editor in sync with
* all connected cloudfoundry targets.
* <p>
* Usage: just call the 'start()' method once, at the first time the information is desired.
*/
@SuppressWarnings("rawtypes")
public class BootDashTargetInfoSynchronizer {
private static AtomicBoolean started = new AtomicBoolean(false);
public static void start() {
if (started.compareAndSet(false, true)) {
model().getRunTargets().addListener(targetsListener);
}
}
public static void stop() {
if (started.compareAndSet(true, false)) {
updateCloudTargetsInManifestEditor(ImmutableSet.of());
}
}
private static final ValueListener<ImmutableSet<RunTarget>> targetsListener = (e, v) -> {
// On target changes, add the client listener in each target so that when the client changes, a notification is sent
addClientChangeListeners(e.getValue());
};
private static final ValueListener<ClientRequests> clientsChangedListener = (exp, client) -> {
if (client instanceof DefaultClientRequestsV2) {
addRefreshTokenListener((DefaultClientRequestsV2) client);
}
};
/**
* Add a listener to be notified when the refresh token becomes available OR
* changes
*/
static private void addRefreshTokenListener(DefaultClientRequestsV2 client) {
if (client != null && client.getRefreshTokens() != null && model() != null
&& model().getRunTargets() != null) {
client.getRefreshTokens().doOnNext((token) ->
// Although the refresh token change is for ONE client (i.e. one target)
// compute cloud target information for ALL currently connected targets
// as the manifest editor is updated with the full up-to-date list of connected
// boot dash targets
updateCloudTargetsInManifestEditor(model().getRunTargets().getValue())
).subscribe();
client.getRefreshTokens().doOnComplete(() ->
updateCloudTargetsInManifestEditor(model().getRunTargets().getValue())
).subscribe();
}
}
static private void updateCloudTargetsInManifestEditor(ImmutableSet<RunTarget> value) {
Set<RunTarget> toUpdate = value == null ? ImmutableSet.of() : value;
CfTargetsInfo targetsInfo = asTargetsInfo(toUpdate);
CloudFoundryManifestLanguageServer.setCfTargetLoginOptions(targetsInfo);
}
private static BootDashViewModel model() {
return BootDashActivator.getDefault().getModel();
}
private static void addClientChangeListeners(ImmutableSet<RunTarget> targets) {
if (targets != null) {
for (RunTarget runTarget : targets) {
if (runTarget instanceof CloudFoundryRunTarget) {
((CloudFoundryRunTarget) runTarget).getClientExp().addListener(clientsChangedListener);
}
}
}
}
private static CfTargetsInfo asTargetsInfo(Collection<RunTarget> targets) {
List<CfTargetsInfo.Target> collectedTargets = new ArrayList<>();
for (RunTarget runTarget : targets) {
if (runTarget instanceof CloudFoundryRunTarget) {
CloudFoundryRunTarget cloudFoundryRunTarget = (CloudFoundryRunTarget) runTarget;
if (cloudFoundryRunTarget.isConnected()) {
String token = cloudFoundryRunTarget.getClient().getRefreshToken();
if (token != null) {
CloudFoundryTargetProperties properties = cloudFoundryRunTarget.getTargetProperties();
String target = properties.getUrl();
String org = properties.getOrganizationName();
String space = properties.getSpaceName();
boolean sslDisabled = properties.skipSslValidation();
CfTargetsInfo.Target integrationTarget = new Target();
integrationTarget.setApi(target);
integrationTarget.setOrg(org);
integrationTarget.setSpace(space);
integrationTarget.setSslDisabled(sslDisabled);
integrationTarget.setRefreshToken(token);
collectedTargets.add(integrationTarget);
}
}
}
}
CfTargetsInfo targetsInfo = new CfTargetsInfo();
targetsInfo.setCfTargets(collectedTargets);
targetsInfo.setDiagnosticMessages(getDiagnosticMessages());
return targetsInfo ;
}
// NOTE: using ':' to separate the "shorter" part of the message from the longer. The longer part may be shown in the UI by expanding the hover info
private static final String TARGET_SOURCE = "Boot Dashboard";
private static final String NO_ORG_SPACE = "Boot Dashboard - No org/space selected: Verify Cloud Foundry target connection in Boot Dashboard or login via 'cf' CLI";
// Make this a "generic" message, instead of using "Boot Dash" prefix as it shows general instructions when there are not targets
private static final String NO_TARGETS = "No Cloud Foundry targets found: Create a target in Boot Dashboard or login via 'cf' CLI";
private static final String CONNECTION_ERROR = "Boot Dashboard - Error connecting to Cloud Foundry target: Verify network connection or that existing target has valid credentials.";
private static TargetDiagnosticMessages getDiagnosticMessages() {
TargetDiagnosticMessages messages = new TargetDiagnosticMessages();
messages.setConnectionError(CONNECTION_ERROR);
messages.setNoOrgSpace(NO_ORG_SPACE);
messages.setNoTargetsFound(NO_TARGETS);
messages.setTargetSource(TARGET_SOURCE);
return messages;
}
}

View File

@@ -1,45 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.model.BootDashElement;
import org.springframework.ide.eclipse.boot.dash.views.AbstractBootDashElementsAction;
import org.springframework.ide.eclipse.boot.dash.views.AbstractBootDashElementsAction.Params;
/**
* Action for {@link CloudAppDashElement} elements only
*
* @author Alex Boyko
*
*/
public class AbstractCloudAppDashElementsAction extends AbstractBootDashElementsAction {
public AbstractCloudAppDashElementsAction(Params params) {
super(params);
}
@Override
public void updateVisibility() {
boolean visible = false;
if (!getSelectedElements().isEmpty()) {
visible = true;
for (BootDashElement e : getSelectedElements()) {
if (!(e instanceof CloudAppDashElement)) {
visible = false;
break;
}
}
}
setVisible(visible);
}
}

View File

@@ -1,41 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springframework.ide.eclipse.boot.dash.cf.ui.CfUserInteractions;
import org.springframework.ide.eclipse.boot.dash.di.SimpleDIContext;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springframework.ide.eclipse.boot.dash.views.AbstractBootDashModelAction;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
/**
* Action for Cloud Foundry dash model element
*
* @author Alex Boyko
*
*/
public class AbstractCloudDashModelAction extends AbstractBootDashModelAction {
protected AbstractCloudDashModelAction(LiveExpression<BootDashModel> section, SimpleDIContext ui) {
super(section, ui);
}
protected CfUserInteractions cfUi() {
return context.getBean(CfUserInteractions.class);
}
@Override
public void updateVisibility() {
this.setVisible(sectionSelection.getValue() instanceof CloudFoundryBootDashModel);
}
}

View File

@@ -1,57 +0,0 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import org.springframework.ide.eclipse.boot.dash.di.SimpleDIContext;
import org.springframework.ide.eclipse.boot.dash.liveprocess.LiveProcessCommandsExecutor;
import org.springframework.ide.eclipse.boot.dash.livexp.MultiSelection;
import org.springframework.ide.eclipse.boot.dash.model.BootDashElement;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel;
import org.springframework.ide.eclipse.boot.dash.views.AbstractBootDashAction;
import org.springframework.ide.eclipse.boot.dash.views.BootDashActions;
import org.springframework.ide.eclipse.boot.dash.views.ToggleBootDashModelConnection;
import org.springframework.ide.eclipse.boot.dash.views.AbstractBootDashElementsAction.Params;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import com.google.common.collect.ImmutableList;
public class CfBootDashActions {
public static BootDashActions.Factory factory = (
BootDashActions actions,
BootDashViewModel model,
MultiSelection<BootDashElement> selection,
LiveExpression<BootDashModel> section,
SimpleDIContext context,
LiveProcessCommandsExecutor liveProcessCmds
) -> {
Params defaultActionParams = new Params(actions)
.setModel(model)
.setSelection(selection)
.setContext(context)
.setLiveProcessCmds(liveProcessCmds);
ImmutableList.Builder<AbstractBootDashAction> builder = ImmutableList.builder();
builder.add(new RestartApplicationOnlyAction(defaultActionParams));
builder.add(new SelectManifestAction(defaultActionParams));
builder.add(new CloudFoundryRestartWithRemoteDevClientAction(defaultActionParams));
builder.add(new EnableJmxSshTunnelAction(defaultActionParams));
builder.add(new ReconnectCloudConsoleAction(defaultActionParams));
if (section!=null) {
builder.add(new UpdatePasswordAction(section, context));
builder.add(new OpenCloudAdminConsoleAction(section, context));
builder.add(new CustmomizeTargetAppManagerURLAction(section, context));
}
return builder.build();
};
}

View File

@@ -1,91 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2017 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import java.net.URL;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.PlatformUI;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springframework.ide.eclipse.boot.dash.model.BootDashElement;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel.ElementStateListener;
/**
* Action for starting/restarting Remove DevTools Client application
*
* @author Alex Boyko
*
*/
public class CloudFoundryRestartWithRemoteDevClientAction extends AbstractCloudAppDashElementsAction {
private ElementStateListener stateListener;
public CloudFoundryRestartWithRemoteDevClientAction(Params params) {
super(params);
this.setText("(Re)start Remote DevTools Client");
this.setToolTipText("Restarts application with the Remote DevTools Client attched.");
URL url = FileLocator.find(Platform.getBundle("org.springframework.ide.eclipse.boot"), new Path("resources/icons/boot-devtools-icon.png"), null);
if (url != null) {
this.setImageDescriptor(ImageDescriptor.createFromURL(url));
}
if (model != null) {
model.addElementStateListener(stateListener = new ElementStateListener() {
public void stateChanged(BootDashElement e) {
if (getSelectedElements().contains(e) && !PlatformUI.getWorkbench().isClosing()) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
updateEnablement();
}
});
}
}
});
}
}
@Override
public void updateEnablement() {
boolean enable = false;
if (!getSelectedElements().isEmpty()) {
enable = true;
for (BootDashElement e : getSelectedElements()) {
if (!(e instanceof CloudAppDashElement) || e.getProject() == null) {
enable = false;
}
}
}
this.setEnabled(enable);
}
@Override
public void run() {
for (BootDashElement _e : getSelectedElements()) {
if (_e instanceof CloudAppDashElement && _e.getBootDashModel() instanceof CloudFoundryBootDashModel && _e.getProject() != null) {
CloudAppDashElement e = (CloudAppDashElement) _e;
e.restartWithRemoteClient(ui(), e.createCancelationToken());
}
}
}
@Override
public void dispose() {
if (model != null && stateListener != null) {
model.removeElementStateListener(stateListener);
stateListener = null;
}
super.dispose();
}
}

View File

@@ -1,61 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import java.util.EnumSet;
import org.springframework.ide.eclipse.boot.dash.cf.dialogs.CustomizeAppsManagerURLDialogModel;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springframework.ide.eclipse.boot.dash.di.SimpleDIContext;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springsource.ide.eclipse.commons.core.pstore.PropertyStoreApi;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
public class CustmomizeTargetAppManagerURLAction extends AbstractCloudDashModelAction{
protected CustmomizeTargetAppManagerURLAction(LiveExpression<BootDashModel> section, SimpleDIContext context) {
super(section, context);
setText("Customize Cloud Admin Console URL...");
}
@Override
public void updateEnablement() {
this.setEnabled(isApplicable(sectionSelection.getValue()));
}
@Override
public void updateVisibility() {
this.setVisible(isApplicable(sectionSelection.getValue()));
}
private boolean isApplicable(BootDashModel section) {
if (section != null && section instanceof CloudFoundryBootDashModel) {
PropertyStoreApi props = section.getRunTarget().getType().getPersistentProperties();
return props != null;
}
return false;
}
@Override
public void run() {
final BootDashModel section = sectionSelection.getValue();
if (isApplicable(section)) {
CustomizeAppsManagerURLDialogModel model = new CustomizeAppsManagerURLDialogModel((CloudFoundryBootDashModel)section);
cfUi().openEditAppsManagerURLDialog(model);
}
}
@Override
public EnumSet<Location> showIn() {
return EnumSet.of(Location.CUSTOMIZE_MENU);
}
}

View File

@@ -1,116 +0,0 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import java.net.URL;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.PlatformUI;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springframework.ide.eclipse.boot.dash.model.BootDashElement;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel.ElementStateListener;
import org.springframework.ide.eclipse.boot.dash.model.RunState;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
public class EnableJmxSshTunnelAction extends AbstractCloudAppDashElementsAction {
private ElementStateListener stateListener;
public EnableJmxSshTunnelAction(Params params) {
super(params);
this.setText("Enable JMX Ssh Tunneling");
this.setToolTipText("Enables JMX on this app and creates an SSH Tunnel the next time the app is started.");
URL url = FileLocator.find(Platform.getBundle("org.springframework.ide.eclipse.boot.dash"), new Path("icons/link_to_editor.png"), null);
if (url != null) {
this.setImageDescriptor(ImageDescriptor.createFromURL(url));
}
if (model != null) {
model.addElementStateListener(stateListener = new ElementStateListener() {
@Override
public void stateChanged(BootDashElement e) {
if (getSelectedElements().contains(e) && !PlatformUI.getWorkbench().isClosing()) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
updateEnablement();
}
});
}
}
});
}
}
@Override
public void updateEnablement() {
boolean enable = false;
boolean enableTunnel = false;
if (getSelectedElements().size()==1) {
BootDashElement e = getSelectedElements().iterator().next();
if (e instanceof CloudAppDashElement && e.getProject() != null) {
CloudAppDashElement cde = (CloudAppDashElement) e;
enable = true;
enableTunnel = !cde.getEnableJmxSshTunnel();
}
}
this.setEnabled(enable);
if (enable) {
setText((enableTunnel ? "Enable" : "Disable")+" JMX Ssh Tunnelling");
} else {
setText("Toggle JMX Ssh Tunnelling");
}
}
@Override
public void run() {
try {
for (BootDashElement _e : getSelectedElements()) {
if (_e instanceof CloudAppDashElement && _e.getBootDashModel() instanceof CloudFoundryBootDashModel && _e.getProject() != null) {
CloudAppDashElement e = (CloudAppDashElement) _e;
boolean enable = !e.getEnableJmxSshTunnel();
e.setEnableJmxSshTunnel(enable);
RunState runstate = e.getRunState();
if (runstate!=RunState.INACTIVE) {
int answer = ui().confirmOperation((enable ? "Enabling" : "Disabling") +" JMX Requires Restart",
"Enabling JMX is done by setting environment parameters on startup. For this setting to take effect the app needs to be restarted.\n\n" +
"Do you want to restart now?",
new String[] {
"Yes, Restart NOW",
"No, I'll restart MANUALLY later"
},
0
);
if (answer==0) {
RunState runningOrDebugging = runstate==RunState.DEBUGGING ? RunState.DEBUGGING : RunState.RUNNING;
e.restart(runningOrDebugging, ui());
}
}
}
}
} catch (Exception e) {
Log.log(e);
}
}
@Override
public void dispose() {
if (model != null && stateListener != null) {
model.removeElementStateListener(stateListener);
stateListener = null;
}
super.dispose();
}
}

View File

@@ -1,55 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.di.SimpleDIContext;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springframework.ide.eclipse.boot.dash.model.RunTarget;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
/**
* @author Martin Lippert
*/
public class OpenCloudAdminConsoleAction extends AbstractCloudDashModelAction {
public OpenCloudAdminConsoleAction(LiveExpression<BootDashModel> sectionSelection, SimpleDIContext ui) {
super(sectionSelection, ui);
this.setText("Open Cloud Admin Console");
this.setToolTipText("Opens the Cloud Administration Console for this Cloud Foundry Space and Organisation");
this.setImageDescriptor(BootDashActivator.getImageDescriptor("icons/cloud_obj.png"));
}
@Override
public void run() {
final BootDashModel targetModel = sectionSelection.getValue();
if (targetModel != null) {
RunTarget target = targetModel.getRunTarget();
if (target instanceof CloudFoundryRunTarget) {
((CloudFoundryRunTarget) target).openCloudAdminConsole(ui());
}
}
}
@Override
public void updateEnablement() {
final BootDashModel targetModel = sectionSelection.getValue();
if (targetModel != null) {
RunTarget runTarget = targetModel.getRunTarget();
this.setEnabled(runTarget instanceof CloudFoundryRunTarget);
} else {
this.setEnabled(false);
}
}
}

View File

@@ -1,89 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import java.util.Collection;
import java.util.Iterator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.model.BootDashElement;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springframework.ide.eclipse.boot.dash.views.AbstractBootDashElementsAction;
import org.springframework.ide.eclipse.boot.dash.views.AbstractBootDashElementsAction.Params;
public class ReconnectCloudConsoleAction extends AbstractCloudAppDashElementsAction {
public ReconnectCloudConsoleAction(Params params) {
super(params);
this.setText("Reconnect Console");
this.setToolTipText("Reconnect Console");
this.setImageDescriptor(BootDashActivator.getImageDescriptor("icons/open_console.png"));
this.setDisabledImageDescriptor(BootDashActivator.getImageDescriptor("icons/open_console_disabled.png"));
}
@Override
public void run() {
final Collection<BootDashElement> selecteds = getSelectedElements();
Job job = new Job("Reconnecting console") {
@Override
protected IStatus run(IProgressMonitor monitor) {
doShowConsoles(selecteds);
return Status.OK_STATUS;
}
};
job.schedule();
}
protected void doShowConsoles(Collection<BootDashElement> selectedElements) {
if (selectedElements != null) {
Iterator<BootDashElement> it = selectedElements.iterator();
// Show first element only for now
if (it.hasNext()) {
BootDashElement element = selectedElements.iterator().next();
BootDashModel model = element.getBootDashModel();
try {
if (model.getElementConsoleManager() != null) {
model.getElementConsoleManager().reconnect(element);
}
} catch (Exception e) {
ui().errorPopup("Reconnect Console Failure", e.getMessage());
}
}
}
}
@Override
public void updateEnablement() {
boolean enable = false;
if (!getSelectedElements().isEmpty()) {
enable = true;
for (BootDashElement e : getSelectedElements()) {
if (!(e instanceof CloudAppDashElement)) {
enable = false;
break;
}
}
}
this.setEnabled(enable);
}
}

View File

@@ -1,72 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.model.BootDashElement;
import org.springframework.ide.eclipse.boot.dash.views.AbstractBootDashElementsAction;
import org.springframework.ide.eclipse.boot.dash.views.AbstractBootDashElementsAction.Params;
public class RestartApplicationOnlyAction extends AbstractCloudAppDashElementsAction {
public RestartApplicationOnlyAction(Params params) {
super(params);
this.setText("Restart Only");
this.setToolTipText("Restarts the application without uploading changes.");
this.setImageDescriptor(BootDashActivator.getImageDescriptor("icons/restart.png"));
this.setDisabledImageDescriptor(BootDashActivator.getImageDescriptor("icons/restart_disabled.png"));
}
@Override
public void run() {
Job job = new Job("Restarting apps") {
@Override
protected IStatus run(IProgressMonitor monitor) {
for (BootDashElement el : getSelectedElements()) {
if (el instanceof CloudAppDashElement) {
try {
CloudAppDashElement cloudAppDashElement = (CloudAppDashElement) el;
cloudAppDashElement.restartOnlyAsynch(ui(), cloudAppDashElement.createCancelationToken());
} catch (Exception e) {
ui().errorPopup("Error restarting application", e.getMessage());
}
}
}
return Status.OK_STATUS;
}
};
job.schedule();
}
@Override
public void updateEnablement() {
boolean enable = false;
if (!getSelectedElements().isEmpty()) {
enable = true;
for (BootDashElement e : getSelectedElements()) {
if (!(e instanceof CloudAppDashElement)) {
enable = false;
break;
}
}
}
this.setEnabled(enable);
}
}

View File

@@ -1,58 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springframework.ide.eclipse.boot.dash.cf.ops.SelectManifestOp;
import org.springframework.ide.eclipse.boot.dash.model.BootDashElement;
import org.springframework.ide.eclipse.boot.dash.model.UserInteractions;
public class SelectManifestAction extends AbstractCloudAppDashElementsAction {
public SelectManifestAction(Params params) {
super(params);
params.getContext().assertDefinitionFor(UserInteractions.class);
this.setText("Select Manifest");
this.setToolTipText("Selects a manifest YAML file to use during application restart.");
this.setImageDescriptor(BootDashActivator.getImageDescriptor("icons/selectmanifest.gif"));
}
@Override
public void run() {
CloudAppDashElement element = getSelectedCloudElementWithProject();
if (element != null) {
CloudFoundryBootDashModel model = element.getBootDashModel();
model.runAsynch(new SelectManifestOp(element), ui());
}
}
@Override
public void updateEnablement() {
this.setEnabled(getSelectedCloudElementWithProject() != null);
}
protected CloudAppDashElement getSelectedCloudElementWithProject() {
if (getSelectedElements().size() == 1) {
for (BootDashElement e : getSelectedElements()) {
if (e instanceof CloudAppDashElement) {
CloudAppDashElement cde = (CloudAppDashElement) e;
if (cde.getProject() != null && cde.getProject().isAccessible()) {
return cde;
}
}
}
}
return null;
}
}

View File

@@ -1,89 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.actions;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.eclipse.core.runtime.Status;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.di.SimpleDIContext;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springframework.ide.eclipse.boot.dash.model.RunTarget;
import org.springframework.ide.eclipse.boot.dash.model.RunTargetWithProperties;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
public class UpdatePasswordAction extends AbstractCloudDashModelAction {
public UpdatePasswordAction(LiveExpression<BootDashModel> sectionSelection, SimpleDIContext context) {
super(sectionSelection, context);
this.setText("Update Password");
this.setToolTipText("Update password locally for the selected target.");
this.setImageDescriptor(BootDashActivator.getImageDescriptor("icons/update_password.png"));
}
CompletableFuture<Void> lastRunCompletion = null;
/**
* This is meant for test-code only, so that test can we wait for the action (part f which is executed in
* a job) to complete.
*/
public void waitFor() throws InterruptedException, ExecutionException {
if (lastRunCompletion!=null) {
lastRunCompletion.get();
}
}
@Override
public void run() {
CompletableFuture<Void> lastRunCompletion = this.lastRunCompletion = new CompletableFuture<>();
try {
final CloudFoundryBootDashModel targetModel = (CloudFoundryBootDashModel) sectionSelection.getValue();
final CloudFoundryRunTarget runTarget = targetModel.getRunTarget();
if (runTarget!=null) {
targetModel.refreshTracker.callAsync("Updating password...", () -> {
runTarget.updatePasswordAndConnect();
lastRunCompletion.complete(null);
return Status.OK_STATUS;
});
}
} catch (Exception e) {
Log.log(e);
}
}
@Override
public void updateEnablement() {
this.setEnabled(getCredentialsHolder(sectionSelection.getValue())!=null);
}
@Override
public void updateVisibility() {
setVisible(getCredentialsHolder(sectionSelection.getValue()) != null);
}
private RunTargetWithProperties getCredentialsHolder(BootDashModel section) {
if (section!=null) {
RunTarget target = section.getRunTarget();
if (target instanceof RunTargetWithProperties) {
RunTargetWithProperties targetWithProps = (RunTargetWithProperties) target;
if (targetWithProps.requiresCredentials()) {
return targetWithProps;
}
}
}
return null;
}
}

View File

@@ -1,9 +0,0 @@
package org.springframework.ide.eclipse.boot.dash.cf.client;
public enum CFAppState {
STOPPED,
STARTED,
UNKNOWN
}

View File

@@ -1,36 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public interface CFApplication extends CFEntity {
//TODO: lots of this infos should be moved to application details
int getInstances();
int getRunningInstances();
int getMemory();
UUID getGuid();
List<String> getServices();
String getBuildpackUrl();
List<String> getUris();
CFAppState getState();
int getDiskQuota();
Integer getTimeout();
String getCommand();
String getStack();
Map<String,String> getEnvAsMap();
String getHealthCheckType();
String getHealthCheckHttpEndpoint();
}

View File

@@ -1,5 +0,0 @@
package org.springframework.ide.eclipse.boot.dash.cf.client;
public interface CFApplicationArchive {
}

View File

@@ -1,17 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import java.util.List;
public interface CFApplicationDetail extends CFApplication {
List<CFInstanceStats> getInstanceDetails();
}

View File

@@ -1,15 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
public interface CFBuildpack {
String getName();
}

View File

@@ -1,168 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import java.net.URI;
import org.eclipse.core.runtime.Assert;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCredentials.CFCredentialType;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryTargetProperties;
/**
* All the parameters needed to create a CF client.
*
* @author Kris De Volder
*/
public class CFClientParams {
private final String apiUrl;
private final String username;
private CFCredentials credentials;
private final boolean isSelfSigned;
private final boolean skipSslValidation;
private String orgName; // optional
private String spaceName; //optional
public CFClientParams(String apiUrl,
String username,
CFCredentials credentials,
boolean isSelfSigned,
String orgName,
String spaceName,
boolean skipSslValidation
) {
Assert.isNotNull(apiUrl, "apiUrl is required");
Assert.isNotNull(credentials, "credentials required");
if (credentials.getType()==CFCredentialType.PASSWORD) {
Assert.isNotNull(username, "username is required");
}
this.apiUrl = apiUrl;
this.username = username;
this.credentials = credentials;
this.isSelfSigned = isSelfSigned;
this.skipSslValidation = skipSslValidation;
this.orgName = orgName;
this.spaceName = spaceName;
}
public CFClientParams(CloudFoundryTargetProperties targetProperties) throws Exception {
this(
targetProperties.getUrl(),
targetProperties.getUsername(),
targetProperties.getCredentials(),
targetProperties.isSelfsigned(),
targetProperties.getOrganizationName(),
targetProperties.getSpaceName(),
targetProperties.skipSslValidation()
);
}
public CFCredentials getCredentials() {
return credentials;
}
public String getUsername() {
return username;
}
public boolean isSelfsigned() {
return isSelfSigned;
}
public boolean skipSslValidation() {
return skipSslValidation;
}
public String getApiUrl() {
return apiUrl;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public String getSpaceName() {
return spaceName;
}
public void setSpaceName(String spaceName) {
this.spaceName = spaceName;
}
public String getHost() {
try {
URI uri = new URI(getApiUrl());
return uri.getHost();
} catch (Exception e) {
BootActivator.log(e);
}
return null;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((apiUrl == null) ? 0 : apiUrl.hashCode());
result = prime * result + (isSelfSigned ? 1231 : 1237);
result = prime * result + ((orgName == null) ? 0 : orgName.hashCode());
result = prime * result + ((credentials == null) ? 0 : credentials.hashCode());
result = prime * result + ((spaceName == null) ? 0 : spaceName.hashCode());
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CFClientParams other = (CFClientParams) obj;
if (apiUrl == null) {
if (other.apiUrl != null)
return false;
} else if (!apiUrl.equals(other.apiUrl))
return false;
if (isSelfSigned != other.isSelfSigned)
return false;
if (orgName == null) {
if (other.orgName != null)
return false;
} else if (!orgName.equals(other.orgName))
return false;
if (credentials == null) {
if (other.credentials != null)
return false;
} else if (!credentials.equals(other.credentials))
return false;
if (spaceName == null) {
if (other.spaceName != null)
return false;
} else if (!spaceName.equals(other.spaceName))
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
}

View File

@@ -1,31 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import org.springframework.ide.eclipse.boot.dash.cf.client.v2.CFDomainStatus;
public interface CFCloudDomain {
String getName();
CFDomainType getType();
CFDomainStatus getStatus();
/**
* If the given hostAndDomain is of the form ${host}.${domain} then
* return the host part. Otherwise return null.
*/
default String splitHost(String hostAndDomain) {
String name = getName();
if (hostAndDomain.endsWith("."+name)) {
return hostAndDomain.substring(0, hostAndDomain.length()-name.length()-1);
}
return null;
}
}

View File

@@ -1,151 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.Assert;
import org.springsource.ide.eclipse.commons.livexp.ui.Ilabelable;
public class CFCredentials {
public enum CFCredentialType {
PASSWORD,
TEMPORARY_CODE,
REFRESH_TOKEN;
public LoginMethod toLoginMethod() {
switch (this) {
case PASSWORD:
return LoginMethod.PASSWORD;
case TEMPORARY_CODE:
return LoginMethod.TEMPORARY_CODE;
default:
return null;
}
}
}
public enum LoginMethod implements Ilabelable {
PASSWORD,
TEMPORARY_CODE;
@Override
public String getLabel() {
String[] pieces = name().split("_");
StringBuilder label = new StringBuilder();
for (int i = 0; i < pieces.length; i++) {
if (i>0) {
label.append(" ");
}
label.append(StringUtils.capitalize(pieces[i].toLowerCase()));
}
return label.toString();
}
}
private final CFCredentialType type;
private final String secret;
/**
* Deprecated, use fromLogin instead
*/
@Deprecated
public static CFCredentials fromPassword(String password) {
return fromLogin(LoginMethod.PASSWORD, password);
}
public static CFCredentials fromLogin(LoginMethod method, String secret) {
CFCredentialType type;
switch (method) {
case PASSWORD:
type = CFCredentialType.PASSWORD;
break;
case TEMPORARY_CODE:
type = CFCredentialType.TEMPORARY_CODE;
break;
default:
throw new IllegalStateException("Bug! Missing switch case?");
}
return new CFCredentials(type, secret);
}
public static CFCredentials fromRefreshToken(String refreshToken) {
Assert.isNotNull(refreshToken);
return new CFCredentials(CFCredentialType.REFRESH_TOKEN, refreshToken);
}
public String getSecret() {
return secret;
}
/////////////////////////////////////////////////////////////////////////
/**
* Private constuctor, use static `fromXXX` factory methods instead.
*/
private CFCredentials(CFCredentialType type, String secret) {
this.type = type;
this.secret = secret;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((secret == null) ? 0 : secret.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CFCredentials other = (CFCredentials) obj;
if (secret == null) {
if (other.secret != null)
return false;
} else if (!secret.equals(other.secret))
return false;
if (type != other.type)
return false;
return true;
}
@Override
public String toString() {
return "CFCredentials [type=" + type + ", secret=" + hidePassword(type, secret) + "]";
}
private String hidePassword(CFCredentialType type, String password) {
if (password==null) {
return null;
}
return type==CFCredentialType.PASSWORD
? "****"
: password;
}
public CFCredentialType getType() {
return type;
}
public static CFCredentials fromSsoToken(String ssoToken) {
return CFCredentials.fromLogin(LoginMethod.TEMPORARY_CODE, ssoToken);
}
}

View File

@@ -1,16 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Spring IDE Developers
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
public enum CFDomainType {
HTTP, TCP
}

View File

@@ -1,15 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
public interface CFEntity {
String getName();
}

View File

@@ -1,33 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import org.cloudfoundry.uaa.UaaException;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
/**
* Static methods to recognize specific types of exceptions CF client
* may throw.
*
* @author Kris De Volder
*/
public class CFExceptions {
public static boolean isAuthFailure(Exception e) {
String msg = ExceptionUtil.getMessage(e);
return msg.contains("Bad credentials") || ExceptionUtil.getDeepestCause(e) instanceof UaaException;
}
public static boolean isSSLCertificateFailure(Exception e) {
Throwable cause = ExceptionUtil.getDeepestCause(e);
return cause.getClass().getName().equals("sun.security.provider.certpath.SunCertPathBuilderException");
}
}

View File

@@ -1,5 +0,0 @@
package org.springframework.ide.eclipse.boot.dash.cf.client;
public enum CFInstanceState {
RUNNING, CRASHED, FLAPPING, STARTING, DOWN, UNKNOWN
}

View File

@@ -1,7 +0,0 @@
package org.springframework.ide.eclipse.boot.dash.cf.client;
public interface CFInstanceStats {
CFInstanceState getState();
}

View File

@@ -1,17 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import java.util.UUID;
public interface CFOrganization extends CFEntity {
UUID getGuid();
}

View File

@@ -1,24 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
public interface CFServiceInstance extends CFEntity {
String getName();
String getService();
String getPlan();
String getDescription();
String getDocumentationUrl();
String getDashboardUrl();
//TODO: last operation info?
}

View File

@@ -1,18 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import java.util.UUID;
public interface CFSpace extends CFEntity {
CFOrganization getOrganization();
UUID getGuid();
}

View File

@@ -1,15 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
public interface CFStack extends CFEntity {
}

View File

@@ -1,84 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.osgi.framework.Version;
import org.springframework.ide.eclipse.boot.dash.cf.client.v2.CFPushArguments;
import org.springframework.ide.eclipse.boot.dash.console.IApplicationLogConsole;
import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens.CancelationToken;
import org.springsource.ide.eclipse.commons.livexp.ui.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public interface ClientRequests extends Disposable {
/**
* The actual Rest API version that cloud controller claims to be.
*/
Version getApiVersion() throws Exception;
/**
* The minimum version that the CF V2 java client claims to support.
*/
Version getSupportedApiVersion();
/**
* Returns null if the application does not exist. Throws some kind of Exception if there's any other kind of problem.
*/
CFApplicationDetail getApplication(String appName) throws Exception;
//TODO: consider removing the getXXXSupport method and directly adding the apis that these support
// objects provide.
SshClientSupport getSshClientSupport() throws Exception;
void deleteApplication(String name) throws Exception;
List<CFApplication> getApplicationsWithBasicInfo() throws Exception;
List<CFBuildpack> getBuildpacks() throws Exception;
List<CFCloudDomain> getDomains() throws Exception;
List<CFServiceInstance> getServices() throws Exception;
List<CFSpace> getSpaces() throws Exception;
List<CFStack> getStacks() throws Exception;
void restartApplication(String appName, CancelationToken token) throws Exception;
void stopApplication(String appName) throws Exception;
reactor.core.Disposable streamLogs(String appName, IApplicationLogConsole logConsole) throws Exception;
Flux<CFApplicationDetail> getApplicationDetails(List<CFApplication> appsToLookUp) throws Exception;
String getHealthCheck(UUID appGuid) throws Exception;
void setHealthCheck(UUID guid, String hcType) throws Exception;
boolean applicationExists(String appName) throws Exception;
//Added since v2:
void push(CFPushArguments args, CancelationToken cancelationToken) throws Exception;
Map<String, String> getApplicationEnvironment(String appName) throws Exception;
Mono<Void> deleteServiceAsync(String serviceName);
/**
* Gets current value of the client's refresh token. Note that the token is only set once it is known.
* Initially, if a client is created via password auth, then the refreshToken won't be known until
* some operation has been executed.
*
* @return Refresh token if it is already known, null otherwise.
*/
String getRefreshToken();
Mono<String> getUserName();
/**
* The returned flux will provide the current token in its onNext immediately, if there is a current
* token already. Subsequent onNext will be fired whenever the token changes.
*/
Flux<String> getRefreshTokens();
}

View File

@@ -1,42 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import java.util.List;
import org.eclipse.core.runtime.Assert;
/**
* A Cloud application with additional stats and instances information
*
* This should be removed. Use {@link CFApplicationDetail} instead. It contains
* all the same infos.
*/
@Deprecated
public class CloudAppInstances {
private final CFApplication app;
private final List<CFInstanceStats> stats;
public CloudAppInstances(CFApplication app, List<CFInstanceStats> stats) {
Assert.isNotNull(app);
this.app = app;
this.stats = stats;
}
public CFApplication getApplication() {
return app;
}
public List<CFInstanceStats> getStats() {
return stats;
}
}

View File

@@ -1,33 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryTargetProperties;
public abstract class CloudFoundryClientFactory {
public abstract ClientRequests getClient(CFClientParams params);
/**
* Get the client for an existing {@link CloudFoundryRunTarget}. Note that
* this may require the password to be set for that runtarget.
*
* @param runTarget
* @return client if connection was successful.
* @throws Exception
* if there was an error connecting, including if password is
* not set or invalid.
*/
public final ClientRequests getClient(CloudFoundryTargetProperties targetProperties) throws Exception {
return getClient(new CFClientParams(targetProperties));
}
}

View File

@@ -1,19 +0,0 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
public class HealthChecks {
public static final String HC_PORT = "port";
public static final String HC_PROCESS = "process";
public static final String HC_HTTP = "http";
public static final String[] HC_ALL = {HC_PORT, HC_PROCESS, HC_HTTP};
}

View File

@@ -1,30 +0,0 @@
/*******************************************************************************
* Copyright (c) 2009-2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
/**
* Enum used for the state of an instance
*
* Note: copied over from CF V1 client code.
*
* @author Thomas Risberg
*/
public enum InstanceState {
DOWN, STARTING, RUNNING, CRASHED, FLAPPING, UNKNOWN;
public static InstanceState valueOfWithDefault(String s) {
try {
return InstanceState.valueOf(s);
} catch (IllegalArgumentException e) {
return InstanceState.UNKNOWN;
}
}
}

View File

@@ -1,25 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
public class RequestErrorHandler {
/**
*
* @param e
* @return true if request error should be treated as an error and thrown. False error
* should be ignored.
*/
public boolean throwError(Throwable e) {
return true;
}
}

View File

@@ -1,27 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
import java.util.UUID;
public interface SshClientSupport {
SshHost getSshHost() throws Exception;
String getSshUser(String appName, int instance) throws Exception;
String getSshCode() throws Exception;
/**
* Deprecated because it is not supported with V2. Use the method based on appName instead.
*/
@Deprecated
String getSshUser(UUID appGuid, int instance) throws Exception;
}

View File

@@ -1,53 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client;
/**
* Info object containing various bits of info about the host to which an ssh
* client may wish to connect.
*
* @author Kris De Volder
*/
public class SshHost {
final private String host;
final private int port;
final private String fingerPrint;
public SshHost(String host, int port, String fingerPrint) {
super();
this.host = host;
this.port = port;
this.fingerPrint = fingerPrint;
}
public String getFingerPrint() {
return fingerPrint;
}
public int getPort() {
return port;
}
public String getHost() {
return host;
}
@Override
public String toString() {
return "SshHost [host=" //$NON-NLS-1$
+ host + ", port=" //$NON-NLS-1$
+ port + ", fingerPrint=" //$NON-NLS-1$
+ fingerPrint + "]";//$NON-NLS-1$
}
}

View File

@@ -1,33 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;
/**
* Extrat bits of info that are 'prefetched' asynchronously to fill
* out the CFApplication with info that C2 client doesn't initially return.
*
* @author Kris De Volder
*/
public interface ApplicationExtras {
Mono<Map<String,String>> getEnv();
Mono<List<String>> getServices();
Mono<String> getBuildpack();
Mono<String> getStack();
Mono<Integer> getTimeout();
Mono<String> getCommand();
Mono<String> getHealthCheckType();
Mono<String> getHealthCheckHttpEndpoint();
}

View File

@@ -1,62 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import java.util.List;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFApplicationDetail;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFInstanceStats;
public class CFApplicationDetailData extends CFApplicationSummaryData implements CFApplicationDetail {
private List<CFInstanceStats> instanceDetails;
public CFApplicationDetailData(
CFApplicationSummaryData app,
List<CFInstanceStats> instanceDetails
) {
super(
app.getName(),
app.getInstances(),
app.getRunningInstances(),
app.getMemory(),
app.getGuid(),
app.getUris(),
app.getState(),
app.getDiskQuota(),
app.extras
);
this.instanceDetails = instanceDetails;
}
@Override
public List<CFInstanceStats> getInstanceDetails() {
return instanceDetails;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("CFApplicationDetail(\n");
buf.append(" name = "+getName()+"\n");
buf.append(" instance = "+getInstances()+"\n");
buf.append(" runningInstances = "+getRunningInstances()+"\n");
buf.append(" memory = "+getMemory()+"\n");
buf.append(" guid = "+getGuid()+"\n");
buf.append(" uris = "+getUris()+"\n");
buf.append(" state = "+getState()+"\n");
buf.append(" diskQuota = "+getDiskQuota()+"\n");
buf.append(" instanceDetails = "+getInstanceDetails()+"\n");
buf.append(")\n");
return buf.toString();
}
}

View File

@@ -1,135 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFAppState;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFApplication;
public class CFApplicationSummaryData implements CFApplication {
private String name;
private int instances;
private int runningInstances;
private int memory;
private UUID guid;
private List<String> uris;
private CFAppState state;
private int diskQuota;
protected ApplicationExtras extras;
public CFApplicationSummaryData(
String name,
int instances,
int runningInstances,
int memory,
UUID guid,
List<String> uris,
CFAppState state,
int diskQuota,
ApplicationExtras extras
) {
super();
this.name = name;
this.instances = instances;
this.runningInstances = runningInstances;
this.memory = memory;
this.guid = guid;
this.uris = uris;
this.state = state;
this.diskQuota = diskQuota;
this.extras = extras;
}
@Override
public String getName() {
return name;
}
@Override
public int getInstances() {
return instances;
}
@Override
public int getRunningInstances() {
return runningInstances;
}
@Override
public int getMemory() {
return memory;
}
@Override
public UUID getGuid() {
return guid;
}
@Override
public List<String> getServices() {
return extras.getServices().block(DefaultClientRequestsV2.GET_SERVICES_TIMEOUT);
}
@Override
public String getBuildpackUrl() {
return extras.getBuildpack().block(DefaultClientRequestsV2.GET_SMALL_INFO_TIMEOUT);
}
@Override
public List<String> getUris() {
return uris;
}
@Override
public CFAppState getState() {
return state;
}
@Override
public int getDiskQuota() {
return diskQuota;
}
@Override
public Integer getTimeout() {
return extras.getTimeout().block(DefaultClientRequestsV2.GET_SMALL_INFO_TIMEOUT);
}
@Override
public String getHealthCheckType() {
return extras.getHealthCheckType().block(DefaultClientRequestsV2.GET_SMALL_INFO_TIMEOUT);
}
@Override
public String getCommand() {
return extras.getCommand().block(DefaultClientRequestsV2.GET_SMALL_INFO_TIMEOUT);
}
@Override
public String getStack() {
return extras.getStack().block(DefaultClientRequestsV2.GET_SMALL_INFO_TIMEOUT);
}
@Override
public Map<String, String> getEnvAsMap() {
return extras.getEnv().block(DefaultClientRequestsV2.GET_SMALL_INFO_TIMEOUT);
}
@Override
public String getHealthCheckHttpEndpoint() {
return extras.getHealthCheckHttpEndpoint().block(DefaultClientRequestsV2.GET_SMALL_INFO_TIMEOUT);
}
}

View File

@@ -1,83 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Spring IDE Developers
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCloudDomain;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFDomainType;
public final class CFCloudDomainData implements CFCloudDomain {
private final String name;
private final CFDomainType type;
private final CFDomainStatus status;
public CFCloudDomainData(String name, CFDomainType type, CFDomainStatus status) {
super();
this.name = name;
this.type = type;
this.status = status;
}
public CFCloudDomainData(String name) {
this(name, CFDomainType.HTTP, CFDomainStatus.SHARED);
}
@Override
public CFDomainType getType() {
return type;
}
@Override
public CFDomainStatus getStatus() {
return status;
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return "CFCloudDomainData [name=" + name + ", type=" + type + ", status=" + status + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((status == null) ? 0 : status.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CFCloudDomainData other = (CFCloudDomainData) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (status != other.status)
return false;
if (type != other.type)
return false;
return true;
}
}

View File

@@ -1,15 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Spring IDE Developers
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
public enum CFDomainStatus {
OWNED, SHARED
}

View File

@@ -1,171 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import java.io.File;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipFile;
import org.eclipse.core.runtime.Assert;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.deployment.DeploymentProperties;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
/**
* Arguments passed to push operation.
*
* @author Kris De Volder
* @author Nieraj Singh
*/
public class CFPushArguments implements AutoCloseable {
private List<String> routes = ImmutableList.of();
private String appName;
private Integer memory;
private Integer diskQuota;
private Integer timeout;
private String buildpack;
private String command;
private String stack;
private String healthCheckType;
private Map<String, String> env = ImmutableMap.of();
private Integer instances;
private List<String> services = ImmutableList.of();
private File applicationDataAsFile;
private boolean noStart = false;
private boolean randomRoute = false;
private String healthCheckHttpEndpoint;
public CFPushArguments() {
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public Integer getMemory() {
return memory;
}
public void setMemory(Integer memory) {
this.memory = memory;
}
public Integer getDiskQuota() {
return diskQuota;
}
public void setDiskQuota(Integer diskQuota) {
this.diskQuota = diskQuota;
}
public Integer getTimeout() {
return timeout;
}
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
public String getBuildpack() {
return buildpack;
}
public void setBuildpack(String buildpack) {
this.buildpack = buildpack;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getStack() {
return stack;
}
public void setStack(String stack) {
this.stack = stack;
}
public Map<String, String> getEnv() {
return env;
}
public void setEnv(Map<String, String> env) {
this.env = env;
}
public Integer getInstances() {
return instances;
}
public void setInstances(Integer instances) {
this.instances = instances;
}
public List<String> getServices() {
return services;
}
public void setServices(List<String> services) {
this.services = services;
}
public boolean getRandomRoute() {
return this.randomRoute;
}
public void setRandomRoute(boolean randomRoute) {
this.randomRoute = randomRoute;
}
public File getApplicationDataAsFile() {
return applicationDataAsFile;
}
public void setApplicationData(File archive) throws Exception {
Assert.isLegal(this.applicationDataAsFile==null, "Can only set this once");
this.applicationDataAsFile=archive;
}
public boolean isNoStart() {
return noStart;
}
public void setNoStart(boolean noStart) {
this.noStart = noStart;
}
public String getHealthCheckType() {
if (healthCheckType==null) {
return DeploymentProperties.DEFAULT_HEALTH_CHECK_TYPE;
}
return healthCheckType;
}
public void setHealthCheckType(String healthCheckType) {
this.healthCheckType = healthCheckType;
}
public List<String> getRoutes() {
return routes;
}
public void setRoutes(Collection<String> routes) {
this.routes = routes == null ? ImmutableList.of() : ImmutableList.copyOf(routes);
}
public void setRoutes(String... routes) {
setRoutes(ImmutableList.copyOf(routes));
}
@Override
public String toString() {
return "CFPushArguments [appName=" + appName + ", routes=" + routes + ", memory=" + memory + ", diskQuota="
+ diskQuota + ", timeout=" + timeout + ", buildpack=" + buildpack + ", command=" + command + ", stack="
+ stack + ", env=" + env + ", instances=" + instances + ", services=" + services + ", noStart="
+ noStart + ", healthCheckType="+ healthCheckType+ ", randomRoute="+ randomRoute+" ]";
}
public void setHealthCheckHttpEndpoint(String healthCheckHttpEndpoint) {
this.healthCheckHttpEndpoint = healthCheckHttpEndpoint;
}
public String getHealthCheckHttpEndpoint() {
return this.healthCheckHttpEndpoint;
}
@Override
public void close() throws Exception {
//This used to do something more useful, we kept it for now to avoid having to
// change a lot of code. But calling `close` is no longer needed and does nothing.
}
}

View File

@@ -1,117 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
public class CFRoute {
public static final int NO_PORT = -1;
public static final String EMPTY_ROUTE = "";
final private String domain;
final private String host;
final private String path;
final private int port;
final private String fullRoute;
CFRoute(String domain, String host, String path, int port, String fullRoute) {
super();
this.domain = domain;
this.host = host;
this.path = path;
this.port = port;
this.fullRoute = fullRoute;
}
public String getDomain() {
return domain;
}
public String getHost() {
return host;
}
public String getPath() {
return path;
}
public int getPort() {
return port;
}
public String getRoute() {
return fullRoute;
}
public void validate() throws Exception {
if (!StringUtil.hasText(getDomain())) {
throw ExceptionUtil.coreException("Invalid route: No domain set.");
}
}
@Override
public String toString() {
return "CFRoute [domain=" + domain + ", host=" + host + ", path=" + path + ", port=" + port +"]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((domain == null) ? 0 : domain.hashCode());
result = prime * result + ((fullRoute == null) ? 0 : fullRoute.hashCode());
result = prime * result + ((host == null) ? 0 : host.hashCode());
result = prime * result + ((path == null) ? 0 : path.hashCode());
result = prime * result + port;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CFRoute other = (CFRoute) obj;
if (domain == null) {
if (other.domain != null)
return false;
} else if (!domain.equals(other.domain))
return false;
if (fullRoute == null) {
if (other.fullRoute != null)
return false;
} else if (!fullRoute.equals(other.fullRoute))
return false;
if (host == null) {
if (other.host != null)
return false;
} else if (!host.equals(other.host))
return false;
if (path == null) {
if (other.path != null)
return false;
} else if (!path.equals(other.path))
return false;
if (port != other.port)
return false;
return true;
}
public static CFRouteBuilder builder() {
return new CFRouteBuilder();
}
}

View File

@@ -1,248 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.cloudfoundry.operations.routes.Route;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCloudDomain;
import org.springframework.ide.eclipse.boot.util.Log;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
public class CFRouteBuilder {
private String domain;
private String host;
private String path;
private int port = CFRoute.NO_PORT;
private String fullRoute;
public CFRoute build() {
return new CFRoute(this.domain, this.host, this.path, this.port, this.fullRoute);
}
public CFRouteBuilder domain(String domain) {
this.domain = domain;
// may seem like the more ideal place is to build the full route when
// building the route, rather than repeating
// the process each time a domain, host, path or port value is set
// but the "from" option should be allowed to overwrite the full route
// as well since it already
// has the full value. Therefore re-construct the full value if the
// route is being built piece by piece, but not in from
this.fullRoute = buildRouteVal(this.host, this.domain, this.path, this.port);
return this;
}
public CFRouteBuilder host(String host) {
this.host = host;
this.fullRoute = buildRouteVal(this.host, this.domain, this.path, this.port);
return this;
}
public CFRouteBuilder path(String path) {
this.path = path;
this.fullRoute = buildRouteVal(this.host, this.domain, this.path, this.port);
return this;
}
public CFRouteBuilder port(int port) {
this.port = port;
this.fullRoute = buildRouteVal(this.host, this.domain, this.path, this.port);
return this;
}
public CFRouteBuilder from(Route route) {
// Route doesn't seem to have API to get a port
this.port = CFRoute.NO_PORT;
this.domain = route.getDomain();
this.host = route.getHost();
this.path = route.getPath();
this.fullRoute = buildRouteVal(this.host, this.domain, this.path, this.port);
return this;
}
/**
* Builds a {@link CFRoute} given a desiredUrl. This does NOT validate, and
* will attempt to build a route the best way it can given the desiredUrl.
* External components, like the CF Java client, can then validate the
* CFRoute.
*
* @param desiredUrl
* @param domains
* @return this builder
*/
public CFRouteBuilder from(String desiredUrl, Collection<String> domains) {
//If it is empty or null, there is nothing to build. However, be sure that the
// full route value is non-null, even if the "components" may be null
if (!StringUtil.hasText(desiredUrl)) {
this.fullRoute = CFRoute.EMPTY_ROUTE;
return this;
} else {
// Be sure to set the full route.
this.fullRoute = desiredUrl;
}
// Based on CLI cf/actors/routes.go and testing CLI directly with
// different "routes" values:
// 1. Paths is not allowed in TCP route (valid TCP route:
// "tcp.spring.io:8888")
// 2. Ports are not allowed in HTTP route (valid HTTP route:
// "myapps.cfapps.io/pathToApp/home")
// 3. Schemes (e.g. "http://") are not allowed in routes values.
// Anything that has a ":" is assumed to be TCP route followed by a port
// 4. Route can just be domain, or host and domain
//
// Therefore, routes values cannot be treated as URIs or URLs, but a
// combination of domain, host, path and port
// NOTE: The validation above doesn't need to take place here. The
// client or CF will validate correct combinations of routes.
// However, We may want to implement similar
// validation to the CF manifest editor though.
String matchedHost = null;
String hostAndDomain = null;
// Split into hostDomain segment, port and path
int slashIndex = desiredUrl.indexOf('/');
if (slashIndex >= 0) {
hostAndDomain = desiredUrl.substring(0, slashIndex);
String tempPath = desiredUrl.substring(slashIndex);
// Do not set empty strings. If there is no path, then it should be
// null
if (StringUtil.hasText(tempPath)) {
this.path = tempPath;
}
} else {
hostAndDomain = desiredUrl;
}
// CF Route builder does not validate, so don't allow exceptions to
// prevent parsing of the route. The builder should attempt to build
// a route the best way it can, even if it may have invalid information.
// This allows external participants, like the CF Java client, to
// perform validation
try {
String[] portSegments = hostAndDomain.split(":");
if (portSegments.length == 2) {
hostAndDomain = portSegments[0];
this.port = Integer.parseInt(portSegments[1]);
}
} catch (NumberFormatException e) {
Log.log(e);
}
this.domain = findDomain(hostAndDomain, domains);
if (this.domain != null) {
matchedHost = hostAndDomain.substring(0, hostAndDomain.length() - this.domain.length());
if (matchedHost.endsWith(".")) {
matchedHost = matchedHost.substring(0, matchedHost.length() - 1);
}
// Don't set empty strings
if (StringUtil.hasText(matchedHost)) {
this.host = matchedHost;
}
} else {
// Do a basic split on '.', where first segment is the host, and the
// rest domain
int firstDotIndex = hostAndDomain.indexOf('.');
if (firstDotIndex >= 0) {
String tempDomain = hostAndDomain.substring(firstDotIndex + 1);
// Don't set empty strings
if (StringUtil.hasText(tempDomain)) {
this.domain = tempDomain;
}
String tempHost = hostAndDomain.substring(0, firstDotIndex);
if (StringUtil.hasText(tempHost)) {
this.host = tempHost;
}
} else {
if (StringUtil.hasText(hostAndDomain)) {
this.host = hostAndDomain;
}
}
}
return this;
}
public static String findDomain(String hostDomain, Collection<String> domains) {
if (hostDomain == null) {
return null;
}
// find exact match
for (String name : domains) {
if (hostDomain.equals(name)) {
return hostDomain;
}
}
// Otherwise split on the first "." and try again
if (hostDomain.indexOf(".") >= 0 && hostDomain.indexOf(".") + 1 < hostDomain.length()) {
String remaining = hostDomain.substring(hostDomain.indexOf(".") + 1, hostDomain.length());
return findDomain(remaining, domains);
} else {
return null;
}
}
/**
* A basic building of a full route value. It performs no validation, just
* builds based on whether the parameter are set
*
* @param host
* @param domain
* @param path
* @param port
* @return Route value build with the given components. Always returns a non-null route. Empty route if no arguments are passed.
*/
public static String buildRouteVal(String host, String domain, String path, int port) {
StringBuilder builder = new StringBuilder();
if (StringUtil.hasText(host)) {
builder.append(host);
}
if (StringUtil.hasText(domain)) {
if (StringUtil.hasText(host)) {
builder.append('.');
}
builder.append(domain);
}
if (port != CFRoute.NO_PORT) {
builder.append(':');
builder.append(Integer.toString(port));
}
if (StringUtil.hasText(path)) {
if (!path.startsWith("/")) {
builder.append('/');
}
builder.append(path);
}
return builder.toString();
}
public CFRouteBuilder from(String desiredUrl, List<CFCloudDomain> cloudDomains) {
List<String> domains = cloudDomains
.stream()
.map(CFCloudDomain::getName)
.collect(Collectors.toList());
return from(desiredUrl, domains);
}
}

View File

@@ -1,293 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.cloudfoundry.client.v2.buildpacks.BuildpackResource;
import org.cloudfoundry.operations.applications.ApplicationDetail;
import org.cloudfoundry.operations.applications.ApplicationSummary;
import org.cloudfoundry.operations.applications.InstanceDetail;
import org.cloudfoundry.operations.domains.Domain;
import org.cloudfoundry.operations.domains.Status;
import org.cloudfoundry.operations.organizations.OrganizationSummary;
import org.cloudfoundry.operations.services.ServiceInstance;
import org.cloudfoundry.operations.services.ServiceInstanceType;
import org.cloudfoundry.operations.spaces.SpaceSummary;
import org.cloudfoundry.operations.stacks.Stack;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFAppState;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFApplication;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFApplicationDetail;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFBuildpack;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCloudDomain;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFDomainType;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFInstanceState;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFInstanceStats;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFOrganization;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFServiceInstance;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFSpace;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFStack;
import org.springframework.ide.eclipse.boot.util.Log;
import com.google.common.collect.ImmutableList;
/**
* Various helper methods to 'wrap' objects returned by CF client into
* our own types, so that we do not directly expose library types to our
* code.
*
* @author Kris De Volder
*/
public class CFWrappingV2 {
public static CFBuildpack wrap(BuildpackResource rsrc) {
String name = rsrc.getEntity().getName();
return new CFBuildpack() {
@Override
public String getName() {
return name;
}
};
}
public static CFApplicationDetail wrap(ApplicationDetail details, ApplicationExtras extras) {
if (details!=null) {
List<CFInstanceStats> instances = ImmutableList.copyOf(
details.getInstanceDetails()
.stream()
.map(CFWrappingV2::wrap)
.collect(Collectors.toList())
);
CFApplicationSummaryData summary = wrapSummary(details, extras);
return new CFApplicationDetailData(
summary,
instances
);
}
return null;
}
public static CFStack wrap(Stack stack) {
if (stack!=null) {
String name = stack.getName();
return new CFStack() {
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return "CFStack("+name+")";
}
};
}
return null;
}
public static CFApplicationDetail wrap(
CFApplicationSummaryData summary,
ApplicationDetail details
) {
List<CFInstanceStats> instanceDetails = ImmutableList.copyOf(
details
.getInstanceDetails()
.stream()
.map(CFWrappingV2::wrap)
.collect(Collectors.toList())
);
return new CFApplicationDetailData(summary, instanceDetails);
}
public static CFCloudDomain wrap(Domain domain) {
if (domain!=null) {
return new CFCloudDomainData(
domain.getName(),
CFWrappingV2.wrapDomainType(domain.getType()),
CFWrappingV2.wrap(domain.getStatus())
);
}
return null;
}
public static CFDomainStatus wrap(Status status) {
if (status!=null) {
return CFDomainStatus.valueOf(status.name());
}
return null;
}
public static CFDomainType wrapDomainType(String type) {
if (type!=null) {
return CFDomainType.valueOf(type.toUpperCase());
}
return CFDomainType.HTTP;
}
public static CFInstanceStats wrap(InstanceDetail instanceDetail) {
return new CFInstanceStats() {
@Override
public CFInstanceState getState() {
try {
return CFInstanceState.valueOf(instanceDetail.getState());
} catch (Exception e) {
Log.log(e);
return CFInstanceState.UNKNOWN;
}
}
@Override
public String toString() {
return ""+getState();
}
};
}
private static CFApplicationSummaryData wrapSummary(ApplicationDetail app, ApplicationExtras extras) {
CFAppState state;
try {
state = CFAppState.valueOf(app.getRequestedState());
} catch (Exception e) {
Log.log(e);
state = CFAppState.UNKNOWN;
}
return new CFApplicationSummaryData(
app.getName(),
app.getInstances(),
app.getRunningInstances(),
app.getMemoryLimit(),
UUID.fromString(app.getId()),
app.getUrls(),
state,
app.getDiskQuota(),
extras
);
}
public static CFApplication wrap(ApplicationSummary app, ApplicationExtras extras) {
CFAppState state;
try {
state = CFAppState.valueOf(app.getRequestedState());
} catch (Exception e) {
Log.log(e);
state = CFAppState.UNKNOWN;
}
return new CFApplicationSummaryData(
app.getName(),
app.getInstances(),
app.getRunningInstances(),
app.getMemoryLimit(),
UUID.fromString(app.getId()),
app.getUrls(),
state,
app.getDiskQuota(),
extras
);
}
public static CFServiceInstance wrap(final ServiceInstance service) {
return new CFServiceInstance() {
@Override
public String getName() {
return service.getName();
}
@Override
public String getPlan() {
return service.getPlan();
}
@Override
public String getDashboardUrl() {
return service.getDashboardUrl();
}
@Override
public String getService() {
if (service.getType()==ServiceInstanceType.USER_PROVIDED) {
return "user-provided";
} else {
return service.getService();
}
}
@Override
public String getDescription() {
return service.getDescription();
}
@Override
public String getDocumentationUrl() {
return service.getDocumentationUrl();
}
};
}
public static CFAppState wrapAppState(String s) {
try {
return CFAppState.valueOf(s);
} catch (Exception e) {
Log.log(e);
return CFAppState.UNKNOWN;
}
}
public static CFSpace wrap(OrganizationSummary org, SpaceSummary space) {
return new CFSpace() {
@Override
public String getName() {
return space.getName();
}
@Override
public CFOrganization getOrganization() {
return wrap(org);
}
@Override
public UUID getGuid() {
return UUID.fromString(space.getId());
}
@Override
public String toString() {
return "CFSpace("+org.getName()+" / "+getName()+")";
}
};
}
public static CFOrganization wrap(OrganizationSummary org) {
return new CFOrganization() {
@Override
public String getName() {
return org.getName();
}
@Override
public UUID getGuid() {
return UUID.fromString(org.getId());
}
};
}
public static CFBuildpack buildpack(String name) {
return new CFBuildpack() {
@Override
public String getName() {
return name;
}
};
}
}

View File

@@ -1,261 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import java.net.URL;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.cloudfoundry.client.CloudFoundryClient;
import org.cloudfoundry.client.v2.info.GetInfoRequest;
import org.cloudfoundry.client.v2.info.GetInfoResponse;
import org.cloudfoundry.reactor.DefaultConnectionContext;
import org.cloudfoundry.reactor.ProxyConfiguration;
import org.cloudfoundry.reactor.TokenProvider;
import org.cloudfoundry.reactor.client.ReactorCloudFoundryClient;
import org.cloudfoundry.reactor.doppler.ReactorDopplerClient;
import org.cloudfoundry.reactor.tokenprovider.OneTimePasscodeTokenProvider;
import org.cloudfoundry.reactor.tokenprovider.PasswordGrantTokenProvider;
import org.cloudfoundry.reactor.tokenprovider.RefreshTokenGrantTokenProvider;
import org.cloudfoundry.reactor.uaa.ReactorUaaClient;
import org.eclipse.core.net.proxy.IProxyData;
import org.eclipse.core.net.proxy.IProxyService;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCredentials;
import org.springframework.ide.eclipse.boot.util.Log;
import org.springsource.ide.eclipse.commons.frameworks.core.util.StringUtils;
import reactor.core.publisher.Mono;
/**
* TODO: Remove this class when the 'thread leak bug' in V2 client is fixed.
*
* At the moment each time {@link SpringCloudFoundryClient} is create a threadpool
* is created by the client and it is never cleaned up. The only way we have
* to mitigate this leak is to try and create as few clients as possible.
* <p>
* So we have a permanent cache of clients here that is reused.
* <p>
* When the bug is fixed then this should no longer be necessary and we can removed this cache
* and just create the client as needed.
*
* @author Kris De Volder
*/
public class CloudFoundryClientCache {
public class CFClientProvider {
final DefaultConnectionContext connection;
final TokenProvider tokenProvider;
//Note: the three client objects below are 'stateless' wrappers and it would be
// fine to recreate as needed instead of store them
final CloudFoundryClient client;
final ReactorUaaClient uaaClient;
final ReactorDopplerClient doppler;
final Mono<GetInfoResponse> info;
private ProxyConfiguration getProxy(String host) {
try {
if (StringUtils.hasText(host)) {
URL url = new URL("https://"+host);
// In certain cases, the activator would have stopped and the plugin may
// no longer be available. Usually onl happens on shutdown.
BootDashActivator plugin = BootDashActivator.getDefault();
if (plugin != null) {
IProxyService proxyService = plugin.getProxyService();
if (proxyService != null) {
IProxyData[] selectedProxies = proxyService.select(url.toURI());
// No proxy configured or not found
if (selectedProxies == null || selectedProxies.length == 0) {
return null;
}
IProxyData data = selectedProxies[0];
int proxyPort = data.getPort();
String proxyHost = data.getHost();
String user = data.getUserId();
String password = data.getPassword();
if (proxyHost!=null) {
return ProxyConfiguration.builder()
.host(proxyHost)
.port(proxyPort==-1?Optional.empty():Optional.of(proxyPort))
.username(Optional.ofNullable(user))
.password(Optional.ofNullable(password))
.build();
// return proxyHost != null ? new HttpProxyConfiguration(proxyHost, proxyPort,
// data.isRequiresAuthentication(), user, password) : null;
}
}
}
}
} catch (Exception e) {
Log.log(e);
}
return null;
}
public CFClientProvider(Params params) {
long sslTimeout = Long.getLong("sts.bootdash.cf.client.ssl.handshake.timeout", 60); //TODO: make a preference for this?
Optional<Boolean> keepAlive = getBooleanSystemProp("http.keepAlive");
debug("cf client keepAlive = "+keepAlive);
connection = DefaultConnectionContext.builder()
.proxyConfiguration(Optional.ofNullable(getProxy(params.host)))
.apiHost(params.host)
.sslHandshakeTimeout(Duration.ofSeconds(sslTimeout))
.keepAlive(keepAlive)
.skipSslValidation(params.skipSsl)
.build();
tokenProvider = createTokenProvider(params);
client = ReactorCloudFoundryClient.builder()
.connectionContext(connection)
.tokenProvider(tokenProvider)
.build();
uaaClient = ReactorUaaClient.builder()
.connectionContext(connection)
.tokenProvider(tokenProvider)
.build();
doppler = ReactorDopplerClient.builder()
.connectionContext(connection)
.tokenProvider(tokenProvider)
.build();
// Cache CF client info - workaround for https://www.pivotaltracker.com/story/show/158741609
info = client.info().get(GetInfoRequest.builder().build()).cache();
}
private TokenProvider createTokenProvider(Params params) {
CFCredentials creds = params.credentials;
switch (creds.getType()) {
case PASSWORD:
return PasswordGrantTokenProvider.builder()
.username(params.username)
.password(creds.getSecret())
.build();
case REFRESH_TOKEN:
return RefreshTokenGrantTokenProvider.builder()
.token(creds.getSecret())
.build();
case TEMPORARY_CODE:
return OneTimePasscodeTokenProvider.builder()
.passcode(creds.getSecret())
.build();
default:
throw new IllegalStateException("BUG! Missing switch case?");
}
}
private Optional<Boolean> getBooleanSystemProp(String name) {
String str = System.getProperty(name);
if (str!=null) {
return Optional.of(Boolean.valueOf(str));
}
return Optional.empty();
}
}
private static final boolean DEBUG = true;
private static void debug(String string) {
if (DEBUG) {
System.out.println(string);
}
}
public static class Params {
public final String username;
public final CFCredentials credentials;
public final String host;
public final boolean skipSsl;
public Params(String username, CFCredentials credentials, String host, boolean skipSsl) {
super();
this.username = username;
this.credentials = credentials;
this.host = host;
this.skipSsl = skipSsl;
}
@Override
public String toString() {
return "Params [username=" + username + ", host=" + host + ", skipSsl=" + skipSsl
+ "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((host == null) ? 0 : host.hashCode());
result = prime * result + ((credentials == null) ? 0 : credentials.hashCode());
result = prime * result + (skipSsl ? 1231 : 1237);
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Params other = (Params) obj;
if (host == null) {
if (other.host != null)
return false;
} else if (!host.equals(other.host))
return false;
if (credentials == null) {
if (other.credentials != null)
return false;
} else if (!credentials.equals(other.credentials))
return false;
if (skipSsl != other.skipSsl)
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
}
private Map<Params, CFClientProvider> cache = new HashMap<>();
private int clientCount = 0;
public synchronized CFClientProvider getOrCreate(String username, CFCredentials credentials, String host, boolean skipSsl) {
Params params = new Params(username, credentials, host, skipSsl);
CFClientProvider client = cache.get(params);
if (client==null) {
clientCount++;
debug("Creating client ["+clientCount+"]: "+params);
cache.put(params, client = create(params));
} else {
debug("Reusing client ["+clientCount+"]: "+params);
}
return client;
}
protected CFClientProvider create(Params params) {
return new CFClientProvider(params);
}
}

View File

@@ -1,32 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFClientParams;
import org.springframework.ide.eclipse.boot.dash.cf.client.ClientRequests;
import org.springframework.ide.eclipse.boot.dash.cf.client.CloudFoundryClientFactory;
public class DefaultCloudFoundryClientFactoryV2 extends CloudFoundryClientFactory {
public static final DefaultCloudFoundryClientFactoryV2 INSTANCE = new DefaultCloudFoundryClientFactoryV2();
/**
* Use 'INSTANCE' constant instead. This class is a singleton.
*/
private DefaultCloudFoundryClientFactoryV2() {}
private CloudFoundryClientCache clientFactory = new CloudFoundryClientCache();
@Override
public ClientRequests getClient(CFClientParams params) {
return new DefaultClientRequestsV2(clientFactory, params);
}
}

View File

@@ -1,289 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.client.v2;
import java.io.IOException;
import java.time.Duration;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import org.eclipse.core.runtime.OperationCanceledException;
import org.reactivestreams.Publisher;
import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens.CancelationToken;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;
/**
* @author Kris De Volder
*/
public class ReactorUtils {
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(45); // reflects default timeout of Mono.block in reactor 2.x.
public static boolean DUMP_STACK_ON_TIMEOUT = false;
/**
* Convert a {@link CancelationToken} into a Mono that raises
* an {@link OperationCanceledException} when the token is canceled.
*/
public static <T> Mono<T> toMono(CancelationToken cancelToken) {
return Mono.delay(Duration.ofSeconds(1))
.flatMap((ping) ->
cancelToken.isCanceled()
? Mono.<T>error(new OperationCanceledException())
: Mono.empty()
)
.repeatWhenEmpty((x) -> x);
}
/**
* Similar to Mono.get but logs a more traceable version of the exception to Eclipse's error
* log before 'rethrowing' it.
* <p>
* This is useful because the actual exception is pretty hard to trace. It doesn't even 'point'
* to the line where 'get' was called.
*/
public static <T> T get(Mono<T> mono) throws Exception {
try {
return mono.block(DEFAULT_TIMEOUT);
} catch (Exception e) {
dumpStacks();
throw new IOException(e);
}
}
/**
* Similar to Mono.get but logs a more traceable version of the exception to Eclipse's error
* log before 'rethrowing' it.
* <p>
* This is useful because the actual exception is pretty hard to trace. It doesn't even 'point'
* to the line where 'get' was called.
*/
public static <T> T get(Duration timeout, CancelationToken cancelationToken, Mono<T> mono) throws Exception {
try {
return Mono.first(mono, toMono(cancelationToken))
.onErrorResume(errorFilter(cancelationToken))
.block(timeout);
} catch (Exception e) {
//dumpStacks();
throw new IOException(e);
}
}
public static <T> List<T> get(Duration t, Mono<List<T>> m) throws IOException {
try {
return m.block(t);
} catch (Exception e) {
dumpStacks();
throw new IOException(e);
}
}
private static void dumpStacks() {
if (DUMP_STACK_ON_TIMEOUT) {
System.out.println(getStackDumps().toString());
}
}
/**
* A 'filter' to use as a Mono.otherwise hanlder. It transforms any exception into {@link OperationCanceledException}
* when cancelationToken has been canceled.
*/
private static <T> Function<Throwable, Mono<T>> errorFilter(CancelationToken cancelationToken) {
return (Throwable e) -> cancelationToken.isCanceled()?Mono.error(new OperationCanceledException()):Mono.error(e);
}
/**
* Deprecated because this is really the same as Mono.justOrEmpty, so use that instead.
*/
@Deprecated
public static <T> Mono<T> just(T it) {
return it == null ? Mono.empty() : Mono.just(it);
}
/**
* @return A function that can be passed to Mono.otherwise to convert a specific exception type into
* Mono.empty().
*/
public static <T> Function<Throwable, Mono<T>> suppressException(Class<? extends Throwable> exceptionType) {
return (Throwable caught) -> {
if (exceptionType.isAssignableFrom(caught.getClass())) {
return Mono.empty();
} else {
return Mono.error(caught);
}
};
}
/**
* Build a Mono<Void> that executes a given number of Mono<Void> one after the
* other.
*/
@SafeVarargs
public static Mono<Void> sequence(Mono<Void>... tasks) {
Mono<Void> seq = Mono.empty();
for (Mono<Void> t : tasks) {
seq = seq.then(t);
}
return seq;
}
/**
* Execute a bunch of mono in parallel. All monos are executed to completion (rather than canceled early
* when one of them fails)
* <p>
* When at least one operation has failed then, upon completion or failure of the last Mono we guarantee that at least
* one of the exceptions is propagated.
*/
public static Mono<Void> safeMerge(Flux<Mono<Void>> operations, int concurrency) {
AtomicReference<Throwable> failure = new AtomicReference<>(null);
return Flux.merge(
operations
.map((Mono<Void> op) -> {
return op.onErrorResume((e) -> {
failure.compareAndSet(null, e);
return Mono.empty();
});
}),
concurrency //limit concurrency otherwise troubles (flooding/choking request broker?)
)
.then(Mono.defer(() -> {
Throwable error = failure.get();
if (error!=null) {
return Mono.error(error);
} else {
return Mono.empty();
}
}));
}
/**
* Attach a timestamp to each element in a Stream
*/
public static <T> Flux<Tuple2<T,Long>> timestamp(Flux<T> stream) {
return stream.map((e) -> Tuples.of(e, System.currentTimeMillis()));
}
/**
* Sorts the elements in a flux in a moving time window. I.e. this assumes element order may be
* scrambled but the scrambling has a certain 'time localilty' to it. So we only need to consider
* sorting of elements that arrive 'close to eachother'.
* <p>
* WARNING: The returned flux is intended for a single subscriber. It only maintains a
* single buffer for sorting stream elements. This buffer is consumed when elements
* are released to any subscriber. Therefore if one subscriber received a element it is gone
* from the buffer and will not be delivered to the other subscribers.
*
* @param stream The stream to be sorted
* @param comparator Compare function to sort with
* @param bufferTime The 'window' of time beyond which we don't need to compare elements.
*/
public static <T> Flux<T> sort(Flux<T> stream, Comparator<T> comparator, Duration bufferTime) {
class SorterAccumulator {
final PriorityQueue<Tuple2<T, Long>> holdingPen = new PriorityQueue<>((Tuple2<T, Long> o1, Tuple2<T, Long> o2) -> {
return comparator.compare(o1.getT1(), o2.getT1());
});
final Flux<T> released = Flux.fromIterable(() -> new Iterator<T>() {
@Override
public boolean hasNext() {
Tuple2<T, Long> nxt;
synchronized (holdingPen) {
nxt = holdingPen.peek();
}
return nxt!=null && isOldEnough(nxt);
}
private boolean isOldEnough(Tuple2<T, Long> nxt) {
long age = System.currentTimeMillis() - nxt.getT2();
return age > bufferTime.toMillis();
}
@Override
public T next() {
synchronized (holdingPen) {
return holdingPen.remove().getT1();
}
}
});
public SorterAccumulator next(Flux<Tuple2<T, Long>> window) {
window.subscribe((e) -> {
synchronized (holdingPen) {
holdingPen.add(e);
}
});
return this;
}
public Flux<T> getReleased() {
return released;
}
public Publisher<? extends T> drain() {
return Flux.fromIterable(holdingPen)
.map(Tuple2::getT1);
}
}
SorterAccumulator sorter = new SorterAccumulator();
return timestamp(stream)
.window(bufferTime)
.scan(sorter, SorterAccumulator::next)
.concatMap(SorterAccumulator::getReleased)
.concatWith(sorter.drain());
}
protected static StringBuffer getStackDumps() {
StringBuffer sb = new StringBuffer();
Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
for (Map.Entry<Thread, StackTraceElement[]> entry : traces.entrySet()) {
sb.append(entry.getKey().toString());
sb.append("\n");
for (StackTraceElement element : entry.getValue()) {
sb.append(" ");
sb.append(element.toString());
sb.append("\n");
}
sb.append("\n");
}
return sb;
}
/**
* Connect a mono to a CompletableFuture so that the result of the mono
* can be retrieved from the {@link CompletableFuture} by calling it's 'get'
* method.
*/
public static <T> void completeWith(CompletableFuture<T> future, Mono<T> mono) {
mono.doOnNext((T v) -> {
future.complete(v);
})
.doOnError((Throwable e) -> {
future.completeExceptionally(e);
})
.subscribeOn(Schedulers.elastic())
.subscribe();
}
}

View File

@@ -1,53 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel;
import org.springsource.ide.eclipse.commons.livexp.ui.Disposable;
/**
* This class is responsible for managing the debug strategies. It is also responsible for managing the life
* cycle of process listeners abd other {@link Disposable}s that may be associated with a debug strategy and
* should be disposed when the manager itself is disposed.
* <p>
*
* @author Kris De Volder
*/
public class DebugStrategyManager implements Disposable {
private DebugSupport strategy;
private Disposable processTracker;
public DebugStrategyManager(List<DebugSupport> list, BootDashViewModel viewModel) {
// At the moment there's only one supported debug strategy, in the future there may be more than one.
// In that case this class may need be generalized to manage a collection of strategies somehow.
Assert.isLegal(list.size()==1);
this.strategy = list.get(0);
this.processTracker = strategy.createProcessTracker(viewModel);
}
@Override
public void dispose() {
if (this.processTracker!=null) {
this.processTracker.dispose();
this.processTracker = null;
}
}
public DebugSupport getStrategy() {
return strategy;
}
}

View File

@@ -1,112 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import java.util.Map;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IProcess;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.cf.ops.Operation;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel;
import org.springframework.ide.eclipse.boot.dash.model.UserInteractions;
import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens.CancelationToken;
import org.springframework.ide.eclipse.boot.util.ProcessListenerAdapter;
import org.springframework.ide.eclipse.boot.util.ProcessTracker;
/**
* Abstract class that must be implemented to add debug support to a CF application.
*
* @author Kris De Volder
*/
public abstract class DebugSupport {
/**
* Determine whether debugging can be supported (using the strategy impemented by this DebugSupport instance)
*/
public abstract boolean isSupported(CloudAppDashElement app);
/**
* If isSupported returns false than the support strategy may also return an explanation why the strategy is not
* supported (e.g. PCF version too old, SSH support disabled etc.)
*/
public abstract String getNotSupportedMessage(CloudAppDashElement app);
/**
* Creates operation that does whatever is needed to get debugger connected to the targetted app.
*/
public abstract Operation<?> createOperation(CloudAppDashElement app, String opName, UserInteractions ui, CancelationToken cancelToken);
/**
* Called to allow debug support to muck around with environment variables so that it can
* do things like add debugging options to 'JAVA_OPTS'.
*/
public abstract void setupEnvVars(Map<String, String> environmentVariables);
/**
* Like setupEnvVars, but called when debugging is disabled. The debug strategy should try to
* undo any changes it made to the env vars to enable debugging.
*/
public abstract void clearEnvVars(Map<String, String> environmentVariables);
/**
* Determines whether debugger is currently attached to the targetted app.
*/
public abstract boolean isDebuggerAttached(CloudAppDashElement app);
/**
* A debug strategy typically involves creating some type of launch that establishes
* a debug connection. To be able to update the debug state in response to changes in
* launches, its necessary to be able to determine what dashboard element corresponds to
* a given launch. Therefore a debug strategy must implement this method.
*
* @return The corresponding CDE for a given launch, or null
*/
public abstract CloudAppDashElement getElementFor(ILaunch l, BootDashViewModel viewModel);
/**
* Provides a process tracker. Subclasses may override if the default implementation is not suitable.
* <p>
* The process tracker is responsible to listen for changes in the Eclipse debug ui so it can make
* bootdash elements 'debug' state update when processes and/or debug targets are created or terminated.
*/
public ProcessTracker createProcessTracker(final BootDashViewModel viewModel) {
return new ProcessTracker(new ProcessListenerAdapter() {
@Override
public void debugTargetCreated(ProcessTracker tracker, IDebugTarget target) {
handleStateChange(target.getLaunch(), "debugTargetCreated");
}
@Override
public void debugTargetTerminated(ProcessTracker tracker, IDebugTarget target) {
handleStateChange(target.getLaunch(), "debugTargetTerminated");
}
@Override
public void processTerminated(ProcessTracker tracker, IProcess process) {
//Typically a debug strategy only needs to care about debugtargets, not IProcesses.
// So nothing to do here.
}
@Override
public void processCreated(ProcessTracker tracker, IProcess process) {
//Typically a debug strategy only needs to care about debugtargets, not IProcesses.
// So nothing to do here.
}
private void handleStateChange(ILaunch l, Object info) {
CloudAppDashElement e = getElementFor(l, viewModel);
if (e!=null) {
BootDashModel model = e.getBootDashModel();
model.notifyElementChanged(e, info);
}
}
});
}
}

View File

@@ -1,73 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel;
import org.springframework.ide.eclipse.boot.dash.model.RunTarget;
import org.springframework.ide.eclipse.boot.launch.LaunchTabSelectionModel;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import com.google.common.collect.ImmutableSet;
public class SelectRunTargetLaunchTabModel extends LaunchTabSelectionModel<CloudFoundryRunTarget> {
// public static SelectProjectLaunchTabModel create() {
// LiveVariable<IProject> project = new LiveVariable<IProject>();
// ExistingBootProjectSelectionValidator validator = new ExistingBootProjectSelectionValidator(project);
// return new SelectProjectLaunchTabModel(project, validator);
// }
private BootDashViewModel context;
public SelectRunTargetLaunchTabModel(LiveVariable<CloudFoundryRunTarget> variable, LiveExpression<ValidationResult> validator, BootDashViewModel context) {
super(variable, validator);
this.context = context;
}
@Override
public void initializeFrom(ILaunchConfiguration conf) {
selection.setValue(SshDebugLaunchConfigurationDelegate.getRunTarget(conf, context));
getDirtyState().setValue(false);
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy conf) {
SshDebugLaunchConfigurationDelegate.setRunTarget(conf, selection.getValue());
getDirtyState().setValue(false);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy conf) {
// if (choices.length>0) {
// SshDebugLaunchConfigurationDelegate.setRunTarget(conf, choices[0]);
// }
}
public CloudFoundryRunTarget[] getChoices() {
ImmutableSet<RunTarget> targets = context.getRunTargets().getValues();
ArrayList<CloudFoundryRunTarget> interesting = new ArrayList<>(targets.size());
for (RunTarget t : targets) {
if (t instanceof CloudFoundryRunTarget) {
interesting.add((CloudFoundryRunTarget) t);
}
}
return interesting.toArray(new CloudFoundryRunTarget[interesting.size()]);
}
}

View File

@@ -1,33 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.util.NameableLabelProvider;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
import org.springframework.ide.eclipse.boot.launch.util.ILaunchConfigurationTabSection;
import org.springsource.ide.eclipse.commons.livexp.ui.ChooseOneSectionCombo;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
/**
* @author Kris De Volder
*/
public class SelectRunTargetLaunchTabSection {
public static ILaunchConfigurationTabSection create(IPageWithSections owner, SelectRunTargetLaunchTabModel model) {
ChooseOneSectionCombo<CloudFoundryRunTarget> ui =
new ChooseOneSectionCombo<CloudFoundryRunTarget>(owner, "Target", model, model.getChoices());
ui.setLabelProvider(new NameableLabelProvider());
return new DelegatingLaunchConfigurationTabSection(owner, model, ui);
}
}

View File

@@ -1,386 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.jdt.internal.launching.LaunchingMessages;
import org.eclipse.jdt.internal.launching.LaunchingPlugin;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IVMConnector;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.osgi.util.NLS;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.client.SshClientSupport;
import org.springframework.ide.eclipse.boot.dash.cf.client.SshHost;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel;
import org.springframework.ide.eclipse.boot.dash.model.RunTarget;
import org.springframework.ide.eclipse.boot.launch.AbstractBootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.util.WaitFor;
import org.springframework.ide.eclipse.boot.util.ProcessListenerAdapter;
import org.springframework.ide.eclipse.boot.util.ProcessTracker;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
@SuppressWarnings("restriction")
public class SshDebugLaunchConfigurationDelegate extends AbstractBootLaunchConfigurationDelegate {
public static final String TYPE_ID = "org.springframework.ide.eclipse.boot.dash.ssh.tunnel.launch";
private static final long DEBUG_CONNECT_TIMEOUT = 20000;
public static final String RUN_TARGET = "ssh.debug.runtarget.id";
public static final String APP_NAME = "ssh.debug.app.name";
public static final String INSTANCE_IDX = "ssh.debug.app.instance";
private static BootDashViewModel getContext() {
//TODO: it may be necessary to allow injecting this, for example via setting a threadlocal,
// to make code more amenable to unit testing.
//This method is here because LaunchConf delegates are created by eclipse debug framework and
// so we can't easily inject a context object into it.
//The only method that should be calling this is 'launch'. Everything else should be doing
// the proper thing and pass in the model as parameter somehow.
return BootDashActivator.getDefault().getModel();
}
@Override
public void launch(ILaunchConfiguration conf, String mode, ILaunch launch, IProgressMonitor mon)
throws CoreException {
conf = configureClassPathProviders(conf);
Assert.isTrue(ILaunchManager.DEBUG_MODE.equals(mode));
BootDashViewModel context = getContext();
mon.beginTask("Establish SSH Debug Connection to "+getAppName(conf)+" on "+getRunTarget(conf, context), 4);
CloudAppDashElement app = null;
try {
CloudFoundryRunTarget target = getRunTarget(conf, context);
SshDebugSupport debugSupport = getDebugSupport(conf, context);
app = getApp(conf, context);
if (app!=null && target!=null && debugSupport.isSupported(app)) {
//1: determine SSH tunnel parameters
app.log("Fetching SSH tunnel parameters...");
SshClientSupport sshInfo = target.getSshClientSupport();
SshHost sshHost = sshInfo.getSshHost();
String sshUser = sshInfo.getSshUser(app.getAppGuid(), getInstanceIndex(conf));
String sshCode = sshInfo.getSshCode();
int remotePort = debugSupport.getRemotePort();
app.log("SSH tunnel parameters:");
app.log(" host: "+sshHost);
app.log(" user: "+sshUser);
app.log(" code: "+sshCode);
app.log(" remote port: "+remotePort);
mon.worked(1);
//2: create tunnel
app.log("Creating tunnel...");
SshTunnel tunnel = new SshTunnelImpl(sshHost, sshUser, sshCode, remotePort, app); //TODO: use SshTunnelFactory?
//3: connect debugger stuff
app.log("Launching remote debug connector...");
launchRemote(tunnel, conf, launch, new SubProgressMonitor(mon, 1));
app.log("Launching remote debug connector... DONE");
}
} catch (Exception e) {
if (app!=null) {
app.log("ERROR: "+ExceptionUtil.getMessage(e));
}
throw ExceptionUtil.coreException(e);
} finally {
mon.done();
}
}
public static int getInstanceIndex(ILaunchConfiguration conf) {
try {
return conf.getAttribute(INSTANCE_IDX, 0);
} catch (Exception e) {
BootDashActivator.log(e);
}
return 0;
}
private SshDebugSupport getDebugSupport(ILaunchConfiguration conf, BootDashViewModel context) {
CloudAppDashElement app = getApp(conf, context);
if (app!=null) {
DebugSupport ds = app.getDebugSupport();
if (ds instanceof DebugSupport) {
return (SshDebugSupport) ds;
}
}
return null;
}
public static String getAppName(ILaunchConfiguration conf) {
return getString(conf, APP_NAME);
}
public static CloudAppDashElement getApp(ILaunchConfiguration conf, BootDashViewModel context) {
String appName = getAppName(conf);
if (appName!=null) {
BootDashModel section = context.getSectionByTargetId(getRunTargetId(conf));
if (section instanceof CloudFoundryBootDashModel) {
CloudFoundryBootDashModel cfmodel = (CloudFoundryBootDashModel) section;
return cfmodel.getApplication(appName);
}
}
return null;
}
public static void setAppName(ILaunchConfigurationWorkingCopy conf, String name) {
if (name!=null) {
conf.setAttribute(APP_NAME, name);
} else {
conf.removeAttribute(APP_NAME);
}
}
public static void setRunTarget(ILaunchConfigurationWorkingCopy conf, CloudFoundryRunTarget target) {
if (target!=null) {
conf.setAttribute(RUN_TARGET, target.getId());
} else {
conf.removeAttribute(RUN_TARGET);
}
}
private static String getRunTargetId(ILaunchConfiguration conf) {
String at = RUN_TARGET;
return getString(conf, at);
}
protected static String getString(ILaunchConfiguration conf, String attName) {
try {
return conf.getAttribute(attName, (String)null);
} catch (CoreException e) {
BootDashActivator.log(e);
}
return null;
}
public static CloudFoundryRunTarget getRunTarget(ILaunchConfiguration conf, BootDashViewModel context) {
try {
String id = conf.getAttribute(RUN_TARGET, (String)null);
if (id!=null) {
RunTarget target = context.getRunTargetById(id);
if (target instanceof CloudFoundryRunTarget) {
return (CloudFoundryRunTarget) target;
}
}
} catch (Exception e) {
BootDashActivator.log(e);
}
return null;
}
/**
* Create debugging target similar to a remote debugging session would and add them to the launch.
* This is to support debugging of the remote boot-app that is reachable over http tunnel
* the client creates. From our side this just as if we are opening a remote debug
* session to the client.
*/
private void launchRemote(final SshTunnel tunnel, ILaunchConfiguration configuration, final ILaunch launch, IProgressMonitor _monitor) throws CoreException {
int port = tunnel.getLocalPort();
final IProgressMonitor monitor = _monitor==null?new NullProgressMonitor():_monitor;
monitor.beginTask(NLS.bind(LaunchingMessages.JavaRemoteApplicationLaunchConfigurationDelegate_Attaching_to__0_____1, new String[]{configuration.getName()}), 3);
// check for cancellation
if (monitor.isCanceled()) {
return;
}
try {
monitor.subTask(LaunchingMessages.JavaRemoteApplicationLaunchConfigurationDelegate_Verifying_launch_attributes____1);
//String connectorId = "org.eclipse.jdt.launching.socketListenConnector";//getVMConnectorId(configuration);
String connectorId = "org.eclipse.jdt.launching.socketAttachConnector";
final IVMConnector connector = JavaRuntime.getVMConnector(connectorId);
if (connector == null) {
abort(LaunchingMessages.JavaRemoteApplicationLaunchConfigurationDelegate_Connector_not_specified_2, null, IJavaLaunchConfigurationConstants.ERR_CONNECTOR_NOT_AVAILABLE);
}
final Map<String, String> argMap = new HashMap<>();
int connectTimeout = Platform.getPreferencesService().getInt(
LaunchingPlugin.ID_PLUGIN,
JavaRuntime.PREF_CONNECT_TIMEOUT,
JavaRuntime.DEF_CONNECT_TIMEOUT,
null);
argMap.put("hostname", "localhost");
argMap.put("timeout", ""+connectTimeout);
argMap.put("port", ""+port);
// check for cancellation
if (monitor.isCanceled()) {
return;
}
monitor.worked(1);
monitor.subTask(LaunchingMessages.JavaRemoteApplicationLaunchConfigurationDelegate_Creating_source_locator____2);
// set the default source locator if required
setDefaultSourceLocator(launch, configuration);
monitor.worked(1);
// connect to remote VM
try {
new WaitFor(DEBUG_CONNECT_TIMEOUT) {
@Override
public void run() throws Exception {
connector.connect(argMap, monitor, launch);
}
};
new ProcessTracker(new ProcessListenerAdapter() {
@Override
public void debugTargetTerminated(ProcessTracker tracker, IDebugTarget target) {
handleTermination(tracker, target.getLaunch());
}
@Override
public void processTerminated(ProcessTracker tracker, IProcess process) {
handleTermination(tracker, process.getLaunch());
}
private void handleTermination(ProcessTracker tracker, ILaunch targetLaunch) {
if (launch.equals(targetLaunch)) {
tracker.dispose();
tunnel.dispose();
}
}
});
} catch (Exception e) {
terminateAllTargets(launch);
throw ExceptionUtil.coreException(e);
}
// check for cancellation
if (monitor.isCanceled()) {
terminateAllTargets(launch);
return;
}
}
finally {
monitor.done();
}
}
public void terminateAllTargets(final ILaunch launch) {
//Note: its better to discconect debugtargets before terminating processes
// because that allows a cleaner disconnect from the debugged process.
// (If the devtools client process is terminated its no longer possible to talk to the
// debugged process).
IDebugTarget[] debugTargets = launch.getDebugTargets();
for (int i = 0; i < debugTargets.length; i++) {
IDebugTarget target = debugTargets[i];
if (target.canDisconnect()) {
try {
target.disconnect();
} catch (Exception e) {
BootActivator.log(e);
}
}
}
IProcess[] processes = launch.getProcesses();
for (IProcess process : processes) {
if (process.canTerminate()) {
try {
process.terminate();
} catch (Exception e) {
BootActivator.log(e);
}
}
}
}
public static ILaunchConfiguration getOrCreateLaunchConfig(CloudAppDashElement app) throws CoreException {
IProject project = app.getProject();
String appName = app.getName();
CloudFoundryRunTarget target = app.getTarget();
Assert.isTrue(project!=null);
ILaunchConfiguration existing = findConfig(project, target, appName);
ILaunchConfigurationWorkingCopy wc;
if (existing!=null) {
return existing;
} else {
wc = createConfiguration(project, target, appName);
return wc.doSave();
}
}
private static ILaunchConfigurationWorkingCopy createConfiguration(IProject project, CloudFoundryRunTarget target, String appName) throws CoreException {
ILaunchConfigurationType configType = getLaunchType();
ILaunchConfigurationWorkingCopy wc = configType.newInstance(null, getLaunchMan().generateLaunchConfigurationName("ssh-tunnel["+appName+"]"));
BootLaunchConfigurationDelegate.setProject(wc, project);
setRunTarget(wc, target);
setAppName(wc, appName);
wc.setMappedResources(new IResource[] {project});
return wc;
}
public static ILaunchConfiguration findConfig(CloudAppDashElement app) {
IProject project = app.getProject();
String appName = app.getName();
CloudFoundryRunTarget target = app.getTarget();
return findConfig(project, target, appName);
}
private static ILaunchConfiguration findConfig(IProject project, CloudFoundryRunTarget target, String appName) {
try {
if (project!=null) {
for (ILaunchConfiguration c : getLaunchMan().getLaunchConfigurations(getLaunchType())) {
if (
project.equals(BootLaunchConfigurationDelegate.getProject(c)) &&
target.getId().equals(getRunTargetId(c)) &&
appName.equals(getAppName(c))
) {
return c;
}
}
}
} catch (CoreException e) {
BootActivator.log(e);
}
return null;
}
public static ILaunchConfigurationType getLaunchType() {
return getLaunchMan().getLaunchConfigurationType(TYPE_ID);
}
public static void doLaunch(CloudAppDashElement app, IProgressMonitor monitor) throws CoreException {
ILaunchConfiguration conf = SshDebugLaunchConfigurationDelegate.getOrCreateLaunchConfig(app);
conf.launch(ILaunchManager.DEBUG_MODE, monitor);
}
}

View File

@@ -1,39 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
import org.eclipse.debug.ui.CommonTab;
import org.eclipse.debug.ui.ILaunchConfigurationDialog;
import org.eclipse.debug.ui.ILaunchConfigurationTab;
import org.eclipse.debug.ui.ILaunchConfigurationTabGroup;
import org.eclipse.debug.ui.sourcelookup.SourceLookupTab;
public class SshDebugLaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup {
/**
* @see ILaunchConfigurationTabGroup#createTabs(ILaunchConfigurationDialog, String)
*/
@Override
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
new SshDebugMainTab(),
//new JavaArgumentsTab(),
//new JavaJRETab(),
//new JavaClasspathTab(),
new SourceLookupTab(),
//new EnvironmentTab(),
new CommonTab()
};
setTabs(tabs);
}
}

View File

@@ -1,122 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import org.eclipse.core.resources.IProject;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel;
import org.springframework.ide.eclipse.boot.launch.ExistingBootProjectSelectionValidator;
import org.springframework.ide.eclipse.boot.launch.SelectProjectLaunchTabModel;
import org.springframework.ide.eclipse.boot.launch.devtools.DevtoolsEnabledValidator;
import org.springframework.ide.eclipse.boot.launch.devtools.StringFieldLaunchTabModel;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
import org.springsource.ide.eclipse.commons.livexp.core.CompositeValidator;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable;
import org.springsource.ide.eclipse.commons.livexp.core.StringFieldModel;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.Validator;
/**
* Model for the 'main type' selection widgetry on a launchconfiguration tab.
* <p>
* Contains the 'logic' for the UI except for the widgets themselves.
* Can be unit tested without having to instantiate launch configuration dialogs
* etc.
*
* @author Kris De Volder
*/
public class SshDebugLaunchUIModel {
public final SelectProjectLaunchTabModel project;
public final SelectRunTargetLaunchTabModel cfTarget;
public final StringFieldLaunchTabModel appName;
private BootDashViewModel context;
public SshDebugLaunchUIModel(BootDashViewModel context) {
this.context = context;
project = createProjectSelectionModel();
cfTarget = createCfTargetModel(context);
appName = createAppNameModel();
}
private SelectRunTargetLaunchTabModel createCfTargetModel(BootDashViewModel context) {
LiveVariable<CloudFoundryRunTarget> variable = new LiveVariable<>();
LiveExpression<ValidationResult> validator = Validator.notNull(variable, "No Target selected");
return new SelectRunTargetLaunchTabModel(variable, validator, context);
}
private StringFieldLaunchTabModel createAppNameModel() {
StringFieldModel field = new StringFieldModel("App Name", "");
field.validator(appNameValidator(project.selection, cfTarget.selection, field.getVariable()));
return new StringFieldLaunchTabModel(field, SshDebugLaunchConfigurationDelegate.APP_NAME);
}
private LiveExpression<ValidationResult> appNameValidator(
final LiveExpression<IProject> projectSelection,
final LiveExpression<CloudFoundryRunTarget> targetSelection,
final LiveExpression<String> appNameSelection
) {
return new Validator() {
{
dependsOn(projectSelection);
dependsOn(appNameSelection);
dependsOn(targetSelection);
}
protected ValidationResult compute() {
CloudFoundryRunTarget target = targetSelection.getValue();
if (target!=null) {
String name = appNameSelection.getValue();
if (!StringUtil.hasText(name)) {
return ValidationResult.error("App Name not set");
}
BootDashModel section = context.getSectionByTargetId(target.getId());
if (section instanceof CloudFoundryBootDashModel) {
CloudFoundryBootDashModel cfSection = (CloudFoundryBootDashModel) section;
CloudAppDashElement app = cfSection.getApplication(name);
if (app==null) {
return ValidationResult.error("There is no app '"+name+"' in '"+target+"'");
}
IProject project = projectSelection.getValue();
IProject appProject = app.getProject();
if (appProject==null) {
// we don't know what project goes with selected app so give the user
// the benefit of the doubdt
// => no error!
} else if (project==null) {
// not this validator's problem. (project validator should check this)
// => no error
} else {
if (!project.equals(appProject)) {
return ValidationResult.error("The app '"+name+"' is bound to a different project ("+appProject.getName()+")");
}
}
}
}
return ValidationResult.OK;
}
};
}
private SelectProjectLaunchTabModel createProjectSelectionModel() {
LiveVariable<IProject> project = new LiveVariable<>();
CompositeValidator validator = new CompositeValidator();
validator.addChild(new ExistingBootProjectSelectionValidator(project));
validator.addChild(new DevtoolsEnabledValidator(project));
return new SelectProjectLaunchTabModel(project, validator);
}
}

View File

@@ -1,52 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import static org.springframework.ide.eclipse.boot.ui.BootUIImages.BOOT_DEVTOOLS_ICON;
import java.util.Arrays;
import java.util.List;
import org.eclipse.swt.graphics.Image;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.launch.SelectProjectLaunchTabSection;
import org.springframework.ide.eclipse.boot.launch.devtools.StringFieldLaunchTabSection;
import org.springframework.ide.eclipse.boot.launch.util.LaunchConfigurationTabWithSections;
import org.springframework.ide.eclipse.boot.ui.BootUIImages;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
/**
* @author Kris De Volder
*/
public class SshDebugMainTab extends LaunchConfigurationTabWithSections implements IPageWithSections {
@Override
public String getName() {
return "SSH Tunnel";
}
@Override
public Image getImage() {
return BootUIImages.getImage(BOOT_DEVTOOLS_ICON);
}
@Override
protected List<IPageSection> createSections() {
SshDebugLaunchUIModel model = new SshDebugLaunchUIModel(BootDashActivator.getDefault().getModel());
return Arrays.asList(new IPageSection[] {
SelectProjectLaunchTabSection.create(this, model.project),
SelectRunTargetLaunchTabSection.create(this, model.cfTarget),
StringFieldLaunchTabSection.create(this, model.appName)
});
}
}

View File

@@ -1,36 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.cf.ops.CloudApplicationOperation;
import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens.CancelationToken;
/**
* @author Kris De Volder
*/
public class SshDebugStartOperation extends CloudApplicationOperation {
private CloudAppDashElement app;
public SshDebugStartOperation(CloudAppDashElement app, DebugSupport debugSupport, CancelationToken cancelationToken) {
super("Starting SSH debugging for app '"+app.getName()+"'", app.getBootDashModel(), app.getName(), cancelationToken);
this.app = app;
}
@Override
protected void doCloudOp(IProgressMonitor monitor) throws Exception, OperationCanceledException {
//TODO: wireup a progress monitor to pass to 'doLaunch' so its aware of cancelation token
SshDebugLaunchConfigurationDelegate.doLaunch(app, monitor);
}
}

View File

@@ -1,154 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import static org.springframework.ide.eclipse.boot.dash.cf.debug.SshDebugLaunchConfigurationDelegate.getApp;
import static org.springframework.ide.eclipse.boot.dash.cf.debug.SshDebugLaunchConfigurationDelegate.getLaunchType;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.model.IDebugTarget;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudAppDashElement;
import org.springframework.ide.eclipse.boot.dash.cf.ops.Operation;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.model.BootDashViewModel;
import org.springframework.ide.eclipse.boot.dash.model.UserInteractions;
import org.springframework.ide.eclipse.boot.dash.util.CancelationTokens.CancelationToken;
import org.springframework.ide.eclipse.boot.launch.util.BootLaunchUtils;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
/**
* Uses ssh tunnelling on Diego to support debugging of app running on CF.
*
* @author Kris De Volder
*/
public class SshDebugSupport extends DebugSupport {
public static final SshDebugSupport INSTANCE = new SshDebugSupport();
private static final int REMOTE_DEBUG_PORT = 47822;
private static final String REMOTE_DEBUG_JVM_ARGS = "-Xdebug -Xrunjdwp:server=y,transport=dt_socket,suspend=n,address="+REMOTE_DEBUG_PORT;
private static final String JAVA_OPTS = "JAVA_OPTS";
private SshDebugSupport() {}
@Override
public boolean isSupported(CloudAppDashElement app) {
String notSupportedMessage = getNotSupportedMessage(app);
return notSupportedMessage==null;
}
@Override
public String getNotSupportedMessage(CloudAppDashElement app) {
//TODO: There are a number of different ways that ssh and/or diego might be disabled for
// an app. e.g. it can be disabled on the app itself, on the space, on the org or
// on the whole CF installation. This check should recognize these situation.
// At the moment it pretty much returns true if the CF has global info about the 'ssh-host',
// but this probably usually the case on any recent enough version of PCF, even if
// ssh has been explicitly disabled.
CloudFoundryRunTarget target = app.getTarget();
try {
if (target.getSshClientSupport().getSshHost()==null) {
return "Cloud controller doesn't specify an ssh-host. This probably means your version of CloudFoundry doesn't support SSH.";
}
return null;
} catch (Exception e) {
BootDashActivator.log(e); //for traceability
String msg = ExceptionUtil.getMessage(e);
if (!StringUtil.hasText(msg)) {
msg = "Exception: "+e.getClass().getName();
}
return msg;
}
}
@Override
public boolean isDebuggerAttached(CloudAppDashElement app) {
ILaunchConfiguration conf = SshDebugLaunchConfigurationDelegate.findConfig(app);
if (conf!=null) {
for (ILaunch l : BootLaunchUtils.getLaunches(conf)) {
if (!l.isTerminated()) {
for (IDebugTarget dt : l.getDebugTargets()) {
if (!dt.isTerminated()) {
//Active debug target found, so debugger is attached.
return true;
}
}
return true;
}
}
}
return false;
}
@Override
public Operation<?> createOperation(CloudAppDashElement app, String opName, UserInteractions ui, CancelationToken cancelationToken) {
return new SshDebugStartOperation(app, this, cancelationToken);
}
@Override
public void setupEnvVars(Map<String, String> env) {
String javaOpts = clearJavaOpts(env.get(JAVA_OPTS));
StringBuilder sb = new StringBuilder(javaOpts);
if (sb.length() > 0) {
sb.append(' ');
}
sb.append(REMOTE_DEBUG_JVM_ARGS);
env.put(JAVA_OPTS, sb.toString());
}
private static String clearJavaOpts(String opts) {
if (opts!=null) {
opts = opts.replaceAll(REMOTE_DEBUG_JVM_ARGS + "\\s*", "");
return opts;
} else {
return "";
}
}
@Override
public void clearEnvVars(Map<String, String> env) {
String jopts = clearJavaOpts(env.get(JAVA_OPTS));
if (StringUtil.hasText(jopts)) {
env.put(JAVA_OPTS, clearJavaOpts(env.get(JAVA_OPTS)));
} else {
env.remove(JAVA_OPTS);
}
}
public int getRemotePort() {
return REMOTE_DEBUG_PORT;
}
@Override
public CloudAppDashElement getElementFor(ILaunch l, BootDashViewModel context) {
try {
ILaunchConfigurationType interestingType = getLaunchType();
ILaunchConfiguration conf = l.getLaunchConfiguration();
if (interestingType.equals(conf.getType())) {
return getApp(conf, context);
}
} catch (CoreException e) {
BootActivator.log(e);
}
return null;
}
}

View File

@@ -1,12 +0,0 @@
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import org.springsource.ide.eclipse.commons.livexp.core.OnDispose;
import org.springsource.ide.eclipse.commons.livexp.ui.Disposable;
public interface SshTunnel extends Disposable, OnDispose {
int getLocalPort();
boolean isDisposed();
}

View File

@@ -1,28 +0,0 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import org.springframework.ide.eclipse.boot.dash.cf.client.SshHost;
import org.springframework.ide.eclipse.boot.dash.util.LogSink;
@FunctionalInterface
public interface SshTunnelFactory {
SshTunnel create(
SshHost sshHost,
String user,
String oneTimeCode,
int remotePort,
LogSink log,
int localPort
) throws Exception;
}

View File

@@ -1,125 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.debug;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.SocketTimeoutException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.springframework.ide.eclipse.boot.dash.cf.client.SshHost;
import org.springframework.ide.eclipse.boot.dash.util.LogSink;
import org.springsource.ide.eclipse.commons.livexp.core.AbstractDisposable;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.keepalive.KeepAliveRunner;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder;
/**
* This class is responsible for creating an ssh tunnel to a remote port. This class implements
* Closeable, its close method must be called to close the tunnel and avoid resource leak.
*/
public class SshTunnelImpl extends AbstractDisposable implements SshTunnel {
private boolean closeRequested = false;
private int localPort;
private SSHClient ssh;
private LocalPortForwarder portForwarder;
public SshTunnelImpl(SshHost sshHost, String user, String oneTimeCode, int remotePort, LogSink log) throws Exception {
this(sshHost, user, oneTimeCode, remotePort, log, 0);
}
public SshTunnelImpl(SshHost sshHost, String user, String oneTimeCode, int remotePort, LogSink log, int _localPort) throws Exception {
DefaultConfig config = new DefaultConfig();
config.setKeepAliveProvider(KeepAliveProvider.KEEP_ALIVE); // Hopefuly this is better at detecting dropped connections from server (e.g. when app is stopped or dies).
ssh = new SSHClient(config);
ssh.addHostKeyVerifier(sshHost.getFingerPrint());
log.log("Ssh client created");
ssh.connect(sshHost.getHost(), sshHost.getPort());
ssh.authPassword(user, oneTimeCode);
KeepAliveRunner keepAlive = (KeepAliveRunner) ssh.getConnection().getKeepAlive();
keepAlive.setKeepAliveInterval(5);
keepAlive.setMaxAliveCount(1);
log.log("Ssh client connected");
ServerSocket ss = new ServerSocket(_localPort);
ss.setSoTimeout(5_000);
localPort = ss.getLocalPort();
Job job = new Job("SshTunnel port forwarding") {
@Override
protected IStatus run(IProgressMonitor arg0) {
final LocalPortForwarder.Parameters params = new LocalPortForwarder.Parameters("0.0.0.0", localPort, "localhost", remotePort);
try {
portForwarder = ssh.newLocalPortForwarder(params, ss);
boolean retry;
do {
retry = false;
try {
portForwarder.listen();
} catch (IOException e) {
if (!closeRequested) {
if (e instanceof SocketTimeoutException) {
//don't log it happens all the time and is expected
} else {
log.log(ExceptionUtil.getMessage(e));
}
retry = true;
}
}
} while (retry);
} finally {
try {
ss.close();
} catch (IOException e) {
}
}
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.schedule();
log.log("Ssh tunnel created: localPort = "+localPort);
}
@Override
synchronized public void dispose() {
if (portForwarder!=null) {
try {
portForwarder.close();
} catch (Exception e) {
}
portForwarder = null;
}
if (ssh!=null) {
try {
ssh.disconnect();
} catch (Exception e) {
}
ssh = null;
}
super.dispose();
}
public int getLocalPort() {
return localPort;
}
}

View File

@@ -1,139 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationPresentation;
import org.eclipse.jface.text.source.ImageUtilities;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
/**
* Annotation for application name. Can be selected and unselected. Based on JDT
* projection annotation
*
* @author Alex Boyko
*
*/
public class AppNameAnnotation extends Annotation implements IAnnotationPresentation {
/**
* The type of CF application name annotations.
*/
public static final String TYPE = "cf.app.name"; //$NON-NLS-1$
private static final int COLOR= SWT.COLOR_GRAY;
/** The state of this annotation */
private boolean fIsSelected= false;
/** Indicates whether this annotation should be painted as range */
private boolean fIsRangeIndication= false;
/**
* Creates a new annotation. When <code>isSelected</code>
* is <code>true</code> the annotation is initially selected.
*
* @param isSelected <code>true</code> if the annotation should initially be selected, <code>false</code> otherwise
*/
public AppNameAnnotation(String text) {
super(TYPE, false, text);
}
/**
* Creates a new annotation.
*
* @param text the app name
* @param selected selected or unselected
*/
public AppNameAnnotation(String text, boolean selected) {
this(text);
fIsSelected = selected;
}
/**
* Enables and disables the range indication for this annotation.
*
* @param rangeIndication the enable state for the range indication
*/
public void setRangeIndication(boolean rangeIndication) {
fIsRangeIndication= rangeIndication;
}
private void drawRangeIndication(GC gc, Canvas canvas, Rectangle r) {
final int MARGIN= 3;
/* cap the height - at least on GTK, large numbers are converted to
* negatives at some point */
int height= Math.min(r.y + r.height - MARGIN, canvas.getSize().y);
gc.setForeground(canvas.getDisplay().getSystemColor(COLOR));
gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
gc.drawLine(r.x + 4, r.y + 12, r.x + 4, height);
gc.drawLine(r.x + 4, height, r.x + r.width - MARGIN, height);
}
/*
* @see org.eclipse.jface.text.source.IAnnotationPresentation#paint(org.eclipse.swt.graphics.GC, org.eclipse.swt.widgets.Canvas, org.eclipse.swt.graphics.Rectangle)
*/
public void paint(GC gc, Canvas canvas, Rectangle rectangle) {
Image image= getImage();
if (image != null) {
ImageUtilities.drawImage(image, gc, canvas, rectangle, SWT.CENTER, SWT.TOP);
if (fIsRangeIndication) {
FontMetrics fontMetrics= gc.getFontMetrics();
int delta= (fontMetrics.getHeight() - image.getBounds().height)/2;
rectangle.y += delta;
rectangle.height -= delta;
drawRangeIndication(gc, canvas, rectangle);
}
}
}
/*
* @see org.eclipse.jface.text.source.IAnnotationPresentation#getLayer()
*/
public int getLayer() {
return IAnnotationPresentation.DEFAULT_LAYER;
}
private Image getImage() {
return isSelected() ? BootDashActivator.getDefault().getImageRegistry().get(BootDashActivator.CHECK_ICON)
: BootDashActivator.getDefault().getImageRegistry().get(BootDashActivator.CHECK_GREYSCALE_ICON);
}
/**
* Returns the state of this annotation.
*
* @return <code>true</code> if collapsed
*/
public boolean isSelected() {
return fIsSelected;
}
/**
* Marks this annotation as being selected.
*/
public void markSelected() {
fIsSelected= true;
}
/**
* Marks this annotation as being unselected.
*/
public void markUnselected() {
fIsSelected= false;
}
}

View File

@@ -1,112 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2019 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import java.util.Iterator;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModel;
/**
* Application Name annotations model
*
* @author Alex Boyko
*
*/
public class AppNameAnnotationModel extends AnnotationModel {
/**
* Annotation model key for attachment to viewer visual annotations model
*/
public static final Object APP_NAME_MODEL_KEY = new Object();
/**
* Constant application name. If not <code>null</code> then corresponding annotation must be selected
*/
public final String fixedAppName;
/**
* Creates a new, empty projection annotation model.
*/
public AppNameAnnotationModel(String fixedAppName) {
this.fixedAppName = fixedAppName;
}
/**
* Marks the given annotation as selected. An appropriate
* annotation model change event is sent out.
*
* @param annotation the annotation
*/
public void markSelected(Annotation annotation) {
if (annotation instanceof AppNameAnnotation) {
AppNameAnnotation appName = (AppNameAnnotation) annotation;
Iterator<?> iterator= getAnnotationIterator();
while(iterator.hasNext()) {
Object o = iterator.next();
if (o instanceof AppNameAnnotation && appName != o) {
AppNameAnnotation a = (AppNameAnnotation) o;
if (a.isSelected()) {
a.markUnselected();
modifyAnnotation(a, true);
}
}
}
if (!appName.isSelected()) {
appName.markSelected();
modifyAnnotation(appName, true);
}
}
}
/**
* Finds the first selected annotation
*
* @return the selected annotation
*/
public AppNameAnnotation getSelectedAppAnnotation() {
Iterator<?> iterator= getAnnotationIterator();
while(iterator.hasNext()) {
Object o = iterator.next();
if (o instanceof AppNameAnnotation) {
AppNameAnnotation a = (AppNameAnnotation) o;
if (a.isSelected()) {
return a;
}
}
}
return null;
}
/**
* Finds application name annotation corresponding to the passed in
* application name text parameter
*
* @param text Application name text
* @return annotation corresponding to the application name text
*/
public AppNameAnnotation getAnnotation(String text) {
if (text != null) {
Iterator<?> iterator= getAnnotationIterator();
while(iterator.hasNext()) {
Object o = iterator.next();
if (o instanceof AppNameAnnotation) {
AppNameAnnotation a = (AppNameAnnotation) o;
if (text.equals(a.getText())) {
return a;
}
}
}
}
return null;
}
}

View File

@@ -1,260 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2019 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import java.util.Iterator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextInputListener;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationPainter;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationHover;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension2;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.custom.StyledTextContent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.RGB;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
/**
* Application name annotation support for the {@link ISourceViewer}
*
* @author Alex Boyko
*
*/
public class AppNameAnnotationSupport {
/**
* Unselected application name annotation line color
*/
private static final RGB APP_NAME_COLOR = new RGB(0xBB, 0xBB, 0xBB);
/**
* Selected application name annotation line color
*/
private static final RGB SELECTED_APP_NAME_COLOR = new RGB(0x00, 0x87, 0x00);
/**
* Alpha value for painting application name annotations line highlighting
* (Must be close to 0 to ensure the text behind is visible enough)
*/
private static final int APP_NAME_ANNOTATION_ALPHA = 0x17;
/**
* Width of the application name annotations vertical ruler control
*/
private static final int ANNOTATION_COLUMN_WIDTH = 12;
/**
* The source viewer
*/
private SourceViewer fViewer = null;
/**
* Application name annotations vertical ruler
*/
private AppNameRulerColumn fColumn = null;
/**
* Constant colors cache (passed in)
*/
private ISharedTextColors fColorsCache;
private final String fixedAppName;
/**
* Listen to viewer input changes to attach the application name annotations model if necessary
*/
private ITextInputListener textInputListener = new ITextInputListener() {
public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
}
@Override
public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
initAppAnnotationModel();
}
};
private void initAppAnnotationModel() {
IAnnotationModel annotationModel = fViewer.getVisualAnnotationModel();
if (annotationModel instanceof IAnnotationModelExtension) {
IAnnotationModelExtension extension = (IAnnotationModelExtension) annotationModel;
if (extension.getAnnotationModel(AppNameAnnotationModel.APP_NAME_MODEL_KEY) == null) {
extension.addAnnotationModel(AppNameAnnotationModel.APP_NAME_MODEL_KEY, new AppNameAnnotationModel(fixedAppName));
}
}
fColumn.setModel(annotationModel);
}
public AppNameAnnotationSupport(SourceViewer viewer, IAnnotationAccess annotationAccess, ISharedTextColors colorsCache, String fixedAppName) {
super();
this.fixedAppName = fixedAppName;
fViewer = viewer;
fViewer.addTextInputListener(textInputListener);
fColorsCache = colorsCache;
fColumn = new AppNameRulerColumn(ANNOTATION_COLUMN_WIDTH, annotationAccess);
fColumn.addAnnotationType(AppNameAnnotation.TYPE);
/*
* Setup tooltip for application name annotations
*/
fColumn.setHover(new IAnnotationHover() {
@Override
public String getHoverInfo(ISourceViewer sourceViewer, int lineNumber) {
for (Iterator<?> itr = fColumn.getModel().getAnnotationIterator(); itr.hasNext();) {
Object o = itr.next();
if (o instanceof AppNameAnnotation) {
AppNameAnnotation a = (AppNameAnnotation) o;
Position p = fColumn.getModel().getPosition(a);
try {
if (fViewer.getDocument().getLineOfOffset(p.getOffset()) <= lineNumber && fViewer.getDocument().getLineOfOffset(p.getOffset() + p.getLength() - 1) >= lineNumber) {
String hoverText = "Application '" + a.getText() + "'";
if (a.isSelected()) {
hoverText += " is selected";
}
return hoverText;
}
} catch (BadLocationException e) {
Log.log(e);
}
}
}
return null;
}
});
/*
* Setup application name line highlight painting on the viewer's text widget
*/
AppNameAnnotationsPainter annotationPainter= new AppNameAnnotationsPainter(fViewer, annotationAccess);
annotationPainter.addDrawingStrategy(AppNameAnnotationModel.APP_NAME_MODEL_KEY, new AppNameDrawingStrategy());
annotationPainter.addAnnotationType(AppNameAnnotation.TYPE, AppNameAnnotationModel.APP_NAME_MODEL_KEY);
annotationPainter.setAnnotationTypeColor(AppNameAnnotation.TYPE, fViewer.getControl().getDisplay().getSystemColor(SWT.COLOR_GRAY));
initAppAnnotationModel();
fViewer.addPainter(annotationPainter);
/*
* Attach application annotations ruler to the viewer
*/
if (fixedAppName == null) {
fViewer.addVerticalRulerColumn(fColumn);
}
}
public void dispose() {
fViewer.removeTextInputListener(textInputListener);
}
/**
* Finds and returns viewer's application name annotations model
*
* @param viewer Source viewer
* @return Viewer's application name annotations model
*/
public static AppNameAnnotationModel getAppNameAnnotationModel(ISourceViewer viewer) {
IAnnotationModel model = viewer instanceof ISourceViewerExtension2
? ((ISourceViewerExtension2) viewer).getVisualAnnotationModel() : viewer.getAnnotationModel();
AppNameAnnotationModel appNameModel = null;
if (model instanceof IAnnotationModelExtension) {
appNameModel = (AppNameAnnotationModel) ((IAnnotationModelExtension) model)
.getAnnotationModel(AppNameAnnotationModel.APP_NAME_MODEL_KEY);
} else if (model instanceof AppNameAnnotationModel) {
appNameModel = (AppNameAnnotationModel) model;
}
return appNameModel;
}
private static class AppNameAnnotationsPainter extends AnnotationPainter {
/**
* Creates a new painter indicating the location of collapsed regions.
*
* @param sourceViewer the source viewer for the painter
* @param access the annotation access
*/
public AppNameAnnotationsPainter(ISourceViewer sourceViewer, IAnnotationAccess access) {
super(sourceViewer, access);
}
/*
* @see org.eclipse.jface.text.source.AnnotationPainter#findAnnotationModel(org.eclipse.jface.text.source.ISourceViewer)
*/
protected IAnnotationModel findAnnotationModel(ISourceViewer sourceViewer) {
return getAppNameAnnotationModel(sourceViewer);
}
/*
* @see org.eclipse.jface.text.source.AnnotationPainter#skip(org.eclipse.jface.text.source.Annotation)
*/
protected boolean skip(Annotation annotation) {
return !(annotation instanceof AppNameAnnotation);
}
}
private class AppNameDrawingStrategy implements AnnotationPainter.IDrawingStrategy {
/*
* @see org.eclipse.jface.text.source.AnnotationPainter.IDrawingStrategy#draw(org.eclipse.swt.graphics.GC, org.eclipse.swt.custom.StyledText, int, int, org.eclipse.swt.graphics.Color)
*/
public void draw(Annotation annotation, GC gc, StyledText textWidget, int offset, int length, Color color) {
if (annotation instanceof AppNameAnnotation) {
AppNameAnnotation a = (AppNameAnnotation) annotation;
StyledTextContent content = textWidget.getContent();
final int line = content.getLineAtOffset(offset);
if (gc == null) {
/*
* Clear off highlighting case
*/
textWidget.setLineBackground(line, 1, null);
textWidget.redrawRange(offset, length, true);
} else {
/*
* Show highlighting case IMPORTANT: do not modify
* 'textWidget' graphical parameters! It would start
* scheduling async updates indefinitely! Hence other parts
* of the UI won't have a chance to repaint themselves
*/
Position p = fColumn.getModel().getPosition(annotation);
if (p != null && line == content.getLineAtOffset(p.getOffset())) {
/*
* Draw transparent line highlight rectangle. Ensure
* it's transparent such that text behind it is visible
*/
Color lineColor = a.isSelected() ? fColorsCache.getColor(SELECTED_APP_NAME_COLOR)
: fColorsCache.getColor(APP_NAME_COLOR);
Color c = gc.getBackground();
int opacity = gc.getAlpha();
gc.setBackground(lineColor);
gc.setAlpha(APP_NAME_ANNOTATION_ALPHA);
gc.fillRectangle(0, textWidget.getLocationAtOffset(offset).y, textWidget.getClientArea().width,
textWidget.getLineHeight());
gc.setAlpha(opacity);
gc.setBackground(c);
}
}
}
}
}
}

View File

@@ -1,239 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2019 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.springframework.ide.eclipse.editor.support.yaml.ast.YamlASTProvider;
import org.springframework.ide.eclipse.editor.support.yaml.ast.YamlFileAST;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
import org.yaml.snakeyaml.nodes.SequenceNode;
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.scanner.ScannerException;
/**
* Reconciler responsible for creating annotation at application names positions
* in the Deployment YAML document
*
* @author Alex Boyko
*
*/
public class AppNameReconciler {
/**
* YAML parser
*/
private YamlASTProvider fParser;
public AppNameReconciler(YamlASTProvider parser) {
fParser = parser;
}
/**
* Re-populates annotation model with app name annotations based on document contents
*
* @param document The YAML document
* @param annotationModel Application Names annotation model
* @param monitor Progress monitor
*/
public void reconcile(IDocument document, AppNameAnnotationModel annotationModel, IProgressMonitor monitor) {
if (annotationModel == null) {
return;
}
List<Annotation> toRemove= new ArrayList<>();
Iterator<? extends Annotation> iter= annotationModel.getAnnotationIterator();
while (iter.hasNext()) {
Annotation annotation= iter.next();
if (AppNameAnnotation.TYPE.equals(annotation.getType())) {
toRemove.add(annotation);
}
}
Annotation[] annotationsToRemove= toRemove.toArray(new Annotation[toRemove.size()]);
/*
* Create brand new annotation to position map based on docs contents
*/
Map<AppNameAnnotation, Position> annotationsToAdd = createAnnotations(document, annotationModel, monitor);
/*
* Update annotation model
*/
if (annotationModel instanceof IAnnotationModelExtension)
((IAnnotationModelExtension)annotationModel).replaceAnnotations(annotationsToRemove, annotationsToAdd);
else {
for (int i= 0; i < annotationsToRemove.length; i++)
annotationModel.removeAnnotation(annotationsToRemove[i]);
for (iter= annotationsToAdd.keySet().iterator(); iter.hasNext();) {
Annotation annotation= iter.next();
annotationModel.addAnnotation(annotation, annotationsToAdd.get(annotation));
}
}
}
/**
* Create new annotation to position mapping based on the document contents
*
* @param annotationModel Application name annotations model
* @return Map of annotations to their corresponding positions
*/
private Map<AppNameAnnotation, Position> createAnnotations(IDocument document, AppNameAnnotationModel annotationModel, IProgressMonitor monitor) {
Map<AppNameAnnotation, Position> annotationsMap = new LinkedHashMap<>();
monitor.beginTask("Calculating application names", 100);
try {
YamlFileAST ast = fParser.getAST(document);
String contents = document.get();
List<Node> rootList = ast.getNodes();
monitor.worked(70);
if (rootList.size() == 1) {
Node root = rootList.get(0);
SequenceNode applicationsNode = YamlGraphDeploymentProperties.getNode(root, ApplicationManifestHandler.APPLICATIONS_PROP, SequenceNode.class);
if (applicationsNode == null) {
/*
* No 'applications' YAML node consider root elements to the deployment properties of an application
*/
ScalarNode node = YamlGraphDeploymentProperties.getPropertyValue(root, ApplicationManifestHandler.NAME_PROP, ScalarNode.class);
if (node != null) {
/*
* There is 'name' property present, so yes root has application deployment props
*/
annotationsMap.put(new AppNameAnnotation(node.getValue(), true),
new Position(root.getStartMark().getIndex(),
getLastWhiteCharIndex(contents, root.getEndMark().getIndex())
- root.getStartMark().getIndex()));
}
} else {
/*
* Go through entries in the 'applications' sequence node
*/
for (Node appNode : applicationsNode.getValue()) {
ScalarNode node = YamlGraphDeploymentProperties.getNode(appNode, ApplicationManifestHandler.NAME_PROP, ScalarNode.class);
if (node != null) {
/*
* Add application name annotation entry
*/
annotationsMap.put(new AppNameAnnotation(node.getValue()), new Position(appNode.getStartMark().getIndex(), getLastWhiteCharIndex(contents, appNode.getEndMark().getIndex()) - appNode.getStartMark().getIndex()));
}
}
}
monitor.worked(20);
if (!annotationsMap.isEmpty()) {
if (annotationModel.fixedAppName == null) {
/*
* Select either previously selected app name annotation or the first found
*/
reselectAnnotation(annotationModel, annotationsMap);
} else {
/*
* Select annotation corresponding to application name == to fAppName
*/
selectAnnotationByAppName(annotationsMap, annotationModel.fixedAppName);
}
monitor.worked(10);
}
}
} catch (ParserException | ScannerException e) {
// Ignore these exceptions as they'd appear as syntax errors in the editor
} catch (Throwable t) {
Log.log(t);
} finally {
monitor.done();
}
return annotationsMap;
}
/**
* Selects annotation from the map corresponding to currently selected
* annotation. Otherwise just selects the first found annotation
*
* @param annotationModel Application name annotations model
* @param annotationsMap Map of application name annotations to positions
*/
private void reselectAnnotation(AppNameAnnotationModel annotationModel, Map<AppNameAnnotation, Position> annotationsMap) {
AppNameAnnotation selected = annotationModel.getSelectedAppAnnotation();
Map.Entry<AppNameAnnotation, Position> newSelected = null;
if (selected != null) {
Position selectedPosition = annotationModel.getPosition(selected);
for (Map.Entry<AppNameAnnotation, Position> entry : annotationsMap.entrySet()) {
/*
* Check if application name matches
*/
if (entry.getKey().getText().equals(selected.getText())) {
/*
* If name matches see if previous match is further away
* from previously selected annotation offset than the
* current match. Update the match accordingly.
*/
if (newSelected == null) {
newSelected = entry;
} else if (Math.abs(newSelected.getValue().getOffset() - selectedPosition.getOffset()) > Math.abs(entry.getValue().getOffset() - selectedPosition.getOffset())){
newSelected = entry;
}
} else if (entry.getValue().getOffset() == selectedPosition.getOffset() && newSelected == null) {
newSelected = entry;
}
}
}
if (newSelected == null) {
/*
* No matches found. Select the first annotation to have something selected.
*/
newSelected = annotationsMap.entrySet().iterator().next();
}
newSelected.getKey().markSelected();
}
/**
* Select annotation matching constant application name, i.e. <code>FAppName</code>
*
* @param annotationsMap Map of application name annotations to positions
*/
private void selectAnnotationByAppName(Map<AppNameAnnotation, Position> annotationsMap, String appName) {
for (Map.Entry<AppNameAnnotation, Position> entry : annotationsMap.entrySet()) {
if (entry.getKey().getText().equals(appName)) {
entry.getKey().markSelected();
return;
}
}
}
/**
* Returns the first 'white' char after a word appearing before the passed index
*
* @param text Text
* @param index Index to start looking from
* @return The first 'white' char position in a string
*/
private static int getLastWhiteCharIndex(String text, int index) {
if (index == text.length()) {
return index;
}
int i = index;
for (; i >= 0 && Character.isWhitespace(text.charAt(i)); i--) {
// Nothing to do
}
// Special case: if non white char is at position 'index' then return value of 'index'
return i == index ? i : i + 1;
}
}

View File

@@ -1,125 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2019 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.reconciler.DirtyRegion;
import org.eclipse.jface.text.reconciler.IReconcilingStrategy;
import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.ISourceViewerExtension2;
import org.springframework.ide.eclipse.editor.support.yaml.ast.YamlASTProvider;
/**
* Reconciling strategy responsible for keeping track of application name
* annotations
*
* @author Alex Boyko
*
*/
public class AppNameReconcilingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension {
/**
* Reconciler for application name annotations
*/
private AppNameReconciler fReconciler;
/**
* Source viewer
*/
private ISourceViewer fViewer;
/**
* Document to perform reconciling on
*/
private IDocument fDocument;
/**
* Reconciling cycle progress monitor
*/
private IProgressMonitor fProgressMonitor;
/**
* Creates new instance of the reconciler
*
* @param viewer Source viewer
* @param parser YAML parser
* @param appName Application name to keep selected all the time
*/
public AppNameReconcilingStrategy(YamlASTProvider parser) {
fReconciler = new AppNameReconciler(parser);
}
/*
* @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#initialReconcile()
*/
public void initialReconcile() {
reconcile(new Region(0, fDocument.getLength()));
}
/*
* @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion,org.eclipse.jface.text.IRegion)
*/
public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
try {
IRegion startLineInfo= fDocument.getLineInformationOfOffset(subRegion.getOffset());
IRegion endLineInfo= fDocument.getLineInformationOfOffset(subRegion.getOffset() + Math.max(0, subRegion.getLength() - 1));
if (startLineInfo.getOffset() == endLineInfo.getOffset())
subRegion= startLineInfo;
else
subRegion= new Region(startLineInfo.getOffset(), endLineInfo.getOffset() + Math.max(0, endLineInfo.getLength() - 1) - startLineInfo.getOffset());
} catch (BadLocationException e) {
subRegion= new Region(0, fDocument.getLength());
}
reconcile(subRegion);
}
private AppNameAnnotationModel getAppNameAnnotationModel() {
IAnnotationModel model = fViewer instanceof ISourceViewerExtension2 ? ((ISourceViewerExtension2)fViewer).getVisualAnnotationModel() : fViewer.getAnnotationModel();
if (model instanceof IAnnotationModelExtension) {
return (AppNameAnnotationModel) ((IAnnotationModelExtension) model).getAnnotationModel(AppNameAnnotationModel.APP_NAME_MODEL_KEY);
}
return (AppNameAnnotationModel) model;
}
@Override
public void reconcile(IRegion region) {
fReconciler.reconcile(fDocument, getAppNameAnnotationModel(), fProgressMonitor);
}
/*
* @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#setProgressMonitor(org.eclipse.core.runtime.IProgressMonitor)
*/
public final void setProgressMonitor(IProgressMonitor monitor) {
fProgressMonitor= monitor;
}
@Override
public void setDocument(IDocument document) {
fDocument= document;
}
public void install(ISourceViewer viewer) {
fViewer = viewer;
}
public void uninstall() {
fViewer = null;
}
}

View File

@@ -1,227 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import java.util.Iterator;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.source.AnnotationRulerColumn;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationAccess;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelExtension;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
/**
* Application name annotations ruler control. Implementation based on
* ProjectionRulerColumn class implementation
*
* @author Alex Boyko
*
*/
public class AppNameRulerColumn extends AnnotationRulerColumn {
/**
* Currently examined/hovered over annotation
*/
private AppNameAnnotation fCurrentAnnotation;
/**
* Line number recorded on mouse down.
*/
private int fMouseDownLine;
public AppNameRulerColumn(IAnnotationModel model, int width, IAnnotationAccess annotationAccess) {
super(model, width, annotationAccess);
}
public AppNameRulerColumn(int width, IAnnotationAccess annotationAccess) {
super(width, annotationAccess);
}
@Override
protected void mouseClicked(int line) {
clearCurrentAnnotation();
if (fMouseDownLine != line)
return;
AppNameAnnotation annotation= findAnnotation(line, true);
if (annotation != null) {
AppNameAnnotationModel model= (AppNameAnnotationModel) getModel();
model.markSelected(annotation);
}
}
@Override
protected void mouseDown(int rulerLine) {
fMouseDownLine= rulerLine;
}
@Override
protected void mouseDoubleClicked(int rulerLine) {
if (findAnnotation(rulerLine, true) != null)
return;
AppNameAnnotation annotation= findAnnotation(rulerLine, false);
if (annotation != null) {
AppNameAnnotationModel model= (AppNameAnnotationModel) getModel();
model.markSelected(annotation);
}
}
/**
* Returns the app's name annotation of the column's annotation
* model that contains the given line.
*
* @param line the line
* @param exact <code>true</code> if the annotation range must match exactly
* @return the app name annotation containing the given line
*/
private AppNameAnnotation findAnnotation(int line, boolean exact) {
AppNameAnnotation previousAnnotation= null;
IAnnotationModel model= getModel();
if (model != null) {
IDocument document= getCachedTextViewer().getDocument();
int previousDistance= Integer.MAX_VALUE;
Iterator<?> e= model.getAnnotationIterator();
while (e.hasNext()) {
Object next= e.next();
if (next instanceof AppNameAnnotation) {
AppNameAnnotation annotation= (AppNameAnnotation) next;
Position p= model.getPosition(annotation);
if (p == null)
continue;
int distance= getDistance(annotation, p, document, line);
if (distance == -1)
continue;
if (!exact) {
if (distance < previousDistance) {
previousAnnotation= annotation;
previousDistance= distance;
}
} else if (distance == 0) {
previousAnnotation= annotation;
}
}
}
}
return previousAnnotation;
}
/**
* Returns the distance of the given line to the start line of the given position in the given document. The distance is
* <code>-1</code> when the line is not included in the given position.
*
* @param annotation the annotation
* @param position the position
* @param document the document
* @param line the line
* @return <code>-1</code> if line is not contained, a position number otherwise
*/
private int getDistance(AppNameAnnotation annotation, Position position, IDocument document, int line) {
if (position.getOffset() > -1 && position.getLength() > -1) {
try {
int startLine= document.getLineOfOffset(position.getOffset());
int endLine= document.getLineOfOffset(position.getOffset() + position.getLength());
if (startLine <= line && line < endLine) {
return line - startLine;
}
} catch (BadLocationException x) {
}
}
return -1;
}
private boolean clearCurrentAnnotation() {
if (fCurrentAnnotation != null) {
fCurrentAnnotation.setRangeIndication(false);
fCurrentAnnotation= null;
return true;
}
return false;
}
@Override
public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
Control control= super.createControl(parentRuler, parentControl);
// set background
Color background= getCachedTextViewer().getTextWidget().getBackground();
control.setBackground(background);
// install hover listener
control.addMouseTrackListener(new MouseTrackAdapter() {
public void mouseExit(MouseEvent e) {
if (clearCurrentAnnotation())
redraw();
}
});
// install mouse move listener
control.addMouseMoveListener(new MouseMoveListener() {
public void mouseMove(MouseEvent e) {
boolean redraw= false;
AppNameAnnotation annotation= findAnnotation(toDocumentLineNumber(e.y), false);
if (annotation != fCurrentAnnotation) {
if (fCurrentAnnotation != null) {
fCurrentAnnotation.setRangeIndication(false);
redraw= true;
}
fCurrentAnnotation= annotation;
if (fCurrentAnnotation != null) {
fCurrentAnnotation.setRangeIndication(true);
redraw= true;
}
}
if (redraw)
redraw();
}
});
return control;
}
@Override
public void setModel(IAnnotationModel model) {
if (!(model instanceof AppNameAnnotationModel) && model instanceof IAnnotationModelExtension) {
IAnnotationModelExtension extension= (IAnnotationModelExtension) model;
model= extension.getAnnotationModel(AppNameAnnotationModel.APP_NAME_MODEL_KEY);
}
super.setModel(model);
}
/*
* @see org.eclipse.jface.text.source.AnnotationRulerColumn#isPropagatingMouseListener()
*/
protected boolean isPropagatingMouseListener() {
return false;
}
/*
* @see org.eclipse.jface.text.source.AnnotationRulerColumn#hasAnnotation(int)
*/
protected boolean hasAnnotation(int lineNumber) {
return findAnnotation(lineNumber, true) != null;
}
}

View File

@@ -1,52 +0,0 @@
/*******************************************************************************
* Copyright (c) 2019 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.reconciler.Reconciler;
import org.eclipse.jface.text.source.ISourceViewer;
import org.springframework.ide.eclipse.editor.support.yaml.ast.YamlASTProvider;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;
/**
* Reconciler for Application names in CF deployment manifest YAML
*
* @author Alex Boyko
*
*/
public class ApplicationNameReconciler extends Reconciler {
private AppNameReconcilingStrategy strategy;
public ApplicationNameReconciler() {
super();
YamlASTProvider parser = new YamlASTProvider(new Yaml(new SafeConstructor()));
strategy= new AppNameReconcilingStrategy(parser);
this.setReconcilingStrategy(strategy, IDocument.DEFAULT_CONTENT_TYPE);
}
@Override
public void install(ITextViewer textViewer) {
super.install(textViewer);
if (textViewer instanceof ISourceViewer) {
strategy.install((ISourceViewer)textViewer);
}
}
@Override
public void uninstall() {
super.uninstall();
strategy.uninstall();
}
}

View File

@@ -1,329 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFApplication;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCloudDomain;
import org.springframework.ide.eclipse.boot.dash.cf.client.v2.CFPushArguments;
import org.springframework.ide.eclipse.boot.dash.cf.client.v2.CFRoute;
import org.springframework.ide.eclipse.boot.dash.cloudfoundry.deployment.DeploymentProperties;
import org.springframework.ide.eclipse.boot.util.JavaProjectUtil;
import com.google.common.collect.ImmutableList;
public class CloudApplicationDeploymentProperties implements DeploymentProperties {
private boolean enableJmxSshTunnel;
private List<String> boundServices;
private Map<String, String> environmentVariables;
private String buildpack;
private List<String> buildpacks;
private int instances;
/*
* URLs should never be null. If no URLs are needed, keep list empty
*/
private LinkedHashSet<String> urls;
private String appName;
public boolean getEnableJmxSshTunnel() {
return enableJmxSshTunnel;
}
public void setEnableJmxSshTunnel(boolean enableJmxSshTunnel) {
this.enableJmxSshTunnel = enableJmxSshTunnel;
}
private IProject project;
private int memory;
private int diskQuota;
private IFile manifestFile;
private Integer timeout;
private String command;
private String stack;
/**
* Path to a zipFile containing the contents of the stuff to deploy.
*/
private File archive;
private String healthCheckType;
private String healthCheckHttpEndpoint;
private boolean randomeRoute = false;
public CloudApplicationDeploymentProperties() {
boundServices = new ArrayList<>();
environmentVariables = new HashMap<>();
buildpack = "";
buildpacks = new ArrayList<>();
instances = DeploymentProperties.DEFAULT_INSTANCES;
urls = new LinkedHashSet<>();
appName = null;
project = null;
memory = DeploymentProperties.DEFAULT_MEMORY;
diskQuota = DeploymentProperties.DEFAULT_MEMORY;
manifestFile = null;
timeout = null;
command = null;
stack = null;
}
public void setProject(IProject project) {
this.project = project;
}
public IProject getProject() {
return project;
}
public void setMemory(int memory) {
this.memory = memory;
}
public int getMemory() {
return memory;
}
public void setDiskQuota(int diskQuota) {
this.diskQuota = diskQuota;
}
public int getDiskQuota() {
return diskQuota;
}
public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
public Integer getTimeout() {
return timeout;
}
public String getHealthCheckType() {
return healthCheckType;
}
public void setHealthCheckType(String healthCheckType) {
this.healthCheckType = healthCheckType;
}
public String getHealthCheckHttpEndpoint() {
return healthCheckHttpEndpoint;
}
public void setHealthCheckHttpEndpoint(String hche) {
this.healthCheckHttpEndpoint = hche;
}
public void setCommand(String command) {
this.command = command;
}
public String getCommand() {
return command;
}
public void setStack(String stack) {
this.stack = stack;
}
public String getStack() {
return stack;
}
public void setManifestFile(IFile file) {
this.manifestFile = file;
}
public IFile getManifestFile() {
return this.manifestFile;
}
/**
* Returns a copy of the list of URLs for the application
*
* @return never null
*/
public Set<String> getUris() {
return urls;
}
public void setUris(Collection<String> urls) {
this.urls = urls == null ? new LinkedHashSet<>() : new LinkedHashSet<>(urls);
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getAppName() {
return this.appName;
}
public void setBuildpack(String buildpack) {
this.buildpack = buildpack;
}
public void setServices(List<String> services) {
/*
* List should be read/write accessible hence create new instance rather
* than use emptyList from Collections if null is passed in
*/
boundServices = services == null ? new ArrayList<>() : services;
}
public void setBuildpacks(List<String> buildpacks) {
this.buildpacks = buildpacks == null ? new ArrayList<>() : buildpacks;
}
public void setInstances(int instances) {
this.instances = instances;
}
public void setRandomRoute(boolean randomRoute) {
this.randomeRoute = randomRoute;
}
public boolean getRandomRoute() {
return this.randomeRoute ;
}
public void setEnvironmentVariables(Map<String, String> environmentVariables) {
/*
* Map should be read/write accessible hence create new instance rather
* than use emptyMap from Collections if null is passed in
*/
this.environmentVariables = environmentVariables == null ? new HashMap<>() : environmentVariables;
}
public String getBuildpack() {
return buildpack == null || buildpack.isEmpty() ? null : buildpack;
}
@Override
public List<String> getBuildpacks() {
return ImmutableList.copyOf(buildpacks);
}
public int getInstances() {
return instances;
}
/**
* @return never null
*/
public Map<String, String> getEnvironmentVariables() {
return environmentVariables;
}
/**
*
* @return never null
*/
public List<String> getServices() {
return boundServices;
}
public static CloudApplicationDeploymentProperties getFor(IProject project, CloudData cloudData, CFApplication app) {
CloudApplicationDeploymentProperties properties = new CloudApplicationDeploymentProperties();
properties.setAppName(app == null ? project.getName() : app.getName());
properties.setProject(project);
properties.setBuildpack(app == null ? cloudData.getBuildpack() : app.getBuildpackUrl());
/*
* TODO: Re-evaluate whether JAVA_OPTS need to be treated differently
* Boot Dash Tooling adds staff to JAVA-OPTS behind the scenes. Consider
* JAVA_OPTS env variable as the one not exposed to users
*/
Map<String, String> env = new LinkedHashMap<>();
if (app != null) {
env.putAll(app.getEnvAsMap());
env.remove("JAVA_OPTS");
}
// PT 174076433 - Support deploying of Java 11 apps to CF
if (JavaProjectUtil.isJava11(project)) {
env.put("JBP_CONFIG_OPEN_JDK_JRE", "{ jre: { version: 11.+}}");
}
properties.setEnvironmentVariables(env);
properties.setInstances(app == null ? 1 : app.getInstances());
properties.setMemory(app == null ? DeploymentProperties.DEFAULT_MEMORY : app.getMemory());
properties.setServices(app == null ? Collections.<String>emptyList() : app.getServices());
properties.setDiskQuota(app == null ? DeploymentProperties.DEFAULT_MEMORY : app.getDiskQuota());
properties.setTimeout(app == null ? null : app.getTimeout());
properties.setHealthCheckType(app==null ? null : app.getHealthCheckType());
properties.setHealthCheckHttpEndpoint(app == null ? null : app.getHealthCheckHttpEndpoint());
properties.setCommand(app == null ? null : app.getCommand());
properties.setStack(app == null ? null : app.getStack());
if (app == null) {
CFRoute route = CFRoute.builder().host(project.getName()).domain(cloudData.getDefaultDomain()).build();
properties.setUris(Collections.singletonList(route.getRoute()));
} else {
properties.setUris(app.getUris());
}
return properties;
}
public CFPushArguments toPushArguments(List<CFCloudDomain> cloudDomains) throws Exception {
Set<String> uris = getUris();
CFPushArguments args = new CFPushArguments();
args.setRoutes(uris);
args.setAppName(getAppName());
args.setMemory(getMemory());
args.setDiskQuota(getDiskQuota());
args.setTimeout(getTimeout());
args.setHealthCheckType(getHealthCheckType());
args.setHealthCheckHttpEndpoint(getHealthCheckHttpEndpoint());
args.setBuildpack(getBuildpack());
args.setCommand(getCommand());
args.setStack(getStack());
args.setEnv(getEnvironmentVariables());
args.setInstances(getInstances());
args.setServices(getServices());
args.setApplicationData(getArchive());
args.setRandomRoute(getRandomRoute());
return args;
}
public File getArchive() {
return archive;
}
public void setArchive(File archive) {
this.archive = archive;
}
}

View File

@@ -1,60 +0,0 @@
/*******************************************************************************
* Copyright (c) 2017 Spring IDE Developers
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import java.util.List;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCloudDomain;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFDomainType;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFStack;
import org.springframework.ide.eclipse.boot.dash.cf.client.v2.CFDomainStatus;
import com.google.common.collect.ImmutableList;
public class CloudData {
private List<CFCloudDomain> domains;
private String buildpack;
private List<CFStack> stacks;
public CloudData(List<CFCloudDomain> domains, String buildpack, List<CFStack> stacks) {
this.domains = domains;
this.buildpack = buildpack;
this.stacks = stacks;
}
public String getBuildpack() {
return buildpack;
}
public List<CFCloudDomain> getDomains() {
if (domains!=null) {
return domains;
}
return ImmutableList.of();
}
public List<CFStack> getStacks() {
if (stacks!=null) {
return stacks;
}
return ImmutableList.of();
}
public String getDefaultDomain() {
return getDomains().stream()
.filter(d -> d.getStatus()==CFDomainStatus.SHARED && d.getType()==CFDomainType.HTTP)
.findFirst()
.map(d -> d.getName())
.orElse(null);
}
}

View File

@@ -1,147 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import org.eclipse.compare.CompareEditorInput;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.DialogSettings;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.dialogs.ManifestDiffDialogModel;
import org.springframework.ide.eclipse.boot.dash.dialogs.ManifestDiffDialogModel.Result;
/**
* Dialog to compare and merge manifest file with deployment properties from CF.
* Hosts eclipse's compare and merge editor composite.
*
* @author Alex Boyko
*
*/
public class ManifestDiffDialog extends TitleAreaDialog {
protected final CompareEditorInput fCompareEditorInput;
private String title = "Merge Manifest File";
/**
* Create a dialog to host the given input.
* @param shell a shell
* @param input the dialog input
*/
public ManifestDiffDialog(Shell shell, ManifestDiffDialogModel model) {
super(shell);
CompareEditorInput input = model.getInput();
setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX);
Assert.isNotNull(input);
fCompareEditorInput= input;
}
public ManifestDiffDialog(Shell shell, ManifestDiffDialogModel model, String title) {
this(shell, model);
this.title = title;
}
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.YES_ID, "Use Manifest", true);
createButton(parent, IDialogConstants.CANCEL_ID, fCompareEditorInput.getCancelButtonLabel(), false);
createButton(parent, IDialogConstants.NO_ID, "Forget Manifest", false);
}
protected Control createDialogArea(Composite parent2) {
Composite parent= (Composite) super.createDialogArea(parent2);
Control c= fCompareEditorInput.createContents(parent);
c.setLayoutData(new GridData(GridData.FILL_BOTH));
Shell shell= c.getShell();
shell.setText(fCompareEditorInput.getTitle());
shell.setImage(fCompareEditorInput.getTitleImage());
applyDialogFont(parent);
return parent;
}
@Override
public void create() {
super.create();
setTitle(title);
if (fCompareEditorInput != null && fCompareEditorInput.getMessage() != null) {
setMessage(fCompareEditorInput.getMessage(), IMessageProvider.WARNING);
} else {
setMessage(
"Manifest file deployment properties are different from current deployment properties on CF. Please merge changes if applicable.",
IMessageProvider.INFORMATION);
}
}
protected void buttonPressed(int buttonId) {
switch (buttonId) {
case IDialogConstants.NO_ID:
fCompareEditorInput.cancelPressed();
setReturnCode(buttonId);
close();
break;
case IDialogConstants.YES_ID:
if (fCompareEditorInput.isDirty()) {
if (!fCompareEditorInput.okPressed())
return;
} else {
fCompareEditorInput.cancelPressed();
}
setReturnCode(buttonId);
close();
break;
case IDialogConstants.CANCEL_ID:
fCompareEditorInput.cancelPressed();
super.buttonPressed(buttonId);
break;
default:
super.buttonPressed(buttonId);
}
}
protected IDialogSettings getDialogBoundsSettings() {
return DialogSettings.getOrCreateSection(BootDashActivator.getDefault().getDialogSettings(), "MergeManifestDialog");
}
/**
* Return the compare editor input for this dialog.
* @return the compare editor input for this dialog
*/
protected final CompareEditorInput getInput() {
return fCompareEditorInput;
}
@Override
protected int getDialogBoundsStrategy() {
return DIALOG_PERSISTSIZE;
}
public static Result getResultForCode(int buttonId) {
switch (buttonId) {
case IDialogConstants.NO_ID:
return Result.FORGET_MANIFEST;
case IDialogConstants.YES_ID:
return Result.USE_MANIFEST;
case IDialogConstants.CANCEL_ID:
return Result.CANCELED;
default:
throw new IllegalArgumentException("Unknown button ID");
}
}
}

View File

@@ -1,73 +0,0 @@
/*******************************************************************************
* Copyright (c) 2018 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.deployment;
import java.util.List;
import org.eclipse.core.runtime.OperationCanceledException;
import org.springframework.ide.eclipse.boot.dash.model.UserInteractions;
import com.google.common.collect.ImmutableList;
public class UnsupportedPushProperties {
public UnsupportedPushProperties() {
}
/**
* Check for unsupported push properties in the given deployment properties. Either allow the
* caller of this check to continue if unsupported properties are found, or
* cancel. If no unsupported properties are found, nothing happens.
*
* @param ui
* @param cde
* @param deploymentProperties
* @throws OperationCanceledException if operation is cancelled
*/
public void allowOrCancelIfFound(UserInteractions ui,
CloudApplicationDeploymentProperties deploymentProperties) throws OperationCanceledException {
List<String> unsupportedProperties = findUnsupportedProperties(deploymentProperties);
if (!unsupportedProperties.isEmpty()) {
StringBuilder builder = new StringBuilder();
builder.append(
"The following properties are not currently supported when pushing an application to Cloud Foundry. These properties will be ignored. Continue with deployment?");
builder.append('\n');
for (String prop : unsupportedProperties) {
builder.append('\n');
builder.append("- ");
builder.append(prop);
}
if (!ui.confirmOperation("Unsupported Push Properties", builder.toString())) {
throw new OperationCanceledException();
}
}
}
/**
* Check the given deployment properties for unsupported properties. Return
* properties found that are unsupported, or empty list if nothing is found.
*
* @param properties
* @return
*/
public List<String> findUnsupportedProperties(CloudApplicationDeploymentProperties properties) {
if (properties != null) {
if (properties.getBuildpacks() != null && !properties.getBuildpacks().isEmpty()) {
return ImmutableList.of("buildpacks");
}
}
return ImmutableList.of();
}
}

View File

@@ -1,473 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.dialogs;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.equinox.security.storage.StorageException;
import org.eclipse.jface.operation.IRunnableContext;
import org.springframework.ide.eclipse.boot.dash.api.RunTargetType;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCredentials;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCredentials.LoginMethod;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFExceptions;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFSpace;
import org.springframework.ide.eclipse.boot.dash.cf.client.ClientRequests;
import org.springframework.ide.eclipse.boot.dash.cf.client.CloudFoundryClientFactory;
import org.springframework.ide.eclipse.boot.dash.cf.ops.Operation;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryTargetProperties;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModelContext;
import org.springframework.ide.eclipse.boot.dash.model.RunTarget;
import org.springframework.ide.eclipse.boot.dash.model.WizardModelUserInteractions;
import org.springframework.ide.eclipse.boot.dash.model.runtargettypes.CannotAccessPropertyException;
import org.springframework.ide.eclipse.boot.dash.model.runtargettypes.TargetProperties;
import org.springframework.ide.eclipse.boot.util.Log;
import org.springsource.ide.eclipse.commons.livexp.core.CompositeValidator;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.Validator;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
import com.google.common.collect.ImmutableSet;
/**
* Cloud Foundry Target properties that uses {@link LiveExpression} and
* {@link Validator}.
*/
public class CloudFoundryTargetWizardModel {
private RunTargetType runTargetType;
private BootDashModelContext context;
private final LiveVariable<String> url = new LiveVariable<>();
private final LiveVariable<CFSpace> space = new LiveVariable<>();
private final LiveVariable<Boolean> selfsigned = new LiveVariable<>(false);
private final LiveVariable<Boolean> skipSslValidation = new LiveVariable<>(false);
private final LiveVariable<LoginMethod> method = new LiveVariable<>(LoginMethod.PASSWORD);
private final LiveVariable<String> userName = new LiveVariable<>();
private final LiveVariable<String> password = new LiveVariable<>();
private final LiveVariable<StoreCredentialsMode> storeCredentials = new LiveVariable<>(StoreCredentialsMode.STORE_NOTHING);
private final LiveVariable<ValidationResult> spaceResolutionStatus = new LiveVariable<>(ValidationResult.OK); // has an error if resolution failed.
private final LiveVariable<OrgsAndSpaces> resolvedSpaces = new LiveVariable<>();
private String refreshToken = null;
private final Validator credentialsValidator = new Validator() {
{
dependsOn(url);
dependsOn(userName);
dependsOn(method);
dependsOn(password);
}
@Override
protected ValidationResult compute() {
if (isEmpty(userName.getValue()) && method.getValue()==LoginMethod.PASSWORD) {
return ValidationResult.info("Enter a username");
} else if (isEmpty(url.getValue())) {
try {
new URL(url.getValue());
return ValidationResult.info("Enter a target URL");
} catch (MalformedURLException e) {
return ValidationResult.error(e.getMessage());
}
} else if (method.getValue()==LoginMethod.PASSWORD) {
if (isEmpty(password.getValue())) {
return ValidationResult.info("Enter a password");
}
} else if (method.getValue()==LoginMethod.TEMPORARY_CODE) {
if (isEmpty(password.getValue())) {
return ValidationResult.info("Enter a Temporary Access Code");
}
}
return ValidationResult.OK;
}
protected boolean isEmpty(String value) {
return value == null || value.trim().length() == 0;
}
};
private final Validator spaceValidator = new Validator() {
{
dependsOn(space);
}
@Override
protected ValidationResult compute() {
if (getSpaceName() == null || getOrganizationName() == null) {
return ValidationResult.info("Select a Cloud space");
}
if (space.getValue() != null) {
RunTarget existing = CloudFoundryTargetWizardModel.this.getExistingRunTarget(space.getValue());
if (existing != null) {
return ValidationResult.error("A run target for that space already exists: '" + existing.getName()
+ "'. Please select another space.");
}
}
return ValidationResult.OK;
}
};
private Validator resolvedSpacesValidator = new Validator() {
{
dependsOn(spaceResolutionStatus);
dependsOn(resolvedSpaces);
}
@Override
protected ValidationResult compute() {
ValidationResult resolveStatus = spaceResolutionStatus.getValue();
if (!resolveStatus.isOk()) {
return resolveStatus;
}
if (resolvedSpaces.getValue() == null || resolvedSpaces.getValue().getAllSpaces() == null) {
return ValidationResult.info("Select a space to validate the credentials.");
}
if (resolvedSpaces.getValue().getAllSpaces().isEmpty()) {
return ValidationResult.error(
"No spaces available to select. Please check that the credentials and target URL are correct, and spaces are defined in the target.");
}
return ValidationResult.OK;
}
};
private Validator storeCredentialsValidator = PasswordDialogModel.makeStoreCredentialsValidator(method, storeCredentials);
private CompositeValidator allPropertiesValidator = new CompositeValidator();
private CloudFoundryClientFactory clientFactory;
private ImmutableSet<RunTarget> existingTargets;
private WizardModelUserInteractions interactions;
public CloudFoundryTargetWizardModel(RunTargetType runTargetType, CloudFoundryClientFactory clientFactory,
ImmutableSet<RunTarget> existingTargets, BootDashModelContext context) {
this(runTargetType, clientFactory, existingTargets, context, null);
}
public CloudFoundryTargetWizardModel(RunTargetType runTargetType, CloudFoundryClientFactory clientFactory,
ImmutableSet<RunTarget> existingTargets, BootDashModelContext context, WizardModelUserInteractions interactions) {
this.runTargetType = runTargetType;
this.context = context;
Assert.isNotNull(clientFactory, "clientFactory should not be null");
this.interactions = interactions;
this.existingTargets = existingTargets == null ? ImmutableSet.<RunTarget>of() : existingTargets;
this.clientFactory = clientFactory;
// Aggregate of the credentials and space validators.
allPropertiesValidator.addChild(credentialsValidator);
allPropertiesValidator.addChild(storeCredentialsValidator);
allPropertiesValidator.addChild(resolvedSpacesValidator);
allPropertiesValidator.addChild(spaceValidator);
}
public void setUrl(String url) {
this.url.setValue(url);
}
public void setSelfsigned(boolean selfsigned) {
this.selfsigned.setValue(selfsigned);
}
public void skipSslValidation(boolean skipSsl) {
this.skipSslValidation.setValue(skipSsl);
}
public void setUsername(String userName) {
this.userName.setValue(userName);
}
public void setPassword(String password) throws CannotAccessPropertyException {
this.password.setValue(password);
}
public void setSpace(CFSpace space) {
this.space.setValue(space);
}
public String getPassword() throws CannotAccessPropertyException {
return password.getValue();
}
public String getUrl() {
return url.getValue();
}
public String getUsername() {
return userName.getValue();
}
public void setStoreCredentials(StoreCredentialsMode store) {
storeCredentials.setValue(store);
}
public StoreCredentialsMode getStoreCredentials() {
return storeCredentials.getValue();
}
public OrgsAndSpaces resolveSpaces(IRunnableContext context) {
try {
boolean toFetchSpaces = true;
OrgsAndSpaces spaces = getCloudSpaces(createTargetProperties(toFetchSpaces), context);
resolvedSpaces.setValue(spaces);
spaceResolutionStatus.setValue(ValidationResult.OK);
return resolvedSpaces.getValue();
} catch (Exception e) {
if (CFExceptions.isAuthFailure(e) || CFExceptions.isSSLCertificateFailure(e)) {
//don't log, its expected if user just typed bad password,
//or didn't check ssl box when they should have.
} else {
Log.log(e);
}
resolvedSpaces.setValue(null);
spaceResolutionStatus.setValue(ValidationResult.error(ExceptionUtil.getMessage(e)));
return null;
}
}
private OrgsAndSpaces getCloudSpaces(final CloudFoundryTargetProperties targetProperties, IRunnableContext context)
throws Exception {
OrgsAndSpaces spaces = null;
Operation<List<CFSpace>> op = new Operation<List<CFSpace>>(
"Connecting to the Cloud Foundry target. Please wait while the list of spaces is resolved...") {
@Override
protected List<CFSpace> runOp(IProgressMonitor monitor) throws Exception, OperationCanceledException {
ClientRequests client = clientFactory.getClient(targetProperties);
try {
List<CFSpace> spaces = client.getSpaces();
String t = client.getRefreshToken();
if (t!=null) {
refreshToken = t;
}
String effectiveUser = getUserName(client);
if (effectiveUser!=null) {
userName.setValue(effectiveUser);
}
return spaces;
} finally {
client.close();
}
}
private String getUserName(ClientRequests client) {
try {
return client.getUserName().block();
} catch (Exception e) {
Log.log(e);
}
return null;
}
};
List<CFSpace> actualSpaces = op.run(context, true);
if (actualSpaces != null && !actualSpaces.isEmpty()) {
spaces = new OrgsAndSpaces(actualSpaces);
}
return spaces;
}
/**
* Create target properties based on current input values in the wizard.
* <p>
* Note that there are two slightly different ways to produce these properties.
* <p>
* a) to create a intermediate client just to fetch orgs and spaces.
* <p>
* b) the final properties used to create the client after space is selected and the user
* clicks 'finish' button.
*/
private CloudFoundryTargetProperties createTargetProperties(boolean toFetchSpaces) throws CannotAccessPropertyException {
CloudFoundryTargetProperties targetProps = new CloudFoundryTargetProperties(null, runTargetType, context.injections);
if (!toFetchSpaces) {
//Take care: when fetching spaces the space may not be known yet, so neither is the id
String id = CloudFoundryTargetProperties.getId(
this.getUsername(),
this.getUrl(),
this.getOrganizationName(),
this.getSpaceName()
);
targetProps.put(TargetProperties.RUN_TARGET_ID, id);
}
targetProps.setUrl(url.getValue());
targetProps.setSelfSigned(selfsigned.getValue());
targetProps.setSkipSslValidation(skipSslValidation.getValue());
targetProps.setUserName(userName.getValue());
if (toFetchSpaces) {
targetProps.setStoreCredentials(StoreCredentialsMode.STORE_NOTHING);
targetProps.setCredentials(CFCredentials.fromLogin(method.getValue(), password.getValue()));
} else {
//use credentials of a style that is consistent with the 'store mode'.
if (method.getValue()==LoginMethod.TEMPORARY_CODE && storeCredentials.getValue()==StoreCredentialsMode.STORE_PASSWORD) {
//The temporary token shouldn't be stored since its meaningless. Silently downgrade storemode:
storeCredentials.setValue(StoreCredentialsMode.STORE_NOTHING);
}
StoreCredentialsMode mode = storeCredentials.getValue();
targetProps.setStoreCredentials(storeCredentials.getValue());
switch (mode) {
case STORE_NOTHING:
case STORE_TOKEN:
Assert.isTrue(refreshToken!=null);
targetProps.setCredentials(CFCredentials.fromRefreshToken(refreshToken));
break;
case STORE_PASSWORD:
targetProps.setCredentials(CFCredentials.fromPassword(password.getValue()));
break;
default:
throw new IllegalStateException("BUG: Missing switch case?");
}
}
targetProps.setSpace(space.getValue());
return targetProps;
}
public OrgsAndSpaces getSpaces() {
return resolvedSpaces.getValue();
}
protected RunTarget getExistingRunTarget(CFSpace space) {
if (space != null) {
String targetId = CloudFoundryTargetProperties.getId(getUsername(), getUrl(),
space.getOrganization().getName(), space.getName());
for (RunTarget target : existingTargets) {
if (targetId.equals(target.getId())) {
return target;
}
}
}
return null;
}
public CloudFoundryRunTarget finish() throws Exception {
CloudFoundryTargetProperties targetProps = null;
try {
targetProps = createTargetProperties(/*toFetchSpaces*/false);
} catch (Exception e) {
final StorageException storageException = getStorageException(e);
// Allow run target to be created on storage exceptions as the run target can still be created and connected
if (storageException != null) {
Log.log(storageException);
if (interactions != null) {
String message = "Failed to store credentials in secure storage. Please check your secure storage preferences. Error: "
+ storageException.getMessage();
interactions.informationPopup("Secure Storage Error", message);
}
storeCredentials.setValue(StoreCredentialsMode.STORE_NOTHING);
targetProps = createTargetProperties(/*toFetchSpaces*/false);
} else {
throw e;
}
}
return (CloudFoundryRunTarget) runTargetType.createRunTarget(targetProps);
}
public String getSpaceName() {
CFSpace space = this.space.getValue();
if (space!=null) {
return space.getName();
}
return null;
}
public String getOrganizationName() {
CFSpace space = this.space.getValue();
if (space!=null) {
return space.getOrganization().getName();
}
return null;
}
protected StorageException getStorageException(Exception e) {
if (e instanceof StorageException) {
return (StorageException) e;
}
if (e.getCause() instanceof StorageException) {
return (StorageException) e.getCause();
}
return null;
}
/**
* @return A 'complete' validator that reflects the validation state of all the inputs in this 'ui'.
*/
public LiveExpression<ValidationResult> getValidator() {
return allPropertiesValidator;
}
public String getRefreshToken() {
return refreshToken;
}
public LiveVariable<LoginMethod> getMethodVar() {
return method;
}
public LiveVariable<String> getUserNameVar() {
return userName;
}
public LiveVariable<String> getPasswordVar() {
return password;
}
public LiveVariable<StoreCredentialsMode> getStoreVar() {
return storeCredentials;
}
public LiveVariable<Boolean> getSkipSslVar() {
return skipSslValidation;
}
public LiveVariable<String> getUrlVar() {
return url;
}
public LiveVariable<CFSpace> getSpaceVar() {
return space;
}
public LiveExpression<Boolean> getEnableSpacesUI() {
return credentialsValidator.apply((r) -> r.isOk());
}
public LiveExpression<Boolean> getEnableUserName() {
return method.apply((method) -> method==LoginMethod.PASSWORD);
}
public LiveExpression<ValidationResult> getSpaceValidator() {
return spaceValidator;
}
public LiveExpression<ValidationResult> getResolvedSpacesValidator() {
return resolvedSpacesValidator;
}
public LiveExpression<ValidationResult> getCredentialsValidator() {
return credentialsValidator;
}
public void setMethod(LoginMethod v) {
method.setValue(v);
}
public Validator getStoreCredentialsValidator() {
return storeCredentialsValidator;
}
}

View File

@@ -1,173 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015, 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.dialogs;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFCredentials.LoginMethod;
import org.springframework.ide.eclipse.boot.dash.cf.runtarget.CloudFoundryRunTarget;
import org.springframework.ide.eclipse.boot.dash.model.RunTarget;
import org.springframework.ide.eclipse.editor.support.util.CollectionUtil;
import org.springsource.ide.eclipse.commons.livexp.core.CompositeValidator;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.ui.CheckboxSection;
import org.springsource.ide.eclipse.commons.livexp.ui.ChooseOneSectionCombo;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.StringFieldSection;
import org.springsource.ide.eclipse.commons.livexp.ui.UIConstants;
import org.springsource.ide.eclipse.commons.livexp.ui.ValidatorSection;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageWithSections;
/**
* Creates a Cloud Foundry target by prompting user for credentials and Cloud
* Foundry target URL.
*
*
*/
public class CloudFoundryTargetWizardPage extends WizardPageWithSections {
private CloudFoundryTargetWizardModel model;
private class SelectSpaceSection extends WizardPageSection {
private CompositeValidator spaceSectionValidator = new CompositeValidator();
{
//Do these two really need to be exposed from the model as separate entities?
// I don't think they are really used separately?
// Keeping it like this for now as it sort of make sense. The ui here has two pieces
// one is a box showing a selected space. And the other a button to resolve spaces.
spaceSectionValidator.addChild(model.getResolvedSpacesValidator());
spaceSectionValidator.addChild(model.getSpaceValidator());
}
public SelectSpaceSection(IPageWithSections owner) {
super(owner);
}
@Override
public void createContents(Composite page) {
Composite buttonComposite = new Composite(page, SWT.NONE);
GridDataFactory.fillDefaults().grab(true, false).applyTo(buttonComposite);
GridLayout layout = GridLayoutFactory.fillDefaults().numColumns(3).margins(0,2).create();
buttonComposite.setLayout(layout);
Label label = new Label(buttonComposite, SWT.NONE);
label.setText("Space:");
GridDataFactory.fillDefaults()
.hint(UIConstants.fieldLabelWidthHint(label), SWT.DEFAULT)
.align(SWT.BEGINNING, SWT.CENTER)
.applyTo(label);
Text spaceValueText = new Text(buttonComposite, SWT.BORDER);
spaceValueText.setEnabled(false);
spaceValueText.setBackground(buttonComposite.getBackground());
GridDataFactory.fillDefaults().grab(true, false).applyTo(spaceValueText);
model.getSpaceVar().addListener((exp, value) -> {
if (spaceValueText != null && !spaceValueText.isDisposed()) {
spaceValueText.setText(value != null ? value.getName() : "");
}
});
Button selectSpaceButton = new Button(buttonComposite, SWT.PUSH);
selectSpaceButton.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false));
selectSpaceButton.setText("Select Space...");
selectSpaceButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
// Fetch an updated list of orgs and spaces in a cancellable
// operation (i.e. the operation
// can be cancelled in the wizard's progress bar)
model.resolveSpaces(getWizard().getContainer());
OrgsAndSpaces spaces = model.getSpaces();
if (spaces != null && CollectionUtil.hasElements(spaces.getAllSpaces())) {
OrgsAndSpacesWizard spacesWizard = new OrgsAndSpacesWizard(model);
WizardDialog dialog = new WizardDialog(getShell(), spacesWizard);
dialog.open();
}
}
});
//Enable the "Select Space" button once credentials are complete.
model.getCredentialsValidator().addListener((exp, value) -> {
if (selectSpaceButton != null && !selectSpaceButton.isDisposed()) {
selectSpaceButton.setEnabled(value.isOk());
}
});
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return spaceSectionValidator;
}
}
private CloudFoundryRunTarget runTarget = null;
public CloudFoundryTargetWizardPage(CloudFoundryTargetWizardModel model) {
super("page1", "Add a Cloud Foundry Target", BootDashActivator.getImageDescriptor("icons/wizban_cloudfoundry.png"));
this.model = model;
setDescription("Enter credentials and a Cloud Foundry target URL.");
}
@Override
protected List<WizardPageSection> createSections() {
List<WizardPageSection> sections = new ArrayList<>();
sections.add(new ChooseOneSectionCombo<>(this, "Method:", model.getMethodVar(), EnumSet.allOf(LoginMethod.class)));
//TODO: hide password or passcode field depending on the method.
sections.add(new StringFieldSection(this, "Email:", model.getUserNameVar())
.setEnabler(model.getEnableUserName()));
sections.add(new StringFieldSection(this, "Password:", model.getPasswordVar()).setPassword(true));
sections.add(UpdatePasswordDialog.storeCredentialsSection(this, model.getStoreVar(), model.getStoreCredentialsValidator()));
sections.add(new StringFieldSection(this, "Url:", model.getUrlVar()));
sections.add(new ValidatorSection(model.getCredentialsValidator(), this));
sections.add(new SelectSpaceSection(this));
sections.add(new CheckboxSection(this, model.getSkipSslVar(), "Skip SSL Validation"));
return sections;
}
/**
* Creates a run target ONCE.
* @return created run target. Returns cached target if already created.
*/
public CloudFoundryRunTarget createRunTarget() {
// Cache to avoid creating run target multiple times in the same wizard session
if (runTarget == null) {
try {
runTarget = model.finish();
} catch (Exception e) {
setErrorMessage(e.getMessage());
}
}
return runTarget;
}
}

View File

@@ -1,45 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.dialogs;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.swt.widgets.Shell;
import org.springsource.ide.eclipse.commons.livexp.ui.ButtonSection;
import org.springsource.ide.eclipse.commons.livexp.ui.DialogWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.StringFieldSection;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
/**
* @author Martin Lippert
*/
public class CustomizeAppsManagerURLDialog extends DialogWithSections {
private CustomizeAppsManagerURLDialogModel model;
public CustomizeAppsManagerURLDialog(CustomizeAppsManagerURLDialogModel model, Shell shell) {
super("Customize Host URL of Apps Manager", model, shell);
this.model = model;
}
@Override
protected List<WizardPageSection> createSections() throws CoreException {
Builder<WizardPageSection> sections = ImmutableList.builder();
sections.add(new StringFieldSection(this, model.host).tooltip(model.getHelpText()));
sections.add(new ButtonSection(this, "Restore Defaults", model.restoreDefaultsHandler));
return sections.build();
}
}

View File

@@ -1,53 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.dialogs;
import java.util.concurrent.Callable;
import org.springframework.ide.eclipse.boot.dash.cf.model.CloudFoundryBootDashModel;
import org.springsource.ide.eclipse.commons.livexp.core.StringFieldModel;
import org.springsource.ide.eclipse.commons.livexp.ui.OkButtonHandler;
/**
* @author Martin Lippert
*/
public class CustomizeAppsManagerURLDialogModel implements OkButtonHandler {
public final StringFieldModel host = new StringFieldModel("Apps Manager", "");
private final CloudFoundryBootDashModel cloudFoundrySection;
public CustomizeAppsManagerURLDialogModel(CloudFoundryBootDashModel cloudFoundrySection) {
this.cloudFoundrySection = cloudFoundrySection;
host.getVariable().setValue(this.cloudFoundrySection.getRunTarget().getAppsManagerHost());
}
public Callable<Void> restoreDefaultsHandler = new Callable<Void>() {
public Void call() throws Exception {
host.getVariable().setValue(getDefaultValue());
return null;
}
};
@Override
public void performOk() throws Exception {
cloudFoundrySection.getRunTarget().setAppsManagerHost(host.getValue());
cloudFoundrySection.notifyModelStateChanged();
}
public String getHelpText() {
return "no help available";
}
public String getDefaultValue() {
return cloudFoundrySection.getRunTarget().getAppsManagerHostDefault();
}
}

View File

@@ -1,705 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.dialogs;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.dialogs.DialogSettings;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.IHandlerActivation;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.model.BaseWorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.springframework.ide.eclipse.boot.dash.BootDashActivator;
import org.springframework.ide.eclipse.boot.dash.cf.dialogs.DeploymentPropertiesDialogModel.ManifestType;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.UIValueListener;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.ValueListener;
import org.springsource.ide.eclipse.commons.livexp.ui.util.SwtConnect;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
/**
* Cloud Foundry Application deployment properties dialog. Allows user to select
* manifest YAML file or enter deployment manifest YAML manually.
*
* @author Alex Boyko
*
*/
public class DeploymentPropertiesDialog extends TitleAreaDialog {
public static final String CONTEXT_DEPLOYMENT_PROPERTIES_DIALOG = "deployment-properties-dialog";
final static private String DIALOG_LIST_HEIGHT_SETTING = "ManifestFileDialog.listHeight"; //$NON-NLS-1$
final static private String YML_EXTENSION = "yml"; //$NON-NLS-1$
final static private String[] FILE_FILTER_NAMES = new String[] {"Manifest YAML files - *manifest*.yml", "YAML files - *.yml", "All files - *.*"};
private static abstract class DeepFileFilter extends ViewerFilter {
@Override
public boolean select(Viewer viewer, Object parent, Object element) {
if (element instanceof IResource && !((IResource)element).isDerived()) {
if (element instanceof IFile) {
return acceptFile((IFile)element);
}
if (element instanceof IContainer) {
try {
IContainer container = (IContainer) element;
for (IResource resource : container.members()) {
boolean select = select(viewer, container, resource);
if (select) {
return true;
}
}
} catch (CoreException e) {
// ignore
}
}
}
return false;
}
abstract protected boolean acceptFile(IFile file);
}
final static private ViewerFilter YAML_FILE_FILTER = new DeepFileFilter() {
@Override
protected boolean acceptFile(IFile file) {
return YML_EXTENSION.equals(file.getFileExtension());
}
};
final static private ViewerFilter MANIFEST_YAML_FILE_FILTER = new DeepFileFilter() {
@Override
protected boolean acceptFile(IFile file) {
return file.getName().toLowerCase().contains("manifest") && YML_EXTENSION.equals(file.getFileExtension());
}
};
final static private ViewerFilter ALL_FILES = new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parent, Object element) {
return (element instanceof IResource) && !((IResource)element).isDerived();
}
};
final static private ViewerFilter[][] RESOURCE_FILTERS = new ViewerFilter[][] {
{MANIFEST_YAML_FILE_FILTER},
{YAML_FILE_FILTER},
{ALL_FILES}
};
final static private int DEFAULT_WORKSPACE_GROUP_HEIGHT = 200;
private Label fileLabel;
private Sash resizeSash;
private TreeViewer workspaceViewer;
private Button refreshButton;
private Button buttonFileManifest;
private Button buttonManualManifest;
private Group fileGroup;
private Group yamlGroup;
private Composite fileYamlComposite;
private Composite manualYamlComposite;
private Combo fileFilterCombo;
private IHandlerService service;
private List<IHandlerActivation> activations;
private EditorActionHandler[] handlers = new EditorActionHandler[] {
new EditorActionHandler(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, SourceViewer.CONTENTASSIST_PROPOSALS),
new EditorActionHandler(IWorkbenchCommandConstants.EDIT_UNDO, SourceViewer.UNDO),
new EditorActionHandler(IWorkbenchCommandConstants.EDIT_REDO, SourceViewer.REDO),
};
private ISelectionChangedListener selectionListener = new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent e) {
IResource resource = (IResource) getStructuredSelection(workspaceViewer).getFirstElement();
model.setSelectedManifest(resource);
}
};
final private DeploymentPropertiesDialogModel model;
private Button buttonEnableJmx;
public DeploymentPropertiesDialog(Shell parentShell, DeploymentPropertiesDialogModel model) {
super(parentShell);
this.model = model;
this.service = (IHandlerService) PlatformUI.getWorkbench().getAdapter(IHandlerService.class);
this.activations = new ArrayList<>(handlers.length);
}
@Override
protected Control createDialogArea(Composite parent) {
setTitle("Select Deployment Manifest for project '" + model.getProjectName() + "'");
Composite container = (Composite) super.createDialogArea(parent);
final Composite composite = new Composite(container, parent.getStyle());
composite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
composite.setLayout(new GridLayout());
createModeSwitchGroup(composite);
createFileGroup(composite);
createResizeSash(composite);
createYamlContentsGroup(composite);
if (model.supportsSsh) {
createJmxSshOptionsGroup(composite);
}
activateHandlers();
model.type.addListener(new ValueListener<ManifestType>() {
@Override
public void gotValue(LiveExpression<ManifestType> exp, ManifestType type) {
GridData gridData;
boolean isFile = type == ManifestType.FILE;
buttonFileManifest.setSelection(isFile);
buttonManualManifest.setSelection(!isFile);
refreshButton.setEnabled(isFile && !workspaceViewer.getSelection().isEmpty());
workspaceViewer.getControl().setEnabled(isFile);
fileLabel.setEnabled(isFile);
gridData = GridDataFactory.copyData((GridData) fileGroup.getLayoutData());
gridData.exclude = !isFile;
fileGroup.setVisible(isFile);
fileGroup.setLayoutData(gridData);
gridData = GridDataFactory.copyData((GridData) resizeSash.getLayoutData());
gridData.exclude = !isFile;
resizeSash.setVisible(isFile);
resizeSash.setLayoutData(gridData);
fileGroup.getParent().layout();
fileYamlComposite.setVisible(isFile);
gridData = GridDataFactory.copyData((GridData) fileYamlComposite.getLayoutData());
gridData.exclude = !isFile;
fileYamlComposite.setLayoutData(gridData);
manualYamlComposite.setVisible(!isFile);
gridData = GridDataFactory.copyData((GridData) manualYamlComposite.getLayoutData());
gridData.exclude = isFile;
manualYamlComposite.setLayoutData(gridData);
yamlGroup.layout();
yamlGroup.getParent().layout();
}
});
model.getValidator().addListener(new UIValueListener<ValidationResult>() {
@Override
protected void uiGotValue(LiveExpression<ValidationResult> exp, ValidationResult value) {
ValidationResult result = exp.getValue();
if (getButton(IDialogConstants.OK_ID) != null) {
getButton(IDialogConstants.OK_ID).setEnabled(result.status != IStatus.ERROR);
}
setMessage(result.msg, result.getMessageProviderStatus());
}
});
parent.pack(true);
/*
* Reveal the selected manifest file in the workspace viewer now when
* controls are created and laid out
*/
if (!workspaceViewer.getSelection().isEmpty()) {
workspaceViewer.setSelection(workspaceViewer.getSelection(), true);
}
return container;
}
private void createJmxSshOptionsGroup(Composite composite) {
Group group = new Group(composite, SWT.NONE);
group.setText("JMX Ssh Tunnel");
group.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
group.setLayout(new GridLayout(2, true));
buttonEnableJmx = new Button(group, SWT.CHECK);
buttonEnableJmx.setText("Enable");
buttonEnableJmx.setToolTipText("Activate JMX on the deployed app and create an SSH tunnel to it so it can be accessed locally.");
SwtConnect.checkbox(buttonEnableJmx, model.enableJmxSshTunnel);
}
private void createModeSwitchGroup(Composite composite) {
Group typeGroup = new Group(composite, SWT.NONE);
typeGroup.setText("Manifest Type");
typeGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
typeGroup.setLayout(new GridLayout(2, true));
buttonFileManifest = new Button(typeGroup, SWT.RADIO);
buttonFileManifest.setText("File");
buttonFileManifest.setSelection(model.isFileManifestType());
buttonFileManifest.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (buttonFileManifest.getSelection()) {
model.setManifestType(ManifestType.FILE);
}
}
});
buttonFileManifest.setLayoutData(GridDataFactory.fillDefaults().create());
buttonManualManifest = new Button(typeGroup, SWT.RADIO);
buttonManualManifest.setText("Manual");
buttonManualManifest.setSelection(model.isManualManifestType());
buttonManualManifest.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (buttonManualManifest.getSelection()) {
model.setManifestType(ManifestType.MANUAL);
}
}
});
buttonManualManifest.setLayoutData(GridDataFactory.fillDefaults().create());
}
private void createFileGroup(Composite composite) {
fileGroup = new Group(composite, SWT.NONE);
fileGroup.setText("Workspace File");
fileGroup.setLayout(new GridLayout(2, false));
int height = DEFAULT_WORKSPACE_GROUP_HEIGHT;
try {
height = getDialogBoundsSettings().getInt(DIALOG_LIST_HEIGHT_SETTING);
} catch (NumberFormatException e) {
// ignore exception
}
fileGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).hint(SWT.DEFAULT, height).create());
workspaceViewer = new TreeViewer(fileGroup);
workspaceViewer.setContentProvider(new BaseWorkbenchContentProvider() {
@Override
public Object[] getChildren(Object element) {
Object[] children = super.getChildren(element);
if (element instanceof IFolder) {
IFolder folder = (IFolder) element;
if (folder.getParent() instanceof IProject) {
if (".settings".equals(folder.getName())) {
List<Object> filtered = new ArrayList<>();
for (Object child : children) {
if (child instanceof IFile) {
// Skip the temporary manifest file for manual mode
IFile f = (IFile) child;
if (DeploymentPropertiesDialogModel.DUMMY_MANUAL_MANIFEST_YML.equals(f.getName())
|| DeploymentPropertiesDialogModel.DUMMY_FILE_MANIFEST_YML.equals(f.getName())) {
continue;
}
}
filtered.add(child);
}
children = filtered.toArray(new Object[filtered.size()]);
}
}
}
if (element instanceof IProject) {
IJavaProject javaProject = JavaCore.create((IProject)element);
if (javaProject != null) {
List<Object> filtered = new ArrayList<>();
for (Object child : children) {
// Filter out obvious output folders
if (child instanceof IFolder) {
IFolder folder = (IFolder) child;
if ("target".equals(folder.getName()) || "bin".equals(folder.getName())) {
continue;
}
}
filtered.add(child);
}
children = filtered.toArray(new Object[filtered.size()]);
}
}
return children;
}
});
workspaceViewer.setLabelProvider(new WorkbenchLabelProvider());
workspaceViewer.setInput(ResourcesPlugin.getWorkspace().getRoot());
workspaceViewer.getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
/*
* Do not set the selection based on manifest file changes outside of UI. Bad. SWT doesn't like it.
*/
if (model.getSelectedManifest() != null) {
workspaceViewer.setSelection(new StructuredSelection(new Object[] { model.getSelectedManifest() }), true);
}
workspaceViewer.addSelectionChangedListener(selectionListener);
Composite fileButtonsComposite = new Composite(fileGroup, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.horizontalSpacing = 0;
layout.verticalSpacing = 0;
fileButtonsComposite.setLayout(layout);
fileButtonsComposite.setLayoutData(GridDataFactory.fillDefaults().create());
refreshButton = new Button(fileButtonsComposite, SWT.PUSH);
refreshButton.setImage(BootDashActivator.getDefault().getImageRegistry().get(BootDashActivator.REFRESH_ICON));
refreshButton.setText("Refresh");
refreshButton.setEnabled(false);
refreshButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
refreshManifests();
}
});
refreshButton.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
fileFilterCombo = new Combo(fileGroup, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);
fileFilterCombo.setItems(FILE_FILTER_NAMES);
int selectionIndex = 0;
IResource manifestFile = model.getSelectedManifest();
if (manifestFile != null) {
selectionIndex = RESOURCE_FILTERS.length - 1;
for (int i = 0; i < RESOURCE_FILTERS.length; i++) {
boolean accept = true;
for (ViewerFilter filter : RESOURCE_FILTERS[i]) {
accept = filter.select(null, null, manifestFile);
if (!accept) {
break;
}
}
if (accept) {
selectionIndex = i;
break;
}
}
}
workspaceViewer.setFilters(RESOURCE_FILTERS[selectionIndex]);
fileFilterCombo.select(selectionIndex);
fileFilterCombo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Remove selection listener to set selection from current pathModel value
workspaceViewer.removeSelectionChangedListener(selectionListener);
workspaceViewer.setFilters(RESOURCE_FILTERS[fileFilterCombo.getSelectionIndex()]);
// Add the selection listener back after the initial value has been set
workspaceViewer.addSelectionChangedListener(selectionListener);
}
});
}
private void createResizeSash(Composite composite) {
resizeSash = new Sash(composite, SWT.HORIZONTAL);
resizeSash.setLayoutData(GridDataFactory.fillDefaults().hint(SWT.DEFAULT, 4).grab(true, false).create());
resizeSash.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
GridData listLayoutData = (GridData) fileGroup.getLayoutData();
int newHeight = listLayoutData.heightHint + e.y - resizeSash.getBounds().y;
if (newHeight < listLayoutData.minimumHeight) {
newHeight = listLayoutData.minimumHeight;
e.doit = false;
}
listLayoutData.heightHint = newHeight;
fileGroup.setLayoutData(listLayoutData);
fileGroup.getParent().layout();
}
});
}
private void createYamlContentsGroup(Composite composite) {
yamlGroup = new Group(composite, SWT.NONE);
yamlGroup.setText("YAML Content");
yamlGroup.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
yamlGroup.setLayout(new GridLayout());
fileYamlComposite = new Composite(yamlGroup, SWT.NONE);
fileYamlComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
GridLayout layout = new GridLayout(3, false);
layout.marginWidth = 0;
fileYamlComposite.setLayout(layout);
fileLabel = new Label(fileYamlComposite, SWT.WRAP);
fileLabel.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.CENTER).span(3, SWT.DEFAULT).create());
model.getFileLabel().addListener(new UIValueListener<String>() {
@Override
protected void uiGotValue(LiveExpression<String> exp, String value) {
if (!fileLabel.isDisposed()) {
fileLabel.setText(exp.getValue());
}
}
});
model.getFileYamlEditor().setContext(CONTEXT_DEPLOYMENT_PROPERTIES_DIALOG);
try {
model.getFileYamlEditor().createControl(fileYamlComposite);
} catch (CoreException e) {
Log.log(e);
}
model.getFileYamlEditor().getViewer().getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 200).create());
model.fileYamlEditorControlCreated();
manualYamlComposite = new Composite(yamlGroup, SWT.NONE);
manualYamlComposite.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
layout = new GridLayout();
layout.marginWidth = 0;
manualYamlComposite.setLayout(layout);
Label manualYamlDescriptionLabel = new Label(manualYamlComposite, SWT.WRAP);
manualYamlDescriptionLabel.setText(model.isManualManifestReadOnly() ? "Preview of the contents of the auto-generated deployment manifest:" : "Edit deployment manifest contents:");
manualYamlDescriptionLabel.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create());
model.getManualYamlEditor().setContext(CONTEXT_DEPLOYMENT_PROPERTIES_DIALOG);
try {
model.getManualYamlEditor().createControl(manualYamlComposite);
} catch (CoreException e) {
Log.log(e);
}
model.getManualYamlEditor().getViewer().getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 200).create());
ProjectionViewer manualYamlViewer = model.getManualYamlEditor().getViewer();
if (model.isManualManifestReadOnly()) {
manualYamlViewer.setEditable(false);
manualYamlViewer.getTextWidget().setCaret(null);
manualYamlViewer.getTextWidget().setCursor(getShell().getDisplay().getSystemCursor(SWT.CURSOR_ARROW));
}
model.manualYamlEditorControlCreated();
/*
* Set the proper Font on the YAML viewers
*/
model.getFileYamlEditor().getViewer().getTextWidget().setFont(JFaceResources.getTextFont());
manualYamlViewer.getTextWidget().setFont(JFaceResources.getTextFont());
}
private void activateHandlers() {
if (service != null) {
for (EditorActionHandler handler : handlers) {
activations.add(service.activateHandler(handler.getActionId(), handler));
}
}
}
private void deactivateHandlers() {
if (service != null) {
for (IHandlerActivation activation : activations) {
service.deactivateHandler(activation);
}
activations.clear();
}
}
private void refreshManifests() {
IResource selectedResource = (IResource) ((IStructuredSelection) workspaceViewer.getSelection()).getFirstElement();
final IResource resourceToRefresh = selectedResource instanceof IFile ? selectedResource.getParent() : selectedResource;
Job job = new Job("Refreshing resources for '" + resourceToRefresh.getName() + "'") {
@Override
protected IStatus run(IProgressMonitor monitor) {
IStatus status = Status.OK_STATUS;
try {
resourceToRefresh.refreshLocal(IResource.DEPTH_INFINITE, monitor);
} catch (CoreException e) {
status = e.getStatus();
}
getParentShell().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
// Remove selection listener to set selection from current pathModel value
workspaceViewer.removeSelectionChangedListener(selectionListener);
workspaceViewer.refresh(resourceToRefresh);
// Add the selection listener back after the initial value has been set
workspaceViewer.addSelectionChangedListener(selectionListener);
}
});
return status;
}
};
job.setRule(resourceToRefresh);
job.schedule();
}
@Override
protected boolean isResizable() {
return true;
}
@Override
protected IDialogSettings getDialogBoundsSettings() {
return DialogSettings.getOrCreateSection(BootDashActivator.getDefault().getDialogSettings(), "ManifestFileDialog");
}
@Override
public boolean close() {
if (getReturnCode() == IDialogConstants.CANCEL_ID) {
model.cancelPressed();
} else {
if (!model.okPressed()) {
return false;
}
}
getDialogBoundsSettings().put(DIALOG_LIST_HEIGHT_SETTING, ((GridData)fileGroup.getLayoutData()).heightHint);
boolean close = super.close();
dispose();
return close;
}
protected void dispose() {
/*
* Deactivate handlers for key bindings
*/
deactivateHandlers();
model.dispose();
}
// TODO: this should be replaced with TreeViewer.getStructuredSelection once we drop support for Eclipse 4.4
// the TreeViewer.getStructuredSelection() API got introduced in Eclipse 4.5
private ITreeSelection getStructuredSelection(TreeViewer treeViewer) {
ISelection selection = treeViewer.getSelection();
if (selection instanceof ITreeSelection) {
return (ITreeSelection) selection;
}
throw new ClassCastException("AbstractTreeViewer should return an instance of ITreeSelection from its getSelection() method."); //$NON-NLS-1$
}
@Override
protected int getDialogBoundsStrategy() {
return DIALOG_PERSISTSIZE;
}
@Override
protected Point getInitialSize() {
Point size = super.getInitialSize();
/*
* If manual mode is selected fileGroup is missing and not accounted in
* the size of the dialog shell. Add its height here manually if dialog
* size was not persisted previously
*/
GridData fileGroupLayoutData = (GridData)fileGroup.getLayoutData();
if (fileGroupLayoutData.exclude) {
try {
/*
* Hack: check if dialog width/height was persisted. If
* persisted then no need to calculate dialog size
*/
getDialogBoundsSettings().getInt("DIALOG_WIDTH");
} catch (NumberFormatException e) {
/*
* Exception is thrown if dialog width/height cannot be read
* from storage
*/
size.y += fileGroupLayoutData.heightHint;
}
}
return size;
}
private class EditorActionHandler extends AbstractHandler {
private String actionId;
private int operationId;
public EditorActionHandler(String actionId, int operationId) {
super();
this.actionId = actionId;
this.operationId = operationId;
}
public String getActionId() {
return actionId;
}
@Override
public Object execute(ExecutionEvent arg0) throws ExecutionException {
ProjectionViewer manualYamlViewer = model.getManualYamlEditor().getViewer();
ProjectionViewer fileYamlViewer = model.getFileYamlEditor().getViewer();
if (manualYamlViewer.isEditable() && manualYamlViewer.getControl().isVisible()
&& manualYamlViewer.getTextWidget().isFocusControl()) {
manualYamlViewer.doOperation(operationId);
} else if (fileYamlViewer.isEditable() && fileYamlViewer.getControl().isVisible()
&& fileYamlViewer.getTextWidget().isFocusControl()) {
fileYamlViewer.doOperation(operationId);
}
return null;
}
}
public static IFile findManifestYamlFile(IProject project) {
if (project == null) {
return null;
}
IFile file = project.getFile("manifest.yml");
if (file.exists()) {
return file;
}
IFile yamlFile = null;
try {
for (IResource r : project.members()) {
if (r instanceof IFile) {
file = (IFile) r;
if (MANIFEST_YAML_FILE_FILTER.select(null, project, file)) {
return file;
} else if (YAML_FILE_FILTER.select(null, project, file)) {
yamlFile = file;
}
}
}
} catch (CoreException e) {
// ignore
}
return yamlFile;
}
}

View File

@@ -1,973 +0,0 @@
/*******************************************************************************
* Copyright (c) 2016, 2020 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.dialogs;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.AnnotationModelEvent;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelListener;
import org.eclipse.jface.text.source.IAnnotationModelListenerExtension;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.internal.genericeditor.ExtensionBasedTextViewerConfiguration;
import org.eclipse.ui.part.FileEditorInput;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFApplication;
import org.springframework.ide.eclipse.boot.dash.cf.deployment.AppNameAnnotation;
import org.springframework.ide.eclipse.boot.dash.cf.deployment.AppNameAnnotationModel;
import org.springframework.ide.eclipse.boot.dash.cf.deployment.AppNameAnnotationSupport;
import org.springframework.ide.eclipse.boot.dash.cf.deployment.ApplicationManifestHandler;
import org.springframework.ide.eclipse.boot.dash.cf.deployment.CloudApplicationDeploymentProperties;
import org.springframework.ide.eclipse.boot.dash.cf.deployment.CloudData;
import org.springframework.ide.eclipse.boot.dash.model.UserInteractions;
import org.springframework.ide.eclipse.boot.launch.properties.EmbeddedEditor;
import org.springsource.ide.eclipse.commons.livexp.core.AbstractDisposable;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.Validator;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.Yaml;
@SuppressWarnings("restriction")
public class DeploymentPropertiesDialogModel extends AbstractDisposable {
public static final String LSP_ERROR_ANNOTATION_TYPE = "org.eclipse.ui.workbench.texteditor.error";
public static final String DUMMY_FILE_MANIFEST_YML = ".file-manifest.yml";
public static final String DUMMY_MANUAL_MANIFEST_YML = ".manual-manifest.yml";
public static final String UNKNOWN_DEPLOYMENT_MANIFEST_TYPE_MUST_BE_EITHER_FILE_OR_MANUAL = "Unknown deployment manifest type. Must be either 'File' or 'Manual'.";
public static final String NO_SUPPORT_TO_DETERMINE_APP_NAMES = "Support for determining application names is unavailable";
public static final String MANIFEST_DOES_NOT_CONTAIN_DEPLOYMENT_PROPERTIES_FOR_APPLICATION_WITH_NAME = "Manifest does not contain deployment properties for application with name ''{0}''.";
public static final String APPLICATION_NAME_NOT_SELECTED = "Application name not selected";
public static final String MANIFEST_DOES_NOT_HAVE_ANY_APPLICATION_DEFINED = "Manifest does not have any application defined.";
public static final String ENTER_DEPLOYMENT_MANIFEST_YAML_MANUALLY = "Enter deployment manifest YAML manually.";
public static final String CURRENT_GENERATED_DEPLOYMENT_MANIFEST = "Current generated deployment manifest.";
public static final String CHOOSE_AN_EXISTING_DEPLOYMENT_MANIFEST_YAML_FILE_FROM_THE_LOCAL_FILE_SYSTEM = "Choose an existing deployment manifest YAML file from the local file system.";
public static final String DEPLOYMENT_MANIFEST_FILE_NOT_SELECTED = "Deployment manifest file not selected.";
public static final String MANIFEST_YAML_ERRORS = "Deployment manifest YAML has errors.";
public static enum ManifestType {
FILE,
MANUAL
}
private UserInteractions ui;
private abstract class AbstractSubModel extends AbstractDisposable {
EmbeddedEditor editor;
AppNameAnnotationSupport appNameAnnotationSupport;
final LiveVariable<Boolean> editorControlCreated;
final LiveVariable<IResource> selectedFile;
final LiveExpression<FileEditorInput> editorInput;
LiveExpression<AppNameAnnotationModel> appNameAnnotationModel;
LiveExpression<IAnnotationModel> resourceAnnotationModel;
LiveExpression<List<String>> applicationNames;
LiveExpression<Boolean> errorsInYaml;
LiveExpression<String> selectedAppName;
/**
* When dialog closed and viewer disposed live expression would still keep the document which will be used to detrmine DeploymentProperties
*/
final LiveExpression<IDocument> document;
protected IFile getFile() {
IResource r = selectedFile.getValue();
return r instanceof IFile ? (IFile) r : null;
}
protected void saveOrDiscardIfNeeded() {
FileEditorInput input = editorInput.getValue();
if (input != null) {
saveOrDiscardIfNeeded(input);
}
}
protected void saveOrDiscardIfNeeded(FileEditorInput file) {
if (editor.isDirty()) {
if (ui.confirmOperation("Changes Detected", "Manifest file '" + file.getFile().getFullPath().toOSString()
+ "' has been changed. Do you want to save changes or discard them?", new String[] {"Save", "Don't Save"}, 0) == 0) {
editor.doSave(new NullProgressMonitor());
}
}
}
abstract String getManifestContents();
/**
* Return manifest from which contents are takes as an {@link IFile}
* Return null if manifest content doesn't come from a file
* @return
*/
abstract IFile getManifest();
CloudApplicationDeploymentProperties getDeploymentProperties() throws Exception {
CloudApplicationDeploymentProperties deploymentProperties = toDeploymentProperties(cloudData,
getManifestContents(), project,
deployedApp == null ? selectedAppName.getValue() : getDeployedAppName());
if (deploymentProperties != null) {
deploymentProperties.setManifestFile(getManifest());
}
return deploymentProperties;
}
protected AbstractSubModel(String fixedAppName) {
IPreferenceStore preferenceStore = JavaPlugin.getDefault().getCombinedPreferenceStore();
this.editor = new EmbeddedEditor(editor -> new ExtensionBasedTextViewerConfiguration(editor, preferenceStore), preferenceStore, false, true);
editorControlCreated = new LiveVariable<>(false);
selectedFile = new LiveVariable<>();
editorControlCreated.addListener((exp, value) -> {
if (appNameAnnotationSupport == null && value != null && value.booleanValue()) {
appNameAnnotationSupport = new AppNameAnnotationSupport(editor.getViewer(),
editor.getAnnotationAccess(), editor.getSharedColors(), fixedAppName);
}
});
editorInput = new LiveExpression<FileEditorInput>() {
{
dependsOn(selectedFile);
}
@Override
protected FileEditorInput compute() {
if (editor != null) {
IFile file = getFile();
FileEditorInput currentInput = getValue();
boolean changed = currentInput == null || !currentInput.getFile().equals(file);
if (changed) {
if (currentInput != null) {
saveOrDiscardIfNeeded(currentInput);
}
try {
if (file != null) {
FileEditorInput input = new FileEditorInput(file);
editor.setInput(input);
return input;
} else {
editor.setInput(null);
return null;
}
} catch (CoreException e) {
Log.log(e);
}
}
}
return null;
}
};
appNameAnnotationModel = new LiveExpression<AppNameAnnotationModel>() {
{
dependsOn(editorControlCreated);
dependsOn(editorInput);
}
@Override
protected AppNameAnnotationModel compute() {
if (editor != null && editor.getViewer() != null) {
return AppNameAnnotationSupport.getAppNameAnnotationModel(editor.getViewer());
}
return null;
}
};
resourceAnnotationModel = new LiveExpression<IAnnotationModel>() {
{
dependsOn(editorControlCreated);
dependsOn(editorInput);
}
@Override
protected IAnnotationModel compute() {
if (editor != null && editor.getViewer() != null) {
return editor.getViewer().getAnnotationModel();
}
return null;
}
};
applicationNames = new LiveExpression<List<String>>() {
private AppNameAnnotationModel attachedTo = null;
private AnnotationModelListener listener = new AnnotationModelListener() {
@Override
public void modelChanged(AnnotationModelEvent event) {
refresh();
}
};
{
dependsOn(appNameAnnotationModel);
}
@Override
protected List<String> compute() {
AppNameAnnotationModel annotationModel = appNameAnnotationModel.getValue();
if (annotationModel != null) {
attachListener(annotationModel);
List<String> applicationNames = new ArrayList<>();
for (Iterator<Annotation> itr = annotationModel.getAnnotationIterator(); itr.hasNext();) {
Annotation next = itr.next();
if (next instanceof AppNameAnnotation) {
AppNameAnnotation a = (AppNameAnnotation) next;
applicationNames.add(a.getText());
}
}
return applicationNames;
}
return Collections.emptyList();
}
synchronized private void attachListener(AppNameAnnotationModel annotationModel) {
if (attachedTo == annotationModel) {
return;
}
if (attachedTo != null) {
attachedTo.removeAnnotationModelListener(listener);
}
if (annotationModel != null) {
annotationModel.addAnnotationModelListener(listener);
}
attachedTo = annotationModel;
}
};
errorsInYaml = new LiveExpression<Boolean>() {
private IAnnotationModel attachedTo = null;
private AnnotationModelListener listener = new AnnotationModelListener() {
@Override
public void modelChanged(AnnotationModelEvent event) {
refresh();
}
};
{
dependsOn(resourceAnnotationModel);
}
{
onDispose((d) -> {
if (attachedTo != null) {
attachedTo.removeAnnotationModelListener(listener);
}
});
}
@Override
protected Boolean compute() {
IAnnotationModel annotationModel = resourceAnnotationModel.getValue();
if (annotationModel != null) {
attachListener(annotationModel);
for (Iterator<Annotation> itr = annotationModel.getAnnotationIterator(); itr.hasNext();) {
Annotation next = itr.next();
if (LSP_ERROR_ANNOTATION_TYPE.equals(next.getType())) {
return Boolean.TRUE;
}
}
}
return Boolean.FALSE;
}
synchronized private void attachListener(IAnnotationModel annotationModel) {
if (attachedTo == annotationModel) {
return;
}
if (attachedTo != null) {
attachedTo.removeAnnotationModelListener(listener);
}
if (annotationModel != null) {
annotationModel.addAnnotationModelListener(listener);
}
attachedTo = annotationModel;
}
};
selectedAppName = new LiveExpression<String>() {
private AppNameAnnotationModel attachedTo = null;
private AnnotationModelListener listener = new AnnotationModelListener() {
@Override
public void modelChanged(AnnotationModelEvent event) {
refresh();
}
};
{
dependsOn(appNameAnnotationModel);
}
{
onDispose((d) -> {
if (attachedTo != null) {
attachedTo.removeAnnotationModelListener(listener);
}
});
}
@Override
protected String compute() {
AppNameAnnotationModel annotationModel = appNameAnnotationModel.getValue();
if (annotationModel != null) {
attachListener(annotationModel);
AppNameAnnotation a = annotationModel.getSelectedAppAnnotation();
if (a != null) {
return a.getText();
}
}
return null;
}
synchronized private void attachListener(AppNameAnnotationModel annotationModel) {
if (attachedTo == annotationModel) {
return;
}
if (attachedTo != null) {
attachedTo.removeAnnotationModelListener(listener);
}
if (annotationModel != null) {
annotationModel.addAnnotationModelListener(listener);
}
attachedTo = annotationModel;
}
};
document = new LiveExpression<IDocument>(new Document("")) {
{
dependsOn(editorInput);
dependsOn(editorControlCreated);
}
@Override
protected IDocument compute() {
IDocument doc = editor == null || editor.getViewer() == null ? null : editor.getViewer().getDocument();
return doc;
}
};
onDispose((d) -> {
appNameAnnotationSupport.dispose();
editorControlCreated.dispose();
editorInput.dispose();
applicationNames.dispose();
appNameAnnotationModel.dispose();
errorsInYaml.dispose();
resourceAnnotationModel.dispose();
selectedAppName.dispose();
document.dispose();
editor.dispose();
});
}
}
public class FileDeploymentPropertiesDialogModel extends AbstractSubModel {
final private LiveExpression<String> fileLabel = new LiveExpression<String>() {
{
dependsOn(editorInput);
}
@Override
protected String compute() {
FileEditorInput input = editorInput.getValue();
if (input != null) {
return editorInput.getValue().getFile().getFullPath().toOSString() + (editor.isDirty() ? "*" : "");
}
return "";
}
};
private final IPropertyListener editorListener = (Object source, int propId) -> {
if (propId == IEditorPart.PROP_DIRTY) {
fileLabel.refresh();
}
};
Validator validator = new Validator() {
{
dependsOn(editorInput);
dependsOn(appNameAnnotationModel);
dependsOn(errorsInYaml);
dependsOn(applicationNames);
dependsOn(selectedAppName);
}
@Override
protected ValidationResult compute() {
ValidationResult result = ValidationResult.OK;
if (editorInput.getValue() == null) {
result = ValidationResult.error(DEPLOYMENT_MANIFEST_FILE_NOT_SELECTED);
}
if (result.isOk()) {
AppNameAnnotationModel appNamesModel = appNameAnnotationModel.getValue();
if (appNamesModel == null) {
result = ValidationResult.error(NO_SUPPORT_TO_DETERMINE_APP_NAMES);
}
if (result.isOk()) {
String appName = getDeployedAppName();
if (applicationNames.getValue().isEmpty()) {
result = ValidationResult.error(MANIFEST_DOES_NOT_HAVE_ANY_APPLICATION_DEFINED);
} else {
if (errorsInYaml.getValue().booleanValue()) {
result = ValidationResult.error(MANIFEST_YAML_ERRORS);
} else {
String selectedAnnotation = selectedAppName.getValue();
if (appName == null) {
if (selectedAnnotation == null) {
result = ValidationResult.error(APPLICATION_NAME_NOT_SELECTED);
}
} else {
if (selectedAnnotation == null || !appName.equals(selectedAnnotation)) {
result = ValidationResult.error(MessageFormat.format(
MANIFEST_DOES_NOT_CONTAIN_DEPLOYMENT_PROPERTIES_FOR_APPLICATION_WITH_NAME,
appName));
}
}
}
}
}
}
if (result.isOk()) {
result = ValidationResult.info(CHOOSE_AN_EXISTING_DEPLOYMENT_MANIFEST_YAML_FILE_FROM_THE_LOCAL_FILE_SYSTEM);
}
return result;
}
};
private IFile tempFile;
{
onDispose((d) -> {
if (tempFile != null && tempFile.exists()) {
try {
tempFile.delete(true, new NullProgressMonitor());
} catch (CoreException e) {
Log.log(e);
}
}
editor.removePropertyListener(editorListener);
validator.dispose();
fileLabel.dispose();
});
}
FileDeploymentPropertiesDialogModel(String fixedAppName) {
super(fixedAppName);
editor.addPropertyListener(editorListener);
}
public void init(IFile tempFile) {
this.tempFile = tempFile;
if (getManifest() == null) {
// No manifest file? Generate dumb empty manifest YAML to get proper Manifest LS
// based reconciler, CA etc
try {
generateTempManifestFile(tempFile, "");
editor.init(null, new FileEditorInput(tempFile));
} catch (CoreException e) {
Log.log(e);
}
}
}
public IAnnotationModel getAnnotationModel() {
return editor.getViewer().getAnnotationModel();
}
@Override
String getManifestContents() {
return document.getValue().get();
}
@Override
IFile getManifest() {
return getFile();
}
void reopenSameFile() {
document.refresh();
}
}
public class ManualDeploymentPropertiesDialogModel extends AbstractSubModel {
private IFile tempFile;
private boolean readOnly;
Validator validator = new Validator() {
{
dependsOn(appNameAnnotationModel);
dependsOn(errorsInYaml);
dependsOn(applicationNames);
dependsOn(selectedAppName);
}
@Override
protected ValidationResult compute() {
ValidationResult result = ValidationResult.OK;
AppNameAnnotationModel appNamesModel = appNameAnnotationModel.getValue();
if (appNamesModel == null) {
result = ValidationResult.error(NO_SUPPORT_TO_DETERMINE_APP_NAMES);
}
if (result.isOk()) {
String appName = getDeployedAppName();
if (applicationNames.getValue().isEmpty()) {
result = ValidationResult.error(MANIFEST_DOES_NOT_HAVE_ANY_APPLICATION_DEFINED);
} else {
if (errorsInYaml.getValue().booleanValue()) {
result = ValidationResult.error(MANIFEST_YAML_ERRORS);
} else {
String selectedAnnotation = selectedAppName.getValue();
if (appName == null) {
if (selectedAnnotation == null) {
result = ValidationResult.error(APPLICATION_NAME_NOT_SELECTED);
}
} else {
if (selectedAnnotation == null || !appName.equals(selectedAnnotation)) {
result = ValidationResult.error(MessageFormat.format(
MANIFEST_DOES_NOT_CONTAIN_DEPLOYMENT_PROPERTIES_FOR_APPLICATION_WITH_NAME,
appName));
}
}
}
}
}
if (result.isOk()) {
result = ValidationResult.info(readOnly ? CURRENT_GENERATED_DEPLOYMENT_MANIFEST : ENTER_DEPLOYMENT_MANIFEST_YAML_MANUALLY);
}
return result;
}
};
{
onDispose((d) -> {
validator.dispose();
try {
if (tempFile != null) {
if (tempFile.exists()) {
tempFile.delete(true, new NullProgressMonitor());
}
tempFile = null;
}
} catch (CoreException e) {
e.printStackTrace();
}
});
}
ManualDeploymentPropertiesDialogModel(String fixedAppName, boolean readOnly) {
super(fixedAppName);
this.readOnly = readOnly;
}
public void init(IFile tempFile) {
this.tempFile = tempFile;
try {
generateTempManifestFile(tempFile, generateDefaultContent());
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
editor.init(null, new FileEditorInput(tempFile));
selectedFile.setValue(tempFile);
} catch (PartInitException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void setText(String s) {
if (readOnly) {
throw new IllegalStateException("The model is read-only!");
}
IDocument doc = document.getValue();
if (doc != null) {
doc.set(s);
}
}
public String getText() {
IDocument doc = document.getValue();
return doc == null ? null : doc.get();
}
public IAnnotationModel getAnnotationModel() {
return editor.getViewer().getAnnotationModel();
}
@Override
String getManifestContents() {
return getText();
}
@Override
IFile getManifest() {
return null;
}
}
private abstract class AnnotationModelListener implements IAnnotationModelListener, IAnnotationModelListenerExtension {
@Override
public void modelChanged(IAnnotationModel model) {
// Leave empty. AnnotationModelEvent method is the one that will be called
}
@Override
abstract public void modelChanged(AnnotationModelEvent event);
}
final public LiveVariable<ManifestType> type = new LiveVariable<>();
final public LiveVariable<Boolean> enableJmxSshTunnel = new LiveVariable<>();
final private CFApplication deployedApp;
final private CloudData cloudData;
final IProject project;
final private FileDeploymentPropertiesDialogModel fileModel;
final private ManualDeploymentPropertiesDialogModel manualModel;
private boolean isCancelled = false;
final private Validator validator;
final public boolean supportsSsh;
public DeploymentPropertiesDialogModel(UserInteractions ui, CloudData cloudData, IProject project, CFApplication deployedApp, boolean supportsSsh) {
super();
this.supportsSsh = supportsSsh;
this.ui = ui;
this.deployedApp = deployedApp;
this.cloudData = cloudData;
this.project = project;
this.manualModel = new ManualDeploymentPropertiesDialogModel(getDeployedAppName(), deployedApp != null);
this.fileModel = new FileDeploymentPropertiesDialogModel(getDeployedAppName());
this.validator = new Validator() {
{
dependsOn(type);
dependsOn(fileModel.validator);
dependsOn(manualModel.validator);
}
@Override
protected ValidationResult compute() {
if (isFileManifestType()) {
return fileModel.validator.getValue();
} else if (isManualManifestType()) {
return manualModel.validator.getValue();
} else {
return ValidationResult.error(UNKNOWN_DEPLOYMENT_MANIFEST_TYPE_MUST_BE_EITHER_FILE_OR_MANUAL);
}
}
};
onDispose((d) -> {
fileModel.dispose();
manualModel.dispose();
});
}
public void initFileModel() {
fileModel.init(project.getFolder(".settings").getFile(DUMMY_FILE_MANIFEST_YML));
}
public void initManualModel() {
manualModel.init(project.getFolder(".settings").getFile(DUMMY_MANUAL_MANIFEST_YML));
}
public CloudApplicationDeploymentProperties getDeploymentProperties() throws Exception {
if (isCancelled) {
throw new OperationCanceledException();
}
if (type.getValue() == null) {
return null;
}
CloudApplicationDeploymentProperties props = null;
switch (type.getValue()) {
case FILE:
props = fileModel.getDeploymentProperties();
break;
case MANUAL:
props = manualModel.getDeploymentProperties();
break;
default:
}
if (props!=null) {
Boolean enableJmx = enableJmxSshTunnel.getValue();
props.setEnableJmxSshTunnel(enableJmx!=null && enableJmx);
}
return props;
}
public void cancelPressed() {
fileModel.saveOrDiscardIfNeeded();
isCancelled = true;
// try {
// fileModel.editor.setInput(null);
// manualModel.editor.setInput(null);
// } catch (CoreException e) {
// Log.log(e);
// }
}
public boolean okPressed() {
fileModel.saveOrDiscardIfNeeded();
isCancelled = false;
try {
CloudApplicationDeploymentProperties deploymentProperties = getDeploymentProperties();
// fileModel.editor.setInput(null);
// manualModel.editor.setInput(null);
return deploymentProperties != null;
} catch (Exception e) {
fileModel.reopenSameFile();
ui.errorPopup("Invalid YAML content", ExceptionUtil.getMessage(e));
return false;
}
}
public void setSelectedManifest(IResource manifest) {
fileModel.selectedFile.setValue(manifest);
}
public void setManualManifest(String manifestText) {
manualModel.setText(manifestText);
}
public void setManifestType(ManifestType type) {
this.type.setValue(type);
}
public String getProjectName() {
return project.getName();
}
public IProject getProject() {
return project;
}
public boolean isFileManifestType() {
return type.getValue() == ManifestType.FILE;
}
public boolean isManualManifestType() {
return type.getValue() == ManifestType.MANUAL;
}
public IResource getSelectedManifest() {
return fileModel.selectedFile.getValue();
}
public String getDeployedAppName() {
return deployedApp == null ? null : deployedApp.getName();
}
public EmbeddedEditor getFileYamlEditor() {
return fileModel.editor;
}
public EmbeddedEditor getManualYamlEditor() {
return manualModel.editor;
}
public IDocument getManualDocument() {
return manualModel.document.getValue();
}
public boolean isManualManifestReadOnly() {
return deployedApp!=null;
}
public IDocument getFileDocument() {
return fileModel.document.getValue();
}
public LiveExpression<FileEditorInput> getFileEditorInput() {
return fileModel.editorInput;
}
public LiveExpression<String> getFileLabel() {
return fileModel.fileLabel;
}
private CFApplication getDeployedApp() {
return deployedApp;
}
public boolean isCanceled() {
return isCancelled;
}
public Validator getValidator() {
return validator;
}
public LiveExpression<AppNameAnnotationModel> getManualAppNameAnnotationModel() {
return manualModel.appNameAnnotationModel;
}
public LiveExpression<AppNameAnnotationModel> getFileAppNameAnnotationModel() {
return fileModel.appNameAnnotationModel;
}
public void fileYamlEditorControlCreated() {
fileModel.editorControlCreated.setValue(true);
}
public void manualYamlEditorControlCreated() {
manualModel.editorControlCreated.setValue(true);
}
public IAnnotationModel getManualResourceAnnotationModel() {
return manualModel.resourceAnnotationModel.getValue();
}
public IAnnotationModel getFileResourceAnnotationModel() {
return fileModel.resourceAnnotationModel.getValue();
}
public String getFileSelectedAppName() {
return fileModel.selectedAppName.getValue();
}
public String getManualSelectedAppName() {
return manualModel.selectedAppName.getValue();
}
private String generateDefaultContent() {
CloudApplicationDeploymentProperties props = CloudApplicationDeploymentProperties.getFor(project, cloudData,
getDeployedApp());
Map<Object, Object> yaml = ApplicationManifestHandler.toYaml(props, cloudData);
DumperOptions options = new DumperOptions();
options.setExplicitStart(true);
options.setCanonical(false);
options.setPrettyFlow(true);
options.setDefaultFlowStyle(FlowStyle.BLOCK);
return new Yaml(new SafeConstructor(), new Representer(options), options).dump(yaml);
}
private static IFile generateTempManifestFile(IFile manifestFile, String content) throws CoreException {
if (manifestFile.exists()) {
manifestFile.setContents(new ByteArrayInputStream(content.getBytes()), true, false, new NullProgressMonitor());
} else {
if (!manifestFile.getParent().exists()) {
((IFolder)manifestFile.getParent()).create(true, true, new NullProgressMonitor());
}
manifestFile.create(new ByteArrayInputStream(content.getBytes()), true, new NullProgressMonitor());
}
return manifestFile;
}
public static CloudApplicationDeploymentProperties toDeploymentProperties(CloudData cloudData, String yaml, IProject project, String applicationName) throws Exception {
List<CloudApplicationDeploymentProperties> propsList = new ApplicationManifestHandler(project, cloudData, null) {
@Override
protected InputStream getInputStream() throws Exception {
return new ByteArrayInputStream(yaml.getBytes());
}
}.load(new NullProgressMonitor());
/*
* If "Select Manifest..." action is invoked appName is not null,
* but we should allow for any manifest file selected for now. Hence
* set the applicationName var to null in that case
*/
CloudApplicationDeploymentProperties deploymentProperties = null;
if (applicationName == null) {
deploymentProperties = propsList.get(0);
} else {
for (CloudApplicationDeploymentProperties p : propsList) {
if (applicationName.equals(p.getAppName())) {
deploymentProperties = p;
break;
}
}
}
return deploymentProperties;
}
public CloudApplicationDeploymentProperties getDeploymentProperties(String yaml, String appName) throws Exception {
if (yaml == null) {
yaml = generateDefaultContent();
}
return toDeploymentProperties(cloudData, yaml, project, appName);
}
}

View File

@@ -1,97 +0,0 @@
/*******************************************************************************
* Copyright (c) 2015 Pivotal, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.cf.dialogs;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFOrganization;
import org.springframework.ide.eclipse.boot.dash.cf.client.CFSpace;
/**
* Hierarchical representation of existing orgs and spaces in a Cloud Foundry
* target.
*
*/
public class OrgsAndSpaces {
private final List<CFSpace> originalSpaces;
private Map<String, List<CFSpace>> orgIDtoSpaces;
private Map<String,CFOrganization> orgIDtoOrg;
/**
*
* @param spaces
* a flat list of all spaces for a given set of credentials and
* server URL. Should not be empty or null.
* @throws CoreException
* if given cloud server does not support orgs and spaces
*/
public OrgsAndSpaces(List<CFSpace> spaces) {
this.originalSpaces = spaces;
setValues();
}
public CFSpace getSpace(String orgName, String spaceName) {
List<CFSpace> oSpaces = orgIDtoSpaces.get(orgName);
if (oSpaces != null) {
for (CFSpace clSpace : oSpaces) {
if (clSpace.getName().equals(spaceName)) {
return clSpace;
}
}
}
return null;
}
public List<CFOrganization> getOrgs() {
Collection<CFOrganization> orgList = orgIDtoOrg.values();
return new ArrayList<CFOrganization>(orgList);
}
protected void setValues() {
orgIDtoSpaces = new HashMap<String, List<CFSpace>>();
orgIDtoOrg = new HashMap<String, CFOrganization>();
for (CFSpace clSpace : originalSpaces) {
CFOrganization org = clSpace.getOrganization();
List<CFSpace> spaces = orgIDtoSpaces.get(org.getName());
if (spaces == null) {
spaces = new ArrayList<CFSpace>();
orgIDtoSpaces.put(org.getName(), spaces);
orgIDtoOrg.put(org.getName(), org);
}
spaces.add(clSpace);
}
}
/**
* @param orgName
* @return
*/
public List<CFSpace> getOrgSpaces(String orgName) {
return orgIDtoSpaces.get(orgName);
}
/**
* @return all spaces available for the given account. Never null, although
* may be empty if no spaces are resolved.
*/
public List<CFSpace> getAllSpaces() {
return originalSpaces != null ? new ArrayList<CFSpace>(originalSpaces) : new ArrayList<CFSpace>(0);
}
}

Some files were not shown because too many files have changed in this diff Show More