repo-reorg-2020: moved sts4-relevant spring-ide bundles from https://github.com/spring-projects/spring-ide/releases/tag/REPO-REORG-2020 into sts4 repo

This commit is contained in:
Martin Lippert
2020-09-29 14:00:01 +02:00
parent 8edfab1a16
commit de16884b60
2210 changed files with 160282 additions and 43 deletions

View File

@@ -0,0 +1,7 @@
<?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 kind="output" path="bin"/>
</classpath>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.springframework.ide.eclipse.boot.launch</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>
<filteredResources>
<filter>
<id>1421885564492</id>
<name></name>
<type>10</type>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>
<arguments>1.0-projectRelativePath-matches-false-false-target</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

View File

@@ -0,0 +1,7 @@
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.source=1.8

View File

@@ -0,0 +1,60 @@
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=false
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=true
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
sp_cleanup.use_type_arguments=false

View File

@@ -0,0 +1,44 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Boot Launch
Bundle-SymbolicName: org.springframework.ide.eclipse.boot.launch;singleton:=true
Bundle-Vendor: Pivotal Software Inc.
Bundle-Version: 4.8.1.qualifier
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.core.resources,
org.eclipse.debug.core,
org.eclipse.jdt.core,
org.eclipse.jdt.debug.ui,
org.eclipse.jdt.launching,
org.springsource.ide.eclipse.commons.frameworks.core,
org.eclipse.debug.ui,
org.springsource.ide.eclipse.commons.livexp;bundle-version="3.8.4",
org.springframework.ide.eclipse.boot,
org.eclipse.core.expressions,
org.springsource.ide.eclipse.commons.frameworks.ui,
org.springsource.ide.eclipse.commons.ui,
org.springsource.ide.eclipse.commons.core,
org.eclipse.ui.console,
org.eclipse.m2e.jdt,
com.google.guava,
org.springframework.ide.eclipse.editor.support,
org.eclipse.jdt.debug,
org.eclipse.ui.workbench.texteditor,
org.eclipse.ui.editors,
org.eclipse.jface.text,
org.eclipse.jdt.ui,
org.eclipse.ui.genericeditor,
org.eclipse.ui.ide
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
Export-Package: org.springframework.ide.eclipse.boot.launch,
org.springframework.ide.eclipse.boot.launch.cli,
org.springframework.ide.eclipse.boot.launch.devtools,
org.springframework.ide.eclipse.boot.launch.livebean,
org.springframework.ide.eclipse.boot.launch.process,
org.springframework.ide.eclipse.boot.launch.profiles,
org.springframework.ide.eclipse.boot.launch.properties,
org.springframework.ide.eclipse.boot.launch.util
Bundle-Activator: org.springframework.ide.eclipse.boot.launch.BootLaunchActivator
Automatic-Module-Name: org.springframework.ide.eclipse.boot.launch

View File

