diff --git a/CHANGELOG.json b/CHANGELOG.json
index 43ebeca2..beaab57d 100644
--- a/CHANGELOG.json
+++ b/CHANGELOG.json
@@ -1,5 +1,14 @@
{
"current": [
+ "**Support AWS SAM Local to locally debug Lambda functions and AWS Gateway**",
+ "",
+ "* Debug Lambda Function",
+ "![debug-lambda-function](https://s3.amazonaws.com/aws-eclipse-toolkit/eclipse/release/debug-lambda-function.gif)",
+ "",
+ "* Debug API Gateway",
+ "![debug-api-gateway](https://s3.amazonaws.com/aws-eclipse-toolkit/eclipse/release/debug-api-gateway.gif)"
+ ],
+ "v201710260046": [
"* Update Java SDK samples."
],
"v201710231659": [
diff --git a/bundles/com.amazonaws.eclipse.codecommit/src/com/amazonaws/eclipse/codecommit/widgets/GitCredentialsComposite.java b/bundles/com.amazonaws.eclipse.codecommit/src/com/amazonaws/eclipse/codecommit/widgets/GitCredentialsComposite.java
index 0e7b6ac1..ca75dd39 100644
--- a/bundles/com.amazonaws.eclipse.codecommit/src/com/amazonaws/eclipse/codecommit/widgets/GitCredentialsComposite.java
+++ b/bundles/com.amazonaws.eclipse.codecommit/src/com/amazonaws/eclipse/codecommit/widgets/GitCredentialsComposite.java
@@ -30,6 +30,7 @@
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.beans.PojoProperties;
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
@@ -117,19 +118,13 @@ private void createUsernamePasswordSection() {
+ "Toolkit for Eclipse to create a new set of Git credentials under the current selected account. see "
+ "CreateServiceSpecificCredential for more information.", GIT_CREDENTIALS_DOC, CREATE_SERVICE_SPECIFIC_CREDENTIALS_DOC), 2);
- usernameComplex = TextComplex.builder()
- .composite(this)
- .dataBindingContext(dataBindingContext)
- .pojoObservableValue(PojoObservables.observeValue(dataModel, P_USERNAME))
+ usernameComplex = TextComplex.builder(this, dataBindingContext, PojoObservables.observeValue(dataModel, P_USERNAME))
.addValidator(usernameValidator == null ? new NotEmptyValidator("User name must be provided!") : usernameValidator)
.labelValue("User name:")
.defaultValue(dataModel.getUsername())
.build();
- passwordComplex = TextComplex.builder()
- .composite(this)
- .dataBindingContext(dataBindingContext)
- .pojoObservableValue(PojoObservables.observeValue(dataModel, P_PASSWORD))
+ passwordComplex = TextComplex.builder(this, dataBindingContext, PojoObservables.observeValue(dataModel, P_PASSWORD))
.addValidator(passwordValidator == null ? new NotEmptyValidator("Password must be provided!") : passwordValidator)
.labelValue("Password: ")
.defaultValue(dataModel.getPassword())
diff --git a/bundles/com.amazonaws.eclipse.core/META-INF/MANIFEST.MF b/bundles/com.amazonaws.eclipse.core/META-INF/MANIFEST.MF
index 8bf3b8cb..65af1ddc 100644
--- a/bundles/com.amazonaws.eclipse.core/META-INF/MANIFEST.MF
+++ b/bundles/com.amazonaws.eclipse.core/META-INF/MANIFEST.MF
@@ -16,6 +16,7 @@ Export-Package: com.amazonaws.eclipse.core,
com.amazonaws.eclipse.core.accounts,
com.amazonaws.eclipse.core.accounts.preferences,
com.amazonaws.eclipse.core.accounts.profiles,
+ com.amazonaws.eclipse.core.ansi,
com.amazonaws.eclipse.core.diagnostic.utils,
com.amazonaws.eclipse.core.egit,
com.amazonaws.eclipse.core.egit.jobs,
@@ -60,6 +61,7 @@ Require-Bundle: org.eclipse.swt,
org.eclipse.m2e.archetype.common;bundle-version="1.5.1",
org.eclipse.m2e.maven.runtime;bundle-version="1.5.1",
org.eclipse.ui.ide,
+ org.eclipse.ui.console,
org.eclipse.egit;bundle-version="3.4.2",
org.eclipse.egit.core;bundle-version="3.4.2",
org.eclipse.egit.doc;bundle-version="3.4.2",
@@ -68,7 +70,12 @@ Require-Bundle: org.eclipse.swt,
org.eclipse.core.variables;bundle-version="3.2.800",
org.eclipse.equinox.security;bundle-version="1.2.0",
org.eclipse.core.databinding.property,
- com.amazonaws.eclipse.javasdk;bundle-version="1.11.130"
+ com.amazonaws.eclipse.javasdk;bundle-version="1.11.130",
+ org.eclipse.m2e.launching
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
-Import-Package: org.eclipse.jdt.launching
+Import-Package: org.eclipse.debug.core,
+ org.eclipse.debug.core.model,
+ org.eclipse.debug.ui,
+ org.eclipse.jdt.annotation,
+ org.eclipse.jdt.launching
diff --git a/bundles/com.amazonaws.eclipse.core/etc/regions.xml b/bundles/com.amazonaws.eclipse.core/etc/regions.xml
index b3b2e6ff..96022616 100644
--- a/bundles/com.amazonaws.eclipse.core/etc/regions.xml
+++ b/bundles/com.amazonaws.eclipse.core/etc/regions.xml
@@ -11,6 +11,7 @@
https://s3-us-gov-west-1.amazonaws.com/
https://iam.us-gov.amazonaws.com/
https://ec2.us-gov-west-1.amazonaws.com/
+ https://elasticbeanstalk.us-gov-west-1.amazonaws.com/
https://sns.us-gov-west-1.amazonaws.com/
https://sqs.us-gov-west-1.amazonaws.com/
https://rds.us-gov-west-1.amazonaws.com/
@@ -18,8 +19,10 @@
https://autoscaling.us-gov-west-1.amazonaws.com/
https://monitoring.us-gov-west-1.amazonaws.com/
https://cloudformation.us-gov-west-1.amazonaws.com/
+ https://lambda.us-gov-west-1.amazonaws.com/
https://dynamodb.us-gov-west-1.amazonaws.com/
https://streams.dynamodb.us-gov-west-1.amazonaws.com/
+ https://codedeploy.us-gov-west-1.amazonaws.com/
https://logs.us-gov-west-1.amazonaws.com/
https://events.us-gov-west-1.amazonaws.com/
https://kms.us-gov-west-1.amazonaws.com
@@ -39,6 +42,8 @@
https://queue.amazonaws.com/
https://sdb.amazonaws.com/
https://ec2.us-east-1.amazonaws.com/
+ https://ecr.us-east-1.amazonaws.com/
+ https://ecs.us-east-1.amazonaws.com/
https://elasticbeanstalk.us-east-1.amazonaws.com/
https://git.elasticbeanstalk.us-east-1.amazonaws.com/
https://rds.us-east-1.amazonaws.com/
@@ -72,6 +77,8 @@
https://sns.us-east-2.amazonaws.com/
https://sqs.us-east-2.amazonaws.com/
https://ec2.us-east-2.amazonaws.com/
+ https://ecr.us-east-2.amazonaws.com/
+ https://ecs.us-east-2.amazonaws.com/
https://rds.us-east-2.amazonaws.com/
https://cloudformation.us-east-2.amazonaws.com/
https://elasticloadbalancing.us-east-2.amazonaws.com/
@@ -88,6 +95,7 @@
https://codestar.us-east-2.amazonaws.com
https://events.us-east-2.amazonaws.com/
https://kms.us-east-2.amazonaws.com
+ https://opsworks.us-east-2.amazonaws.com/
@@ -104,6 +112,8 @@
https://sqs.us-west-2.amazonaws.com/
https://sdb.us-west-2.amazonaws.com/
https://ec2.us-west-2.amazonaws.com/
+ https://ecr.us-west-2.amazonaws.com/
+ https://ecs.us-west-2.amazonaws.com/
https://rds.us-west-2.amazonaws.com/
https://cloudformation.us-west-2.amazonaws.com/
https://elasticloadbalancing.us-west-2.amazonaws.com/
@@ -121,6 +131,7 @@
https://codestar.us-west-2.amazonaws.com
https://events.us-west-2.amazonaws.com/
https://kms.us-west-2.amazonaws.com
+ https://opsworks.us-west-2.amazonaws.com/
@@ -137,6 +148,8 @@
https://sqs.us-west-1.amazonaws.com/
https://sdb.us-west-1.amazonaws.com/
https://ec2.us-west-1.amazonaws.com/
+ https://ecr.us-west-1.amazonaws.com/
+ https://ecs.us-west-1.amazonaws.com/
https://rds.us-west-1.amazonaws.com/
https://cloudformation.us-west-1.amazonaws.com/
https://elasticloadbalancing.us-west-1.amazonaws.com/
@@ -149,8 +162,11 @@
https://kinesis.us-west-1.amazonaws.com/
https://lambda.us-west-1.amazonaws.com/
https://logs.us-west-1.amazonaws.com/
+ https://codecommit.us-west-1.amazonaws.com
https://events.us-west-1.amazonaws.com/
https://kms.us-west-1.amazonaws.com
+ https://codestar.us-west-1.amazonaws.com
+ https://opsworks.us-west-1.amazonaws.com/
@@ -166,6 +182,8 @@
https://sns.ca-central-1.amazonaws.com/
https://sqs.ca-central-1.amazonaws.com/
https://ec2.ca-central-1.amazonaws.com/
+ https://ecr.ca-central-1.amazonaws.com/
+ https://ecs.ca-central-1.amazonaws.com/
https://rds.ca-central-1.amazonaws.com/
https://cloudformation.ca-central-1.amazonaws.com/
https://elasticloadbalancing.ca-central-1.amazonaws.com/
@@ -176,8 +194,11 @@
https://elasticbeanstalk.ca-central-1.amazonaws.com/
https://codedeploy.ca-central-1.amazonaws.com/
https://kinesis.ca-central-1.amazonaws.com/
+ https://lambda.ca-central-1.amazonaws.com/
https://logs.ca-central-1.amazonaws.com/
+ https://codecommit.ca-central-1.amazonaws.com
https://kms.ca-central-1.amazonaws.com
+ https://opsworks.ca-central-1.amazonaws.com/
@@ -194,6 +215,8 @@
https://sqs.eu-west-1.amazonaws.com/
https://sdb.eu-west-1.amazonaws.com/
https://ec2.eu-west-1.amazonaws.com/
+ https://ecr.eu-west-1.amazonaws.com/
+ https://ecs.eu-west-1.amazonaws.com/
https://elasticbeanstalk.eu-west-1.amazonaws.com/
https://git.elasticbeanstalk.eu-west-1.amazonaws.com/
https://rds.eu-west-1.amazonaws.com/
@@ -211,6 +234,7 @@
https://codestar.eu-west-1.amazonaws.com
https://events.eu-west-1.amazonaws.com/
https://kms.eu-west-1.amazonaws.com
+ https://opsworks.eu-west-1.amazonaws.com/
@@ -226,6 +250,8 @@
https://sns.eu-west-2.amazonaws.com/
https://sqs.eu-west-2.amazonaws.com/
https://ec2.eu-west-2.amazonaws.com/
+ https://ecr.eu-west-2.amazonaws.com/
+ https://ecs.eu-west-2.amazonaws.com/
https://elasticbeanstalk.eu-west-2.amazonaws.com/
https://git.elasticbeanstalk.eu-west-2.amazonaws.com/
https://rds.eu-west-2.amazonaws.com/
@@ -236,9 +262,13 @@
https://codedeploy.eu-west-2.amazonaws.com/
https://dynamodb.eu-west-2.amazonaws.com/
https://streams.dynamodb.eu-west-2.amazonaws.com/
+ https://lambda.eu-west-2.amazonaws.com/
https://kinesis.eu-west-2.amazonaws.com/
https://logs.eu-west-2.amazonaws.com/
+ https://codecommit.eu-west-2.amazonaws.com
https://kms.eu-west-2.amazonaws.com
+ https://codestar.eu-west-2.amazonaws.com
+ https://opsworks.eu-west-2.amazonaws.com/
@@ -254,6 +284,8 @@
https://sns.eu-central-1.amazonaws.com/
https://sqs.eu-central-1.amazonaws.com/
https://ec2.eu-central-1.amazonaws.com/
+ https://ecr.eu-central-1.amazonaws.com/
+ https://ecs.eu-central-1.amazonaws.com/
https://elasticbeanstalk.eu-central-1.amazonaws.com/
https://git.elasticbeanstalk.eu-central-1.amazonaws.com/
https://rds.eu-central-1.amazonaws.com/
@@ -265,9 +297,12 @@
https://streams.dynamodb.eu-central-1.amazonaws.com/
https://lambda.eu-central-1.amazonaws.com/
https://kinesis.eu-central-1.amazonaws.com/
+ https://codecommit.eu-central-1.amazonaws.com
https://logs.eu-central-1.amazonaws.com/
+ https://codestar.eu-central-1.amazonaws.com
https://events.eu-central-1.amazonaws.com/
https://kms.eu-central-1.amazonaws.com
+ https://opsworks.eu-central-1.amazonaws.com/
@@ -283,6 +318,8 @@
https://sns.ap-south-1.amazonaws.com/
https://sqs.ap-south-1.amazonaws.com/
https://ec2.ap-south-1.amazonaws.com/
+ https://ecr.ap-south-1.amazonaws.com/
+ https://ecs.ap-south-1.amazonaws.com/
https://rds.ap-south-1.amazonaws.com/
https://cloudformation.ap-south-1.amazonaws.com/
https://elasticbeanstalk.ap-south-1.amazonaws.com/
@@ -294,8 +331,10 @@
https://lambda.ap-south-1.amazonaws.com/
https://kinesis.ap-south-1.amazonaws.com/
https://logs.ap-south-1.amazonaws.com/
+ https://codecommit.ap-south-1.amazonaws.com
https://events.ap-south-1.amazonaws.com/
https://kms.ap-south-1.amazonaws.com
+ https://opsworks.ap-south-1.amazonaws.com/
@@ -312,6 +351,8 @@
https://sqs.ap-southeast-1.amazonaws.com/
https://sdb.ap-southeast-1.amazonaws.com/
https://ec2.ap-southeast-1.amazonaws.com/
+ https://ecr.ap-southeast-1.amazonaws.com/
+ https://ecs.ap-southeast-1.amazonaws.com/
https://rds.ap-southeast-1.amazonaws.com/
https://cloudformation.ap-southeast-1.amazonaws.com/
https://elasticbeanstalk.ap-southeast-1.amazonaws.com/
@@ -323,9 +364,12 @@
https://streams.dynamodb.ap-southeast-1.amazonaws.com/
https://kinesis.ap-southeast-1.amazonaws.com/
https://logs.ap-southeast-1.amazonaws.com/
+ https://codecommit.ap-southeast-1.amazonaws.com
https://lambda.ap-southeast-1.amazonaws.com/
+ https://codestar.ap-southeast-1.amazonaws.com
https://events.ap-southeast-1.amazonaws.com/
https://kms.ap-southeast-1.amazonaws.com
+ https://opsworks.ap-southeast-1.amazonaws.com/
@@ -342,6 +386,8 @@
http://sqs.ap-northeast-1.amazonaws.com/
https://sdb.ap-northeast-1.amazonaws.com/
https://ec2.ap-northeast-1.amazonaws.com/
+ https://ecr.ap-northeast-1.amazonaws.com/
+ https://ecs.ap-northeast-1.amazonaws.com/
https://rds.ap-northeast-1.amazonaws.com/
https://cloudformation.ap-northeast-1.amazonaws.com/
https://elasticbeanstalk.ap-northeast-1.amazonaws.com/
@@ -354,9 +400,11 @@
https://streams.dynamodb.ap-northeast-1.amazonaws.com/
https://kinesis.ap-northeast-1.amazonaws.com/
https://logs.ap-northeast-1.amazonaws.com/
+ https://codecommit.ap-northeast-1.amazonaws.com
https://lambda.ap-northeast-1.amazonaws.com/
https://events.ap-northeast-1.amazonaws.com/
https://kms.ap-northeast-1.amazonaws.com
+ https://opsworks.ap-northeast-1.amazonaws.com/
@@ -384,9 +432,11 @@
https://streams.dynamodb.ap-northeast-2.amazonaws.com/
https://kinesis.ap-northeast-2.amazonaws.com/
https://logs.ap-northeast-2.amazonaws.com/
+ https://codecommit.ap-northeast-2.amazonaws.com
https://lambda.ap-northeast-2.amazonaws.com/
https://events.ap-northeast-2.amazonaws.com/
https://kms.ap-northeast-2.amazonaws.com
+ https://opsworks.ap-northeast-2.amazonaws.com/
@@ -403,6 +453,8 @@
https://sqs.ap-southeast-2.amazonaws.com/
https://sdb.ap-southeast-2.amazonaws.com/
https://ec2.ap-southeast-2.amazonaws.com/
+ https://ecr.ap-southeast-2.amazonaws.com/
+ https://ecs.ap-southeast-2.amazonaws.com/
https://rds.ap-southeast-2.amazonaws.com/
https://cloudformation.ap-southeast-2.amazonaws.com/
https://elasticbeanstalk.ap-southeast-2.amazonaws.com/
@@ -415,9 +467,12 @@
https://streams.dynamodb.ap-southeast-2.amazonaws.com/
https://kinesis.ap-southeast-2.amazonaws.com/
https://logs.ap-southeast-2.amazonaws.com/
+ https://codecommit.ap-southeast-2.amazonaws.com
https://lambda.ap-southeast-2.amazonaws.com/
+ https://codestar.ap-southeast-2.amazonaws.com
https://events.ap-southeast-2.amazonaws.com/
https://kms.ap-southeast-2.amazonaws.com
+ https://opsworks.ap-southeast-2.amazonaws.com/
@@ -444,8 +499,11 @@
https://elasticbeanstalk.sa-east-1.amazonaws.com/
https://git.elasticbeanstalk.sa-east-1.amazonaws.com/
https://logs.sa-east-1.amazonaws.com/
+ https://codecommit.sa-east-1.amazonaws.com
+ https://lambda.sa-east-1.amazonaws.com/
https://events.sa-east-1.amazonaws.com/
https://kms.sa-east-1.amazonaws.com
+ https://opsworks.sa-east-1.amazonaws.com/
@@ -459,6 +517,8 @@
https://s3.cn-north-1.amazonaws.com.cn/
https://iam.cn-north-1.amazonaws.com.cn/
https://ec2.cn-north-1.amazonaws.com.cn/
+ https://ecr.cn-north-1.amazonaws.com.cn/
+ https://ecs.cn-north-1.amazonaws.com.cn/
https://sns.cn-north-1.amazonaws.com.cn/
https://sqs.cn-north-1.amazonaws.com.cn/
https://rds.cn-north-1.amazonaws.com.cn/
diff --git a/bundles/com.amazonaws.eclipse.core/icons/flags/canada.png b/bundles/com.amazonaws.eclipse.core/icons/flags/canada.png
new file mode 100644
index 00000000..7988ee52
Binary files /dev/null and b/bundles/com.amazonaws.eclipse.core/icons/flags/canada.png differ
diff --git a/bundles/com.amazonaws.eclipse.core/icons/flags/uk.png b/bundles/com.amazonaws.eclipse.core/icons/flags/uk.png
new file mode 100644
index 00000000..c0050667
Binary files /dev/null and b/bundles/com.amazonaws.eclipse.core/icons/flags/uk.png differ
diff --git a/bundles/com.amazonaws.eclipse.core/icons/logo_aws.png b/bundles/com.amazonaws.eclipse.core/icons/logo_aws.png
index 210f0ea9..c0bc75da 100644
Binary files a/bundles/com.amazonaws.eclipse.core/icons/logo_aws.png and b/bundles/com.amazonaws.eclipse.core/icons/logo_aws.png differ
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AWSClientFactory.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AWSClientFactory.java
index 23c1f28a..93d71b3d 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AWSClientFactory.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AWSClientFactory.java
@@ -570,11 +570,7 @@ private T createClient(String endpoint, Class
// Low layer method for building a service client by using the client builder.
@SuppressWarnings("unchecked")
private T createClientByRegion(AwsSyncClientBuilder extends AwsSyncClientBuilder, T> builder, String region, String endpoint) {
- if (region.equals("local")) {
- builder.withEndpointConfiguration(new EndpointConfiguration(endpoint, region));
- } else {
- builder.withRegion(region);
- }
+ builder.withEndpointConfiguration(new EndpointConfiguration(endpoint, region));
Object client = builder
.withCredentials(new AWSStaticCredentialsProvider(getAwsCredentials()))
.withClientConfiguration(createClientConfiguration(endpoint))
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AwsToolkitCore.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AwsToolkitCore.java
index e2fe35b3..eb9d0499 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AwsToolkitCore.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/AwsToolkitCore.java
@@ -523,8 +523,9 @@ private static void registerCustomErrorSupport() {
Policy.setErrorSupportProvider(awsProvider);
}
- // TODO: any better way to check debug mode?
- public static final boolean DEBUG_MODE = false;
+ public boolean isDebugMode() {
+ return getBundleVersion().contains("qualifier");
+ }
private ToolkitAnalyticsManager initializeToolkitAnalyticsManager() {
@@ -535,7 +536,7 @@ private ToolkitAnalyticsManager initializeToolkitAnalyticsManager() {
if (enabled) {
try {
- if (DEBUG_MODE) {
+ if (isDebugMode()) {
toReturn = new ToolkitAnalyticsManagerImpl(
AWSCognitoCredentialsProvider.TEST_PROVIDER,
ClientContextConfig.TEST_CONFIG);
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiCommands.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiCommands.java
new file mode 100644
index 00000000..f4d0833c
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiCommands.java
@@ -0,0 +1,58 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.amazonaws.eclipse.core.ansi;
+
+//From Wikipedia, http://en.wikipedia.org/wiki/ANSI_escape_code
+public class AnsiCommands {
+ public static final int COMMAND_ATTR_RESET = 0; // Reset / Normal (all attributes off)
+ public static final int COMMAND_ATTR_INTENSITY_BRIGHT = 1; // Bright (increased intensity) or Bold
+ public static final int COMMAND_ATTR_INTENSITY_FAINT = 2; // Faint (decreased intensity) (not widely supported)
+ public static final int COMMAND_ATTR_ITALIC = 3; // Italic: on not widely supported. Sometimes treated as inverse.
+ public static final int COMMAND_ATTR_UNDERLINE = 4; // Underline: Single
+ public static final int COMMAND_ATTR_BLINK_SLOW = 5; // Blink: Slow (less than 150 per minute)
+ public static final int COMMAND_ATTR_BLINK_FAST = 6; // Blink: Rapid (MS-DOS ANSI.SYS; 150 per minute or more; not widely supported)
+ public static final int COMMAND_ATTR_NEGATIVE_ON = 7; // Image: Negative (inverse or reverse; swap foreground and background)
+ public static final int COMMAND_ATTR_CONCEAL_ON = 8; // Conceal (not widely supported)
+ public static final int COMMAND_ATTR_CROSSOUT_ON = 9; // Crossed-out (Characters legible, but marked for deletion. Not widely supported.)
+ public static final int COMMAND_ATTR_UNDERLINE_DOUBLE = 21; // Bright/Bold: off or Underline: Double (bold off not widely supported, double underline hardly ever)
+ public static final int COMMAND_ATTR_INTENSITY_NORMAL = 22; // Normal color or intensity (neither bright, bold nor faint)
+ public static final int COMMAND_ATTR_ITALIC_OFF = 23; // Not italic, not Fraktur
+ public static final int COMMAND_ATTR_UNDERLINE_OFF = 24; // Underline: None (not singly or doubly underlined)
+ public static final int COMMAND_ATTR_BLINK_OFF = 25; // Blink: off
+ public static final int COMMAND_ATTR_NEGATIVE_OFF = 27; // Image: Positive
+ public static final int COMMAND_ATTR_CONCEAL_OFF = 28; // Reveal (conceal off)
+ public static final int COMMAND_ATTR_CROSSOUT_OFF = 29; // Not crossed out
+
+ // Extended colors. Next arguments are 5; or 2;;;
+ public static final int COMMAND_HICOLOR_FOREGROUND = 38; // Set text color
+ public static final int COMMAND_HICOLOR_BACKGROUND = 48; // Set background color
+
+ public static final int COMMAND_COLOR_FOREGROUND_RESET = 39; // Default text color
+ public static final int COMMAND_COLOR_BACKGROUND_RESET = 49; // Default background color
+
+ public static final int COMMAND_COLOR_FOREGROUND_FIRST = 30; // First text color
+ public static final int COMMAND_COLOR_FOREGROUND_LAST = 37; // Last text color
+ public static final int COMMAND_COLOR_BACKGROUND_FIRST = 40; // First background text color
+ public static final int COMMAND_COLOR_BACKGROUND_LAST = 47; // Last background text color
+
+ public static final int COMMAND_ATTR_FRAMED_ON = 51; // Framed
+ public static final int COMMAND_ATTR_FRAMED_OFF = 54; // Not framed or encircled
+
+ public static final int COMMAND_HICOLOR_FOREGROUND_FIRST = 90; // First text color
+ public static final int COMMAND_HICOLOR_FOREGROUND_LAST = 97; // Last text color
+ public static final int COMMAND_HICOLOR_BACKGROUND_FIRST = 100; // First background text color
+ public static final int COMMAND_HICOLOR_BACKGROUND_LAST = 107; // Last background text color
+
+ public static final int COMMAND_COLOR_INTENSITY_DELTA = 8; // Last background text color
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsoleAttributes.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsoleAttributes.java
new file mode 100644
index 00000000..a7bed711
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsoleAttributes.java
@@ -0,0 +1,170 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.amazonaws.eclipse.core.ansi;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+
+import com.amazonaws.eclipse.core.util.OsPlatformUtils;
+
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.*;
+
+public class AnsiConsoleAttributes implements Cloneable {
+ public final static int UNDERLINE_NONE = -1; // nothing in SWT, a bit of an abuse
+
+ public Integer currentBgColor;
+ public Integer currentFgColor;
+ public int underline;
+ public boolean bold;
+ public boolean italic;
+ public boolean invert;
+ public boolean conceal;
+ public boolean strike;
+ public boolean framed;
+
+ public AnsiConsoleAttributes() {
+ reset();
+ }
+
+ public void reset() {
+ currentBgColor = null;
+ currentFgColor = null;
+ underline = UNDERLINE_NONE;
+ bold = false;
+ italic = false;
+ invert = false;
+ conceal = false;
+ strike = false;
+ framed = false;
+ }
+
+ @Override
+ public AnsiConsoleAttributes clone() {
+ AnsiConsoleAttributes result = new AnsiConsoleAttributes();
+ result.currentBgColor = currentBgColor;
+ result.currentFgColor = currentFgColor;
+ result.underline = underline;
+ result.bold = bold;
+ result.italic = italic;
+ result.invert = invert;
+ result.conceal = conceal;
+ result.strike = strike;
+ result.framed = framed;
+ return result;
+ }
+
+ public static Color hiliteRgbColor(Color c) {
+ if (c == null)
+ return new Color(null, new RGB(0xff, 0xff, 0xff));
+ int red = c.getRed() * 2;
+ int green = c.getGreen() * 2;
+ int blue = c.getBlue() * 2;
+
+ if (red > 0xff) red = 0xff;
+ if (green > 0xff) green = 0xff;
+ if (blue > 0xff) blue = 0xff;
+
+ return new Color(null, new RGB(red, green, blue)); // here
+ }
+
+ // This function maps from the current attributes as "described" by escape sequences to real,
+ // Eclipse console specific attributes (resolving color palette, default colors, etc.)
+ public static void updateRangeStyle(StyleRange range, AnsiConsoleAttributes attribute) {
+ boolean useWindowsMapping = OsPlatformUtils.isWindows();
+ AnsiConsoleAttributes tempAttrib = attribute.clone();
+
+ boolean hilite = false;
+
+ if (useWindowsMapping) {
+ if (tempAttrib.bold) {
+ tempAttrib.bold = false; // not supported, rendered as intense, already done that
+ hilite = true;
+ }
+ if (tempAttrib.italic) {
+ tempAttrib.italic = false;
+ tempAttrib.invert = true;
+ }
+ tempAttrib.underline = UNDERLINE_NONE; // not supported on Windows
+ tempAttrib.strike = false; // not supported on Windows
+ tempAttrib.framed = false; // not supported on Windows
+ }
+
+ // Prepare the foreground color
+ if (hilite) {
+ if (tempAttrib.currentFgColor == null) {
+ range.foreground = AnsiConsolePreferenceUtils.getDebugConsoleFgColor();
+ range.foreground = hiliteRgbColor(range.foreground);
+ } else {
+ if (tempAttrib.currentFgColor < COMMAND_COLOR_INTENSITY_DELTA)
+ range.foreground = new Color(null, AnsiConsoleColorPalette.getColor(tempAttrib.currentFgColor + COMMAND_COLOR_INTENSITY_DELTA));
+ else
+ range.foreground = new Color(null, AnsiConsoleColorPalette.getColor(tempAttrib.currentFgColor));
+ }
+ } else {
+ if (tempAttrib.currentFgColor != null)
+ range.foreground = new Color(null, AnsiConsoleColorPalette.getColor(tempAttrib.currentFgColor));
+ }
+
+ // Prepare the background color
+ if (tempAttrib.currentBgColor != null)
+ range.background = new Color(null, AnsiConsoleColorPalette.getColor(tempAttrib.currentBgColor));
+
+ // These two still mess with the foreground/background colors
+ // We need to solve them before we use them for strike/underline/frame colors
+ if (tempAttrib.invert) {
+ if (range.foreground == null)
+ range.foreground = AnsiConsolePreferenceUtils.getDebugConsoleFgColor();
+ if (range.background == null)
+ range.background = AnsiConsolePreferenceUtils.getDebugConsoleBgColor();
+ Color tmp = range.background;
+ range.background = range.foreground;
+ range.foreground = tmp;
+ }
+
+ if (tempAttrib.conceal) {
+ if (range.background == null)
+ range.background = AnsiConsolePreferenceUtils.getDebugConsoleBgColor();
+ range.foreground = range.background;
+ }
+
+ range.font = null;
+ range.fontStyle = SWT.NORMAL;
+ // Prepare the rest of the attributes
+ if (tempAttrib.bold)
+ range.fontStyle |= SWT.BOLD;
+
+ if (tempAttrib.italic)
+ range.fontStyle |= SWT.ITALIC;
+
+ if (tempAttrib.underline != UNDERLINE_NONE) {
+ range.underline = true;
+ range.underlineColor = range.foreground;
+ range.underlineStyle = tempAttrib.underline;
+ }
+ else
+ range.underline = false;
+
+ range.strikeout = tempAttrib.strike;
+ range.strikeoutColor = range.foreground;
+
+ if (tempAttrib.framed) {
+ range.borderStyle = SWT.BORDER_SOLID;
+ range.borderColor = range.foreground;
+ }
+ else
+ range.borderStyle = SWT.NONE;
+ }
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsoleColorPalette.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsoleColorPalette.java
new file mode 100644
index 00000000..10b7fdd7
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsoleColorPalette.java
@@ -0,0 +1,139 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.amazonaws.eclipse.core.ansi;
+
+import org.eclipse.swt.graphics.RGB;
+
+import com.amazonaws.eclipse.core.util.OsPlatformUtils;
+
+public class AnsiConsoleColorPalette {
+ private static final int PALETTE_SIZE = 256;
+
+ // From Wikipedia, http://en.wikipedia.org/wiki/ANSI_escape_code
+ private final static RGB[] paletteXP = {
+ new RGB( 0, 0, 0), // black
+ new RGB(128, 0, 0), // red
+ new RGB( 0, 128, 0), // green
+ new RGB(128, 128, 0), // brown/yellow
+ new RGB( 0, 0, 128), // blue
+ new RGB(128, 0, 128), // magenta
+ new RGB( 0, 128, 128), // cyan
+ new RGB(192, 192, 192), // gray
+ new RGB(128, 128, 128), // dark gray
+ new RGB(255, 0, 0), // bright red
+ new RGB( 0, 255, 0), // bright green
+ new RGB(255, 255, 0), // yellow
+ new RGB( 0, 0, 255), // bright blue
+ new RGB(255, 0, 255), // bright magenta
+ new RGB( 0, 255, 255), // bright cyan
+ new RGB(255, 255, 255) // white
+ };
+ private final static RGB[] paletteMac = {
+ new RGB( 0, 0, 0), // black
+ new RGB(194, 54, 33), // red
+ new RGB( 37, 188, 36), // green
+ new RGB(173, 173, 39), // brown/yellow
+ new RGB( 73, 46, 225), // blue
+ new RGB(211, 56, 211), // magenta
+ new RGB( 51, 187, 200), // cyan
+ new RGB(203, 204, 205), // gray
+ new RGB(129, 131, 131), // dark gray
+ new RGB(252, 57, 31), // bright red
+ new RGB( 49, 231, 34), // bright green
+ new RGB(234, 236, 35), // yellow
+ new RGB( 88, 51, 255), // bright blue
+ new RGB(249, 53, 248), // bright magenta
+ new RGB( 20, 240, 240), // bright cyan
+ new RGB(233, 235, 235) // white
+ };
+ private final static RGB[] paletteXTerm = {
+ new RGB( 0, 0, 0), // black
+ new RGB(205, 0, 0), // red
+ new RGB( 0, 205, 0), // green
+ new RGB(205, 205, 0), // brown/yellow
+ new RGB( 0, 0, 238), // blue
+ new RGB(205, 0, 205), // magenta
+ new RGB( 0, 205, 205), // cyan
+ new RGB(229, 229, 229), // gray
+ new RGB(127, 127, 127), // dark gray
+ new RGB(255, 0, 0), // bright red
+ new RGB( 0, 255, 0), // bright green
+ new RGB(255, 255, 0), // yellow
+ new RGB( 92, 92, 255), // bright blue
+ new RGB(255, 0, 255), // bright magenta
+ new RGB( 0, 255, 255), // bright cyan
+ new RGB(255, 255, 255) // white
+ };
+ private static RGB[] palette = getDefaultPalette();
+
+ public static boolean isValidIndex(int value) {
+ return value >= 0 && value < PALETTE_SIZE;
+ }
+
+ static int TRUE_RGB_FLAG = 0x10000000; // Representing true RGB colors as 0x10RRGGBB
+
+ public static int hackRgb(int r, int g, int b) {
+ if (!isValidIndex(r)) return -1;
+ if (!isValidIndex(g)) return -1;
+ if (!isValidIndex(b)) return -1;
+ return TRUE_RGB_FLAG | r << 16 | g << 8 | b;
+ }
+
+ static int safe256(int value, int modulo) {
+ int result = value * PALETTE_SIZE / modulo;
+ return result < PALETTE_SIZE ? result : PALETTE_SIZE - 1;
+ }
+
+ public static RGB getColor(Integer index) {
+ if (null == index)
+ return null;
+
+ if (index >= TRUE_RGB_FLAG) {
+ int red = index >> 16 & 0xff;
+ int green = index >> 8 & 0xff;
+ int blue = index & 0xff;
+ return new RGB(red, green, blue);
+ }
+
+ if (index >= 0 && index < palette.length) // basic, 16 color palette
+ return palette[index];
+
+ if (index >= 16 && index < 232) { // 6x6x6 color matrix
+ int color = index - 16;
+ int blue = color % 6;
+ color = color / 6;
+ int green = color % 6;
+ int red = color / 6;
+
+ return new RGB(safe256(red, 6), safe256(green, 6), safe256(blue, 6));
+ }
+
+ if (index >= 232 && index < PALETTE_SIZE) { // grayscale
+ int gray = safe256(index - 232, 24);
+ return new RGB(gray, gray, gray);
+ }
+
+ return null;
+ }
+
+ private static RGB[] getDefaultPalette() {
+ if (OsPlatformUtils.isWindows()) {
+ return paletteXP;
+ } else if (OsPlatformUtils.isMac()) {
+ return paletteMac;
+ } else {
+ return paletteXTerm;
+ }
+ }
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsolePageParticipant.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsolePageParticipant.java
new file mode 100644
index 00000000..66020294
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsolePageParticipant.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.amazonaws.eclipse.core.ansi;
+
+import org.eclipse.swt.custom.StyledText;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IConsolePageParticipant;
+import org.eclipse.ui.part.IPageBookViewPage;
+
+public class AnsiConsolePageParticipant implements IConsolePageParticipant {
+ @Override
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+ @Override
+ public void activated() {}
+
+ @Override
+ public void deactivated() {}
+
+ @Override
+ public void dispose() {}
+
+ @Override
+ public void init(IPageBookViewPage page, IConsole console) {
+ if (page.getControl() instanceof StyledText) {
+ StyledText viewer = (StyledText) page.getControl();
+ viewer.addLineStyleListener(new AnsiConsoleStyleListener());
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsolePreferenceUtils.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsolePreferenceUtils.java
new file mode 100644
index 00000000..19501c12
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsolePreferenceUtils.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.amazonaws.eclipse.core.ansi;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.IPreferencesService;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.RGB;
+
+public class AnsiConsolePreferenceUtils {
+ private final static String DEBUG_CONSOLE_PLUGIN_ID = "org.eclipse.debug.ui";
+ private final static String DEBUG_CONSOLE_FALLBACK_BKCOLOR = "0,0,0";
+ private final static String DEBUG_CONSOLE_FALLBACK_FGCOLOR = "192,192,192";
+
+ static Color colorFromStringRgb(String strRgb) {
+ Color result = null;
+ String[] splitted = strRgb.split(",");
+ if (splitted != null && splitted.length == 3) {
+ int red = tryParseInteger(splitted[0]);
+ int green = tryParseInteger(splitted[1]);
+ int blue = tryParseInteger(splitted[2]);
+ result = new Color(null, new RGB(red, green, blue));
+ }
+ return result;
+ }
+
+ public static Color getDebugConsoleBgColor() {
+ IPreferencesService ps = Platform.getPreferencesService();
+ String value = ps.getString(DEBUG_CONSOLE_PLUGIN_ID, "org.eclipse.debug.ui.consoleBackground",
+ DEBUG_CONSOLE_FALLBACK_BKCOLOR, null);
+ return colorFromStringRgb(value);
+ }
+
+ public static Color getDebugConsoleFgColor() {
+ IPreferencesService ps = Platform.getPreferencesService();
+ String value = ps.getString(DEBUG_CONSOLE_PLUGIN_ID, "org.eclipse.debug.ui.outColor",
+ DEBUG_CONSOLE_FALLBACK_FGCOLOR, null);
+ return colorFromStringRgb(value);
+ }
+
+ public static int tryParseInteger(String text) {
+ if ("".equals(text))
+ return -1;
+
+ try {
+ return Integer.parseInt(text);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsoleStyleListener.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsoleStyleListener.java
new file mode 100644
index 00000000..6cd99b87
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ansi/AnsiConsoleStyleListener.java
@@ -0,0 +1,214 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.amazonaws.eclipse.core.ansi;
+
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_CONCEAL_OFF;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_CONCEAL_ON;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_CROSSOUT_OFF;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_CROSSOUT_ON;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_FRAMED_OFF;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_FRAMED_ON;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_INTENSITY_BRIGHT;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_INTENSITY_FAINT;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_INTENSITY_NORMAL;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_ITALIC;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_ITALIC_OFF;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_NEGATIVE_OFF;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_NEGATIVE_ON;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_RESET;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_UNDERLINE;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_UNDERLINE_DOUBLE;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_ATTR_UNDERLINE_OFF;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_COLOR_BACKGROUND_FIRST;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_COLOR_BACKGROUND_LAST;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_COLOR_BACKGROUND_RESET;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_COLOR_FOREGROUND_FIRST;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_COLOR_FOREGROUND_LAST;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_COLOR_FOREGROUND_RESET;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_COLOR_INTENSITY_DELTA;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_HICOLOR_BACKGROUND;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_HICOLOR_BACKGROUND_FIRST;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_HICOLOR_BACKGROUND_LAST;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_HICOLOR_FOREGROUND;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_HICOLOR_FOREGROUND_FIRST;
+import static com.amazonaws.eclipse.core.ansi.AnsiCommands.COMMAND_HICOLOR_FOREGROUND_LAST;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.LineStyleEvent;
+import org.eclipse.swt.custom.LineStyleListener;
+import org.eclipse.swt.custom.StyleRange;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GlyphMetrics;
+
+public class AnsiConsoleStyleListener implements LineStyleListener {
+ private AnsiConsoleAttributes lastAttributes = new AnsiConsoleAttributes();
+ private AnsiConsoleAttributes currentAttributes = new AnsiConsoleAttributes();
+ private final static Pattern pattern = Pattern.compile("\u001b\\[[\\d;]*[A-HJKSTfimnsu]");
+ private final static char ESCAPE_SGR = 'm';
+
+ int lastRangeEnd = 0;
+
+ private boolean interpretCommand(List nCommands) {
+ boolean result = false;
+
+ Iterator iter = nCommands.iterator();
+ while (iter.hasNext()) {
+ int nCmd = iter.next();
+ switch (nCmd) {
+ case COMMAND_ATTR_RESET: currentAttributes.reset(); break;
+
+ case COMMAND_ATTR_INTENSITY_BRIGHT: currentAttributes.bold = true; break;
+ case COMMAND_ATTR_INTENSITY_FAINT: currentAttributes.bold = false; break;
+ case COMMAND_ATTR_INTENSITY_NORMAL: currentAttributes.bold = false; break;
+
+ case COMMAND_ATTR_ITALIC: currentAttributes.italic = true; break;
+ case COMMAND_ATTR_ITALIC_OFF: currentAttributes.italic = false; break;
+
+ case COMMAND_ATTR_UNDERLINE: currentAttributes.underline = SWT.UNDERLINE_SINGLE; break;
+ case COMMAND_ATTR_UNDERLINE_DOUBLE: currentAttributes.underline = SWT.UNDERLINE_DOUBLE; break;
+ case COMMAND_ATTR_UNDERLINE_OFF: currentAttributes.underline = AnsiConsoleAttributes.UNDERLINE_NONE; break;
+
+ case COMMAND_ATTR_CROSSOUT_ON: currentAttributes.strike = true; break;
+ case COMMAND_ATTR_CROSSOUT_OFF: currentAttributes.strike = false; break;
+
+ case COMMAND_ATTR_NEGATIVE_ON: currentAttributes.invert = true; break;
+ case COMMAND_ATTR_NEGATIVE_OFF: currentAttributes.invert = false; break;
+
+ case COMMAND_ATTR_CONCEAL_ON: currentAttributes.conceal = true; break;
+ case COMMAND_ATTR_CONCEAL_OFF: currentAttributes.conceal = false; break;
+
+ case COMMAND_ATTR_FRAMED_ON: currentAttributes.framed = true; break;
+ case COMMAND_ATTR_FRAMED_OFF: currentAttributes.framed = false; break;
+
+ case COMMAND_COLOR_FOREGROUND_RESET: currentAttributes.currentFgColor = null; break;
+ case COMMAND_COLOR_BACKGROUND_RESET: currentAttributes.currentBgColor = null; break;
+
+ case COMMAND_HICOLOR_FOREGROUND:
+ case COMMAND_HICOLOR_BACKGROUND: // {esc}[48;5;{color}m
+ int color = -1;
+ int nMustBe2or5 = iter.hasNext() ? iter.next() : -1;
+ if (nMustBe2or5 == 5) { // 256 colors
+ color = iter.hasNext() ? iter.next() : -1;
+ if (!AnsiConsoleColorPalette.isValidIndex(color))
+ color = -1;
+ } else if (nMustBe2or5 == 2) { // rgb colors
+ int r = iter.hasNext() ? iter.next() : -1;
+ int g = iter.hasNext() ? iter.next() : -1;
+ int b = iter.hasNext() ? iter.next() : -1;
+ color = AnsiConsoleColorPalette.hackRgb(r, g, b);
+ }
+ if (color != -1) {
+ if (nCmd == COMMAND_HICOLOR_FOREGROUND)
+ currentAttributes.currentFgColor = color;
+ else
+ currentAttributes.currentBgColor = color;
+ }
+ break;
+
+ case -1: break; // do nothing
+
+ default:
+ if (nCmd >= COMMAND_COLOR_FOREGROUND_FIRST && nCmd <= COMMAND_COLOR_FOREGROUND_LAST) // text color
+ currentAttributes.currentFgColor = nCmd - COMMAND_COLOR_FOREGROUND_FIRST;
+ else if (nCmd >= COMMAND_COLOR_BACKGROUND_FIRST && nCmd <= COMMAND_COLOR_BACKGROUND_LAST) // background color
+ currentAttributes.currentBgColor = nCmd - COMMAND_COLOR_BACKGROUND_FIRST;
+ else if (nCmd >= COMMAND_HICOLOR_FOREGROUND_FIRST && nCmd <= COMMAND_HICOLOR_FOREGROUND_LAST) // text color
+ currentAttributes.currentFgColor = nCmd - COMMAND_HICOLOR_FOREGROUND_FIRST + COMMAND_COLOR_INTENSITY_DELTA;
+ else if (nCmd >= COMMAND_HICOLOR_BACKGROUND_FIRST && nCmd <= COMMAND_HICOLOR_BACKGROUND_LAST) // background color
+ currentAttributes.currentBgColor = nCmd - COMMAND_HICOLOR_BACKGROUND_FIRST + COMMAND_COLOR_INTENSITY_DELTA;
+ }
+ }
+
+ return result;
+ }
+
+ private void addRange(List ranges, int start, int length, Color foreground, boolean isCode) {
+ StyleRange range = new StyleRange(start, length, foreground, null);
+ AnsiConsoleAttributes.updateRangeStyle(range, lastAttributes);
+ if (isCode) {
+ range.metrics = new GlyphMetrics(0, 0, 0);
+ }
+ ranges.add(range);
+ lastRangeEnd = lastRangeEnd + range.length;
+ }
+
+ @Override
+ public void lineGetStyle(LineStyleEvent event) {
+ if (event == null || event.lineText == null || event.lineText.length() == 0)
+ return;
+
+ String currentText = event.lineText;
+ Matcher matcher = pattern.matcher(currentText);
+
+ // Return directly if the pattern is not found.
+ if (!matcher.find()) {
+ return;
+ }
+
+ StyleRange defStyle;
+
+ if (event.styles != null && event.styles.length > 0) {
+ defStyle = (StyleRange) event.styles[0].clone();
+ if (defStyle.background == null)
+ defStyle.background = AnsiConsolePreferenceUtils.getDebugConsoleBgColor();
+ } else {
+ defStyle = new StyleRange(1, lastRangeEnd,
+ new Color(null, AnsiConsoleColorPalette.getColor(0)),
+ new Color(null, AnsiConsoleColorPalette.getColor(15)),
+ SWT.NORMAL);
+ }
+
+ lastRangeEnd = 0;
+ List ranges = new ArrayList();
+
+ do {
+ int start = matcher.start();
+ int end = matcher.end();
+
+ String theEscape = currentText.substring(start + 2, end - 1);
+ char code = currentText.charAt(end - 1);
+ if (code == ESCAPE_SGR) {
+ // Select Graphic Rendition (SGR) escape sequence
+ List nCommands = new ArrayList();
+ for (String cmd : theEscape.split(";")) {
+ int nCmd = AnsiConsolePreferenceUtils.tryParseInteger(cmd);
+ if (nCmd != -1)
+ nCommands.add(nCmd);
+ }
+ if (nCommands.isEmpty())
+ nCommands.add(0);
+ interpretCommand(nCommands);
+ }
+
+ if (lastRangeEnd != start)
+ addRange(ranges, event.lineOffset + lastRangeEnd, start - lastRangeEnd, defStyle.foreground, false);
+ lastAttributes = currentAttributes.clone();
+
+ addRange(ranges, event.lineOffset + start, end - start, defStyle.foreground, true);
+ } while (matcher.find());
+
+ if (lastRangeEnd != currentText.length())
+ addRange(ranges, event.lineOffset + lastRangeEnd, currentText.length() - lastRangeEnd, defStyle.foreground, false);
+ lastAttributes = currentAttributes.clone();
+
+ if (!ranges.isEmpty())
+ event.styles = ranges.toArray(new StyleRange[ranges.size()]);
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/diagnostic/utils/PlatformEnvironmentDataCollector.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/diagnostic/utils/PlatformEnvironmentDataCollector.java
index 7bf17f5c..44edc600 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/diagnostic/utils/PlatformEnvironmentDataCollector.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/diagnostic/utils/PlatformEnvironmentDataCollector.java
@@ -30,7 +30,7 @@ final public class PlatformEnvironmentDataCollector {
private static final PlatformDataModel DATA_TEST = getPlatformDataModel(Constants.AWS_TOOLKIT_FOR_ECLIPSE_PRODUCT_NAME_TEST);
public static PlatformDataModel getData() {
- return AwsToolkitCore.DEBUG_MODE ? DATA_TEST : DATA;
+ return AwsToolkitCore.getDefault().isDebugMode() ? DATA_TEST : DATA;
}
private static PlatformDataModel getPlatformDataModel(final String productName) {
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/mobileanalytics/AwsToolkitMetricType.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/mobileanalytics/AwsToolkitMetricType.java
index ac34614b..9f4c176d 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/mobileanalytics/AwsToolkitMetricType.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/mobileanalytics/AwsToolkitMetricType.java
@@ -90,6 +90,9 @@ public enum AwsToolkitMetricType {
LAMBDA_UPLOAD_FUNCTION_WIZARD("Lambda-UploadFunctionWizard"),
LAMBDA_INVOKE_FUNCTION_DIALOG("Lambda-InvokeFunctionDialog"),
LAMBDA_DEPLOY_SERVERLESS_PROJECT_WIZARD("Lambda-DeployServerlessProjectWizard"),
+ /* SAM Local Events */
+ SAMLOCAL_GENERATE_EVENT("SamLocal-GenerateEvent"),
+ SAMLOCAL_LAUNCH("SamLocal-Launch"),
;
private final String metricName;
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/model/RegionDataModel.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/model/RegionDataModel.java
index bdf37af8..0e7758b8 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/model/RegionDataModel.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/model/RegionDataModel.java
@@ -32,6 +32,11 @@ public void setRegion(Region region) {
this.region = region;
}
+ @JsonProperty
+ public String getRegionId() {
+ return region.getId();
+ }
+
@JsonProperty
public String getRegionName() {
return region.getName();
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/plugin/AbstractAwsPlugin.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/plugin/AbstractAwsPlugin.java
index 0a634d41..fb3e08ab 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/plugin/AbstractAwsPlugin.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/plugin/AbstractAwsPlugin.java
@@ -88,4 +88,8 @@ protected ImageRegistry createImageRegistry() {
protected Map getImageRegistryMap() {
return Collections.emptyMap();
}
+
+ protected String getBundleVersion() {
+ return getBundle().getVersion().toString();
+ }
}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionUtils.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionUtils.java
index 0fe1dc71..cc2442b0 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionUtils.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/regions/RegionUtils.java
@@ -47,19 +47,28 @@
import com.amazonaws.services.s3.model.ObjectMetadata;
/**
- * Utilities for working with regions.
+ * Utilities for loading and working with regions. The AWS regions loading priorities are:
+ *
+ * {@link #P_REGIONS_FILE_OVERRIDE} // Use the regions file from the provided system property - used for accessing private regions
+ * > {@link #P_USE_LOCAL_REGION_FILE} // Use the embedded regions file /etc/regions.xml explicitly - used for testing purpose
+ * > {@link #LOCAL_REGION_FILE_OVERRIDE} // Use the embedded regions file /etc/override.xml if exists - used for accessing private partitions
+ * > {@link #CLOUDFRONT_DISTRO} // Use the remote shared ServiceEndPoints.xml file - used in most cases for accessing public regions
+ * > /etc/regions.xml // Use the local embedded file if failed to download the remote file - fall back to the embedded version which could be outdated
*/
public class RegionUtils {
public static final String S3_US_EAST_1_REGIONAL_ENDPOINT = "https://s3-external-1.amazonaws.com";
private static final String CLOUDFRONT_DISTRO = "http://vstoolkit.amazonwebservices.com/";
- private static final String REGIONS_FILE_OVERRIDE = RegionUtils.class.getName() + ".fileOverride";
- public static final String USE_LOCAL_REGION_FILE = RegionUtils.class.getName() + ".useLocalRegionFile";
-
private static final String REGIONS_METADATA_S3_BUCKET = "aws-vs-toolkit";
private static final String REGIONS_METADATA_S3_OBJECT = "ServiceEndPoints.xml";
+ // System property name whose value is the path of the overriding file.
+ private static final String P_REGIONS_FILE_OVERRIDE = RegionUtils.class.getName() + ".fileOverride";
+ // System property name whose value is a boolean whether to use the embedded region file.
+ private static final String P_USE_LOCAL_REGION_FILE = RegionUtils.class.getName() + ".useLocalRegionFile";
+ // This file overrides the remote ServiceEndPoints.xml file if exists.
+ private static final String LOCAL_REGION_FILE_OVERRIDE = "/etc/regions-override.xml";
private static final String LOCAL_REGION_FILE = "/etc/regions.xml";
private static List regions;
@@ -82,7 +91,7 @@ public static boolean isServiceSupportedInCurrentRegion(String serviceAbbreviati
* Returns a list of the available AWS regions.
*/
public synchronized static List getRegions() {
- if ( regions == null ) {
+ if (regions == null) {
init();
}
@@ -120,8 +129,8 @@ public synchronized static void addLocalService(
*/
public synchronized static List getRegionsForService(String serviceAbbreviation) {
List regions = new LinkedList<>();
- for ( Region r : getRegions() ) {
- if ( r.isServiceSupported(serviceAbbreviation) ) {
+ for (Region r : getRegions()) {
+ if (r.isServiceSupported(serviceAbbreviation)) {
regions.add(r);
}
}
@@ -132,8 +141,8 @@ public synchronized static List getRegionsForService(String serviceAbbre
* Returns the region with the id given, if it exists. Otherwise, returns null.
*/
public static Region getRegion(String regionId) {
- for ( Region r : getRegions() ) {
- if ( r.getId().equals(regionId) ) {
+ for (Region r : getRegions()) {
+ if (r.getId().equals(regionId)) {
return r;
}
}
@@ -199,21 +208,21 @@ public static Region getRegionByEndpoint(String endpoint) {
URL targetEndpointUrl = null;
try {
targetEndpointUrl = new URL(endpoint);
- } catch ( MalformedURLException e ) {
+ } catch (MalformedURLException e) {
throw new RuntimeException(
"Unable to parse service endpoint: " + e.getMessage());
}
String targetHost = targetEndpointUrl.getHost();
- for ( Region region : getRegions() ) {
- for ( String serviceEndpoint
- : region.getServiceEndpoints().values() ) {
+ for (Region region : getRegions()) {
+ for (String serviceEndpoint
+ : region.getServiceEndpoints().values()) {
try {
URL serviceEndpointUrl = new URL(serviceEndpoint);
- if ( serviceEndpointUrl.getHost().equals(targetHost) ) {
+ if (serviceEndpointUrl.getHost().equals(targetHost)) {
return region;
}
- } catch ( MalformedURLException e ) {
+ } catch (MalformedURLException e) {
AwsToolkitCore.getDefault().reportException("Unable to parse service endpoint: " + serviceEndpoint, e);
}
}
@@ -230,20 +239,18 @@ public static Region getRegionByEndpoint(String endpoint) {
* initializes the static list of regions with it.
*/
public static synchronized void init() {
-
- if (System.getProperty(REGIONS_FILE_OVERRIDE) != null) {
+ // Use overriding file for testing unlaunched services.
+ if (System.getProperty(P_REGIONS_FILE_OVERRIDE) != null) {
loadRegionsFromOverrideFile();
- } else if (!Boolean.valueOf(System.getProperty(USE_LOCAL_REGION_FILE))) {
- IPath stateLocation = Platform.getStateLocation(AwsToolkitCore
- .getDefault().getBundle());
- File regionsDir = new File(stateLocation.toFile(), "regions");
- File regionsFile = new File(regionsDir, "regions.xml");
-
- cacheRegionsFile(regionsFile);
- initCachedRegions(regionsFile);
+ // Use the local region override file
+ } else if (localRegionOverrideFileExists()) {
+ initBundledRegionsOverride();
+ // Use the remote ServiceEndpoints.xml file
+ } else if (!Boolean.valueOf(System.getProperty(P_USE_LOCAL_REGION_FILE))) {
+ initRegionsFromS3();
}
// Fall back onto the version we ship with the toolkit
- if ( regions == null ) {
+ if (regions == null) {
initBundledRegions();
}
@@ -260,21 +267,33 @@ private static void loadRegionsFromOverrideFile() {
try {
System.setProperty("com.amazonaws.sdk.disableCertChecking", "true");
File regionsFile =
- new File(System.getProperty(REGIONS_FILE_OVERRIDE));
- InputStream override = new FileInputStream(regionsFile);
- regions = parseRegionMetadata(override);
+ new File(System.getProperty(P_REGIONS_FILE_OVERRIDE));
+ try (InputStream override = new FileInputStream(regionsFile)) {
+ regions = parseRegionMetadata(override);
+ }
+
try {
cacheFlags(regionsFile.getParentFile());
- } catch ( Exception e ) {
+ } catch (Exception e) {
AwsToolkitCore.getDefault().logError(
"Couldn't cache flag icons", e);
}
- } catch ( Exception e ) {
+ } catch (Exception e) {
AwsToolkitCore.getDefault().logError(
"Couldn't load regions override", e);
}
}
+ private static void initRegionsFromS3() {
+ IPath stateLocation = Platform.getStateLocation(AwsToolkitCore
+ .getDefault().getBundle());
+ File regionsDir = new File(stateLocation.toFile(), "regions");
+ File regionsFile = new File(regionsDir, "regions.xml");
+
+ cacheRegionsFile(regionsFile);
+ initCachedRegions(regionsFile);
+ }
+
/**
* Caches the regions file stored in cloudfront to the destination file
* given. Tries S3 if cloudfront is unavailable.
@@ -283,7 +302,7 @@ private static void loadRegionsFromOverrideFile() {
*/
private static void cacheRegionsFile(File regionsFile) {
Date regionsFileLastModified = new Date(0);
- if ( !regionsFile.exists() ) {
+ if (!regionsFile.exists()) {
regionsFile.getParentFile().mkdirs();
} else {
regionsFileLastModified = new Date(regionsFile.lastModified());
@@ -294,11 +313,11 @@ private static void cacheRegionsFile(File regionsFile) {
AWSClientFactory.getAnonymousS3Client();
ObjectMetadata objectMetadata =
s3.getObjectMetadata(REGIONS_METADATA_S3_BUCKET, REGIONS_METADATA_S3_OBJECT);
- if ( objectMetadata.getLastModified()
- .after(regionsFileLastModified) ) {
+ if (objectMetadata.getLastModified()
+ .after(regionsFileLastModified)) {
cacheRegionsFile(regionsFile, s3);
}
- } catch ( Exception e ) {
+ } catch (Exception e) {
AwsToolkitCore.getDefault().logError(
"Failed to cache regions file", e);
}
@@ -310,16 +329,15 @@ private static void cacheRegionsFile(File regionsFile) {
* on the next startup.
*/
private static void initCachedRegions(File regionsFile) {
- try {
- InputStream inputStream = new FileInputStream(regionsFile);
+ try (InputStream inputStream = new FileInputStream(regionsFile)) {
regions = parseRegionMetadata(inputStream);
try {
cacheFlags(regionsFile.getParentFile());
- } catch ( Exception e ) {
+ } catch (Exception e) {
AwsToolkitCore.getDefault().logError(
"Couldn't cache flag icons", e);
}
- } catch ( Exception e ) {
+ } catch (Exception e) {
AwsToolkitCore.getDefault().logError(
"Couldn't read regions file", e);
// Clear out the regions file so that it will get cached again at
@@ -333,11 +351,28 @@ private static void initCachedRegions(File regionsFile) {
* the plugin, in case it cannot be fetched from the remote source.
*/
private static void initBundledRegions() {
- ClassLoader classLoader = RegionUtils.class.getClassLoader();
- InputStream inputStream =
- classLoader.getResourceAsStream(LOCAL_REGION_FILE);
- regions = parseRegionMetadata(inputStream);
- for ( Region r : regions ) {
+ try {
+ regions = loadRegionsFromLocalRegionFile();
+ registerRegionFlagsFromBundle(regions);
+ } catch (IOException e) {
+ // Do nothing, the regions remains null in this case.
+ }
+ }
+
+ private static void initBundledRegionsOverride() {
+ try {
+ regions = loadRegionsFromLocalRegionOverrideFile();
+ registerRegionFlagsFromBundle(regions);
+ } catch (IOException e) {
+ // Do nothing, the regions remains null in this case.
+ }
+ }
+
+ private static final void registerRegionFlagsFromBundle(List regions) {
+ if (regions == null) {
+ return;
+ }
+ for (Region r : regions) {
if (r == LocalRegion.INSTANCE) {
// No flag to load for the local region.
continue;
@@ -360,6 +395,9 @@ private static void initBundledRegions() {
* special "local" region.
*/
private static List parseRegionMetadata(InputStream inputStream) {
+ if (inputStream == null) {
+ return null;
+ }
List list = PARSER.parseRegionMetadata(inputStream);
list.add(LocalRegion.INSTANCE);
replaceS3GlobalEndpointWithRegional(list);
@@ -396,10 +434,10 @@ private static void cacheRegionsFile(File regionsFile, AmazonS3 s3) {
*/
private static void truncateFile(File file)
throws FileNotFoundException, IOException {
- if ( file.exists() ) {
- RandomAccessFile raf = new RandomAccessFile(file, "rw");
- raf.getChannel().truncate(0);
- raf.close();
+ if (file.exists()) {
+ try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) {
+ raf.getChannel().truncate(0);
+ }
}
}
@@ -408,18 +446,18 @@ private static void truncateFile(File file)
*/
private static void cacheFlags(File regionsDir)
throws ClientProtocolException, IOException {
- if ( !regionsDir.exists() ) {
+ if (!regionsDir.exists()) {
return;
}
- for ( Region r : regions ) {
+ for (Region r : regions) {
if (r == LocalRegion.INSTANCE) {
// Local region has no flag to initialize.
continue;
}
File icon = new File(regionsDir, r.getFlagIconPath());
- if ( icon.exists() == false ) {
+ if (icon.exists() == false) {
icon.getParentFile().mkdirs();
String iconUrl = CLOUDFRONT_DISTRO + r.getFlagIconPath();
fetchFile(iconUrl, icon);
@@ -446,18 +484,13 @@ private static void fetchFile(String url, File destinationFile)
HttpGet httpget = new HttpGet(url);
HttpResponse response = httpclient.execute(httpget);
HttpEntity entity = response.getEntity();
- if ( entity != null ) {
- InputStream instream = entity.getContent();
- FileOutputStream output = new FileOutputStream(destinationFile);
- try {
+ if (entity != null) {
+ try (InputStream instream = entity.getContent(); FileOutputStream output = new FileOutputStream(destinationFile)) {
int l;
byte[] tmp = new byte[2048];
- while ( (l = instream.read(tmp)) != -1 ) {
+ while ((l = instream.read(tmp)) != -1) {
output.write(tmp, 0, l);
}
- } finally {
- output.close();
- instream.close();
}
}
}
@@ -465,25 +498,36 @@ private static void fetchFile(String url, File destinationFile)
/**
* Load regions from remote S3 bucket.
*/
- public static List loadRegionsFromS3() {
+ public static List loadRegionsFromS3() throws IOException {
AmazonS3 s3 = AWSClientFactory.getAnonymousS3Client();
- InputStream inputStream =
+ try (InputStream inputStream =
s3.getObject(REGIONS_METADATA_S3_BUCKET, REGIONS_METADATA_S3_OBJECT)
- .getObjectContent();
- return parseRegionMetadata(inputStream);
+ .getObjectContent()) {
+ return parseRegionMetadata(inputStream);
+ }
}
/**
* Load regions from local file.
*/
- public static List loadRegionsFromLocalFile() {
+ private static List loadRegionsFromLocalFile(String localFileName) throws IOException {
ClassLoader classLoader = RegionUtils.class.getClassLoader();
- InputStream inputStream =
- classLoader.getResourceAsStream(LOCAL_REGION_FILE);
- return parseRegionMetadata(inputStream);
+ try (InputStream inputStream =
+ classLoader.getResourceAsStream(localFileName)) {
+ return parseRegionMetadata(inputStream);
+ }
+ }
+
+ private static boolean localRegionOverrideFileExists() {
+ ClassLoader classLoader = RegionUtils.class.getClassLoader();
+ return classLoader.getResource(LOCAL_REGION_FILE_OVERRIDE) != null;
+ }
+
+ private static List loadRegionsFromLocalRegionOverrideFile() throws IOException {
+ return loadRegionsFromLocalFile(LOCAL_REGION_FILE_OVERRIDE);
}
- public static void useBuiltInRegionFile() {
- System.setProperty(USE_LOCAL_REGION_FILE, "true");
+ public static List loadRegionsFromLocalRegionFile() throws IOException {
+ return loadRegionsFromLocalFile(LOCAL_REGION_FILE);
}
}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/AccountSelectionComposite.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/AccountSelectionComposite.java
index 0ce2ab8f..26d8eaf6 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/AccountSelectionComposite.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/AccountSelectionComposite.java
@@ -63,6 +63,7 @@ public void addSelectionListener(SelectionListener listner) {
public AccountSelectionComposite(final Composite parent, final int style) {
super(parent, style);
+ setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
setLayout(new GridLayout(3, false));
createChildWidgets();
@@ -90,7 +91,7 @@ protected void createNoAccountLabel() {
protected void createAccountConfigurationLink() {
Link link = new Link(this, SWT.NONE);
link.setFont(this.getFont());
- link.setText("" + "Configure AWS accounts..." + ""); //$NON-NLS-1$ //$NON-NLS-2$
+ link.setText("" + "Configure AWS profiles..." + "");
link.addSelectionListener(new SelectionListener() {
@@ -121,7 +122,7 @@ public void widgetDefaultSelected(final SelectionEvent e) {
protected void createAccountSelectionCombo() {
Label selectAccount = new Label(this, SWT.None);
- selectAccount.setText("Select Account:"); //$NON-NLS-1$
+ selectAccount.setText("Select profile:");
this.accountSelection = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY);
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/ImportFileComposite.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/ImportFileComposite.java
index 9dbfec99..9f67306f 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/ImportFileComposite.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/ImportFileComposite.java
@@ -18,9 +18,22 @@
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newPushButton;
import org.eclipse.core.databinding.DataBindingContext;
-import org.eclipse.core.databinding.beans.PojoObservables;
+import org.eclipse.core.databinding.beans.PojoProperties;
import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.variables.VariablesPlugin;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.ui.JavaElementLabelProvider;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
@@ -28,26 +41,31 @@
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.ui.dialogs.ContainerSelectionDialog;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
+import org.eclipse.ui.model.BaseWorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import com.amazonaws.eclipse.core.AwsToolkitCore;
import com.amazonaws.eclipse.core.model.ImportFileDataModel;
+import com.amazonaws.eclipse.core.validator.NoopValidator;
import com.amazonaws.eclipse.core.widget.TextComplex;
/**
* A reusable File import widget composite.
*/
public class ImportFileComposite extends Composite {
-
private TextComplex filePathComplex;
private Button browseButton;
- private final IValidator filePathValidator;
- public ImportFileComposite(Composite parent, DataBindingContext context,
- ImportFileDataModel dataModel, IValidator validator) {
+ private ImportFileComposite(Composite parent, DataBindingContext context,
+ ImportFileDataModel dataModel, IValidator validator, String textLabel,
+ String buttonLabel, ModifyListener modifyListener, String textMessage) {
super(parent, SWT.NONE);
- filePathValidator = validator;
- setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
setLayout(new GridLayout(3, false));
- createControl(context, dataModel);
+ createControl(context, dataModel, validator, textLabel, buttonLabel, modifyListener, textMessage);
}
@Override
@@ -56,25 +74,201 @@ public void setEnabled(boolean enabled) {
browseButton.setEnabled(enabled);
}
- private void createControl(DataBindingContext context, ImportFileDataModel dataModel) {
- filePathComplex = TextComplex.builder()
- .composite(this)
- .dataBindingContext(context)
- .pojoObservableValue(PojoObservables.observeValue(dataModel, P_FILE_PATH))
- .labelValue("Import:")
+ @NonNull
+ public static ImportFileCompositeBuilder builder(
+ @NonNull Composite parent,
+ @NonNull DataBindingContext dataBindingContext,
+ @NonNull ImportFileDataModel dataModel) {
+ return new ImportFileCompositeBuilder(parent, dataBindingContext, dataModel);
+ }
+
+ private void createControl(DataBindingContext context, ImportFileDataModel dataModel,
+ IValidator filePathValidator, String textLabel, String buttonLabel,
+ ModifyListener modifyListener, String textMessage) {
+
+ filePathComplex = TextComplex.builder(this, context, PojoProperties.value(P_FILE_PATH).observe(dataModel))
+ .labelValue(textLabel)
.addValidator(filePathValidator)
.defaultValue(dataModel.getFilePath())
+ .modifyListener(modifyListener)
+ .textMessage(textMessage)
.build();
- browseButton = newPushButton(this, "Browse");
+ browseButton = newPushButton(this, buttonLabel);
+ }
+
+ public void setFilePath(String filePath) {
+ filePathComplex.setText(filePath);
+ }
+
+ /**
+ * The browse Button opens a general file selection dialog.
+ */
+ private void setButtonListenerToBrowseFile() {
browseButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
FileDialog dialog = new FileDialog(getShell(), SWT.SINGLE);
String path = dialog.open();
- if (path != null) filePathComplex.setText(path);
+ if (path != null) {
+ filePathComplex.setText(path);
+ }
+ }
+ });
+ }
+
+ /**
+ * The browse Button opens a dialog to only allow to select folders under the workspace.
+ */
+ private void setButtonListenerToBrowseWorkspaceDir() {
+ browseButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ContainerSelectionDialog dialog = new ContainerSelectionDialog(getShell(),
+ ResourcesPlugin.getWorkspace().getRoot(), false, "Choose Base Directory");
+ dialog.showClosedProjects(false);
+
+ int buttonId = dialog.open();
+ if (buttonId == IDialogConstants.OK_ID) {
+ Object[] resource = dialog.getResult();
+ if (resource != null && resource.length > 0) {
+ String fileLoc = VariablesPlugin.getDefault().getStringVariableManager()
+ .generateVariableExpression("workspace_loc", ((IPath) resource[0]).toString());
+ filePathComplex.setText(fileLoc);
+ }
+ }
}
});
}
+ /**
+ * The browse Button opens a dialog to only allow to select files under the workspace.
+ */
+ private void setButtonListenerToBrowseWorkspaceFile() {
+ browseButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(),
+ new WorkbenchLabelProvider(), new BaseWorkbenchContentProvider());
+ dialog.setTitle("Choose File");
+ dialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
+
+ int buttonId = dialog.open();
+ if (buttonId == IDialogConstants.OK_ID) {
+ Object[] resource = dialog.getResult();
+ if (resource != null && resource.length > 0) {
+ String fileLoc = VariablesPlugin.getDefault().getStringVariableManager()
+ .generateVariableExpression("workspace_loc",
+ ((IResource) resource[0]).getFullPath().toString());
+ filePathComplex.setText(fileLoc);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * The browse Button opens a dialog to only allow to select project under the workspace.
+ */
+ private void setButtonListenerToBrowseWorkspaceProject() {
+ browseButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ ILabelProvider labelProvider= new JavaElementLabelProvider(JavaElementLabelProvider.SHOW_DEFAULT);
+ ElementListSelectionDialog dialog= new ElementListSelectionDialog(getShell(), labelProvider);
+ dialog.setTitle("Choose project");
+ dialog.setMessage("Choose the project for the job");
+ try {
+ dialog.setElements(JavaCore.create(ResourcesPlugin.getWorkspace().getRoot()).getJavaProjects());
+ } catch (JavaModelException jme) {
+ AwsToolkitCore.getDefault().logError(jme.getMessage(), jme);
+ }
+ if (dialog.open() == Window.OK) {
+ filePathComplex.setText(((IJavaProject) dialog.getFirstResult()).getElementName());
+ }
+ }
+ });
+ }
+
+ public static final class ImportFileCompositeBuilder {
+ private final Composite parent;
+ private final DataBindingContext context;
+ private final ImportFileDataModel dataModel;
+
+ // Optional parameters
+ private IValidator filePathValidator = new NoopValidator();
+ private ModifyListener modifyListener;
+ private String textLabel = "Import:";
+ private String buttonLabel = "Browse";
+ private String textMessage = "";
+
+ private ImportFileCompositeBuilder(
+ @NonNull Composite parent,
+ @NonNull DataBindingContext context,
+ @NonNull ImportFileDataModel dataModel) {
+ this.parent = parent;
+ this.context = context;
+ this.dataModel = dataModel;
+ }
+
+ public ImportFileCompositeBuilder filePathValidator(IValidator filePathValidator) {
+ this.filePathValidator = filePathValidator;
+ return this;
+ }
+
+ public ImportFileCompositeBuilder textLabel(String textLabel) {
+ this.textLabel = textLabel;
+ return this;
+ }
+
+ public ImportFileCompositeBuilder buttonLabel(String buttonLabel) {
+ this.buttonLabel = buttonLabel;
+ return this;
+ }
+
+ public ImportFileCompositeBuilder modifyListener(ModifyListener modifyListener) {
+ this.modifyListener = modifyListener;
+ return this;
+ }
+
+ public ImportFileCompositeBuilder textMessage(String textMessage) {
+ this.textMessage = textMessage;
+ return this;
+ }
+
+ /**
+ * Build a general file importer component.
+ */
+ public ImportFileComposite build() {
+ ImportFileComposite composite = new ImportFileComposite(
+ parent, context, dataModel,
+ filePathValidator, textLabel, buttonLabel, modifyListener, textMessage);
+ composite.setButtonListenerToBrowseFile();
+ return composite;
+ }
+
+ public ImportFileComposite buildWorkspaceDirBrowser() {
+ ImportFileComposite composite = new ImportFileComposite(
+ parent, context, dataModel,
+ filePathValidator, textLabel, buttonLabel, modifyListener, textMessage);
+ composite.setButtonListenerToBrowseWorkspaceDir();
+ return composite;
+ }
+
+ public ImportFileComposite buildWorkspaceFileBrowser() {
+ ImportFileComposite composite = new ImportFileComposite(
+ parent, context, dataModel,
+ filePathValidator, textLabel, buttonLabel, modifyListener, textMessage);
+ composite.setButtonListenerToBrowseWorkspaceFile();
+ return composite;
+ }
+
+ public ImportFileComposite buildWorkspaceProjectBrowser() {
+ ImportFileComposite composite = new ImportFileComposite(
+ parent, context, dataModel,
+ filePathValidator, textLabel, buttonLabel, modifyListener, textMessage);
+ composite.setButtonListenerToBrowseWorkspaceProject();
+ return composite;
+ }
+ }
}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/KeyValueSetEditingComposite.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/KeyValueSetEditingComposite.java
index 1bc8cbb5..249457c3 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/KeyValueSetEditingComposite.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/KeyValueSetEditingComposite.java
@@ -426,23 +426,17 @@ protected Control createDialogArea(Composite parent) {
GridLayout layout = new GridLayout(2, false);
container.setLayout(layout);
- keyText = TextComplex.builder()
- .composite(container)
- .dataBindingContext(dataBindingContext)
+ keyText = TextComplex.builder(container, dataBindingContext, PojoObservables.observeValue(pairModel, Pair.P_KEY))
.labelValue(keyLabel)
.defaultValue(pairModel.getKey())
- .pojoObservableValue(PojoObservables.observeValue(pairModel, Pair.P_KEY))
.addValidator(new NotEmptyValidator("The key name cannot be empty."))
.addValidator(new KeyNotDuplicateValidator(pairSet, pairModel, "This field must not contain duplicate items."))
.addValidators(keyValidators)
.build();
- valueText = TextComplex.builder()
- .composite(container)
- .dataBindingContext(dataBindingContext)
+ valueText = TextComplex.builder(container, dataBindingContext, PojoObservables.observeValue(pairModel, Pair.P_VALUE))
.labelValue(valueLabel)
.defaultValue(pairModel.getValue())
- .pojoObservableValue(PojoObservables.observeValue(pairModel, Pair.P_VALUE))
.addValidators(valueValidators)
.build();
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/MavenConfigurationComposite.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/MavenConfigurationComposite.java
index bd3881d8..f0c13f33 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/MavenConfigurationComposite.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/MavenConfigurationComposite.java
@@ -14,10 +14,14 @@
*/
package com.amazonaws.eclipse.core.ui;
+import static com.amazonaws.eclipse.core.model.MavenConfigurationDataModel.P_ARTIFACT_ID;
+import static com.amazonaws.eclipse.core.model.MavenConfigurationDataModel.P_GROUP_ID;
+import static com.amazonaws.eclipse.core.model.MavenConfigurationDataModel.P_PACKAGE_NAME;
+import static com.amazonaws.eclipse.core.model.MavenConfigurationDataModel.P_VERSION;
+
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
@@ -45,43 +49,31 @@ public MavenConfigurationComposite(Composite parent, DataBindingContext context,
}
private void createControl(DataBindingContext context, MavenConfigurationDataModel dataModel) {
- groupIdComplex = TextComplex.builder()
- .composite(this)
- .dataBindingContext(context)
- .pojoObservableValue(PojoObservables.observeValue(dataModel, MavenConfigurationDataModel.P_GROUP_ID))
+ groupIdComplex = TextComplex.builder(this, context, PojoObservables.observeValue(dataModel, P_GROUP_ID))
.addValidator(new NotEmptyValidator("Group ID must be provided!"))
- .modifyListener((e) -> {
+ .modifyListener(e -> {
onMavenConfigurationChange();
})
.labelValue("Group ID:")
.defaultValue(dataModel.getGroupId())
.build();
- artifactIdComplex = TextComplex.builder()
- .composite(this)
- .dataBindingContext(context)
- .pojoObservableValue(PojoObservables.observeValue(dataModel, MavenConfigurationDataModel.P_ARTIFACT_ID))
+ artifactIdComplex = TextComplex.builder(this, context, PojoObservables.observeValue(dataModel, P_ARTIFACT_ID))
.addValidator(new NotEmptyValidator("Artifact ID must be provided!"))
- .modifyListener((e) -> {
+ .modifyListener(e -> {
onMavenConfigurationChange();
})
.labelValue("Artifact ID:")
.defaultValue(dataModel.getArtifactId())
.build();
- versionComplex = TextComplex.builder()
- .composite(this)
- .dataBindingContext(context)
- .pojoObservableValue(PojoObservables.observeValue(dataModel, MavenConfigurationDataModel.P_VERSION))
+ versionComplex = TextComplex.builder(this, context, PojoObservables.observeValue(dataModel, P_VERSION))
.addValidator(new NotEmptyValidator("Version must be provided!"))
.labelValue("Version:")
.defaultValue(dataModel.getVersion())
.build();
- packageComplex = TextComplex.builder()
- .composite(this)
- .dataBindingContext(context)
- .pojoObservableValue(PojoObservables.observeValue(dataModel, MavenConfigurationDataModel.P_PACKAGE_NAME))
+ packageComplex = TextComplex.builder(this, context, PojoObservables.observeValue(dataModel, P_PACKAGE_NAME))
.addValidator(new PackageNameValidator("Package name must be provided!"))
.labelValue("Package name:")
.defaultValue(dataModel.getPackageName())
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/ProjectNameComposite.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/ProjectNameComposite.java
index 415a0aec..ea461ccf 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/ProjectNameComposite.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/ProjectNameComposite.java
@@ -14,6 +14,8 @@
*/
package com.amazonaws.eclipse.core.ui;
+import static com.amazonaws.eclipse.core.model.ProjectNameDataModel.P_PROJECT_NAME;
+
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.swt.SWT;
@@ -40,10 +42,7 @@ public ProjectNameComposite(Composite parent, DataBindingContext context, Projec
}
private void createControl(DataBindingContext context, ProjectNameDataModel dataModel) {
- projectNameComplex = TextComplex.builder()
- .composite(this)
- .dataBindingContext(context)
- .pojoObservableValue(PojoObservables.observeValue(dataModel, ProjectNameDataModel.P_PROJECT_NAME))
+ projectNameComplex = TextComplex.builder(this, context, PojoObservables.observeValue(dataModel, P_PROJECT_NAME))
.addValidator(new ProjectNameValidator())
.defaultValue(dataModel.getProjectName())
.labelValue("Project name:")
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/RegionComposite.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/RegionComposite.java
index f202ccf9..71ecf99a 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/RegionComposite.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/RegionComposite.java
@@ -62,8 +62,8 @@ private RegionComposite(
this.serviceName = serviceName;
this.labelValue = labelValue;
this.listeners = listeners;
- this.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
- this.setLayout(new GridLayout(3, true));
+ this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ this.setLayout(new GridLayout(3, false));
createControl();
}
@@ -110,7 +110,7 @@ public static final class RegionCompositeBuilder {
private DataBindingContext bindingContext;
private RegionDataModel dataModel;
private String serviceName;
- private String labelValue = "Select Regions:";
+ private String labelValue = "Select region:";
private final List listeners = new ArrayList<>();
public RegionComposite build() {
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/SelectOrInputComposite.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/SelectOrInputComposite.java
index aceecbe6..7d1d17ea 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/SelectOrInputComposite.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/SelectOrInputComposite.java
@@ -184,12 +184,8 @@ public void widgetSelected(SelectionEvent e) {
})
.build();
- this.createText = TextComplex.builder()
- .composite(this)
+ this.createText = TextComplex.builder(this, bindingContext, PojoProperties.value(P_NEW_RESOURCE_NAME).observe(dataModel))
.createLabel(false)
- .dataBindingContext(bindingContext)
- .pojoObservableValue(PojoProperties.value(P_NEW_RESOURCE_NAME, String.class)
- .observe(dataModel))
.addValidators(createTextValidators)
.build();
}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/wizards/WizardWidgetFactory.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/wizards/WizardWidgetFactory.java
index 1aa11ee6..85abfd9b 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/wizards/WizardWidgetFactory.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/ui/wizards/WizardWidgetFactory.java
@@ -158,6 +158,7 @@ public static ComboViewer newComboViewer(Composite parent, int colspan) {
GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
gridData.horizontalSpan = colspan;
comboViewer.getCombo().setLayoutData(gridData);
+
return comboViewer;
}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/AbstractApplicationLauncher.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/AbstractApplicationLauncher.java
new file mode 100644
index 00000000..2ca48c38
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/AbstractApplicationLauncher.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.core.util;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.ui.DebugUITools;
+
+/**
+ * Super class for launching an application.
+ *
+ * @see MavenBuildLauncher
+ * @see RemoteDebugLauncher
+ */
+public abstract class AbstractApplicationLauncher {
+ protected long timeIntervalMilli = 5000L;
+ protected final String mode;
+ protected final IProgressMonitor monitor;
+
+ protected AbstractApplicationLauncher(String mode, IProgressMonitor monitor) {
+ this.mode = mode;
+ this.monitor = monitor;
+ }
+
+ /**
+ * Validate the required parameters for launching this application.
+ * @throws IllegalArgumentException - If the required parameters are not provided or invalid.
+ */
+ protected abstract void validateParameters() throws IllegalArgumentException;
+
+ /**
+ * Create a new {@link ILaunchConfiguration} for this application.
+ */
+ protected abstract ILaunchConfiguration createLaunchConfiguration() throws CoreException;
+
+ public final ILaunch launchAsync() throws CoreException {
+ validateParameters();
+ ILaunchConfiguration launchConfiguration = createLaunchConfiguration();
+ return DebugUITools.buildAndLaunch(launchConfiguration, mode, monitor);
+ }
+
+ /**
+ * Blocking call for launching the application. This call blocks until the application terminates.
+ */
+ public final ILaunch launch() throws CoreException {
+ ILaunch launch = launchAsync();
+ while (!launch.isTerminated()) {
+ try {
+ Thread.sleep(timeIntervalMilli);
+ } catch (InterruptedException e) {
+ }
+ }
+ return launch;
+ }
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/CliUtil.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/CliUtil.java
new file mode 100644
index 00000000..bd3a08e3
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/CliUtil.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.core.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility for calling a CLI command, and managing the input/output of the process.
+ */
+public class CliUtil {
+
+ public static CliProcessTracker executeCommand(List commandLine, Map envp, OutputStream stdOut, OutputStream stdErr)
+ throws IOException {
+ Process process = buildProcess(commandLine, envp);
+ PipeThread stdInputOutput = new PipeThread("CLI stdout", process.getInputStream(), stdOut);
+ PipeThread stdErrThread = new PipeThread("CLI stderr", process.getErrorStream(), stdErr);
+ stdInputOutput.start();
+ stdErrThread.start();
+ return new CliProcessTracker(process, stdInputOutput, stdErrThread);
+ }
+
+ /**
+ * Using {@link ProcessBuilder} to build a {@link Process}. {@link ProcessBuilder} aggregates a copy of the current process environment
+ * so that we don't have to manually copy these.
+ *
+ * @see {@link ProcessBuilder#environment()}
+ */
+ public static Process buildProcess(List commandLine, Map envp) throws IOException {
+ ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
+ processBuilder.environment().putAll(envp);
+ return processBuilder.start();
+ }
+
+ /**
+ * Tracks the process along with its two stream threads.
+ */
+ public static class CliProcessTracker {
+ private final Process process;
+ private final PipeThread stdOutThread;
+ private final PipeThread stdErrThread;
+
+ public CliProcessTracker(Process process, PipeThread stdOutThread, PipeThread stdErrThread) {
+ this.process = process;
+ this.stdOutThread = stdOutThread;
+ this.stdErrThread = stdErrThread;
+ }
+
+ public Process getProcess() {
+ return process;
+ }
+
+ /**
+ * Destroy the process and wait for the Stream threads to finish.
+ */
+ public void destroy() {
+ process.destroy();
+ waitForStream();
+ }
+
+ /**
+ * Wait for the Stream threads to finish. Ie. to drain the input streams, and return the exit code.
+ */
+ public int waitForStream() {
+ try {
+ stdOutThread.join();
+ stdErrThread.join();
+ } catch (InterruptedException e) {
+ }
+ return process.exitValue();
+ }
+ }
+
+ /**
+ * A thread that keeps reading from a target InputStream, and writes to an OutputStream.
+ */
+ private static class PipeThread extends Thread {
+ private final InputStream inputStream;
+ private final OutputStream outputStream;
+
+ private PipeThread(String name, InputStream inputStream, OutputStream outputStream) {
+ this.inputStream = inputStream;
+ this.outputStream = outputStream;
+ this.setName(name);
+ }
+
+ @Override
+ public void run() {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ outputStream.write(String.format("%s%n", line).getBytes(StandardCharsets.UTF_8));
+ }
+ } catch (IOException e) {}
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/MavenBuildLauncher.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/MavenBuildLauncher.java
new file mode 100644
index 00000000..55dd6eb5
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/MavenBuildLauncher.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.core.util;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+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.IDebugUIConstants;
+import org.eclipse.debug.ui.RefreshTab;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+import org.eclipse.jdt.launching.JavaRuntime;
+import org.eclipse.m2e.actions.MavenLaunchConstants;
+import org.eclipse.m2e.core.MavenPlugin;
+import org.eclipse.m2e.core.project.IMavenProjectFacade;
+import org.eclipse.m2e.core.project.IMavenProjectRegistry;
+import org.eclipse.m2e.core.project.ResolverConfiguration;
+
+/**
+ * Utility class for launching a customized Maven build.
+ *
+ * @see org.eclipse.m2e.actions.ExecutePomAction
+ */
+public class MavenBuildLauncher extends AbstractApplicationLauncher {
+ private static final String POM_FILE_NAME = "pom.xml";
+
+ private static final String executePomActionExecutingMessage(String goals, String projectLocation) {
+ return String.format("Executing %s in %s", goals, projectLocation);
+ }
+
+ private final IProject project;
+ private final String goals;
+
+ public MavenBuildLauncher(IProject project, String goals, IProgressMonitor monitor) {
+ super(ILaunchManager.RUN_MODE, monitor);
+ this.project = project;
+ this.goals = goals;
+ }
+
+ @Override
+ protected ILaunchConfiguration createLaunchConfiguration() throws CoreException {
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType launchConfigurationType = launchManager
+ .getLaunchConfigurationType(MavenLaunchConstants.LAUNCH_CONFIGURATION_TYPE_ID);
+
+ String rawConfigName = executePomActionExecutingMessage(goals, project.getLocation().toString());
+ String safeConfigName = launchManager.generateLaunchConfigurationName(rawConfigName);
+
+ ILaunchConfigurationWorkingCopy workingCopy = launchConfigurationType.newInstance(null, safeConfigName);
+ workingCopy.setAttribute(MavenLaunchConstants.ATTR_POM_DIR, project.getLocation().toOSString());
+ workingCopy.setAttribute(MavenLaunchConstants.ATTR_GOALS, goals);
+ workingCopy.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
+ workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_SCOPE, "${project}");
+ workingCopy.setAttribute(RefreshTab.ATTR_REFRESH_RECURSIVE, true);
+
+ setProjectConfiguration(workingCopy, project);
+
+ IPath path = getJREContainerPath(project);
+ if (path != null) {
+ workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH,
+ path.toPortableString());
+ }
+
+ return workingCopy;
+ }
+
+ private void setProjectConfiguration(ILaunchConfigurationWorkingCopy workingCopy, IContainer basedir) {
+ IMavenProjectRegistry projectManager = MavenPlugin.getMavenProjectRegistry();
+ IFile pomFile = basedir.getFile(new Path(POM_FILE_NAME));
+ IMavenProjectFacade projectFacade = projectManager.create(pomFile, false, new NullProgressMonitor());
+ if (projectFacade != null) {
+ ResolverConfiguration configuration = projectFacade.getResolverConfiguration();
+
+ String selectedProfiles = configuration.getSelectedProfiles();
+ if (selectedProfiles != null && selectedProfiles.length() > 0) {
+ workingCopy.setAttribute(MavenLaunchConstants.ATTR_PROFILES, selectedProfiles);
+ }
+ }
+ }
+
+ private IPath getJREContainerPath(IContainer basedir) throws CoreException {
+ IProject project = basedir.getProject();
+ if (project != null && project.hasNature(JavaCore.NATURE_ID)) {
+ IJavaProject javaProject = JavaCore.create(project);
+ IClasspathEntry[] entries = javaProject.getRawClasspath();
+ for (int i = 0; i < entries.length; i++) {
+ IClasspathEntry entry = entries[i];
+ if (JavaRuntime.JRE_CONTAINER.equals(entry.getPath().segment(0))) {
+ return entry.getPath();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Validate all the provided parameters to perform a launch.
+ *
+ * @throws IllegalArgumentException if any parameter is not legal.
+ */
+ @Override
+ protected void validateParameters() {
+ if (project == null) {
+ throw new IllegalArgumentException("The provided project cannot be null!");
+ }
+ if (project.findMember(POM_FILE_NAME) == null) {
+ throw new IllegalArgumentException("The project must be a Maven project with a " + POM_FILE_NAME + " file in the root!");
+ }
+ if (goals == null || goals.isEmpty()) {
+ throw new IllegalArgumentException("The goals specified must not be empty!");
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/OsPlatformUtils.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/OsPlatformUtils.java
new file mode 100644
index 00000000..71236634
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/OsPlatformUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.core.util;
+
+import org.eclipse.core.runtime.Platform;
+
+public class OsPlatformUtils {
+
+ public static boolean isWindows() {
+ return Platform.getOS().equals(Platform.OS_WIN32);
+ }
+
+ public static boolean isMac() {
+ return Platform.getOS().equals(Platform.OS_MACOSX);
+ }
+
+ public static boolean isLinux() {
+ return Platform.getOS().equals(Platform.OS_LINUX);
+ }
+
+ public static String currentUser() {
+ return System.getProperty("user.name");
+ }
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/PluginUtils.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/PluginUtils.java
new file mode 100644
index 00000000..c415dd3b
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/PluginUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.core.util;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.variables.VariablesPlugin;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IConsoleManager;
+import org.eclipse.ui.console.MessageConsole;
+
+/**
+ * Utilities for invoking other plugin features.
+ */
+public class PluginUtils {
+
+ /**
+ * Get or create a new MessageConsole given the console name.
+ */
+ public static MessageConsole getOrCreateMessageConsole(String consoleName) {
+ IConsoleManager consoleManager = ConsolePlugin.getDefault().getConsoleManager();
+
+ // Search existing consoles
+ IConsole[] consoles = consoleManager.getConsoles();
+ if (consoles != null) {
+ for (IConsole console : consoles) {
+ if (consoleName.equals(console.getName())
+ && (console instanceof MessageConsole)) {
+ return (MessageConsole)console;
+ }
+ }
+ }
+
+ // If not found, create a new console
+ MessageConsole newConsole = new MessageConsole(consoleName, null);
+ ConsolePlugin.getDefault().getConsoleManager()
+ .addConsoles(new IConsole[] { newConsole });
+ return newConsole;
+ }
+
+ /**
+ * Replace the placeholder variables in the original string with the real ones.
+ * E.g. ${workspace_loc:/Project/file.txt} will be replaced to the real workspace location.
+ *
+ * @throws CoreException - If unable to resolve the value of one or more variables
+ */
+ public static String variablePluginReplace(String originalValue) throws CoreException {
+ if (originalValue == null || originalValue.isEmpty()) {
+ return originalValue;
+ }
+ return VariablesPlugin.getDefault().getStringVariableManager().performStringSubstitution(originalValue);
+ }
+
+ private static final String WORKSPACE_LOC = "workspace_loc";
+ /**
+ * Generate a String with workspace location variable, and the relative path argument.
+ */
+ public static String variablePluginGenerateWorkspacePath(String argument) {
+ return VariablesPlugin.getDefault().getStringVariableManager().generateVariableExpression(WORKSPACE_LOC, argument);
+ }
+
+ public static String variablePluginGenerateWorkspacePath(IPath relativePath) {
+ return variablePluginGenerateWorkspacePath(relativePath.toString());
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/RemoteDebugLauncher.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/RemoteDebugLauncher.java
new file mode 100644
index 00000000..2e76ea9e
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/RemoteDebugLauncher.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.core.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+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.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.core.ILaunchManager;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+
+/**
+ * Launch a remote debugger.
+ *
+ * @see org.eclipse.m2e.internal.launch.MavenConsoleLineTracker
+ */
+public class RemoteDebugLauncher extends AbstractApplicationLauncher {
+ private final IProject project;
+ private final int portNo;
+
+ public RemoteDebugLauncher(IProject project, int port, IProgressMonitor monitor) {
+ super(ILaunchManager.DEBUG_MODE, monitor);
+ this.project = project;
+ this.portNo = port;
+ }
+
+ @Override
+ protected ILaunchConfiguration createLaunchConfiguration() throws CoreException {
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType launchConfigurationType = launchManager
+ .getLaunchConfigurationType(IJavaLaunchConfigurationConstants.ID_REMOTE_JAVA_APPLICATION);
+
+ ILaunchConfigurationWorkingCopy workingCopy = launchConfigurationType.newInstance(null,
+ "Connecting debugger to port " + portNo);
+ workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_ALLOW_TERMINATE, true);
+ workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_VM_CONNECTOR,
+ IJavaLaunchConfigurationConstants.ID_SOCKET_ATTACH_VM_CONNECTOR);
+
+ Map connectMap = new HashMap();
+ connectMap.put("port", String.valueOf(portNo));
+ connectMap.put("hostname", "localhost");
+ workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CONNECT_MAP, connectMap);
+
+ workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.getName());
+
+ return workingCopy;
+ }
+
+ @Override
+ protected void validateParameters() {
+ if (project == null) {
+ throw new IllegalArgumentException("The project must be specified!");
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/WorkbenchUtils.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/WorkbenchUtils.java
index bc107c66..3c377110 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/WorkbenchUtils.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/util/WorkbenchUtils.java
@@ -77,7 +77,10 @@ public void run() {
try {
if (url == null || workbench == null) return;
IWorkbenchBrowserSupport browserSupport = workbench.getBrowserSupport();
- browserSupport.createBrowser(IWorkbenchBrowserSupport.AS_EDITOR, null, null, null)
+ browserSupport.createBrowser(
+ IWorkbenchBrowserSupport.AS_EDITOR
+ | IWorkbenchBrowserSupport.LOCATION_BAR
+ | IWorkbenchBrowserSupport.NAVIGATION_BAR, null, null, null)
.openURL(url);
} catch (PartInitException e) {
AwsToolkitCore.getDefault().logWarning(String.format("Failed to open the url %s in the editor!", url.toString()), e);
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/FilePathValidator.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/FilePathValidator.java
index 07d13026..a2d3d67e 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/FilePathValidator.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/FilePathValidator.java
@@ -21,9 +21,14 @@
import org.eclipse.core.runtime.IStatus;
/**
- *
+ * File exists and valid validator.
*/
public class FilePathValidator implements IValidator {
+ private final String propertyName;
+
+ public FilePathValidator(String propertyName) {
+ this.propertyName = propertyName;
+ }
/* (non-Javadoc)
* @see org.eclipse.core.databinding.validation.IValidator#validate(java.lang.Object)
@@ -33,9 +38,8 @@ public IStatus validate(Object value) {
String filePath = (String) value;
File file = new File(filePath);
if (!file.exists() || !file.isFile()) {
- return ValidationStatus.error("Not a valid file!");
+ return ValidationStatus.error("Property " + propertyName + " does not contain a valid file!");
}
return ValidationStatus.ok();
}
-
}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/IntegerRangeValidator.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/IntegerRangeValidator.java
new file mode 100644
index 00000000..da506784
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/IntegerRangeValidator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.core.validator;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.IStatus;
+
+public class IntegerRangeValidator implements IValidator {
+ private final String propertyName;
+ private final int min;
+ private final int max;
+
+ public IntegerRangeValidator(String propertyName, int min, int max) {
+ this.propertyName = propertyName;
+ this.min = min;
+ this.max = max;
+ }
+
+ @Override
+ public IStatus validate(final Object value) {
+ if (!(value instanceof String)) {
+ return ValidationStatus.error(propertyName + " value must be specified!");
+ }
+
+ int number;
+ try {
+ number = Integer.parseInt((String) value);
+ } catch (NumberFormatException exception) {
+ return ValidationStatus.error(propertyName + " value must be an integer!");
+ }
+
+ if (number < min || number > max) {
+ return ValidationStatus.error(String.format("%s value must be between %d and %d", propertyName, min, max));
+ }
+
+ return ValidationStatus.ok();
+ }
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/WorkspacePathValidator.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/WorkspacePathValidator.java
new file mode 100644
index 00000000..706233b9
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/validator/WorkspacePathValidator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.core.validator;
+
+import java.io.File;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+
+import com.amazonaws.eclipse.core.util.PluginUtils;
+
+/**
+ * Validator that validates the provided path is valid and the underlying file exists.
+ */
+public class WorkspacePathValidator implements IValidator {
+ private final String propertyName;
+ private final boolean isEmptyAllowed;
+
+ public WorkspacePathValidator(String propertyName, boolean isEmptyAllowed) {
+ this.propertyName = propertyName;
+ this.isEmptyAllowed = isEmptyAllowed;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.databinding.validation.IValidator#validate(java.lang.Object)
+ */
+ @Override
+ public IStatus validate(Object value) {
+ try {
+ String filePath = (String) value;
+ if (filePath == null || filePath.isEmpty()) {
+ if (isEmptyAllowed) {
+ return ValidationStatus.ok();
+ } else {
+ return ValidationStatus.error(propertyName + " value must not be empty.");
+ }
+ }
+ filePath = PluginUtils.variablePluginReplace(filePath);
+ File file = new File(filePath);
+ if (!file.exists() || !file.isFile()) {
+ return ValidationStatus.error(propertyName + " value is not a valid file!");
+ }
+ return ValidationStatus.ok();
+ } catch (CoreException e) {
+ return ValidationStatus.error(propertyName + " value is not a valid file!");
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/widget/ComboViewerComplex.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/widget/ComboViewerComplex.java
index e8eef46f..dd5f26fa 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/widget/ComboViewerComplex.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/widget/ComboViewerComplex.java
@@ -23,6 +23,7 @@
import java.util.Collection;
import java.util.List;
+import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
@@ -44,7 +45,6 @@
* and binds it to the model.
*/
public class ComboViewerComplex {
-
private final ComboViewer comboViewer;
private final IObservableValue enabler = new WritableValue();
@@ -59,6 +59,7 @@ protected ComboViewerComplex(
String labelValue,
int comboSpan,
List listeners) {
+
if (labelValue != null) {
newLabel(parent, labelValue);
}
@@ -68,7 +69,7 @@ protected ComboViewerComplex(
comboViewer.setInput(items);
IViewerObservableValue viewerObservableValue = ViewerProperties.singleSelection().observe(comboViewer);
- bindingContext.bindValue(viewerObservableValue, pojoObservableValue);
+ Binding binding = bindingContext.bindValue(viewerObservableValue, pojoObservableValue);
enabler.setValue(true);
ChainValidator validatorChain = new ChainValidator<>(viewerObservableValue, enabler, validators);
@@ -83,6 +84,12 @@ protected ComboViewerComplex(
for (ISelectionChangedListener listener : listeners) {
comboViewer.addSelectionChangedListener(listener);
}
+ comboViewer.getCombo().addDisposeListener(e -> {
+ bindingContext.removeBinding(binding);
+ bindingContext.removeValidationStatusProvider(validatorChain);
+ viewerObservableValue.dispose();
+ validatorChain.dispose();
+ });
}
public ComboViewer getComboViewer() {
@@ -94,6 +101,13 @@ public void setEnabled(boolean enabled) {
enabler.setValue(enabled);
}
+ public void selectItem(T item) {
+ Collection items = (Collection) comboViewer.getInput();
+ if (item != null && items.contains(item)) {
+ comboViewer.setSelection(new StructuredSelection(item));
+ }
+ }
+
public static ComboViewerComplexBuilder builder() {
return new ComboViewerComplexBuilder<>();
}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/widget/TextComplex.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/widget/TextComplex.java
index 561a1320..93c713bd 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/widget/TextComplex.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/core/widget/TextComplex.java
@@ -17,22 +17,24 @@
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newControlDecoration;
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newLabel;
import static com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory.newText;
-import static com.amazonaws.util.ValidationUtils.assertNotNull;
-import static com.amazonaws.util.ValidationUtils.assertStringNotEmpty;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
+import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
-import org.eclipse.jface.databinding.swt.SWTObservables;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import com.amazonaws.eclipse.databinding.ChainValidator;
@@ -42,12 +44,9 @@
* A complex Text widget including a Label, DataBinding, Validator and Decoration.
*/
public class TextComplex {
-
private final Text text;
- private ControlDecoration controlDecoration;
- private ISWTObservableValue swtObservableValue;
+
private final IObservableValue enabler = new WritableValue();
- private final ChainValidator validatorChain;
private TextComplex(
Composite composite,
@@ -62,22 +61,34 @@ private TextComplex(
int labelColSpan,
String textMessage) {
- if (createLabel) newLabel(composite, labelValue, labelColSpan);
- text = newText(composite, "", textColSpan);
+ if (createLabel) {
+ Label label = newLabel(composite, labelValue, labelColSpan);
+ label.setToolTipText(textMessage);
+ }
+ text = newText(composite, defaultValue, textColSpan);
+ text.setToolTipText(textMessage);
text.setMessage(textMessage);
- controlDecoration = newControlDecoration(text, "");
-
- swtObservableValue = SWTObservables.observeText(text, SWT.Modify);
- dataBindingContext.bindValue(swtObservableValue, pojoObservableValue);
+ ControlDecoration controlDecoration = newControlDecoration(text, "");
+ ISWTObservableValue swtObservableValue = WidgetProperties.text(SWT.Modify).observe(text);
+ Binding binding = dataBindingContext.bindValue(swtObservableValue, pojoObservableValue);
enabler.setValue(true);
- validatorChain = new ChainValidator<>(
- swtObservableValue, enabler, validators);
+ ChainValidator validatorChain = new ChainValidator<>(swtObservableValue, enabler, validators);
dataBindingContext.addValidationStatusProvider(validatorChain);
- new DecorationChangeListener(controlDecoration,
- validatorChain.getValidationStatus());
- if (modifyListener != null) text.addModifyListener(modifyListener);
+ new DecorationChangeListener(controlDecoration, validatorChain.getValidationStatus());
swtObservableValue.setValue(defaultValue);
+
+ text.addDisposeListener(e -> {
+ dataBindingContext.removeBinding(binding);
+ dataBindingContext.removeValidationStatusProvider(validatorChain);
+ validatorChain.dispose();
+ controlDecoration.dispose();
+ swtObservableValue.dispose();
+ });
+
+ if (modifyListener != null) {
+ text.addModifyListener(modifyListener);
+ }
}
// Whether enable widget or not.
@@ -94,17 +105,21 @@ public Text getText() {
return text;
}
- public static TextComplexBuilder builder() {
- return new TextComplexBuilder();
+ @NonNull
+ public static TextComplexBuilder builder(
+ @NonNull Composite parent,
+ @NonNull DataBindingContext dataBindingContext,
+ @NonNull IObservableValue pojoObservableValue) {
+ return new TextComplexBuilder(parent, dataBindingContext, pojoObservableValue);
}
public static class TextComplexBuilder {
+ private final Composite composite;
+ private final DataBindingContext dataBindingContext;
+ private final IObservableValue pojoObservableValue;
+ private final List validators = new ArrayList<>();
- private Composite composite;
- private DataBindingContext dataBindingContext;
- private IObservableValue pojoObservableValue;
- private List validators = new ArrayList<>();
- private String labelValue;
+ private String labelValue = "Label: ";
private String textMessage = "";
private ModifyListener modifyListener;
@@ -113,29 +128,21 @@ public static class TextComplexBuilder {
private int textColSpan = 1;
private int labelColSpan = 1;
- public TextComplex build() {
- validateParameters();
+ private TextComplexBuilder(
+ @NonNull Composite parent,
+ @NonNull DataBindingContext dataBindingContext,
+ @NonNull IObservableValue pojoObservableValue) {
+ this.composite = parent;
+ this.dataBindingContext = dataBindingContext;
+ this.pojoObservableValue = pojoObservableValue;
+ }
+ public TextComplex build() {
return new TextComplex(
composite, dataBindingContext, pojoObservableValue, validators, modifyListener,
createLabel, labelValue, defaultValue, textColSpan, labelColSpan, textMessage);
}
- public TextComplexBuilder composite(Composite composite) {
- this.composite = composite;
- return this;
- }
-
- public TextComplexBuilder dataBindingContext(DataBindingContext dataBindingContext) {
- this.dataBindingContext = dataBindingContext;
- return this;
- }
-
- public TextComplexBuilder pojoObservableValue(IObservableValue pojoObservableValue) {
- this.pojoObservableValue = pojoObservableValue;
- return this;
- }
-
public TextComplexBuilder addValidator(IValidator validator) {
this.validators.add(validator);
return this;
@@ -162,7 +169,7 @@ public TextComplexBuilder labelValue(String labelValue) {
}
public TextComplexBuilder defaultValue(String defaultValue) {
- this.defaultValue = defaultValue;
+ this.defaultValue = Optional.ofNullable(defaultValue).orElse("");
return this;
}
@@ -180,13 +187,5 @@ public TextComplexBuilder textMessage(String textMessage) {
this.textMessage = textMessage;
return this;
}
-
- private void validateParameters() {
- assertNotNull(composite, "Composite");
- assertNotNull(dataBindingContext, "DataBindingContext");
- assertNotNull(pojoObservableValue, "PojoObservableValue");
- if (createLabel) assertStringNotEmpty(labelValue, "LabelValue");
- }
}
-
}
diff --git a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/explorer/AwsAction.java b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/explorer/AwsAction.java
index 9f000b67..bcb49355 100644
--- a/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/explorer/AwsAction.java
+++ b/bundles/com.amazonaws.eclipse.core/src/com/amazonaws/eclipse/explorer/AwsAction.java
@@ -76,18 +76,33 @@ public void run() {
protected abstract void doRun();
- // Helper method to publish a filed action metric immediately
+ // Helper method to publish a failed action metric immediately
public static void publishFailedAction(AwsToolkitMetricType metricType) {
- new MetricsDataModel(metricType).addAttribute(END_RESULT, FAILED).publishEvent();
+ publishFailedAction(new MetricsDataModel(metricType));
+ }
+
+ // Helper method to publish a failed action metric immediately
+ public static void publishFailedAction(MetricsDataModel dataModel) {
+ dataModel.addAttribute(END_RESULT, FAILED).publishEvent();
}
// Helper method to publish a succeeded action metric immediately
public static void publishSucceededAction(AwsToolkitMetricType metricType) {
- new MetricsDataModel(metricType).addAttribute(END_RESULT, SUCCEEDED).publishEvent();
+ publishSucceededAction(new MetricsDataModel(metricType));
+ }
+
+ // Helper method to publish a succeeded action metric immediately
+ public static void publishSucceededAction(MetricsDataModel dataModel) {
+ dataModel.addAttribute(END_RESULT, SUCCEEDED).publishEvent();
}
// Helper method to publish a performed action metric immediately
public static void publishPerformedAction(AwsToolkitMetricType metricType) {
- new MetricsDataModel(metricType).addAttribute(END_RESULT, PERFORMED).publishEvent();
+ publishPerformedAction(new MetricsDataModel(metricType));
+ }
+
+ // Helper method to publish a performed action metric immediately
+ public static void publishPerformedAction(MetricsDataModel dataModel) {
+ dataModel.addAttribute(END_RESULT, PERFORMED).publishEvent();
}
}
diff --git a/bundles/com.amazonaws.eclipse.dynamodb/src/com/amazonaws/eclipse/dynamodb/testtool/StartTestToolConfigurationWizardPage.java b/bundles/com.amazonaws.eclipse.dynamodb/src/com/amazonaws/eclipse/dynamodb/testtool/StartTestToolConfigurationWizardPage.java
index 53d50e21..e7fb76c1 100644
--- a/bundles/com.amazonaws.eclipse.dynamodb/src/com/amazonaws/eclipse/dynamodb/testtool/StartTestToolConfigurationWizardPage.java
+++ b/bundles/com.amazonaws.eclipse.dynamodb/src/com/amazonaws/eclipse/dynamodb/testtool/StartTestToolConfigurationWizardPage.java
@@ -35,6 +35,7 @@
import com.amazonaws.eclipse.core.AwsToolkitCore;
import com.amazonaws.eclipse.core.ui.wizards.ErrorDecorator;
+import com.amazonaws.eclipse.core.validator.IntegerRangeValidator;
import com.amazonaws.eclipse.databinding.ChainValidator;
import com.amazonaws.eclipse.dynamodb.DynamoDBPlugin;
import com.amazonaws.eclipse.dynamodb.preferences.TestToolPreferencePage;
@@ -149,6 +150,7 @@ public void handleChange(final ChangeEvent event) {
/**
* A validator that ensures the input is a valid port number.
+ * @deprecated for {@link IntegerRangeValidator}
*/
private static class PortValidator implements IValidator {
@Override
diff --git a/bundles/com.amazonaws.eclipse.ec2/src/com/amazonaws/eclipse/ec2/PlatformUtils.java b/bundles/com.amazonaws.eclipse.ec2/src/com/amazonaws/eclipse/ec2/PlatformUtils.java
index 18603a5d..ff6b5dcc 100644
--- a/bundles/com.amazonaws.eclipse.ec2/src/com/amazonaws/eclipse/ec2/PlatformUtils.java
+++ b/bundles/com.amazonaws.eclipse.ec2/src/com/amazonaws/eclipse/ec2/PlatformUtils.java
@@ -16,6 +16,10 @@
package com.amazonaws.eclipse.ec2;
import java.io.File;
+import static com.amazonaws.eclipse.core.util.OsPlatformUtils.isWindows;
+import static com.amazonaws.eclipse.core.util.OsPlatformUtils.isMac;
+import static com.amazonaws.eclipse.core.util.OsPlatformUtils.isLinux;
+
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
@@ -38,63 +42,6 @@ public class PlatformUtils {
private static final String PPK_CONVERTER_EXE = "/lib/PemToPPKConverter.exe";
private static final Logger logger = Logger.getLogger(PlatformUtils.class.getName());
- /**
- * Returns true if the current platform is a windows platform.
- *
- * @return True if the current platform is a windows platform.
- */
- public boolean isWindows() {
- String platform = System.getProperty("os.name");
-
- if (platform == null) {
- Status status = new Status(IStatus.WARNING, Ec2Plugin.PLUGIN_ID,
- "No system property for 'os.name'");
- StatusManager.getManager().handle(status, StatusManager.LOG);
-
- return false;
- }
-
- return platform.toLowerCase().contains("windows");
- }
-
- /**
- * Returns true if the current platform is a Linux platform.
- *
- * @return True if the current platform is a Linux platform.
- */
- public boolean isLinux() {
- String platform = System.getProperty("os.name");
-
- if (platform == null) {
- Status status = new Status(IStatus.WARNING, Ec2Plugin.PLUGIN_ID,
- "No system property for 'os.name'");
- StatusManager.getManager().handle(status, StatusManager.LOG);
-
- return false;
- }
-
- return platform.toLowerCase().contains("linux");
- }
-
- /**
- * Returns true if the current platform is a Mac platform.
- *
- * @return True if the current platform is a Mac platform.
- */
- public boolean isMac() {
- String platform = System.getProperty("os.name");
-
- if (platform == null) {
- Status status = new Status(IStatus.WARNING, Ec2Plugin.PLUGIN_ID,
- "No system property for 'os.name'");
- StatusManager.getManager().handle(status, StatusManager.LOG);
-
- return false;
- }
-
- return platform.toLowerCase().contains("mac os");
- }
-
/**
* Returns true if the platform specific SSH client is correctly configured
* and ready to be used on this system.
diff --git a/bundles/com.amazonaws.eclipse.ec2/src/com/amazonaws/eclipse/ec2/preferences/ExternalToolsPreferencePage.java b/bundles/com.amazonaws.eclipse.ec2/src/com/amazonaws/eclipse/ec2/preferences/ExternalToolsPreferencePage.java
index 30c691ba..8fbe4cfb 100644
--- a/bundles/com.amazonaws.eclipse.ec2/src/com/amazonaws/eclipse/ec2/preferences/ExternalToolsPreferencePage.java
+++ b/bundles/com.amazonaws.eclipse.ec2/src/com/amazonaws/eclipse/ec2/preferences/ExternalToolsPreferencePage.java
@@ -1,16 +1,16 @@
/*
- * Copyright 2008-2012 Amazon Technologies, Inc.
+ * Copyright 2008-2012 Amazon Technologies, Inc.
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
- *
+ *
* http://aws.amazon.com/apache2.0
*
- * This file 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.
+ * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
+ * OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.amazonaws.eclipse.ec2.preferences;
@@ -29,6 +29,7 @@
import org.eclipse.ui.IWorkbenchPreferencePage;
import com.amazonaws.eclipse.core.ui.WebLinkListener;
+import com.amazonaws.eclipse.core.util.OsPlatformUtils;
import com.amazonaws.eclipse.ec2.Ec2Plugin;
import com.amazonaws.eclipse.ec2.PlatformUtils;
@@ -44,9 +45,9 @@ public class ExternalToolsPreferencePage
private StringFieldEditor sshOptions;
private FileFieldEditor puttyExecutable;
private StringFieldEditor sshUser;
-
- private static final int MAX_FIELD_EDITOR_COLUMNS = 3;
-
+
+ private static final int MAX_FIELD_EDITOR_COLUMNS = 3;
+
public ExternalToolsPreferencePage() {
super("External Tool Configuration");
setPreferenceStore(Ec2Plugin.getDefault().getPreferenceStore());
@@ -62,20 +63,19 @@ public void init(IWorkbench workbench) {}
@Override
protected Control createContents(Composite parent) {
Composite top = new Composite(parent, SWT.LEFT);
-
- // Sets the layout data for the top composite's
+
+ // Sets the layout data for the top composite's
// place in its parent's layout.
top.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
top.setLayout(new GridLayout());
-
+
Group sshClientGroup = new Group(top, SWT.LEFT);
sshClientGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
sshClientGroup.setText("SSH Client:");
-
- PlatformUtils platformUtils = new PlatformUtils();
- if (platformUtils.isWindows()) {
+
+ if (OsPlatformUtils.isWindows()) {
String puttyUrl = "http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html";
-
+
WebLinkListener webLinkListener = new WebLinkListener();
Link l = new Link(sshClientGroup, SWT.NONE);
l.setText("PuTTY is required for remote shell connections "
@@ -85,15 +85,15 @@ protected Control createContents(Composite parent) {
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = MAX_FIELD_EDITOR_COLUMNS;
l.setLayoutData(data);
-
+
puttyExecutable = newFileFieldEditor(PreferenceConstants.P_PUTTY_EXECUTABLE, "PuTTY Executable:", sshClientGroup);
} else {
// For the Mac, we use a custom AppleScript script that we ship with the
// plugin so we don't need a seperate terminal exectuable.
- if (!platformUtils.isMac()) {
+ if (!OsPlatformUtils.isMac()) {
terminalExecutable = newFileFieldEditor(PreferenceConstants.P_TERMINAL_EXECUTABLE, "Terminal:", sshClientGroup);
}
-
+
sshClient = newFileFieldEditor(PreferenceConstants.P_SSH_CLIENT, "SSH Client:", sshClientGroup);
sshOptions = new StringFieldEditor(PreferenceConstants.P_SSH_OPTIONS, "SSH Options: ", sshClientGroup);
@@ -102,19 +102,19 @@ protected Control createContents(Composite parent) {
sshOptions.load();
sshOptions.fillIntoGrid(sshClientGroup, MAX_FIELD_EDITOR_COLUMNS);
}
-
+
sshUser = new StringFieldEditor(PreferenceConstants.P_SSH_USER, "SSH User: ", sshClientGroup);
sshUser.setPage(this);
sshUser.setPreferenceStore(this.getPreferenceStore());
sshUser.load();
sshUser.fillIntoGrid(sshClientGroup, MAX_FIELD_EDITOR_COLUMNS);
-
+
// Reset the layout to three columns after the FieldEditors have mucked with it
GridLayout layout = (GridLayout)(sshClientGroup.getLayout());
layout.numColumns = MAX_FIELD_EDITOR_COLUMNS;
layout.marginWidth = 10;
layout.marginHeight = 8;
-
+
return top;
}
@@ -127,9 +127,9 @@ protected void performDefaults() {
if (sshClient != null) sshClient.loadDefault();
if (sshOptions != null) sshOptions.loadDefault();
if (sshUser != null) sshUser.loadDefault();
-
+
if (puttyExecutable != null) puttyExecutable.loadDefault();
-
+
super.performDefaults();
}
@@ -141,10 +141,10 @@ public boolean performOk() {
if (terminalExecutable != null) terminalExecutable.store();
if (sshClient != null) sshClient.store();
if (sshOptions != null) sshOptions.store();
- if (sshUser != null) sshUser.store();
+ if (sshUser != null) sshUser.store();
if (puttyExecutable != null) puttyExecutable.store();
-
+
return super.performOk();
}
@@ -154,14 +154,14 @@ public boolean performOk() {
/**
* Convenience method for creating a FileFieldEditor.
- *
+ *
* @param preferenceName
* The preference managed by this FileFieldEditor.
* @param label
* The label for this FieldEditor.
* @param parent
* The parent for this new FieldEditor widget.
- *
+ *
* @return The new FileFieldEditor.
*/
private FileFieldEditor newFileFieldEditor(String preferenceName, String label, Composite parent) {
diff --git a/bundles/com.amazonaws.eclipse.elasticbeanstalk/src/com/amazonaws/eclipse/elasticbeanstalk/server/ui/databinding/MinMaxLengthValidator.java b/bundles/com.amazonaws.eclipse.elasticbeanstalk/src/com/amazonaws/eclipse/elasticbeanstalk/server/ui/databinding/MinMaxLengthValidator.java
index ef3272c7..0fb9e404 100644
--- a/bundles/com.amazonaws.eclipse.elasticbeanstalk/src/com/amazonaws/eclipse/elasticbeanstalk/server/ui/databinding/MinMaxLengthValidator.java
+++ b/bundles/com.amazonaws.eclipse.elasticbeanstalk/src/com/amazonaws/eclipse/elasticbeanstalk/server/ui/databinding/MinMaxLengthValidator.java
@@ -18,10 +18,14 @@
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
+import com.amazonaws.eclipse.core.validator.StringLengthValidator;
+
/**
* IValidator implementation that tests that a string for length constraints.
* Excludes the range specified.
+ * @deprecated to {@link StringLengthValidator}
*/
+@Deprecated
public class MinMaxLengthValidator implements IValidator {
private final String message = "%s must be %d to %d characters in length.";
private final int min;
diff --git a/bundles/com.amazonaws.eclipse.lambda/META-INF/MANIFEST.MF b/bundles/com.amazonaws.eclipse.lambda/META-INF/MANIFEST.MF
index 1d3e6847..45257776 100644
--- a/bundles/com.amazonaws.eclipse.lambda/META-INF/MANIFEST.MF
+++ b/bundles/com.amazonaws.eclipse.lambda/META-INF/MANIFEST.MF
@@ -34,10 +34,13 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.m2e.core.ui;bundle-version="1.5.1",
org.eclipse.m2e.maven.runtime;bundle-version="1.5.1",
org.eclipse.m2e.archetype.common;bundle-version="1.5.1",
- com.amazonaws.eclipse.javasdk;bundle-version="1.11.130"
+ com.amazonaws.eclipse.javasdk;bundle-version="1.11.130",
+ org.eclipse.jdt.debug.ui,
+ org.eclipse.jdt.annotation
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
Export-Package: com.amazonaws.eclipse.lambda.blueprint,
+ com.amazonaws.eclipse.lambda.launching,
com.amazonaws.eclipse.lambda.project.template,
com.amazonaws.eclipse.lambda.project.template.data,
com.amazonaws.eclipse.lambda.project.wizard.model,
@@ -46,4 +49,9 @@ Export-Package: com.amazonaws.eclipse.lambda.blueprint,
com.amazonaws.eclipse.lambda.serverless.model.transform,
com.amazonaws.eclipse.lambda.serverless.template,
freemarker.template
-Import-Package: org.eclipse.ui.navigator
+Import-Package: org.eclipse.core.variables,
+ org.eclipse.debug.internal.ui,
+ org.eclipse.debug.ui,
+ org.eclipse.debug.ui.console,
+ org.eclipse.m2e.actions,
+ org.eclipse.ui.navigator
diff --git a/bundles/com.amazonaws.eclipse.lambda/icons/sam-local.png b/bundles/com.amazonaws.eclipse.lambda/icons/sam-local.png
new file mode 100644
index 00000000..7f4c7cb5
Binary files /dev/null and b/bundles/com.amazonaws.eclipse.lambda/icons/sam-local.png differ
diff --git a/bundles/com.amazonaws.eclipse.lambda/plugin.xml b/bundles/com.amazonaws.eclipse.lambda/plugin.xml
index 595687f6..a6a2e7b3 100644
--- a/bundles/com.amazonaws.eclipse.lambda/plugin.xml
+++ b/bundles/com.amazonaws.eclipse.lambda/plugin.xml
@@ -2,6 +2,7 @@
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
+
+
-
-
+
+
+
+
+
+
+
+
@@ -253,4 +304,134 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/LambdaPlugin.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/LambdaPlugin.java
index 4edcffef..51bc0419 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/LambdaPlugin.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/LambdaPlugin.java
@@ -33,12 +33,14 @@ public class LambdaPlugin extends AbstractAwsPlugin {
public static final String DEFAULT_REGION = "us-east-1";
public static final String IMAGE_LAMBDA = "lambda-service";
public static final String IMAGE_FUNCTION = "function";
+ public static final String IMAGE_SAM_LOCAL = "sam-local";
private static final Map IMAGE_REGISTRY_MAP = new HashMap<>();
static {
IMAGE_REGISTRY_MAP.put(IMAGE_LAMBDA, "/icons/lambda-service.png");
IMAGE_REGISTRY_MAP.put(IMAGE_FUNCTION, "/icons/function.png");
+ IMAGE_REGISTRY_MAP.put(IMAGE_SAM_LOCAL, "/icons/sam-local.png");
}
/*
@@ -57,12 +59,11 @@ public class LambdaPlugin extends AbstractAwsPlugin {
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
+ plugin = this;
initializePreferenceStoreDefaults();
projectChangeTracker.clearDirtyFlags();
projectChangeTracker.start();
-
- plugin = this;
}
/**
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/dialog/SamLocalGenerateEventDialog.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/dialog/SamLocalGenerateEventDialog.java
new file mode 100644
index 00000000..fc5f178a
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/dialog/SamLocalGenerateEventDialog.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.dialog;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.map.WritableMap;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.ScrolledComposite;
+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.eclipse.ui.dialogs.SaveAsDialog;
+
+import com.amazonaws.eclipse.core.mobileanalytics.AwsToolkitMetricType;
+import com.amazonaws.eclipse.core.mobileanalytics.MetricsDataModel;
+import com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory;
+import com.amazonaws.eclipse.core.util.CliUtil;
+import com.amazonaws.eclipse.core.util.CliUtil.CliProcessTracker;
+import com.amazonaws.eclipse.core.widget.ComboViewerComplex;
+import com.amazonaws.eclipse.core.widget.TextComplex;
+import com.amazonaws.eclipse.explorer.AwsAction;
+import com.amazonaws.eclipse.lambda.LambdaPlugin;
+import com.amazonaws.eclipse.lambda.launching.SamLocalConstants;
+import com.amazonaws.eclipse.lambda.project.wizard.util.FunctionProjectUtil;
+
+/**
+ * Dialog for generating Lambda handler event json file using SAM Local.
+ */
+public class SamLocalGenerateEventDialog extends TitleAreaDialog {
+ private final SamLocalLambdaEventDataModel dataModel = new SamLocalLambdaEventDataModel();
+ private final DataBindingContext bindingContext;
+
+ private ScrolledComposite scrolledComposite;
+ private Composite composite;
+
+ private ComboViewerComplex lambdaEventCombo;
+
+ public SamLocalGenerateEventDialog(Shell parentShell) {
+ super(parentShell);
+ this.bindingContext = new DataBindingContext();
+ }
+
+ public SamLocalLambdaEventDataModel getDataModel() {
+ return dataModel;
+ }
+
+ @Override
+ public void create() {
+ super.create();
+ setTitle("Generate Lambda event template");
+ setMessage("Generates Lambda events (e.g. for S3/Kinesis etc) that can be used as the input of the Lambda function.");
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite rootComposite = (Composite) super.createDialogArea(parent);
+
+ lambdaEventCombo = ComboViewerComplex.builder()
+ .composite(WizardWidgetFactory.newComposite(rootComposite, 1, 2))
+ .labelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof SamLocalLambdaEvent) {
+ return ((SamLocalLambdaEvent) element).presentation;
+ }
+ return super.getText(element);
+ }
+ })
+ .pojoObservableValue(PojoProperties.value(SamLocalLambdaEventDataModel.P_EVENT_TYPE).observe(dataModel))
+ .bindingContext(bindingContext)
+ .items(Arrays.asList(SamLocalLambdaEvent.values()))
+ .defaultItem(SamLocalLambdaEvent.S3)
+ .labelValue("Select event type: ")
+ .addListeners(e -> onLambdaEventComboSelect())
+ .build();
+
+ Group parameterListGroup = WizardWidgetFactory.newGroup(rootComposite, "Parameter List");
+
+ scrolledComposite = new ScrolledComposite(parameterListGroup, SWT.V_SCROLL);
+ scrolledComposite.setExpandHorizontal(true);
+ scrolledComposite.setExpandVertical(true);
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(scrolledComposite);
+
+ composite = new Composite(scrolledComposite, SWT.None);
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(composite);
+ GridLayoutFactory.fillDefaults().numColumns(2).applyTo(composite);
+ scrolledComposite.setContent(composite);
+
+ onLambdaEventComboSelect();
+
+ return rootComposite;
+ }
+
+ private void onLambdaEventComboSelect() {
+ for (Control control : composite.getChildren()) {
+ control.dispose();
+ }
+
+ SamLocalLambdaEvent eventType = dataModel.getEventType();
+ for (SamLocalEventParameter parameter : eventType.parameterList) {
+ TextComplex.builder(composite, bindingContext, Observables.observeMapEntry(dataModel.getParameterList(), parameter.name, String.class))
+ .defaultValue(parameter.defaultValue)
+ .labelValue(parameter.presentation)
+ .textMessage(parameter.description)
+ .build();
+ }
+ setMessage(eventType.description);
+
+ Shell shell = composite.getShell();
+ shell.setMinimumSize(getInitialSize());
+ shell.layout(true, true);
+ }
+
+ @Override
+ protected void okPressed() {
+ SaveAsDialog saveAsDialog = new SaveAsDialog(this.getShell());
+ if (saveAsDialog.open() == Window.OK) {
+ dataModel.setResultPath(saveAsDialog.getResult());
+ generateEventFile();
+ }
+ super.okPressed();
+ }
+
+ @Override
+ protected boolean isResizable() {
+ return true;
+ }
+
+ private void generateEventFile() {
+ SamLocalLambdaEvent eventType = dataModel.getEventType();
+ List builder = new ArrayList<>();
+ builder.add(LambdaPlugin.getDefault().getPreferenceStore().getString(SamLocalConstants.P_SAM_LOCAL_EXECUTABLE));
+ builder.add("local");
+ builder.add("generate-event");
+ builder.add(eventType.commandName);
+
+ for (SamLocalEventParameter parameter : eventType.parameterList) {
+ String value = (String) dataModel.parameterList.get(parameter.name);
+ if (value == null || value.isEmpty()) {
+ value = parameter.defaultValue;
+ }
+ builder.add("--" + parameter.name);
+ builder.add(value.replace("\"", "\\\""));
+ }
+
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(dataModel.resultPath.segment(0));
+ File outputFile = project.getLocation().append(dataModel.resultPath.removeFirstSegments(1)).toFile();
+ MetricsDataModel metricsDataModel = new MetricsDataModel(AwsToolkitMetricType.SAMLOCAL_GENERATE_EVENT);
+ metricsDataModel.addAttribute("EventType", eventType.presentation);
+ try {
+ ByteArrayOutputStream stdErrOutput = new ByteArrayOutputStream();
+ CliProcessTracker tracker = CliUtil.executeCommand(builder, Collections.emptyMap(),
+ new FileOutputStream(outputFile), stdErrOutput);
+ int exitValue = tracker.waitForStream();
+ FunctionProjectUtil.refreshProject(project);
+
+ String stdErr = stdErrOutput.toString();
+ if (exitValue != 0 && stdErr != null && !stdErr.isEmpty()) {
+ LambdaPlugin.getDefault().reportException(stdErr, null);
+ AwsAction.publishFailedAction(metricsDataModel);
+ } else {
+ AwsAction.publishSucceededAction(metricsDataModel);
+ }
+ } catch (IOException e1) {
+ LambdaPlugin.getDefault().reportException("Failed to generate event json file.", e1);
+ AwsAction.publishFailedAction(metricsDataModel);
+ }
+ }
+
+ public static class SamLocalLambdaEventDataModel {
+ public static final String P_EVENT_TYPE = "eventType";
+
+ private SamLocalLambdaEvent eventType;
+ private WritableMap parameterList = new WritableMap(String.class, String.class);
+ private IPath resultPath;
+
+ public IPath getResultPath() {
+ return resultPath;
+ }
+
+ public void setResultPath(IPath resultPath) {
+ this.resultPath = resultPath;
+ }
+
+ public SamLocalLambdaEvent getEventType() {
+ return eventType;
+ }
+
+ public void setEventType(SamLocalLambdaEvent eventType) {
+ this.eventType = eventType;
+ }
+
+ public WritableMap getParameterList() {
+ return parameterList;
+ }
+ }
+
+ public static enum SamLocalLambdaEvent {
+ S3("s3", "S3 Event", "Generates a sample Amazon S3 event",
+ new SamLocalEventParameter("region", "Region", "The region the event should come from (default: \"us-east-1\")", "us-east-1"),
+ new SamLocalEventParameter("bucket", "Bucket", "The S3 bucket the event should reference (default: \"example-bucket\")", "example-bucket"),
+ new SamLocalEventParameter("key", "Key", "The S3 key the event should reference (default: \"test/key\")", "test/key")),
+ SNS("sns", "SNS Event", "Generates a sample Amazon SNS event",
+ new SamLocalEventParameter("message", "Message", "The SNS message body (default: \"example message\")", "example message"),
+ new SamLocalEventParameter("topic", "Topic", "The SNS topic (default: \"arn:aws:sns:us-east-1:111122223333:ExampleTopic\")", "arn:aws:sns:us-east-1:111122223333:ExampleTopic"),
+ new SamLocalEventParameter("subject", "Subject", "The SNS subject (default: \"example subject\")", "example subject")),
+ KINESIS("kinesis", "Kinesis Event", "Generates a sample Amazon Kinesis event",
+ new SamLocalEventParameter("region", "AWS Region", "The region the event should come from (default: \"us-east-1\")", "us-east-1"),
+ new SamLocalEventParameter("partition", "Partition", "The Kinesis partition key (default: \"partitionKey-03\")", "partitionKey-03"),
+ new SamLocalEventParameter("sequence", "Sequence", "The Kinesis sequence number (default: \"49545115243490985018280067714973144582180062593244200961\")", "49545115243490985018280067714973144582180062593244200961"),
+ new SamLocalEventParameter("data", "Data", "The Kinesis message payload, with no base64 encoded (default: \"Hello, this is a test 123.\")", "Hello, this is a test 123.")),
+ DYNAMODB("dynamodb", "DynamoDB Event", "Generates a sample Amazon DynamoDB event",
+ new SamLocalEventParameter("region", "Region", "The region the event should come from (default: \"us-east-1\")", "us-east-1")),
+ API("api", "API Gateway Event", "Generates a sample Amazon API Gateway event",
+ new SamLocalEventParameter("method", "Method", "HTTP method (default: \"POST\")", "POST"),
+ new SamLocalEventParameter("body", "Body", "HTTP body (default: \"{ \"test\": \"body\"}\")", "{ \"test\": \"body\"}"),
+ new SamLocalEventParameter("resource", "Resource", "API Gateway resource name (default: \"/{proxy+}\")", "/{proxy+}"),
+ new SamLocalEventParameter("path", "Path", "HTTP path (default: \"/examplepath\")", "/examplepath")),
+ SCHEDULE("schedule", "Scheduled Event", "Generates a sample scheduled event",
+ new SamLocalEventParameter("region", "Region", "The region the event should come from (default: \"us-east-1\")", "us-east-1")),
+ ;
+
+ private final String commandName;
+ private final String presentation;
+ private final String description;
+ private final SamLocalEventParameter[] parameterList;
+
+ private SamLocalLambdaEvent(String commandName, String presentation, String description, SamLocalEventParameter... eventParameters) {
+ this.commandName = commandName;
+ this.presentation = presentation;
+ this.description = description;
+ parameterList = eventParameters;
+ }
+ }
+
+ public static class SamLocalEventParameter {
+ private final String name;
+ private final String presentation;
+ private final String description;
+ private final String defaultValue;
+
+ public SamLocalEventParameter(String name, String presentation, String description, String defaultValue) {
+ this.name = name;
+ this.presentation = presentation;
+ this.description = description;
+ this.defaultValue = defaultValue;
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/invoke/handler/InvokeFunctionHandler.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/invoke/handler/InvokeFunctionHandler.java
index 11d805af..47006faa 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/invoke/handler/InvokeFunctionHandler.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/invoke/handler/InvokeFunctionHandler.java
@@ -32,12 +32,11 @@
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.console.ConsolePlugin;
-import org.eclipse.ui.console.IConsole;
-import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.console.MessageConsoleStream;
import com.amazonaws.eclipse.core.AwsToolkitCore;
+import com.amazonaws.eclipse.core.util.PluginUtils;
import com.amazonaws.eclipse.lambda.LambdaAnalytics;
import com.amazonaws.eclipse.lambda.LambdaPlugin;
import com.amazonaws.eclipse.lambda.invoke.logs.CloudWatchLogsUtils;
@@ -139,7 +138,7 @@ private static void _doInvoke(final IProject project,
String handlerToBeInvoked = metadata.getLastInvokeHandler();
- MessageConsole lambdaConsole = getOrCreateLambdaConsoleIfNotExist(
+ MessageConsole lambdaConsole = PluginUtils.getOrCreateMessageConsole(
handlerToBeInvoked + " Lambda Console");
lambdaConsole.clearConsole();
ConsolePlugin.getDefault().getConsoleManager().showConsoleView(lambdaConsole);
@@ -233,27 +232,6 @@ private static void showLambdaLiveLog(InvokeResult result, MessageConsoleStream
}
}
- private static MessageConsole getOrCreateLambdaConsoleIfNotExist(String consoleName) {
- IConsoleManager consoleManager = ConsolePlugin.getDefault()
- .getConsoleManager();
-
- // Search existing consoles
- if (consoleManager.getConsoles() != null) {
- for (IConsole console : consoleManager.getConsoles()) {
- if (consoleName.equals(console.getName())
- && (console instanceof MessageConsole)) {
- return (MessageConsole)console;
- }
- }
- }
-
- // If not found, create a new console
- MessageConsole newConsole = new MessageConsole(consoleName, null);
- ConsolePlugin.getDefault().getConsoleManager()
- .addConsoles(new IConsole[] { newConsole });
- return newConsole;
- }
-
private static void updateFunctionCode(AWSLambda lambda, IProject project,
String funcName, String bucketName, MessageConsoleStream out)
throws IOException {
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalConsoleColorProvider.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalConsoleColorProvider.java
new file mode 100644
index 00000000..09af525f
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalConsoleColorProvider.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.launching;
+
+import org.eclipse.debug.ui.console.ConsoleColorProvider;
+import org.eclipse.swt.graphics.Color;
+
+import com.amazonaws.eclipse.lambda.ui.LambdaPluginColors;
+
+/**
+ * Color for SAM Local terminal output. The default color is red as SAM Local's output
+ * goes to stderr even they are not actually error messages. We override it to be black.
+ */
+public class SamLocalConsoleColorProvider extends ConsoleColorProvider {
+
+ @Override
+ public Color getColor(String streamIdentifer) {
+ // Make the output black.
+ return LambdaPluginColors.BLACK;
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalConsoleLineTracker.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalConsoleLineTracker.java
new file mode 100644
index 00000000..4702e7a2
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalConsoleLineTracker.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.launching;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.debug.ui.console.IConsole;
+import org.eclipse.debug.ui.console.IConsoleLineTracker;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.console.IHyperlink;
+
+import com.amazonaws.eclipse.core.util.WorkbenchUtils;
+import com.amazonaws.eclipse.lambda.LambdaPlugin;
+
+/**
+ * Recognize the console URL and convert it as a link to be clickable.
+ */
+public class SamLocalConsoleLineTracker implements IConsoleLineTracker {
+ // The pattern accept a four-digit IP address or localhost as a special IP address, with a port number at the end.
+ private static final String URL_LINK_PATTERN_STRING = "((http|https)://)?"
+ + "(((\\d|\\d{2}|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(\\d|\\d{2}|1\\d{2}|2[0-4]\\d|25[0-5])|localhost)"
+ + ":(6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5]|[0-5]?\\d{1,5})";
+
+ public static final Pattern URL_PATTERN = Pattern.compile(URL_LINK_PATTERN_STRING);
+
+ private IConsole console;
+
+ @Override
+ public void init(IConsole console) {
+ this.console = console;
+ }
+
+ @Override
+ public void lineAppended(IRegion line) {
+ try {
+ int offset = line.getOffset();
+ int length = line.getLength();
+ String text = console.getDocument().get(offset, length);
+ Matcher matcher = URL_PATTERN.matcher(text);
+
+ String url = null;
+ if (matcher.find()) {
+ url = matcher.group();
+ offset += matcher.start();
+ }
+
+ if (url != null) {
+ final String thisUrl = url;
+ console.addLink(new IHyperlink() {
+
+ @Override
+ public void linkExited() {}
+
+ @Override
+ public void linkEntered() {}
+
+ @Override
+ public void linkActivated() {
+ try {
+ WorkbenchUtils.openInternalBrowserAsEditor(new URL(thisUrl), PlatformUI.getWorkbench());
+ } catch (MalformedURLException e) {
+ LambdaPlugin.getDefault().logError("Cannot open the URL: " + thisUrl, e);
+ }
+ }
+ }, offset, url.length());
+ }
+ } catch (BadLocationException e) {
+ LambdaPlugin.getDefault().logError("Failed to process the console document.", e);
+ }
+ }
+
+ @Override
+ public void dispose() {
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalConstants.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalConstants.java
new file mode 100644
index 00000000..dba499b7
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalConstants.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.launching;
+
+/**
+ * Constants used in SAM Local feature, mostly are attribute constants of SAM Local Launch configurations.
+ */
+public class SamLocalConstants {
+
+ // Preference name for AWS SAM Local executable
+ public static String P_SAM_LOCAL_EXECUTABLE = "AwsSamLocalExecutable";
+
+ // SAM Local process type. This is configured in plugin.xml
+ public static String PROCESS_TYPE = "sam_local";
+
+ // Attribute names for executing a SAM Local command.
+ public static String A_SAM = "sam";
+ public static String A_MAVEN_GOALS = "maven-goals";
+ public static String A_ACTION = "action";
+ public static String A_PROJECT = "project";
+
+ // These attribute names must be identical to those of the command line parameter names
+ public static String A_PROFILE = "profile";
+ public static String A_TEMPLATE = "template";
+ public static String A_ENV_VARS = "env-vars";
+ public static String A_DEBUG_PORT = "debug-port";
+ public static String A_REGION = "region";
+ public static String A_CODE_URI = "code-uri";
+ public static String A_TIME_OUT = "timeout";
+
+ // Attributes for `sam local invoke` only
+ public static String A_LAMBDA_IDENTIFIER = "lambda-id";
+ public static String A_EVENT = "event";
+
+ // Attributes for `sam local start-api` only
+ public static String A_PORT = "port";
+ public static String A_HOST = "host";
+
+ // Doc links
+ public static String LINKS_INSTALL_SAM_LOCAL = "https://github.com/awslabs/aws-sam-local#installation";
+ public static String LINKS_SAM_LOCAL_ANNOUNCEMENT = "https://aws.amazon.com/about-aws/whats-new/2017/08/introducing-aws-sam-local-a-cli-tool-to-test-aws-lambda-functions-locally/";
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalDelegate.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalDelegate.java
new file mode 100644
index 00000000..086321ee
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalDelegate.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.launching;
+
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_ACTION;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_CODE_URI;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_DEBUG_PORT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_ENV_VARS;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_EVENT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_HOST;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_LAMBDA_IDENTIFIER;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_MAVEN_GOALS;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_PORT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_PROFILE;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_PROJECT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_REGION;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_SAM;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_TEMPLATE;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_TIME_OUT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.PROCESS_TYPE;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.ILaunchConfigurationDelegate;
+import org.eclipse.debug.core.model.IProcess;
+import org.eclipse.debug.ui.DebugUITools;
+import org.eclipse.ui.console.IOConsole;
+import org.eclipse.ui.console.IOConsoleOutputStream;
+
+import com.amazonaws.eclipse.core.mobileanalytics.AwsToolkitMetricType;
+import com.amazonaws.eclipse.core.mobileanalytics.MetricsDataModel;
+import com.amazonaws.eclipse.core.util.CliUtil;
+import com.amazonaws.eclipse.core.util.MavenBuildLauncher;
+import com.amazonaws.eclipse.core.util.PluginUtils;
+import com.amazonaws.eclipse.core.util.RemoteDebugLauncher;
+import com.amazonaws.eclipse.explorer.AwsAction;
+import com.amazonaws.eclipse.lambda.LambdaPlugin;
+import com.amazonaws.eclipse.lambda.launching.SamLocalExecution.LaunchMode;
+import com.amazonaws.eclipse.lambda.project.wizard.model.RunSamLocalDataModel;
+import com.amazonaws.eclipse.lambda.project.wizard.model.RunSamLocalDataModel.SamAction;
+import com.amazonaws.eclipse.lambda.serverless.Serverless;
+import com.amazonaws.eclipse.lambda.serverless.model.transform.ServerlessModel;
+import com.amazonaws.eclipse.lambda.ui.LambdaPluginColors;
+
+public class SamLocalDelegate implements ILaunchConfigurationDelegate {
+
+ @Override
+ public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor)
+ throws CoreException {
+
+ MetricsDataModel metricsDataModel = new MetricsDataModel(AwsToolkitMetricType.SAMLOCAL_LAUNCH);
+ try {
+ LaunchMode launchMode = LaunchMode.fromValue(mode);
+ metricsDataModel.addAttribute("LaunchMode", launchMode.getMode());
+ String projectName = configuration.getAttribute(A_PROJECT, (String) null);
+
+ if (projectName == null || projectName.isEmpty()) {
+ throw new IllegalArgumentException("The project name must be provided!");
+ }
+ IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
+ SubMonitor subMonitor = SubMonitor.convert(monitor, 100);
+
+ subMonitor.setTaskName("Running Maven build to generate the artifact...");
+ long startTime = System.currentTimeMillis();
+ new MavenBuildLauncher(project, configuration.getAttribute(A_MAVEN_GOALS, RunSamLocalDataModel.DEFAULT_MAVEN_GOALS),
+ subMonitor.newChild(30)).launch();
+ long endTime = System.currentTimeMillis();
+ metricsDataModel.addMetric("MavenBuildTimeMilli", (double)(endTime-startTime));
+ subMonitor.worked(30);
+
+ List commandLine = buildSamLocalCommandLine(launchMode, configuration.getAttributes());
+ Map envvar = new HashMap<>();
+ String regionValue = configuration.getAttribute(A_REGION, RunSamLocalDataModel.DEFAULT_REGION);
+ envvar.put("AWS_REGION", regionValue);
+ Process samLocalCliProcess = CliUtil.buildProcess(commandLine, envvar);
+
+ Map attributes = new HashMap<>();
+ attributes.put(IProcess.ATTR_PROCESS_TYPE, PROCESS_TYPE);
+ IProcess samLocalProcess = DebugPlugin.newProcess(launch, samLocalCliProcess, projectName, attributes);
+
+ IOConsole samLocalConsole = (IOConsole) DebugUITools.getConsole(samLocalProcess);
+ IOConsoleOutputStream samLocalOutputStream = samLocalConsole.newOutputStream();
+ samLocalOutputStream.setColor(LambdaPluginColors.GREY);
+ safeWriteToConsole(samLocalOutputStream, "Running command: " + commandLine.stream().collect(Collectors.joining(" ")));
+
+ int debugPort = Integer.parseInt(configuration.getAttribute(A_DEBUG_PORT, String.valueOf(RunSamLocalDataModel.DEFAULT_DEBUG_PORT)));
+ SamAction command = SamAction.fromValue(configuration.getAttribute(A_ACTION, SamAction.INVOKE.getName()));
+ subMonitor.worked(20);
+
+ metricsDataModel.addAttribute("SamLocalCommand", command.getName());
+ if (command == SamAction.INVOKE) {
+ try {
+ waitAndLaunchDebugger(project, launchMode, debugPort, samLocalCliProcess, subMonitor, samLocalOutputStream, 50);
+ } catch (CoreException e) {
+ samLocalProcess.terminate();
+ throw new RuntimeException("Failed to launch Eclipse Debugger", e);
+ }
+ } else if (command == SamAction.START_API) {
+ try {
+ int invokeTimes = 0;
+ while (!subMonitor.isCanceled() && !samLocalProcess.isTerminated()) {
+ waitAndLaunchDebugger(project, launchMode, debugPort, samLocalCliProcess, subMonitor, samLocalOutputStream, 1);
+ ++invokeTimes;
+ }
+ metricsDataModel.addMetric("InvokeTimes", (double)invokeTimes);
+ } catch (CoreException e) {
+ samLocalProcess.terminate();
+ throw new RuntimeException("Failed to launch Eclipse Debugger", e);
+ }
+ }
+ if (subMonitor.isCanceled()) {
+ samLocalProcess.terminate();
+ }
+ while (!samLocalProcess.isTerminated()) {
+ try {
+ Thread.sleep(100L);
+ } catch (InterruptedException e) {
+ }
+ }
+ safeWriteToConsole(samLocalOutputStream, "SAM Local invocation done.");
+ subMonitor.done();
+ AwsAction.publishSucceededAction(metricsDataModel);
+ } catch (Exception e) {
+ LambdaPlugin.getDefault().reportException("Failed to launch SAM Local.", e);
+ AwsAction.publishFailedAction(metricsDataModel);
+ }
+ }
+
+ /**
+ * Wait the target port to be taken by the process and kick off Eclipse Debugger.
+ *
+ * @param project The target project the Debugger is running in
+ * @param mode debug or run mode
+ * @param debugPort The debug port
+ * @param samLocalCliProcess SAM Local process
+ * @param subMonitor {@link SubMonitor} for reporting the progress
+ * @param outputStream The console output stream for showing the current status
+ * @param totalWork Total work the Debugger takes
+ * @throws CoreException
+ */
+ private void waitAndLaunchDebugger(IProject project, LaunchMode mode, int debugPort,
+ Process samLocalCliProcess, SubMonitor subMonitor, IOConsoleOutputStream outputStream, int totalWork) throws CoreException {
+ if (mode == LaunchMode.DEBUG) {
+ String statusUpdateMessage = "Waiting for SAM Local to attach the port " + debugPort;
+ subMonitor.setTaskName(statusUpdateMessage);
+ safeWriteToConsole(outputStream, statusUpdateMessage);
+ }
+
+ if (waitForPortBeingTakenByProcess(samLocalCliProcess, debugPort, subMonitor)) {
+ String statusUpdateMessage = "Running Eclipse Debugger...";
+ subMonitor.setTaskName(statusUpdateMessage);
+ safeWriteToConsole(outputStream, statusUpdateMessage);
+ new RemoteDebugLauncher(project, debugPort, subMonitor.newChild(totalWork)).launch();
+ subMonitor.worked(totalWork);
+ }
+ }
+
+ private void safeWriteToConsole(IOConsoleOutputStream outputStream, String content) {
+ try {
+ outputStream.write("[AWS Toolkit] " + content + "\n");
+ } catch (IOException e) {
+ LambdaPlugin.getDefault().logError("Failed to write to SAM Local console.", e);
+ }
+ }
+
+ /**
+ * Create a temporary template file based on the provided configuration map. It modifies the
+ * template file in A_TEMPLATE with the overriding value of A_CODE_URI and A_TIME_OUT.
+ */
+ private String createTemporaryTemplateFile(Map config) throws CoreException {
+ String codeUri = (String) config.get(A_CODE_URI);
+ String timeOut = (String) config.get(A_TIME_OUT);
+ String project = (String) config.get(A_PROJECT);
+ String templateFile = (String) config.get(A_TEMPLATE);
+
+ String templateFilePath = PluginUtils.variablePluginReplace(templateFile);
+ try {
+ ServerlessModel serverlessModel = Serverless.load(templateFilePath);
+ serverlessModel.getServerlessFunctions().forEach((k, v) -> {
+ if (codeUri != null && !codeUri.isEmpty()) {
+ v.setCodeUri(codeUri);
+ }
+ v.setTimeout(Integer.parseInt(timeOut));
+ });
+
+ IPath tempTemplate = ResourcesPlugin.getWorkspace().getRoot().getProject(project).getLocation();
+
+ String outputPath = tempTemplate.append(".serverless.template").toOSString();
+ File outputFile = Serverless.write(serverlessModel, outputPath);
+ outputFile.deleteOnExit();
+ return outputPath;
+ } catch (IOException e ) {
+ // Any error occurs, fall back to the original template file.
+ return templateFilePath;
+ }
+ }
+
+ private List buildSamLocalCommandLine(LaunchMode mode, Map config) throws CoreException {
+ final Set invokeParamSet = new HashSet<>(Arrays.asList(A_TEMPLATE, A_ENV_VARS, A_EVENT, A_PROFILE));
+ final Set startApiParamSet = new HashSet<>(Arrays.asList(A_TEMPLATE, A_ENV_VARS, A_PROFILE, A_HOST, A_PORT));
+
+ if (mode == LaunchMode.DEBUG) {
+ invokeParamSet.add(A_DEBUG_PORT);
+ startApiParamSet.add(A_DEBUG_PORT);
+ }
+
+ List commandLine = new ArrayList<>();
+ commandLine.add(config.get(A_SAM).toString());
+ commandLine.add("local");
+ commandLine.add(config.get(A_ACTION).toString());
+
+ SamAction samAction = SamAction.fromValue(config.get(A_ACTION).toString());
+ switch (samAction) {
+ case INVOKE:
+ if (config.get(A_LAMBDA_IDENTIFIER) != null) {
+ commandLine.add(config.get(A_LAMBDA_IDENTIFIER).toString());
+ }
+ commandLine = buildCommandLineFromMap(commandLine, config, invokeParamSet);
+ break;
+ case START_API:
+ commandLine = buildCommandLineFromMap(commandLine, config, startApiParamSet);
+ break;
+ }
+
+ return commandLine;
+ }
+
+ private List buildCommandLineFromMap(List commandLine, Map parameters, Set parameterSet)
+ throws CoreException {
+ // Replacing these attributes with actually values from the path placeholder
+ Set replacableParams = new HashSet<>(Arrays.asList(A_TEMPLATE, A_EVENT, A_ENV_VARS));
+ for (Entry entry : parameters.entrySet()) {
+ String key = entry.getKey();
+ if (!parameterSet.contains(key)) {
+ continue;
+ }
+ String value = parameters.get(key).toString();
+ if (replacableParams.contains(key)) {
+ value = PluginUtils.variablePluginReplace(value);
+ }
+ commandLine.add("--" + key);
+ if (key.equals(A_TEMPLATE)) {
+ value = createTemporaryTemplateFile(parameters);
+ }
+ commandLine.add(value);
+ }
+ return commandLine;
+ }
+
+ // Wait for the port being take by the specified Process.
+ // Return true if the port is taken in the given condition, otherwise false.
+ private boolean waitForPortBeingTakenByProcess(Process process, int portNo, IProgressMonitor monitor) {
+ long pingIntervalMilli = 100;
+ while (process.isAlive() && !monitor.isCanceled()) {
+ try (Socket socket = new Socket("localhost", portNo)) {
+ return true;
+ } catch (UnknownHostException e1) {
+ return false;
+ } catch (IOException e1) {}
+
+ try {
+ Thread.sleep(pingIntervalMilli);
+ } catch (InterruptedException e) {}
+ }
+ return false;
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalExecution.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalExecution.java
new file mode 100644
index 00000000..2da4fdf1
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalExecution.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.launching;
+
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_PROJECT;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+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.ILaunchGroup;
+import org.eclipse.debug.ui.ILaunchShortcut;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+
+import com.amazonaws.eclipse.core.AwsToolkitCore;
+import com.amazonaws.eclipse.lambda.LambdaPlugin;
+
+public class SamLocalExecution implements ILaunchShortcut {
+ private static final String SAM_LOCAL_LAUNCH_CONFIGURATION_ID = "com.amazonaws.eclipse.lambda.launching.samLocal";
+
+ /**
+ * Launch SAM Local from project explorer
+ */
+ @Override
+ public void launch(ISelection selection, String mode) {
+ if (selection instanceof IStructuredSelection) {
+ IStructuredSelection structuredSelection = (IStructuredSelection ) selection;
+ Object firstElement = structuredSelection.getFirstElement();
+ if (firstElement instanceof IJavaElement) {
+ launch((IJavaElement)firstElement, LaunchMode.fromValue(mode));
+ }
+ }
+ }
+
+ /**
+ * Launch SAM Local from file editor
+ */
+ @Override
+ public void launch(IEditorPart editor, String mode) {
+ IEditorInput editorInput = editor.getEditorInput();
+ if (editorInput instanceof IFileEditorInput) {
+ IFile file = ((IFileEditorInput)editorInput).getFile();
+ IJavaElement element = JavaCore.create(file);
+ if (element != null) {
+ launch(element, LaunchMode.fromValue(mode));
+ }
+ }
+ }
+
+ /**
+ * Central place for all entries of invoking SAM Local.
+ */
+ public static void launch(IJavaElement javaElement, LaunchMode mode) {
+ try {
+ IProject project = javaElement.getJavaProject().getProject();
+ ILaunchConfiguration configuration = getLaunchConfiguration(project, mode);
+ if (configuration == null) {
+ configuration = createLaunchConfiguration(project, mode);
+ ILaunchGroup group = DebugUITools.getLaunchGroup(configuration, mode.getMode());
+ DebugUITools.openLaunchConfigurationDialog(Display.getCurrent().getActiveShell(),
+ configuration, group.getIdentifier(), null);
+ } else {
+ DebugUITools.launch(configuration, mode.getMode());
+ }
+ } catch (Exception e) {
+ LambdaPlugin.getDefault().reportException("Failed to run SAM Locol", e);
+ }
+ }
+
+ /**
+ * Create a new {@link ILaunchConfiguration} for the specified project.
+ */
+ private static ILaunchConfiguration createLaunchConfiguration(IContainer basedir, LaunchMode mode) throws CoreException {
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType launchConfigurationType = launchManager.getLaunchConfigurationType(SAM_LOCAL_LAUNCH_CONFIGURATION_ID);
+
+ String configName = launchManager.generateLaunchConfigurationName("AWS SAM Local " + basedir.getName());
+ ILaunchConfigurationWorkingCopy workingCopy = launchConfigurationType.newInstance(null, configName);
+
+ workingCopy.setAttribute(A_PROJECT, basedir.getName());
+
+ return workingCopy.doSave();
+ }
+
+ /**
+ * Returns an existing {@link ILaunchConfiguration} based on the provided project.
+ *
+ * @return The only existing one or the chosen one {@link ILaunchConfiguration}, or null if no one exists.
+ * @throws CoreException
+ */
+ private static ILaunchConfiguration getLaunchConfiguration(IContainer basedir, LaunchMode mode) throws CoreException {
+ ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
+ ILaunchConfigurationType launchConfigurationType = launchManager
+ .getLaunchConfigurationType(SAM_LOCAL_LAUNCH_CONFIGURATION_ID);
+
+ if (launchConfigurationType == null) {
+ return null;
+ }
+
+ // Scan existing launch configurations
+ ILaunchConfiguration[] launchConfigurations = launchManager.getLaunchConfigurations(launchConfigurationType);
+
+ List matchingConfigs = Arrays.stream(launchConfigurations).filter(config -> {
+ try {
+ String projectName = config.getAttribute(A_PROJECT, (String) null);
+ return projectName != null && projectName.equals(basedir.getName());
+ } catch (CoreException e) {
+ return false;
+ }
+ }).collect(Collectors.toList());
+
+ if (matchingConfigs.size() == 1) {
+ return matchingConfigs.get(0);
+ } else if (matchingConfigs.size() > 1) {
+ IDebugModelPresentation labelProvider = DebugUITools.newDebugModelPresentation();
+ ElementListSelectionDialog dialog = new ElementListSelectionDialog(
+ AwsToolkitCore.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(), labelProvider);
+ dialog.setElements(matchingConfigs.toArray(new ILaunchConfiguration[matchingConfigs.size()]));
+ dialog.setTitle("Select SAM Local Configuration");
+ dialog.setMessage("Choose which launch profile to use");
+ dialog.setMultipleSelection(false);
+ int result = dialog.open();
+ labelProvider.dispose();
+ return result == Window.OK ? (ILaunchConfiguration) dialog.getFirstResult() : null;
+ }
+ return null;
+ }
+
+ public static enum LaunchMode {
+ RUN("run"),
+ DEBUG("debug"),
+ ;
+
+ private final String mode;
+ private LaunchMode(String mode) {
+ this.mode = mode;
+ }
+
+ public static LaunchMode fromValue(String mode) {
+ for (LaunchMode launchMode : LaunchMode.values()) {
+ if (launchMode.getMode().equals(mode)) {
+ return launchMode;
+ }
+ }
+ throw new IllegalArgumentException(mode + " is not a valid mode for running AWS SAM Local.");
+ }
+
+ public String getMode() {
+ return mode;
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalPathFinder.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalPathFinder.java
new file mode 100644
index 00000000..c99e91d8
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalPathFinder.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.launching;
+
+import java.io.File;
+import java.util.Arrays;
+
+import org.apache.maven.model.Model;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.m2e.core.MavenPlugin;
+
+import com.amazonaws.eclipse.core.util.PluginUtils;
+
+public class SamLocalPathFinder {
+ private static final String[] TEMPLATE_FILES = {
+ "template.yaml", "template.yml", "serverless.template", "serverless.json", "template.json", "sam.template", "sam.json"
+ };
+ private static final String[] ENV_VAR_FILES = {"envvars.json", "envvar.json", "env-vars.json"};
+ private static final String[] EVENT_FILES = {"s3-event.json", "sns-event.json", "kinesis-event.json", "dynamodb-event.json", "api-event.json", "schedule-event.json"};
+
+ public static String findTemplateFile(String projectName) {
+ return find(projectName, TEMPLATE_FILES);
+ }
+
+ public static String findEnvVarFile(String projectName) {
+ return find(projectName, ENV_VAR_FILES);
+ }
+
+ public static String findEventFile(String projectName) {
+ return find(projectName, EVENT_FILES);
+ }
+
+ /**
+ * Parse the project pom.xml file to generate the location for the build artifact.
+ */
+ public static String findCodeUri(String projectName) {
+ if (projectName == null || projectName.isEmpty()) {
+ return "";
+ }
+
+ IFile pomFile = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName).getFile("pom.xml");
+ if (pomFile.exists()) {
+ try {
+ Model mavenModel = MavenPlugin.getMavenModelManager().readMavenModel(pomFile);
+
+ String artifactId = mavenModel.getArtifactId();
+ String version = mavenModel.getVersion();
+ return String.format("./target/%s-%s.jar", artifactId, version);
+ } catch (CoreException e) {
+ return "";
+ }
+ }
+
+ return "";
+ }
+
+ private static String variablePluginReplace(String path) {
+ try {
+ return PluginUtils.variablePluginReplace(path);
+ } catch (CoreException e) {
+ return path;
+ }
+ }
+
+ private static boolean fileExists(String path) {
+ File file = new File(path);
+ return file.exists() && file.isFile();
+ }
+
+ private static String find(String projectName, String[] candidatePaths) {
+ if (projectName == null || projectName.isEmpty()) {
+ return "";
+ }
+ return Arrays.stream(candidatePaths)
+ .map(subpath -> PluginUtils.variablePluginGenerateWorkspacePath(projectName + "/" + subpath))
+ .filter(path -> fileExists(variablePluginReplace(path)))
+ .findFirst().orElse("");
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalTab.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalTab.java
new file mode 100644
index 00000000..b4b752b3
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalTab.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.launching;
+
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_ACTION;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_CODE_URI;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_DEBUG_PORT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_ENV_VARS;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_EVENT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_HOST;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_LAMBDA_IDENTIFIER;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_MAVEN_GOALS;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_PORT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_PROFILE;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_PROJECT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_REGION;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_SAM;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_TEMPLATE;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_TIME_OUT;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.eclipse.core.databinding.AggregateValidationStatus;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.PojoProperties;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTab;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.forms.events.ExpansionAdapter;
+import org.eclipse.ui.forms.events.ExpansionEvent;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+
+import com.amazonaws.eclipse.core.AwsToolkitCore;
+import com.amazonaws.eclipse.core.regions.RegionUtils;
+import com.amazonaws.eclipse.core.regions.ServiceAbbreviations;
+import com.amazonaws.eclipse.core.ui.AccountSelectionComposite;
+import com.amazonaws.eclipse.core.ui.ImportFileComposite;
+import com.amazonaws.eclipse.core.ui.RegionComposite;
+import com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory;
+import com.amazonaws.eclipse.core.util.PluginUtils;
+import com.amazonaws.eclipse.core.validator.FilePathValidator;
+import com.amazonaws.eclipse.core.validator.IntegerRangeValidator;
+import com.amazonaws.eclipse.core.validator.WorkspacePathValidator;
+import com.amazonaws.eclipse.core.widget.ComboViewerComplex;
+import com.amazonaws.eclipse.core.widget.TextComplex;
+import com.amazonaws.eclipse.databinding.NotEmptyValidator;
+import com.amazonaws.eclipse.lambda.LambdaConstants;
+import com.amazonaws.eclipse.lambda.LambdaPlugin;
+import com.amazonaws.eclipse.lambda.dialog.SamLocalGenerateEventDialog;
+import com.amazonaws.eclipse.lambda.dialog.SamLocalGenerateEventDialog.SamLocalLambdaEventDataModel;
+import com.amazonaws.eclipse.lambda.preferences.SamLocalPreferencePage;
+import com.amazonaws.eclipse.lambda.project.wizard.model.RunSamLocalDataModel;
+import com.amazonaws.eclipse.lambda.project.wizard.model.RunSamLocalDataModel.SamAction;
+import com.amazonaws.eclipse.lambda.project.wizard.model.RunSamLocalDataModel.SamLocalInvokeFunctionDataModel;
+import com.amazonaws.eclipse.lambda.project.wizard.model.RunSamLocalDataModel.SamLocalStartApiDataModel;
+import com.amazonaws.eclipse.lambda.serverless.model.transform.ServerlessModel;
+import com.amazonaws.eclipse.lambda.serverless.validator.ServerlessTemplateFilePathValidator;
+
+public class SamLocalTab extends AbstractLaunchConfigurationTab {
+ private final RunSamLocalDataModel dataModel = new RunSamLocalDataModel();
+
+ private final SamLocalInvokeFunctionDataModel invokeDataModel = new SamLocalInvokeFunctionDataModel();
+ private final SamLocalStartApiDataModel startApiDataModel = new SamLocalStartApiDataModel();
+
+ private final DataBindingContext bindingContext;
+ private final AggregateValidationStatus aggregateValidationStatus;
+
+ // UI
+ // AWS specific UI
+ private AccountSelectionComposite accountComposite;
+ private RegionComposite regionComposite;
+
+ // SAM Local UI for common settings
+ private ImportFileComposite workspaceComposite;
+ private TextComplex mavenGoalsComplex;
+ private TextComplex samLocalExecutableComplex;
+ private ImportFileComposite templateFileComposite;
+ private ImportFileComposite envvarFileComposite;
+ private TextComplex debugPortComplex;
+
+ // UI for Lambda function settings
+ private TextComplex codeUriComplex;
+ private TextComplex timeoutComplex;
+
+ private ComboViewerComplex samCommandsComboViewer;
+ private Group samCommandGroup;
+
+ // SAM Local invoke specific UI
+ private ComboViewerComplex lambdaPhysicalIdCombo;
+ private ImportFileComposite eventFileComposite;
+
+ // SAM Local start-api specific UI
+ private TextComplex hostComplex;
+ private TextComplex portComplex;
+
+ private ExpandableComposite advancedSettingsExpandable;
+
+ public SamLocalTab() {
+ this.bindingContext = new DataBindingContext();
+ this.aggregateValidationStatus = new AggregateValidationStatus(
+ bindingContext, AggregateValidationStatus.MAX_SEVERITY);
+ }
+
+ @Override
+ public boolean isValid(ILaunchConfiguration launchConfig) {
+ setErrorMessage(null);
+
+ IStatus status = getValidationStatus();
+ if (status == null) {
+ return true;
+ }
+
+ if (status.getSeverity() != IStatus.OK) {
+ setErrorMessage(status.getMessage());
+ return false;
+ }
+ return true;
+ }
+
+ private void entriesChanges() {
+ setDirty(true);
+ updateLaunchConfigurationDialog();
+ }
+
+ private IStatus getValidationStatus() {
+ if (aggregateValidationStatus == null) return null;
+ Object value = aggregateValidationStatus.getValue();
+ if (!(value instanceof IStatus)) return null;
+ return (IStatus)value;
+ }
+
+ private void createAwsScopeGroup(Composite composite) {
+ Group group = WizardWidgetFactory.newGroup(composite, "AWS Configuration");
+ accountComposite = new AccountSelectionComposite(group, SWT.NONE);
+ accountComposite.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ dataModel.setAccount(AwsToolkitCore.getDefault().getAccountManager()
+ .getAccountInfo(accountComposite.getSelectedAccountId()));
+ entriesChanges();
+ }
+ });
+ regionComposite = RegionComposite.builder()
+ .parent(group)
+ .bindingContext(bindingContext)
+ .dataModel(dataModel.getRegionDataModel())
+ .serviceName(ServiceAbbreviations.LAMBDA)
+ .addListener(e -> entriesChanges())
+ .build();
+ }
+
+ private void createAdvancedSettingsGroup(Composite composite) {
+ Group group = WizardWidgetFactory.newGroup(composite, "SAM Local Configuration");
+
+ mavenGoalsComplex = TextComplex.builder(WizardWidgetFactory.newComposite(group, 1, 2, false),
+ bindingContext, PojoProperties.value(RunSamLocalDataModel.P_MAVEN_GOALS).observe(dataModel))
+ .defaultValue(dataModel.getMavenGoals())
+ .labelValue("Maven goals: ")
+ .addValidator(new NotEmptyValidator("Maven goals must not be empty!"))
+ .modifyListener(e -> entriesChanges())
+ .textMessage("Maven goals for generating the uber jar of your Lambda project.")
+ .build();
+
+ Composite samLocalRuntimeComposite = WizardWidgetFactory.newComposite(group, 1, 3);
+ samLocalExecutableComplex = TextComplex.builder(
+ samLocalRuntimeComposite, bindingContext, PojoProperties.value(
+ RunSamLocalDataModel.P_SAM_RUNTIME).observe(dataModel))
+ .createLabel(true)
+ .labelValue("SAM runtime: ")
+ .addValidator(new FilePathValidator("SAM runtime"))
+ .textMessage("AWS SAM Local executable path.")
+ .modifyListener(e -> entriesChanges())
+ .build();
+
+ // Only disable this Text widget but not the validation.
+ samLocalExecutableComplex.getText().setEditable(false);
+
+ Link link = new Link(samLocalRuntimeComposite, SWT.NONE);
+ link.setText("Configure AWS SAM Local...");
+ link.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ PreferencesUtil.createPreferenceDialogOn(Display.getDefault().getActiveShell(),
+ SamLocalPreferencePage.ID, new String[] { SamLocalPreferencePage.ID }, null).open();
+ samLocalExecutableComplex.setText(LambdaPlugin.getDefault().getPreferenceStore().getString(
+ SamLocalConstants.P_SAM_LOCAL_EXECUTABLE));
+ }
+ });
+
+ debugPortComplex = TextComplex.builder(WizardWidgetFactory.newComposite(group, 1, 2, false),
+ bindingContext, PojoProperties.value(RunSamLocalDataModel.P_DEBUG_PORT).observe(dataModel))
+ .defaultValue(String.valueOf(dataModel.getDebugPort()))
+ .labelValue("Debug port: ")
+ .addValidator(new IntegerRangeValidator("Debug port", 1024, 65535))
+ .modifyListener(e -> entriesChanges())
+ .textMessage("When specified, Lambda function container will start in debug mode and will expose this port on localhost.")
+ .build();
+
+ envvarFileComposite = ImportFileComposite.builder(group, bindingContext, dataModel.getEnvvarFileLocationDataModel())
+ .textLabel("Env vars: ")
+ .filePathValidator(new WorkspacePathValidator("Env vars file", true))
+ .modifyListener(e -> entriesChanges())
+ .textMessage("JSON file containing values for Lambda function's environment variables.")
+ .buildWorkspaceFileBrowser();
+ }
+
+ private void createLambdaFunctionGroup(Composite composite) {
+ Group group = WizardWidgetFactory.newGroup(composite, "Lambda Function Configuration");
+
+ codeUriComplex = TextComplex.builder(WizardWidgetFactory.newComposite(group, 1, 2, false),
+ bindingContext, PojoProperties.value(RunSamLocalDataModel.P_CODE_URI).observe(dataModel))
+ .labelValue("Code URI: ")
+ .modifyListener(e -> entriesChanges())
+ .textMessage("Location to the function code as a Lambda deployment package.")
+ .build();
+
+ timeoutComplex = TextComplex.builder(WizardWidgetFactory.newComposite(group, 1, 2, false),
+ bindingContext, PojoProperties.value(RunSamLocalDataModel.P_TIME_OUT).observe(dataModel))
+ .defaultValue(String.valueOf(RunSamLocalDataModel.DEFAULT_TIME_OUT))
+ .addValidator(new IntegerRangeValidator("Lambda function timeout", 0, 300))
+ .labelValue("Timeout (secs): ")
+ .modifyListener(e -> entriesChanges())
+ .textMessage("Lambda function execution time (in seconds) after which Lambda terminates the function.")
+ .build();
+ }
+
+ private void createSamLocalConfigSection(Composite composite) {
+ Group group = WizardWidgetFactory.newGroup(composite, "SAM Local Configuration");
+
+ WizardWidgetFactory.newLink(
+ group,
+ LambdaConstants.webLinkListener,
+ "AWS Toolkit for Eclipse is using AWS SAM Local for locally debugging your Lambda function. You need to preinstall Docker and SAM for this feature.", 1, 100, 30);
+
+ workspaceComposite = ImportFileComposite.builder(group, bindingContext, dataModel.getWorkspaceDataModel())
+ .textLabel("Project:")
+ .filePathValidator(new NotEmptyValidator("Project must be specified!"))
+ .modifyListener(e -> {
+ entriesChanges();
+ onProjectSelectChanged();
+ })
+ .textMessage("Target SAM project which must be a Maven project.")
+ .buildWorkspaceProjectBrowser();
+
+ templateFileComposite = ImportFileComposite.builder(group, bindingContext, dataModel.getTemplateFileLocationDataModel())
+ .textLabel("Template:")
+ .filePathValidator(new ServerlessTemplateFilePathValidator())
+ .modifyListener(e -> {
+ entriesChanges();
+ onSamLocalTemplateFileChanged();
+ })
+ .textMessage("AWS SAM template file (default: \"serverless.[template|json], sam.[template|json]\")")
+ .buildWorkspaceFileBrowser();
+
+ samCommandsComboViewer = ComboViewerComplex.builder()
+ .composite(WizardWidgetFactory.newComposite(group, 1, 2, false))
+ .bindingContext(bindingContext)
+ .pojoObservableValue(PojoProperties.value(RunSamLocalDataModel.P_SAM_ACTION).observe(dataModel))
+ .addListeners((e) -> {
+ onSamCommandsComboViewerSelect();
+ entriesChanges();
+ })
+ .items(SamAction.toList())
+ .labelValue("Run as: ")
+ .labelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof SamAction) {
+ SamAction samAction = (SamAction) element;
+ return samAction.getDescription();
+ } else {
+ return super.getText(element);
+ }
+ }
+ })
+ .build();
+ }
+
+ private void onSamCommandsComboViewerSelect() {
+ switch (dataModel.getSamAction()) {
+ case INVOKE:
+ dataModel.setActionDataModel(invokeDataModel);
+ createInvokeSection();
+ break;
+ case START_API:
+ dataModel.setActionDataModel(startApiDataModel);
+ createStartApiSection();
+ break;
+ }
+ }
+
+ private void onProjectSelectChanged() {
+ String projectName = dataModel.getWorkspaceDataModel().getFilePath();
+ String codeUri = SamLocalPathFinder.findCodeUri(projectName);
+ codeUriComplex.setText(codeUri);
+ }
+
+ private void onSamLocalTemplateFileChanged() {
+ if (lambdaPhysicalIdCombo == null) {
+ return;
+ }
+
+ Collection data = getLambdaFunctionPhysicalIDs();
+ lambdaPhysicalIdCombo.getComboViewer().setInput(data);
+ if (!data.isEmpty()) {
+ lambdaPhysicalIdCombo.selectItem(data.iterator().next());
+ }
+ lambdaPhysicalIdCombo.getComboViewer().refresh();
+ }
+
+ private static final String UNAVAILABLE_PHYSICAL_ID = "Lambda function not found...";
+
+ private Collection getLambdaFunctionPhysicalIDs() {
+ ServerlessTemplateFilePathValidator validator = new ServerlessTemplateFilePathValidator();
+ try {
+ ServerlessModel model = validator.validateFilePath(dataModel.getTemplateFileLocationDataModel().getFilePath());
+ return model.getServerlessFunctions().keySet();
+ } catch (Exception e) {
+ return Arrays.asList(UNAVAILABLE_PHYSICAL_ID);
+ }
+ }
+
+ private void createSamLocalActionSection(Composite parent) {
+ samCommandGroup = WizardWidgetFactory.newGroup(parent, "SAM Local Command Configuration");
+ onSamCommandsComboViewerSelect();
+ }
+
+ private void createInvokeSection() {
+ for (Control control : samCommandGroup.getChildren()) {
+ control.dispose();
+ }
+
+ hostComplex = null;
+ portComplex = null;
+
+ lambdaPhysicalIdCombo = ComboViewerComplex.builder()
+ .bindingContext(bindingContext)
+ .composite(WizardWidgetFactory.newComposite(samCommandGroup, 1, 2, false))
+ .labelValue("Function identifier: ")
+ .labelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ if (element instanceof String) {
+ String text = (String) element;
+ return text;
+ }
+ return super.getText(element);
+ }
+ })
+ .pojoObservableValue(PojoProperties.value(invokeDataModel.P_LAMBDA_IDENTIFIER).observe(invokeDataModel))
+ .build();
+ onSamLocalTemplateFileChanged();
+
+ Composite composite = WizardWidgetFactory.newComposite(samCommandGroup, 1, 2, false);
+ eventFileComposite = ImportFileComposite.builder(composite, bindingContext, invokeDataModel.getEventFileLocationDataModel())
+ .textLabel("Event:")
+ .filePathValidator(new WorkspacePathValidator("Event", false))
+ .modifyListener(e -> entriesChanges())
+ .textMessage("JSON file containing event data passed to the Lambda function during invoke")
+ .buildWorkspaceFileBrowser();
+ Button generateEventButton = WizardWidgetFactory.newPushButton(composite, "Generate", 1);
+ generateEventButton.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ SamLocalGenerateEventDialog generateEventDialog = new SamLocalGenerateEventDialog(getShell());
+ int returnValue = generateEventDialog.open();
+ if (returnValue == Window.OK) {
+ SamLocalLambdaEventDataModel dataModel = generateEventDialog.getDataModel();
+ IPath resultPath = dataModel.getResultPath();
+ eventFileComposite.setFilePath(PluginUtils.variablePluginGenerateWorkspacePath(resultPath));
+ }
+ }
+ });
+ samCommandGroup.layout();
+ }
+
+ private void createStartApiSection() {
+ for (Control control : samCommandGroup.getChildren()) {
+ control.dispose();
+ }
+
+ lambdaPhysicalIdCombo = null;
+ eventFileComposite = null;
+
+ Composite composite = WizardWidgetFactory.newComposite(samCommandGroup, 1, 2);
+ hostComplex = TextComplex.builder(composite, bindingContext, PojoProperties.value(SamLocalStartApiDataModel.P_HOST).observe(startApiDataModel))
+ .defaultValue(startApiDataModel.getHost())
+ .labelValue("Host:")
+ .textMessage("Local hostname or IP address to bind to (default: \"127.0.0.1\")")
+ .modifyListener(e -> entriesChanges())
+ .build();
+
+ portComplex = TextComplex.builder(composite, bindingContext, PojoProperties.value(SamLocalStartApiDataModel.P_PORT).observe(startApiDataModel))
+ .defaultValue(String.valueOf(startApiDataModel.getPort()))
+ .labelValue("Port:")
+ .textMessage("Local port number to listen on (default: \"3000\")")
+ .modifyListener(e -> entriesChanges())
+ .build();
+
+ samCommandGroup.layout();
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ Composite rootComposite = WizardWidgetFactory.newComposite(parent, 1, 1);
+ setControl(rootComposite);
+
+ rootComposite.setLayout(new GridLayout(1, false));
+
+ createSamLocalConfigSection(rootComposite);
+ createSamLocalActionSection(rootComposite);
+
+ createAdvancedSettingSection(rootComposite);
+ dataModel.setAccount(AwsToolkitCore.getDefault().getAccountManager()
+ .getAccountInfo(accountComposite.getSelectedAccountId()));
+ }
+
+ private void createAdvancedSettingSection(final Composite parent) {
+ advancedSettingsExpandable = new ExpandableComposite(parent, SWT.NONE,
+ ExpandableComposite.TWISTIE | ExpandableComposite.COMPACT | ExpandableComposite.EXPANDED);
+ advancedSettingsExpandable.setText("Advanced Settings");
+ advancedSettingsExpandable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ Composite expandableComposite = WizardWidgetFactory.newComposite(advancedSettingsExpandable, 1, 1);
+ advancedSettingsExpandable.setClient(expandableComposite);
+ advancedSettingsExpandable.addExpansionListener(new ExpansionAdapter() {
+ @Override
+ public void expansionStateChanged(ExpansionEvent e) {
+ Shell shell = parent.getShell();
+ shell.layout(true, true);
+ }
+ });
+
+ createAwsScopeGroup(expandableComposite);
+ createAdvancedSettingsGroup(expandableComposite);
+ createLambdaFunctionGroup(expandableComposite);
+ }
+
+ @Override
+ public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
+ }
+
+ @Override
+ public void initializeFrom(ILaunchConfiguration configuration) {
+ accountComposite.selectAccountName(getAttribute(configuration, A_PROFILE, RunSamLocalDataModel.DEFAULT_PROFILE));
+ dataModel.setAccount(AwsToolkitCore.getDefault().getAccountManager()
+ .getAccountInfo(accountComposite.getSelectedAccountId()));
+
+ regionComposite.selectAwsRegion(RegionUtils.getRegion(getAttribute(configuration, A_REGION, RunSamLocalDataModel.DEFAULT_REGION)));
+ String projectName = getAttribute(configuration, A_PROJECT, "");
+ workspaceComposite.setFilePath(projectName);
+
+ mavenGoalsComplex.setText(getAttribute(configuration, A_MAVEN_GOALS, RunSamLocalDataModel.DEFAULT_MAVEN_GOALS));
+
+ samLocalExecutableComplex.setText(getAttribute(configuration, A_SAM,
+ LambdaPlugin.getDefault().getPreferenceStore().getString(SamLocalConstants.P_SAM_LOCAL_EXECUTABLE)));
+
+ debugPortComplex.setText(getAttribute(configuration, A_DEBUG_PORT, String.valueOf(RunSamLocalDataModel.DEFAULT_DEBUG_PORT)));
+ templateFileComposite.setFilePath(getAttribute(configuration, A_TEMPLATE, SamLocalPathFinder.findTemplateFile(projectName)));
+ envvarFileComposite.setFilePath(getAttribute(configuration, A_ENV_VARS, ""));
+
+ codeUriComplex.setText(getAttribute(configuration, A_CODE_URI, SamLocalPathFinder.findCodeUri(projectName)));
+ timeoutComplex.setText(getAttribute(configuration, A_TIME_OUT, String.valueOf(RunSamLocalDataModel.DEFAULT_TIME_OUT)));
+
+ SamAction samCommand = SamAction.fromValue(getAttribute(configuration, A_ACTION, SamAction.INVOKE.getName()));
+ samCommandsComboViewer.selectItem(samCommand);
+
+ switch (samCommand) {
+ case INVOKE:
+ lambdaPhysicalIdCombo.selectItem(getAttribute(configuration, A_LAMBDA_IDENTIFIER, (String) null));
+ eventFileComposite.setFilePath(getAttribute(configuration, A_EVENT, SamLocalPathFinder.findEventFile(projectName)));
+ break;
+ case START_API:
+ hostComplex.setText(getAttribute(configuration, A_HOST, SamLocalStartApiDataModel.DEFAULT_HOST));
+ portComplex.setText(getAttribute(configuration, A_PORT, String.valueOf(SamLocalStartApiDataModel.DEFAULT_PORT)));
+ break;
+ }
+
+ advancedSettingsExpandable.setExpanded(false);
+ setDirty(false);
+ }
+
+ @Override
+ public void performApply(ILaunchConfigurationWorkingCopy configuration) {
+ configuration.setAttribute(A_PROFILE, dataModel.getAccount().getAccountName());
+ configuration.setAttribute(A_REGION, dataModel.getRegionDataModel().getRegionId());
+ configuration.setAttribute(A_PROJECT, dataModel.getWorkspaceDataModel().getFilePath());
+ configuration.setAttribute(A_MAVEN_GOALS, dataModel.getMavenGoals());
+ configuration.setAttribute(A_SAM, dataModel.getSamRuntime());
+ configuration.setAttribute(A_DEBUG_PORT, String.valueOf(dataModel.getDebugPort()));
+ configuration.setAttribute(A_TEMPLATE, dataModel.getTemplateFileLocationDataModel().getFilePath());
+ configuration.setAttribute(A_ENV_VARS, dataModel.getEnvvarFileLocationDataModel().getFilePath());
+
+ configuration.setAttribute(A_CODE_URI, dataModel.getCodeUri());
+ configuration.setAttribute(A_TIME_OUT, String.valueOf(dataModel.getTimeOut()));
+
+ SamAction samAction = dataModel.getSamAction();
+ configuration.setAttribute(A_ACTION, samAction.getName());
+ dataModel.getActionDataModel().toAttributeMap().forEach(configuration::setAttribute);
+ }
+
+ @Override
+ public String getName() {
+ return "Main";
+ }
+
+ @Override
+ public Image getImage() {
+ return LambdaPlugin.getDefault().getImageRegistry().getDescriptor(LambdaPlugin.IMAGE_SAM_LOCAL).createImage();
+ }
+
+ private String getAttribute(ILaunchConfiguration configuration, String name, String defaultValue) {
+ try {
+ return configuration.getAttribute(name, defaultValue);
+ } catch (CoreException ex) {
+ return defaultValue;
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalTabGroup.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalTabGroup.java
new file mode 100644
index 00000000..b71d186d
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/launching/SamLocalTabGroup.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.launching;
+
+import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup;
+import org.eclipse.debug.ui.CommonTab;
+import org.eclipse.debug.ui.ILaunchConfigurationDialog;
+import org.eclipse.debug.ui.ILaunchConfigurationTab;
+
+public class SamLocalTabGroup extends AbstractLaunchConfigurationTabGroup {
+
+ @Override
+ public void createTabs(ILaunchConfigurationDialog dialog, String mode) {
+ setTabs(new ILaunchConfigurationTab[] {
+ new SamLocalTab(),
+ new CommonTab()
+ });
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/preferences/PreferenceInitializer.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/preferences/PreferenceInitializer.java
new file mode 100644
index 00000000..e1c92d7f
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/preferences/PreferenceInitializer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.preferences;
+
+import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import com.amazonaws.eclipse.core.util.OsPlatformUtils;
+import com.amazonaws.eclipse.lambda.LambdaPlugin;
+import com.amazonaws.eclipse.lambda.launching.SamLocalConstants;
+
+public class PreferenceInitializer extends AbstractPreferenceInitializer {
+ private static final String SAM_LOCAL_WINDOWS_DEFAULT_LOCATION =
+ String.format("C:\\Users\\%s\\AppData\\Roaming\\npm\\sam.exe", OsPlatformUtils.currentUser());
+ private static final String SAM_LOCAL_LINUX_DEFAULT_LOCATION = "/usr/local/bin/sam";
+
+ @Override
+ public void initializeDefaultPreferences() {
+ IPreferenceStore store = LambdaPlugin.getDefault().getPreferenceStore();
+ store.setDefault(SamLocalConstants.P_SAM_LOCAL_EXECUTABLE, getDefaultSamLocalLocation());
+ }
+
+ private String getDefaultSamLocalLocation() {
+ if (OsPlatformUtils.isWindows()) {
+ return SAM_LOCAL_WINDOWS_DEFAULT_LOCATION;
+ } else {
+ return SAM_LOCAL_LINUX_DEFAULT_LOCATION;
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/preferences/SamLocalPreferencePage.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/preferences/SamLocalPreferencePage.java
new file mode 100644
index 00000000..a9f44d07
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/preferences/SamLocalPreferencePage.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.preferences;
+
+import org.eclipse.core.databinding.AggregateValidationStatus;
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+import com.amazonaws.eclipse.core.model.ImportFileDataModel;
+import com.amazonaws.eclipse.core.ui.ImportFileComposite;
+import com.amazonaws.eclipse.core.ui.preferences.AwsToolkitPreferencePage;
+import com.amazonaws.eclipse.core.ui.wizards.WizardWidgetFactory;
+import com.amazonaws.eclipse.core.validator.FilePathValidator;
+import com.amazonaws.eclipse.lambda.LambdaConstants;
+import com.amazonaws.eclipse.lambda.LambdaPlugin;
+import com.amazonaws.eclipse.lambda.launching.SamLocalConstants;
+
+public class SamLocalPreferencePage extends AwsToolkitPreferencePage implements IWorkbenchPreferencePage {
+ private static final String PAGE_NAME = SamLocalPreferencePage.class.getName();
+ public static final String ID = "com.amazonaws.eclipse.lambda.preferences.SamLocalPreferencePage";
+
+ private SamLocalPreferencesDataModel dataModel = new SamLocalPreferencesDataModel();
+ private DataBindingContext dataBindingContext = new DataBindingContext();
+ private AggregateValidationStatus aggregateValidationStatus =
+ new AggregateValidationStatus(dataBindingContext, AggregateValidationStatus.MAX_SEVERITY);
+
+ private ImportFileComposite samLocalExecutableComposite;
+
+ public SamLocalPreferencePage() {
+ super(PAGE_NAME);
+ setDescription("AWS SAM Local Configuration");
+ this.dispose();
+ }
+
+ @Override
+ public void init(IWorkbench workbench) {
+ setPreferenceStore(LambdaPlugin.getDefault().getPreferenceStore());
+ aggregateValidationStatus.addChangeListener(e -> {
+ Object value = aggregateValidationStatus.getValue();
+ if (value instanceof IStatus == false) return;
+
+ IStatus status = (IStatus)value;
+ boolean success = (status.getSeverity() == IStatus.OK);
+ if (success) {
+ setErrorMessage(null);
+ } else {
+ setErrorMessage(status.getMessage());
+ }
+ setValid(success);
+ });
+ initDataModel();
+ }
+
+ private void initDataModel() {
+ dataModel.getSamLocalExecutable().setFilePath(
+ getPreferenceStore().getString(SamLocalConstants.P_SAM_LOCAL_EXECUTABLE));
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ Composite composite = WizardWidgetFactory.newComposite(parent, 1, 1);
+
+ WizardWidgetFactory.newLink(
+ composite,
+ LambdaConstants.webLinkListener,
+ "To install AWS SAM Local and the required dependencies, see Installation.", 1, 100, 30);
+
+ samLocalExecutableComposite = ImportFileComposite.builder(
+ composite, dataBindingContext, dataModel.getSamLocalExecutable())
+ .buttonLabel("Browse...")
+ .filePathValidator(new FilePathValidator("SAM Local Executbale"))
+ .textLabel("SAM Local Executable:")
+ .textMessage("Absolute path for sam command...")
+ .build();
+
+ return composite;
+ }
+
+ @Override
+ protected void performDefaults() {
+ if (samLocalExecutableComposite != null) {
+ samLocalExecutableComposite.setFilePath(
+ getPreferenceStore().getDefaultString(SamLocalConstants.P_SAM_LOCAL_EXECUTABLE));
+ }
+ super.performDefaults();
+ }
+
+ @Override
+ public boolean performOk() {
+ aggregateValidationStatus.dispose();
+ dataBindingContext.dispose();
+ onApplyButton();
+ return super.performOk();
+ }
+
+ @Override
+ protected void performApply() {
+ onApplyButton();
+ super.performApply();
+ }
+
+ private void onApplyButton() {
+ String filePath = dataModel.getSamLocalExecutable().getFilePath();
+ getPreferenceStore().setValue(SamLocalConstants.P_SAM_LOCAL_EXECUTABLE, filePath);
+ }
+
+ private static class SamLocalPreferencesDataModel {
+ private final ImportFileDataModel samLocalExecutable = new ImportFileDataModel();
+
+ public ImportFileDataModel getSamLocalExecutable() {
+ return samLocalExecutable;
+ }
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/NewServerlessProjectDataModel.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/NewServerlessProjectDataModel.java
index e56b74ef..421be8e2 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/NewServerlessProjectDataModel.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/NewServerlessProjectDataModel.java
@@ -155,7 +155,7 @@ private void buildServerlessModel() throws JsonParseException, JsonMappingExcept
ServerlessBlueprint blueprint = getSelectedBlueprint();
String content = CodeTemplateManager.processTemplateWithData(
CodeTemplateManager.getInstance().getServerlessSamTemplate(blueprint), getServerlessSamTemplateData());
- serverlessModel = Serverless.load(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
+ serverlessModel = Serverless.loadFromContent(content);
} else {
serverlessModel = Serverless.load(importFileDataModel.getFilePath());
}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/RunSamLocalDataModel.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/RunSamLocalDataModel.java
new file mode 100644
index 00000000..fae0b952
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/model/RunSamLocalDataModel.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.project.wizard.model;
+
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_EVENT;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_HOST;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_LAMBDA_IDENTIFIER;
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConstants.A_PORT;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.amazonaws.eclipse.core.AccountInfo;
+import com.amazonaws.eclipse.core.model.ImportFileDataModel;
+import com.amazonaws.eclipse.core.model.RegionDataModel;
+
+/**
+ * Data model for debugging Sam function using SAM Local.
+ */
+public class RunSamLocalDataModel {
+ public static final int DEFAULT_DEBUG_PORT = 5858;
+ public static final String DEFAULT_MAVEN_GOALS = "clean package";
+ public static final String DEFAULT_PROFILE = "default";
+ public static final String DEFAULT_REGION = "us-east-1";
+ public static final int DEFAULT_TIME_OUT = 300;
+
+ public static final String P_DEBUG_PORT = "debugPort";
+ public static final String P_SAM_ACTION = "samAction";
+ public static final String P_SAM_RUNTIME = "samRuntime";
+ public static final String P_MAVEN_GOALS = "mavenGoals";
+ public static final String P_CODE_URI = "codeUri";
+ public static final String P_TIME_OUT = "timeOut";
+
+ private final RegionDataModel regionDataModel = new RegionDataModel();
+ private final ImportFileDataModel workspaceDataModel = new ImportFileDataModel();
+ private final ImportFileDataModel templateFileLocationDataModel = new ImportFileDataModel();
+ private final ImportFileDataModel envvarFileLocationDataModel = new ImportFileDataModel();
+
+ private AccountInfo account;
+ private String mavenGoals = DEFAULT_MAVEN_GOALS;
+ private String samRuntime;
+ private int debugPort = DEFAULT_DEBUG_PORT;
+ private SamAction samAction = SamAction.INVOKE;
+ private AttributeMapConvertible actionDataModel;
+ private String codeUri;
+ private int timeOut = DEFAULT_TIME_OUT;
+
+ public String getMavenGoals() {
+ return mavenGoals;
+ }
+
+ public void setMavenGoals(String mavenGoals) {
+ this.mavenGoals = mavenGoals;
+ }
+
+ public String getSamRuntime() {
+ return samRuntime;
+ }
+
+ public void setSamRuntime(String samRuntime) {
+ this.samRuntime = samRuntime;
+ }
+
+ public String getCodeUri() {
+ return codeUri;
+ }
+
+ public void setCodeUri(String codeUri) {
+ this.codeUri = codeUri;
+ }
+
+ public int getTimeOut() {
+ return timeOut;
+ }
+
+ public void setTimeOut(int timeOut) {
+ this.timeOut = timeOut;
+ }
+
+ public enum SamAction {
+ INVOKE("invoke", "Lambda Function"),
+ START_API("start-api", "API Gateway"),
+ ;
+
+ // Action name must be a valid command line action `sam local ${action}`
+ private final String name;
+ private final String description;
+
+ private SamAction(String name, String description) {
+ this.name = name;
+ this.description = description;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public static List toList() {
+ return Arrays.asList(SamAction.values());
+ }
+
+ public static SamAction fromValue(String value) {
+ for (SamAction samAction : SamAction.values()) {
+ if (samAction.getName().equals(value)) {
+ return samAction;
+ }
+ }
+ return null;
+ }
+ }
+
+ public AccountInfo getAccount() {
+ return account;
+ }
+
+ public void setAccount(AccountInfo account) {
+ this.account = account;
+ }
+
+ public RegionDataModel getRegionDataModel() {
+ return regionDataModel;
+ }
+
+ public ImportFileDataModel getWorkspaceDataModel() {
+ return workspaceDataModel;
+ }
+
+ public ImportFileDataModel getTemplateFileLocationDataModel() {
+ return templateFileLocationDataModel;
+ }
+
+ public ImportFileDataModel getEnvvarFileLocationDataModel() {
+ return envvarFileLocationDataModel;
+ }
+
+ public int getDebugPort() {
+ return debugPort;
+ }
+
+ public void setDebugPort(int debugPort) {
+ this.debugPort = debugPort;
+ }
+
+ public AttributeMapConvertible getActionDataModel() {
+ return actionDataModel;
+ }
+
+ public void setActionDataModel(AttributeMapConvertible actionDataModel) {
+ this.actionDataModel = actionDataModel;
+ }
+
+ public SamAction getSamAction() {
+ return samAction;
+ }
+
+ public void setSamAction(SamAction samAction) {
+ this.samAction = samAction;
+ }
+
+ public static class SamLocalInvokeFunctionDataModel implements AttributeMapConvertible {
+ public static final String P_LAMBDA_IDENTIFIER = "lambdaIdentifier";
+
+ private String lambdaIdentifier;
+ private final ImportFileDataModel eventFileLocationDataModel = new ImportFileDataModel();
+
+ public String getLambdaIdentifier() {
+ return lambdaIdentifier;
+ }
+
+ public void setLambdaIdentifier(String lambdaIdentifier) {
+ this.lambdaIdentifier = lambdaIdentifier;
+ }
+
+ public ImportFileDataModel getEventFileLocationDataModel() {
+ return eventFileLocationDataModel;
+ }
+
+ @Override
+ public Map toAttributeMap() {
+ Map attributes = new HashMap<>();
+
+ attributes.put(A_EVENT, eventFileLocationDataModel.getFilePath());
+ attributes.put(A_LAMBDA_IDENTIFIER, lambdaIdentifier);
+
+ return attributes;
+ }
+ }
+
+ public static class SamLocalStartApiDataModel implements AttributeMapConvertible {
+ public static final int DEFAULT_PORT = 3000;
+ public static final String DEFAULT_HOST = "127.0.0.1";
+
+ public static final String P_PORT = "port";
+ public static final String P_HOST = "host";
+
+ private String host = DEFAULT_HOST;
+ private int port = DEFAULT_PORT;
+
+ public String getHost() {
+ return host;
+ }
+ public void setHost(String host) {
+ this.host = host;
+ }
+ public int getPort() {
+ return port;
+ }
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ @Override
+ public Map toAttributeMap() {
+ Map attributes = new HashMap<>();
+ attributes.put(A_HOST, host);
+ attributes.put(A_PORT, String.valueOf(port));
+ return attributes;
+ }
+ }
+
+ public static interface AttributeMapConvertible {
+ Map toAttributeMap();
+ }
+}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/page/NewServerlessProjectWizardPageOne.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/page/NewServerlessProjectWizardPageOne.java
index 094af36c..3fa379ee 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/page/NewServerlessProjectWizardPageOne.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/page/NewServerlessProjectWizardPageOne.java
@@ -203,8 +203,9 @@ private void onBlueprintSelectionViewerSelectionChange() {
}
private void createServerlessTemplateImportSection(Composite parent) {
- importFileComposite = new ImportFileComposite(
- parent, bindingContext, dataModel.getImportFileDataModel(), new ServerlessTemplateFilePathValidator());
+ importFileComposite = ImportFileComposite.builder(parent, bindingContext, dataModel.getImportFileDataModel())
+ .filePathValidator(new ServerlessTemplateFilePathValidator())
+ .build();
}
private void setServerlessTemplateImportSectionEnabled(boolean enabled) {
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/util/FunctionProjectUtil.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/util/FunctionProjectUtil.java
index ef5f716e..1f85f8e6 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/util/FunctionProjectUtil.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/util/FunctionProjectUtil.java
@@ -331,8 +331,7 @@ private static File addFileToProject(IPath targetPath, String fileName, String f
* Return the absolute path of the specified location relative to the project.
*/
public static IPath getProjectDirectory(IProject project, String path) {
- IPath workspaceRoot = project.getWorkspace().getRoot().getRawLocation();
- IPath projectRoot = workspaceRoot.append(project.getFullPath());
+ IPath projectRoot = project.getLocation();
if (path == null) {
return projectRoot;
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/util/LambdaFunctionComposite.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/util/LambdaFunctionComposite.java
index 95dbf4a3..7b0dbf40 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/util/LambdaFunctionComposite.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/project/wizard/util/LambdaFunctionComposite.java
@@ -114,10 +114,8 @@ public void createControl() {
}
public void createClassNameControl() {
- this.classNameComplex = TextComplex.builder()
- .composite(inputComposite)
- .dataBindingContext(dataBindingContext)
- .pojoObservableValue(PojoProperties.value(LambdaFunctionDataModel.class, P_CLASS_NAME).observe(dataModel))
+ this.classNameComplex = TextComplex.builder(inputComposite, dataBindingContext,
+ PojoProperties.value(P_CLASS_NAME).observe(dataModel))
.addValidator(new NotEmptyValidator("Please provide a valid class name for the handler"))
.labelValue("Class Name:")
.defaultValue(dataModel.getClassName())
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/Serverless.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/Serverless.java
index f93a4a3d..b3be669b 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/Serverless.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/Serverless.java
@@ -21,20 +21,24 @@
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.stream.Stream;
import com.amazonaws.eclipse.lambda.serverless.model.ServerlessTemplate;
import com.amazonaws.eclipse.lambda.serverless.model.TypeProperties;
import com.amazonaws.eclipse.lambda.serverless.model.transform.ServerlessFunction;
import com.amazonaws.eclipse.lambda.serverless.model.transform.ServerlessModel;
+import com.amazonaws.util.IOUtils;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
/**
* A class for processing serverless template file, including loading Serverless template file into a model,
@@ -42,34 +46,61 @@
*/
public class Serverless {
- private static final ObjectMapper MAPPER = new ObjectMapper();
+ private static final ObjectMapper JSON_MAPPER = initMapper(new ObjectMapper());
+ private static final ObjectMapper YAML_MAPPER = initMapper(new ObjectMapper(new YAMLFactory()));
- static {
- MAPPER.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
- MAPPER.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
- MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ private static ObjectMapper initMapper(ObjectMapper mapper) {
+ mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);
+ mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
+ mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ return mapper;
}
public static ServerlessModel load(String templatePath) throws JsonParseException, JsonMappingException, IOException {
return load(new File(templatePath));
}
+ // Load Serverless model from the given template file, and close the input stream safely.
public static ServerlessModel load(File templateFile) throws JsonParseException, JsonMappingException, IOException {
- return load(new FileInputStream(templateFile));
+ try (InputStream inputStream = new FileInputStream(templateFile)) {
+ return load(inputStream);
+ }
}
public static ServerlessModel load(InputStream templateInput) throws JsonParseException, JsonMappingException, IOException {
- ServerlessTemplate serverlessTemplate = MAPPER.readValue(templateInput, ServerlessTemplate.class);
+ String content = IOUtils.toString(templateInput);
+ return loadFromContent(content);
+ }
+
+ public static ServerlessModel loadFromContent(String content) throws JsonParseException, JsonMappingException, IOException {
+ ObjectMapper mapper = isContentInJsonFormat(content) ? JSON_MAPPER : YAML_MAPPER;
+ ServerlessTemplate serverlessTemplate = mapper.readValue(content.getBytes(StandardCharsets.UTF_8), ServerlessTemplate.class);
return convert(serverlessTemplate);
}
public static File write(ServerlessModel model, String path) throws JsonGenerationException, JsonMappingException, IOException {
File file = new File(path);
ServerlessTemplate template = convert(model);
- MAPPER.writerWithDefaultPrettyPrinter().writeValue(new FileWriter(file), template);
+ JSON_MAPPER.writerWithDefaultPrettyPrinter().writeValue(new FileWriter(file), template);
return file;
}
+ /**
+ * Return whether the underlying String content is in Json format based on the first non-whitespace character.
+ */
+ private static boolean isContentInJsonFormat(String content) {
+ if (content == null || content.isEmpty()) {
+ return false;
+ }
+ for (int index = 0; index < content.length(); ++index) {
+ if (Character.isWhitespace(content.charAt(index))) {
+ continue;
+ }
+ return '{' == content.charAt(index);
+ }
+ return false;
+ }
+
/**
* Make this raw Serverless model to one that could be recognized by CloudFormation.
*
@@ -148,7 +179,7 @@ private static void convertResource(ServerlessModel model, String resourceLogica
private static ServerlessFunction convertServerlessFunction(TypeProperties tp) {
Map resource = tp.getProperties();
- ServerlessFunction function = MAPPER.convertValue(resource, ServerlessFunction.class);
+ ServerlessFunction function = JSON_MAPPER.convertValue(resource, ServerlessFunction.class);
for (Entry entry : tp.getAdditionalProperties().entrySet()) {
function.addAdditionalTopLevelProperty(entry.getKey(), entry.getValue());
}
@@ -158,7 +189,7 @@ private static ServerlessFunction convertServerlessFunction(TypeProperties tp) {
private static Map convert(Map map) {
Map typeProperties = new HashMap<>();
for (Entry entry : map.entrySet()) {
- typeProperties.put(entry.getKey(), MAPPER.convertValue(entry.getValue(), TypeProperties.class));
+ typeProperties.put(entry.getKey(), JSON_MAPPER.convertValue(entry.getValue(), TypeProperties.class));
}
return typeProperties;
}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/handler/SamLocalHandler.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/handler/SamLocalHandler.java
new file mode 100644
index 00000000..fbd3b85b
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/handler/SamLocalHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.serverless.handler;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jdt.core.IJavaElement;
+
+import com.amazonaws.eclipse.lambda.launching.SamLocalExecution;
+import com.amazonaws.eclipse.lambda.launching.SamLocalExecution.LaunchMode;
+import com.amazonaws.eclipse.lambda.ui.LambdaJavaProjectUtil;
+
+/**
+ * Action of running/debugging SAM application when right clicking project explorer.
+ */
+public class SamLocalHandler {
+
+ public static class RunSamLocalHandler extends AbstractHandler {
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IJavaElement selectedJavaElement = LambdaJavaProjectUtil.getSelectedJavaElementFromCommandEvent(event);
+ SamLocalExecution.launch(selectedJavaElement, LaunchMode.RUN);
+ return null;
+ }
+ }
+
+ public static class DebugSamLocalHandler extends AbstractHandler {
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ IJavaElement selectedJavaElement = LambdaJavaProjectUtil.getSelectedJavaElementFromCommandEvent(event);
+ SamLocalExecution.launch(selectedJavaElement, LaunchMode.DEBUG);
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/ServerlessTemplate.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/ServerlessTemplate.java
index b8ac2902..b54b29ac 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/ServerlessTemplate.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/ServerlessTemplate.java
@@ -15,6 +15,7 @@
package com.amazonaws.eclipse.lambda.serverless.model;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -24,7 +25,7 @@ public class ServerlessTemplate extends AdditionalProperties {
private String AWSTemplateFormatVersion;
@JsonProperty("Transform")
- private String transform;
+ private List transform;
@JsonProperty("Description")
private String description;
@@ -42,11 +43,11 @@ public void setAWSTemplateFormatVersion(String aWSTemplateFormatVersion) {
AWSTemplateFormatVersion = aWSTemplateFormatVersion;
}
- public String getTransform() {
+ public List getTransform() {
return transform;
}
- public void setTransform(String transform) {
+ public void setTransform(List transform) {
this.transform = transform;
}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/transform/ServerlessFunction.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/transform/ServerlessFunction.java
index 5c999bdb..bac6a243 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/transform/ServerlessFunction.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/transform/ServerlessFunction.java
@@ -25,6 +25,7 @@
import com.amazonaws.eclipse.lambda.serverless.model.TypeProperties;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ServerlessFunction extends Resource {
@@ -52,7 +53,7 @@ public class ServerlessFunction extends Resource {
private Integer timeout;
@JsonProperty("Role")
- private String role;
+ private JsonNode role;
@JsonProperty("Policies")
private final List policies = new ArrayList<>();
@@ -109,11 +110,11 @@ public void setTimeout(Integer timeout) {
this.timeout = timeout;
}
- public String getRole() {
+ public JsonNode getRole() {
return role;
}
- public void setRole(String role) {
+ public void setRole(JsonNode role) {
this.role = role;
}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/transform/ServerlessModel.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/transform/ServerlessModel.java
index 9a0a7622..f7fd9801 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/transform/ServerlessModel.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/model/transform/ServerlessModel.java
@@ -14,7 +14,9 @@
*/
package com.amazonaws.eclipse.lambda.serverless.model.transform;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import com.amazonaws.eclipse.lambda.serverless.model.AdditionalProperties;
@@ -23,14 +25,15 @@
public class ServerlessModel extends AdditionalProperties {
private static final String DEFAULT_AWS_TEMPLATE_FORMAT_VERSION = "2010-09-09";
- private static final String DEFAULT_TRANSFORM = "AWS::Serverless-2016-10-31";
+ private static final List DEFAULT_TRANSFORM = Arrays.asList("AWS::Serverless-2016-10-31");
@JsonProperty("AWSTemplateFormatVersion")
private String awsTemplateFormatVersion;
- private String transform;
+ private List transform;
private String description;
private final Map serverlessFunctions = new HashMap<>();
+ // A map of Lambda handler name to Lambda function physical ID
private final Map serverlessFunctionPhysicalIds = new HashMap<>();
// Unrecognized resources
private final Map additionalResources = new HashMap<>();
@@ -59,14 +62,14 @@ public void setAWSTemplateFormatVersion(String awsTemplateFormatVersion) {
this.awsTemplateFormatVersion = awsTemplateFormatVersion;
}
- public String getTransform() {
+ public List getTransform() {
if (transform == null) {
transform = DEFAULT_TRANSFORM;
}
return transform;
}
- public void setTransform(String transform) {
+ public void setTransform(List transform) {
this.transform = transform;
}
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/validator/ServerlessTemplateFilePathValidator.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/validator/ServerlessTemplateFilePathValidator.java
index 599c3584..68afc9b8 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/validator/ServerlessTemplateFilePathValidator.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/validator/ServerlessTemplateFilePathValidator.java
@@ -14,27 +14,44 @@
*/
package com.amazonaws.eclipse.lambda.serverless.validator;
+import java.io.IOException;
+
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
+import com.amazonaws.eclipse.core.util.PluginUtils;
import com.amazonaws.eclipse.lambda.serverless.Serverless;
+import com.amazonaws.eclipse.lambda.serverless.model.transform.ServerlessModel;
import com.amazonaws.util.StringUtils;
public class ServerlessTemplateFilePathValidator implements IValidator {
-
@Override
public IStatus validate(Object value) {
- String s = (String)value;
- if (StringUtils.isNullOrEmpty(s)) {
- return ValidationStatus.error("Serverless template file path must be provided!");
- }
+ String filePath = (String) value;
+
try {
- Serverless.load(s);
+ validateFilePath(filePath);
} catch (Exception e) {
return ValidationStatus.error(e.getMessage());
}
return ValidationStatus.ok();
}
-}
+ public ServerlessModel validateFilePath(String filePath) {
+ if (StringUtils.isNullOrEmpty(filePath)) {
+ throw new IllegalArgumentException("Serverless template file path must be provided!");
+ }
+ try {
+ filePath = PluginUtils.variablePluginReplace(filePath);
+ ServerlessModel model = Serverless.load(filePath);
+ if (model.getAdditionalResources().isEmpty() && model.getServerlessFunctions().isEmpty()) {
+ throw new IllegalArgumentException("Serverless template file is not valid, you need to define at least one AWS Resource.");
+ }
+ return model;
+ } catch (IOException | CoreException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/wizard/editoraction/SamLocalAction.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/wizard/editoraction/SamLocalAction.java
new file mode 100644
index 00000000..0bfef97d
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/serverless/wizard/editoraction/SamLocalAction.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.serverless.wizard.editoraction;
+
+import org.eclipse.jface.action.IAction;
+
+import com.amazonaws.eclipse.lambda.launching.SamLocalExecution;
+import com.amazonaws.eclipse.lambda.launching.SamLocalExecution.LaunchMode;
+import com.amazonaws.eclipse.lambda.upload.wizard.editoraction.AbstractLambdaEditorAction;
+
+/**
+ * Action of local debugging SAM application triggered by right clicking Lambda function editor
+ */
+public class SamLocalAction {
+
+ public static class RunSamLocalAction extends AbstractLambdaEditorAction {
+ @Override
+ public void run(IAction action) {
+ SamLocalExecution.launch(selectedJavaElement, LaunchMode.RUN);
+ }
+ }
+
+ public static class DebugSamLocalAction extends AbstractLambdaEditorAction {
+ @Override
+ public void run(IAction action) {
+ SamLocalExecution.launch(selectedJavaElement, LaunchMode.DEBUG);
+ }
+ }
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/ui/LambdaPluginColors.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/ui/LambdaPluginColors.java
new file mode 100644
index 00000000..4917a4aa
--- /dev/null
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/ui/LambdaPluginColors.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.ui;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Predefined colors to be used by Lambda plugin.
+ */
+public class LambdaPluginColors {
+ public static final Color BLACK = new Color(Display.getCurrent(), 0, 0, 0);
+ public static final Color GREY = new Color(Display.getCurrent(), 214, 214, 214);
+}
\ No newline at end of file
diff --git a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/upload/wizard/page/FunctionConfigurationPage.java b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/upload/wizard/page/FunctionConfigurationPage.java
index 0ce1761a..29a04f30 100644
--- a/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/upload/wizard/page/FunctionConfigurationPage.java
+++ b/bundles/com.amazonaws.eclipse.lambda/src/com/amazonaws/eclipse/lambda/upload/wizard/page/FunctionConfigurationPage.java
@@ -29,7 +29,6 @@
import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.DataBindingContext;
-import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.beans.PojoProperties;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
@@ -198,15 +197,12 @@ private void createBasicSettingSection(Composite parent) {
newLabel(group, "Name:");
functionNameLabel = newFillingLabel(group, "", 2);
- descriptionTextComplex = TextComplex.builder()
- .composite(group)
+ descriptionTextComplex = TextComplex.builder(group,
+ bindingContext, PojoProperties.value(FunctionConfigPageDataModel.P_DESCRIPTION).observe(dataModel.getFunctionConfigPageDataModel()))
.createLabel(true)
.labelColSpan(1)
- .dataBindingContext(bindingContext)
.labelValue("Description:")
.textColSpan(2)
- .pojoObservableValue(PojoObservables.observeValue(
- dataModel.getFunctionConfigPageDataModel(), FunctionConfigPageDataModel.P_DESCRIPTION))
.defaultValue(dataModel.getFunctionConfigPageDataModel().getDescription())
.textMessage("The description for the function (optional)")
.build();
@@ -239,7 +235,7 @@ private void createFunctionVersioningAndAliasSettingSection(Composite parent) {
.dataBindingContext(bindingContext)
.defaultValue(false)
.labelValue("Publish new version")
- .pojoObservableValue(PojoObservables.observeValue(dataModel.getFunctionConfigPageDataModel(), P_PUBLISH_NEW_VERSION))
+ .pojoObservableValue(PojoProperties.value(P_PUBLISH_NEW_VERSION).observe(dataModel.getFunctionConfigPageDataModel()))
.selectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
@@ -253,7 +249,7 @@ public void widgetSelected(SelectionEvent e) {
.dataBindingContext(bindingContext)
.defaultValue(false)
.labelValue("Provide an alias to this new version")
- .pojoObservableValue(PojoObservables.observeValue(dataModel.getFunctionConfigPageDataModel(), P_CREATE_NEW_VERSION_ALIAS))
+ .pojoObservableValue(PojoProperties.value(P_CREATE_NEW_VERSION_ALIAS).observe(dataModel.getFunctionConfigPageDataModel()))
.selectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
@@ -344,31 +340,25 @@ private void createAdvancedSettingSection(Composite parent) {
"Please enter a timeout within the range of [%d, %d]",
MIN_TIMEOUT, MAX_TIMEOUT);
- memoryTextComplex = TextComplex.builder()
- .composite(group)
+ memoryTextComplex = TextComplex.builder(group,
+ bindingContext, PojoProperties.value(FunctionConfigPageDataModel.P_MEMORY).observe(dataModel.getFunctionConfigPageDataModel()))
.addValidator(new RangeValidator(memoryErrMsg,
MIN_MEMORY, MAX_MEMORY))
.createLabel(true)
- .dataBindingContext(bindingContext)
.defaultValue(Integer.toString(DEFAULT_MEMORY))
.labelColSpan(1)
.labelValue("Memory (MB):")
- .pojoObservableValue(PojoObservables.observeValue(
- dataModel.getFunctionConfigPageDataModel(), FunctionConfigPageDataModel.P_MEMORY))
.textColSpan(2)
.build();
- timeoutTextComplex = TextComplex.builder()
- .composite(group)
+ timeoutTextComplex = TextComplex.builder(group,
+ bindingContext, PojoProperties.value(FunctionConfigPageDataModel.P_TIMEOUT).observe(dataModel.getFunctionConfigPageDataModel()))
.addValidator(new RangeValidator(timeoutErrMsg,
MIN_TIMEOUT, MAX_TIMEOUT))
.createLabel(true)
- .dataBindingContext(bindingContext)
.defaultValue(Integer.toString(DEFAULT_TIMEOUT))
.labelColSpan(1)
.labelValue("Timeout (s):")
- .pojoObservableValue(PojoObservables.observeValue(
- dataModel.getFunctionConfigPageDataModel(), FunctionConfigPageDataModel.P_TIMEOUT))
.textColSpan(2)
.build();
}
diff --git a/pom.xml b/pom.xml
index 87d0987d..69a5179f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
1.0.0
UTF-8
mars
- 1.11.130
+ 1.11.248
1.8
1.8
diff --git a/tests/com.amazonaws.eclipse.core.tests/src/com/amazonaws/eclipse/core/regions/RegionUtilsTest.java b/tests/com.amazonaws.eclipse.core.tests/src/com/amazonaws/eclipse/core/regions/RegionUtilsTest.java
index ca420a62..fb5990ca 100644
--- a/tests/com.amazonaws.eclipse.core.tests/src/com/amazonaws/eclipse/core/regions/RegionUtilsTest.java
+++ b/tests/com.amazonaws.eclipse.core.tests/src/com/amazonaws/eclipse/core/regions/RegionUtilsTest.java
@@ -1,5 +1,6 @@
package com.amazonaws.eclipse.core.regions;
+import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;
@@ -16,20 +17,20 @@ public class RegionUtilsTest {
// regular region endpoints
Pattern.compile("^(http|https)://\\w+\\.(\\w+\\.)?(ca|us|eu|ap|sa)-(gov-)?(east|west|south|north|central|northeast|southeast)-(1|2)\\.amazonaws\\.com(/)?$"),
// China region endpoints, currently we only have cn-north-1 region
- Pattern.compile("^(http|https)://\\w+\\.(\\w+\\.)?cn-north-1.amazonaws.com.cn(/)?$"),
+ Pattern.compile("^(http|https)://\\w+\\.(\\w+\\.)?cn-(north|northwest)-1.amazonaws.com.cn(/)?$"),
// us-gov region endpoints
Pattern.compile("^(http|https)://\\w+\\.us-gov(-west-1)?.amazonaws.com(/)?$")
};
@Test
- public void testRemoteRegionFile() {
+ public void testRemoteRegionFile() throws IOException {
List regions = RegionUtils.loadRegionsFromS3();
assertRegionEndpointsValid(regions);
}
@Test
- public void testLocalRegionFile() {
- List regions = RegionUtils.loadRegionsFromLocalFile();
+ public void testLocalRegionFile() throws IOException {
+ List regions = RegionUtils.loadRegionsFromLocalRegionFile();
assertRegionEndpointsValid(regions);
}
diff --git a/tests/com.amazonaws.eclipse.lambda.tests/.classpath b/tests/com.amazonaws.eclipse.lambda.tests/.classpath
index 625a2791..b6adeb1e 100644
--- a/tests/com.amazonaws.eclipse.lambda.tests/.classpath
+++ b/tests/com.amazonaws.eclipse.lambda.tests/.classpath
@@ -1,6 +1,6 @@
-
+
diff --git a/tests/com.amazonaws.eclipse.lambda.tests/.settings/org.eclipse.jdt.core.prefs b/tests/com.amazonaws.eclipse.lambda.tests/.settings/org.eclipse.jdt.core.prefs
index f42de363..0c68a61d 100644
--- a/tests/com.amazonaws.eclipse.lambda.tests/.settings/org.eclipse.jdt.core.prefs
+++ b/tests/com.amazonaws.eclipse.lambda.tests/.settings/org.eclipse.jdt.core.prefs
@@ -1,7 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
-org.eclipse.jdt.core.compiler.compliance=1.7
+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.7
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/tests/com.amazonaws.eclipse.lambda.tests/META-INF/MANIFEST.MF b/tests/com.amazonaws.eclipse.lambda.tests/META-INF/MANIFEST.MF
index 1a7b9c30..0477ab04 100644
--- a/tests/com.amazonaws.eclipse.lambda.tests/META-INF/MANIFEST.MF
+++ b/tests/com.amazonaws.eclipse.lambda.tests/META-INF/MANIFEST.MF
@@ -10,4 +10,5 @@ Import-Package: com.fasterxml.jackson.core,
com.fasterxml.jackson.databind,
org.junit
Require-Bundle: com.amazonaws.eclipse.lambda;bundle-version="1.0.0",
- com.amazonaws.eclipse.core;bundle-version="2.3.1"
+ com.amazonaws.eclipse.core;bundle-version="2.3.1",
+ org.eclipse.debug.ui
diff --git a/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/blueprint/BlueprintsTest.java b/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/blueprint/BlueprintsTest.java
index d5e8c292..e39f2dcf 100644
--- a/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/blueprint/BlueprintsTest.java
+++ b/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/blueprint/BlueprintsTest.java
@@ -182,7 +182,7 @@ private void assertSamTemplateValid(Object dataModel, CodeTemplateManager manage
try {
String content = CodeTemplateManager.processTemplateWithData(samTemplate, dataModel);
assertStringNotEmpty(content);
- ServerlessModel model = Serverless.load(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)));
+ ServerlessModel model = Serverless.loadFromContent(content);
Set physicalIds = model.getServerlessFunctions().keySet();
Set pathIds = blueprint.getHandlerTemplatePaths().keySet();
assertEquals(physicalIds, pathIds);
diff --git a/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/launching/SamLocalConsoleLineTrackerTest.java b/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/launching/SamLocalConsoleLineTrackerTest.java
new file mode 100644
index 00000000..70aa7eea
--- /dev/null
+++ b/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/launching/SamLocalConsoleLineTrackerTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.amazonaws.eclipse.lambda.launching;
+
+import java.util.regex.Matcher;
+
+import static com.amazonaws.eclipse.lambda.launching.SamLocalConsoleLineTracker.URL_PATTERN;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SamLocalConsoleLineTrackerTest {
+ private TestCase[] testCases = {
+ new TestCase("foo: http://234.12.21.12:2000", true, "http://234.12.21.12:2000"),
+ new TestCase("foo: http://234.12.21.12:2000bar", true, "http://234.12.21.12:2000"),
+ new TestCase("foo: https://234.12.21.12:3000", true, "https://234.12.21.12:3000"),
+ new TestCase("foo: 234.12.255.12:2012", true, "234.12.255.12:2012"),
+ new TestCase("foo: localhost:3000", true, "localhost:3000"),
+ new TestCase("217.0.0.1:3000", true, "217.0.0.1:3000"),
+ new TestCase("217.0.0.1", false, null),
+ new TestCase("This is foo", false, null)
+ };
+
+ private static class TestCase {
+ String text;
+ boolean valid;
+ String ipAddress;
+
+ private TestCase(String text, boolean valid, String ipAddress) {
+ this.text = text;
+ this.valid = valid;
+ this.ipAddress = ipAddress;
+ }
+ }
+
+ @Test
+ public void testPatterns() {
+ for (TestCase testCase : testCases) {
+ doTestPattern(testCase.text, testCase.valid, testCase.ipAddress);
+ }
+ }
+
+ private void doTestPattern(String line, boolean match, String matchString) {
+ Matcher matcher = URL_PATTERN.matcher(line);
+ Assert.assertEquals(match, matcher.find());
+ if (match) {
+ String actualMatchString = matcher.group();
+ Assert.assertEquals(matchString, actualMatchString);
+ }
+ }
+}
diff --git a/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/serverless/ServerlessTemplateMapperTest.java b/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/serverless/ServerlessTemplateMapperTest.java
index f20dc23d..cc5b7e82 100644
--- a/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/serverless/ServerlessTemplateMapperTest.java
+++ b/tests/com.amazonaws.eclipse.lambda.tests/src/com/amazonaws/eclipse/lambda/serverless/ServerlessTemplateMapperTest.java
@@ -16,6 +16,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertArrayEquals;
import java.io.IOException;
import java.io.InputStream;
@@ -23,7 +24,9 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -36,131 +39,201 @@
public class ServerlessTemplateMapperTest {
- private static final String SERVERLESS_TEMPLATE_FILE = "serverless-template.json";
- private ServerlessModel model;
- private ServerlessTemplate template;
+ private static final String SERVERLESS_JSON_TEMPLATE_FILE = "serverless-template.json";
+ private static final String SERVERLESS_YAML_TEMPLATE_FILE = "serverless-template.yaml";
+ private static final String CODESTAR_YAML_TEMPLATE_FILE = "codestar.template.yml";
+
+ private ServerlessModel jsonModel;
+ private ServerlessTemplate jsonTemplate;
+
+ private ServerlessModel yamlModel;
+ private ServerlessTemplate yamlTemplate;
+
+ private ServerlessModel codestarModel;
+ private ServerlessTemplate codestarTemplate;
@Before
public void setUp() throws JsonParseException, JsonMappingException, IOException {
- InputStream serverlessTemplateInputStream = ServerlessTemplateMapperTest.class.getResourceAsStream(SERVERLESS_TEMPLATE_FILE);
- model = Serverless.load(serverlessTemplateInputStream);
- template = Serverless.convert(model);
+ try (InputStream serverlessTemplateInputStream = ServerlessTemplateMapperTest.class.getResourceAsStream(SERVERLESS_JSON_TEMPLATE_FILE)) {
+ jsonModel = Serverless.load(serverlessTemplateInputStream);
+ jsonTemplate = Serverless.convert(jsonModel);
+ }
+
+ try (InputStream serverlessTemplateInputStream = ServerlessTemplateMapperTest.class.getResourceAsStream(SERVERLESS_YAML_TEMPLATE_FILE)) {
+ yamlModel = Serverless.load(serverlessTemplateInputStream);
+ yamlTemplate = Serverless.convert(yamlModel);
+ }
+
+ try (InputStream serverlessTemplateInputStream = ServerlessTemplateMapperTest.class.getResourceAsStream(CODESTAR_YAML_TEMPLATE_FILE)) {
+ codestarModel = Serverless.load(serverlessTemplateInputStream);
+ codestarTemplate = Serverless.convert(codestarModel);
+ }
+ }
+
+ @Test
+ public void testCodeStarTransform() throws IOException {
+ Assert.assertArrayEquals(new String[]{"AWS::Serverless-2016-10-31", "AWS::CodeStar"},
+ codestarTemplate.getTransform().toArray());
}
@Test
public void testModel_additionalProperties() {
- Map additionalProperties = model.getAdditionalProperties();
- testValuePath(additionalProperties, "bar", "foo");
- testValuePath(additionalProperties, "bar", "foo1", "foo");
+ Consumer testModel_additionalProperties = (model) -> {
+ Map additionalProperties = model.getAdditionalProperties();
+ testValuePath(additionalProperties, "bar", "foo");
+ testValuePath(additionalProperties, "bar", "foo1", "foo");
+ };
+
+ testModel_additionalProperties.accept(jsonModel);
+ testModel_additionalProperties.accept(yamlModel);
}
@Test
public void testModel_ServerlessFunction() {
- Map functions = model.getServerlessFunctions();
+ Consumer testModel_ServerlessFunction = (model) -> {
+ Map functions = jsonModel.getServerlessFunctions();
+ ServerlessFunction function = testServerlessFunction(functions, "ServerlessFunction",
+ "fakeCodeUri", "fakeHandler", "java8", 512, 300, Arrays.asList("Policy1", "Policy2"));
- ServerlessFunction function = testServerlessFunction(functions, "ServerlessFunction",
- "fakeCodeUri", "fakeHandler", "java8", 512, 300, Arrays.asList("Policy1", "Policy2"));
+ Map additionalTopLevelProperties = function.getAdditionalTopLevelProperties();
+ testValuePath(additionalTopLevelProperties, "bar", "foo");
- Map additionalTopLevelProperties = function.getAdditionalTopLevelProperties();
- testValuePath(additionalTopLevelProperties, "bar", "foo");
+ Map additionalProperties = function.getAdditionalProperties();
+ assertTrue(additionalProperties.containsKey("Events"));
+ assertS3EventMatches((Map)additionalProperties.get("Events"), "S3Event", "fakeBucket");
+ };
- Map additionalProperties = function.getAdditionalProperties();
- assertTrue(additionalProperties.containsKey("Events"));
- assertS3EventMatches((Map)additionalProperties.get("Events"), "S3Event", "fakeBucket");
+ testModel_ServerlessFunction.accept(jsonModel);
+ testModel_ServerlessFunction.accept(yamlModel);
}
@Test
public void testModel_ServerlessFunction2() {
- Map functions = model.getServerlessFunctions();
+ Consumer testModel_ServerlessFunction2 = (model) -> {
+ Map functions = jsonModel.getServerlessFunctions();
+
+ ServerlessFunction function = testServerlessFunction(functions, "ServerlessFunction2",
+ "fakeCodeUri", "fakeHandler", "fakeRuntime", 100, 100, Collections.emptyList());
- ServerlessFunction function = testServerlessFunction(functions, "ServerlessFunction2",
- "fakeCodeUri", "fakeHandler", "fakeRuntime", 100, 100, Collections.emptyList());
+ Map additionalTopLevelProperties = function.getAdditionalTopLevelProperties();
+ assertTrue(additionalTopLevelProperties.isEmpty());
- Map additionalTopLevelProperties = function.getAdditionalTopLevelProperties();
- assertTrue(additionalTopLevelProperties.isEmpty());
+ Map additionalProperties = function.getAdditionalProperties();
- Map additionalProperties = function.getAdditionalProperties();
+ testValuePath(additionalProperties, "value1", "Environment", "Variables", "key1");
+ testValuePath(additionalProperties, "value2", "Environment", "Variables", "key2");
+ };
- testValuePath(additionalProperties, "value1", "Environment", "Variables", "key1");
- testValuePath(additionalProperties, "value2", "Environment", "Variables", "key2");
+ testModel_ServerlessFunction2.accept(jsonModel);
+ testModel_ServerlessFunction2.accept(yamlModel);
}
@Test
public void testModel_additionalResources() {
- Map resources = model.getAdditionalResources();
+ Consumer testModel_additionalResources = (model) -> {
+ Map resources = jsonModel.getAdditionalResources();
- assertTrue(resources.containsKey("IamRole"));
- TypeProperties tp = resources.get("IamRole");
- assertEquals("AWS::IAM::Role", tp.getType());
+ assertTrue(resources.containsKey("IamRole"));
+ TypeProperties tp = resources.get("IamRole");
+ assertEquals("AWS::IAM::Role", tp.getType());
- Map properties = tp.getProperties();
- assertEquals("fakeValue", properties.get("fakeKey"));
+ Map properties = tp.getProperties();
+ assertEquals("fakeValue", properties.get("fakeKey"));
- assertTrue(tp.getAdditionalProperties().containsKey("foo"));
- assertEquals("bar", tp.getAdditionalProperties().get("foo"));
+ assertTrue(tp.getAdditionalProperties().containsKey("foo"));
+ assertEquals("bar", tp.getAdditionalProperties().get("foo"));
+ };
+
+ testModel_additionalResources.accept(jsonModel);
+ testModel_additionalResources.accept(yamlModel);
}
@Test
public void testTemplate_Metadata() {
- assertEquals("2010-09-09", template.getAWSTemplateFormatVersion());
- assertEquals(null, template.getDescription());
- assertEquals("AWS::Serverless-2016-10-31", template.getTransform());
+ Consumer testTemplate_Metadata = (model) -> {
+ assertEquals("2010-09-09", jsonTemplate.getAWSTemplateFormatVersion());
+ assertEquals(null, jsonTemplate.getDescription());
+ assertArrayEquals(new String[]{"AWS::Serverless-2016-10-31"}, jsonTemplate.getTransform().toArray());
+ };
+
+ testTemplate_Metadata.accept(jsonModel);
+ testTemplate_Metadata.accept(yamlModel);
}
@Test
public void testTemplate_AdditionalProperties() {
- Map additionalProperties = template.getAdditionalProperties();
- testValuePath(additionalProperties, "bar", "foo");
- testValuePath(additionalProperties, "bar", "foo1", "foo");
+ Consumer testTemplate_AdditionalProperties = (template) -> {
+ Map additionalProperties = jsonTemplate.getAdditionalProperties();
+ testValuePath(additionalProperties, "bar", "foo");
+ testValuePath(additionalProperties, "bar", "foo1", "foo");
+ };
+
+ testTemplate_AdditionalProperties.accept(jsonTemplate);
+ testTemplate_AdditionalProperties.accept(yamlTemplate);
}
@Test
public void testTemplate_ServerlessFunction() {
- Map resources = template.getResources();
- TypeProperties resource = testTemplateResource(resources, "ServerlessFunction", "AWS::Serverless::Function");
-
- Map additionalProperties = resource.getAdditionalProperties();
- testValuePath(additionalProperties, "bar", "foo");
-
- Map properties = resource.getProperties();
- testValuePath(properties, "fakeCodeUri", "CodeUri");
- testValuePath(properties, "fakeHandler", "Handler");
- testValuePath(properties, "S3", "Events", "S3Event", "Type");
- testValuePath(properties, "fakeBucket", "Events", "S3Event", "Properties", "Bucket");
+ Consumer testTemplate_ServerlessFunction = (template) -> {
+ Map resources = jsonTemplate.getResources();
+ TypeProperties resource = testTemplateResource(resources, "ServerlessFunction", "AWS::Serverless::Function");
+
+ Map additionalProperties = resource.getAdditionalProperties();
+ testValuePath(additionalProperties, "bar", "foo");
+
+ Map properties = resource.getProperties();
+ testValuePath(properties, "fakeCodeUri", "CodeUri");
+ testValuePath(properties, "fakeHandler", "Handler");
+ testValuePath(properties, "S3", "Events", "S3Event", "Type");
+ testValuePath(properties, "fakeBucket", "Events", "S3Event", "Properties", "Bucket");
+ };
+
+ testTemplate_ServerlessFunction.accept(jsonTemplate);
+ testTemplate_ServerlessFunction.accept(yamlTemplate);
}
@Test
public void testTemplate_ServerlessFunction2() {
- Map resources = template.getResources();
- TypeProperties resource = testTemplateResource(resources, "ServerlessFunction2", "AWS::Serverless::Function");
-
- Map additionalProperties = resource.getAdditionalProperties();
- assertTrue(additionalProperties.isEmpty());
-
- Map properties = resource.getProperties();
- testValuePath(properties, "fakeCodeUri", "CodeUri");
- testValuePath(properties, "fakeHandler", "Handler");
- testValuePath(properties, "fakeRuntime", "Runtime");
- testValuePath(properties, "fakeFunctionName", "FunctionName");
- testValuePath(properties, new Integer(100), "MemorySize");
- testValuePath(properties, new Integer(100), "Timeout");
- testValuePath(properties, "value1", "Environment", "Variables", "key1");
- testValuePath(properties, "value2", "Environment", "Variables", "key2");
+ Consumer testTemplate_ServerlessFunction2 = (template) -> {
+ Map resources = jsonTemplate.getResources();
+ TypeProperties resource = testTemplateResource(resources, "ServerlessFunction2", "AWS::Serverless::Function");
+
+ Map additionalProperties = resource.getAdditionalProperties();
+ assertTrue(additionalProperties.isEmpty());
+
+ Map properties = resource.getProperties();
+ testValuePath(properties, "fakeCodeUri", "CodeUri");
+ testValuePath(properties, "fakeHandler", "Handler");
+ testValuePath(properties, "fakeRuntime", "Runtime");
+ testValuePath(properties, "fakeFunctionName", "FunctionName");
+ testValuePath(properties, new Integer(100), "MemorySize");
+ testValuePath(properties, new Integer(100), "Timeout");
+ testValuePath(properties, "value1", "Environment", "Variables", "key1");
+ testValuePath(properties, "value2", "Environment", "Variables", "key2");
+ };
+
+ testTemplate_ServerlessFunction2.accept(jsonTemplate);
+ testTemplate_ServerlessFunction2.accept(yamlTemplate);
}
@Test
public void testTemplate_IamRole() {
- Map resources = template.getResources();
- TypeProperties resource = testTemplateResource(resources, "ServerlessFunction", "AWS::Serverless::Function");
-
- Map additionalProperties = resource.getAdditionalProperties();
- testValuePath(additionalProperties, "bar", "foo");
-
- Map properties = resource.getProperties();
- testValuePath(properties, "fakeCodeUri", "CodeUri");
- testValuePath(properties, "fakeHandler", "Handler");
- testValuePath(properties, "S3", "Events", "S3Event", "Type");
- testValuePath(properties, "fakeBucket", "Events", "S3Event", "Properties", "Bucket");
+ Consumer testTemplate_IamRole = (template) -> {
+ Map resources = jsonTemplate.getResources();
+ TypeProperties resource = testTemplateResource(resources, "ServerlessFunction", "AWS::Serverless::Function");
+
+ Map additionalProperties = resource.getAdditionalProperties();
+ testValuePath(additionalProperties, "bar", "foo");
+
+ Map properties = resource.getProperties();
+ testValuePath(properties, "fakeCodeUri", "CodeUri");
+ testValuePath(properties, "fakeHandler", "Handler");
+ testValuePath(properties, "S3", "Events", "S3Event", "Type");
+ testValuePath(properties, "fakeBucket", "Events", "S3Event", "Properties", "Bucket");
+ };
+
+ testTemplate_IamRole.accept(jsonTemplate);
+ testTemplate_IamRole.accept(yamlTemplate);
}
private TypeProperties testTemplateResource(Map resources, String resourceName, String resourceType) {
@@ -203,5 +276,4 @@ private ServerlessFunction testServerlessFunction(Map1.0.0-SNAPSHOT
com.amazonaws.eclipse.javasdk
- 1.11.130
+ 1.11.248
bundle
@@ -134,6 +134,10 @@
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+
com.fasterxml.jackson.module
jackson-module-jaxb-annotations
diff --git a/thirdparty/pom.xml b/thirdparty/pom.xml
index 13b4f283..0e09f033 100644
--- a/thirdparty/pom.xml
+++ b/thirdparty/pom.xml
@@ -12,7 +12,7 @@
- 1.11.130
+ 1.11.248
2.4
2.6.6
META-INF
@@ -41,6 +41,11 @@
jackson-dataformat-xml
${jackson-version}
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+ ${jackson-version}
+
com.fasterxml.jackson.module
jackson-module-jaxb-annotations