Unable to run pmd inside java process inside spring - java

I want to run pmd inside a java process(created using ProcessBuilder) from within spring as a service.
public class PMDService {
private ProcessBuilder processBuilder;
private void createProcess() {
processBuilder = new ProcessBuilder();
final Map<String, String> envMap = processBuilder.environment();
String path = envMap.get("Path");
path += "../../../../../../../static-code-analyzers/pmd/bin;";
envMap.put("Path", path);
}
public String getCommand(PMDParameters params) {
final StringJoiner command = new StringJoiner(" ");
command.add("cmd")
.add("/c")
.add("pmd")
.add("-d")
.add(params.getSourceCodePath())
.add("-f")
.add(params.getOutputFormat())
.add("-R")
.add(params.getResultSet())
.add(">")
.add(params.getResultsPath());
return command.toString();
}
public void runAnalyzer(PMDParameters params) throws IOException, InterruptedException {
createProcess();
processBuilder.command(getCommand(params));
final Process process = processBuilder.start();
process.waitFor();
}
When I test the rest endpoint using postman i get following error :
""message": "Cannot run program \"cmd /c pmd -d
C:/bootcamp/Spring/springbootcode/springbootdemo -f xml -R
rulesets/java/quickstart.xml > .\": CreateProcess error=2, The system
cannot find the file specified","
Input json in postman
{
"sourceCodePath": "C:/bootcamp/Spring/springbootcode/springbootdemo",
"resultsPath": ".",
"outputFormat": "xml",
"resultSet": "rulesets/java/quickstart.xml"
}

Analysis
In the provided piece of code the program and its arguments are being passed as the single string value as the ProcessBuilder ProcessBuilder.command(String... command) method parameter.
But that should not be the case: instead, the program and its arguments should be passed separately.
Solution
Let's use the ProcessBuilder ProcessBuilder.command(List<String> command) method.
We will prepare the string list appropriately: it will contain the program and its arguments.
The draft solution:
public List<String> getProgramAndArguments(PMDParameters params) {
final List<String> programAndArguments = new ArrayList<String>();
programAndArguments.add("cmd");
programAndArguments.add("/c");
programAndArguments.add("pmd");
programAndArguments.add("-d");
programAndArguments.add(params.getSourceCodePath());
programAndArguments.add("-f");
programAndArguments.add(params.getOutputFormat());
programAndArguments.add("-R");
programAndArguments.add(params.getResultSet());
programAndArguments.add(">");
programAndArguments.add(params.getResultsPath());
return programAndArguments;
}
public void runAnalyzer(PMDParameters params) throws IOException, InterruptedException {
createProcess();
processBuilder.command(getProgramAndArguments(params));
final Process process = processBuilder.start();
process.waitFor();
}
Additionally, please, make sure that params.getResultsPath() has the correct value by providing the correct input: now it is ..

Related

How can I run a java web application interacting with another java cli application?

I want to run a command line application and command it over the REST API. For example, this CLI-application has a login command where it first takes "Login" as a command then asks you to enter a password, after entering the password, the CLI-application asks you to enter the password again. I want this connection stay alive. I want to send any command to the CLI-application in the form of a REST request, and then get a response from CLI-app. The CLI-application constantly takes commands from the user and acts accordingly. I want the CLI-application to interact with the user through the REST. Does anyone have any ideas?
I used Quarkus as REST application and my CLI-application placed in the "C:/my_files/working/test_n_learn/wallet-cli/build/libs/wallet-cli.jar" directory.
public class ConnectToWallet {
private final Runtime rt;
private final String command ;
public Process process;
public ConnectToWallet() throws IOException {
rt = Runtime.getRuntime();
command = "java -jar C:/my_files/working/test_n_learn/wallet-cli/build/libs/wallet-cli.jar";
process = rt.exec(command);
}
}
public class In {
private String command;
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
}
#Path("/command")
public class ExampleResource {
private static final ConnectToWallet CONNECT;
static {
try {
CONNECT = new ConnectToWallet();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#POST
#Produces(MediaType.APPLICATION_JSON)
public Response hello(In in) throws IOException {
BufferedWriter processInput = new BufferedWriter(new OutputStreamWriter(CONNECT.process.getOutputStream()));
BufferedReader processOutput = new BufferedReader(new InputStreamReader(CONNECT.process.getInputStream()));
processInput.write(in.getCommand());
processInput.flush();
StringBuilder builder = new StringBuilder();
processOutput.lines().forEach(builder::append);
return Response.ok().entity(builder.toString()).build();
}
}
I used the process and runtime to execute but it only responds for the first commands and I don't know how to send the next commands and get the responses.

How to call a jar by passing arguments from a java class

I have a spring boot project and out of which i have created a jar, and i am calling this jar from another project by passing arguments.
Not able to get the output and it is getting stuck.
The below is the project from which i am getting a jar.
public class Demo1Application {
public static void main(String[] args) {
System.out.println("jar called");
for(String arg : args) {
System.out.println("next argument is"+ arg );
}
SpringApplication.run(Demo1Application.class, args);
}
}
Its a simple spring boot main class.
The below is the class of another project from which i want to invoke this jar by passing arguments.
public class AAAAAAAAAAAAAAAAA {
public static void main(String[] args) throws IOException, InterruptedException {
File jarFile = new File("D:\\NewConfigWorkSpace\\Demo1\\target\\Demo1-0.0.1-SNAPSHOT.jar");
Process p = Runtime.getRuntime().exec("java -jar D:\\NewConfigWorkSpace\\Demo1\\target\\Demo1-0.0.1-SNAPSHOT.jar bisnu mohan");
p.waitFor();
System.out.println("finished");
}
}
How to see the console when i am calling the jar, how to track how much execution has been happened.
What you need is the input stream of created process. This is what is normally returned to a console when you run your application.
Process p = Runtime.getRuntime().exec("java -jar D:\\NewConfigWorkSpace\\Demo1\\target\\Demo1-0.0.1-SNAPSHOT.jar bisnu mohan");
InputStream inputStream = p.getInputStream();
You can then read contents of it and print to a console of a running process like this:
StringBuilder outputLines = new StringBuilder();
String output;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
logger.info("Command execution output: " + line);
outputLines.append(line).append("\n");
}
} finally {
output = outputLines.toString().trim();
}
It would also be a good idea to handle error stream the same way, because you can then see if your subprocess returns some errors.
InputStream errorStream = p.getErrorStream();
Use this streams handling in a separate threads so they don't block each other.
From my point of view, you don't need to execute JAR via command line.
If you include JAR into your project you can just import SpringApplication from JAR and run it directly like this:
public class AAAAAAAAAAAAAAAAA {
public static void main(String[] args) throws IOException, InterruptedException {
SpringApplication.run(Demo1Application.class, args);
}
}

How do launch an external program on ubuntu shell (WSL) from Java on Windows

I'm on Windows, and i try to work on a Java application that was written to be use on a Linux OS, because the program will launch some shell script at some point.
I have WSL (Windows Subsystem for Linux, also know as Ubuntu bash), so executing shell script should not be a problem, but i have an error : 0x80070057
The code that launch the external process :
public Process startProcess(List<String> commands ) throws IOException {
ProcessBuilder etProcessBuilder= new ProcessBuilder(commands);
Process etProcess = etProcessBuilder.start();
ProcessOutputReader stdReader= new ProcessOutputReader(etProcess.getInputStream(), LOGGER::info);
ProcessOutputReader errReader= new ProcessOutputReader(etProcess.getErrorStream(), LOGGER::error);
new Thread(stdReader).start();
new Thread(errReader).start();
return etProcess;
}
The commands param are set with with something like this :
"/mnt/d/some/path/scripts/initEAF.sh"
"-argForTheScript"
"some value"
"-anotherArg"
"other value"
I also tried to add "bash.exe" as first command, but it doesn't seems to work.
The ProcessOutputReaderis a class to log the stream from the process
class ProcessOutputReader implements Runnable {
private final InputStream inputStream;
private Consumer<String> loggingFunction;
ProcessOutputReader(InputStream inputStream, Consumer<String> loggingFunction) {
this.inputStream = inputStream;
this.loggingFunction = loggingFunction;
}
private BufferedReader getBufferedReader(InputStream is) {
return new BufferedReader(new InputStreamReader(is));
}
#Override
public void run() {
BufferedReader br = getBufferedReader(inputStream);
String ligne;
try {
while ((ligne = br.readLine()) != null) {
loggingFunction.accept(ligne);
}
} catch (IOException e) {
LOGGER.error("Error occur while reading the output of process ", e);
}
}
}
Any idea is welcome.
*.sh is not an executable file.
You need run it by a shell, such as bash xxx.sh -args or sh xxx.sh -args if your java app run inside wsl.
If your java app run on Windows, it should be bash.exe -c xxx.sh

passing parameters through hashmap to function in java

i was trying to build a command line tool alike in java, for example, if i write down in console "dir c:/....", it will activate my Dir class and will get the "c:/...." path as a parameter to the Dir class, and doing so with hashmap.
i dont know how to pass parameters through the commandline and hashmap,
is it even possible?
every command has it's own class, which implements the main "Command" interface, with a doCommand() function.
after running the start() function in the CLI class, it should take commands and do the requested command.
Command Interface:
public interface Command {
public void doCommand();
}
my CLI class:
public class CLI {
BufferedReader in;
PrintWriter out;
HashMap<String, Command> hashMap;
Controller controller;
public CLI(Controller controller, BufferedReader in, PrintWriter out,
HashMap<String, Command> hashMap) {
this.in = in;
this.out = out;
this.hashMap = hashMap;
}
public void start() {
new Thread(new Runnable() {
#Override
public void run() {
try {
out.println("Enter a command please:");
String string = in.readLine();
while (!string.equals("exit")) {
Command command = hashMap.get(string);
command.doCommand();
string = in.readLine();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
lets take for example my DirCommmand, as i said before, which should recognize the "dir" string through my hashMap configuration, and should pass the next word as a string parameter for the path
public class DirCommand implements Command {
#Override
public void doCommand() {
System.out.println("doing dir command...");
}
}
and my hashmap configuration:
hashMap.put("dir", new DirCommand());
which sets in a diffrent class the hashMap configuration and pass it to the CLI class's hashMap object at the start of the project.
i would love for some help because i have no idea how to do so.
First of all, in order to pass the parameters to doCommand, what I would do is use a variable param list, like:
public interface Command {
public void doCommand(String... params);
}
Second, I would split the input string on spaces as:
String[] result = command.split(" ");
Finally, the command would be the result[0] and the rest you would pass to the doCommand method.

One "Java context" per thread?

Is it possible to launch a Java program from another Java program, just as if I were launching it using another Java command? When calling the main() method of a program from another program directly, the Java context is common to these both executions. I'm trying to have one Java context per thread.
Illustration:
src/com/project/ProjectLauncher.java
public class ProjectLauncher {
static {
PropertyConfigurator.configure("log4j.properties");
}
public static void main(String[] args) {
Logger.getLogger(ProjectLauncher.class).info("started!");
// Logs well as expected.
}
}
test/com/project/TestProject.java
public class TestProject extends TestCase {
public void testProject() {
ProjectLauncher.main(null);
Logger.getLogger(TestProject.class).info("tested!");
// The above line logs well, while log4j has been initialized in ProjectLauncher.
// I would like it to need its own initialization in this class.
}
}
I tried to launch the main method in another thread/runnable, but the logger is still initialized by ProjectLauncher.
Well when you start a Java process, its a new Instance of JVM. If you wish to start another JVM instance, then you need to start a separate process of it.
i.e.
List<String> command = new ArrayList<String>();
command.add("java");
command.add("ProjectLauncher");
ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);
final Process process = builder.start();
try {
process.waitFor();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
//if you wish to read the output of it then below code else you can omit it.
InputStream is = process.getErrorStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
Logger.getLogger(MyClass.class.getName()).severe(line);
}
Above we are ultimately starting a new process which in reality is java ProjectLauncher. In case if the class is not already compiled, then you will have to compile it similar to above but using javac instead of java and ProjectLauncher.java instead of ProjectLauncher etc.

Categories

Resources