@@ -0,0 +1,248 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<title>About Spring IDE</title>
<meta name="author" content="Spring IDE Developers">
</head>
<body>
<h2>About Spring IDE</h2>
<p>March 26, 2007</p>
<h3>Abstract</h3>
<p>Spring IDE is a set of plugins which provide a user interface for
<a href="https://www.springframework.org">The Spring Framework</a>'s bean
factory XML configuration files including support for Spring 2.0
namespaces, AOP configuration and Spring Web Flow.</p>
<h3>License</h3>
<p>The Spring IDE Project makes available all content in this
plug-in (&quot;Content&quot;). Unless otherwise indicated below, the
Content is provided to you under the terms and conditions of the Eclipse
Public License Version 1.0 (&quot;EPL&quot;). A copy of the EPL is
available at <a href="https://www.eclipse.org/legal/epl-v10.html">https://www.eclipse.org/legal/epl-v10.html</a>.
For purposes of the EPL, &quot;Program&quot; will mean the Content.</p>
<p>If you did not receive this Content directly from the Spring IDE
Project, the Content is being redistributed by another party
(&quot;Redistributor&quot;) and different terms and conditions may apply
to your use of any object code in the Content. Check the Redistributor's
license that was provided with the Content. If no such license exists,
contact the Redistributor. Unless otherwise indicated below, the terms
and conditions of the EPL still apply to any source code in the Content
and such source code may be obtained at <a href="https://www.eclipse.org">https://www.eclipse.org</a>.</p>
<h3>Third Party Content</h3>
<p>The Content includes items that have been sourced from third
parties as set out below. If you did not receive this Content directly
from the Spring IDE Project, the following is provided for informational
purposes only, and you should look to the Redistributor's license for
terms and conditions of use.</p>
<p><em> <strong>The Spring Framework v3.0.5</strong>
<p>This product includes software developed by the Spring Framework
Project (<a href="https://www.springframework.org">https://www.springframework.org</a>).</p>
<p><em> <strong>Spring Web Flow v2.0.7.A</strong>
<p>This product includes software developed by the Spring Framework
Project (<a href="https://www.springframework.org">https://www.springframework.org</a>).</p>
<p><em> <strong>Spring Web Services v1.5.8</strong>
<p>This product includes software developed by the Spring Framework
Project (<a href="https://www.springframework.org">https://www.springframework.org</a>).</p>
<p><em> <strong>Spring Security v3.0.2</strong>
<p>This product includes software developed by the Spring Framework
Project (<a href="https://www.springframework.org">https://www.springframework.org</a>).</p>
<p><em> <strong>Spring Dynamic Modules for OSGi(TM) Runtimes v2.0.0.M2</strong>
<p>This product includes software developed by the Spring Framework
Project (<a href="https://www.springframework.org">https://www.springframework.org</a>).</p>
<p><em> <strong>Spring Batch v2.1.0</strong>
<p>This product includes software developed by the Spring Framework
Project (<a href="https://www.springframework.org">https://www.springframework.org</a>).</p>
<p><em> <strong>Commons Codec v1.3.0</strong>
<p>This product includes software developed by The Apache Software
Foundation (<a href="https://www.apache.org/">https://www.apache.org/</a>).</p>
<p><em> <strong>Commons Collections v3.2.0</strong>
<p>This product includes software developed by The Apache Software
Foundation (<a href="https://www.apache.org/">https://www.apache.org/</a>).</p>
<p><em> <strong>Commons Logging v1.1.1</strong>
<p>This product includes software developed by The Apache Software
Foundation (<a href="https://www.apache.org/">https://www.apache.org/</a>).</p>
<p><em> <strong>Apache MyFaces v1.2.2</strong>
<p>This product includes software developed by The Apache Software
Foundation (<a href="https://www.apache.org/">https://www.apache.org/</a>).</p>
<p><em> <strong>AspectJ Weaver v1.6.10</strong>
<p>This product includes software developed by The Eclipse Software
Foundation (<a href="https://www.eclipse.org/">https://www.eclipse.org/</a>).</p>
<p><em> <strong>Antlr v3.0.1</strong>
<p>Spring IDE includes a binary version of Antlr v3.0.1 (<a
href="https://www.antlr.org">https://www.antlr.org/</a>).
The source code for Antlr is available from the Antlr download site at
<a href="https://www.antlr.org/download.html">https://www.antlr.org/download.html</a>.
<p>The Antlr license is available at <a
href="https://www.antlr.org/license.html">https://www.antlr.org/license.html</a>.
The license is also reproduced here:</p>
<pre>Copyright (c) 2003-2007, Terence Parr
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions
and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</pre></p>
<p><em> <strong>ASM v2.2.3</strong>
<p>Spring IDE includes a binary version of ASM v2.2.3 (<a
href="https://asm.objectweb.org/index.html">https://asm.objectweb.org/</a>).
The source code for ASM is available from the ObjectWeb download site at
<a href="https://asm.objectweb.org/download/">https://asm.objectweb.org/download/</a>.
<p>The ASM license is available at <a
href="https://asm.objectweb.org/license.html">https://asm.objectweb.org/license.html</a>.
The license is also reproduced here:</p>
<pre>Copyright (c) 2000-2005 INRIA, France Telecom
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
</pre></p>
<p><em> <strong>backport-util-concurrent 3.1.0</strong>
<p>Spring IDE includes a binary version of backport-util-concurrent v3.1.0 (<a
href="https://dcl.mathcs.emory.edu/util/backport-util-concurrent/">https://dcl.mathcs.emory.edu/util/backport-util-concurrent/</a>).
The source code for backport-util-concurrent is available from the download site at
<a href="https://dcl.mathcs.emory.edu/util/backport-util-concurrent/dist/">https://dcl.mathcs.emory.edu/util/backport-util-concurrent/dist/</a>.
<p>The backport-util-concurrent license is available at <a
href="https://creativecommons.org/licenses/publicdomain">https://creativecommons.org/licenses/publicdomain</a>.
The license is also reproduced here:</p>
<pre><em>Copyright-Only Dedication (based on United States law) or Public Domain Certification</em>
The person or persons who have associated work with this document (the
"Dedicator" or "Certifier") hereby either (a) certifies that, to the
best of his knowledge, the work of authorship identified is in the public
domain of the country from which the work is published, or (b) hereby
dedicates whatever copyright the dedicators holds in the work of authorship
identified below (the "Work") to the public domain. A certifier, moreover,
dedicates any copyright interest he may have in the associated work, and
for these purposes, is described as a "dedicator" below.
A certifier has taken reasonable steps to verify the copyright status of
this work. Certifier recognizes that his good faith efforts may not shield
him from liability if in fact the work certified is not in the public domain.
Dedicator makes this dedication for the benefit of the public at large and
to the detriment of the Dedicator's heirs and successors. Dedicator intends
this dedication to be an overt act of relinquishment in perpetuity of all
present and future rights under copyright law, whether vested or contingent,
in the Work. Dedicator understands that such relinquishment of all rights
includes the relinquishment of all rights to enforce (by lawsuit or otherwise)
those copyrights in the Work.
Dedicator recognizes that, once placed in the public domain, the Work may be
freely reproduced, distributed, transmitted, used, modified, built upon, or
otherwise exploited by anyone for any purpose, commercial or non-commercial,
and in any way, including by methods that have not yet been invented or
conceived.
</pre></p>
<p><em> <strong>AOP Alliance</strong>
<p>LICENCE: all the source code provided by AOP Alliance is Public Domain.
</p>
<p><em> <strong>javax.el v1.0.0</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
<p><em> <strong>javax.jms v1.1.0</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
<p><em> <strong>javax.servlet v2.5.0</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
<p><em> <strong>javax.servlet.jsp v2.1.0</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
<p><em> <strong>javax.servlet.jsp.jstl v1.2.0</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
<p><em> <strong>javax.activation v1.1.0</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
<p><em> <strong>javax.annotation v1.0.0</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
<p><em> <strong>javax.mail v1.4.0</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
<p><em> <strong>javax.xml.stream v1.0.1</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
<p><em> <strong>javax.xml.ws v2.1.0</strong>
<p>This product includes software distributed by Sun (meanwhile Oracle) under CDDL license
(<a href="https://www.sun.com/cddl/cddl.html">https://www.sun.com/cddl/cddl.html</a>).</p>
</em></p>
<address>Copyright (c) 2005, 2011 Spring IDE Developers</address>
</body>
</html>

View File

@@ -0,0 +1,6 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
about.html,\
plugin.xml

View File

@@ -0,0 +1,267 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.eclipse.debug.ui.launchShortcuts">
<shortcut
class="org.springframework.ide.eclipse.boot.launch.BootLaunchShortcut"
description="Run or Debug as a Standalone Spring Boot Application"
id="org.springsource.ide.eclipse.boot.BootLaunchShortcut"
icon="platform:/plugin/org.springframework.ide.eclipse.boot/resources/icons/spring.png"
label="Spring Boot App"
modes="run, debug">
<contextualLaunch>
<enablement>
<count value="1"/>
<iterate>
<and>
<adapt type="org.eclipse.jdt.core.IJavaElement">
<test property="org.springsource.ide.eclipse.boot.javaelement.isInBootProject"/>
</adapt>
<or>
<test property="org.eclipse.jdt.launching.hasMain"/>
<test property="org.eclipse.jdt.launching.isContainer"/>
<test property="org.eclipse.jdt.launching.isPackageFragment"/>
<test property="org.eclipse.jdt.launching.isPackageFragmentRoot"/>
</or>
</and>
</iterate>
</enablement>
</contextualLaunch>
<configurationType
id="org.springframework.ide.eclipse.boot.launch">
</configurationType>
<description
description="Run as Spring Boot App"
mode="run">
</description>
<description
description="Debug as Spring Boot App"
mode="debug">
</description>
</shortcut>
<shortcut
class="org.springframework.ide.eclipse.boot.launch.devtools.BootDevtoolsClientLaunchShortcut"
description="Connect Spring Devtools Client to a Remote Spring Boot Application"
id="org.springframework.ide.eclipse.boot.launch.devtools.BootDevtoolsClientLaunchShortcut"
icon="platform:/plugin/org.springframework.ide.eclipse.boot/resources/icons/boot-devtools-icon.png"
label="Spring Devtools Client"
modes="run, debug">
<contextualLaunch>
<enablement>
<count value="1"/>
<iterate>
<adapt type="org.eclipse.core.resources.IResource">
<test property="org.springsource.ide.eclipse.boot.isBootProject"/>
</adapt>
</iterate>
</enablement>
</contextualLaunch>
<configurationType
id="org.springframework.ide.eclipse.boot.devtools.client.launch">
</configurationType>
<description
description="Run as Spring Devtools Client"
mode="run">
</description>
<description
description="Debug as Spring Devtools Client"
mode="debug">
</description>
</shortcut>
</extension>
<extension point="org.eclipse.debug.core.launchConfigurationTypes">
<launchConfigurationType
name="Spring Boot CLI Script"
delegate="org.springframework.ide.eclipse.boot.launch.cli.BootGroovyScriptLaunchConfigurationDelegate"
category="org.eclipse.ui.externaltools"
modes="run"
id="org.springsource.ide.eclipse.boot.groovy.script.launch">
</launchConfigurationType>
<launchConfigurationType
id="org.springframework.ide.eclipse.boot.launch"
name="Spring Boot App"
delegate="org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate"
modes="run,debug"
sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer">
</launchConfigurationType>
<launchConfigurationType
id="org.springframework.ide.eclipse.boot.devtools.client.launch"
name="Spring Boot Devtools Client"
delegate="org.springframework.ide.eclipse.boot.launch.devtools.BootDevtoolsClientLaunchConfigurationDelegate"
modes="run,debug"
sourceLocatorId="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"
sourcePathComputerId="org.eclipse.jdt.launching.sourceLookup.javaSourcePathComputer">
</launchConfigurationType>
<launchConfigurationType
category="org.eclipse.ui.externaltools"
delegate="org.springframework.ide.eclipse.boot.launch.cli.CloudCliServiceLaunchConfigurationDelegate"
id="org.springframework.ide.eclipse.boot.launch.cloud.cli.service"
modes="run"
name="Spring Cloud CLI Service">
</launchConfigurationType>
</extension>
<extension
point="org.eclipse.debug.ui.launchShortcuts">
<shortcut
class="org.springframework.ide.eclipse.boot.launch.cli.BootGroovyScriptLaunchShortcut"
description="Run or Debug as a Spring Boot CLI Script"
id="org.springsource.ide.eclipse.boot.BootLaunchGroovyScriptShortcut"
icon="platform:/plugin/org.springframework.ide.eclipse.boot/resources/icons/spring.png"
label="Spring Boot CLI Script"
modes="run"> <!-- debug not yet support -->
<contextualLaunch>
<enablement>
<count value="1"/>
<iterate>
<adapt type="org.eclipse.core.resources.IFile">
<test
property="org.eclipse.core.resources.name"
value="*.groovy">
</test>
</adapt>
</iterate>
</enablement>
</contextualLaunch>
<configurationType
id="org.eclipse.jdt.launching.localJavaApplication">
</configurationType>
<description
description="Run as Spring Boot CLI Script"
mode="run">
</description>
<!-- Debugging is not yet supported.
<description
description="Debug as Spring Boot CLI Script"
mode="debug">
</description> -->
</shortcut>
</extension>
<extension point="org.eclipse.debug.ui.launchConfigurationTypeImages">
<launchConfigurationTypeImage
icon="platform:/plugin/org.springframework.ide.eclipse.boot/resources/icons/boot-icon.png"
configTypeID="org.springframework.ide.eclipse.boot.launch"
id="org.springframework.ide.eclipse.boot.launch.image">
</launchConfigurationTypeImage>
<launchConfigurationTypeImage
icon="platform:/plugin/org.springframework.ide.eclipse.boot/resources/icons/boot-devtools-icon.png"
configTypeID="org.springframework.ide.eclipse.boot.devtools.client.launch"
id="org.springframework.ide.eclipse.boot.launch.image">
</launchConfigurationTypeImage>
</extension>
<extension point="org.eclipse.debug.ui.launchConfigurationTabGroups">
<launchConfigurationTabGroup
type="org.springframework.ide.eclipse.boot.launch"
class="org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationTabGroup"
id="org.springframework.ide.eclipse.boot.launch.LaunchConfigurationTabGroup">
</launchConfigurationTabGroup>
<launchConfigurationTabGroup
type="org.springframework.ide.eclipse.boot.devtools.client.launch"
class="org.springframework.ide.eclipse.boot.launch.devtools.BootDevtoolsClientLaunchConfigurationTabGroup"
id="org.springframework.ide.eclipse.boot.launch.devtools.BootDevtoolsClientLaunchConfigurationTabGroup">
</launchConfigurationTabGroup>
</extension>
<extension point="org.eclipse.jdt.launching.classpathProviders">
<classpathProvider
id="org.springframework.ide.eclipse.boot.launch.BootMavenSourcePathProvider"
class="org.springframework.ide.eclipse.boot.launch.BootMavenSourcePathProvider"/>
<classpathProvider
id="org.springframework.ide.eclipse.boot.launch.BootMavenClassPathProvider"
class="org.springframework.ide.eclipse.boot.launch.BootMavenClassPathProvider"/>
</extension>
<!-- TODO: ui for editing Spring Groovy CLI launch configs
<extension point="org.eclipse.debug.ui.launchConfigurationTypeImages">
<launchConfigurationTypeImage
icon="resources/icons/spring.png"
configTypeID="org.springsource.ide.eclipse.boot.groovy.script.launch"
id="org.springsource.ide.eclipse.boot.groovy.script.launch.image">
</launchConfigurationTypeImage>
</extension>
<extension point="org.eclipse.debug.ui.launchConfigurationTabGroups">
<launchConfigurationTabGroup
type="org.springsource.ide.eclipse.boot.groovy.script.launch"
class="org.springframework.ide.eclipse.boot.core.cli.BootGroovyScriptLaunchConfigurationTabGroup"
id="org.springframework.ide.eclipse.boot.core.cli.BootGroovyScriptLaunchConfigurationTabGroup">
</launchConfigurationTabGroup>
</extension>
-->
<extension point="org.eclipse.ui.commands">
<command
name="Debug Spring Boot App"
description="Debug Spring Boot App"
categoryId="org.eclipse.debug.ui.category.run"
id="org.springsource.ide.eclipse.boot.BootLaunchShortcut.debug">
</command>
<command
name="Run Spring Boot App"
description="Run Spring Boot App"
categoryId="org.eclipse.debug.ui.category.run"
id="org.springsource.ide.eclipse.boot.BootLaunchShortcut.run">
</command>
<category
id="org.springframework.ide.eclipse.boot.commands.category"
name="Spring Boot">
</category>
<command
categoryId="org.springframework.ide.eclipse.boot.commands.category"
id="org.springframework.ide.eclipse.boot.commands.editStartersCommand"
name="Edit Starters">
</command>
</extension>
<extension point="org.eclipse.ui.bindings">
<key
sequence="M2+M3+X B"
commandId="org.springsource.ide.eclipse.boot.BootLaunchShortcut.run"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/>
</extension>
<extension point="org.eclipse.ui.bindings">
<key
sequence="M2+M3+D B"
commandId="org.springsource.ide.eclipse.boot.BootLaunchShortcut.debug"
schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/>
</extension>
<extension point="org.eclipse.debug.core.processFactories">
<processFactory
id="org.springframework.ide.eclipse.boot.launch.process.BootProcessFactory"
class="org.springframework.ide.eclipse.boot.launch.process.BootProcessFactory">
</processFactory>
<processFactory
class="org.springframework.ide.eclipse.boot.launch.cli.CloudCliServiceLaunchConfigurationDelegate$CloudCliProcessFactory"
id="org.springframework.ide.eclipse.boot.launch.cli.CloudCliServiceLaunchConfigurationDelegate$CloudCliProcessFactory">
</processFactory>
</extension>
<extension point="org.eclipse.ui.console.consolePageParticipants">
<consolePageParticipant
id="org.springframework.ide.eclipse.boot.launch.devtools.DevtoolsClientConsolePageParticipant"
class="org.springframework.ide.eclipse.boot.launch.console.BootConsolePageParticipant">
<enablement>
<instanceof value="org.eclipse.debug.internal.ui.views.console.ProcessConsole"/>
</enablement>
</consolePageParticipant>
</extension>
<extension
point="org.eclipse.core.expressions.propertyTesters">
<propertyTester
class="org.springframework.ide.eclipse.boot.launch.properties.EmbeddedEditorPropertyTester"
id="org.springframework.ide.eclipse.boot.EmbeddedEditor"
namespace="org.springframework.ide.eclipse.boot.EmbeddedEditor"
properties="context"
type="java.lang.Object">
</propertyTester>
</extension>
</plugin>

View File

@@ -0,0 +1,69 @@
<?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.8.1-SNAPSHOT</version>
<relativePath>../../eclipse-distribution/pom.xml</relativePath>
</parent>
<groupId>org.springframework.ide.eclipse</groupId>
<artifactId>org.springframework.ide.eclipse.boot.launch</artifactId>
<packaging>eclipse-plugin</packaging>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho-version}</version>
<configuration>
<resolver>p2</resolver>
<pomDependencies>ignore</pomDependencies>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-plugin</artifactId>
<version>${tycho-version}</version>
</plugin>
<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

@@ -0,0 +1,489 @@
/*******************************************************************************
* 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.launch;
import static org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Consumer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
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.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.sourcelookup.advanced.AdvancedJavaLaunchDelegate;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.core.BootPreferences;
import org.springframework.ide.eclipse.boot.core.SpringBootCore;
import org.springframework.ide.eclipse.boot.util.ProcessListenerAdapter;
import org.springframework.ide.eclipse.boot.util.ProcessTracker;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
public abstract class AbstractBootLaunchConfigurationDelegate extends AdvancedJavaLaunchDelegate {
private static final String JDT_JAVA_APPLICATION = "org.eclipse.jdt.launching.localJavaApplication";
private static final String SILENT_EXIT_EXCEPTION = "org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException";
private static final String M2E_CLASSPATH_PROVIDER = "org.eclipse.m2e.launchconfig.classpathProvider";
protected static final String M2E_SOURCEPATH_PROVIDER = "org.eclipse.m2e.launchconfig.sourcepathProvider";
public static final String JAVA_LAUNCH_CONFIG_TYPE_ID = IJavaLaunchConfigurationConstants.ID_JAVA_APPLICATION;
public static final String ENABLE_DEBUG_OUTPUT = "spring.boot.debug.enable";
public static final boolean DEFAULT_ENABLE_DEBUG_OUTPUT = false;
private static final String BOOT_MAVEN_SOURCE_PATH_PROVIDER = "org.springframework.ide.eclipse.boot.launch.BootMavenSourcePathProvider";
private static final String BOOT_MAVEN_CLASS_PATH_PROVIDER = "org.springframework.ide.eclipse.boot.launch.BootMavenClassPathProvider";
private static final String BUILDSHIP_CLASS_PATH_PROVIDER = "org.eclipse.buildship.core.classpathprovider";
/**
* Spring boot properties are stored as launch confiuration properties with
* an extra prefix added to property name to avoid name clashes with
* other launch config properties.
*/
private static final String PROPS_PREFIX = "spring.boot.prop.";
private static final String APPLICATION_PROPERTIES = "spring.boot.app.properties";
/**
* To be able to store multiple assignment to the same spring boot
* property name we add a 'oid' at the end of each stored
* property name. ?_SEPERATOR is used to separate the 'real'
* property name from the 'oid' string.
*/
private static final char OID_SEPERATOR = ':';
/*
* Remove once old launch properties format is irrelevant
*/
@Deprecated
public static class PropVal {
public String name;
public String value;
public boolean isChecked;
public PropVal(String name, String value, boolean isChecked) {
//Don't use null, use empty Strings
Assert.isNotNull(name);
Assert.isNotNull(value);
this.name = name;
this.value = value;
this.isChecked = isChecked;
}
@Override
public String toString() {
return (isChecked?"[X] ":"[ ] ") +
name + "="+ value;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (isChecked ? 1231 : 1237);
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((value == null) ? 0 : value.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;
PropVal other = (PropVal) obj;
if (isChecked != other.isChecked)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
}
public static List<ILaunchConfiguration> getLaunchConfigs(IProject p, String confTypeId) {
try {
ILaunchManager lm = getLaunchMan();
ILaunchConfigurationType type = lm.getLaunchConfigurationType(confTypeId);
if (type!=null) {
ILaunchConfiguration[] configs = lm.getLaunchConfigurations(type);
if (configs!=null && configs.length>0) {
ArrayList<ILaunchConfiguration> result = new ArrayList<>();
for (ILaunchConfiguration conf : configs) {
if (p.equals(getProject(conf))) {
result.add(conf);
}
}
return result;
}
}
} catch (Exception e) {
Log.log(e);
}
return Collections.emptyList();
}
public static void clearProperties(ILaunchConfigurationWorkingCopy conf) {
try {
//note: e43 doesn't use generics for conf.getAttributes, hence the
// funky casting below.
conf.removeAttribute(APPLICATION_PROPERTIES);
// Legacy properties
for (Object _prefixedProp : conf.getAttributes().keySet()) {
String prefixedProp = (String) _prefixedProp;
if (prefixedProp.startsWith(PROPS_PREFIX)) {
conf.removeAttribute(prefixedProp);
}
}
} catch (Exception e) {
Log.log(e);
}
}
public static String getMainType(ILaunchConfiguration config) throws CoreException {
return config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, (String)null);
}
public static void setMainType(ILaunchConfigurationWorkingCopy config, String typeName) {
config.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, typeName);
}
@SuppressWarnings("unchecked")
private static List<PropVal> getProperties(ILaunchConfiguration conf) {
ArrayList<PropVal> props = new ArrayList<>();
try {
//Note: in e43 conf.getAttributes doesn't use generics yet. So to
//build with 4.3 we need to to some funky casting below.
for (Object _e : conf.getAttributes().entrySet()) {
try {
Map.Entry<String, Object> e = (Entry<String, Object>) _e;
String prefixed = e.getKey();
if (prefixed.startsWith(PROPS_PREFIX)) {
String name = prefixed.substring(PROPS_PREFIX.length());
int dotPos = name.lastIndexOf(OID_SEPERATOR);
if (dotPos>=0) {
name = name.substring(0, dotPos);
}
String valueEnablement = (String)e.getValue();
String value = valueEnablement.substring(1);
boolean enabled = valueEnablement.charAt(0)=='1';
props.add(new PropVal(name, value, enabled));
}
} catch (Exception ignore) {
//silently ignore invalid property data.
}
}
} catch (Exception e) {
Log.log(e);
}
return props;
}
public static String getRawApplicationProperties(ILaunchConfiguration conf) {
try {
if (conf.hasAttribute(APPLICATION_PROPERTIES)) {
return conf.getAttribute(APPLICATION_PROPERTIES, "");
} else {
// Legacy properties
Properties properties = new Properties();
getProperties(conf).forEach(p -> properties.setProperty(p.name, p.value));
StringWriter writer = new StringWriter();
properties.store(writer, null);
// Remove all comments generated by java.util.Properties serialization
return writer.getBuffer().toString().replaceAll("(?m)^\\s*#.*$", "").trim();
}
} catch (Exception e) {
Log.log(e);
return "";
}
}
public static void setRawApplicationProperties(ILaunchConfigurationWorkingCopy conf, String props) {
conf.setAttribute(APPLICATION_PROPERTIES, props);
}
public static Properties getApplicationProperties(ILaunchConfiguration conf) {
String propetiesString = getRawApplicationProperties(conf);
Properties properties = new Properties();
try {
properties.load(new ByteArrayInputStream(propetiesString.getBytes()));
} catch (IOException e) {
Log.log(e);
}
return properties;
}
@SuppressWarnings("rawtypes")
protected void addPropertiesArguments(ArrayList<String> args, Properties props) {
for (Map.Entry e : props.entrySet()) {
String name = (String) e.getKey();
String value = (String) e.getValue();
//spring boot doesn't like empty option keys/values so skip those.
if (!name.isEmpty()) {
args.add(propertyAssignmentArgument(name, value));
}
}
}
protected String propertyAssignmentArgument(String name, String value) {
if (name.contains("=")) {
//spring boot has no handling of escape sequences like '\='
//so we cannot represent keys containing '='.
throw new IllegalArgumentException("property name shouldn't contain '=':"+name);
}
if (value.isEmpty()) {
return "--"+name;
} else {
return "--"+name + "=" +value;
}
}
public static boolean getEnableDebugOutput(ILaunchConfiguration conf) {
try {
return conf.getAttribute(ENABLE_DEBUG_OUTPUT, DEFAULT_ENABLE_DEBUG_OUTPUT);
} catch (Exception e) {
Log.log(e);
return DEFAULT_ENABLE_DEBUG_OUTPUT;
}
}
public static void setEnableDebugOutput(ILaunchConfigurationWorkingCopy conf, boolean enable) {
conf.setAttribute(ENABLE_DEBUG_OUTPUT, enable);
}
/**
* Get the project associated with this a luanch config. Note that this
* method returns an IProject reference regardless of whether or not the
* project exists.
*/
public static IProject getProject(ILaunchConfiguration conf) {
try {
String pname = getProjectName(conf);
if (StringUtil.hasText(pname)) {
IProject p = ResourcesPlugin.getWorkspace().getRoot().getProject(pname);
//debug(conf, "getProject => "+p);
return p;
}
} catch (Exception e) {
Log.log(e);
}
//debug(conf, "getProject => NULL");
return null;
}
public static String getProjectName(ILaunchConfiguration conf)
throws CoreException {
return conf.getAttribute(ATTR_PROJECT_NAME, "");
}
public static void setProject(ILaunchConfigurationWorkingCopy conf, IProject p) {
//debug(conf, "setProject <= "+p);
if (p==null) {
conf.removeAttribute(ATTR_PROJECT_NAME);
} else {
conf.setAttribute(ATTR_PROJECT_NAME, p.getName());
}
}
/**
* Enable maven classpath provider if applicable to this conf.
* Addresses https://issuetracker.springsource.com/browse/STS-4085
*/
static void enableMavenClasspathProvider(ILaunchConfigurationWorkingCopy conf) {
try {
if (conf.getType().getIdentifier().equals(JAVA_LAUNCH_CONFIG_TYPE_ID)) {
//Take care not to add this a 'real' Boot launch config or it will cause m2e to throw exceptions
//These 'magic' attributes should only be added to a 'cloned' copy of our config with the right type.
IProject p = getProject(conf);
if (p!=null && p.hasNature(SpringBootCore.M2E_NATURE)) {
if (!conf.hasAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER)) {
conf.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, M2E_CLASSPATH_PROVIDER);
}
if (!conf.hasAttribute(IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH_PROVIDER)) {
conf.setAttribute(IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH_PROVIDER, M2E_SOURCEPATH_PROVIDER);
}
}
}
} catch (Exception e) {
BootActivator.log(e);
}
}
public static ILaunchManager getLaunchMan() {
return DebugPlugin.getDefault().getLaunchManager();
}
@Override
public void launch(ILaunchConfiguration conf, String mode, final ILaunch launch, IProgressMonitor monitor)
throws CoreException {
conf = configureClassPathProviders(conf);
if (ILaunchManager.DEBUG_MODE.equals(mode) && isIgnoreSilentExitException(conf)) {
final IgnoreExceptionOfType breakpointListener = new IgnoreExceptionOfType(launch, SILENT_EXIT_EXCEPTION);
new ProcessTracker(new ProcessListenerAdapter() {
@Override
public void debugTargetTerminated(ProcessTracker tracker, IDebugTarget target) {
if (launch.equals(target.getLaunch())){
breakpointListener.dispose();
tracker.dispose();
}
}
});
}
super.launch(conf, mode, launch, monitor);
}
protected ILaunchConfiguration configureClassPathProviders(ILaunchConfiguration conf) throws CoreException {
IProject project = BootLaunchConfigurationDelegate.getProject(conf);
if (project!=null) {
if (project.hasNature(SpringBootCore.M2E_NATURE)) {
conf = modify(conf, (ILaunchConfigurationWorkingCopy wc) -> {
enableMavenClasspathProviders(wc);
});
} else if (project.hasNature(SpringBootCore.BUILDSHIP_NATURE)) {
conf = modify(conf, wc -> {
enableGradleClasspathProviders(wc);
});
}
}
return conf;
}
@Override
public String[][] getClasspathAndModulepath(ILaunchConfiguration configuration) throws CoreException {
if (configuration.hasAttribute(IJavaLaunchConfigurationConstants.ATTR_EXCLUDE_TEST_CODE)) {
//TODO: This is a dirty hack. We 'trick' BuildShip to treat our launch config as if it is a plain JDT launch by making
// a temporary copy of it.
//A request to make BuildShip provide a cleaner way to do this was filed here:
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=543328
//If that bug is resolved this code should be removed.
configuration = BootLaunchConfigurationDelegate.copyAs(configuration, JDT_JAVA_APPLICATION);
}
return super.getClasspathAndModulepath(configuration);
}
@Override
public String[] getClasspath(ILaunchConfiguration configuration) throws CoreException {
if (configuration.hasAttribute(IJavaLaunchConfigurationConstants.ATTR_EXCLUDE_TEST_CODE)) {
//TODO: This is a dirty hack. We 'trick' BuildShip to treat our launch config as if it is a plain JDT launch by making
// a temporary copy of it.
//A request to make BuildShip provide a cleaner way to do this was filed here:
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=543328
//If that bug is resolved this code should be removed.
configuration = BootLaunchConfigurationDelegate.copyAs(configuration, JDT_JAVA_APPLICATION);
}
return super.getClasspath(configuration);
}
@Override
public String[][] getBootpathExt(ILaunchConfiguration configuration) throws CoreException {
if (configuration.hasAttribute(IJavaLaunchConfigurationConstants.ATTR_EXCLUDE_TEST_CODE)) {
//TODO: This is a dirty hack. We 'trick' BuildShip to treat our launch config as if it is a plain JDT launch by making
// a temporary copy of it.
//A request to make BuildShip provide a cleaner way to do this was filed here:
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=543328
//If that bug is resolved this code should be removed.
configuration = BootLaunchConfigurationDelegate.copyAs(configuration, JDT_JAVA_APPLICATION);
}
return super.getBootpathExt(configuration);
}
public static void enableMavenClasspathProviders(ILaunchConfigurationWorkingCopy wc) {
setAttribute(wc, IJavaLaunchConfigurationConstants.ATTR_SOURCE_PATH_PROVIDER, BOOT_MAVEN_SOURCE_PATH_PROVIDER);
setAttribute(wc, IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, BOOT_MAVEN_CLASS_PATH_PROVIDER);
try {
if (!wc.hasAttribute(IJavaLaunchConfigurationConstants.ATTR_EXCLUDE_TEST_CODE)) {
setAttribute(wc, IJavaLaunchConfigurationConstants.ATTR_EXCLUDE_TEST_CODE, true);
}
} catch (CoreException e) {
Log.log(e);
}
}
public static void enableGradleClasspathProviders(ILaunchConfigurationWorkingCopy wc) {
/* This is found in typical java launch config for buildship project. It plays a crucial role in
* computing correct runtime classpath:
*
* <booleanAttribute key="org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE" value="true"/>
* <stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.buildship.core.classpathprovider"/>
*/
setAttribute(wc, IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER, BUILDSHIP_CLASS_PATH_PROVIDER);
}
private ILaunchConfiguration modify(ILaunchConfiguration conf, Consumer<ILaunchConfigurationWorkingCopy> mutator) throws CoreException {
ILaunchConfigurationWorkingCopy wc = conf.getWorkingCopy();
try {
mutator.accept(wc);
} finally {
if (wc.isDirty()) {
conf = wc.doSave();
}
}
return conf;
}
private static void setAttribute(ILaunchConfigurationWorkingCopy wc, String a, boolean v) {
try {
if (!wc.hasAttribute(a) || v != wc.getAttribute(a, false)) {
wc.setAttribute(a, v);
}
} catch (CoreException e) {
Log.log(e);
}
}
private static void setAttribute(ILaunchConfigurationWorkingCopy wc, String a, String v) {
try {
if (!Objects.equals(v, wc.getAttribute(a, (String)null))) {
wc.setAttribute(a, v);
}
} catch (CoreException e) {
Log.log(e);
}
}
public static boolean isIgnoreSilentExitException(ILaunchConfiguration conf) {
//This might be controlled by individual launch conf in future, but for now, it is just a global preference.
return BootPreferences.getInstance().isIgnoreSilentExit();
}
}

View File

@@ -0,0 +1,43 @@
/*******************************************************************************
* 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.launch;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.ui.ILaunchConfigurationTab;
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaClasspathTab;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.springframework.ide.eclipse.boot.core.SpringBootCore;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
public class BootClasspathTab extends JavaClasspathTab implements ILaunchConfigurationTab {
@Override
public void initializeFrom(ILaunchConfiguration conf) {
//See bug: https://github.com/spring-projects/spring-ide/issues/222
// If user did not create boot launch config via boot dash and tries to immediately edit classpath
// an incorrect default classpath will be computed because of the missing classpath provider.
// So make sure the classpath provider is present.
IProject project = BootLaunchConfigurationDelegate.getProject(conf);
try {
if (project!=null && project.hasNature(SpringBootCore.M2E_NATURE) && !conf.hasAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH_PROVIDER)) {
ILaunchConfigurationWorkingCopy wc = conf.getWorkingCopy();
BootLaunchConfigurationDelegate.enableMavenClasspathProviders(wc);
conf = wc;
}
} catch (CoreException e) {
Log.log(e);
}
super.initializeFrom(conf);
}
}

View File

@@ -0,0 +1,89 @@
/*******************************************************************************
* 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.launch;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.service.prefs.BackingStoreException;
import org.springframework.ide.eclipse.boot.launch.util.BootLaunchConfDeleter;
/**
* @author Kris De Volder
*/
public class BootLaunchActivator extends AbstractUIPlugin {
private static BootLaunchActivator instance;
private BootLaunchConfDeleter workspaceListener;
public BootLaunchActivator() {
}
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
workspaceListener = new BootLaunchConfDeleter(ResourcesPlugin.getWorkspace(), DebugPlugin.getDefault().getLaunchManager());
instance = this;
IPreferenceStore myStore = instance.getPreferenceStore();
if (!myStore.getBoolean("cglib.breakpoint.warning.disabled")) {
setPreference("org.eclipse.jdt.debug.ui", "org.eclipse.jdt.debug.ui.prompt_unable_to_install_breakpoint", false);
myStore.setValue("cglib.breakpoint.warning.disabled", true);
}
}
private void setPreference(String plugin, String key, boolean value) {
try {
IEclipsePreferences store = DefaultScope.INSTANCE.getNode(plugin);
store.putBoolean(key, value);
store.flush();
} catch (BackingStoreException e) {
//ignore
}
try {
IEclipsePreferences store = InstanceScope.INSTANCE.getNode(plugin);
store.putBoolean(key, value);
store.flush();
} catch (BackingStoreException e) {
//ignore
}
}
@Override
public void stop(BundleContext context) throws Exception {
instance = null;
if (workspaceListener!=null) {
workspaceListener.dispose();
}
super.stop(context);
}
public static BootLaunchActivator getInstance() {
return instance;
}
private static final String LIVEBEAN_SUPPORT_BUNDLE_ID = "org.springframework.ide.eclipse.boot.launch.livebean";
public boolean isLiveBeanSupported() {
for (Bundle bndl : getBundle().getBundleContext().getBundles()) {
if (bndl.getSymbolicName().equals(LIVEBEAN_SUPPORT_BUNDLE_ID)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,652 @@
/*******************************************************************************
* Copyright (c) 2015, 2020 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.launch;
import static org.eclipse.debug.core.DebugPlugin.ATTR_PROCESS_FACTORY_ID;
import static org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants.ATTR_VM_ARGUMENTS;
import static org.springsource.ide.eclipse.commons.core.util.StringUtil.hasText;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Properties;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
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.Platform;
import org.eclipse.debug.core.DebugPlugin;
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.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import org.osgi.framework.Bundle;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.core.BootPreferences;
import org.springframework.ide.eclipse.boot.core.BootPropertyTester;
import org.springframework.ide.eclipse.boot.core.SpringBootCore;
import org.springframework.ide.eclipse.boot.launch.livebean.JmxBeanSupport;
import org.springframework.ide.eclipse.boot.launch.livebean.JmxBeanSupport.Feature;
import org.springframework.ide.eclipse.boot.launch.process.BootProcessFactory;
import org.springframework.ide.eclipse.boot.launch.profiles.ProfileHistory;
import org.springframework.ide.eclipse.boot.launch.util.PortFinder;
import org.springsource.ide.eclipse.commons.core.util.OsUtils;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
/**
* @author Kris De Volder
*/
public class BootLaunchConfigurationDelegate extends AbstractBootLaunchConfigurationDelegate {
private static DeletedLaunchConfTerminator deletedLaunchConfTerminator = null;
public synchronized static void ensureDeletedLaunchConfTerminator() {
if (deletedLaunchConfTerminator==null) {
deletedLaunchConfTerminator = new DeletedLaunchConfTerminator(DebugPlugin.getDefault().getLaunchManager(), (ILaunch l) -> {
try {
return l!=null && Boolean.valueOf(l.getAttribute(BOOT_LAUNCH_MARKER));
} catch (Exception e) {
Log.log(e);
return false;
}
});
}
}
// private static final boolean DEBUG = (""+Platform.getLocation()).contains("kdvolder");
// private static void debug(String string) {
// if (DEBUG) {
// System.out.println(string);
// }
// }
public static final String TYPE_ID = "org.springframework.ide.eclipse.boot.launch";
/**
* Launch attribute that helps recognize a launch as a boot launch even after the launch configuration has
* been deleted.
*/
public static final String BOOT_LAUNCH_MARKER = "isBootLaunch";
public static final String ENABLE_LIVE_BEAN_SUPPORT = "spring.boot.livebean.enable";
public static final boolean DEFAULT_ENABLE_LIVE_BEAN_SUPPORT() {
BootLaunchActivator ins = BootLaunchActivator.getInstance();
return ins!=null && ins.isLiveBeanSupported();
}
public static final String ENABLE_JMX = "spring.boot.jmx.enable";
public static final boolean DEFAULT_ENABLE_JMX = true;
public static final String JMX_PORT = "spring.boot.livebean.port";
public static final int DEFAULT_JMX_PORT = 0; //means pick it dynamically
public static final String ANSI_CONSOLE_OUTPUT = "spring.boot.ansi.console";
public static final String FAST_STARTUP = "spring.boot.fast.startup";
private static final String PROFILE = "spring.boot.profile";
public static final String DEFAULT_PROFILE = "";
public static final String ENABLE_LIFE_CYCLE = "spring.boot.lifecycle.enable";
public static final boolean DEFAULT_ENABLE_LIFE_CYCLE = true;
public static final String HIDE_FROM_BOOT_DASH = "spring.boot.dash.hidden";
public static final boolean DEFAULT_HIDE_FROM_BOOT_DASH = false;
public static final String PROCESS_ID = "spring.boot.process.id";
private static final String ENABLE_CHEAP_ENTROPY_VM_ARGS = "-Djava.security.egd=file:/dev/./urandom ";
private static final String TERMINATION_TIMEOUT = "spring.boot.lifecycle.termination.timeout";
public static final long DEFAULT_TERMINATION_TIMEOUT = 15000; // 15 seconds
public static final String USE_THIN_WRAPPER = "spring.boot.thinwrapper.enable";
public static final boolean DEFAULT_USE_THIN_WRAPPER = false;
public static final String SPRING_PROJECT_NAME_ATTRIBUTE = "spring.boot.project.name";
private ProfileHistory profileHistory = new ProfileHistory();
/**
* Use threadlocal to gain access to current launch in some of the methods
* (i.e. getVMArguments in particular) of the {@link AbstractBootLaunchConfigurationDelegate}
* framework that, unfortunately don't pass it along as parameters. It's either this, or copy
* a whole bunch of inherited code just so we can modify it to add an extra argument.
*/
private static final ThreadLocal<ILaunch> CURRENT_LAUNCH = new ThreadLocal<>();
@Override
public void launch(ILaunchConfiguration conf, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {
ensureDeletedLaunchConfTerminator();
launch.setAttribute(BOOT_LAUNCH_MARKER, "true");
CURRENT_LAUNCH.set(launch);
try {
profileHistory.updateHistory(getProject(conf), getProfile(conf));
super.launch(conf, mode, launch, monitor);
} finally {
CURRENT_LAUNCH.remove();
}
}
@Override
public String getProgramArguments(ILaunchConfiguration conf) throws CoreException {
Properties props = getApplicationProperties(conf);
String profile = getProfile(conf);
boolean debugOutput = getEnableDebugOutput(conf);
boolean enableAnsiConsole = supportsAnsiConsoleOutput() && getEnableAnsiConsoleOutput(conf);
if ((props==null || props.isEmpty()) && !debugOutput && !hasText(profile) && !enableAnsiConsole && !useThinWrapper(conf)) {
//shortcut for case where no boot-specific customizations are specified.
return super.getProgramArguments(conf);
}
ArrayList<String> args = new ArrayList<>();
if (useThinWrapper(conf)) {
String realMain = super.getMainTypeName(conf);
//--thin.main=com.example.SampleApplication
// --thin.archive=target/classes - the first one is the main class of the boot app, as already configured in the boot launch config, the second one points to the compiled classes (the entry that was on the regular classpath before without the maven dependencies)
args.add("--thin.main="+realMain);
args.add("--thin.archive="+getThinArchive(conf));
}
if (debugOutput) {
args.add("--debug");
}
if (hasText(profile)) {
args.add(propertyAssignmentArgument("spring.profiles.active", profile));
}
if (enableAnsiConsole) {
args.add(propertyAssignmentArgument("spring.output.ansi.enabled", "always"));
}
addPropertiesArguments(args, props);
args.addAll(Arrays.asList(DebugPlugin.parseArguments(super.getProgramArguments(conf))));
return DebugPlugin.renderArguments(args.toArray(new String[args.size()]), null);
}
private String getThinArchive(ILaunchConfiguration conf) throws CoreException {
IRuntimeClasspathEntry[] entries = {
JavaRuntime.newProjectRuntimeClasspathEntry(getJavaProject(conf))
};
IRuntimeClasspathEntry[] realClasspath = JavaRuntime.resolveRuntimeClasspath(entries, conf);
StringBuilder classpathString = new StringBuilder();
boolean first = true;
for (IRuntimeClasspathEntry entry : realClasspath) {
if (!first) {
classpathString.append(File.pathSeparatorChar);
}
classpathString.append(entry.getLocation());
first = false;
}
if (first) {
throw new IllegalStateException("Could not determine the 'thin archive' location");
}
return classpathString.toString();
}
@Override
public String getVMArguments(ILaunchConfiguration conf)
throws CoreException {
try {
List<String> vmArgs = new ArrayList<>();
vmArgs.addAll(Arrays.asList(DebugPlugin.parseArguments(super.getVMArguments(conf))));
// VM args for JMX connection
EnumSet<JmxBeanSupport.Feature> enabled = getEnabledJmxFeatures(conf);
if (!enabled.isEmpty()) {
int port = 0;
try {
port = Integer.parseInt(getJMXPort(conf));
} catch (Exception e) {
//ignore: bad data in launch config.
}
if (port==0) {
port = PortFinder.findFreePort(); //slightly better than calling JmxBeanSupport.randomPort()
}
String[] enableLiveBeanArgs = DebugPlugin.parseArguments(JmxBeanSupport.jmxBeanVmArgs(port, enabled));
for (int i = 0; i < enableLiveBeanArgs.length; i++) {
vmArgs.add(i, enableLiveBeanArgs[i]);
}
ILaunch currentLaunch = CURRENT_LAUNCH.get();
if (currentLaunch!=null) {
currentLaunch.setAttribute(JMX_PORT, ""+port);
}
}
// Fast startup VM args
String fastStartupArgs = BootActivator.getDefault().getPreferenceStore().getString(BootPreferences.PREF_BOOT_FAST_STARTUP_JVM_ARGS);
boolean fastStartup = getFastStartup(conf) && fastStartupArgs != null && !fastStartupArgs.isEmpty();
if (fastStartup && !fastStartupArgs.trim().isEmpty()) {
// Add space to separate fast startup args from the preceding arguments
vmArgs.addAll(Arrays.asList(DebugPlugin.parseArguments(fastStartupArgs)));
}
String projectName = AbstractBootLaunchConfigurationDelegate.getProjectName(conf);
vmArgs.add("-D" + SPRING_PROJECT_NAME_ATTRIBUTE + "=" + projectName);
return DebugPlugin.renderArguments(vmArgs.toArray(new String[vmArgs.size()]), null);
} catch (Exception e) {
Log.log(e);
}
return super.getVMArguments(conf);
}
public static EnumSet<Feature> getEnabledJmxFeatures(ILaunchConfiguration conf) {
EnumSet<Feature> enabled = EnumSet.noneOf(Feature.class);
if (getEnableJmx(conf)) {
enabled.add(Feature.JMX);
}
if (getEnableLiveBeanSupport(conf)) {
enabled.add(Feature.LIVE_BEAN_GRAPH);
}
if (getEnableLifeCycle(conf)) {
enabled.add(Feature.LIFE_CYCLE);
}
return enabled;
}
public static boolean isHiddenFromBootDash(ILaunchConfiguration conf) {
try {
return conf.getAttribute(HIDE_FROM_BOOT_DASH, DEFAULT_HIDE_FROM_BOOT_DASH);
} catch (CoreException e) {
Log.log(e);
}
return DEFAULT_HIDE_FROM_BOOT_DASH;
}
public static void setHiddenFromBootDash(ILaunchConfigurationWorkingCopy conf, boolean hide) {
conf.setAttribute(HIDE_FROM_BOOT_DASH, hide);
}
/**
* Retrieve the 'Enable Life Cycle Tracking' option from the config. Note that
* this doesn't necesarily mean that this feature is effectively enabled as
* it is only supported on recent enough versions of Boot.
* <p>
* See also the 'supportsLifeCycleManagement' method.
*/
public static boolean getEnableLifeCycle(ILaunchConfiguration conf) {
try {
return conf.getAttribute(ENABLE_LIFE_CYCLE, DEFAULT_ENABLE_LIFE_CYCLE);
} catch (Exception e) {
Log.log(e);
}
return DEFAULT_ENABLE_LIFE_CYCLE;
}
public static boolean getFastStartup(ILaunchConfiguration conf) {
boolean defaultValue = BootActivator.getDefault().getPreferenceStore().getBoolean(BootPreferences.PREF_BOOT_FAST_STARTUP_DEFAULT);
try {
return conf.getAttribute(FAST_STARTUP, defaultValue);
} catch (Exception e) {
Log.log(e);
}
return defaultValue;
}
public static void setEnableJMX(ILaunchConfigurationWorkingCopy wc, boolean enable) {
wc.setAttribute(ENABLE_JMX, enable);
}
public static void setEnableLifeCycle(ILaunchConfigurationWorkingCopy wc, boolean enable) {
wc.setAttribute(ENABLE_LIFE_CYCLE, enable);
}
public static void setFastStartup(ILaunchConfigurationWorkingCopy wc, boolean enable) {
wc.setAttribute(FAST_STARTUP, enable);
}
public static boolean canUseLifeCycle(ILaunchConfiguration conf) {
return BootLaunchConfigurationDelegate.getEnableLifeCycle(conf)
&& BootLaunchConfigurationDelegate.supportsLifeCycleManagement(conf);
}
public static boolean canUseLifeCycle(ILaunch launch) {
ILaunchConfiguration conf = launch.getLaunchConfiguration();
return conf!=null && canUseLifeCycle(conf);
}
public static boolean supportsLifeCycleManagement(ILaunchConfiguration conf) {
IProject p = getProject(conf);
if (p!=null) {
return BootPropertyTester.supportsLifeCycleManagement(p);
}
return false;
}
/**
* Sets minimal default values to create a runnable launch configuration.
*/
public static void setDefaults(ILaunchConfigurationWorkingCopy wc,
IProject project,
String mainType
) throws CoreException {
setProcessFactory(wc, BootProcessFactory.class);
setProject(wc, project);
if (project!=null && project.hasNature(SpringBootCore.M2E_NATURE)) {
enableMavenClasspathProviders(wc);
} else if (project!=null && project.hasNature(SpringBootCore.BUILDSHIP_NATURE)) {
enableGradleClasspathProviders(wc);
}
wc.setAttribute(IJavaLaunchConfigurationConstants.ATTR_EXCLUDE_TEST_CODE, true);
if (mainType!=null) {
setMainType(wc, mainType);
}
setEnableJMX(wc, DEFAULT_ENABLE_JMX);
setEnableLiveBeanSupport(wc, DEFAULT_ENABLE_LIVE_BEAN_SUPPORT());
setEnableLifeCycle(wc, DEFAULT_ENABLE_LIFE_CYCLE);
setTerminationTimeout(wc,""+DEFAULT_TERMINATION_TIMEOUT);
setJMXPort(wc, ""+DEFAULT_JMX_PORT);
if (!OsUtils.isWindows()) {
setVMArgs(wc, ENABLE_CHEAP_ENTROPY_VM_ARGS);
}
}
public static void setTerminationTimeout(ILaunchConfigurationWorkingCopy wc, String value) {
wc.setAttribute(TERMINATION_TIMEOUT, ""+value);
}
public static String getTerminationTimeout(ILaunchConfiguration conf) {
try {
return conf.getAttribute(TERMINATION_TIMEOUT, ""+DEFAULT_TERMINATION_TIMEOUT);
} catch (Exception e) {
Log.log(e);
return ""+DEFAULT_TERMINATION_TIMEOUT;
}
}
public static long getTerminationTimeoutAsLong(ILaunchConfiguration conf) {
String v = getTerminationTimeout(conf);
if (StringUtil.hasText(v)) {
try {
return Long.parseLong(v);
} catch (Exception e) {
Log.log(e);
}
}
return DEFAULT_TERMINATION_TIMEOUT;
}
private static void setVMArgs(ILaunchConfigurationWorkingCopy wc, String vmArgs) {
wc.setAttribute(ATTR_VM_ARGUMENTS, vmArgs);
}
/**
* Notes:
* <p>
* 1. we are assuming that the processFactoryId is the same as the classname of
* the class that implements it. This is not a given, but a convenient and logical convention.
* <p>
* 2. The class must be registered to this ID using plugin.xml (extension point
* org.eclipse.debug.core.processFactories)
*/
public static void setProcessFactory(ILaunchConfigurationWorkingCopy wc, Class<? extends BootProcessFactory> klass) {
wc.setAttribute(ATTR_PROCESS_FACTORY_ID, klass.getName());
}
public static boolean getEnableJmx(ILaunchConfiguration conf) {
try {
return conf.getAttribute(ENABLE_JMX, DEFAULT_ENABLE_JMX);
} catch (Exception e) {
Log.log(e);
}
return DEFAULT_ENABLE_JMX;
}
public static boolean getEnableLiveBeanSupport(ILaunchConfiguration conf) {
try {
return conf.getAttribute(ENABLE_LIVE_BEAN_SUPPORT, DEFAULT_ENABLE_LIVE_BEAN_SUPPORT());
} catch (Exception e) {
Log.log(e);
}
return DEFAULT_ENABLE_LIVE_BEAN_SUPPORT();
}
public static String getJMXPort(ILaunchConfiguration conf) {
try {
return conf.getAttribute(JMX_PORT, "");
} catch (CoreException e) {
Log.log(e);
}
return "";
}
public static void setEnableLiveBeanSupport(ILaunchConfigurationWorkingCopy conf, boolean value) {
conf.setAttribute(ENABLE_LIVE_BEAN_SUPPORT, value);
}
public static void setJMXPort(ILaunchConfigurationWorkingCopy conf, String portAsStr) {
conf.setAttribute(JMX_PORT, portAsStr);
}
public static String getProfile(ILaunchConfiguration conf) {
try {
return conf.getAttribute(PROFILE, DEFAULT_PROFILE);
} catch (CoreException e) {
Log.log(e);
return DEFAULT_PROFILE;
}
}
public static void setProfile(ILaunchConfigurationWorkingCopy conf, String profile) {
conf.setAttribute(PROFILE, profile);
}
public static ILaunchConfiguration duplicate(ILaunchConfiguration conf) throws CoreException {
String newName = DebugPlugin.getDefault().getLaunchManager().generateLaunchConfigurationName(conf.getName());
ILaunchConfigurationWorkingCopy copy = conf.copy(newName);
int existingJmxPort = getJMXPortAsInt(conf);
if (existingJmxPort>0) {
//change port on duplicated config, but only if it was set to a specific port.
setJMXPort(copy, ""+JmxBeanSupport.randomPort());
}
return copy.doSave();
}
public static ILaunchConfigurationWorkingCopy createWorkingCopy(String nameHint) throws CoreException {
String name = getLaunchMan().generateLaunchConfigurationName(nameHint);
return getConfType().newInstance(null, name);
}
public static ILaunchConfigurationType getConfType() {
return getLaunchMan().getLaunchConfigurationType(TYPE_ID);
}
public static ILaunchConfiguration createConf(IType type) throws CoreException {
ILaunchConfigurationWorkingCopy wc = createWorkingCopy(type);
return wc.doSave();
}
public static ILaunchConfigurationWorkingCopy createWorkingCopy(IType type) throws CoreException {
ILaunchConfigurationWorkingCopy wc = null;
ILaunchConfigurationType configType = getConfType();
IProject project = type.getJavaProject().getProject();
String projectName = type.getJavaProject().getElementName();
String shortTypeName = type.getTypeQualifiedName('.');
String typeName = type.getFullyQualifiedName();
wc = configType.newInstance(null, getLaunchMan().generateLaunchConfigurationName(
projectName+" - "+shortTypeName));
BootLaunchConfigurationDelegate.setDefaults(wc, project, typeName);
wc.setMappedResources(new IResource[] {type.getUnderlyingResource()});
return wc;
}
public static ILaunchConfiguration createConf(IProject project) throws CoreException {
return createConf(JavaCore.create(project));
}
public static ILaunchConfiguration createConf(IJavaProject project) throws CoreException {
ILaunchConfigurationWorkingCopy wc = null;
ILaunchConfigurationType configType = getConfType();
String projectName = project.getElementName();
wc = configType.newInstance(null, getLaunchMan().generateLaunchConfigurationName(projectName));
BootLaunchConfigurationDelegate.setDefaults(wc, project.getProject(), null);
wc.setMappedResources(new IResource[] {project.getUnderlyingResource()});
return wc.doSave();
}
public static int getJMXPortAsInt(ILaunchConfiguration conf) {
String jmxPortStr = getJMXPort(conf);
if (jmxPortStr!=null) {
try {
return Integer.parseInt(jmxPortStr);
} catch (Exception e) {
//Ignore
}
}
return -1;
}
public static int getJMXPortAsInt(ILaunch launch) {
String jmxPortStr = launch.getAttribute(JMX_PORT);
if (jmxPortStr!=null) {
try {
return Integer.parseInt(jmxPortStr);
} catch (Exception e) {
//Ignore
}
}
return -1;
}
public static long getTerminationTimeoutAsLong(ILaunch launch) {
ILaunchConfiguration conf = launch.getLaunchConfiguration();
if (conf!=null) {
return BootLaunchConfigurationDelegate.getTerminationTimeoutAsLong(conf);
}
return BootLaunchConfigurationDelegate.DEFAULT_TERMINATION_TIMEOUT;
}
public static boolean supportsAnsiConsoleOutput() {
Bundle bundle = Platform.getBundle("net.mihai-nita.ansicon.plugin");
return bundle != null && bundle.getState() != Bundle.UNINSTALLED;
}
public static boolean getEnableAnsiConsoleOutput(ILaunchConfiguration conf) {
boolean defaultValue = supportsAnsiConsoleOutput();
try {
return conf.getAttribute(ANSI_CONSOLE_OUTPUT, defaultValue);
} catch (CoreException e) {
return defaultValue;
}
}
public static void setEnableAnsiConsoleOutput(ILaunchConfigurationWorkingCopy wc, boolean enable) {
wc.setAttribute(ANSI_CONSOLE_OUTPUT, enable);
}
@Override
public String[][] getClasspathAndModulepath(ILaunchConfiguration conf) throws CoreException {
//with Java 9 Beta installed we need this method, because getClasspath is no longer called.
try {
if (useThinWrapper(conf)) {
return new String[][] {
getClasspath(conf),
new String[] {}
};
};
return super.getClasspathAndModulepath(conf);
} catch (Throwable e) {
throw ExceptionUtil.coreException(e);
}
}
@Override
public String[] getClasspath(ILaunchConfiguration conf) throws CoreException {
if (useThinWrapper(conf)) {
File thinWrapper = BootPreferences.getInstance().getThinWrapper();
Assert.isLegal(thinWrapper!=null, "'Use thin wrapper' option is selected, but thin wrapper is not defined");
Assert.isLegal(thinWrapper.isFile(), "'Use thin wrapper' option is selected, but thin wrapper ("+thinWrapper+") is not an existing file");
return new String[] {
thinWrapper.getAbsolutePath()
};
}
return super.getClasspath(conf);
}
@Override
public String getMainTypeName(ILaunchConfiguration conf) throws CoreException {
try {
if (useThinWrapper(conf)) {
return getThinWrapperMain(conf);
}
} catch (Exception e) {
Log.log(e);
}
return super.getMainTypeName(conf);
}
protected String getThinWrapperMain(ILaunchConfiguration conf) throws Exception {
File thinWrapper = BootPreferences.getInstance().getThinWrapper();
try (ZipInputStream zipStream = new ZipInputStream(new FileInputStream(thinWrapper))) {
ZipEntry zipEntry;
while (null!=(zipEntry = zipStream.getNextEntry())) {
String name = zipEntry.getName();
if (name.equals("META-INF/MANIFEST.MF")) {
Manifest manifest = new Manifest(zipStream);
String mainClass = manifest.getMainAttributes().getValue("Main-Class");
if (mainClass!=null) {
return mainClass;
} else {
throw new IllegalArgumentException("Thin wrapper '"+thinWrapper+"' doesn't have a 'Main-Class' attribute in its jar manifest");
}
}
}
throw new IllegalArgumentException("No META-INF/MANIFEST.MF found in '"+thinWrapper+"'. Is it a proper 'thin boot wrapper' jar?");
}
}
/**
* Copy a given launch config into a 'clone' that has all the same attributes but
* a different type id.
*/
public static ILaunchConfigurationWorkingCopy copyAs(ILaunchConfiguration conf,
String newType) throws CoreException {
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType launchConfigurationType = launchManager
.getLaunchConfigurationType(newType);
ILaunchConfigurationWorkingCopy wc = launchConfigurationType.newInstance(null,
launchManager.generateLaunchConfigurationName(conf.getName()));
wc.setAttributes(conf.getAttributes());
return wc;
}
public static boolean useThinWrapper(ILaunchConfiguration conf) {
try {
return BootPreferences.getInstance().getThinWrapper()!=null && conf.getAttribute(USE_THIN_WRAPPER, DEFAULT_USE_THIN_WRAPPER);
} catch (CoreException e) {
Log.log(e);
}
return DEFAULT_USE_THIN_WRAPPER;
}
public static void setUseThinWrapper(ILaunchConfigurationWorkingCopy wc, boolean b) {
wc.setAttribute(USE_THIN_WRAPPER, b);
}
}

View File

@@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 2015 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
import org.eclipse.debug.ui.CommonTab;
import org.eclipse.debug.ui.EnvironmentTab;
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;
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaArgumentsTab;
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaClasspathTab;
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaJRETab;
public class BootLaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup {
/**
* @see ILaunchConfigurationTabGroup#createTabs(ILaunchConfigurationDialog, String)
*/
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
new BootMainTab(),
new JavaArgumentsTab(),
new JavaJRETab(),
new BootClasspathTab(),
new SourceLookupTab(),
new EnvironmentTab(),
new CommonTab()
};
setTabs(tabs);
}
}

View File

@@ -0,0 +1,173 @@
/*******************************************************************************
* Copyright (c) 2013 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaApplicationLaunchShortcut;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.internal.debug.ui.launcher.LauncherMessages;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.StructuredSelection;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springsource.ide.eclipse.commons.frameworks.core.ExceptionUtil;
import org.springsource.ide.eclipse.commons.frameworks.core.maintype.MainTypeFinder;
@SuppressWarnings("restriction")
public class BootLaunchShortcut extends JavaApplicationLaunchShortcut {
/**
* Launch configuration id of the configs created by this shortcut.
*/
public static final String LAUNCH_CONFIG_TYPE_ID = BootLaunchConfigurationDelegate.TYPE_ID;
@Override
public IType[] findTypes(Object[] elements, IRunnableContext context)
throws InterruptedException, CoreException {
//For spring boot app, instead of searching for a main type in the entire project and all its
// libraries... try to look inside the project's pom for the corresponding property.
for (Object e : elements) {
if (e instanceof IProject) {
try {
e = JavaCore.create((IProject)e);
} catch (Throwable ignore) {
}
}
{
IType type = isMainMethod(elements[0]);
if(type != null) {
return new IType[] {type};
}
}
if (e instanceof IJavaElement) {
if (e instanceof IType) {
if (hasMainMethod((IType) e)) {
return new IType[] {(IType)e};
}
}
if (e instanceof ICompilationUnit) {
for (IType t : ((ICompilationUnit) e).getAllTypes()) {
if (hasMainMethod(t)) {
return new IType[] {t};
}
}
}
final IJavaProject jp = ((IJavaElement)e).getJavaProject();
final IType[][] result = new IType[][] { null };
try {
context.run(/*fork*/false, /*true*/false, new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
try {
result[0] = MainTypeFinder.guessMainTypes(jp, monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
}
}
});
} catch (InvocationTargetException exception) {
throw ExceptionUtil.coreException(exception);
}
return result[0];
}
}
//This isn't the best thing to to do as it searches also in all the library jars for main types. But it is
// only a fallback option if the above code failed. (Or should we rather signal an error instead?)
return super.findTypes(elements, context);
}
private boolean hasMainMethod(IType t) {
try {
for (IMethod m : t.getMethods()) {
if (m.isMainMethod()) {
return true;
}
}
} catch (Exception e) {
BootActivator.log(e);
}
return false;
}
/**
* Returns the smallest enclosing <code>IType</code> if the specified object is a main method, or <code>null</code>
* @param o the object to inspect
* @return the smallest enclosing <code>IType</code> of the specified object if it is a main method or <code>null</code> if it is not
*/
private IType isMainMethod(Object o) {
if(o instanceof IAdaptable) {
IAdaptable adapt = (IAdaptable) o;
IJavaElement element = (IJavaElement) adapt.getAdapter(IJavaElement.class);
if(element != null && element.getElementType() == IJavaElement.METHOD) {
try {
IMethod method = (IMethod) element;
if(method.isMainMethod()) {
return method.getDeclaringType();
}
}
catch (JavaModelException jme) {JDIDebugUIPlugin.log(jme);}
}
}
return null;
}
@Override
protected ILaunchConfigurationType getConfigurationType() {
return getLaunchManager().getLaunchConfigurationType(LAUNCH_CONFIG_TYPE_ID);
}
/**
* Overridden, copied and changed to alter the generated launch configuration name.
*/
@Override
public ILaunchConfiguration createConfiguration(IType type) {
ILaunchConfiguration config = null;
try {
config = BootLaunchConfigurationDelegate.createConf(type);
} catch (CoreException exception) {
MessageDialog.openError(JDIDebugUIPlugin.getActiveWorkbenchShell(), LauncherMessages.JavaLaunchShortcut_3, exception.getStatus().getMessage());
}
return config;
}
/**
* Returns the singleton launch manager.
*
* @return launch manager
*/
private ILaunchManager getLaunchManager() {
return DebugPlugin.getDefault().getLaunchManager();
}
public static void launch(IProject project, String mode) {
BootLaunchShortcut shortcut = new BootLaunchShortcut();
StructuredSelection selection = new StructuredSelection(new Object[] {project});
shortcut.launch(selection, mode);
}
}

View File

@@ -0,0 +1,112 @@
/*******************************************************************************
* 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.launch;
import static org.springframework.ide.eclipse.boot.launch.AbstractBootLaunchConfigurationDelegate.DEFAULT_ENABLE_DEBUG_OUTPUT;
import static org.springframework.ide.eclipse.boot.launch.AbstractBootLaunchConfigurationDelegate.ENABLE_DEBUG_OUTPUT;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.ANSI_CONSOLE_OUTPUT;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.DEFAULT_HIDE_FROM_BOOT_DASH;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.FAST_STARTUP;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.*;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.core.BootPreferences;
import org.springframework.ide.eclipse.boot.core.SpringBootCore;
import org.springframework.ide.eclipse.boot.launch.livebean.EnableJmxFeaturesModel;
import org.springframework.ide.eclipse.boot.util.Log;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
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;
/**
* 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 BootLaunchUIModel {
// TODO pulling out all of the logic for regression testing is a work in progress.
// Only some of the UI elements are represented in here. The other ones
// still are 'tangled' with the UI widgetry code.
public static class MainTypeValidator extends Validator {
private LiveVariable<String> mainTypeName;
public MainTypeValidator(LiveVariable<String> n) {
this.mainTypeName = n;
dependsOn(mainTypeName);
}
protected ValidationResult compute() {
String name = mainTypeName.getValue();
if (!StringUtil.hasText(name)) {
return ValidationResult.error("No Main type selected");
}
return ValidationResult.OK;
}
}
public final SelectProjectLaunchTabModel project;
public final MainTypeNameLaunchTabModel mainTypeName;
public final ProfileLaunchTabModel profile;
public final LaunchTabSelectionModel<Boolean> enableDebug;
public final EnableJmxFeaturesModel enableJmx;
public final LaunchTabSelectionModel<Boolean> hideFromDash;
public final LaunchTabSelectionModel<Boolean> ansiConsoleOutput;
public final LaunchTabSelectionModel<Boolean> fastStartup;
public final LaunchTabSelectionModel<Boolean> useThinWrapper;
public BootLaunchUIModel(IProfileHistory profileHistory) {
project = SelectProjectLaunchTabModel.create();
mainTypeName = MainTypeNameLaunchTabModel.create();
profile = ProfileLaunchTabModel.create(project.selection, profileHistory);
enableDebug = CheckboxLaunchTabModel.create(ENABLE_DEBUG_OUTPUT, DEFAULT_ENABLE_DEBUG_OUTPUT);
enableJmx = new EnableJmxFeaturesModel();
hideFromDash = CheckboxLaunchTabModel.create(HIDE_FROM_BOOT_DASH, DEFAULT_HIDE_FROM_BOOT_DASH);
ansiConsoleOutput = CheckboxLaunchTabModel.create(ANSI_CONSOLE_OUTPUT, BootLaunchConfigurationDelegate.supportsAnsiConsoleOutput());
fastStartup = CheckboxLaunchTabModel.create(FAST_STARTUP, BootActivator.getDefault().getPreferenceStore()
.getBoolean(BootPreferences.PREF_BOOT_FAST_STARTUP_DEFAULT));
Validator thinWrapperValidator = new Validator() {
@Override
protected ValidationResult compute() {
IProject p = project.selection.getValue();
if (p!=null && p.isAccessible() && useThinWrapper!=null) {
Boolean useThinWrapperValue = useThinWrapper.selection.getValue();
if (useThinWrapperValue!=null && useThinWrapperValue) {
try {
if (!p.hasNature(SpringBootCore.M2E_NATURE)) {
return ValidationResult.error("Thin launcher support only works for Maven projects");
}
} catch (CoreException e) {
Log.log(e);
}
}
}
return ValidationResult.OK;
}
};
useThinWrapper = CheckboxLaunchTabModel.create(USE_THIN_WRAPPER, DEFAULT_USE_THIN_WRAPPER, thinWrapperValidator);
thinWrapperValidator.dependsOn(useThinWrapper.selection);
thinWrapperValidator.dependsOn(project.selection);
}
}

View File

@@ -0,0 +1,80 @@
/*******************************************************************************
* 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.launch;
import static org.springframework.ide.eclipse.boot.ui.BootUIImages.BOOT_ICON;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.swt.graphics.Image;
import org.springframework.ide.eclipse.boot.core.BootPreferences;
import org.springframework.ide.eclipse.boot.launch.livebean.EnableJmxSection;
import org.springframework.ide.eclipse.boot.launch.profiles.ProfileHistory;
import org.springframework.ide.eclipse.boot.launch.profiles.ProfileLaunchTabSection;
import org.springframework.ide.eclipse.boot.launch.properties.PropertiesEditorSection;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
import org.springframework.ide.eclipse.boot.launch.util.GroupLaunchTabSection;
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.CheckboxSection;
import org.springsource.ide.eclipse.commons.livexp.ui.HLineSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
/**
* @author Kris De Volder
*/
public class BootMainTab extends LaunchConfigurationTabWithSections implements IPageWithSections {
@Override
public String getName() {
return "Spring Boot";
}
@Override
public Image getImage() {
return BootUIImages.getImage(BOOT_ICON);
}
@Override
protected List<IPageSection> createSections() {
BootLaunchUIModel model = new BootLaunchUIModel(new ProfileHistory());
List<WizardPageSection> jvmArgsSections = new ArrayList<>(4);
jvmArgsSections.add(new EnableDebugSection(this, model.enableDebug));
jvmArgsSections.add(new HideFromBootDashSection(this, model.hideFromDash));
jvmArgsSections.add(new FastStartupLaunchTabSection(this, model.fastStartup));
/*
* Show UI for enabling/disabling ANSI console output only if
* IDE supports ANSI console output
*/
if (BootLaunchConfigurationDelegate.supportsAnsiConsoleOutput()) {
jvmArgsSections.add(new DelegatingLaunchConfigurationTabSection(this, model.ansiConsoleOutput, new CheckboxSection(this, model.ansiConsoleOutput, "ANSI console output")));
}
if (BootPreferences.getInstance().getThinWrapper()!=null) {
jvmArgsSections.add(new UseThinWrapperSection(this, model.useThinWrapper));
}
return Arrays.asList(new IPageSection[] {
SelectProjectLaunchTabSection.create(this, model.project),
new MainTypeLaunchTabSection(this, model.project.selection, model.mainTypeName),
new ProfileLaunchTabSection(this, model.profile),
new HLineSection(this),
new GroupLaunchTabSection(this, null, jvmArgsSections.toArray(new WizardPageSection[jvmArgsSections.size()])).columns(2),
new EnableJmxSection(this, model.enableJmx),
new HLineSection(this),
new PropertiesEditorSection(this, model.project.selection)
});
}
}

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2017, 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.launch;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.m2e.jdt.internal.launch.MavenRuntimeClasspathProvider;
@SuppressWarnings("restriction")
public class BootMavenClassPathProvider extends MavenRuntimeClasspathProvider {
@Override
public IRuntimeClasspathEntry[] resolveClasspath(IRuntimeClasspathEntry[] entries,
ILaunchConfiguration configuration) throws CoreException {
return super.resolveClasspath(entries, BootLaunchConfigurationDelegate.copyAs(configuration, JDT_JAVA_APPLICATION));
}
}

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* 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.launch;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.m2e.jdt.internal.launch.MavenSourcePathProvider;
@SuppressWarnings("restriction")
public class BootMavenSourcePathProvider extends MavenSourcePathProvider {
@Override
public IRuntimeClasspathEntry[] resolveClasspath(IRuntimeClasspathEntry[] entries,
ILaunchConfiguration configuration) throws CoreException {
return super.resolveClasspath(entries, BootLaunchConfigurationDelegate.copyAs(configuration, JDT_JAVA_APPLICATION));
}
}

View File

@@ -0,0 +1,77 @@
/*******************************************************************************
* 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.launch;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.springframework.ide.eclipse.boot.core.BootActivator;
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;
/**
* Basic model for a checkbox who's value is stored into a launch configuration attribute.
*
* @author Kris De Volder
*/
public class CheckboxLaunchTabModel extends LaunchTabSelectionModel<Boolean> {
public static CheckboxLaunchTabModel create(String attributeName, boolean defaultValue, LiveExpression<ValidationResult> validator) {
LiveVariable<Boolean> enable = new LiveVariable<>();
return new CheckboxLaunchTabModel(attributeName, defaultValue, enable, validator);
}
public static CheckboxLaunchTabModel create(String attributeName, boolean defaultValue) {
LiveVariable<Boolean> enable = new LiveVariable<>();
return new CheckboxLaunchTabModel(attributeName, defaultValue, enable, Validator.OK);
}
private String attributeName;
private boolean defaultValue;
protected CheckboxLaunchTabModel(String attributeName, boolean defaultValue, LiveVariable<Boolean> selection, LiveExpression<ValidationResult> validator) {
super(selection, validator);
this.attributeName = attributeName;
this.defaultValue = defaultValue;
}
@Override
public void initializeFrom(ILaunchConfiguration conf) {
selection.setValue(getAttribute(conf));
getDirtyState().setValue(false);
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy conf) {
setAttribute(conf, selection.getValue());
getDirtyState().setValue(false);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy conf) {
setAttribute(conf, defaultValue);
}
protected boolean getAttribute(ILaunchConfiguration conf) {
try {
return conf.getAttribute(attributeName, defaultValue);
} catch (CoreException e) {
BootActivator.log(e);
}
return defaultValue;
}
protected void setAttribute(ILaunchConfigurationWorkingCopy conf, Boolean value) {
conf.setAttribute(attributeName, value);
}
}

View File

@@ -0,0 +1,72 @@
/*******************************************************************************
* 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.launch;
import java.util.function.Predicate;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationListener;
import org.eclipse.debug.core.ILaunchManager;
import org.springframework.ide.eclipse.boot.util.Log;
/**
* Listens for deleted launch configurations and checks for 'orphaned' launches.
* Any orphaned launches which meet the 'isInteresting' test are terminated.
*
* @author Kris De Volder
*/
public class DeletedLaunchConfTerminator implements ILaunchConfigurationListener {
private ILaunchManager lm;
private final Predicate<ILaunch> isInteresting;
public DeletedLaunchConfTerminator(ILaunchManager lm, Predicate<ILaunch> isInteresting) {
this.isInteresting = isInteresting;
this.lm = lm;
this.lm.addLaunchConfigurationListener(this);
}
@Override
public void launchConfigurationAdded(ILaunchConfiguration configuration) {
//don't care
}
@Override
public void launchConfigurationChanged(ILaunchConfiguration configuration) {
//don't care
}
@Override
public void launchConfigurationRemoved(ILaunchConfiguration deletedConf) {
//Careful we are somewhat limited on what we can do with this config since it has been deleted.
System.out.println("Deleted conf: "+deletedConf);
for (ILaunch l : lm.getLaunches()) {
if (l.canTerminate()
&& !l.isTerminated()
&& isInteresting.test(l)
) {
ILaunchConfiguration conf = l.getLaunchConfiguration();
//Careful conf could be null (because it was deleted), or not.
//It depends on when we get here (race condition of some kind).
//I've seen it happen either way! So we have to handle both cases!
if (conf==null || deletedConf.equals(conf)) {
try {
l.terminate();
} catch (Exception e) {
Log.log(e);
}
}
}
};
}
}

View File

@@ -0,0 +1,26 @@
/*******************************************************************************
* 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.launch;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
import org.springsource.ide.eclipse.commons.livexp.ui.CheckboxSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
/**
* @author Kris De Volder
*/
public class EnableDebugSection extends DelegatingLaunchConfigurationTabSection {
public EnableDebugSection(IPageWithSections owner, LaunchTabSelectionModel<Boolean> model) {
super(owner, model, new CheckboxSection(owner, model, "Enable debug output"));
}
}

View File

@@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2015 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch;
import static org.springsource.ide.eclipse.commons.livexp.core.ValidationResult.error;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.Assert;
import org.springframework.ide.eclipse.boot.core.BootPropertyTester;
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.core.Validator;
/**
* Validation for a 'existing spring-boot-project selection'.
*
* @author Kris De Volder
*/
public class ExistingBootProjectSelectionValidator extends Validator {
private LiveExpression<IProject> selection;
public ExistingBootProjectSelectionValidator(LiveExpression<IProject> selection) {
Assert.isNotNull(selection);
this.selection = selection;
dependsOn(selection);
}
@Override
protected ValidationResult compute() {
IProject p = selection.getValue();
if (p==null) {
return error("No project selected");
} else if (!p.exists()) {
return error("Project '"+p.getName()+"' does not exist in the workspace");
} else if (!p.isAccessible()) {
return error("Project '"+p.getName()+"' is closed");
} else if (!BootPropertyTester.isBootProject(p)) {
return error("Project '"+p.getName()+"' does not look like a Boot project");
}
return ValidationResult.OK;
}
}

View File

@@ -0,0 +1,94 @@
/*******************************************************************************
* Copyright (c) 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.launch;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.core.BootPreferences;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
import org.springsource.ide.eclipse.commons.livexp.core.FieldModel;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.SelectionModel;
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.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
/**
* Fast startup UI section for the Boot launch configuration wizard
*
* @author Alex Boyko
*
*/
public class FastStartupLaunchTabSection extends DelegatingLaunchConfigurationTabSection {
static class UI extends WizardPageSection {
private String label;
private Button button;
private SelectionModel<Boolean> model;
public UI(IPageWithSections owner, FieldModel<Boolean> model) {
this(owner, new SelectionModel<>(model.getVariable(), model.getValidator()), model.getLabel());
}
public UI(IPageWithSections owner, SelectionModel<Boolean> model, String label) {
super(owner);
this.model = model;
this.label = label;
}
@Override
public void createContents(Composite page) {
button = new Button(page, SWT.CHECK);
button.setText(label);
button.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
model.selection.setValue(button.getSelection());
if (button.getSelection()) {
IPreferenceStore store = BootActivator.getDefault().getPreferenceStore();
boolean remind = store.getBoolean(BootPreferences.PREF_BOOT_FAST_STARTUP_REMIND_MESSAGE);
if (remind) {
MessageDialogWithToggle.openWarning(e.display.getActiveShell(), "Fast Startup Warning",
"Fast startup performs Java VM arguments tuning to enhance startup at the possible expense of a performance of the application during its runtime. It is recommended to use this setting for debugging purposes only",
"Do not show this message again in the future", !remind, store,
BootPreferences.PREF_BOOT_FAST_STARTUP_REMIND_MESSAGE);
}
}
}
});
model.selection.addListener(new ValueListener<Boolean>() {
public void gotValue(LiveExpression<Boolean> exp, Boolean selected) {
if (selected!=null) {
button.setSelection(selected);
}
}
});
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return model.validator;
}
}
public FastStartupLaunchTabSection(IPageWithSections owner, LaunchTabSelectionModel<Boolean> model) {
super(owner, model, new UI(owner, model, "Fast startup"));
}
}

View File

@@ -0,0 +1,26 @@
/*******************************************************************************
* 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.launch;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
import org.springsource.ide.eclipse.commons.livexp.ui.CheckboxSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
/**
* @author Kris De Volder
*/
public class HideFromBootDashSection extends DelegatingLaunchConfigurationTabSection {
public HideFromBootDashSection(IPageWithSections owner, LaunchTabSelectionModel<Boolean> model) {
super(owner, model, new CheckboxSection(owner, model, "Hide from Boot Dash").grabHor(true));
}
}

View File

@@ -0,0 +1,25 @@
/*******************************************************************************
* 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.launch;
import org.eclipse.core.resources.IProject;
/**
* Provides some means to retrieve profile history for projects. I.e. a list of
* spring boot profiles that have been used to launch a given project in the past.
*
* @author Kris De Volder
*/
public interface IProfileHistory {
String[] getHistory(IProject value);
}

View File

@@ -0,0 +1,121 @@
/*******************************************************************************
* 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.launch;
import java.util.ArrayList;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventFilter;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.jdt.debug.core.IJavaExceptionBreakpoint;
import org.eclipse.jdt.debug.core.IJavaThread;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springsource.ide.eclipse.commons.livexp.ui.Disposable;
/**
* An instance of this type causes a given type of exception to be 'ignored' by the debug UI.
*
* @author Kris De Volder
*/
public class IgnoreExceptionOfType implements IDebugEventFilter, Disposable {
//This class works as follows:
// - an 'EventFilter is attached to the debug model.
// - if an event is detected that corresponds to suspending on an exception breakpoint of the 'ignored' type.
// => a) the event is'filtered' (so that it won't cause a popup to swithc to debugging perspective
// b) the suspended thread is automatically resumed (so that it looks to the user as if the breakpoint wasn't hit)
// Why such a strange way of doing things?
// Because these methods do not work:
//
// a) try to use a BreakPointListener instead. It didn't seem possible to suppress a breakpoint from being installed
// or the breakpoint from causing a suspension. (There's a voting mechanism, but its not possible to override a
// vote to install / suspend cast by another existing listener)
// b) use a DebugEventListener (instead of a filter). This does work, but it has the annoying effect that,
// because breakpoint is actually triggered and then auto-resumed, the debug UI still pops up a dialog
// to switch into the debug perspective. The filter allows us to 'hide' that event. (note that just filtering
// the event isn't enough as this only stops the debug ui from reacting to the breakpoint being hit, but
// the thread still ends up suspended nevertheless)
// private static final boolean DEBUG = true;
//
// private static void debug(String string) {
// if (DEBUG) {
// System.out.println(string);
// }
// }
private ILaunch launch;
private String exceptionToIgnore;
/**
* Create an instance and register it as a listened of the debug model.
*/
public IgnoreExceptionOfType(ILaunch launch, String exceptionToIgnore) {
this.launch = launch;
this.exceptionToIgnore = exceptionToIgnore;
DebugPlugin.getDefault().addDebugEventFilter(this);
}
@Override
public void dispose() {
DebugPlugin.getDefault().removeDebugEventFilter(this);
}
@Override
public DebugEvent[] filterDebugEvents(DebugEvent[] events) {
ArrayList<DebugEvent> filtered = new ArrayList<>(events.length);
for (DebugEvent e : events) {
if (select(e)) {
filtered.add(e);
}
}
if (!filtered.isEmpty()) {
return filtered.toArray(new DebugEvent[filtered.size()]);
}
return null;
}
private boolean select(DebugEvent e) {
if (e.getKind()==DebugEvent.SUSPEND && e.getDetail()==DebugEvent.BREAKPOINT) {
IJavaThread source = (IJavaThread) e.getSource();
if (launch==null || launch.equals(source.getLaunch())) {
IBreakpoint[] bps = source.getBreakpoints();
if (bps!=null) {
for (IBreakpoint bp : bps) {
if (bp instanceof IJavaExceptionBreakpoint) {
IJavaExceptionBreakpoint ebp = (IJavaExceptionBreakpoint) bp;
if (exceptionToIgnore.equals(ebp.getExceptionTypeName())) {
resume(source);
return false;
}
}
}
}
}
}
return true;
}
private void resume(IJavaThread source) {
try {
source.resume();
} catch (DebugException e) {
BootActivator.log(e);
}
}
}

View File

@@ -0,0 +1,58 @@
/*******************************************************************************
* 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.launch;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.springframework.ide.eclipse.boot.launch.util.ILaunchConfigurationTabModel;
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.SelectionModel;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.ValueListener;
/**
* Abstract base clase for a {@link SelectionModel} that also implements {@link ILaunchConfigurationTabModel}
* <p>
* Note that this class manages the 'dirtyState' for the model but only partially. I.e. the state gets set
* to true when the model contents changes, but it is up to the subclass to set it to false whenever
* the model state is synched with {@link ILaunchConfigurationWorkingCopy} state.
*
* @author Kris De Volder
*/
public abstract class LaunchTabSelectionModel<T> extends SelectionModel<T> implements ILaunchConfigurationTabModel {
private LiveVariable<Boolean> dirtyState = new LiveVariable<Boolean>(false);
public LaunchTabSelectionModel(final LiveVariable<T> selection, LiveExpression<ValidationResult> validator) {
super(selection, validator);
selection.addListener(new ValueListener<T>() {
@Override
public void gotValue(LiveExpression<T> exp, T value) {
getDirtyState().setValue(true);
}
});
}
public LaunchTabSelectionModel(SelectionModel<T> selection) {
this(selection.selection, selection.validator);
}
@Override
public final LiveExpression<ValidationResult> getValidator() {
return validator;
}
@Override
public final LiveVariable<Boolean> getDirtyState() {
return dirtyState;
}
}

View File

@@ -0,0 +1,201 @@
/*******************************************************************************
* Copyright (c) 2005, 2012, 2015 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Pivotal Software - Bits and pieces copied to create MainTypeSelectionSection
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.internal.debug.ui.launcher.DebugTypeSelectionDialog;
import org.eclipse.jdt.internal.debug.ui.launcher.LauncherMessages;
import org.eclipse.jdt.internal.debug.ui.launcher.MainMethodSearchEngine;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
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.core.BootActivator;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
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.SelectionModel;
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.IPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.UIConstants;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
/**
* A LaunchConfigurationTabSection hat allows user to choose main type
* in a project.
* <p>
* Basically a simplified version to the JDT JavaMainTab. some of the
* rarely used options have been removed and the code converted
* to be useable as a LaunchConfigurationTabSection so it can be
* composed more freely with other LaunchTabSectons onto a page.
*
* @author Kris De Volder
*/
@SuppressWarnings("restriction")
public class MainTypeLaunchTabSection extends DelegatingLaunchConfigurationTabSection {
public MainTypeLaunchTabSection(IPageWithSections owner, LiveVariable<IProject> project, MainTypeNameLaunchTabModel model) {
super(owner, model, createUi(owner, project, model));
}
private static IPageSection createUi(IPageWithSections owner, final LiveVariable<IProject> project, final SelectionModel<String> mainTypeName) {
return new WizardPageSection(owner) {
private Text fMainText;
private Button fSearchButton;
private LiveVariable<String> mainTypeName() {
return mainTypeName.selection;
}
private LiveVariable<IProject> project() {
return project;
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return mainTypeName.validator;
}
public void createContents(Composite parent) {
String label = "Main type";
GridDataFactory grabHor = GridDataFactory.fillDefaults().grab(true, false);
Composite field = new Composite(parent, SWT.NONE);
GridLayout layout = GridLayoutFactory.fillDefaults().numColumns(3).create();
// GridLayout layout = new GridLayout();
// layout.numColumns = 3;
// layout.marginBottom = 0;
// layout.marginTop = 0;
// layout.marginLeft = 0;
// layout.marginRight = 0;
// layout.marginWidth = 0;
field.setLayout(layout);
grabHor.applyTo(field);
Label fieldNameLabel = new Label(field, SWT.NONE);
fieldNameLabel.setText(label);
GridDataFactory.fillDefaults()
.hint(UIConstants.fieldLabelWidthHint(fieldNameLabel), SWT.DEFAULT)
.align(SWT.BEGINNING, SWT.CENTER)
.applyTo(fieldNameLabel);
fMainText = new Text(field, SWT.BORDER);
grabHor.applyTo(fMainText);
fMainText.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
mainTypeName().setValue(fMainText.getText());
}
});
mainTypeName().addListener(new ValueListener<String>() {
public void gotValue(LiveExpression<String> exp, String newName) {
if (fMainText!=null) {
if (newName!=null) {
String oldName = fMainText.getText();
//Don't set the text if its not actually changed.
// Otherwise change events from typing in the widget will cause the
// text to be set and this (on Mac OS) cause the widget's text selection
// to be reset as well.
if (!oldName.equals(newName)) {
fMainText.setText(newName);
}
}
}
}
});
fSearchButton = SWTFactory.createPushButton(field, "Search...", null);
fSearchButton.addSelectionListener(new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent e) {
}
public void widgetSelected(SelectionEvent e) {
handleSearchButtonSelected();
}
});
}
/**
* Show a dialog that lists all main types
*/
protected void handleSearchButtonSelected() {
IJavaProject project = getJavaProject();
IJavaElement[] elements = null;
if ((project == null) || !project.exists()) {
IJavaModel model = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot());
if (model != null) {
try {
elements = model.getJavaProjects();
}
catch (JavaModelException e) {JDIDebugUIPlugin.log(e);}
}
}
else {
elements = new IJavaElement[]{project};
}
if (elements == null) {
elements = new IJavaElement[]{};
}
int constraints = IJavaSearchScope.SOURCES;
IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(elements, constraints);
MainMethodSearchEngine engine = new MainMethodSearchEngine();
IType[] types = null;
try {
types = engine.searchMainMethods(owner.getRunnableContext(), searchScope, false);
}
catch (Exception e) {
BootActivator.log(e);
return;
}
DebugTypeSelectionDialog mmsd = new DebugTypeSelectionDialog(owner.getShell(), types, LauncherMessages.JavaMainTab_Choose_Main_Type_11);
if (mmsd.open() == Window.CANCEL) {
return;
}
Object[] results = mmsd.getResult();
IType type = (IType)results[0];
if (type != null) {
fMainText.setText(type.getFullyQualifiedName());
project().setValue(type.getJavaProject().getProject());
}
}
private IJavaProject getJavaProject() {
try {
return JavaCore.create(project().getValue());
} catch (Exception e) {
BootActivator.log(e);
return null;
}
}
};
}
}

View File

@@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (c) 2015 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.launch.BootLaunchUIModel.MainTypeValidator;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
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;
/**
* @author Kris De Volder
*/
public class MainTypeNameLaunchTabModel extends LaunchTabSelectionModel<String> {
private MainTypeNameLaunchTabModel(LiveVariable<String> selection,
LiveExpression<ValidationResult> validator) {
super(selection, validator);
}
public static MainTypeNameLaunchTabModel create() {
LiveVariable<String> n = new LiveVariable<>("");
MainTypeValidator nv = new MainTypeValidator(n);
return new MainTypeNameLaunchTabModel(n, nv);
}
@Override
public void initializeFrom(ILaunchConfiguration config) {
try {
selection.setValue(config.getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, ""));
getDirtyState().setValue(false);
} catch (Exception e) {
BootActivator.log(e);
}
}
public void performApply(ILaunchConfigurationWorkingCopy config) {
BootLaunchConfigurationDelegate.setMainType(config, StringUtil.trim(selection.getValue()));
getDirtyState().setValue(false);
}
public void setDefaults(ILaunchConfigurationWorkingCopy config) {
BootLaunchConfigurationDelegate.setMainType(config, "");
}
}

View File

@@ -0,0 +1,147 @@
/*******************************************************************************
* 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.launch;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.DEFAULT_PROFILE;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.getProfile;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.setProfile;
import java.util.LinkedHashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.util.JavaProjectUtil;
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;
/**
* @author Kris De Volder
*/
public class ProfileLaunchTabModel extends LaunchTabSelectionModel<String> {
private static final Pattern FILE_NAME_PAT = Pattern.compile("^application-(.*)\\.properties$");
private static final int FILE_NAME_PAT_GROUP = 1;
private static final String[] NO_PROFILES = new String[0];
public static ProfileLaunchTabModel create(LiveExpression<IProject> project, IProfileHistory profileHistory) {
LiveVariable<String> profile = new LiveVariable<String>("");
return new ProfileLaunchTabModel(project, profile, Validator.OK, profileHistory);
}
private final LiveExpression<String[]> profiles;
protected ProfileLaunchTabModel(LiveExpression<IProject> project, LiveVariable<String> selection, LiveExpression<ValidationResult> validator, IProfileHistory profileHistory) {
super(selection, validator);
profiles = new ProfileOptions(project, profileHistory);
}
@Override
public void initializeFrom(ILaunchConfiguration conf) {
String profile = getProfile(conf);
selection.setValue(profile);
getDirtyState().setValue(false);
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy conf) {
String profile = selection.getValue();
setProfile(conf, profile);
//Note: it seems logical to update profile history here, but it gets called
// too often. I.e. not just when the user presses apply button, but really
// any time a change happens in the launch tab, the LaunchConfig editor copies
// all the data from the ui to a workingcopy by calling performApply.
//Therefore, history is instead updated when a launch config is actually
//launched. (See launch method in BootLaunchConfigurationDelegate)
getDirtyState().setValue(false);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy conf) {
setProfile(conf, DEFAULT_PROFILE);
}
/**
* Live expression that computes list of profiles to show in pull-down menu.
*/
public LiveExpression<String[]> profileOptions() {
return profiles;
}
/**
* LiveExpression that computes list of suggested profiles for the selected
* project.
*
* @author Kris De Volder
*/
private class ProfileOptions extends LiveExpression<String[]> {
private final IProfileHistory profileHistory;
private final LiveExpression<IProject> project;
public ProfileOptions(LiveExpression<IProject> project, IProfileHistory profileHistory) {
super(NO_PROFILES);
this.profileHistory = profileHistory;
this.project = project;
dependsOn(project);
}
@Override
protected String[] compute() {
LinkedHashSet<String> profiles = new LinkedHashSet<String>();
discoverValidProfiles(profiles);
addHistoricProfiles(profiles);
return profiles.toArray(new String[profiles.size()]);
}
/**
* Retrieve a stored list of profiles that have been used in the
* past with the selected project. Add these profiles to
* the provided List.
*/
private void addHistoricProfiles(LinkedHashSet<String> profiles) {
IProject proj = project.getValue();
if (proj!=null) {
for (String profile : profileHistory.getHistory(proj)) {
profiles.add(profile);
}
}
}
/**
* @param profiles discovered profiles are added to this array.
*/
private void discoverValidProfiles(LinkedHashSet<String> profiles) {
try {
for (IContainer srcFolder : JavaProjectUtil.getSourceFolders(project.getValue())) {
for (IResource rsrc : srcFolder.members()) {
if (rsrc.getType()==IResource.FILE) {
String name = rsrc.getName();
Matcher matcher = FILE_NAME_PAT.matcher(name);
if (matcher.matches()) {
profiles.add(matcher.group(FILE_NAME_PAT_GROUP));
}
}
}
}
} catch (Exception e) {
BootActivator.log(e);
}
}
}
}

View File

@@ -0,0 +1,75 @@
/*******************************************************************************
* 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.launch;
import java.util.ArrayList;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.springframework.ide.eclipse.boot.core.BootPropertyTester;
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;
public class SelectProjectLaunchTabModel extends LaunchTabSelectionModel<IProject> {
public static SelectProjectLaunchTabModel create() {
LiveVariable<IProject> project = new LiveVariable<IProject>();
ExistingBootProjectSelectionValidator validator = new ExistingBootProjectSelectionValidator(project);
return new SelectProjectLaunchTabModel(project, validator);
}
public SelectProjectLaunchTabModel(LiveVariable<IProject> p,
LiveExpression<ValidationResult> pv) {
super(p, pv);
}
@Override
public void initializeFrom(ILaunchConfiguration conf) {
selection.setValue(BootLaunchConfigurationDelegate.getProject(conf));
getDirtyState().setValue(false);
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy conf) {
BootLaunchConfigurationDelegate.setProject(conf, selection.getValue());
getDirtyState().setValue(false);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy conf) {
BootLaunchConfigurationDelegate.setProject(conf, null);
}
public IProject[] interestingProjects() {
IProject[] allProjects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
ArrayList<IProject> interesting = new ArrayList<IProject>(allProjects.length);
for (IProject p : allProjects) {
if (isInteresting(p)) {
interesting.add(p);
}
}
return interesting.toArray(new IProject[interesting.size()]);
}
/**
* Decides whether given IProject from the workspace is of interest.
* Only projects 'of interest' will be available from the project
* selector's pull-down menu.
*/
protected boolean isInteresting(IProject project) {
return BootPropertyTester.isBootProject(project);
}
}

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
* 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.launch;
import org.eclipse.core.resources.IProject;
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;
import org.springsource.ide.eclipse.commons.livexp.ui.SimpleLabelProvider;
/**
* @author Kris De Volder
*/
public class SelectProjectLaunchTabSection {
public static ILaunchConfigurationTabSection create(IPageWithSections owner, SelectProjectLaunchTabModel model) {
ChooseOneSectionCombo<IProject> ui =
new ChooseOneSectionCombo<IProject>(owner, "Project", model, model.interestingProjects());
//allowTextEdits(ProjectNameParser.INSTANCE);
ui.setLabelProvider(new SimpleLabelProvider() {
public String getText(Object element) {
if (element instanceof IProject) {
return ((IProject) element).getName();
}
return super.getText(element);
}
});
return new DelegatingLaunchConfigurationTabSection(owner, model, ui);
}
}

