Loading e-Knife/src/main/java/eknife/erlang/ErlConnection.java +80 −46 Original line number Diff line number Diff line /* * e-Knife, a program slicing tool for Erlang based on the EDG * Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit. * EDG, a library to generate and slice Expression Dependence Graphs. * Copyright (c) 2021-2023. David Insa, Carlos Galindo, Sergio Pérez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as Loading @@ -18,10 +18,7 @@ package eknife.erlang; import com.ericsson.otp.erlang.OtpConnection; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpPeer; import com.ericsson.otp.erlang.OtpSelf; import com.ericsson.otp.erlang.*; import eknife.config.Config; import misc.util.Flusher; import misc.util.Flusher.Output; Loading @@ -30,8 +27,17 @@ import java.io.File; import java.io.IOException; import java.util.UUID; public class ErlConnection { /** * Handles the connection between Java and an Erlang process. * <br> * Important configuration settings: {@link #START_SERVER}, {@link #SERVER_COOKIE}, {@link #SERVER_NAME} and * {@link #RANDOMIZE_CLIENT_NAME} * <br> * General usage: {@code sendReceive(...)} handles everything, unless you want the connection to persist across * multiple calls. In that case you need to {@link #connect()} first. Otherwise, a connection is established and * closed for every RPC. */ public class ErlConnection { /** Starting the server can be time-consuming. For anything but standalone use, it might be better to start it * in the background and re-using the same server over different invocations of e-Knife. */ private final static boolean START_SERVER = Boolean.parseBoolean(System.getProperty("erlServer.autoStart", "true")); Loading @@ -43,19 +49,28 @@ public class ErlConnection private static final boolean RANDOMIZE_CLIENT_NAME = Boolean.parseBoolean(System.getProperty("erlServer.randomizeClientName", "false")); private static final String defaultClientNodeName = "client"; private static final ErlConnection instance = new ErlConnection(); public static ErlConnection getInstance() { return instance; } private final Config config = Config.getConfig(); private String serverNodeName; private OtpConnection connection; private Process serverProcess; public boolean isConnected() { public boolean isConnected() { return this.connection != null && this.connection.isConnected(); } public void connect() { /** * Connects or reconnects to an Erlang process, with a maximum of 10 retries if the connection fails. * Behaviour is determined by Java properties, see {@link ErlConnection class JavaDoc}. * @throws RuntimeException When the connection cannot be established even after retrying. */ public void connect() { if (this.isConnected()) this.disconnect(); Loading @@ -63,8 +78,7 @@ public class ErlConnection Exception lastException = null; for (int id = 0; id < 10 && !connected; id++) try { try { String clientNodeName = RANDOMIZE_CLIENT_NAME ? UUID.randomUUID().toString() : defaultClientNodeName + id; this.serverNodeName = START_SERVER ? SERVER_NAME + id : SERVER_NAME; if (START_SERVER) Loading @@ -73,67 +87,87 @@ public class ErlConnection OtpPeer serverNode = new OtpPeer(this.serverNodeName + "@localhost"); this.connection = clientNode.connect(serverNode); connected = true; } catch (Exception e) { } catch (Exception e) { lastException = e; this.closeServer(); } if (!connected) throw new RuntimeException("Could not create and connect to an erlang process!", lastException); } public void disconnect() { /** * Sever the connection between Java and Erlang, and shut the Erlang process * down if it was started by this object. * @throws RuntimeException If not connected or if an error occurs while closing and shutting the process. */ private void disconnect() { if (!this.isConnected()) throw new RuntimeException("The connection is not established"); try { try { if (this.connection != null) this.connection.close(); this.closeServer(); } catch (Exception e) { } catch (Exception e) { throw new RuntimeException(e); } } private void openServer() throws Exception { // Start an `erl` process with a shell, so that it has some output, and we can // measure using Flusher that it has started. /** * Start an Erlang process to act as a server for our RPCs. * @throws IOException Caused by {@link ProcessBuilder#start() starting} the process. */ private void openServer() throws IOException { // Skip the -noshell argument to produce some output and be able to know when the Erlang process has started. final File scriptsFile = this.config.getScriptsFile(); serverProcess = new ProcessBuilder("erl", "-pa", scriptsFile.getAbsolutePath(), "-name", this.serverNodeName + "@localhost", "-setcookie", SERVER_COOKIE).start(); // Wait until the output of the process has been consumed (initial shell prompt). final Flusher flusher = new Flusher(this.serverProcess, true, true); flusher.start(); while (flusher.getOutput(Output.Standard).isEmpty() && flusher.getOutput(Output.Error).isEmpty()); } private void closeServer() { /** * Kill the Erlang process. */ private void closeServer() { if (this.serverProcess != null) this.serverProcess.destroy(); } public void send(String module, String function, OtpErlangObject[] message) { try { this.connection.sendRPC(module, function, message); } catch (IOException e) { throw new RuntimeException(e); public OtpErlangObject sendReceive(String module, String function, String... arguments) { return sendReceive(module, function, string2atom(arguments)); } } public OtpErlangObject receive() { try { /** * Perform a remote process call (RPC) to the currently connected Erlang process. * @param module Module of the RPC * @param function Function of the RPC, within the given module. * @param arguments List of arguments passed to the function. Must match the arity of the function being called. * @return The result returned by the RPC. * @throws RuntimeException For connection and deserialization errors (or if the connection is inactive). */ public OtpErlangObject sendReceive(String module, String function, OtpErlangObject... arguments) { boolean connected = isConnected(); if (!connected) connect(); try { this.connection.sendRPC(module, function, arguments); return this.connection.receiveMsg().getMsg(); } catch (Exception e) { } catch (Exception e) { throw new RuntimeException(e); } finally { if (!connected) disconnect(); } } /** Convert an array of strings to Erlang atoms. */ private OtpErlangObject[] string2atom(String... args) { final OtpErlangObject[] arguments = new OtpErlangObject[args.length]; for (int argIndex = 0; argIndex < args.length; argIndex++) arguments[argIndex] = new OtpErlangAtom(args[argIndex]); return arguments; } } e-Knife/src/main/java/eknife/erlang/ErlangCodeFactory.java +7 −14 Original line number Diff line number Diff line /* * e-Knife, a program slicing tool for Erlang based on the EDG * Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit. * EDG, a library to generate and slice Expression Dependence Graphs. * Copyright (c) 2021-2023. David Insa, Carlos Galindo, Sergio Pérez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as Loading @@ -18,21 +18,15 @@ package eknife.erlang; import java.io.File; import java.util.*; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangList; import com.ericsson.otp.erlang.OtpErlangLong; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpErlangString; import com.ericsson.otp.erlang.OtpErlangTuple; import com.ericsson.otp.erlang.*; import edg.LDASTNodeInfo; import edg.graph.EDG; import edg.graph.Node; import eknife.config.Config; import java.io.File; import java.util.*; public class ErlangCodeFactory { /********************************************************************************************************************************/ Loading @@ -49,8 +43,7 @@ public class ErlangCodeFactory final ErlangCodeFactory erlangFactory = new ErlangCodeFactory(edg, slice); final OtpErlangList outDir = new OtpErlangList(outputFile.getAbsolutePath()); final OtpErlangList asts = erlangFactory.generate(); final Launcher launcher = Launcher.getLauncher(); launcher.launch("saver", "save", outDir, asts); ErlConnection.getInstance().sendReceive("saver", "save", outDir, asts); } /********************************************************************************************************************************/ Loading e-Knife/src/main/java/eknife/erlang/ErlangLASTFactory.java +3 −4 Original line number Diff line number Diff line /* * e-Knife, a program slicing tool for Erlang based on the EDG * Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit. * EDG, a library to generate and slice Expression Dependence Graphs. * Copyright (c) 2021-2023. David Insa, Carlos Galindo, Sergio Pérez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as Loading Loading @@ -43,8 +43,7 @@ public class ErlangLASTFactory extends LASTFactory } public static LAST createLAST(String sourcePath, boolean generateArcs) { final Launcher launcher = Launcher.getLauncher(); final OtpErlangObject response = launcher.launch("ast", "getASTs", sourcePath); final OtpErlangObject response = ErlConnection.getInstance().sendReceive("ast", "getASTs", sourcePath); final OtpErlangTuple tuple = (OtpErlangTuple) response; if (!(tuple.elementAt(1) instanceof OtpErlangList)) throw new RuntimeException("The erlang program could not be loaded: " + tuple.elementAt(1).toString()); Loading e-Knife/src/main/java/eknife/erlang/Launcher.javadeleted 100644 → 0 +0 −83 Original line number Diff line number Diff line /* * e-Knife, a program slicing tool for Erlang based on the EDG * Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package eknife.erlang; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangObject; public class Launcher { private static final Launcher launcher = new Launcher(); public static Launcher getLauncher() { return Launcher.launcher; } private final ErlConnection erlConnection = new ErlConnection(); private Launcher() { } public void open() { this.erlConnection.connect(); } public void close() { this.erlConnection.disconnect(); } public OtpErlangObject launch(String module, String function) { final OtpErlangObject[] arguments = new OtpErlangObject[0]; return this.launch(module, function, arguments); } public OtpErlangObject launch(String module, String function, String... args) { final OtpErlangObject[] arguments = this.getArgs(args); return this.launch(module, function, arguments); } public OtpErlangObject launch(String module, String function, OtpErlangObject... args) { final boolean connected = this.erlConnection.isConnected(); if (!connected) this.erlConnection.connect(); this.erlConnection.send(module, function, args); final OtpErlangObject response = this.erlConnection.receive(); if (!connected) this.erlConnection.disconnect(); return response; } private OtpErlangObject[] getArgs(String... args) { final int argsLength = args.length; final OtpErlangObject[] arguments = new OtpErlangObject[argsLength]; for (int argIndex = 0; argIndex < argsLength; argIndex++) arguments[argIndex] = new OtpErlangAtom(args[argIndex]); return arguments; } } No newline at end of file Loading
e-Knife/src/main/java/eknife/erlang/ErlConnection.java +80 −46 Original line number Diff line number Diff line /* * e-Knife, a program slicing tool for Erlang based on the EDG * Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit. * EDG, a library to generate and slice Expression Dependence Graphs. * Copyright (c) 2021-2023. David Insa, Carlos Galindo, Sergio Pérez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as Loading @@ -18,10 +18,7 @@ package eknife.erlang; import com.ericsson.otp.erlang.OtpConnection; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpPeer; import com.ericsson.otp.erlang.OtpSelf; import com.ericsson.otp.erlang.*; import eknife.config.Config; import misc.util.Flusher; import misc.util.Flusher.Output; Loading @@ -30,8 +27,17 @@ import java.io.File; import java.io.IOException; import java.util.UUID; public class ErlConnection { /** * Handles the connection between Java and an Erlang process. * <br> * Important configuration settings: {@link #START_SERVER}, {@link #SERVER_COOKIE}, {@link #SERVER_NAME} and * {@link #RANDOMIZE_CLIENT_NAME} * <br> * General usage: {@code sendReceive(...)} handles everything, unless you want the connection to persist across * multiple calls. In that case you need to {@link #connect()} first. Otherwise, a connection is established and * closed for every RPC. */ public class ErlConnection { /** Starting the server can be time-consuming. For anything but standalone use, it might be better to start it * in the background and re-using the same server over different invocations of e-Knife. */ private final static boolean START_SERVER = Boolean.parseBoolean(System.getProperty("erlServer.autoStart", "true")); Loading @@ -43,19 +49,28 @@ public class ErlConnection private static final boolean RANDOMIZE_CLIENT_NAME = Boolean.parseBoolean(System.getProperty("erlServer.randomizeClientName", "false")); private static final String defaultClientNodeName = "client"; private static final ErlConnection instance = new ErlConnection(); public static ErlConnection getInstance() { return instance; } private final Config config = Config.getConfig(); private String serverNodeName; private OtpConnection connection; private Process serverProcess; public boolean isConnected() { public boolean isConnected() { return this.connection != null && this.connection.isConnected(); } public void connect() { /** * Connects or reconnects to an Erlang process, with a maximum of 10 retries if the connection fails. * Behaviour is determined by Java properties, see {@link ErlConnection class JavaDoc}. * @throws RuntimeException When the connection cannot be established even after retrying. */ public void connect() { if (this.isConnected()) this.disconnect(); Loading @@ -63,8 +78,7 @@ public class ErlConnection Exception lastException = null; for (int id = 0; id < 10 && !connected; id++) try { try { String clientNodeName = RANDOMIZE_CLIENT_NAME ? UUID.randomUUID().toString() : defaultClientNodeName + id; this.serverNodeName = START_SERVER ? SERVER_NAME + id : SERVER_NAME; if (START_SERVER) Loading @@ -73,67 +87,87 @@ public class ErlConnection OtpPeer serverNode = new OtpPeer(this.serverNodeName + "@localhost"); this.connection = clientNode.connect(serverNode); connected = true; } catch (Exception e) { } catch (Exception e) { lastException = e; this.closeServer(); } if (!connected) throw new RuntimeException("Could not create and connect to an erlang process!", lastException); } public void disconnect() { /** * Sever the connection between Java and Erlang, and shut the Erlang process * down if it was started by this object. * @throws RuntimeException If not connected or if an error occurs while closing and shutting the process. */ private void disconnect() { if (!this.isConnected()) throw new RuntimeException("The connection is not established"); try { try { if (this.connection != null) this.connection.close(); this.closeServer(); } catch (Exception e) { } catch (Exception e) { throw new RuntimeException(e); } } private void openServer() throws Exception { // Start an `erl` process with a shell, so that it has some output, and we can // measure using Flusher that it has started. /** * Start an Erlang process to act as a server for our RPCs. * @throws IOException Caused by {@link ProcessBuilder#start() starting} the process. */ private void openServer() throws IOException { // Skip the -noshell argument to produce some output and be able to know when the Erlang process has started. final File scriptsFile = this.config.getScriptsFile(); serverProcess = new ProcessBuilder("erl", "-pa", scriptsFile.getAbsolutePath(), "-name", this.serverNodeName + "@localhost", "-setcookie", SERVER_COOKIE).start(); // Wait until the output of the process has been consumed (initial shell prompt). final Flusher flusher = new Flusher(this.serverProcess, true, true); flusher.start(); while (flusher.getOutput(Output.Standard).isEmpty() && flusher.getOutput(Output.Error).isEmpty()); } private void closeServer() { /** * Kill the Erlang process. */ private void closeServer() { if (this.serverProcess != null) this.serverProcess.destroy(); } public void send(String module, String function, OtpErlangObject[] message) { try { this.connection.sendRPC(module, function, message); } catch (IOException e) { throw new RuntimeException(e); public OtpErlangObject sendReceive(String module, String function, String... arguments) { return sendReceive(module, function, string2atom(arguments)); } } public OtpErlangObject receive() { try { /** * Perform a remote process call (RPC) to the currently connected Erlang process. * @param module Module of the RPC * @param function Function of the RPC, within the given module. * @param arguments List of arguments passed to the function. Must match the arity of the function being called. * @return The result returned by the RPC. * @throws RuntimeException For connection and deserialization errors (or if the connection is inactive). */ public OtpErlangObject sendReceive(String module, String function, OtpErlangObject... arguments) { boolean connected = isConnected(); if (!connected) connect(); try { this.connection.sendRPC(module, function, arguments); return this.connection.receiveMsg().getMsg(); } catch (Exception e) { } catch (Exception e) { throw new RuntimeException(e); } finally { if (!connected) disconnect(); } } /** Convert an array of strings to Erlang atoms. */ private OtpErlangObject[] string2atom(String... args) { final OtpErlangObject[] arguments = new OtpErlangObject[args.length]; for (int argIndex = 0; argIndex < args.length; argIndex++) arguments[argIndex] = new OtpErlangAtom(args[argIndex]); return arguments; } }
e-Knife/src/main/java/eknife/erlang/ErlangCodeFactory.java +7 −14 Original line number Diff line number Diff line /* * e-Knife, a program slicing tool for Erlang based on the EDG * Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit. * EDG, a library to generate and slice Expression Dependence Graphs. * Copyright (c) 2021-2023. David Insa, Carlos Galindo, Sergio Pérez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as Loading @@ -18,21 +18,15 @@ package eknife.erlang; import java.io.File; import java.util.*; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangList; import com.ericsson.otp.erlang.OtpErlangLong; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpErlangString; import com.ericsson.otp.erlang.OtpErlangTuple; import com.ericsson.otp.erlang.*; import edg.LDASTNodeInfo; import edg.graph.EDG; import edg.graph.Node; import eknife.config.Config; import java.io.File; import java.util.*; public class ErlangCodeFactory { /********************************************************************************************************************************/ Loading @@ -49,8 +43,7 @@ public class ErlangCodeFactory final ErlangCodeFactory erlangFactory = new ErlangCodeFactory(edg, slice); final OtpErlangList outDir = new OtpErlangList(outputFile.getAbsolutePath()); final OtpErlangList asts = erlangFactory.generate(); final Launcher launcher = Launcher.getLauncher(); launcher.launch("saver", "save", outDir, asts); ErlConnection.getInstance().sendReceive("saver", "save", outDir, asts); } /********************************************************************************************************************************/ Loading
e-Knife/src/main/java/eknife/erlang/ErlangLASTFactory.java +3 −4 Original line number Diff line number Diff line /* * e-Knife, a program slicing tool for Erlang based on the EDG * Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit. * EDG, a library to generate and slice Expression Dependence Graphs. * Copyright (c) 2021-2023. David Insa, Carlos Galindo, Sergio Pérez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as Loading Loading @@ -43,8 +43,7 @@ public class ErlangLASTFactory extends LASTFactory } public static LAST createLAST(String sourcePath, boolean generateArcs) { final Launcher launcher = Launcher.getLauncher(); final OtpErlangObject response = launcher.launch("ast", "getASTs", sourcePath); final OtpErlangObject response = ErlConnection.getInstance().sendReceive("ast", "getASTs", sourcePath); final OtpErlangTuple tuple = (OtpErlangTuple) response; if (!(tuple.elementAt(1) instanceof OtpErlangList)) throw new RuntimeException("The erlang program could not be loaded: " + tuple.elementAt(1).toString()); Loading
e-Knife/src/main/java/eknife/erlang/Launcher.javadeleted 100644 → 0 +0 −83 Original line number Diff line number Diff line /* * e-Knife, a program slicing tool for Erlang based on the EDG * Copyright (c) 2021. David Insa, Sergio Pérez, Josep Silva, Salvador Tamarit. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ package eknife.erlang; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangObject; public class Launcher { private static final Launcher launcher = new Launcher(); public static Launcher getLauncher() { return Launcher.launcher; } private final ErlConnection erlConnection = new ErlConnection(); private Launcher() { } public void open() { this.erlConnection.connect(); } public void close() { this.erlConnection.disconnect(); } public OtpErlangObject launch(String module, String function) { final OtpErlangObject[] arguments = new OtpErlangObject[0]; return this.launch(module, function, arguments); } public OtpErlangObject launch(String module, String function, String... args) { final OtpErlangObject[] arguments = this.getArgs(args); return this.launch(module, function, arguments); } public OtpErlangObject launch(String module, String function, OtpErlangObject... args) { final boolean connected = this.erlConnection.isConnected(); if (!connected) this.erlConnection.connect(); this.erlConnection.send(module, function, args); final OtpErlangObject response = this.erlConnection.receive(); if (!connected) this.erlConnection.disconnect(); return response; } private OtpErlangObject[] getArgs(String... args) { final int argsLength = args.length; final OtpErlangObject[] arguments = new OtpErlangObject[argsLength]; for (int argIndex = 0; argIndex < argsLength; argIndex++) arguments[argIndex] = new OtpErlangAtom(args[argIndex]); return arguments; } } No newline at end of file