DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How to Convert XLS to XLSX in Java
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • Java Virtual Threads and Scaling
  • Java’s Next Act: Native Speed for a Cloud-Native World

Trending

  • Doris: Unifying SQL Dialects for a Seamless Data Query Ecosystem
  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  • My LLM Journey as a Software Engineer Exploring a New Domain
  • Build an MCP Server Using Go to Connect AI Agents With Databases
  1. DZone
  2. Coding
  3. Java
  4. Running a Java Class as a Subprocess

Running a Java Class as a Subprocess

Learn more about how to run a Java class as a subprocess in this quick demonstration.

By 
Dan Newton user avatar
Dan Newton
·
May. 21, 19 · Tutorial
Likes (19)
Comment
Save
Tweet
Share
46.7K Views

Join the DZone community and get the full member experience.

Join For Free

Running a Java class (not a jar) as a subprocess is something I needed to do this week. More precisely, I wanted to spawn a new process from within a test, instead of running it inside the test directly (in-process). I don’t think this is anything fancy or a complex thing to do. But, this is not something I have ever needed to do before and didn’t know the exact code to write.

Luckily, a quick Google search and a few Stack Overflow posts later, I found the answer I needed. Although the answer is there, I am rewriting it here for my own benefit and as well as yours.

class JavaProcess {

  private JavaProcess() {
  }

  public static int exec(Class clazz, List<String> jvmArgs, List<String> args) throws IOException,
        InterruptedException {
    String javaHome = System.getProperty("java.home");
    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
    String classpath = System.getProperty("java.class.path");
    String className = clazz.getName();

    List<String> command = new ArrayList<>();
    command.add(javaBin);
    command.addAll(jvmArgs);
    command.add("-cp");
    command.add(classpath);
    command.add(className);
    command.addAll(args);

    ProcessBuilder builder = new ProcessBuilder(command);
    Process process = builder.inheritIO().start();
    process.waitFor();
    return process.exitValue();
  }
}


This static function takes in the Class that you want to execute along with any JVM arguments and arguments that the class’s main method is expecting. Having access to both sets of arguments allows full control over the execution of the subprocess. For example, you might want to execute your class with a low heap space to see how it copes under memory pressure (which is what I needed it for).

Note, for this to work, the class that you want to execute needs to have a main method. This is kind of important.

Accessing the path of the Java executable (stored in javaBin) allows you to execute the subprocess using the same version of Java as the main application. If javaBin was replaced by "java", then you run the risk of executing the subprocess with your machine’s default version of Java. That is probably fine, a lot of the time. But, there are likely to be situations where this is not desired.

Once the commands are all added to the command list, they are passed to the ProcessBuilder. The ProcessBuilder takes this list and uses each value contained in it to generate the command. Each value inside the command list is separated with spaces by the ProcessBuilder. There are other overloads of its constructor, one of which takes in a single string where you can manually define the whole command yourself. This removes the need for you to manually manage the addition of arguments to the command string.

The subprocess is started with its IO passing up to the process that executed it. This is required to see both any stdouts and stderrs it produces. inheritIO is a convenience method and can also be achieved by calling chaining the following code instead (also configures the stdin of the subprocess):

builder
    .redirectInput(ProcessBuilder.Redirect.INHERIT)
    .redirectOutput(ProcessBuilder.Redirect.INHERIT)
    .redirectError(ProcessBuilder.Redirect.INHERIT);


Finally waitFor tells the executing thread to wait for the spawned subprocess to finish. It does not matter if the process ends successfully or errors — as long as the subprocess finishes somehow. The main execution can carry on going. How the process finished is detailed by its exitValue. For example, 0 normally denotes a successful execution and 1 details an invalid syntax error. There are many other exit codes and they can all vary between applications.

Calling the exec method would look something like the below:

JavaProcess.exec(MyProcess.class, List.of("-Xmx200m"), List.of("argument"))


This executes the following command (or something close to it):

/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/bin/java -cp /playing-around-for-blogs MyProcess "argument"


I have cut out a lot of the paths included classpath to keep it a bit tidier. Yours will probably look much longer than this. It really depends on your application. The path in the command above is the bare minimum needed to get it to run (obviously customized for my machine).

The exec method is reasonably flexible and helpful in describing what is going on. Although, if you wish to make it more malleable and applicable in a wider range of situations, I recommend returning the ProcessBuilder itself from the method. Allowing you to reuse this piece of code in several places while providing the flexibility to configure the IO redirects as well as the power to decide whether to run the subprocess in the background or block and wait for it to finish. This would look something like:

public static ProcessBuilder exec(Class clazz, List<String> jvmArgs, List<String> args) {
  String javaHome = System.getProperty("java.home");
  String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
  String classpath = System.getProperty("java.class.path");
  String className = clazz.getName();

  List<String> command = new ArrayList<>();
  command.add(javaBin);
  command.addAll(jvmArgs);
  command.add("-cp");
  command.add(classpath);
  command.add(className);
  command.addAll(args);

  return new ProcessBuilder(command);
}


By utilizing either (or both) of these functions, you will now have the ability to run any class that exists in your application’s classpath. In my situation, this was very helpful in spawning subprocesses inside of an integration test without needing to pre-build any jars. This allowed control over JVM arguments, such as the memory of the subprocesses, which would not be configurable if run directly inside the existing process.

If you enjoyed this post or found it helpful (or both), then please feel free to follow me on Twitter at @LankyDanDev, and remember to share with anyone else who might find this useful!

Java (programming language)

Published at DZone with permission of Dan Newton, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How to Convert XLS to XLSX in Java
  • Recurrent Workflows With Cloud Native Dapr Jobs
  • Java Virtual Threads and Scaling
  • Java’s Next Act: Native Speed for a Cloud-Native World

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!