View File

@@ -0,0 +1,26 @@
/*******************************************************************************
* 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.launch;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
import org.springsource.ide.eclipse.commons.livexp.ui.CheckboxSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
/**
* @author Kris De Volder
*/
public class UseThinWrapperSection extends DelegatingLaunchConfigurationTabSection {
public UseThinWrapperSection(IPageWithSections owner, LaunchTabSelectionModel<Boolean> model) {
super(owner, model, new CheckboxSection(owner, model, "Use Thin Wrapper"));
}
}

View File

@@ -0,0 +1,201 @@
/*******************************************************************************
* Copyright (c) 2013, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.cli;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMRunner;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.VMRunnerConfiguration;
import org.springframework.ide.eclipse.boot.core.cli.BootInstallManager;
import org.springframework.ide.eclipse.boot.core.cli.install.IBootInstall;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.util.Log;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
/**
* Launch Configuration delegate able to start/shut down Spring Boot CLI processes
*
* @author Kris De Volder
* @author Alex Boyko
*
*/
public class BootCliLaunchConfigurationDelegate extends LaunchConfigurationDelegate {
private static String[] getSpringBootClasspath(ILaunchConfiguration conf, IBootInstall install) throws Exception {
File[] bootLibJars = install.getBootLibJars();
List<String> classpath = new ArrayList<>(2 + bootLibJars.length);
classpath.add(".");
Path extensions = install.getHome().toPath().resolve("lib/ext/");
if (Files.exists(extensions)) {
classpath.add(extensions.toString());
}
classpath.addAll(Arrays.stream(bootLibJars).map(jarFile -> jarFile.toString()).collect(Collectors.toList()));
return classpath.toArray(new String[classpath.size()]);
}
/**
* Spring Boot CLI main class to launch (Ideally this shouldn't be overriden)
* @param install Spring Boot CLI install
* @param launch Eclipse's launch
* @param conf Launch configuration
* @return main type class name
* @throws Exception
*/
protected String getMainTypeName(IBootInstall install, ILaunch launch, ILaunchConfiguration conf) throws Exception {
return "org.springframework.boot.loader.JarLauncher";
}
/**
* Environment variables for Spring Boot CLI process
* @param install Spring Boot CLI install
* @param launch Eclipse's launch
* @param conf Launch configuration
* @return environment variables as array of strings, where each string looks like <b>SPRING_HOME=/Users/me/spring-cli</b>
* @throws Exception
*/
protected String[] getEnv(IBootInstall install, ILaunch launch, ILaunchConfiguration conf) throws Exception {
Map<String, String> map = DebugPlugin.getDefault().getLaunchManager().getNativeEnvironment();
IVMInstall vmInstall = verifyVMInstall(conf);
map.put("JAVA_HOME", vmInstall.getInstallLocation().toString());
try {
map.put("SPRING_HOME", install.getHome().toString());
} catch (Exception e) {
Log.log(e);
}
List<String> env = new ArrayList<>(map.size());
String var = null;
for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext();) {
var = iter.next();
String value = map.get(var);
if (value == null) {
value = ""; //$NON-NLS-1$
}
if (var.equalsIgnoreCase("path")) { //$NON-NLS-1$
// if(value.indexOf(jrestr) == -1) {
// value = jrestr+';'+value;
// }
continue;
}
env.add(var+"="+value); //$NON-NLS-1$
}
return env.toArray(new String[env.size()]);
}
/**
* VM arguments for launching the Spring Boot CLI
* @param install Spring Boot CLI install
* @param launch Eclipse's launch
* @param conf Launch configuration
* @return array of VM arguments to launch the CLI
* @throws Exception
*/
protected String[] getVmArgs(IBootInstall install, ILaunch launch, ILaunchConfiguration conf) throws Exception {
if (BootLaunchConfigurationDelegate.supportsAnsiConsoleOutput()) {
return new String[] {"-Dspring.output.ansi.enabled=always"};
} else {
return new String[0];
}
}
/**
* Specific VM attributes for launching Spring Boot CLI
* @param install Spring Boot CLI install
* @param launch Eclipse's launch
* @param conf Launch configuration
* @return specific VM attributes map
* @throws Exception
*/
protected Map<String, Object> getVmSpecificAttributesMap(IBootInstall install, ILaunch launch, ILaunchConfiguration conf) throws Exception {
return Collections.emptyMap();
}
/**
* Program arguments for the Spring Boot CLI. This is the typically overriden method to specify the spring cloud command, i.e. <code>["cloud", "--version"]</code>
* @param install Spring Boot CLI install
* @param launch Eclipse's launch
* @param conf Launch configuration
* @return Spring Boot CLI command as an array of strings
* @throws Exception
*/
protected String[] getProgramArgs(IBootInstall install, ILaunch launch, ILaunchConfiguration conf) throws Exception {
return new String[0];
}
/**
* Working directory for launching the Spring Boot CLI
* @param install Spring Boot CLI install
* @param launch Eclipse's launch
* @param conf Launch configuration
* @return working directory for the launch
* @throws Exception
*/
protected String getWorkingDirectory(IBootInstall install, ILaunch launch, ILaunchConfiguration conf) throws Exception {
return install.getHome().toString();
}
@Override
final public void launch(ILaunchConfiguration conf, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {
//TODO: some common things that Java launch configs do that this one does not (yet) do but probably should
// - offer to save unsaved files
// - check for errors in project
// - source locators (for debugging processes)
// - launching in debug mode
try {
IBootInstall install = BootInstallManager.getInstance().getDefaultInstall();
IVMInstall vm = verifyVMInstall(conf);
IVMRunner runner = vm.getVMRunner(mode);
String mainTypeName = getMainTypeName(install, launch, conf);
String[] classpath = getSpringBootClasspath(conf, install);
VMRunnerConfiguration runConfiguration = new VMRunnerConfiguration(mainTypeName, classpath);
runConfiguration.setProgramArguments(getProgramArgs(install, launch, conf));
runConfiguration.setVMArguments(getVmArgs(install, launch, conf));
runConfiguration.setWorkingDirectory(getWorkingDirectory(install, launch, conf));
runConfiguration.setEnvironment(getEnv(install, launch, conf));
runConfiguration.setVMSpecificAttributesMap(getVmSpecificAttributesMap(install, launch, conf));
runner.run(runConfiguration, launch, monitor);
} catch (Exception e) {
throw ExceptionUtil.coreException(e);
}
}
protected IVMInstall verifyVMInstall(ILaunchConfiguration conf) {
//Extremely simplistic implementation. Just gets the default JVM for this workspace.
//TODO: project specific JVM selection or maybe the JVM should be associated with
// spring boot installation.
return JavaRuntime.getDefaultVMInstall();
}
}

