diff --git a/ide/ide.remote/src/org/netbeans/modules/remote/AsynchronousConnection.java b/ide/ide.remote/src/org/netbeans/modules/remote/AsynchronousConnection.java index a35371554d86..06de74f92787 100644 --- a/ide/ide.remote/src/org/netbeans/modules/remote/AsynchronousConnection.java +++ b/ide/ide.remote/src/org/netbeans/modules/remote/AsynchronousConnection.java @@ -21,6 +21,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -58,7 +61,7 @@ public Sender(InputStream in, OutputStream out) { while (true) { int id = Utils.readInt(in); int size = Utils.readInt(in); - byte[] data = in.readNBytes(size); + byte[] data = in.readNBytes(size + 1); PendingRequest request; synchronized (id2PendingRequest) { @@ -68,8 +71,13 @@ public Sender(InputStream in, OutputStream out) { if (request == null) { LOG.log(Level.SEVERE, "No pending request number {0}", id); } else { - Object value = Utils.gson.fromJson(new String(data, StandardCharsets.UTF_8), request.responseType); - request.pending.complete(value); + String dataText = new String(data, 1, data.length - 1, StandardCharsets.UTF_8); + if (data[0] == 'D') { + Object value = Utils.gson.fromJson(dataText, request.responseType); + request.pending.complete(value); + } else { + request.pending.completeExceptionally(new RuntimeException(dataText)); + } } } } catch (EndOfInput ex) { @@ -103,11 +111,15 @@ private void write(int id, Enum messageKind, Object message) throws IOExcepti } public CompletableFuture sendAndReceive(Enum task, Object request, Class responseType) throws IOException { + return sendAndReceive(task, request, (Type) responseType); + } + + public CompletableFuture sendAndReceive(Enum task, Object request, Type responseType) throws IOException { int id = nextId.getAndIncrement(); CompletableFuture result = new CompletableFuture<>(); synchronized (id2PendingRequest) { - id2PendingRequest.put(id, new PendingRequest<>((CompletableFuture) result, (Class) responseType)); + id2PendingRequest.put(id, new PendingRequest<>((CompletableFuture) result, responseType)); } write(id, task, request); @@ -115,7 +127,7 @@ public CompletableFuture sendAndReceive(Enum task, Object request, Cla return result; } - private record PendingRequest(CompletableFuture pending, Class responseType) {} + private record PendingRequest(CompletableFuture pending, Type responseType) {} } public static final class ReceiverBuilder> { @@ -156,12 +168,23 @@ public Task startReceiver() { E kind = Enum.valueOf(messageTypeClass, kindName); Handler handler = messageType2Handler.get(kind); Object dataValue = Utils.gson.fromJson(new String(dataBytes, StandardCharsets.UTF_8), handler.messageTypeDataClass); - handler.run(dataValue).thenAccept(r -> { - byte[] messageBytes = Utils.gson.toJson(r).getBytes(StandardCharsets.UTF_8); - byte[] output = new byte[messageBytes.length + 8]; + handler.run(dataValue).handle((result, exception) -> { + byte tagChar; + byte[] messageBytes; + if (exception == null) { + tagChar = 'D'; + messageBytes = Utils.gson.toJson(result).getBytes(StandardCharsets.UTF_8); + } else { + StringWriter data = new StringWriter(); + exception.printStackTrace(new PrintWriter(data)); + tagChar = 'E'; + messageBytes = data.toString().getBytes(StandardCharsets.UTF_8); + } + byte[] output = new byte[messageBytes.length + 8 + 1]; Utils.writeInt(output, 0, id); Utils.writeInt(output, 4, messageBytes.length); - System.arraycopy(messageBytes, 0, output, 8, messageBytes.length); + output[8] = tagChar; + System.arraycopy(messageBytes, 0, output, 9, messageBytes.length); synchronized (out) { try { out.write(output); @@ -170,6 +193,7 @@ public Task startReceiver() { Exceptions.printStackTrace(ex); } } + return null; }); } } catch (IOException ex) { diff --git a/ide/ide.remote/src/org/netbeans/modules/remote/RemoteInvocation.java b/ide/ide.remote/src/org/netbeans/modules/remote/RemoteInvocation.java new file mode 100644 index 000000000000..6d38578f660a --- /dev/null +++ b/ide/ide.remote/src/org/netbeans/modules/remote/RemoteInvocation.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.remote; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import org.netbeans.modules.remote.AsynchronousConnection.ReceiverBuilder; +import org.netbeans.modules.remote.AsynchronousConnection.Sender; + +/** + * + */ +public class RemoteInvocation { + + public static T caller(InputStream in, OutputStream out, Class intf) { + Sender sender = new Sender(in, out); + return (T) Proxy.newProxyInstance(RemoteInvocation.class.getClassLoader(), new Class[] {intf}, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String[] encodedParams; + + if (args == null) { + encodedParams = new String[0]; + } else { + encodedParams = Arrays.stream(args) + .map(Utils.gson::toJson) + .toArray(s -> new String[s]); + } + + InvocationData data = new InvocationData(encodeMethod(method), encodedParams); + Type returnType = method.getGenericReturnType(); + if (returnType == void.class) { + returnType = Void.class; + } + return sender.sendAndReceive(Task.INVOKE, data, returnType).get(); + } + }); + } + + public static void receiver(InputStream in, OutputStream out, Object delegate) { + Map encodedMethod2Method = Arrays.stream(delegate.getClass().getMethods()).collect(Collectors.toMap(m -> encodeMethod(m), m -> m)); + + new ReceiverBuilder<>(in, out, Task.class) + .addHandler(Task.INVOKE, InvocationData.class, data -> { + CompletableFuture result = new CompletableFuture<>(); + try { + Object[] args = new Object[data.parameters.length]; + Method toInvoke = encodedMethod2Method.get(data.methodNameAndSignature); + Type[] parameterTypes = toInvoke.getGenericParameterTypes(); + for (int i = 0; i < parameterTypes.length; i++) { + args[i] = Utils.gson.fromJson(data.parameters[i], parameterTypes[i]); + } + toInvoke.setAccessible(true); + result.complete(toInvoke.invoke(delegate, args)); + } catch (Throwable t) { + result.completeExceptionally(t); + } + return result; + }) + .startReceiver(); + } + + public static RuntimeException sneakyThrows(Throwable t) { + return doSneakyThrows(t); + } + + @SuppressWarnings("unchecked") + private static RuntimeException doSneakyThrows(Throwable t) throws T { + throw (T) t; + } + + public enum Task { + INVOKE; + } + + public static final class InvocationData { + public String methodNameAndSignature; + public String[] parameters; + + public InvocationData() { + } + + public InvocationData(String methodNameAndSignature, String[] parameters) { + this.methodNameAndSignature = methodNameAndSignature; + this.parameters = parameters; + } + + } + + private static String encodeMethod(Method m) { + StringBuilder result = new StringBuilder(); + result.append(m.getName()); + result.append("("); + for (Class p : m.getParameterTypes()) { + result.append(p.getCanonicalName()).append(";"); + } + result.append(")"); + return result.toString(); + } +} diff --git a/ide/ide.remote/src/org/netbeans/modules/remote/agent/prj/ProjectService.java b/ide/ide.remote/src/org/netbeans/modules/remote/agent/prj/ProjectService.java index 3a48e4810e02..f8abf9daad03 100644 --- a/ide/ide.remote/src/org/netbeans/modules/remote/agent/prj/ProjectService.java +++ b/ide/ide.remote/src/org/netbeans/modules/remote/agent/prj/ProjectService.java @@ -26,30 +26,28 @@ import java.io.PrintWriter; import java.io.Reader; import java.io.Writer; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import javax.swing.SwingUtilities; import org.netbeans.api.io.Hyperlink; import org.netbeans.api.io.OutputColor; import org.netbeans.api.io.ShowOperation; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectManager; -import org.netbeans.modules.remote.AsynchronousConnection.ReceiverBuilder; import org.netbeans.modules.remote.AsynchronousConnection.Sender; +import org.netbeans.modules.remote.RemoteInvocation; import org.netbeans.modules.remote.Service; import org.netbeans.modules.remote.StreamMultiplexor; import org.netbeans.modules.remote.Streams; import org.netbeans.modules.remote.Utils; -import org.netbeans.modules.remote.ide.prj.ProjectHandler.ContextBasedActionRequest; import org.netbeans.modules.remote.ide.prj.ProjectHandler.GetIORequest; import org.netbeans.modules.remote.ide.prj.ProjectHandler.IOTask; -import org.netbeans.modules.remote.ide.prj.ProjectHandler.LoadProjectResponse; -import org.netbeans.modules.remote.ide.prj.ProjectHandler.PathProjectRequest; -import org.netbeans.modules.remote.ide.prj.ProjectHandler.Task; +import org.netbeans.modules.remote.ide.prj.ProjectHandler.ProjectInterface; import org.netbeans.spi.io.InputOutputProvider; import org.netbeans.spi.project.ActionProvider; import org.openide.filesystems.FileObject; @@ -76,94 +74,82 @@ public void run(InputStream in, OutputStream out) { Streams commands = projectMultiplexor.getStreamsForChannel(0); Streams ioControl = projectMultiplexor.getStreamsForChannel(1); ToClientIOProvider ioProvider = new ToClientIOProvider(projectMultiplexor, ioControl); - new ReceiverBuilder(commands.in(), commands.out(), Task.class) - .addHandler(Task.LOAD_PROJECT, PathProjectRequest.class, p -> { - boolean isProject = false; - try { - FileObject prjDir = Utils.resolveLocalPath(p.path); - isProject = prjDir != null && ProjectManager.getDefault().findProject(prjDir) != null; - } catch (IOException | IllegalArgumentException ex) { - Exceptions.printStackTrace(ex); - } - - CompletableFuture result = new CompletableFuture<>(); - - result.complete(new LoadProjectResponse(isProject)); - - return result; - }).addHandler(Task.GET_SUPPORTED_ACTIONS, PathProjectRequest.class, p -> { - String[] actions = new String[0]; - try { - FileObject prjDir = Utils.resolveLocalPath(p.path); - Project prj = prjDir != null ? ProjectManager.getDefault().findProject(prjDir) : null; - if (prj != null) { - ActionProvider ap = prj.getLookup().lookup(ActionProvider.class); - - if (ap != null) { - actions = ap.getSupportedActions(); - } + RemoteInvocation.receiver(commands.in(), commands.out(), new ProjectInterface() { + @Override + public boolean loadProject(String projectDirectoryCandidate) { + try { + FileObject prjDir = Utils.resolveLocalPath(projectDirectoryCandidate); + return prjDir != null && ProjectManager.getDefault().findProject(prjDir) != null; + } catch (IOException | IllegalArgumentException ex) { + throw RemoteInvocation.sneakyThrows(ex); } - } catch (IOException | IllegalArgumentException ex) { - Exceptions.printStackTrace(ex); } - CompletableFuture result = new CompletableFuture<>(); + @Override + public String[] getSupportedActions(String projectDirectory) { + try { + FileObject prjDir = Utils.resolveLocalPath(projectDirectory); + Project prj = prjDir != null ? ProjectManager.getDefault().findProject(prjDir) : null; + if (prj != null) { + ActionProvider ap = prj.getLookup().lookup(ActionProvider.class); - result.complete(actions); + if (ap != null) { + return ap.getSupportedActions(); + } + } - return result; - }).addHandler(Task.IS_ENABLED_ACTIONS, ContextBasedActionRequest.class, p -> { - Boolean enabled = false; - try { - FileObject prjDir = Utils.resolveLocalPath(p.projectPath); - Project prj = prjDir != null ? ProjectManager.getDefault().findProject(prjDir) : null; - if (prj != null) { - ActionProvider ap = prj.getLookup().lookup(ActionProvider.class); - - if (ap != null) { - List context = new ArrayList<>(); - if (p.selectedFileObjectPath != null) { - context.add(Utils.resolveLocalPath(p.selectedFileObjectPath)); - } - enabled = ap.isActionEnabled(p.action, Lookups.fixed(context.toArray())); - } + return new String[0]; + } catch (IOException | IllegalArgumentException ex) { + throw RemoteInvocation.sneakyThrows(ex); } - } catch (IOException | IllegalArgumentException ex) { - Exceptions.printStackTrace(ex); } - CompletableFuture result = new CompletableFuture<>(); - - result.complete(enabled); - - return result; - }).addHandler(Task.INVOKE_ACTIONS, ContextBasedActionRequest.class, p -> { - CompletableFuture result = new CompletableFuture<>(); - Lookup inputOutputLookup = Lookups.fixed(ioProvider); - Lookups.executeWith(new ProxyLookup(Lookups.exclude(Lookup.getDefault(), InputOutputProvider.class, org.openide.windows.IOProvider.class), inputOutputLookup), () -> { + @Override + public boolean isActionEnabled(String projectDirectory, String command, String lookupSelectedFileObjectPath) { try { - FileObject prjDir = Utils.resolveLocalPath(p.projectPath); + FileObject prjDir = Utils.resolveLocalPath(projectDirectory); Project prj = prjDir != null ? ProjectManager.getDefault().findProject(prjDir) : null; if (prj != null) { ActionProvider ap = prj.getLookup().lookup(ActionProvider.class); if (ap != null) { List context = new ArrayList<>(); - if (p.selectedFileObjectPath != null) { - context.add(Utils.resolveLocalPath(p.selectedFileObjectPath)); + if (lookupSelectedFileObjectPath != null) { + context.add(Utils.resolveLocalPath(lookupSelectedFileObjectPath)); } - ap.invokeAction(p.action, Lookups.fixed(context.toArray())); + return ap.isActionEnabled(command, Lookups.fixed(context.toArray())); } } + return false; } catch (IOException | IllegalArgumentException ex) { - Exceptions.printStackTrace(ex); + throw RemoteInvocation.sneakyThrows(ex); } + } - - result.complete("done"); - }); - return result; - }).startReceiver(); + @Override + public void invokeAction(String projectDirectory, String command, String lookupSelectedFileObjectPath) { + Lookup inputOutputLookup = Lookups.fixed(ioProvider); + Lookups.executeWith(new ProxyLookup(Lookups.exclude(Lookup.getDefault(), InputOutputProvider.class, org.openide.windows.IOProvider.class), inputOutputLookup), () -> { + try { + FileObject prjDir = Utils.resolveLocalPath(projectDirectory); + Project prj = prjDir != null ? ProjectManager.getDefault().findProject(prjDir) : null; + if (prj != null) { + ActionProvider ap = prj.getLookup().lookup(ActionProvider.class); + + if (ap != null) { + List context = new ArrayList<>(); + if (lookupSelectedFileObjectPath != null) { + context.add(Utils.resolveLocalPath(lookupSelectedFileObjectPath)); + } + ap.invokeAction(command, Lookups.fixed(context.toArray())); + } + } + } catch (IOException | IllegalArgumentException ex) { + throw RemoteInvocation.sneakyThrows(ex); + } + }); + } + }); } public static final class ToClientIOProvider implements InputOutputProvider { diff --git a/ide/ide.remote/src/org/netbeans/modules/remote/ide/prj/ProjectHandler.java b/ide/ide.remote/src/org/netbeans/modules/remote/ide/prj/ProjectHandler.java index 7bbf8d0c2681..dcd475f593f2 100644 --- a/ide/ide.remote/src/org/netbeans/modules/remote/ide/prj/ProjectHandler.java +++ b/ide/ide.remote/src/org/netbeans/modules/remote/ide/prj/ProjectHandler.java @@ -26,13 +26,12 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import org.netbeans.api.io.IOProvider; import org.netbeans.api.io.InputOutput; import org.netbeans.api.io.OutputWriter; import org.netbeans.modules.remote.AsynchronousConnection.ReceiverBuilder; -import org.netbeans.modules.remote.AsynchronousConnection.Sender; +import org.netbeans.modules.remote.RemoteInvocation; import org.netbeans.modules.remote.StreamMultiplexor; import org.netbeans.modules.remote.Streams; import org.openide.filesystems.FileObject; @@ -44,14 +43,14 @@ */ public final class ProjectHandler { - private final Sender connection; + private final ProjectInterface outgoingProjectInterface; public ProjectHandler(InputStream in, OutputStream out) { StreamMultiplexor projectMultiplexor = new StreamMultiplexor(in, out); Streams commands = projectMultiplexor.getStreamsForChannel(0); Streams ioControl = projectMultiplexor.getStreamsForChannel(1); AtomicInteger nextChannel = new AtomicInteger(2); - this.connection = new Sender(commands.in(), commands.out()); + this.outgoingProjectInterface = RemoteInvocation.caller(commands.in(), commands.out(), ProjectInterface.class); Map channel2InputOutput = new HashMap<>(); //TODO: clear!! new ReceiverBuilder<>(ioControl.in(), ioControl.out(), IOTask.class) .addHandler(IOTask.GET_IO, GetIORequest.class, io -> { @@ -83,93 +82,26 @@ public ProjectHandler(InputStream in, OutputStream out) { } public boolean loadProject(FileObject folder) { - try { - String relativePath = "/" + folder.getPath(); - LoadProjectResponse response = connection.sendAndReceive(Task.LOAD_PROJECT, new PathProjectRequest(relativePath), LoadProjectResponse.class).get(); - return response.isProject; - } catch (IOException | InterruptedException | ExecutionException ex) { - Exceptions.printStackTrace(ex); - return false; - } + String relativePath = "/" + folder.getPath(); + return outgoingProjectInterface.loadProject(relativePath); } public String[] getSupportedActions(FileObject projectDir) { - try { - String relativePath = "/" + projectDir.getPath(); - String[] actions = connection.sendAndReceive(Task.GET_SUPPORTED_ACTIONS, new PathProjectRequest(relativePath), String[].class).get(); - return actions; - } catch (IOException | InterruptedException | ExecutionException ex) { - Exceptions.printStackTrace(ex); - return new String[0]; - } + String relativePath = "/" + projectDir.getPath(); + return outgoingProjectInterface.getSupportedActions(relativePath); } public boolean isActionEnabled(FileObject projectDir, String command, FileObject selectedFile) { - try { - String relativeProjectPath = "/" + projectDir.getPath(); - String relativeSelectedPath = selectedFile != null ? ("/" + selectedFile.getPath()) : null; - boolean enabled = connection.sendAndReceive(Task.IS_ENABLED_ACTIONS, new ContextBasedActionRequest(relativeProjectPath, command, relativeSelectedPath), Boolean.class).get(); - return enabled; - } catch (IOException | InterruptedException | ExecutionException ex) { - Exceptions.printStackTrace(ex); - return false; - } - } + String relativeProjectPath = "/" + projectDir.getPath(); + String relativeSelectedPath = selectedFile != null ? ("/" + selectedFile.getPath()) : null; - public void invokeAction(FileObject projectDir, String command, FileObject selectedFile) { - try { - String relativeProjectPath = "/" + projectDir.getPath(); - String relativeSelectedPath = selectedFile != null ? ("/" + selectedFile.getPath()) : null; - connection.sendAndReceive(Task.INVOKE_ACTIONS, new ContextBasedActionRequest(relativeProjectPath, command, relativeSelectedPath), Boolean.class).get(); - } catch (IOException | InterruptedException | ExecutionException ex) { - Exceptions.printStackTrace(ex); - } + return outgoingProjectInterface.isActionEnabled(relativeProjectPath, command, relativeSelectedPath); } - public static class PathProjectRequest { - public String path; - - public PathProjectRequest() { - } - - public PathProjectRequest(String path) { - this.path = path; - } - } - - public static class ContextBasedActionRequest { - public String projectPath; - public String action; - public String selectedFileObjectPath; - - public ContextBasedActionRequest() { - } - - public ContextBasedActionRequest(String projectPath, String action, String selectedFileObjectPath) { - this.projectPath = projectPath; - this.action = action; - this.selectedFileObjectPath = selectedFileObjectPath; - } - - - } - - public static class LoadProjectResponse { - public boolean isProject; - - public LoadProjectResponse() { - } - - public LoadProjectResponse(boolean isProject) { - this.isProject = isProject; - } - } - - public enum Task { - LOAD_PROJECT, - GET_SUPPORTED_ACTIONS, - IS_ENABLED_ACTIONS, - INVOKE_ACTIONS, + public void invokeAction(FileObject projectDir, String command, FileObject selectedFile) { + String relativeProjectPath = "/" + projectDir.getPath(); + String relativeSelectedPath = selectedFile != null ? ("/" + selectedFile.getPath()) : null; + outgoingProjectInterface.invokeAction(relativeProjectPath, command, relativeSelectedPath); } public enum IOTask { @@ -187,4 +119,11 @@ public GetIORequest(String name) { } } + + public interface ProjectInterface { + public boolean loadProject(String projectDirectoryCandidate); + public String[] getSupportedActions(String projectDirectory); + public boolean isActionEnabled(String projectDirectory, String command, String lookupSelectedFileObjectPath); + public void invokeAction(String projectDirectory, String command, String lookupSelectedFileObjectPath); + } } diff --git a/ide/ide.remote/test/unit/src/org/netbeans/modules/remote/AsynchronousConnectionTest.java b/ide/ide.remote/test/unit/src/org/netbeans/modules/remote/AsynchronousConnectionTest.java index 7879dc9aac7c..fd30fd2eff2a 100644 --- a/ide/ide.remote/test/unit/src/org/netbeans/modules/remote/AsynchronousConnectionTest.java +++ b/ide/ide.remote/test/unit/src/org/netbeans/modules/remote/AsynchronousConnectionTest.java @@ -105,6 +105,35 @@ public void testClose() throws Throwable { } } + public void testExceptional() throws Throwable { + var server = new ServerSocket(0); + var acceptedSocket = new AtomicReference(); + var connectThread = new Thread(() -> { + try { + Socket accepted = server.accept(); + acceptedSocket.set(accepted); + new ReceiverBuilder(accepted.getInputStream(), accepted.getOutputStream(), MessageType.class) + .addHandler(MessageType.ECHO, Integer.class, in -> { + CompletableFuture result = new CompletableFuture<>(); + result.completeExceptionally(new Exception()); + return result; + }).startReceiver(); + } catch (Throwable t) { + t.printStackTrace(); + } + }); + connectThread.start(); + var clientSocket = new Socket(server.getInetAddress(), server.getLocalPort()); + var sender = new Sender(clientSocket.getInputStream(), clientSocket.getOutputStream()); + CompletableFuture result = sender.sendAndReceive(MessageType.ECHO, 1, Integer.class); + try { + result.get(); + fail("Should throw an exception!"); + } catch (ExecutionException ex) { + //TODO: check? + } + } + public enum MessageType { ECHO; } diff --git a/ide/ide.remote/test/unit/src/org/netbeans/modules/remote/RemoteInvocationTest.java b/ide/ide.remote/test/unit/src/org/netbeans/modules/remote/RemoteInvocationTest.java new file mode 100644 index 000000000000..8d0da344ddb0 --- /dev/null +++ b/ide/ide.remote/test/unit/src/org/netbeans/modules/remote/RemoteInvocationTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.netbeans.modules.remote; + +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Arrays; +import java.util.List; +import static junit.framework.TestCase.assertEquals; +import org.netbeans.junit.NbTestCase; + +/** + * + */ +public class RemoteInvocationTest extends NbTestCase { + + public RemoteInvocationTest(String name) { + super(name); + } + + public void testCommunication() throws Throwable { + var server = new ServerSocket(0); + var ran = new boolean[1]; + var connectThread = new Thread(() -> { + try { + Socket accepted = server.accept(); + RemoteInvocation.receiver(accepted.getInputStream(), accepted.getOutputStream(), new Invocation() { + @Override + public String echo(String i) { + return i; + } + @Override + public String echo(String i, List list) { + return i + ":" + list.toString(); + } + @Override + public void run() { + ran[0] = true; + } + }); + } catch (Throwable t) { + t.printStackTrace(); + } + }); + connectThread.start(); + var clientSocket = new Socket(server.getInetAddress(), server.getLocalPort()); + var sender = RemoteInvocation.caller(clientSocket.getInputStream(), clientSocket.getOutputStream(), Invocation.class); + assertEquals("a", sender.echo("a")); + assertEquals("a:[b, c]", sender.echo("a", Arrays.asList("b", "c"))); + sender.run(); + assertTrue(ran[0]); + } + + public interface Invocation { + public String echo(String i); + public String echo(String i, List list); + public void run(); + } +} diff --git a/java/java.api.common/src/org/netbeans/modules/java/api/common/project/BaseActionProvider.java b/java/java.api.common/src/org/netbeans/modules/java/api/common/project/BaseActionProvider.java index 55e06ffb870a..d7c287d02879 100644 --- a/java/java.api.common/src/org/netbeans/modules/java/api/common/project/BaseActionProvider.java +++ b/java/java.api.common/src/org/netbeans/modules/java/api/common/project/BaseActionProvider.java @@ -220,7 +220,7 @@ protected final Project getProject() { @Messages("LBL_No_Build_XML_Found=The project does not have a build script.") @Override public void invokeAction( final String command, final Lookup context ) throws IllegalArgumentException { - assert EventQueue.isDispatchThread(); +// assert EventQueue.isDispatchThread(); if (isSupportedByDelegate(command)) { getDelegate().invokeAction(command, context); return; diff --git a/java/java.api.common/src/org/netbeans/modules/java/api/common/project/JavaActionProvider.java b/java/java.api.common/src/org/netbeans/modules/java/api/common/project/JavaActionProvider.java index a28d199b9bdb..ac57e433cd55 100644 --- a/java/java.api.common/src/org/netbeans/modules/java/api/common/project/JavaActionProvider.java +++ b/java/java.api.common/src/org/netbeans/modules/java/api/common/project/JavaActionProvider.java @@ -883,7 +883,7 @@ public boolean isActionEnabled( public void invokeAction( @NonNull final String command, @NonNull final Lookup context) throws IllegalArgumentException { - assert SwingUtilities.isEventDispatchThread(); +// assert SwingUtilities.isEventDispatchThread(); Optional.ofNullable(supportedActions.get(command)) .ifPresent((act) -> { final Context ctx = new Context( diff --git a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ScanDialog.java b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ScanDialog.java index 42806c305517..b45bd6819189 100644 --- a/java/java.sourceui/src/org/netbeans/api/java/source/ui/ScanDialog.java +++ b/java/java.sourceui/src/org/netbeans/api/java/source/ui/ScanDialog.java @@ -69,7 +69,7 @@ private ScanDialog () {} public static boolean runWhenScanFinished (final Runnable runnable, final String actionName) { assert runnable != null; assert actionName != null; - assert SwingUtilities.isEventDispatchThread(); +// assert SwingUtilities.isEventDispatchThread(); if (SourceUtils.isScanInProgress()) { class AL implements ActionListener {