From eea446392e1d108cbbdbf7313cdb2f2700085942 Mon Sep 17 00:00:00 2001 From: Shruti Sinha Date: Thu, 26 Sep 2024 12:49:02 -0700 Subject: [PATCH 1/8] Account for Windows and MSFT Edge limitations when loading WebViews --- plugin/META-INF/MANIFEST.MF | 4 +- .../connection/AuthLspConnectionProvider.java | 7 ++- .../lsp/manager/DefaultLspManager.java | 8 ++- .../amazonq/views/AmazonQChatWebview.java | 25 +++++++- .../eclipse/amazonq/views/AmazonQView.java | 62 ++++++++++++++++++- .../amazonq/views/ToolkitLoginWebview.java | 26 ++++++-- 6 files changed, 119 insertions(+), 13 deletions(-) diff --git a/plugin/META-INF/MANIFEST.MF b/plugin/META-INF/MANIFEST.MF index 48350943..f063b8c9 100644 --- a/plugin/META-INF/MANIFEST.MF +++ b/plugin/META-INF/MANIFEST.MF @@ -18,7 +18,9 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="3.31.0", com.google.gson;bundle-version="2.11.0", org.eclipse.ui.ide;bundle-version="3.22.200", org.eclipse.ui.workbench.texteditor;bundle-version="3.17.400", - org.eclipse.mylyn.commons.ui;bundle-version="4.2.0" + org.eclipse.mylyn.commons.ui;bundle-version="4.2.0", + org.eclipse.jetty.server;bundle-version="12.0.9", + org.eclipse.jetty.util;bundle-version="12.0.9" Bundle-Classpath: ., target/dependency/animal-sniffer-annotations-1.9.jar, target/dependency/annotations-2.25.33.jar, diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/connection/AuthLspConnectionProvider.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/connection/AuthLspConnectionProvider.java index da69f3ce..f1bf9d91 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/connection/AuthLspConnectionProvider.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/connection/AuthLspConnectionProvider.java @@ -4,6 +4,8 @@ package software.aws.toolkits.eclipse.amazonq.lsp.connection; import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -13,14 +15,15 @@ public class AuthLspConnectionProvider extends AbstractLspConnectionProvider { - public AuthLspConnectionProvider() throws IOException { + public AuthLspConnectionProvider() throws IOException, URISyntaxException { super(); var authJs = PluginUtils.getResource("auth/packages/server/dist/index.js"); + var authJsPath = Path.of(authJs.toURI()).toString(); var lspManager = LspManagerProvider.getInstance(); List commands = new ArrayList<>(); commands.add(lspManager.getLspInstallation().nodeExecutable().toString()); - commands.add(authJs.getPath()); + commands.add(authJsPath); commands.add("--nolazy"); commands.add("--inspect=5599"); commands.add("--stdio"); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java index f737e171..5e8cb38d 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java @@ -51,7 +51,7 @@ public LspInstallation getLspInstallation() { throw new RuntimeException("Could not find node executable or LSP file in the downloaded contents"); } - makeExecutable(nodeExecutable); + makeExecutable(nodeExecutable, platform); return new LspInstallation(nodeExecutable, lspJs); } catch (Exception e) { @@ -60,7 +60,11 @@ public LspInstallation getLspInstallation() { } } - private static void makeExecutable(final Path filePath) throws IOException { + private static void makeExecutable(final Path filePath, PluginPlatform platform) throws IOException { + // don't set file permissions for windows as it has issues using the executable otherwise + if(platform == PluginPlatform.WINDOWS) { + return; + } var permissions = new HashSet<>(Arrays.asList(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE)); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java index 0d7cbc1b..1d54d6b5 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java @@ -3,6 +3,9 @@ package software.aws.toolkits.eclipse.amazonq.views; +import java.nio.file.Path; + +import org.eclipse.jetty.server.Server; import org.eclipse.swt.browser.BrowserFunction; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; @@ -18,6 +21,7 @@ public class AmazonQChatWebview extends AmazonQView { public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.AmazonQChatWebview"; private AmazonQCommonActions amazonQCommonActions; + private Server server; private final ViewCommandParser commandParser; private final ViewActionHandler actionHandler; @@ -55,18 +59,31 @@ public Object function(final Object[] arguments) { private String getContent() { String jsFile = PluginUtils.getAwsDirectory(LspConstants.LSP_SUBDIRECTORY).resolve("amazonq-ui.js").toString(); + var jsParent = Path.of(jsFile).getParent(); + var jsDirectoryPath = Path.of(jsParent.toUri()).normalize().toString(); + + server = setupVirtualServer(jsDirectoryPath); + if(server == null) { + return "Failed to load JS"; + } + + var chatJsPath = server.getURI().toString()+"amazonq-ui.js"; return String.format("\n" + "\n" + "\n" + " \n" + " \n" + + " " + " Chat UI\n" + " %s\n" + "\n" + "\n" + " %s\n" + "\n" - + "", generateCss(), generateJS(jsFile)); + + "", chatJsPath, generateCss(), generateJS(chatJsPath)); } private String generateCss() { @@ -113,5 +130,11 @@ protected final void handleAuthStatusChange(final boolean isLoggedIn) { } }); } + + @Override + public void dispose() { + stopVirtualServer(server); + super.dispose(); + } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java index e772f485..2736481e 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java @@ -4,6 +4,10 @@ import java.util.Set; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.graphics.Color; @@ -17,6 +21,8 @@ import software.aws.toolkits.eclipse.amazonq.util.AuthStatusChangedListener; import software.aws.toolkits.eclipse.amazonq.util.AuthUtils; import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; +import software.aws.toolkits.eclipse.amazonq.util.PluginPlatform; +import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; import software.aws.toolkits.eclipse.amazonq.views.actions.AmazonQCommonActions; public abstract class AmazonQView extends ViewPart { @@ -78,13 +84,21 @@ protected final void setupAmazonQView(final Composite parent, final boolean isLo } private void setupBrowser(final Composite parent) { - browser = new Browser(parent, SWT.NATIVE); + browser = new Browser(parent, getBrowserStyle()); Display display = Display.getCurrent(); Color black = display.getSystemColor(SWT.COLOR_BLACK); browser.setBackground(black); parent.setBackground(black); } + + private int getBrowserStyle() { + var platform = PluginUtils.getPlatform(); + if(platform == PluginPlatform.WINDOWS) { + return SWT.EDGE; + } + return SWT.NATIVE; + } private void setupActions(final Browser browser, final boolean isLoggedIn) { amazonQCommonActions = new AmazonQCommonActions(browser, isLoggedIn, getViewSite()); @@ -96,9 +110,51 @@ private void setupAuthStatusListeners() { AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getFeedbackDialogContributionAction()); } - @Override + /** + * Sets up virtual host mapping for the given path using jetty server + * @param jsPath + * @return server launched + */ + protected Server setupVirtualServer(String jsPath) { + Server server = null; + try { + server = new Server(0); + var servletContext = new ContextHandler(); + servletContext.setContextPath("/"); + servletContext.addVirtualHosts(new String[]{"localhost"}); + + var handler = new ResourceHandler(); + + ResourceFactory resourceFactory = ResourceFactory.of(server); + handler.setBaseResource(resourceFactory.newResource(jsPath)); + handler.setDirAllowed(true); + servletContext.setHandler(handler); + + server.setHandler(servletContext); + server.start(); + + return server; + + } catch (Exception e) { + stopVirtualServer(server); + PluginLogger.error("Error occurred while attempting to start a virtual server for " + jsPath, e); + return null; + } + } + + protected void stopVirtualServer(Server server) { + if(server != null) { + try { + server.stop(); + } catch (Exception e) { + PluginLogger.error("Error occurred when attempting to stop the virtual server", e); + } + } + } + + @Override public final void setFocus() { - browser.setFocus(); + browser.setFocus(); } /** diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java index cd8e910d..691163b3 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java @@ -2,9 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 package software.aws.toolkits.eclipse.amazonq.views; +import org.eclipse.jetty.server.Server; import java.io.IOException; +import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Path; import org.eclipse.swt.browser.BrowserFunction; import org.eclipse.swt.widgets.Composite; @@ -19,6 +22,7 @@ public final class ToolkitLoginWebview extends AmazonQView { public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.ToolkitLoginWebview"; private AmazonQCommonActions amazonQCommonActions; + private Server server; private final ViewCommandParser commandParser; private final ViewActionHandler actionHandler; @@ -53,7 +57,7 @@ protected void handleAuthStatusChange(final boolean isLoggedIn) { Display.getDefault().asyncExec(() -> { amazonQCommonActions.updateActionVisibility(isLoggedIn, getViewSite()); if (!isLoggedIn) { - browser.setText(getContent()); + browser.setText(getContent()); } else { browser.setText("Signed in"); AmazonQView.showView(AmazonQChatWebview.ID); @@ -64,6 +68,15 @@ protected void handleAuthStatusChange(final boolean isLoggedIn) { private String getContent() { try { URL jsFile = PluginUtils.getResource("webview/build/assets/js/getStart.js"); + var jsParent = Path.of(jsFile.toURI()).getParent(); + var jsDirectoryPath = Path.of(jsParent.toUri()).normalize().toString(); + + server = setupVirtualServer(jsDirectoryPath); + if(server == null) { + return "Failed to load JS"; + } + var loginJsPath = server.getURI().toString()+"getStart.js"; + return String.format("\n" + "\n" + " \n" @@ -83,10 +96,15 @@ private String getContent() { + " });\n" + " \n" + " \n" - + "", jsFile.toString()); - } catch (IOException e) { + + "", loginJsPath); + } catch (IOException | URISyntaxException e) { return "Failed to load JS"; } } - + + @Override + public void dispose() { + stopVirtualServer(server); + super.dispose(); + } } From e30066e4cf5bf575d7c59c57780ef12693d3f438 Mon Sep 17 00:00:00 2001 From: Shruti Sinha Date: Thu, 26 Sep 2024 13:06:06 -0700 Subject: [PATCH 2/8] fix indent --- .../eclipse/amazonq/views/AmazonQView.java | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java index 2736481e..a134eea4 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java @@ -110,52 +110,53 @@ private void setupAuthStatusListeners() { AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getFeedbackDialogContributionAction()); } - /** - * Sets up virtual host mapping for the given path using jetty server - * @param jsPath - * @return server launched - */ - protected Server setupVirtualServer(String jsPath) { - Server server = null; - try { - server = new Server(0); - var servletContext = new ContextHandler(); - servletContext.setContextPath("/"); - servletContext.addVirtualHosts(new String[]{"localhost"}); - - var handler = new ResourceHandler(); - - ResourceFactory resourceFactory = ResourceFactory.of(server); - handler.setBaseResource(resourceFactory.newResource(jsPath)); - handler.setDirAllowed(true); - servletContext.setHandler(handler); - - server.setHandler(servletContext); + /** + * Sets up virtual host mapping for the given path using jetty server + * + * @param jsPath + * @return server launched + */ + protected Server setupVirtualServer(String jsPath) { + Server server = null; + try { + server = new Server(0); + var servletContext = new ContextHandler(); + servletContext.setContextPath("/"); + servletContext.addVirtualHosts(new String[] { "localhost" }); + + var handler = new ResourceHandler(); + + ResourceFactory resourceFactory = ResourceFactory.of(server); + handler.setBaseResource(resourceFactory.newResource(jsPath)); + handler.setDirAllowed(true); + servletContext.setHandler(handler); + + server.setHandler(servletContext); server.start(); - + return server; - + } catch (Exception e) { stopVirtualServer(server); PluginLogger.error("Error occurred while attempting to start a virtual server for " + jsPath, e); return null; } - } + } - protected void stopVirtualServer(Server server) { - if(server != null) { - try { + protected void stopVirtualServer(Server server) { + if (server != null) { + try { server.stop(); - } catch (Exception e) { + } catch (Exception e) { PluginLogger.error("Error occurred when attempting to stop the virtual server", e); } - } + } } @Override - public final void setFocus() { + public final void setFocus() { browser.setFocus(); - } + } /** * Disposes of the resources associated with this view. From e10afd4f1d3c363d51a6d1c94da4b66a035446da Mon Sep 17 00:00:00 2001 From: Shruti Sinha Date: Thu, 26 Sep 2024 13:09:06 -0700 Subject: [PATCH 3/8] indent fix 2 --- .../eclipse/amazonq/views/AmazonQView.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java index a134eea4..6f2d6314 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java @@ -104,11 +104,11 @@ private void setupActions(final Browser browser, final boolean isLoggedIn) { amazonQCommonActions = new AmazonQCommonActions(browser, isLoggedIn, getViewSite()); } - private void setupAuthStatusListeners() { - authStatusChangedListener = this::handleAuthStatusChange; - AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getSignoutAction()); - AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getFeedbackDialogContributionAction()); - } + private void setupAuthStatusListeners() { + authStatusChangedListener = this::handleAuthStatusChange; + AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getSignoutAction()); + AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getFeedbackDialogContributionAction()); + } /** * Sets up virtual host mapping for the given path using jetty server @@ -158,16 +158,16 @@ public final void setFocus() { browser.setFocus(); } - /** - * Disposes of the resources associated with this view. - * - * This method is called when the view is closed. It removes the authentication - * status change listener and the selection listener from the page. - */ - @Override - public void dispose() { - AuthUtils.removeAuthStatusChangeListener(authStatusChangedListener); - super.dispose(); - } + /** + * Disposes of the resources associated with this view. + * + * This method is called when the view is closed. It removes the authentication + * status change listener and the selection listener from the page. + */ + @Override + public void dispose() { + AuthUtils.removeAuthStatusChangeListener(authStatusChangedListener); + super.dispose(); + } } From fba32daa3a5ddd085c1d77ba68229c48cdcc3e67 Mon Sep 17 00:00:00 2001 From: Shruti Sinha Date: Thu, 26 Sep 2024 13:26:01 -0700 Subject: [PATCH 4/8] fix some checkstyle issues --- .../amazonq/views/AmazonQChatWebview.java | 5 +- .../eclipse/amazonq/views/AmazonQView.java | 130 +++++++++--------- 2 files changed, 67 insertions(+), 68 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java index 1d54d6b5..69561b33 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java @@ -4,7 +4,6 @@ package software.aws.toolkits.eclipse.amazonq.views; import java.nio.file.Path; - import org.eclipse.jetty.server.Server; import org.eclipse.swt.browser.BrowserFunction; import org.eclipse.swt.widgets.Composite; @@ -58,12 +57,12 @@ public Object function(final Object[] arguments) { } private String getContent() { - String jsFile = PluginUtils.getAwsDirectory(LspConstants.LSP_SUBDIRECTORY).resolve("amazonq-ui.js").toString(); + String jsFile = PluginUtils.getAwsDirectory(LspConstants.LSP_SUBDIRECTORY).resolve("amazonq-ui.js").toString(); var jsParent = Path.of(jsFile).getParent(); var jsDirectoryPath = Path.of(jsParent.toUri()).normalize().toString(); server = setupVirtualServer(jsDirectoryPath); - if(server == null) { + if (server == null) { return "Failed to load JS"; } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java index 6f2d6314..5caaf388 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java @@ -104,70 +104,70 @@ private void setupActions(final Browser browser, final boolean isLoggedIn) { amazonQCommonActions = new AmazonQCommonActions(browser, isLoggedIn, getViewSite()); } - private void setupAuthStatusListeners() { - authStatusChangedListener = this::handleAuthStatusChange; - AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getSignoutAction()); - AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getFeedbackDialogContributionAction()); - } - - /** - * Sets up virtual host mapping for the given path using jetty server - * - * @param jsPath - * @return server launched - */ - protected Server setupVirtualServer(String jsPath) { - Server server = null; - try { - server = new Server(0); - var servletContext = new ContextHandler(); - servletContext.setContextPath("/"); - servletContext.addVirtualHosts(new String[] { "localhost" }); - - var handler = new ResourceHandler(); - - ResourceFactory resourceFactory = ResourceFactory.of(server); - handler.setBaseResource(resourceFactory.newResource(jsPath)); - handler.setDirAllowed(true); - servletContext.setHandler(handler); - - server.setHandler(servletContext); - server.start(); - - return server; - - } catch (Exception e) { - stopVirtualServer(server); - PluginLogger.error("Error occurred while attempting to start a virtual server for " + jsPath, e); - return null; - } - } - - protected void stopVirtualServer(Server server) { - if (server != null) { - try { - server.stop(); - } catch (Exception e) { - PluginLogger.error("Error occurred when attempting to stop the virtual server", e); - } - } - } - - @Override - public final void setFocus() { - browser.setFocus(); - } - - /** - * Disposes of the resources associated with this view. - * - * This method is called when the view is closed. It removes the authentication - * status change listener and the selection listener from the page. - */ - @Override - public void dispose() { - AuthUtils.removeAuthStatusChangeListener(authStatusChangedListener); - super.dispose(); - } + private void setupAuthStatusListeners() { + authStatusChangedListener = this::handleAuthStatusChange; + AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getSignoutAction()); + AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getFeedbackDialogContributionAction()); + } + + /** + * Sets up virtual host mapping for the given path using jetty server + * + * @param jsPath + * @return server launched + */ + protected Server setupVirtualServer(String jsPath) { + Server server = null; + try { + server = new Server(0); + var servletContext = new ContextHandler(); + servletContext.setContextPath("/"); + servletContext.addVirtualHosts(new String[] { "localhost" }); + + var handler = new ResourceHandler(); + + ResourceFactory resourceFactory = ResourceFactory.of(server); + handler.setBaseResource(resourceFactory.newResource(jsPath)); + handler.setDirAllowed(true); + servletContext.setHandler(handler); + + server.setHandler(servletContext); + server.start(); + + return server; + + } catch (Exception e) { + stopVirtualServer(server); + PluginLogger.error("Error occurred while attempting to start a virtual server for " + jsPath, e); + return null; + } + } + + protected void stopVirtualServer(Server server) { + if (server != null) { + try { + server.stop(); + } catch (Exception e) { + PluginLogger.error("Error occurred when attempting to stop the virtual server", e); + } + } + } + + @Override + public final void setFocus() { + browser.setFocus(); + } + + /** + * Disposes of the resources associated with this view. + * + * This method is called when the view is closed. It removes the authentication + * status change listener and the selection listener from the page. + */ + @Override + public void dispose() { + AuthUtils.removeAuthStatusChangeListener(authStatusChangedListener); + super.dispose(); + } } From 112183989d9495e9f346427e14a2805927346016 Mon Sep 17 00:00:00 2001 From: Shruti Sinha Date: Thu, 26 Sep 2024 13:35:17 -0700 Subject: [PATCH 5/8] if condition checkstyle fixes --- .../amazonq/lsp/manager/DefaultLspManager.java | 9 +++++---- .../toolkits/eclipse/amazonq/views/AmazonQView.java | 12 ++++++------ .../eclipse/amazonq/views/ToolkitLoginWebview.java | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java index 5e8cb38d..e9da557b 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java @@ -61,10 +61,11 @@ public LspInstallation getLspInstallation() { } private static void makeExecutable(final Path filePath, PluginPlatform platform) throws IOException { - // don't set file permissions for windows as it has issues using the executable otherwise - if(platform == PluginPlatform.WINDOWS) { - return; - } + // don't set file permissions for windows as it has issues using the executable + // otherwise + if (platform == PluginPlatform.WINDOWS) { + return; + } var permissions = new HashSet<>(Arrays.asList(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE)); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java index 5caaf388..c5048cec 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java @@ -91,13 +91,13 @@ private void setupBrowser(final Composite parent) { browser.setBackground(black); parent.setBackground(black); } - + private int getBrowserStyle() { - var platform = PluginUtils.getPlatform(); - if(platform == PluginPlatform.WINDOWS) { - return SWT.EDGE; - } - return SWT.NATIVE; + var platform = PluginUtils.getPlatform(); + if (platform == PluginPlatform.WINDOWS) { + return SWT.EDGE; + } + return SWT.NATIVE; } private void setupActions(final Browser browser, final boolean isLoggedIn) { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java index 691163b3..c8f3ed34 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java @@ -72,7 +72,7 @@ private String getContent() { var jsDirectoryPath = Path.of(jsParent.toUri()).normalize().toString(); server = setupVirtualServer(jsDirectoryPath); - if(server == null) { + if (server == null) { return "Failed to load JS"; } var loginJsPath = server.getURI().toString()+"getStart.js"; From 30513462b4f6b284f44ccd1d616712658914f3c8 Mon Sep 17 00:00:00 2001 From: Shruti Sinha Date: Thu, 26 Sep 2024 14:10:43 -0700 Subject: [PATCH 6/8] fix plugin checkstyle issues --- .../amazonq/lsp/manager/DefaultLspManager.java | 2 +- .../amazonq/views/AmazonQChatWebview.java | 18 +++++++++--------- .../eclipse/amazonq/views/AmazonQView.java | 9 ++++----- .../amazonq/views/ToolkitLoginWebview.java | 12 ++++++------ 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java index e9da557b..fc381ac5 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java @@ -60,7 +60,7 @@ public LspInstallation getLspInstallation() { } } - private static void makeExecutable(final Path filePath, PluginPlatform platform) throws IOException { + private static void makeExecutable(final Path filePath, final PluginPlatform platform) throws IOException { // don't set file permissions for windows as it has issues using the executable // otherwise if (platform == PluginPlatform.WINDOWS) { diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java index 69561b33..775311c2 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java @@ -57,16 +57,16 @@ public Object function(final Object[] arguments) { } private String getContent() { - String jsFile = PluginUtils.getAwsDirectory(LspConstants.LSP_SUBDIRECTORY).resolve("amazonq-ui.js").toString(); + String jsFile = PluginUtils.getAwsDirectory(LspConstants.LSP_SUBDIRECTORY).resolve("amazonq-ui.js").toString(); var jsParent = Path.of(jsFile).getParent(); var jsDirectoryPath = Path.of(jsParent.toUri()).normalize().toString(); - + server = setupVirtualServer(jsDirectoryPath); if (server == null) { - return "Failed to load JS"; + return "Failed to load JS"; } - - var chatJsPath = server.getURI().toString()+"amazonq-ui.js"; + + var chatJsPath = server.getURI().toString() + "amazonq-ui.js"; return String.format("\n" + "\n" + "\n" @@ -74,7 +74,8 @@ private String getContent() { + " \n" + " " + " Chat UI\n" + " %s\n" @@ -129,11 +130,10 @@ protected final void handleAuthStatusChange(final boolean isLoggedIn) { } }); } - + @Override - public void dispose() { + public final void dispose() { stopVirtualServer(server); super.dispose(); } - } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java index c5048cec..9b82ead0 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java @@ -111,18 +111,17 @@ private void setupAuthStatusListeners() { } /** - * Sets up virtual host mapping for the given path using jetty server - * + * Sets up virtual host mapping for the given path using Jetty server. * @param jsPath * @return server launched */ - protected Server setupVirtualServer(String jsPath) { + protected Server setupVirtualServer(final String jsPath) { Server server = null; try { server = new Server(0); var servletContext = new ContextHandler(); servletContext.setContextPath("/"); - servletContext.addVirtualHosts(new String[] { "localhost" }); + servletContext.addVirtualHosts(new String[] {"localhost"}); var handler = new ResourceHandler(); @@ -143,7 +142,7 @@ protected Server setupVirtualServer(String jsPath) { } } - protected void stopVirtualServer(Server server) { + protected final void stopVirtualServer(final Server server) { if (server != null) { try { server.stop(); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java index c8f3ed34..087fbf50 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java @@ -57,7 +57,7 @@ protected void handleAuthStatusChange(final boolean isLoggedIn) { Display.getDefault().asyncExec(() -> { amazonQCommonActions.updateActionVisibility(isLoggedIn, getViewSite()); if (!isLoggedIn) { - browser.setText(getContent()); + browser.setText(getContent()); } else { browser.setText("Signed in"); AmazonQView.showView(AmazonQChatWebview.ID); @@ -70,13 +70,13 @@ private String getContent() { URL jsFile = PluginUtils.getResource("webview/build/assets/js/getStart.js"); var jsParent = Path.of(jsFile.toURI()).getParent(); var jsDirectoryPath = Path.of(jsParent.toUri()).normalize().toString(); - + server = setupVirtualServer(jsDirectoryPath); if (server == null) { - return "Failed to load JS"; + return "Failed to load JS"; } - var loginJsPath = server.getURI().toString()+"getStart.js"; - + var loginJsPath = server.getURI().toString() + "getStart.js"; + return String.format("\n" + "\n" + " \n" @@ -101,7 +101,7 @@ private String getContent() { return "Failed to load JS"; } } - + @Override public void dispose() { stopVirtualServer(server); From eb4dd729c17800008d9e1f5bcfaeb4db172c709e Mon Sep 17 00:00:00 2001 From: Shruti Sinha Date: Thu, 26 Sep 2024 16:51:54 -0700 Subject: [PATCH 7/8] use webkit as default browser style --- .../aws/toolkits/eclipse/amazonq/views/AmazonQView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java index 9b82ead0..e2bb3f61 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java @@ -97,7 +97,7 @@ private int getBrowserStyle() { if (platform == PluginPlatform.WINDOWS) { return SWT.EDGE; } - return SWT.NATIVE; + return SWT.WEBKIT; } private void setupActions(final Browser browser, final boolean isLoggedIn) { From 1ed84d2bfdf9a70a7ce393a0e7da43cdbfb382e3 Mon Sep 17 00:00:00 2001 From: Shruti Sinha Date: Thu, 26 Sep 2024 17:36:34 -0700 Subject: [PATCH 8/8] Create WebviewAssetServer as an abstraction and add posix file check --- .../lsp/manager/DefaultLspManager.java | 12 ++-- .../amazonq/util/WebviewAssetServer.java | 55 +++++++++++++++++++ .../amazonq/views/AmazonQChatWebview.java | 24 ++++---- .../eclipse/amazonq/views/AmazonQView.java | 47 ---------------- .../amazonq/views/ToolkitLoginWebview.java | 15 +++-- 5 files changed, 85 insertions(+), 68 deletions(-) create mode 100644 plugin/src/software/aws/toolkits/eclipse/amazonq/util/WebviewAssetServer.java diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java index fc381ac5..e4dd195f 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/manager/DefaultLspManager.java @@ -51,7 +51,7 @@ public LspInstallation getLspInstallation() { throw new RuntimeException("Could not find node executable or LSP file in the downloaded contents"); } - makeExecutable(nodeExecutable, platform); + makeExecutable(nodeExecutable); return new LspInstallation(nodeExecutable, lspJs); } catch (Exception e) { @@ -60,10 +60,8 @@ public LspInstallation getLspInstallation() { } } - private static void makeExecutable(final Path filePath, final PluginPlatform platform) throws IOException { - // don't set file permissions for windows as it has issues using the executable - // otherwise - if (platform == PluginPlatform.WINDOWS) { + private static void makeExecutable(final Path filePath) throws IOException { + if (!hasPosixFilePermissions(filePath)) { return; } var permissions = new HashSet<>(Arrays.asList(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, @@ -72,6 +70,10 @@ private static void makeExecutable(final Path filePath, final PluginPlatform pla Files.setPosixFilePermissions(filePath, permissions); } + private static boolean hasPosixFilePermissions(final Path path) { + return path.getFileSystem().supportedFileAttributeViews().contains("posix"); + } + private static Path findFileWithPrefix(final Path directory, final String prefix) throws IOException { try (var paths = Files.walk(directory)) { return paths diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/util/WebviewAssetServer.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/WebviewAssetServer.java new file mode 100644 index 00000000..662d8b37 --- /dev/null +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/util/WebviewAssetServer.java @@ -0,0 +1,55 @@ +package software.aws.toolkits.eclipse.amazonq.util; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.resource.ResourceFactory; + +public final class WebviewAssetServer { + + private Server server; + + /** + * Sets up virtual host mapping for the given path using Jetty server. + * @param jsPath + * @return boolean indicating if server can be successfully launched + */ + public boolean resolve(final String jsPath) { + try { + server = new Server(0); + var servletContext = new ContextHandler(); + servletContext.setContextPath("/"); + servletContext.addVirtualHosts(new String[] {"localhost"}); + + var handler = new ResourceHandler(); + + ResourceFactory resourceFactory = ResourceFactory.of(server); + handler.setBaseResource(resourceFactory.newResource(jsPath)); + handler.setDirAllowed(true); + servletContext.setHandler(handler); + + server.setHandler(servletContext); + server.start(); + return true; + + } catch (Exception e) { + stop(); + PluginLogger.error("Error occurred while attempting to start a virtual server for " + jsPath, e); + return false; + } + } + + public String getUri() { + return server.getURI().toString(); + } + + public void stop() { + if (server != null) { + try { + server.stop(); + } catch (Exception e) { + PluginLogger.error("Error occurred when attempting to stop the virtual server", e); + } + } + } +} diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java index db504dac..e189d482 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQChatWebview.java @@ -4,7 +4,6 @@ package software.aws.toolkits.eclipse.amazonq.views; import java.nio.file.Path; -import org.eclipse.jetty.server.Server; import org.eclipse.swt.browser.BrowserFunction; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; @@ -13,6 +12,7 @@ import software.aws.toolkits.eclipse.amazonq.util.PluginLogger; import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; import software.aws.toolkits.eclipse.amazonq.util.ThreadingUtils; +import software.aws.toolkits.eclipse.amazonq.util.WebviewAssetServer; import software.aws.toolkits.eclipse.amazonq.views.actions.AmazonQCommonActions; public class AmazonQChatWebview extends AmazonQView { @@ -20,7 +20,7 @@ public class AmazonQChatWebview extends AmazonQView { public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.AmazonQChatWebview"; private AmazonQCommonActions amazonQCommonActions; - private Server server; + private WebviewAssetServer webviewAssetServer; private final ViewCommandParser commandParser; private final ViewActionHandler actionHandler; @@ -61,21 +61,23 @@ private String getContent() { var jsParent = Path.of(jsFile).getParent(); var jsDirectoryPath = Path.of(jsParent.toUri()).normalize().toString(); - server = setupVirtualServer(jsDirectoryPath); - if (server == null) { + webviewAssetServer = new WebviewAssetServer(); + var result = webviewAssetServer.resolve(jsDirectoryPath); + if (!result) { return "Failed to load JS"; } - var chatJsPath = server.getURI().toString() + "amazonq-ui.js"; + var chatJsPath = webviewAssetServer.getUri() + "amazonq-ui.js"; return String.format(""" - Chat UI %s @@ -84,7 +86,7 @@ private String getContent() { %s - """, chatJsPath, generateCss(), generateJS(chatJsPath)); + """, chatJsPath, chatJsPath, generateCss(), generateJS(chatJsPath)); } private String generateCss() { @@ -138,7 +140,9 @@ protected final void handleAuthStatusChange(final boolean isLoggedIn) { @Override public final void dispose() { - stopVirtualServer(server); + if (webviewAssetServer != null) { + webviewAssetServer.stop(); + } super.dispose(); } } diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java index e2bb3f61..05648249 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/AmazonQView.java @@ -3,11 +3,6 @@ package software.aws.toolkits.eclipse.amazonq.views; import java.util.Set; - -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.server.handler.ResourceHandler; -import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.graphics.Color; @@ -110,48 +105,6 @@ private void setupAuthStatusListeners() { AuthUtils.addAuthStatusChangeListener(amazonQCommonActions.getFeedbackDialogContributionAction()); } - /** - * Sets up virtual host mapping for the given path using Jetty server. - * @param jsPath - * @return server launched - */ - protected Server setupVirtualServer(final String jsPath) { - Server server = null; - try { - server = new Server(0); - var servletContext = new ContextHandler(); - servletContext.setContextPath("/"); - servletContext.addVirtualHosts(new String[] {"localhost"}); - - var handler = new ResourceHandler(); - - ResourceFactory resourceFactory = ResourceFactory.of(server); - handler.setBaseResource(resourceFactory.newResource(jsPath)); - handler.setDirAllowed(true); - servletContext.setHandler(handler); - - server.setHandler(servletContext); - server.start(); - - return server; - - } catch (Exception e) { - stopVirtualServer(server); - PluginLogger.error("Error occurred while attempting to start a virtual server for " + jsPath, e); - return null; - } - } - - protected final void stopVirtualServer(final Server server) { - if (server != null) { - try { - server.stop(); - } catch (Exception e) { - PluginLogger.error("Error occurred when attempting to stop the virtual server", e); - } - } - } - @Override public final void setFocus() { browser.setFocus(); diff --git a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java index 00c40188..7cfa4ac9 100644 --- a/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java +++ b/plugin/src/software/aws/toolkits/eclipse/amazonq/views/ToolkitLoginWebview.java @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 package software.aws.toolkits.eclipse.amazonq.views; -import org.eclipse.jetty.server.Server; import java.io.IOException; import java.net.URISyntaxException; @@ -15,6 +14,7 @@ import software.aws.toolkits.eclipse.amazonq.util.AuthUtils; import software.aws.toolkits.eclipse.amazonq.util.PluginUtils; import software.aws.toolkits.eclipse.amazonq.util.ThreadingUtils; +import software.aws.toolkits.eclipse.amazonq.util.WebviewAssetServer; import software.aws.toolkits.eclipse.amazonq.views.actions.AmazonQCommonActions; public final class ToolkitLoginWebview extends AmazonQView { @@ -22,7 +22,7 @@ public final class ToolkitLoginWebview extends AmazonQView { public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.ToolkitLoginWebview"; private AmazonQCommonActions amazonQCommonActions; - private Server server; + private WebviewAssetServer webviewAssetServer; private final ViewCommandParser commandParser; private final ViewActionHandler actionHandler; @@ -71,11 +71,12 @@ private String getContent() { var jsParent = Path.of(jsFile.toURI()).getParent(); var jsDirectoryPath = Path.of(jsParent.toUri()).normalize().toString(); - server = setupVirtualServer(jsDirectoryPath); - if (server == null) { + webviewAssetServer = new WebviewAssetServer(); + var result = webviewAssetServer.resolve(jsDirectoryPath); + if (!result) { return "Failed to load JS"; } - var loginJsPath = server.getURI().toString() + "getStart.js"; + var loginJsPath = webviewAssetServer.getUri() + "getStart.js"; return String.format(""" @@ -106,7 +107,9 @@ private String getContent() { @Override public void dispose() { - stopVirtualServer(server); + if (webviewAssetServer != null) { + webviewAssetServer.stop(); + } super.dispose(); } }