View File

@@ -0,0 +1,94 @@
/*******************************************************************************
* Copyright (c) 2013, 2017 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.cli;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.springframework.ide.eclipse.boot.core.cli.install.IBootInstall;
public class BootGroovyScriptLaunchConfigurationDelegate extends BootCliLaunchConfigurationDelegate {
public static final String ID = "org.springsource.ide.eclipse.boot.groovy.script.launch";
/*
Example of a commandline invocation of the spring boot runtime. This is what we
are to emulate in here:
/usr/lib/jvm/java-7-oracle/bin/java
-cp
.:/home/kdvolder/Applications/spring-0.5.0.M6/bin:/home/kdvolder/Applications/spring-0.5.0.M6/lib/spring-boot-cli-0.5.0.M6.jar
org.springframework.boot.loader.JarLauncher
run
app.groovy
*/
private static final String SCRIPT_RSRC = "spring.groovy.script.rsrc";
public static void setScript(ILaunchConfigurationWorkingCopy wc, IFile rsrc) {
wc.setAttribute(SCRIPT_RSRC, rsrc.getFullPath().toString());
}
private static IFile getScript(ILaunchConfiguration conf) throws CoreException {
String fullPathStr = conf.getAttribute(SCRIPT_RSRC, (String)null);
if (fullPathStr!=null) {
IPath fullPath = new Path(fullPathStr);
Assert.isLegal(fullPath.segmentCount()>=2);
String projectName = fullPath.segment(0);
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
return project.getFile(fullPath.removeFirstSegments(1));
}
return null;
}
private static String getProjectName(ILaunchConfiguration conf) throws CoreException {
String fullPathStr = conf.getAttribute(SCRIPT_RSRC, (String)null);
if (fullPathStr!=null) {
IPath fullPath = new Path(fullPathStr);
return fullPath.segment(0);
}
return null;
}
private static IProject getProject(ILaunchConfiguration conf) throws CoreException {
String name = getProjectName(conf);
if (name!=null) {
return ResourcesPlugin.getWorkspace().getRoot().getProject(name);
}
return null;
}
@Override
protected String getWorkingDirectory(IBootInstall install, ILaunch launch, ILaunchConfiguration conf)
throws Exception {
IProject project = getProject(conf);
return project.getLocation().toFile().toString();
}
@Override
protected String[] getProgramArgs(IBootInstall install, ILaunch launch, ILaunchConfiguration conf) throws Exception {
return new String[] {
"run",
getScript(conf).getProjectRelativePath().toString()
};
}
}

View File

@@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2013, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.cli;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
import org.eclipse.debug.ui.ILaunchConfigurationDialog;
import org.eclipse.debug.ui.ILaunchConfigurationTab;
public class BootGroovyScriptLaunchConfigurationTabGroup extends
AbstractLaunchConfigurationTabGroup {
public BootGroovyScriptLaunchConfigurationTabGroup() {
}
@Override
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
// new SpringCommandTab()
// new AppletParametersTab(),
// new JavaArgumentsTab(),
// new JavaJRETab(),
// new JavaClasspathTab(),
// new CommonTab()
};
setTabs(tabs);
}
}

View File

@@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (c) 2012, 2017 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.cli;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.core.DebugPlugin;
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.ui.DebugUITools;
import org.eclipse.debug.ui.ILaunchShortcut;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.springframework.ide.eclipse.boot.util.Log;
public class BootGroovyScriptLaunchShortcut implements ILaunchShortcut {
@Override
public void launch(ISelection selection, String mode) {
try {
IResource rsrc = getResource(selection);
launch(rsrc, mode);
} catch (Throwable e) {
Log.log(e);
}
}
public void launch(IResource rsrc, String mode) throws CoreException {
if (rsrc!=null && rsrc.getType()==IResource.FILE) {
ILaunchConfiguration conf = createConfiguration((IFile) rsrc);
DebugUITools.launch(conf, mode);
}
}
/**
* Returns the singleton launch manager.
*
* @return launch manager
*/
private ILaunchManager getLaunchManager() {
return DebugPlugin.getDefault().getLaunchManager();
}
protected ILaunchConfiguration createConfiguration(IFile rsrc) throws CoreException {
ILaunchConfigurationType configType = getConfigurationType();
String projectName = rsrc.getProject().getName();
ILaunchConfigurationWorkingCopy wc = configType.newInstance(null, getLaunchManager().generateLaunchConfigurationName(projectName+" "+rsrc.getName()));
BootGroovyScriptLaunchConfigurationDelegate.setScript(wc, rsrc);
wc.setMappedResources(new IResource[] {rsrc});
//Normally you should call:
//config = wc.doSave();
//But we skip it for now. The launch conf will not be saved so it will be 'transient'.
return wc;
}
protected ILaunchConfigurationType getConfigurationType() {
return getLaunchManager().getLaunchConfigurationType(BootGroovyScriptLaunchConfigurationDelegate.ID);
}
private IResource getResource(ISelection selection) {
if (selection instanceof IStructuredSelection) {
IStructuredSelection ss = (IStructuredSelection) selection;
Object el = ss.getFirstElement();
if (el instanceof IResource) {
return (IResource) el;
} else if (el instanceof IAdaptable) {
return (IResource) ((IAdaptable) el).getAdapter(IResource.class);
}
}
return null;
}
@Override
public void launch(IEditorPart editor, String mode) {
try {
IEditorInput input = editor.getEditorInput();
IResource rsrc = (IResource) input.getAdapter(IResource.class);
if (rsrc!=null) {
launch(rsrc, mode);
}
System.out.println(input);
} catch (Throwable e) {
Log.log(e);
}
}
}

View File

@@ -0,0 +1,297 @@
/*******************************************************************************
* Copyright (c) 2017, 2020 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.cli;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugPlugin;
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.IProcess;
import org.eclipse.debug.core.model.RuntimeProcess;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.core.cli.BootInstallManager;
import org.springframework.ide.eclipse.boot.core.cli.install.CloudCliInstall;
import org.springframework.ide.eclipse.boot.core.cli.install.IBootInstall;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.livebean.JmxBeanSupport;
import org.springframework.ide.eclipse.boot.launch.process.BootProcessFactory;
import org.springframework.ide.eclipse.boot.launch.util.PortFinder;
import org.springframework.ide.eclipse.boot.util.version.Version;
import org.springframework.ide.eclipse.boot.util.version.VersionParser;
import org.springframework.ide.eclipse.boot.util.version.VersionRange;
import org.springsource.ide.eclipse.commons.core.util.ProcessUtils;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
/**
* Spring Cloud CLI service launch configuration
*
* @author Alex Boyko
*
*/
public class CloudCliServiceLaunchConfigurationDelegate extends BootCliLaunchConfigurationDelegate {
private static final VersionRange SPRING_CLOUD_CLI_SINGLE_PROCESS_VERSION_RANGE = new VersionRange(new Version(1, 3, 0, null));
private static final VersionRange SPRING_CLOUD_CLI_SUPPORTS_THIN_LAUNCH_PARAM = VersionParser.DEFAULT.parseRange("[1.3.0, 2.2.0)");
public final static String TYPE_ID = "org.springframework.ide.eclipse.boot.launch.cloud.cli.service";
public final static String ATTR_CLOUD_SERVICE_ID = "local-cloud-service-id";
private final static String PREF_DONT_SHOW_PLATFORM_WARNING = "org.springframework.ide.eclipse.boot.launch.cloud.cli.NotSupportedPlatform";
private final static String PREF_DONT_SHOW_JRE_WARNING = "org.springframework.ide.eclipse.boot.launch.cloud.cli.JRE";
private final static String PREF_DONT_SHOW_JDK_WARNING = "org.springframework.ide.eclipse.boot.launch.cloud.cli.JDK";
private List<String> getCloudCliServiceLifeCycleVmArguments(ILaunchConfiguration configuration, int jmxPort) {
List<String> vmArgs = new ArrayList<>();
EnumSet<JmxBeanSupport.Feature> enabled = BootLaunchConfigurationDelegate
.getEnabledJmxFeatures(configuration);
if (!enabled.isEmpty()) {
String enableLiveBeanArgs = JmxBeanSupport.jmxBeanVmArgs(jmxPort, enabled);
vmArgs.addAll(Arrays.asList(enableLiveBeanArgs.split("\n")));
}
return vmArgs;
}
protected String[] getProgramArgs(IBootInstall bootInstall, ILaunch launch, ILaunchConfiguration configuration) {
try {
CloudCliInstall cloudCliInstall = bootInstall.getExtension(CloudCliInstall.class);
if (cloudCliInstall == null) {
Log.error("No Spring Cloud CLI installation found");
} else {
String serviceId = configuration.getAttribute(ATTR_CLOUD_SERVICE_ID, (String) null);
Version cloudCliVersion = cloudCliInstall.getVersion();
List<String> vmArgs = new ArrayList<>();
List<String> args = new ArrayList<>();
args.add(CloudCliInstall.COMMAND_PREFIX);
args.add(serviceId);
if (cloudCliVersion != null && SPRING_CLOUD_CLI_SUPPORTS_THIN_LAUNCH_PARAM.match(cloudCliVersion)) {
args.add("--deployer=thin");
}
args.add("--");
args.add("--logging.level.org.springframework.cloud.launcher.deployer=DEBUG");
// VM argument for the service log output
if (BootLaunchConfigurationDelegate.supportsAnsiConsoleOutput()) {
vmArgs.add("-Dspring.output.ansi.enabled=always");
}
if (CloudCliServiceLaunchConfigurationDelegate.SPRING_CLOUD_CLI_SINGLE_PROCESS_VERSION_RANGE.match(cloudCliVersion)) {
if (!vmArgs.isEmpty()) {
args.add("--spring.cloud.launcher.deployables." + serviceId + ".properties.spring.cloud.deployer.local.javaOpts=" + String.join(",", vmArgs));
}
} else if (CloudCliInstall.CLOUD_CLI_JAVA_OPTS_SUPPORTING_VERSIONS.match(cloudCliVersion)) {
int jmxPort = getJmxPort(configuration);
// Set the JMX port for launch
launch.setAttribute(BootLaunchConfigurationDelegate.JMX_PORT, String.valueOf(jmxPort));
vmArgs.addAll(getCloudCliServiceLifeCycleVmArguments(configuration, jmxPort));
// Set the JMX port connection jvm args for the service
if (!vmArgs.isEmpty()) {
args.add("--spring.cloud.launcher.deployables." + serviceId + ".properties.JAVA_OPTS=" + String.join(",", vmArgs));
}
}
return args.toArray(new String[args.size()]);
}
} catch (Exception e) {
Log.log(e);
}
return new String[0];
}
private int getJmxPort(ILaunchConfiguration configuration) {
int port = 0;
try {
port = Integer.parseInt(BootLaunchConfigurationDelegate.getJMXPort(configuration));
} catch (Exception e) {
// ignore: bad data in launch config.
}
if (port == 0) {
try {
// slightly better than calling JmxBeanSupport.randomPort()
port = PortFinder.findFreePort();
} catch (IOException e) {
Log.log(e);
}
}
return port;
}
public static boolean isLocalCloudServiceLaunch(ILaunchConfiguration conf) {
try {
if (conf!=null) {
String type = conf.getType().getIdentifier();
return TYPE_ID.equals(type);
}
} catch (Exception e) {
Log.log(e);
}
return false;
}
public static ILaunchConfigurationWorkingCopy createLaunchConfig(String serviceId) throws CoreException {
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = launchManager.getLaunchConfigurationType(TYPE_ID);
ILaunchConfigurationWorkingCopy config = type.newInstance(null, serviceId);
// Set default config with life cycle tracking support because it should cover with life cycle tracking and without
BootLaunchConfigurationDelegate.setDefaults(config, null, null);
config.setAttribute(ATTR_CLOUD_SERVICE_ID, serviceId);
// Overwrite process factory class because for latest version of Cloud CLI life cycle tracking through JMX port is not available for services
BootLaunchConfigurationDelegate.setProcessFactory(config, CloudCliProcessFactory.class);
return config;
}
public static boolean canUseLifeCycle(ILaunch launch) {
ILaunchConfiguration conf = launch.getLaunchConfiguration();
return conf!=null && canUseLifeCycle(conf);
}
public static boolean isSingleProcessServiceConfig(ILaunchConfiguration conf) {
try {
if (isCloudCliService(conf)) {
IBootInstall bootInstall = BootInstallManager.getInstance().getDefaultInstall();
if (bootInstall != null) {
Version cloudCliVersion = bootInstall.getExtension(CloudCliInstall.class) == null ? null : bootInstall.getExtension(CloudCliInstall.class).getVersion();
return SPRING_CLOUD_CLI_SINGLE_PROCESS_VERSION_RANGE.match(cloudCliVersion);
}
}
} catch (Exception e) {
// ignore
}
return false;
}
public static boolean isCloudCliService(ILaunchConfiguration conf) {
try {
return TYPE_ID.equals(conf.getType().getIdentifier());
} catch (CoreException e) {
// Ignore
}
return false;
}
public static boolean canUseLifeCycle(ILaunchConfiguration conf) {
try {
if (!isCloudCliService(conf)) {
return false;
}
IBootInstall bootInstall = BootInstallManager.getInstance().getDefaultInstall();
if (bootInstall == null) {
return false;
}
Version cloudCliVersion = bootInstall.getExtension(CloudCliInstall.class) == null ? null : bootInstall.getExtension(CloudCliInstall.class).getVersion();
// Cloud CLI version below 1.2.0 and over 1.3.0 can't have JMX connection to cloud service hence life cycle should be disabled.
if (!canUseLifeCycle(cloudCliVersion)) {
return false;
}
return SPRING_CLOUD_CLI_SINGLE_PROCESS_VERSION_RANGE.match(cloudCliVersion) || BootLaunchConfigurationDelegate.getEnableLifeCycle(conf);
} catch (Exception e) {
// Ignore
}
return false;
}
private static boolean canUseLifeCycle(Version cloudCliVersion) {
// Cloud CLI version below 1.2.0 and over 1.3.0 can't have JMX connection to cloud service hence life cycle should be disabled.
if (cloudCliVersion == null
|| !CloudCliInstall.CLOUD_CLI_JAVA_OPTS_SUPPORTING_VERSIONS.match(cloudCliVersion)
|| SPRING_CLOUD_CLI_SINGLE_PROCESS_VERSION_RANGE.match(cloudCliVersion)) {
return false;
}
return true;
}
public static class CloudCliProcessFactory extends BootProcessFactory {
@Override
public IProcess newProcess(ILaunch launch, Process process, String label, Map<String, String> attributes) {
try {
IBootInstall bootInstall = BootInstallManager.getInstance().getDefaultInstall();
if (bootInstall != null) {
Version cloudCliVersion = bootInstall.getExtension(CloudCliInstall.class) == null ? null : bootInstall.getExtension(CloudCliInstall.class).getVersion();
if (CloudCliServiceLaunchConfigurationDelegate.isSingleProcessServiceConfig(launch.getLaunchConfiguration())) {
final IPreferenceStore store = BootActivator.getDefault().getPreferenceStore();
// Set invalid PID initially thus if PID is failed to be calculated then set PID launch attribute to invalid PID to fallback to default non-JMX process tracking
long pid = -1;
try {
if (ProcessUtils.isLatestJdkForTools()) {
pid = ProcessUtils.getProcessID(process);
} else {
Log.warn("Old JDK version. Need latest JDK to make JMX connection to process using its PID");
if (!store.getBoolean(PREF_DONT_SHOW_JDK_WARNING)) {
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
MessageDialogWithToggle dialog = MessageDialogWithToggle.openWarning(
Display.getCurrent().getActiveShell(), "Cloud CLI Service Info Limitation",
"Cloud service process life-cycle data is limited and port data is unavailable because STS runnning on an old JDK version. Point STS to the latest JDK and restart it to have complete service process life-cycle and port data",
"Don't show this message again",
false, null, null);
store.setValue(PREF_DONT_SHOW_JDK_WARNING, dialog.getToggleState());
});
}
}
} catch (NoClassDefFoundError e) {
Log.warn(e);
if (!store.getBoolean(PREF_DONT_SHOW_JRE_WARNING)) {
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
MessageDialogWithToggle dialog = MessageDialogWithToggle.openWarning(
Display.getCurrent().getActiveShell(), "Cloud CLI Service Info Limitation",
"Cloud service process life-cycle data is limited and port data is unavailable because STS is running on a JRE. Point it to a JDK and restart STS for complete service process life-cycle and port data",
"Don't show this message again",
false, null, null);
store.setValue(PREF_DONT_SHOW_JRE_WARNING, dialog.getToggleState());
});
}
} catch (UnsupportedOperationException e) {
Log.warn(e);
if (!store.getBoolean(PREF_DONT_SHOW_PLATFORM_WARNING)) {
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
MessageDialogWithToggle dialog = MessageDialogWithToggle.openWarning(
Display.getCurrent().getActiveShell(), "Cloud CLI Service Info Limitation",
"Cloud service process life-cycle data is limited and port data is unavailable on the current platform.",
"Don't show this message again",
false, null, null);
store.setValue(PREF_DONT_SHOW_PLATFORM_WARNING, dialog.getToggleState());
});
}
}
launch.setAttribute(BootLaunchConfigurationDelegate.PROCESS_ID, String.valueOf(pid));
return new RuntimeProcess(launch, process, label, attributes);
} else if (canUseLifeCycle(cloudCliVersion)) {
return super.newProcess(launch, process, label, attributes);
}
}
} catch (Exception e) {
Log.log(e);
}
return new RuntimeProcess(launch, process, label, attributes);
}
}
}

View File

@@ -0,0 +1,193 @@
/*******************************************************************************
* Copyright (c) 2012, 2015 Pivotal Software, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License,
* Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
***********************************************************************************/
package org.springframework.ide.eclipse.boot.launch.console;
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.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.internal.ui.views.console.ProcessConsole;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.SubContributionManager;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IConsolePageParticipant;
import org.eclipse.ui.part.IPageBookViewPage;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.devtools.BootDevtoolsClientLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.util.BootLaunchUtils;
import org.springframework.ide.eclipse.boot.ui.BootUIImages;
import org.springframework.ide.eclipse.boot.util.ProcessListenerAdapter;
import org.springframework.ide.eclipse.boot.util.ProcessTracker;
import org.springsource.ide.eclipse.commons.frameworks.core.ExceptionUtil;
/**
* @author Kris De Volder
*/
@SuppressWarnings("restriction")
public class BootConsolePageParticipant implements IConsolePageParticipant {
private class TerminateProcessAction extends Action {
@Override
public void run() {
try {
final IProcess process = console.getProcess();
if (process!=null && process.canTerminate()) {
Job job = new Job("Terminate process") {
protected IStatus run(IProgressMonitor monitor) {
try {
BootLaunchUtils.terminate(process.getLaunch());
return Status.OK_STATUS;
} catch (Exception e) {
return ExceptionUtil.status(e);
}
}
};
job.schedule();
}
} catch (Exception e) {
BootActivator.log(e);
}
}
}
private ProcessConsole console;
private ProcessTracker processTracker;
private TerminateProcessAction terminateAction;
public void activated() {
// ignore
}
public void deactivated() {
// ignore
}
public void dispose() {
if (processTracker!=null) {
processTracker.dispose();
processTracker = null;
}
}
public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
return null;
}
public void init(IPageBookViewPage page, IConsole console) {
this.console = (ProcessConsole)console;
//TODO: This code works assuming that our IConsolePageParticipant is called after the
// ProcessConsolePageParticipant (which creates the action we are replacing
//When testing this that was always the case... but it may not be guaranteed.
if (isDevtoolsClient(this.console) || isBootApp(this.console)) {
terminateAction = new TerminateProcessAction();
try {
terminateAction.setImageDescriptor(BootUIImages.descriptor("icons/stop.png"));
terminateAction.setDisabledImageDescriptor(BootUIImages.descriptor("icons/stop_disabled.png"));
} catch (Exception e) {
BootActivator.log(e);
}
IToolBarManager toolbar = page.getSite().getActionBars().getToolBarManager();
IContributionItem replace = findReplacementItem(toolbar);
if (replace!=null) {
toolbar.appendToGroup(IConsoleConstants.LAUNCH_GROUP, terminateAction);
toolbar.remove(replace);
}
boolean enabled = getConsoleProcess().canTerminate();
terminateAction.setEnabled(enabled);
if (enabled) {
this.processTracker = new ProcessTracker(new ProcessListenerAdapter() {
@Override
public void processTerminated(ProcessTracker tracker, IProcess terminated) {
if (getConsoleProcess().equals(terminated)) {
terminateAction.setEnabled(false);
//after process is terminated... it can't come back to life so... can stop listening now.
tracker.dispose();
}
}
});
}
}
}
private boolean isDevtoolsClient(ProcessConsole console) {
return isLaunchType(console, BootDevtoolsClientLaunchConfigurationDelegate.TYPE_ID);
}
private IProcess getConsoleProcess() {
if (console!=null) {
return console.getProcess();
}
return null;
}
private boolean isBootApp(ProcessConsole console) {
return isLaunchType(console, BootLaunchConfigurationDelegate.TYPE_ID);
}
private IContributionItem findReplacementItem(IToolBarManager toolbar) {
SubContributionManager contributions = (SubContributionManager) toolbar;
for (IContributionItem item : contributions.getItems()) {
if (item instanceof ActionContributionItem) {
ActionContributionItem actionItem = (ActionContributionItem) item;
IAction replaceAction = actionItem.getAction();
if (replaceAction.getClass().getName().equals("org.eclipse.debug.internal.ui.views.console.ConsoleTerminateAction")) {
return item;
}
}
}
return null;
}
private static boolean isLaunchType(ProcessConsole console, String typeId) {
return isLaunchType(console.getProcess(), typeId);
}
private static boolean isLaunchType(IProcess process, String launchTypeId) {
try {
if (process!=null) {
ILaunch launch = process.getLaunch();
if (launch!=null) {
ILaunchConfiguration conf = launch.getLaunchConfiguration();
if (conf!=null) {
return launchTypeId.equals(conf.getType().getIdentifier());
}
}
}
} catch (Exception e) {
BootActivator.log(e);
}
return false;
}
}

View File

@@ -0,0 +1,288 @@
/*******************************************************************************
* 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.launch.devtools;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
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.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
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.launch.AbstractBootLaunchConfigurationDelegate;
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.core.util.StringUtil;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
@SuppressWarnings("restriction")
public class BootDevtoolsClientLaunchConfigurationDelegate extends AbstractBootLaunchConfigurationDelegate {
public static final String TYPE_ID = "org.springframework.ide.eclipse.boot.devtools.client.launch";
private static final long DEBUG_CONNECT_TIMEOUT = 20000;
public static final String REMOTE_SPRING_APPLICATION = "org.springframework.boot.devtools.RemoteSpringApplication";
public static final String REMOTE_URL = "spring.devtools.remote.url";
public static final String REMOTE_SECRET = "spring.devtools.remote.secret";
public static final String DEFAULT_REMOTE_SECRET = "";
public static final String DEBUG_PORT = "spring.devtools.remote.debug.local-port";
private static final String MANAGED = "spring.devtools.isManagedLaunch";
private final ThreadLocal<Integer> localDebugPort = new ThreadLocal<>();
@Override
public String getMainTypeName(ILaunchConfiguration configuration) throws CoreException {
return REMOTE_SPRING_APPLICATION;
}
@Override
public String getProgramArguments(ILaunchConfiguration conf) throws CoreException {
Properties props = getApplicationProperties(conf);
ArrayList<String> args = new ArrayList<>();
addPropertiesArguments(args, props);
String secret = getSecret(conf);
if (StringUtil.hasText(secret)) {
args.add(propertyAssignmentArgument(REMOTE_SECRET, secret));
}
Integer debugPort = localDebugPort.get();
if (debugPort!=null) {
args.add(propertyAssignmentArgument(DEBUG_PORT, ""+debugPort));
}
args.add(getRemoteUrl(conf));
return DebugPlugin.renderArguments(args.toArray(new String[args.size()]), null);
}
private String getSecret(ILaunchConfiguration conf) {
try {
return conf.getAttribute(REMOTE_SECRET, DEFAULT_REMOTE_SECRET);
} catch (CoreException e) {
BootActivator.log(e);
}
return "";
}
@Override
public void launch(ILaunchConfiguration conf, String mode, ILaunch launch, IProgressMonitor mon)
throws CoreException {
boolean isDebug = ILaunchManager.DEBUG_MODE.equals(mode);
int work = isDebug ? 2 : 1;
mon.beginTask("Launching Devtools Client for"+getProjectName(conf), work);
if (isDebug) {
localDebugPort.set(findFreePort());
}
try {
//Launch client: Generally we don't wanna debug the client itself so always use 'RUN_MODE'
super.launch(conf, ILaunchManager.RUN_MODE, launch, new SubProgressMonitor(mon, 1));
if (isDebug) {
//TODO: set debug port in config (or chosen dynamically?)
launchRemote(localDebugPort.get(), conf, launch, new SubProgressMonitor(mon, 1));
}
} finally {
localDebugPort.remove();
mon.done();
}
}
/**
* Returns a free port number on localhost, or -1 if unable to find a free port.
*
* @return a free port number on localhost, or -1 if unable to find a free port
*/
public static int findFreePort() {
try (ServerSocket socket = new ServerSocket(0)) {
socket.setReuseAddress(true);
return socket.getLocalPort();
} catch (IOException e) {
}
return -1;
}
public static String getRemoteUrl(ILaunchConfiguration conf) {
try {
return conf.getAttribute(REMOTE_URL, (String)null);
} catch (CoreException e) {
BootActivator.log(e);
}
return null;
}
public static void setRemoteUrl(ILaunchConfigurationWorkingCopy conf, String value) {
conf.setAttribute(REMOTE_URL, value);
}
public static void setRemoteSecret(ILaunchConfigurationWorkingCopy conf, String value) {
conf.setAttribute(REMOTE_SECRET, value);
}
public static String getRemoteSecret(ILaunchConfiguration conf) {
try {
return conf.getAttribute(REMOTE_SECRET, (String)null);
} catch (CoreException e) {
BootActivator.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(int port, ILaunchConfiguration configuration, final ILaunch launch, IProgressMonitor _monitor) throws CoreException {
if (port<0) {
return;
}
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);
//Don't think we need to set source location since the main launch method already does this.
// 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) {
public void run() throws Exception {
connector.connect(argMap, monitor, launch);
}
};
new ProcessTracker(new ProcessListenerAdapter() {
public void debugTargetTerminated(ProcessTracker tracker, IDebugTarget target) {
handleTermination(tracker, target.getLaunch());
}
public void processTerminated(ProcessTracker tracker, IProcess process) {
handleTermination(tracker, process.getLaunch());
}
private void handleTermination(ProcessTracker tracker, ILaunch targetLaunch) {
if (launch.equals(targetLaunch)) {
tracker.dispose();
terminateAllTargets(launch);
}
}
});
} 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 void setManaged(ILaunchConfigurationWorkingCopy wc, boolean isManaged) {
wc.setAttribute(MANAGED, isManaged);
}
public static boolean isManaged(ILaunchConfiguration c) {
try {
return c.getAttribute(MANAGED, false);
} catch (CoreException e) {
Log.log(e);
return false;
}
}
}

View File

@@ -0,0 +1,41 @@
/*******************************************************************************
* 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.launch.devtools;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
import org.eclipse.debug.ui.CommonTab;
import org.eclipse.debug.ui.EnvironmentTab;
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;
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaClasspathTab;
import org.eclipse.jdt.debug.ui.launchConfigurations.JavaJRETab;
public class BootDevtoolsClientLaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup {
/**
* @see ILaunchConfigurationTabGroup#createTabs(ILaunchConfigurationDialog, String)
*/
public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
ILaunchConfigurationTab[] tabs = new ILaunchConfigurationTab[] {
new BootDevtoolsClientMainTab(),
//new JavaArgumentsTab(), // Removed client is launched with specific arguments set from main tab params
new JavaJRETab(),
new JavaClasspathTab(),
new SourceLookupTab(),
new EnvironmentTab(),
new CommonTab()
};
setTabs(tabs);
}
}

View File

@@ -0,0 +1,210 @@
/*******************************************************************************
* Copyright (c) 2012 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.devtools;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.debug.core.DebugPlugin;
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.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.ILaunchShortcut;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.core.BootPropertyTester;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
@SuppressWarnings("restriction")
public class BootDevtoolsClientLaunchShortcut implements ILaunchShortcut {
@Override
public void launch(ISelection selection, String mode) {
try {
IResource rsrc = getResource(selection);
launch(rsrc, mode);
} catch (Throwable e) {
BootActivator.log(e);
}
}
public void launch(IResource rsrc, String mode) throws CoreException {
if (rsrc!=null && rsrc.getType()==IResource.PROJECT) {
ILaunchConfiguration conf = findOrCreateConfiguration((IProject) rsrc);
if (conf!=null) {
if (isLaunchable(conf)) {
DebugUITools.launch(conf, mode);
} else {
IStructuredSelection selection = new StructuredSelection(new Object[] { conf });
DebugUITools.openLaunchConfigurationDialogOnGroup(getShell(), selection, getLaunchGroup(mode));
}
}
}
}
/**
* Decide whether a launch conf is ready to launch as is or should be opened in
* launc conf editor to allow user to fill in more info.
*/
private boolean isLaunchable(ILaunchConfiguration conf) {
IProject project = BootLaunchConfigurationDelegate.getProject(conf);
String url = BootDevtoolsClientLaunchConfigurationDelegate.getRemoteUrl(conf);
return project!=null &&
BootPropertyTester.isBootProject(project) &&
BootPropertyTester.hasDevtools(project) &&
StringUtil.hasText(url);
}
private String getLaunchGroup(String launchMode) {
if (ILaunchManager.RUN_MODE.equals(launchMode)) {
return IDebugUIConstants.ID_RUN_LAUNCH_GROUP;
} else if (ILaunchManager.DEBUG_MODE.equals(launchMode)) {
return IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP;
} else {
return IDebugUIConstants.ID_DEBUG_LAUNCH_GROUP;
}
}
private ILaunchConfiguration findOrCreateConfiguration(IProject project) throws CoreException {
List<ILaunchConfiguration> candidates = findConfigurations(project);
if (candidates.isEmpty()) {
return createConfiguration(project);
} else if (candidates.size()==1) {
return candidates.get(0);
} else {
return chooseConfiguration(project, candidates);
}
}
private List<ILaunchConfiguration> findConfigurations(IProject project) throws CoreException {
ILaunchManager lm = getLaunchManager();
List<ILaunchConfiguration> configs = new ArrayList<>();
for (ILaunchConfiguration c : lm.getLaunchConfigurations(getLaunchType())) {
if (Objects.equals(BootLaunchConfigurationDelegate.getProject(c), project)) {
configs.add(c);
}
}
return configs;
}
/**
* Returns a configuration from the given collection of configurations that should be launched,
* or <code>null</code> to cancel. Default implementation opens a selection dialog that allows
* the user to choose one of the specified launch configurations. Returns the chosen configuration,
* or <code>null</code> if the user cancels.
*
* @param configList list of configurations to choose from
* @return configuration to launch or <code>null</code> to cancel
*/
private ILaunchConfiguration chooseConfiguration(IProject project, List<ILaunchConfiguration> configList) {
IDebugModelPresentation labelProvider = DebugUITools.newDebugModelPresentation();
ElementListSelectionDialog dialog= new ElementListSelectionDialog(getShell(), labelProvider);
dialog.setElements(configList.toArray());
dialog.setTitle("Several Devtools Client Configs found for "+project.getName());
dialog.setMessage("Select an existing configuration");
dialog.setMultipleSelection(false);
int result = dialog.open();
labelProvider.dispose();
if (result == Window.OK) {
return (ILaunchConfiguration) dialog.getFirstResult();
}
return null;
}
/**
* Convenience method to return the active workbench window shell.
*
* @return active workbench window shell
*/
protected Shell getShell() {
return JDIDebugUIPlugin.getActiveWorkbenchShell();
}
private ILaunchConfigurationType getLaunchType() {
return getLaunchManager().getLaunchConfigurationType(BootDevtoolsClientLaunchConfigurationDelegate.TYPE_ID);
}
/**
* Returns the singleton launch manager.
*
* @return launch manager
*/
private ILaunchManager getLaunchManager() {
return DebugPlugin.getDefault().getLaunchManager();
}
protected ILaunchConfiguration createConfiguration(IProject project) throws CoreException {
ILaunchConfigurationType configType = getConfigurationType();
String projectName = project.getName();
ILaunchConfigurationWorkingCopy wc = configType.newInstance(null, getLaunchManager().generateLaunchConfigurationName("devtools-client["+projectName+"]"));
setDefaults(wc, project);
wc.setMappedResources(new IResource[] {project});
ILaunchConfiguration config = wc.doSave();
return config;
}
private void setDefaults(ILaunchConfigurationWorkingCopy wc, IProject project) {
BootLaunchConfigurationDelegate.setProject(wc, project);
}
protected ILaunchConfigurationType getConfigurationType() {
return getLaunchManager().getLaunchConfigurationType(BootDevtoolsClientLaunchConfigurationDelegate.TYPE_ID);
}
private IResource getResource(ISelection selection) {
if (selection instanceof IStructuredSelection) {
IStructuredSelection ss = (IStructuredSelection) selection;
Object el = ss.getFirstElement();
if (el instanceof IResource) {
return (IResource) el;
} else if (el instanceof IAdaptable) {
//Warning older Eclipse API 'getAdapter() Object not IResource
Object o = ((IAdaptable) el).getAdapter(IResource.class);
return (IResource)o;
}
}
return null;
}
@Override
public void launch(IEditorPart editor, String mode) {
try {
IEditorInput input = editor.getEditorInput();
Object rsrc = input.getAdapter(IResource.class);
if (rsrc!=null) {
launch((IResource)rsrc, mode);
}
} catch (Throwable e) {
BootActivator.log(e);
}
}
}

View File

@@ -0,0 +1,65 @@
/*******************************************************************************
* 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.launch.devtools;
import org.eclipse.core.resources.IProject;
import org.springframework.ide.eclipse.boot.launch.ExistingBootProjectSelectionValidator;
import org.springframework.ide.eclipse.boot.launch.SelectProjectLaunchTabModel;
import org.springsource.ide.eclipse.commons.livexp.core.CompositeValidator;
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.validators.UrlValidator;
/**
* 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 BootDevtoolsClientLaunchUIModel {
public final SelectProjectLaunchTabModel project;
public final StringFieldLaunchTabModel remoteUrl;
public final StringFieldLaunchTabModel remoteSecret;
public BootDevtoolsClientLaunchUIModel() {
project = createProjectSelectionModel();
remoteUrl = createRemoteUrlModel();
remoteSecret = createRemoteSecretModel();
}
private StringFieldLaunchTabModel createRemoteUrlModel() {
StringFieldModel field = new StringFieldModel("Remote Url", "");
field.validator(
new UrlValidator(field)
.allowedSchemes("http", "https")
);
return new StringFieldLaunchTabModel(field, BootDevtoolsClientLaunchConfigurationDelegate.REMOTE_URL);
}
private StringFieldLaunchTabModel createRemoteSecretModel() {
StringFieldModel field = new StringFieldModel("Remote Secret", "");
return new StringFieldLaunchTabModel(field, BootDevtoolsClientLaunchConfigurationDelegate.REMOTE_SECRET);
}
private SelectProjectLaunchTabModel createProjectSelectionModel() {
LiveVariable<IProject> project = new LiveVariable<IProject>();
CompositeValidator validator = new CompositeValidator();
validator.addChild(new ExistingBootProjectSelectionValidator(project));
validator.addChild(new DevtoolsEnabledValidator(project));
return new SelectProjectLaunchTabModel(project, validator);
}
}

View File

@@ -0,0 +1,57 @@
/*******************************************************************************
* 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.launch.devtools;
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.launch.SelectProjectLaunchTabSection;
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 BootDevtoolsClientMainTab extends LaunchConfigurationTabWithSections implements IPageWithSections {
@Override
public String getName() {
return "Devtools Client";
}
@Override
public Image getImage() {
return BootUIImages.getImage(BOOT_DEVTOOLS_ICON);
}
@Override
protected List<IPageSection> createSections() {
BootDevtoolsClientLaunchUIModel model = new BootDevtoolsClientLaunchUIModel();
return Arrays.asList(new IPageSection[] {
SelectProjectLaunchTabSection.create(this, model.project),
StringFieldLaunchTabSection.create(this, model.remoteUrl),
StringFieldLaunchTabSection.create(this, model.remoteSecret),
// new MainTypeLaunchTabSection(this, model.project.selection).readonly(true),
// new ProfileLaunchTabSection(this, model.profile),
// new HLineSection(this),
// new EnableDebugSection(this, model.enableDebug),
// new EnableJmxSection(this, model.enableJmx),
// new HLineSection(this),
// new PropertiesTableSection(this, model.project.selection)
});
}
}

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* 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.launch.devtools;
import org.eclipse.core.resources.IProject;
import org.springframework.ide.eclipse.boot.core.BootPropertyTester;
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;
public class DevtoolsEnabledValidator extends Validator {
private LiveVariable<IProject> project;
public DevtoolsEnabledValidator(LiveVariable<IProject> project) {
this.project = project;
dependsOn(project);
}
@Override
protected ValidationResult compute() {
IProject p = project.getValue();
if (p!=null) {
if (!BootPropertyTester.hasDevtools(p)) {
return ValidationResult.error("Project '"+p.getName()+"' does not have spring-boot-devtools on its classpath");
}
}
return ValidationResult.OK;
}
}

View File

@@ -0,0 +1,67 @@
/*******************************************************************************
* 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.launch.devtools;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.launch.LaunchTabSelectionModel;
import org.springsource.ide.eclipse.commons.livexp.core.StringFieldModel;
public class StringFieldLaunchTabModel extends LaunchTabSelectionModel<String> {
public final StringFieldModel field;
public String attributeId;
public StringFieldLaunchTabModel(StringFieldModel field, String attributeId) {
super(field.getVariable(), field.getValidator());
this.field = field;
this.attributeId = attributeId;
}
@Override
public void initializeFrom(ILaunchConfiguration conf) {
selection.setValue(getAttribute(conf));
getDirtyState().setValue(false);
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy conf) {
setAttribute(conf, selection.getValue());
getDirtyState().setValue(false);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy conf) {
setAttribute(conf, getDefaultValue());
}
/////////////////////////////
private String getAttribute(ILaunchConfiguration conf) {
try {
return conf.getAttribute(attributeId, getDefaultValue());
} catch (CoreException e) {
BootActivator.log(e);
}
return getDefaultValue();
}
private void setAttribute(ILaunchConfigurationWorkingCopy conf, String value) {
conf.setAttribute(attributeId, value);
}
public String getDefaultValue() {
return field.getDefaultValue();
}
}

View File

@@ -0,0 +1,26 @@
/*******************************************************************************
* 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.launch.devtools;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
import org.springframework.ide.eclipse.boot.launch.util.LaunchConfigurationTabWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.StringFieldSection;
public class StringFieldLaunchTabSection {
public static IPageSection create(LaunchConfigurationTabWithSections owner, StringFieldLaunchTabModel field) {
StringFieldSection ui = new StringFieldSection(owner, field.field);
return new DelegatingLaunchConfigurationTabSection(owner, field, ui);
}
}

View File

@@ -0,0 +1,201 @@
/*******************************************************************************
* 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.launch.livebean;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.DEFAULT_ENABLE_JMX;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.DEFAULT_ENABLE_LIFE_CYCLE;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.DEFAULT_ENABLE_LIVE_BEAN_SUPPORT;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.DEFAULT_JMX_PORT;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.DEFAULT_TERMINATION_TIMEOUT;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.setEnableJMX;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.setEnableLifeCycle;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.setEnableLiveBeanSupport;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.setJMXPort;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.setTerminationTimeout;
import static org.springsource.ide.eclipse.commons.livexp.core.ValidationResult.error;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.osgi.framework.Bundle;
import org.springframework.ide.eclipse.boot.launch.BootLaunchActivator;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.util.ILaunchConfigurationTabModel;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
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.core.ValueListener;
/**
* Model for 'enable live bean support' widgetry on a launchconfiguration tab.
*
* @author Kris De Volder
*/
public class EnableJmxFeaturesModel implements ILaunchConfigurationTabModel {
private static final int MAX_PORT = 65536;
public final String portFieldName = "JMX Port";
public final String timeOutFieldName = "Termination timeout";
public final LiveVariable<Boolean> jmxEnabled;
public final LiveVariable<Boolean> liveBeanEnabled;
public final LiveVariable<Boolean> lifeCycleEnabled;
public final LiveVariable<String> port;
public final LiveVariable<String> terminationTimeout;
private final Validator validator;
private LiveVariable<Boolean> dirtyState = new LiveVariable<>(false);
@SuppressWarnings("unchecked")
public EnableJmxFeaturesModel() {
this.jmxEnabled = new LiveVariable<>(DEFAULT_ENABLE_JMX);
this.liveBeanEnabled = new LiveVariable<>(DEFAULT_ENABLE_LIVE_BEAN_SUPPORT());
this.lifeCycleEnabled = new LiveVariable<>(DEFAULT_ENABLE_LIFE_CYCLE);
autoDisableWhenJmxDisabled(liveBeanEnabled, lifeCycleEnabled);
this.port = new LiveVariable<>("");
this.terminationTimeout = new LiveVariable<>("");
jmxEnabled.addListener(makeDirty());
liveBeanEnabled.addListener(makeDirty());
lifeCycleEnabled.addListener(makeDirty());
port.addListener(makeDirty());
terminationTimeout.addListener(makeDirty());
this.validator = new Validator() {
{
dependsOn(jmxEnabled);
dependsOn(lifeCycleEnabled);
dependsOn(liveBeanEnabled);
dependsOn(terminationTimeout);
dependsOn(port);
}
@Override
protected ValidationResult compute() {
boolean isEnabled = jmxEnabled.getValue();
if (isEnabled) {
String portStr = port.getValue();
if (!hasText(portStr)) {
return error(portFieldName+" must be specified");
}
try {
int portValue = Integer.parseInt(portStr.trim());
if (portValue<0) {
return error(portFieldName + " should be a positive integer or 0");
} else if (portValue>MAX_PORT) {
return error(portFieldName + " should be smaller than "+MAX_PORT);
}
} catch (NumberFormatException e) {
return error(portFieldName+" can't be parsed as an Integer");
}
}
if (lifeCycleEnabled.getValue()) {
String timeoutStr = terminationTimeout.getValue();
if (!hasText(timeoutStr)) {
return error(timeOutFieldName+" must be specified");
}
timeoutStr = timeoutStr.trim();
try {
long timeout = Long.parseLong(timeoutStr);
if (!(timeout > 0)) {
return error(timeOutFieldName+" must be positive");
}
} catch (NumberFormatException e) {
return error(timeOutFieldName+" can't be parsed as an Integer");
}
}
// if (liveBeanEnabled.getValue() && !jmxEnabled.getValue()) {
// return error("Live Bean support requires JMX to be enabled");
// }
// if (lifeCycleEnabled.getValue() && !jmxEnabled.getValue()) {
// return error("Lifecycle Management requires JMX to be enabled");
// }
return ValidationResult.OK;
}
};
}
private void autoDisableWhenJmxDisabled(@SuppressWarnings("unchecked") LiveVariable<Boolean>... featuresToDisable) {
jmxEnabled.addListener((exp, value) -> {
if (!value) {
for (LiveVariable<Boolean> feature : featuresToDisable) {
feature.setValue(false);
}
}
});
}
private boolean hasText(String portStr) {
return portStr!=null && !portStr.trim().equals("");
}
@SuppressWarnings("rawtypes")
protected ValueListener makeDirty() {
return new ValueListener() {
public void gotValue(LiveExpression exp, Object value) {
dirtyState.setValue(true);
}
};
}
@Override
public void initializeFrom(ILaunchConfiguration conf) {
jmxEnabled.setValue(BootLaunchConfigurationDelegate.getEnableJmx(conf));
liveBeanEnabled.setValue(BootLaunchConfigurationDelegate.getEnableLiveBeanSupport(conf));
lifeCycleEnabled.setValue(BootLaunchConfigurationDelegate.getEnableLifeCycle(conf));
port.setValue(BootLaunchConfigurationDelegate.getJMXPort(conf));
terminationTimeout.setValue(""+BootLaunchConfigurationDelegate.getTerminationTimeout(conf));
getDirtyState().setValue(false);
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy conf) {
setEnableJMX(conf, jmxEnabled.getValue());
setEnableLiveBeanSupport(conf, liveBeanEnabled.getValue());
setEnableLifeCycle(conf, lifeCycleEnabled.getValue());
setJMXPort(conf, StringUtil.trim(port.getValue()));
setTerminationTimeout(conf, StringUtil.trim(terminationTimeout.getValue()));
getDirtyState().setValue(false);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy conf) {
setEnableLiveBeanSupport(conf, DEFAULT_ENABLE_LIVE_BEAN_SUPPORT());
setEnableLifeCycle(conf, DEFAULT_ENABLE_LIFE_CYCLE);
setEnableJMX(conf, DEFAULT_ENABLE_JMX);
setJMXPort(conf, ""+DEFAULT_JMX_PORT);
setTerminationTimeout(conf, ""+DEFAULT_TERMINATION_TIMEOUT);
}
@Override
public LiveVariable<Boolean> getDirtyState() {
return dirtyState;
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return validator;
}
public boolean isLiveBeanSupported() {
BootLaunchActivator activator = BootLaunchActivator.getInstance();
if (activator!=null) {
return activator.isLiveBeanSupported();
}
return false;
}
}

View File

@@ -0,0 +1,200 @@
/*******************************************************************************
* 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.launch.livebean;
import java.util.EnumSet;
import java.util.Optional;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.launch.livebean.JmxBeanSupport.Feature;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
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.ValueListener;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.UIConstants;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
/**
* LaunchTabSection that enables LiveBean graph support by adding required
* VM and program arguments.
*
* @author Kris De Volder
*/
public class EnableJmxSection extends DelegatingLaunchConfigurationTabSection {
private static final boolean DEBUG = false;//(""+Platform.getLocation()).contains("kdvolder");
private static void debug(String string) {
if (DEBUG) {
System.out.println(string);
}
}
static class UI extends WizardPageSection {
private Button jmxCheckbox;
private Optional<Button> liveBeanCheckbox = Optional.empty();
private Button lifeCycleCheckbox;
private Text portWidget;
private EnableJmxFeaturesModel model;
private Text terminationTimeoutWidget;
public UI(IPageWithSections owner, EnableJmxFeaturesModel model) {
super(owner);
this.model = model;
}
@Override
public void createContents(Composite page) {
boolean liveBeanSupported = model.isLiveBeanSupported();
Composite composite = new Composite(page, SWT.NONE);
composite.setLayout(GridLayoutFactory.fillDefaults().numColumns(3).create());
jmxCheckbox = new Button(composite, SWT.CHECK);
jmxCheckbox.setText("Enable JMX");
jmxCheckbox.setToolTipText(computeTooltipText(
"Enables JMX. This is required for " +
(liveBeanSupported ? "Live Bean support and " : "") +
"Life Cycle Management. " +
"It also allows STS to access information about request-mappings on your app. "+
"Adds these vm args:\n", Feature.JMX
));
String portToolTip = "The port used for communicating with JMX beans (0 means STS should pick the port automatically on startup). ";
if (liveBeanSupported) {
portToolTip += "The same port is used/shared by both 'Life Cycle Management' and the 'Live Beans Graph'";
}
final Label label = new Label(composite, SWT.NONE);
label.setText("Port:");
portWidget = new Text(composite, SWT.BORDER);
GridDataFactory.fillDefaults().hint(UIConstants.fieldLabelWidthHint(portWidget, 7), SWT.DEFAULT)
.applyTo(portWidget);
label.setToolTipText(portToolTip);
portWidget.setToolTipText(portToolTip);
composite = new Composite(page, SWT.NONE);
GridLayout layout = GridLayoutFactory.fillDefaults().numColumns(3).margins(0, 0).create();
layout.marginLeft = UIConstants.fieldLabelWidthHint(label, 4);
composite.setLayout(layout);
if (liveBeanSupported) {
Button liveBeanCheckbox = new Button(composite, SWT.CHECK);
liveBeanCheckbox.setText("Enable Live Bean support.");
liveBeanCheckbox.setToolTipText(computeTooltipText(
"Enables support for Live Beans Graph View by adding vm args:\n",
Feature.LIVE_BEAN_GRAPH));
GridDataFactory.fillDefaults().span(3, 1).applyTo(liveBeanCheckbox);
this.liveBeanCheckbox = Optional.of(liveBeanCheckbox);
}
lifeCycleCheckbox = new Button(composite, SWT.CHECK);
lifeCycleCheckbox.setText("Enable Life Cycle Management.");
lifeCycleCheckbox.setToolTipText(computeTooltipText(
"Requires Boot 1.3.0. Allows Boot Dashboard View to track 'STARTING' state of Boot Apps; allows STS to ask Boot Apps to shutdown nicely. " +
"Adds these vm args: \n",
Feature.LIFE_CYCLE));
final Label terminationTimeoutLabel = new Label(composite, SWT.NONE);
terminationTimeoutLabel.setText("Termination timeout (ms):");
terminationTimeoutWidget = new Text(composite, SWT.BORDER);
GridDataFactory.fillDefaults().hint(UIConstants.fieldLabelWidthHint(terminationTimeoutWidget, 7), SWT.DEFAULT)
.applyTo(terminationTimeoutWidget);
terminationTimeoutWidget.setToolTipText("How long STS should wait, after asking Boot App nicely to stop, before attemptting to kill the process more forcibly.");
model.jmxEnabled.addListener((exp, enable) -> {
portWidget.setEnabled(enable);
liveBeanCheckbox.ifPresent(b -> b.setEnabled(enable));
lifeCycleCheckbox.setEnabled(enable);
label.setEnabled(enable);
terminationTimeoutLabel.setEnabled(enable);
});
model.lifeCycleEnabled.addListener(new ValueListener<Boolean>() {
@Override
public void gotValue(LiveExpression<Boolean> exp, Boolean value) {
terminationTimeoutWidget.setEnabled(value);
}
});
connectCheckbox(model.jmxEnabled, jmxCheckbox);
liveBeanCheckbox.ifPresent(cb -> connectCheckbox(model.liveBeanEnabled, cb));
connectCheckbox(model.lifeCycleEnabled, lifeCycleCheckbox);
connectTextWidget(model.port, portWidget);
connectTextWidget(model.terminationTimeout, terminationTimeoutWidget);
}
private static void connectTextWidget(final LiveVariable<String> model, final Text widget) {
model.addListener(new ValueListener<String>() {
public void gotValue(LiveExpression<String> exp, String value) {
String oldValue = widget.getText();
if (!oldValue.equals(value)) {
widget.setText(value);
}
}
});
widget.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
model.setValue(widget.getText());
}
});
}
private String computeTooltipText(String baseMsg, Feature feature) {
if (feature==Feature.JMX) {
return baseMsg +
JmxBeanSupport.jmxBeanVmArgs("${jmxPort}", EnumSet.of(feature));
} else {
return baseMsg +
feature.vmArg;
}
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return model.getValidator();
}
}
public EnableJmxSection(IPageWithSections owner, EnableJmxFeaturesModel model) {
super(owner, model, new UI(owner, model));
}
private static void connectCheckbox(final LiveVariable<Boolean> checkedState, final Button widget) {
final String name = widget.getText();
checkedState.addListener(new ValueListener<Boolean>() {
public void gotValue(LiveExpression<Boolean> exp, Boolean value) {
boolean enable = value!=null && value;
debug("Widget '"+name+"' <- "+enable);
widget.setSelection(enable);
}
});
widget.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
boolean enable = widget.getSelection();
debug("Model '"+name+"' <- "+enable);
checkedState.setValue(enable);
}
});
}
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (c) 2014, 2019 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.launch.livebean;
import java.util.EnumSet;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
/**
* Helper methods to generate vm args for launch config with JMX support enabled.
*
* @author Kris De Volder
*/
public class JmxBeanSupport {
public static enum Feature {
JMX(null),
LIVE_BEAN_GRAPH("-Dspring.liveBeansView.mbeanDomain"),
LIFE_CYCLE("-Dspring.application.admin.enabled=true");
public final String vmArg;
Feature(String vmArg) {
this.vmArg = vmArg;
}
}
/**
* VM args that enable 'live bean graph' and jmx.
*/
public static String jmxBeanVmArgs(int jmxPort, EnumSet<Feature> enabled) {
return jmxBeanVmArgs(""+jmxPort, enabled);
}
public static String jmxBeanVmArgs(String jmxPort, EnumSet<Feature> enabled) {
if (!enabled.isEmpty()) {
//At least one feature enabled
StringBuilder str = new StringBuilder();
for (String a : enableJmxArgs(jmxPort)) {
str.append(a+"\n");
}
for (Feature feature : enabled) {
if (feature.vmArg!=null) {
str.append(feature.vmArg+"\n");
}
}
return str.toString();
} else {
//No features enabled
return "";
}
}
public static String[] enableJmxArgs(String jmxPort) {
return new String[] {
"-Dcom.sun.management.jmxremote", //enable jmx to access the beans
"-D"+ JMX_PORT_PROP +"="+jmxPort,
"-Dcom.sun.management.jmxremote.authenticate=false",
"-Dcom.sun.management.jmxremote.ssl=false",
"-Djava.rmi.server.hostname=localhost",
// PT 164402781 - For Boot 2.2 and above, jmx is no longer enabled by default.
// We explicitly enable it here. Adding this doesn't seem to affect older boot versions
// and we get live information from JMX for those too.
"-Dspring.jmx.enabled=true"
};
}
public static final String JMX_PORT_PROP = "com.sun.management.jmxremote.port";
public static final String LAUNCH_CONFIG_TYPE_ID = BootLaunchConfigurationDelegate.TYPE_ID;
public static int randomPort() {
return (int) (5000 + Math.random()*60000);
}
}

View File

@@ -0,0 +1,170 @@
/*******************************************************************************
* 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.launch.process;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Callable;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.IProcessFactory;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.debug.core.model.RuntimeProcess;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.util.SpringApplicationLifeCycleClientManager;
import org.springframework.ide.eclipse.boot.launch.util.SpringApplicationLifecycleClient;
import org.springframework.ide.eclipse.boot.util.DumpOutput;
import org.springframework.ide.eclipse.boot.util.RetryUtil;
import org.springsource.ide.eclipse.commons.livexp.ui.Disposable;
public class BootProcessFactory implements IProcessFactory {
/**
* This flag enables dumping the process output onto System.out. This meant to help in
* diagnosing problems during CI build test execution.
*/
public static boolean ENABLE_OUTPUT_DUMPING = false;
public interface StreamsProxyListener {
void streamsProxyCreated(IStreamsProxy streams, ILaunch launch);
}
private static ListenerList<StreamsProxyListener> streamsProxyListeners = null;
//lazy created, only used by testing code, so do not waste space on this in production.
public static Disposable addStreamsProxyListener(StreamsProxyListener listener) {
synchronized (BootProcessFactory.class) {
if (streamsProxyListeners==null) {
streamsProxyListeners = new ListenerList<>();
}
}
streamsProxyListeners.add(listener);
return () -> streamsProxyListeners.remove(listener);
}
private static final boolean DEBUG = false;
private static void debug(String string) {
if (DEBUG) {
System.out.println(string);
}
}
@Override
public IProcess newProcess(ILaunch launch, final Process process, final String label, Map<String, String> attributes) {
final int jmxPort = getJMXPort(launch);
final long timeout = getNiceTerminationTimeout(launch);
RuntimeProcess rtProcess = new RuntimeProcess(launch, process, label, attributes) {
SpringApplicationLifeCycleClientManager clientMgr = new SpringApplicationLifeCycleClientManager(jmxPort);
@Override
public void terminate() throws DebugException {
if (!terminateNicely()) {
//Let eclipse try it more aggressively.
try {
super.terminate();
} catch (DebugException e) {
//Try even harder to destroy the process
if (!destroyForcibly()) {
throw e;
}
}
}
}
@Override
protected IStreamsProxy createStreamsProxy() {
IStreamsProxy streams = super.createStreamsProxy();
if (ENABLE_OUTPUT_DUMPING) {
streams.getOutputStreamMonitor().addListener(new DumpOutput("%out: "));
streams.getErrorStreamMonitor().addListener(new DumpOutput("%err: "));
}
if (streamsProxyListeners!=null) {
for (StreamsProxyListener streamsProxyListener : streamsProxyListeners) {
streamsProxyListener.streamsProxyCreated(streams, launch);
}
}
return streams;
}
private boolean destroyForcibly() {
try {
Method m = Process.class.getDeclaredMethod("destroyForcibly");
m.invoke(process);
return true;
} catch (Exception e) {
BootActivator.log(e);
//ignore... probably means we are not running on Java 8 VM and so we can't use 'destroyForcibly'.
}
return false;
}
private boolean terminateNicely() {
if (isTerminated()) {
return true;
}
if (jmxPort>0) {
try {
debug("Trying to terminate nicely: "+label);
SpringApplicationLifecycleClient client = clientMgr.getLifeCycleClient();
if (client!=null) {
debug("Asking JMX client to 'stop'");
client.stop();
debug("Asking JMX client to 'stop' -> SUCCESS");
} else {
//This case happens if process is already being terminated in another way.
// This happens for debug processes where debug target kills vm before
// asking process to terminate.
debug("PROBLEM? Couldn't get JMX client");
throw new IOException("Couldn't get JMX client.");
}
//Wait for a bit, but not forever until process dies.
RetryUtil.retry(100, timeout, new Callable<Void>() {
public Void call() throws Exception {
debug("process exited?");
process.exitValue(); //throws if process not ready
debug("process exited? -> YES");
return null;
}
});
debug("SUCCESS terminate nicely: "+label);
return true;
} catch (Exception e) {
//ignore... nice termination failed.
//BootActivator.log(e);
} finally {
clientMgr.disposeClient();
}
}
return false;
}
};
return rtProcess;
}
private long getNiceTerminationTimeout(ILaunch launch) {
return BootLaunchConfigurationDelegate.getTerminationTimeoutAsLong(launch);
}
private int getJMXPort(ILaunch launch) {
return BootLaunchConfigurationDelegate.getJMXPortAsInt(launch);
}
}

View File

@@ -0,0 +1,96 @@
/*******************************************************************************
* Copyright (c) 2015 GoPivotal, 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.profiles;
import static org.springsource.ide.eclipse.commons.core.util.StringUtil.*;
import java.util.Arrays;
import java.util.LinkedList;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.launch.IProfileHistory;
import org.springsource.ide.eclipse.commons.frameworks.core.util.ArrayEncoder;
/**
* An instance of this class is capable of storing and retrieving a history
* of Profiles (Strings) in project preferences area.
*
* @author Kris De Volder
*/
public class ProfileHistory implements IProfileHistory {
private static final String PROFILE_HISTORY = "spring.boot.launch.profile.history";
private static final String[] NO_PROFILES = new String[0];
private int maxProfileHistory = 10;
public ProfileHistory setMaxHistory(int max) {
Assert.isLegal(max>=0);
this.maxProfileHistory = max;
return this;
}
public String[] getHistory(IProject project) {
if (project!=null) {
IEclipsePreferences prefs = getPreferences(project);
if (prefs!=null) {
String[] storedHistory = ArrayEncoder.decode(prefs.get(PROFILE_HISTORY, null));
if (storedHistory!=null) {
return storedHistory;
}
}
}
return NO_PROFILES;
}
protected void setHistory(IProject project, String[] profiles) {
try {
IEclipsePreferences prefs = getPreferences(project);
if (prefs!=null) {
prefs.put(PROFILE_HISTORY, ArrayEncoder.encode(profiles));
prefs.flush();
}
} catch (Exception e) {
BootActivator.log(e);
}
}
/**
* Adds a new profile to the history. May remove oldest profile if
* the maxHistory limit is exceeded.
*/
public void updateHistory(IProject project, String profile) {
if (project!=null && hasText(profile)) {
LinkedList<String> history = new LinkedList<>(
Arrays.asList(getHistory(project)));
history.remove(profile);
history.addFirst(profile);
while (history.size()>maxProfileHistory) {
history.removeLast();
}
setHistory(project, history.toArray(new String[history.size()]));
}
}
/**
* Retrieve eclipse preferences node where the history list will be stored.
*/
protected IEclipsePreferences getPreferences(IProject p) {
ProjectScope scope = new ProjectScope(p);
IEclipsePreferences prefs = scope.getNode(BootActivator.PLUGIN_ID);
return prefs;
}
}

View File

@@ -0,0 +1,38 @@
/*******************************************************************************
* 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.launch.profiles;
import org.springframework.ide.eclipse.boot.launch.ProfileLaunchTabModel;
import org.springframework.ide.eclipse.boot.launch.util.DelegatingLaunchConfigurationTabSection;
import org.springsource.ide.eclipse.commons.livexp.ui.ChooseOneSectionCombo;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.util.Parser;
/**
* @author Kris De Volder
*/
public class ProfileLaunchTabSection extends DelegatingLaunchConfigurationTabSection {
public ProfileLaunchTabSection(IPageWithSections owner, ProfileLaunchTabModel model) {
super(owner, model, createUI(owner, model));
}
private static IPageSection createUI(IPageWithSections owner,
ProfileLaunchTabModel model) {
ChooseOneSectionCombo<String> profileChooser = new ChooseOneSectionCombo<String>(owner, "Profile",
model, model.profileOptions());
profileChooser.allowTextEdits(Parser.IDENTITY);
return profileChooser;
}
}

View File

@@ -0,0 +1,656 @@
/*******************************************************************************
* Copyright (c) 2018, 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.launch.properties;
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.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.IRewriteTarget;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISharedTextColors;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.text.source.IVerticalRulerInfo;
import org.eclipse.jface.text.source.OverviewRuler;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.text.source.projection.ProjectionViewer;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.IHandlerActivation;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.internal.editors.text.EditorsPlugin;
import org.eclipse.ui.internal.part.NullEditorInput;
import org.eclipse.ui.texteditor.AnnotationPreference;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;
import org.eclipse.ui.texteditor.DocumentProviderRegistry;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.IElementStateListener;
import org.eclipse.ui.texteditor.IElementStateListenerExtension;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.MarkerAnnotationPreferences;
import org.eclipse.ui.texteditor.SourceViewerDecorationSupport;
import org.springsource.ide.eclipse.commons.internal.ui.UiPlugin;
/**
* Embedded editor that can be nested inside SWT controls such as dialogs
*
* @author Alex Boyko
*
*/
@SuppressWarnings("restriction")
public class EmbeddedEditor implements ITextEditor {
@FunctionalInterface
public interface ViewerConfigurationFactory {
SourceViewerConfiguration create(EmbeddedEditor editor);
}
private static final NullEditorInput NULL_EDITOR_INPUT = new NullEditorInput();
private class EditorActionHandler extends AbstractHandler {
private final String actionId;
private final 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 {
viewer.doOperation(operationId);
return null;
}
}
private final 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 final boolean withHandlers;
private final boolean withDecorations;
private final ViewerConfigurationFactory viewerConfigFactory;
private ProjectionViewer viewer;
private SourceViewerDecorationSupport decorationSupport;
private final List<IHandlerActivation> activations = new ArrayList<>();
private final IPreferenceStore prefStore;
private IEditorInput editorInput;
private IDocumentProvider docProvider;
private IVerticalRuler verticalRuler;
private final ListenerList<IPropertyListener> propertyChangeListeners = new ListenerList<>();
private final IElementStateListener elementStateListener = new ElementStateListener();
private String context;
private DefaultMarkerAnnotationAccess fileMarkerAnnotationAccess;
public EmbeddedEditor(ViewerConfigurationFactory viewerConfigFactory, IPreferenceStore prefStore, boolean withHandlers, boolean withDecorations) {
this.viewerConfigFactory = viewerConfigFactory;
this.prefStore = prefStore;
this.withHandlers = withHandlers;
this.withDecorations = withDecorations;
}
public EmbeddedEditor(ViewerConfigurationFactory viewerConfigFactory, IPreferenceStore prefStore) {
this(viewerConfigFactory, prefStore, true, true);
}
public Control createControl(Composite parent) throws CoreException {
verticalRuler = new CompositeRuler();
fileMarkerAnnotationAccess = new DefaultMarkerAnnotationAccess();
OverviewRuler overviewRuler = new OverviewRuler(fileMarkerAnnotationAccess, 10, getSharedColors());
viewer = new ProjectionViewer(parent, verticalRuler, overviewRuler, true,
SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION);
viewer.configure(viewerConfigFactory.create(this));
if (withDecorations) {
decorationSupport = new SourceViewerDecorationSupport(viewer, overviewRuler,
fileMarkerAnnotationAccess, getSharedColors());
for (AnnotationPreference preference : new MarkerAnnotationPreferences()
.getAnnotationPreferences()) {
decorationSupport.setAnnotationPreference(preference);
}
decorationSupport.install(prefStore);
}
if (withHandlers) {
activateHandlers();
}
initViewer();
return viewer.getControl();
}
public ProjectionViewer getViewer() {
return viewer;
}
public DefaultMarkerAnnotationAccess getAnnotationAccess() {
return fileMarkerAnnotationAccess;
}
public ISharedTextColors getSharedColors() {
return EditorsPlugin.getDefault().getSharedTextColors();
}
private void activateHandlers() {
IHandlerService service = PlatformUI.getWorkbench().getAdapter(IHandlerService.class);
if (service != null) {
for (EditorActionHandler handler : handlers) {
activations.add(service.activateHandler(handler.getActionId(), handler));
}
}
}
private void deactivateHandlers() {
IHandlerService service = PlatformUI.getWorkbench().getAdapter(IHandlerService.class);
if (service != null && activations != null) {
for (IHandlerActivation activation : activations) {
service.deactivateHandler(activation);
}
activations.clear();
}
}
@Override
public IEditorInput getEditorInput() {
return editorInput == null ? NULL_EDITOR_INPUT : editorInput;
}
@Override
public IEditorSite getEditorSite() {
return null;
}
@Override
public void init(IEditorSite site, IEditorInput input) throws PartInitException {
try {
setInput(input);
} catch (CoreException e) {
throw new PartInitException(e.getStatus());
}
}
public void setInput(IEditorInput editorInput) throws CoreException {
if (editorInput == null) {
editorInput = NULL_EDITOR_INPUT;
}
IEditorInput oldInput = this.editorInput;
if (oldInput != null) {
getDocumentProvider().disconnect(oldInput);
}
this.editorInput = editorInput;
updateDocumentProvider(editorInput);
IDocumentProvider provider = getDocumentProvider();
if (provider == null) {
IStatus s = new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, IStatus.OK,
"No document provider for input " + editorInput, null);
throw new CoreException(s);
}
provider.connect(editorInput);
if (viewer != null) {
initViewer();
}
firePropertyChange(IEditorPart.PROP_INPUT);
}
private void initViewer() {
IDocumentProvider provider = getDocumentProvider();
IDocument document = provider.getDocument(editorInput);
IAnnotationModel annotationModel = provider.getAnnotationModel(editorInput);
viewer.setDocument(document, annotationModel);
viewer.setEditable(this.isEditable());
}
private void updateDocumentProvider(IEditorInput editorInput) {
if (docProvider != null) {
docProvider.removeElementStateListener(elementStateListener);
}
docProvider = editorInput == NULL_EDITOR_INPUT ? NULL_DOCUMENT_PROVIDER : DocumentProviderRegistry.getDefault().getDocumentProvider(editorInput);
if (docProvider != null) {
docProvider.addElementStateListener(elementStateListener);
}
}
private void firePropertyChange(int propId) {
propertyChangeListeners.forEach(l -> l.propertyChanged(this, propId));
}
@Override
public void addPropertyListener(IPropertyListener listener) {
propertyChangeListeners.add(listener);
}
@Override
public void createPartControl(Composite parent) {
}
@Override
public void dispose() {
if (withHandlers) {
deactivateHandlers();
}
if (editorInput != null) {
getDocumentProvider().disconnect(editorInput);
editorInput = null;
}
if (decorationSupport != null) {
decorationSupport.dispose();
}
if (viewer != null && !viewer.getControl().isDisposed()) {
viewer.getControl().dispose();
}
}
@Override
public IWorkbenchPartSite getSite() {
return null;
}
@Override
public String getTitle() {
return null;
}
@Override
public Image getTitleImage() {
return null;
}
@Override
public String getTitleToolTip() {
return null;
}
@Override
public void removePropertyListener(IPropertyListener listener) {
propertyChangeListeners.remove(listener);
}
@Override
public void setFocus() {
}
@SuppressWarnings("unchecked")
@Override
public <T> T getAdapter(Class<T> required) {
if (IVerticalRulerInfo.class.equals(required)) {
if (verticalRuler != null) {
return (T) verticalRuler;
}
}
if (ITextOperationTarget.class.equals(required)) {
return (viewer == null ? null : (T) viewer.getTextOperationTarget());
}
if (IRewriteTarget.class.equals(required)) {
if (viewer instanceof ITextViewerExtension) {
ITextViewerExtension extension= viewer;
return (T) extension.getRewriteTarget();
}
return null;
}
if (Control.class.equals(required)) {
return viewer != null ? (T) viewer.getTextWidget() : null;
}
if (ITextViewer.class.isAssignableFrom(required)) {
return (viewer == null ? null : (T) viewer);
}
return null;
}
@Override
public void doSave(IProgressMonitor progressMonitor) {
IDocumentProvider p= getDocumentProvider();
if (p == null) {
return;
}
if (p.isDeleted(getEditorInput())) {
if (isSaveAsAllowed()) {
// No Save As
}
} else {
IDocumentProvider provider= getDocumentProvider();
if (provider == null) {
return;
}
try {
provider.aboutToChange(getEditorInput());
IEditorInput input= getEditorInput();
provider.saveDocument(progressMonitor, input, getDocumentProvider().getDocument(input), false);
} catch (CoreException e) {
UiPlugin.log(e);
} finally {
provider.changed(getEditorInput());
}
}
}
@Override
public void doSaveAs() {
}
@Override
public boolean isDirty() {
IDocumentProvider p= getDocumentProvider();
return p == null ? false : p.canSaveDocument(editorInput);
}
@Override
public boolean isSaveAsAllowed() {
return false;
}
@Override
public boolean isSaveOnCloseNeeded() {
return false;
}
@Override
public IDocumentProvider getDocumentProvider() {
return docProvider;
}
@Override
public void close(boolean save) {
}
@Override
public boolean isEditable() {
return editorInput != NULL_EDITOR_INPUT;
}
@Override
public void doRevertToSaved() {
}
@Override
public void setAction(String actionID, IAction action) {
}
@Override
public IAction getAction(String actionId) {
return null;
}
@Override
public void setActionActivationCode(String actionId, char activationCharacter, int activationKeyCode,
int activationStateMask) {
}
@Override
public void removeActionActivationCode(String actionId) {
}
@Override
public boolean showsHighlightRangeOnly() {
return false;
}
@Override
public void showHighlightRangeOnly(boolean showHighlightRangeOnly) {
}
@Override
public void setHighlightRange(int offset, int length, boolean moveCursor) {
}
@Override
public IRegion getHighlightRange() {
return null;
}
@Override
public void resetHighlightRange() {
}
@Override
public ISelectionProvider getSelectionProvider() {
return null;
}
@Override
public void selectAndReveal(int offset, int length) {
}
public String getContext() {
return context;
}
public void setContext(String context) {
this.context = context;
}
// public void onPreCreateControl(Runnable preCreateControlRunnable) {
// this.preCreateControlRunnable = preCreateControlRunnable;
// }
//
// public void onPostCreateControl(Runnable postCreateControlRunnable) {
// this.postCreateControlRunnable = postCreateControlRunnable;
// }
private static final Document NULL_DOCUMENT = new Document("");
private static final IDocumentProvider NULL_DOCUMENT_PROVIDER = new IDocumentProvider() {
@Override
public void connect(Object element) throws CoreException {
}
@Override
public void disconnect(Object element) {
}
@Override
public IDocument getDocument(Object element) {
return NULL_DOCUMENT;
}
@Override
public void resetDocument(Object element) throws CoreException {
}
@Override
public void saveDocument(IProgressMonitor monitor, Object element, IDocument document, boolean overwrite)
throws CoreException {
}
@Override
public long getModificationStamp(Object element) {
return 0;
}
@Override
public long getSynchronizationStamp(Object element) {
return 0;
}
@Override
public boolean isDeleted(Object element) {
return false;
}
@Override
public boolean mustSaveDocument(Object element) {
return false;
}
@Override
public boolean canSaveDocument(Object element) {
return false;
}
@Override
public IAnnotationModel getAnnotationModel(Object element) {
return null;
}
@Override
public void aboutToChange(Object element) {
}
@Override
public void changed(Object element) {
}
@Override
public void addElementStateListener(IElementStateListener listener) {
}
@Override
public void removeElementStateListener(IElementStateListener listener) {
}
};
class ElementStateListener implements IElementStateListener, IElementStateListenerExtension {
@Override
public void elementStateValidationChanged(final Object element, final boolean isStateValidated) {
}
@Override
public void elementDirtyStateChanged(Object element, boolean isDirty) {
if (element != null && element.equals(getEditorInput())) {
Runnable r = () -> {
firePropertyChange(PROP_DIRTY);
};
execute(r, false);
}
}
@Override
public void elementContentAboutToBeReplaced(Object element) {
}
@Override
public void elementContentReplaced(Object element) {
if (element != null && element.equals(getEditorInput())) {
Runnable r = () -> {
firePropertyChange(PROP_DIRTY);
};
execute(r, false);
}
}
@Override
public void elementDeleted(Object deletedElement) {
if (deletedElement != null && deletedElement.equals(getEditorInput())) {
Runnable r = () -> {
try {
setInput(NULL_EDITOR_INPUT);
} catch (CoreException e) {
UiPlugin.log(e);
}
};
execute(r, false);
}
}
@Override
public void elementMoved(final Object originalElement, final Object movedElement) {
}
@Override
public void elementStateChanging(Object element) {
}
@Override
public void elementStateChangeFailed(Object element) {
}
private void execute(Runnable runnable, boolean postAsync) {
if (postAsync || Display.getCurrent() == null) {
if (viewer != null && !viewer.getControl().isDisposed()) {
Display display = viewer.getControl().getDisplay();
display.asyncExec(runnable);
}
} else {
runnable.run();
}
}
}
}

View File

@@ -0,0 +1,31 @@
/*******************************************************************************
* 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.launch.properties;
import org.eclipse.core.expressions.PropertyTester;
import com.google.common.base.Objects;
public class EmbeddedEditorPropertyTester extends PropertyTester {
@Override
public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
switch (property) {
case "context":
if (receiver instanceof EmbeddedEditor) {
return Objects.equal(expectedValue, ((EmbeddedEditor)receiver).getContext());
}
return false;
}
return false;
}
}

View File

@@ -0,0 +1,280 @@
/*******************************************************************************
* Copyright (c) 2015, 2018 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.properties;
import java.io.ByteArrayInputStream;
import java.lang.reflect.Constructor;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.propertiesfileeditor.IPropertiesFilePartitions;
import org.eclipse.jdt.ui.text.IColorManager;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IAnnotationModelListener;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.internal.genericeditor.ExtensionBasedTextViewerConfiguration;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
import org.osgi.framework.Bundle;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.util.ILaunchConfigurationTabSection;
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.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
import com.google.common.base.Supplier;
/**
* An IPageSection that contains a table-based properties editor.
*
* @author Kris De Volder
* @author Alex Boyko
*/
@SuppressWarnings("restriction")
public class PropertiesEditorSection extends WizardPageSection implements ILaunchConfigurationTabSection {
private static final String LEGACY_PROPERTIES_EDITOR_VIEWER_CONFIG_CLASS_NAME = "org.springframework.ide.eclipse.boot.properties.editor.SpringPropertiesFileSourceViewerConfiguration";
private static final String LEGACY_BOOT_PROPS_EDITOR_PLUGIN_ID = "org.springframework.ide.eclipse.boot.properties.editor";
private static final String BOOT_LS_PLUGIN_ID = "org.springframework.tooling.boot.ls";
@Override
public LiveVariable<Boolean> getDirtyState() {
return dirtyState;
}
public PropertiesEditorSection(IPageWithSections owner, LiveExpression<IProject> project) {
super(owner);
this.project = project;
}
private final LiveExpression<IProject> project;
private LiveVariable<Boolean> dirtyState = new LiveVariable<>(false);
private String props = "";
private LiveVariable<ValidationResult> validator = new LiveVariable<>(ValidationResult.OK);
private EmbeddedEditor embeddedEditor;
private IFile propertiesFile;
private IAnnotationModelListener annotationModelListener = new IAnnotationModelListener() {
@Override
public void modelChanged(IAnnotationModel model) {
boolean errorsPresent = model.getAnnotationIterator().hasNext();
validator.setValue(errorsPresent ? ValidationResult.error("Errors present in application properties.") : ValidationResult.OK);
}
};
private IPropertyListener editorDirtyStateListener = (source, propId) -> {
if (propId == IEditorPart.PROP_DIRTY) {
dirtyState.setValue(((ITextEditor)source).isDirty());
}
};
private static boolean isLanguageServerEnabled() {
return isBundleEnabled(BOOT_LS_PLUGIN_ID);
}
private static boolean isLegacyPropertyEditorEnabled() {
return isBundleEnabled(LEGACY_BOOT_PROPS_EDITOR_PLUGIN_ID);
}
private static boolean isBundleEnabled(String bundleId) {
Bundle lsBundle = Platform.getBundle(bundleId);
return lsBundle != null && lsBundle.getState() != Bundle.INSTALLED;
}
@SuppressWarnings("unchecked")
private SourceViewerConfiguration createViewerConfig(EmbeddedEditor editor, IPreferenceStore prefStore,
IJavaProject jp) {
if (isLanguageServerEnabled()) {
return new ExtensionBasedTextViewerConfiguration(editor, prefStore);
} else if (isLegacyPropertyEditorEnabled()) {
try {
// Avoid depending on the legacy boot properties editor plug-in directly
Class<?> klass = Platform.getBundle(LEGACY_BOOT_PROPS_EDITOR_PLUGIN_ID)
.loadClass(LEGACY_PROPERTIES_EDITOR_VIEWER_CONFIG_CLASS_NAME);
Constructor<SourceViewerConfiguration> constructor = (Constructor<SourceViewerConfiguration>) klass
.getConstructor(IColorManager.class, IPreferenceStore.class, ITextEditor.class, String.class,
Supplier.class);
Supplier<IJavaProject> jpSupplier = () -> jp;
return constructor.newInstance(JavaPlugin.getDefault().getJavaTextTools().getColorManager(), prefStore,
editor, IPropertiesFilePartitions.PROPERTIES_FILE_PARTITIONING, jpSupplier);
} catch (Exception e) {
Log.log(e);
}
}
// If all attempts fail create generic editor viewer configuration
return new ExtensionBasedTextViewerConfiguration(editor, prefStore);
}
@Override
public void createContents(Composite parent) {
Label label = new Label(parent, SWT.NONE);
label.setText("Override properties:");
project.addListener((exp, p) -> {
String content = getPropertiesDocContent();
disposeEmbeddedEditor();
IJavaProject jp = JavaCore.create(project.getValue());
if (jp != null) {
try {
IFile newPropertiesFile = generateTempApplicationPropertiesFile(p, content);
if (!newPropertiesFile.equals(propertiesFile)) {
FileEditorInput editorInput = new FileEditorInput(newPropertiesFile);
IPreferenceStore preferenceStore = JavaPlugin.getDefault().getCombinedPreferenceStore();
embeddedEditor = new EmbeddedEditor((editor) -> createViewerConfig(editor, preferenceStore, jp),
preferenceStore);
embeddedEditor.init(null, editorInput);
Control editorControl = embeddedEditor.createControl(parent);
embeddedEditor.addPropertyListener(editorDirtyStateListener);
GridDataFactory.fillDefaults().grab(true, true).applyTo(editorControl);
watchEditorErrors();
propertiesFile = newPropertiesFile;
}
} catch (Exception e) {
Log.log(e);
}
} else {
dirtyState.setValue(false);
}
parent.layout(true);
});
}
@Override
public void initializeFrom(ILaunchConfiguration conf) {
try {
props = BootLaunchConfigurationDelegate.getRawApplicationProperties(conf);
if (propertiesFile != null) {
if (embeddedEditor != null) {
embeddedEditor.getViewer().getDocument().set(props);
embeddedEditor.doSave(new NullProgressMonitor());
embeddedEditor.getViewer().getUndoManager().reset();
} else {
propertiesFile.setContents(new ByteArrayInputStream(props.getBytes()), true, false, new NullProgressMonitor());
}
}
dirtyState.setValue(false);
} catch (Exception e) {
Log.log(e);
}
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy conf) {
if (embeddedEditor != null) {
String content = embeddedEditor.getViewer().getDocument().get();
BootLaunchConfigurationDelegate.setRawApplicationProperties(conf, content);
}
dirtyState.setValue(false);
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy conf) {
BootLaunchConfigurationDelegate.clearProperties(conf);
}
private IFile generateTempApplicationPropertiesFile(IProject project, String content) throws CoreException {
IFolder folder = project.getFolder(".settings");
IFile propertiesFile = folder.getFile("application.properties");
if (propertiesFile.exists()) {
propertiesFile.setContents(new ByteArrayInputStream(content.getBytes()), true, false, new NullProgressMonitor());
} else {
propertiesFile.create(new ByteArrayInputStream(content.getBytes()), true, new NullProgressMonitor());
}
return propertiesFile;
}
private String getPropertiesDocContent() {
return embeddedEditor == null ? props : embeddedEditor.getViewer().getDocument().get();
}
private void watchEditorErrors() {
if (embeddedEditor != null) {
IAnnotationModel annotationModel = embeddedEditor.getViewer().getAnnotationModel();
if (annotationModel != null) {
annotationModel.addAnnotationModelListener(annotationModelListener);
}
}
}
private void unwatchEditorErrors() {
if (embeddedEditor != null) {
IAnnotationModel annotationModel = embeddedEditor.getViewer().getAnnotationModel();
if (annotationModel != null) {
annotationModel.addAnnotationModelListener(annotationModelListener);
}
}
}
private void disposeTempApplicationPropertiesFile() {
if (propertiesFile != null && propertiesFile.exists()) {
try {
propertiesFile.delete(true, new NullProgressMonitor());
propertiesFile = null;
} catch (CoreException e) {
e.printStackTrace();
}
}
}
private void disposeEmbeddedEditor() {
unwatchEditorErrors();
if (embeddedEditor != null) {
embeddedEditor.removePropertyListener(editorDirtyStateListener);
embeddedEditor.dispose();
embeddedEditor = null;
validator.setValue(ValidationResult.OK);
}
disposeTempApplicationPropertiesFile();
}
@Override
public void dispose() {
disposeEmbeddedEditor();
super.dispose();
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return validator;
}
}

View File

@@ -0,0 +1,185 @@
/*******************************************************************************
* Copyright (c) 2000, 2018, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Sascha Radike - bug 56642
* Martin Oberhuber (Wind River) - [327446] Avoid unnecessary wait-for-build dialog.
* Mohamed Hussein - bug 381175
* Pivotal Inc - allow synchronizing with backgroundLaunch
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.util;
import java.text.MessageFormat;
import java.util.concurrent.CompletableFuture;
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.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.IStatusHandler;
import org.eclipse.debug.internal.ui.DebugUIMessages;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
import org.eclipse.debug.internal.ui.DebugUIPlugin.PendingLaunch;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.ILaunchGroup;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.progress.IProgressConstants2;
import org.eclipse.ui.progress.IProgressService;
/**
* Bits and pieces copied from {@link DebugUITools} and {@link DebugUIPlugin} with some
* minor modifications.
*/
@SuppressWarnings("restriction")
public class BootDebugUITools {
/**
* Copied from DebugUIPlugin and modified to add a CompletableFuture return value,
* to allow for proper synchronization with the background launch operation.
*
* Saves and builds the workspace according to current preference settings and
* launches the given launch configuration in the specified mode in a background
* Job with progress reported via the Job. Exceptions are reported in the Progress
* view.
*
* @param configuration the configuration to launch
* @param mode launch mode
* @param done
* @return CompletableFuture that is completed when the background job finishes.
* @since 3.0
*/
public static void launchInBackground(final ILaunchConfiguration configuration, final String mode, CompletableFuture<Void> done) {
final IJobManager jobManager = Job.getJobManager();
IPreferenceStore store = DebugUIPlugin.getDefault().getPreferenceStore();
boolean wait = (jobManager.find(ResourcesPlugin.FAMILY_AUTO_BUILD).length > 0 && ResourcesPlugin.getWorkspace().isAutoBuilding())
|| (jobManager.find(ResourcesPlugin.FAMILY_MANUAL_BUILD).length > 0);
String waitPref = store.getString(IInternalDebugUIConstants.PREF_WAIT_FOR_BUILD);
if (wait) { // if there are build jobs running, do we wait or not??
if (waitPref.equals(MessageDialogWithToggle.PROMPT)) {
MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoCancelQuestion(DebugUIPlugin.getShell(), DebugUIMessages.DebugUIPlugin_23, DebugUIMessages.DebugUIPlugin_24, null, false, store, IInternalDebugUIConstants.PREF_WAIT_FOR_BUILD); //
switch (dialog.getReturnCode()) {
case IDialogConstants.CANCEL_ID:
done.complete(null);
return;
case IDialogConstants.YES_ID:
wait = true;
break;
case IDialogConstants.NO_ID:
wait = false;
break;
default:
break;
}
}
else {
wait = waitPref.equals(MessageDialogWithToggle.ALWAYS);
}
}
final boolean waitInJob = wait;
Job job = new Job(MessageFormat.format(DebugUIMessages.DebugUIPlugin_25, new Object[] {configuration.getName()})) {
@Override
public IStatus run(final IProgressMonitor monitor) {
/* Setup progress monitor
* - Waiting for jobs to finish (2)
* - Build & launch (98) */
monitor.beginTask(DebugUIMessages.DebugUITools_3, 100);
try {
if(waitInJob) {
StringBuffer buffer = new StringBuffer(configuration.getName());
buffer.append(DebugUIMessages.DebugUIPlugin_0);
ILaunchConfigurationWorkingCopy workingCopy = configuration.copy(buffer.toString());
workingCopy.setAttribute(DebugUIPlugin.ATTR_LAUNCHING_CONFIG_HANDLE, configuration.getMemento());
final ILaunch pendingLaunch = new PendingLaunch(workingCopy, mode, this);
DebugPlugin.getDefault().getLaunchManager().addLaunch(pendingLaunch);
IJobChangeListener listener= new IJobChangeListener() {
@Override
public void sleeping(IJobChangeEvent event) {}
@Override
public void scheduled(IJobChangeEvent event) {}
@Override
public void running(IJobChangeEvent event) {}
@Override
public void awake(IJobChangeEvent event) {}
@Override
public void aboutToRun(IJobChangeEvent event) {}
@Override
public void done(IJobChangeEvent event) {
DebugPlugin dp = DebugPlugin.getDefault();
if (dp != null) {
dp.getLaunchManager().removeLaunch(pendingLaunch);
}
removeJobChangeListener(this);
}
};
addJobChangeListener(listener);
try {
jobManager.join(ResourcesPlugin.FAMILY_MANUAL_BUILD, new SubProgressMonitor(monitor, 1));
jobManager.join(ResourcesPlugin.FAMILY_AUTO_BUILD, new SubProgressMonitor(monitor, 1));
}
catch (InterruptedException e) {/*just continue.*/}
DebugPlugin.getDefault().getLaunchManager().removeLaunch(pendingLaunch);
}
else {
monitor.worked(2); /* don't wait for jobs to finish */
}
if (!monitor.isCanceled()) {
DebugUIPlugin.buildAndLaunch(configuration, mode, new SubProgressMonitor(monitor, 98));
}
} catch (CoreException e) {
final IStatus status = e.getStatus();
IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler(status);
if (handler == null) {
return status;
}
final ILaunchGroup group = DebugUITools.getLaunchGroup(configuration, mode);
if (group == null) {
return status;
}
Runnable r = () -> DebugUITools.openLaunchConfigurationDialogOnGroup(DebugUIPlugin.getShell(), new StructuredSelection(configuration), group.getIdentifier(), status);
DebugUIPlugin.getStandardDisplay().asyncExec(r);
}
finally {
monitor.done();
done.complete(null);
}
return Status.OK_STATUS;
}
};
IWorkbench workbench = DebugUIPlugin.getDefault().getWorkbench();
IProgressService progressService = workbench.getProgressService();
job.setPriority(Job.INTERACTIVE);
job.setProperty(IProgressConstants2.SHOW_IN_TASKBAR_ICON_PROPERTY, Boolean.TRUE);
job.setName(MessageFormat.format(DebugUIMessages.DebugUIPlugin_25, new Object[] {configuration.getName()}));
if (wait) {
progressService.showInDialog(workbench.getActiveWorkbenchWindow().getShell(), job);
}
job.schedule();
}
}

View File

@@ -0,0 +1,111 @@
/*******************************************************************************
* 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.launch.util;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchManager;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.devtools.BootDevtoolsClientLaunchConfigurationDelegate;
import org.springsource.ide.eclipse.commons.frameworks.core.ExceptionUtil;
import org.springsource.ide.eclipse.commons.frameworks.core.workspace.ProjectDeletionListenerManager;
import org.springsource.ide.eclipse.commons.frameworks.core.workspace.ProjectDeletionListenerManager.ProjectDeletionListener;
import org.springsource.ide.eclipse.commons.frameworks.core.util.JobUtil;
/**
* When projects are deleted, also deletes corresponding local launch configs.
*
* @author Kris De Volder
*/
public class BootLaunchConfDeleter {
private ILaunchManager launchMan;
private ProjectDeletionListenerManager listenerManager;
private static final ISchedulingRule RULE = JobUtil.lightRule("LaunchConfDeleteRule");
private String[] TYPE_IDS = {
BootLaunchConfigurationDelegate.TYPE_ID,
BootDevtoolsClientLaunchConfigurationDelegate.TYPE_ID
};
public BootLaunchConfDeleter(IWorkspace workspace, ILaunchManager launchMan) {
this.launchMan = launchMan;
this.listenerManager = new ProjectDeletionListenerManager(workspace, new ProjectDeletionListener() {
@Override
public void projectWasDeleted(IProject project) {
handleDelete(project);
}
});
}
private void handleDelete(IProject project) {
final List<ILaunchConfiguration> confs = getDeletableLaunchConfigs(project);
if (confs!=null && !confs.isEmpty()) {
Job deleteJob = new Job("Delete launch configs for "+project.getName()) {
@Override
protected IStatus run(IProgressMonitor monitor) {
try {
for (ILaunchConfiguration c : confs) {
c.delete();
}
return Status.OK_STATUS;
} catch (Exception e) {
return ExceptionUtil.status(e);
}
}
};
//what rule should we use here? Something less aggressive??
deleteJob.setRule(RULE); //In case many projects deleted in a burst... avoid concurrency issues.
deleteJob.schedule();
}
}
private List<ILaunchConfiguration> getDeletableLaunchConfigs(IProject project) {
List<ILaunchConfiguration> configs = new ArrayList<>();
for (String typeId : TYPE_IDS) {
try {
ILaunchConfigurationType type = launchMan.getLaunchConfigurationType(typeId);
for (ILaunchConfiguration conf : launchMan.getLaunchConfigurations(type)) {
if (
project.equals(BootLaunchConfigurationDelegate.getProject(conf)) &&
isDeletable(conf)
) {
configs.add(conf);
}
}
} catch (Exception e) {
BootActivator.log(e);
}
}
return configs;
}
private boolean isDeletable(ILaunchConfiguration conf) {
return conf.exists() && conf.isLocal();
}
public void dispose() {
listenerManager.dispose();
}
}

View File

@@ -0,0 +1,134 @@
/*******************************************************************************
* Copyright (c) 2013 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.launch.util;
import static org.springsource.ide.eclipse.commons.ui.launch.LaunchUtils.whenTerminated;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import com.google.common.collect.ImmutableSet;
/**
* @author Kris De Volder
*/
public class BootLaunchUtils {
/**
* Boot aware launch termination. Tries to use JMX lifecycle managment bean if available.
*/
public static void terminate(Iterable<ILaunch> launches) {
//TODO: this terminates launches sequentially. It would be better to try to terminate all of them
// in parallel and then wait for completion of each operation.
for (ILaunch l : launches) {
try {
terminate(l);
} catch (Exception e) {
BootActivator.log(e);
}
}
}
/**
* Boot aware launch termination. Tries to use JMX lifecycle managment bean if available.
*/
public static void terminate(ILaunch l) throws DebugException, CoreException {
ILaunchConfiguration conf = l.getLaunchConfiguration();
// try {
if (conf!=null
&& conf.getType().getIdentifier().equals(BootLaunchConfigurationDelegate.TYPE_ID)
&& BootLaunchConfigurationDelegate.canUseLifeCycle(conf)
) {
SpringApplicationLifeCycleClientManager clientMgr = new SpringApplicationLifeCycleClientManager(l);
try {
SpringApplicationLifecycleClient client = clientMgr.getLifeCycleClient();
if (client!=null) {
client.stop();
whenTerminated(l).get(BootLaunchConfigurationDelegate.getTerminationTimeoutAsLong(l), TimeUnit.MILLISECONDS);
return; //Success
}
} catch (Exception e) {
//Nice termination failed. We'll ignore the exception and allow fallback to kick in.
//BootActivator.log(e);
} finally {
clientMgr.disposeClient();
}
}
// Fallback to default implementation if 'nice termination' not available.
l.terminate();
// } catch (Exception e) {
// BootActivator.log(e);
// }
}
public static IProject getProject(ILaunch launch) {
ILaunchConfiguration conf = launch.getLaunchConfiguration();
if (conf!=null) {
return BootLaunchConfigurationDelegate.getProject(conf);
}
return null;
}
public static boolean isBootLaunch(ILaunch l) {
try {
ILaunchConfiguration conf = l.getLaunchConfiguration();
if (conf!=null) {
String type = conf.getType().getIdentifier();
return BootLaunchConfigurationDelegate.TYPE_ID.equals(type);
}
} catch (Exception e) {
BootActivator.log(e);
}
return false;
}
public static boolean isDebugging(ILaunch launch) {
return ILaunchManager.DEBUG_MODE.equals(launch.getLaunchMode());
}
public static List<ILaunch> getLaunches(ILaunchConfiguration c) {
return getLaunches(ImmutableSet.of(c));
}
public static List<ILaunch> getLaunches(Set<ILaunchConfiguration> configs) {
ILaunch[] all = DebugPlugin.getDefault().getLaunchManager().getLaunches();
ArrayList<ILaunch> selected = new ArrayList<>();
for (ILaunch l : all) {
ILaunchConfiguration lConf = l.getLaunchConfiguration();
if (lConf!=null && configs.contains(lConf)) {
selected.add(l);
}
//This weird stuff below is for ngrok support in the boot dash which creates working copys that are really
// used as a kind of 'temporary proxy' for its original LaunchConfiguration.
while (lConf instanceof ILaunchConfigurationWorkingCopy) {
lConf = ((ILaunchConfigurationWorkingCopy) lConf).getOriginal();
if (configs.contains(lConf)) {
selected.add(l);
}
}
}
return selected;
}
}

View File

@@ -0,0 +1,79 @@
/*******************************************************************************
* 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.launch.util;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.swt.widgets.Composite;
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.ui.Disposable;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
/**
* Implementation of {@link ILaunchConfigurationTabSection} that wraps and delegates
* to two objects, one representing the 'model' and the other the 'ui'.
*
* @author Kris De Volder
*/
public class DelegatingLaunchConfigurationTabSection extends WizardPageSection implements ILaunchConfigurationTabSection {
private ILaunchConfigurationTabModel model;
private IPageSection ui;
public DelegatingLaunchConfigurationTabSection(IPageWithSections owner, ILaunchConfigurationTabModel model, IPageSection ui) {
super(owner);
this.model = model;
this.ui = ui;
}
@Override
final public LiveVariable<Boolean> getDirtyState() {
return model.getDirtyState();
}
@Override
final public void initializeFrom(ILaunchConfiguration conf) {
model.initializeFrom(conf);
}
@Override
final public void performApply(ILaunchConfigurationWorkingCopy conf) {
model.performApply(conf);
}
@Override
final public void setDefaults(ILaunchConfigurationWorkingCopy conf) {
model.setDefaults(conf);
}
final public void dispose() {
if (ui instanceof Disposable) {
((Disposable) ui).dispose();
ui = null;
}
}
@Override
public void createContents(Composite page) {
ui.createContents(page);
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return ui.getValidator();
}
}

View File

@@ -0,0 +1,196 @@
/*******************************************************************************
* Copyright (c) 2012 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.launch.util;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Shell;
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.OrExpression;
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.Disposable;
import org.springsource.ide.eclipse.commons.livexp.ui.GroupSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
/**
* Like {@link GroupSection} but implements {@link ILaunchConfigurationTabSection}
*
* @author Kris De Volder
*/
public class GroupLaunchTabSection extends WizardPageSection implements ILaunchConfigurationTabSection{
List<IPageSection> sections;
private CompositeValidator validator;
private String groupTitle;
/**
* Setting isVisible to false will make this group disappear.
* Setting it to true will make it re-appear.
*/
public final LiveVariable<Boolean> isVisible = new LiveVariable<Boolean>(true);
private int columns = 1; //one columnby default
private LiveExpression<Boolean> dirtyState;
/**
* If title is null then it creates a normal composite without a box around it. Otherwise
* it creates a 'group' and uses the title as label for the group.
*/
public GroupLaunchTabSection(IPageWithSections owner, String title, WizardPageSection... _sections) {
super(owner);
this.groupTitle = title;
this.sections = new ArrayList<IPageSection>();
for (IPageSection s : _sections) {
if (s!=null) {
sections.add(s);
}
}
validator = new CompositeValidator();
for (IPageSection s : sections) {
validator.addChild(s.getValidator());
}
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return validator;
}
@Override
public void createContents(Composite page) {
final Composite group = createComposite(page);
for (IPageSection s : sections) {
s.createContents(group);
}
isVisible.addListener(new ValueListener<Boolean>() {
public void gotValue(LiveExpression<Boolean> exp, Boolean isVisible) {
group.setVisible(isVisible);
GridData layout = (GridData) group.getLayoutData();
layout.exclude = !isVisible;
group.setLayoutData(layout);
Shell shell = owner.getShell();
if (shell!=null) {
shell.layout(new Control[] {group});
}
};
});
}
private Composite createComposite(Composite page) {
if (groupTitle!=null) {
//Create a group with box around it and a title
final Group group = new Group(page, SWT.NONE);
group.setText(groupTitle);
group.setLayout(createLayout());
GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
return group;
} else {
//Create a normal composite. No box
final Composite composite = new Composite(page, SWT.NONE);
composite.setLayout(createLayout());
GridDataFactory.fillDefaults().grab(true, false).applyTo(composite);
return composite;
}
}
/**
* Factory method that creates the layout for the group. Subclass may override to change how
* components in the group are layed out. Default creates a gridlayout with specified number of
* columns of equals width
*/
protected GridLayout createLayout() {
GridLayout layout = new GridLayout(this.columns, true);
if (groupTitle==null) {
layout.marginWidth = 0;
}
return layout;
}
@Override
public void dispose() {
for (IPageSection s : sections) {
if (s instanceof Disposable) {
((Disposable) s).dispose();
}
}
if (dirtyState!=null) {
dirtyState.dispose();
}
super.dispose();
}
public GroupLaunchTabSection columns(int i) {
Assert.isLegal(i>=1);
this.columns = i;
return this;
}
@Override
public void initializeFrom(ILaunchConfiguration conf) {
for (IPageSection s : sections) {
if (s instanceof ILaunchConfigurationTabSection) {
((ILaunchConfigurationTabSection) s).initializeFrom(conf);
}
}
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy conf) {
for (IPageSection s : sections) {
if (s instanceof ILaunchConfigurationTabSection) {
((ILaunchConfigurationTabSection) s).performApply(conf);
}
}
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy conf) {
for (IPageSection s : sections) {
if (s instanceof ILaunchConfigurationTabSection) {
((ILaunchConfigurationTabSection) s).setDefaults(conf);
}
}
}
@Override
public LiveExpression<Boolean> getDirtyState() {
if (dirtyState==null) {
OrExpression ds = new OrExpression();
for (IPageSection s : sections) {
if (s instanceof ILaunchConfigurationTabSection) {
ds.addChild(((ILaunchConfigurationTabSection) s).getDirtyState());
}
}
dirtyState = ds;
}
return dirtyState;
}
}

View File

@@ -0,0 +1,43 @@
/*******************************************************************************
* 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.launch.util;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
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;
/**
* Methods that a 'model' should implement so it can be easily used
* as a 'supporting model' for wiring up inside of a
* {@link ILaunchConfigurationTabSection}
*/
public interface ILaunchConfigurationTabModel {
void initializeFrom(ILaunchConfiguration conf);
void performApply(ILaunchConfigurationWorkingCopy conf);
void setDefaults(ILaunchConfigurationWorkingCopy conf);
/**
* Launch config tabs must track their 'dirty state'. This state reflects
* whether any of the UI elements contain 'unsaved edits' since the last time
* the ui was synched with the 'stored state' via 'initializeFrom' or
* 'performApply'.
* <p>
* This state is used by launch config editor to properly enable/disable
* the 'revert' and 'apply' buttons.
*/
LiveVariable<Boolean> getDirtyState();
LiveExpression<ValidationResult> getValidator();
}

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2013 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.launch.util;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
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.ui.IPageSection;
/**
* Additional API that a IPageSection may wish to implement when it
* is placed inside a {@link LaunchConfigurationTabWithSections}.
*
* @author Kris De Volder
*/
public interface ILaunchConfigurationTabSection extends IPageSection {
void initializeFrom(ILaunchConfiguration conf);
void performApply(ILaunchConfigurationWorkingCopy conf);
void setDefaults(ILaunchConfigurationWorkingCopy conf);
/**
* Launch config tabs must track their 'dirty state'. This state reflects
* whether any of the UI elements contain 'unsaved edits' since the last time
* the ui was synched with the 'stored state' via 'initializeFrom' or
* 'performApply'.
* <p>
* This state is used by launch config editor to properly enable/disable
* the 'revert' and 'apply' buttons.
*/
LiveExpression<Boolean> getDirtyState();
}

View File

@@ -0,0 +1,133 @@
/*******************************************************************************
* 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.launch.util;
import java.io.IOException;
import java.net.MalformedURLException;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.springsource.ide.eclipse.commons.frameworks.core.util.StringUtils;
import org.springsource.ide.eclipse.commons.livexp.ui.Disposable;
import org.springsource.ide.eclipse.commons.livexp.util.Log;
/**
* A JMX client for interacting with mbeans.
*
* @author Stephane Nicoll
* @author Kris De Volder
*/
public class JMXClient implements Disposable {
private static final Object[] NO_PARAMS = new Object[0];
private static final String[] NO_SIGNATURES = new String[0];
private JMXConnector connector;
private MBeanServerConnection connection;
public JMXClient(String url) throws IOException {
this(createJmxConnectorFromUrl(url));
}
private JMXClient(JMXConnector connector) throws IOException {
this(connector, connector.getMBeanServerConnection());
}
@Override
public void dispose() {
try {
this.connector.close();
} catch (IOException e) {
//Ignore
}
}
private JMXClient(JMXConnector connector, MBeanServerConnection connection) {
this.connector = connector;
this.connection = connection;
}
@SuppressWarnings("unchecked")
public <T> T getAttribute(Class<T> klass, String objectName, String attributeName) throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, IOException {
Object value = getAttribute(objectName, attributeName);
if (value==null || klass.isInstance(value)) {
return (T)value;
} else {
throw new ClassCastException("Value '"+value+"' can't be cast to "+klass);
}
}
public Object getAttribute(String objectName, String attributeName) throws AttributeNotFoundException, InstanceNotFoundException, MBeanException, ReflectionException, IOException {
return this.connection.getAttribute(toObjectName(objectName), attributeName);
}
public Object callOperation(String objectName, String operationName) throws InstanceNotFoundException, MBeanException, ReflectionException, IOException {
return this.connection.invoke(toObjectName(objectName), operationName, NO_PARAMS, NO_SIGNATURES);
}
private ObjectName toObjectName(String name) {
try {
return new ObjectName(name);
}
catch (MalformedObjectNameException ex) {
throw new IllegalArgumentException("Invalid jmx name '" + name + "'");
}
}
/**
* Create a connector for an {@link javax.management.MBeanServer} exposed on the
* current machine and the current port. Security should be disabled.
* @param port the port on which the mbean server is exposed
* @return a connection
* @throws IOException if the connection to that server failed
*/
public static String createLocalJmxUrl(String portStr) {
if (StringUtils.hasText(portStr)) {
try {
int port = Integer.valueOf(portStr);
return createLocalJmxUrl(port);
} catch (Exception e) {
Log.log(e);
}
}
return null;
}
public static String createLocalJmxUrl(Integer port) {
if (port!=null && port>0) {
return "service:jmx:rmi:///jndi/rmi://127.0.0.1:" + port + "/jmxrmi";
}
return null;
}
public static JMXConnector createJmxConnectorFromUrl(String url) throws MalformedURLException, IOException {
JMXServiceURL serviceUrl = new JMXServiceURL(url);
return JMXConnectorFactory.connect(serviceUrl, null);
}
public static JMXConnector createLocalJmxConnector(int port) throws MalformedURLException, IOException {
if (port>0) {
return createJmxConnectorFromUrl(createLocalJmxUrl(""+port));
}
return null;
}
}

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* 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.launch.util;
import org.springsource.ide.eclipse.commons.livexp.core.LiveVariable;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
/**
* Abstract base clase to implement ILaunchConfigurationTabSection.
*
* @author Kris De Volder
*/
public abstract class LaunchConfigurationTabSection extends WizardPageSection implements ILaunchConfigurationTabSection {
private LiveVariable<Boolean> dirtyState = new LiveVariable<Boolean>(true);
public LaunchConfigurationTabSection(IPageWithSections owner) {
super(owner);
}
@Override
public LiveVariable<Boolean> getDirtyState() {
return dirtyState;
}
}

View File

@@ -0,0 +1,167 @@
/*******************************************************************************
* 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.launch.util;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
import org.eclipse.jface.operation.IRunnableContext;
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.springsource.ide.eclipse.commons.livexp.core.CompositeValidator;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.OrExpression;
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.Disposable;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
@SuppressWarnings("restriction")
public abstract class LaunchConfigurationTabWithSections extends AbstractLaunchConfigurationTab
implements IPageWithSections, Disposable {
private List<IPageSection> sections = null;
private boolean disposed = false;
protected abstract List<IPageSection> createSections();
/**
* This method is final. To add content to a {@link LaunchConfigurationTabWithSections}
* override 'createSections' instead.
*/
@Override
public final void createControl(Composite parent) {
Composite page = SWTFactory.createComposite(parent, parent.getFont(), 1, 1, GridData.FILL_BOTH);
//((GridLayout)page.getLayout()).verticalSpacing = 0;
CompositeValidator validator = new CompositeValidator();
OrExpression dirtyState = new OrExpression();
for (IPageSection s : getSections()) {
s.createContents(page);
validator.addChild(s.getValidator());
if (s instanceof ILaunchConfigurationTabSection) {
dirtyState.addChild(((ILaunchConfigurationTabSection) s).getDirtyState());
}
}
setControl(page);
validator.addListener(new ValueListener<ValidationResult>() {
public void gotValue(LiveExpression<ValidationResult> exp, ValidationResult value) {
if (value.isOk()) {
setErrorMessage(null);
setWarningMessage(null);
scheduleUpdateJob();
} else if (value.status==IStatus.WARNING) {
setErrorMessage(null);
setWarningMessage(value.msg);
scheduleUpdateJob();
} else {
setWarningMessage(null);
setErrorMessage(value.msg);
scheduleUpdateJob();
}
}
});
dirtyState.addListener(new ValueListener<Boolean>() {
public void gotValue(LiveExpression<Boolean> exp, Boolean dirty) {
if (dirty!=null && dirty!=isDirty()) {
setDirty(dirty);
if (dirty) {
//no sense in refreshing UI when state actually became 'clean'.
scheduleUpdateJob();
}
}
}
});
}
@Override
protected void scheduleUpdateJob() {
//ignore 'resfresh' request when there's no UI.
Control control = getControl();
if (control!=null && !control.isDisposed()) {
super.scheduleUpdateJob();
}
}
@Override
public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
for (IPageSection s : getSections()) {
if (s instanceof ILaunchConfigurationTabSection) {
((ILaunchConfigurationTabSection)s).setDefaults(configuration);
}
}
}
private Iterable<IPageSection> getSections() {
if (!disposed) {
if (sections==null) {
sections = createSections();
}
return sections;
}
return Collections.emptyList();
}
@Override
public void initializeFrom(ILaunchConfiguration configuration) {
for (IPageSection s : getSections()) {
if (s instanceof ILaunchConfigurationTabSection) {
((ILaunchConfigurationTabSection)s).initializeFrom(configuration);
}
}
}
@Override
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
for (IPageSection s : getSections()) {
if (s instanceof ILaunchConfigurationTabSection) {
((ILaunchConfigurationTabSection)s).performApply(configuration);
}
}
setDirty(false);
}
@Override
public Shell getShell() {
return super.getShell();
}
@Override
public void dispose() {
if (!disposed) {
if (sections!=null) {
for (IPageSection s : sections) {
if (s instanceof Disposable) {
((Disposable) s).dispose();
}
}
sections = null;
}
disposed = true;
}
super.dispose();
}
public IRunnableContext getRunnableContext() {
return super.getLaunchConfigurationDialog();
};
}

View File

@@ -0,0 +1,52 @@
/*******************************************************************************
* 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.launch.util;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.HashSet;
/**
* @author Kris De Volder
*/
public class PortFinder {
/**
* If you only need the one port you can use this. No need to instantiate the class
*/
public static int findFreePort() throws IOException {
ServerSocket socket = new ServerSocket(0);
try {
return socket.getLocalPort();
} finally {
try {
socket.close();
} catch (IOException e) {
}
}
}
private HashSet<Integer> used = new HashSet<Integer>();
/**
* Finds a port that is currently free and is guaranteed to be different from any of the
* port numbers previously returned by this PortFinder instance.
*/
public synchronized int findUniqueFreePort() throws IOException {
int port;
do {
port = findFreePort();
} while (used.contains(port));
used.add(port);
return port;
}
}

View File

@@ -0,0 +1,32 @@
/*******************************************************************************
* 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.launch.util;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.springsource.ide.eclipse.commons.livexp.util.Parser;
public class ProjectNameParser implements Parser<IProject> {
public static final ProjectNameParser INSTANCE = new ProjectNameParser();
private ProjectNameParser() {
}
@Override
public IProject parse(String projectName) {
if (projectName!=null) {
return ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
}
return null;
}
}

View File

@@ -0,0 +1,104 @@
/*******************************************************************************
* 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.launch.util;
import java.io.IOException;
import java.util.concurrent.Callable;
import javax.management.remote.JMXConnector;
import org.eclipse.core.runtime.Assert;
import org.eclipse.debug.core.ILaunch;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
/**
* Creates and manages an instance of {@link SpringApplicationLifecycleClient}.
*
* @author Kris De Volder
*/
public class SpringApplicationLifeCycleClientManager {
private Callable<JMXConnector> connectionProvider;
private JMXConnector connector;
private SpringApplicationLifecycleClient client;
public SpringApplicationLifeCycleClientManager(Callable<JMXConnector> connectionProvider) {
Assert.isNotNull(connectionProvider);
this.connectionProvider = connectionProvider;
}
/**
* Convenenience method, use ILaunch as the jmxPort provider.
*/
public SpringApplicationLifeCycleClientManager(ILaunch l) {
this(() -> createJMXConnection(BootLaunchConfigurationDelegate.getJMXPortAsInt(l)));
}
private static JMXConnector createJMXConnection(int port) {
if (port <=0) {
throw new IllegalStateException("JMX port not specified");
}
try {
return JMXClient.createLocalJmxConnector(port);
} catch (IOException e) {
return null;
}
}
/**
* Convenenience method, use a given fixed port.
*/
public SpringApplicationLifeCycleClientManager(int resolvedPort) {
this(() -> createJMXConnection(resolvedPort));
}
/**
* Dispose of current client and JMX connection. This does not
* make the manager itself unusable, as an attempt will be made to
* re-establish the connection the next time it is needed.
*/
public synchronized void disposeClient() {
try {
if (connector!=null) {
connector.close();
}
} catch (Exception e) {
//ignore
}
client = null;
connector = null;
}
/**
* Try to obtain a client, may return null if a connection could not be established.
*/
public SpringApplicationLifecycleClient getLifeCycleClient() throws Exception {
try {
if (client==null) {
connector = connectionProvider.call();
if (connector!=null) {
client = new SpringApplicationLifecycleClient(
connector.getMBeanServerConnection(),
SpringApplicationLifecycleClient.DEFAULT_OBJECT_NAME
);
}
}
return client;
} catch (Exception e) {
//e.printStackTrace();
//Someting went wrong creating client (most likely process we are trying to connect
// doesn't exist yet or has been terminated.
disposeClient();
throw e;
}
}
}

View File

@@ -0,0 +1,113 @@
/*******************************************************************************
* Copyright (c) 2013-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.launch.util;
import java.io.IOException;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
/**
* A JMX client for the {@code SpringApplicationLifecycle} mbean. Permits to obtain
* information about the lifecycle of a given Spring application.
*
* @author Stephane Nicoll
*/
public class SpringApplicationLifecycleClient {
// Note: see SpringApplicationLifecycleAutoConfiguration
static final String DEFAULT_OBJECT_NAME = "org.springframework.boot:type=Admin,name=SpringApplication";
private final MBeanServerConnection connection;
private final ObjectName objectName;
public SpringApplicationLifecycleClient(MBeanServerConnection connection,
String jmxName) {
this.connection = connection;
this.objectName = toObjectName(jmxName);
}
/**
* Check if the spring application managed by this instance is ready. Returns
* {@code false} if the mbean is not yet deployed so this method should be repeatedly
* called until a timeout is reached.
* @return {@code true} if the application is ready to service requests
* @throws JmxClientException if the JMX service could not be contacted
*/
public boolean isReady() throws Exception {
try {
return (Boolean) this.connection.getAttribute(this.objectName, "Ready");
}
catch (InstanceNotFoundException ex) {
return false; // Instance not available yet
}
catch (AttributeNotFoundException ex) {
throw new IllegalStateException(
"Unexpected: attribute 'Ready' not available", ex);
}
catch (ReflectionException ex) {
throw new Exception("Failed to retrieve Ready attribute",
ex.getCause());
}
catch (MBeanException ex) {
throw new Exception(ex.getMessage(), ex);
}
catch (IOException ex) {
throw new Exception(ex.getMessage(), ex);
}
}
public int getProperty(String prop, int defaultValue) throws InstanceNotFoundException, MBeanException, ReflectionException, IOException {
Object o = this.connection.invoke(this.objectName,"getProperty",
new String[] {prop},
new String[] {String.class.getName()});
if (o instanceof Integer) {
return (Integer)o;
} else if (o instanceof String) {
return Integer.parseInt((String) o);
}
return defaultValue;
}
/**
* Stop the application managed by this instance.
* @throws JmxClientException if the JMX service could not be contacted
* @throws IOException if an I/O error occurs
* @throws InstanceNotFoundException if the lifecycle mbean cannot be found
*/
public void stop() throws Exception {
try {
this.connection.invoke(this.objectName, "shutdown", null, null);
}
catch (ReflectionException ex) {
throw new Exception("Shutdown failed", ex.getCause());
}
catch (MBeanException ex) {
throw new Exception("Could not invoke shutdown operation", ex);
}
}
public static ObjectName toObjectName(String name) {
try {
return new ObjectName(name);
}
catch (MalformedObjectNameException ex) {
throw new IllegalArgumentException("Invalid jmx name '" + name + "'");
}
}
}

View File

@@ -0,0 +1,79 @@
/*******************************************************************************
* Copyright (c) 2008 Software Competence Center Hagenberg (SCCH) GmbH
* Copyright (c) 2008 Mario Winterer
* 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
*******************************************************************************/
package org.springframework.ide.eclipse.boot.launch.util;
import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.IContentProposalListener2;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.widgets.Composite;
public class TextCellEditorWithContentProposal extends TextCellEditor {
private ContentProposalAdapter contentProposalAdapter;
private boolean popupOpen = false; // true, iff popup is currently open
public TextCellEditorWithContentProposal(Composite parent, IContentProposalProvider contentProposalProvider,
KeyStroke keyStroke, char[] autoActivationCharacters) {
super(parent);
enableContentProposal(contentProposalProvider, keyStroke, autoActivationCharacters);
}
public TextCellEditorWithContentProposal setProposalAcceptanceStyle(int acceptance) {
contentProposalAdapter.setProposalAcceptanceStyle(acceptance);
return this;
}
private void enableContentProposal(IContentProposalProvider contentProposalProvider, KeyStroke keyStroke,
char[] autoActivationCharacters) {
contentProposalAdapter = new ContentProposalAdapter(text, new TextContentAdapter(),
contentProposalProvider, keyStroke, autoActivationCharacters);
// Listen for popup open/close events to be able to handle focus events correctly
contentProposalAdapter.addContentProposalListener(new IContentProposalListener2() {
public void proposalPopupClosed(ContentProposalAdapter adapter) {
popupOpen = false;
}
public void proposalPopupOpened(ContentProposalAdapter adapter) {
popupOpen = true;
}
});
}
/**
* Return the {@link ContentProposalAdapter} of this cell editor.
*
* @return the {@link ContentProposalAdapter}
*/
public ContentProposalAdapter getContentProposalAdapter() {
return contentProposalAdapter;
}
protected void focusLost() {
if (!popupOpen) {
// Focus lost deactivates the cell editor.
// This must not happen if focus lost was caused by activating
// the completion proposal popup.
super.focusLost();
}
}
protected boolean dependsOnExternalFocusListener() {
// Always return false;
// Otherwise, the ColumnViewerEditor will install an additional focus listener
// that cancels cell editing on focus lost, even if focus gets lost due to
// activation of the completion proposal popup. See also bug 58777.
return false;
}
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* 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.launch.util;
import org.springsource.ide.eclipse.commons.frameworks.core.ExceptionUtil;
/**
* Helper class that makes it easy to keep trying to execute a bit of code
* repeatedly until it either succeeds (no exceptions) or times out.
* <P>
* Warning: this is not meant to wrap long blocking operations. Long blocking
* operation will not be interupted to force the timeout to be obeyed.
*
* @author Kris De Volder
*/
public abstract class WaitFor {
private static final long DEFAULT_INTERVAL = 200 /*ms*/;
public WaitFor(long timeout) throws Exception {
this(timeout, DEFAULT_INTERVAL);
}
public WaitFor(long timeout, long interval) throws Exception {
waitForIt(timeout, interval);
}
private void waitForIt(long timeout, long interval) throws Exception {
long endTime = System.currentTimeMillis() + timeout;
Throwable e = null;
boolean retry;
do {
try {
run();
e = null;
} catch (Throwable _e) {
e = _e;
}
retry = e!=null && System.currentTimeMillis() < endTime;
if (retry) {
try {
//System.out.println("Failed: "+e.getMessage());
Thread.sleep(DEFAULT_INTERVAL);
//System.out.println("Retrying");
} catch (InterruptedException ie) {
}
}
} while (retry);
if (e!=null) {
//System.out.println("FAIL");
throw ExceptionUtil.exception(e);
}
//System.out.println("SUCCESS");
}
public abstract void run() throws Exception;
}