CLIProjectInConsoleWindow

Contents


How to run a NetBeans project in an operating system console window

Written by Wade Chandler

This tutorial, more of an explained example really, shows you how to make a NetBeans Java Application project written as a command line interface application run in an operating system console. I wrote this as a user was experiencing a bug in NetBeans 5.5.x which doesn't work well with System.out.print statements. NetBeans 6.0 does not have this same issue. Attached is a working example project which shows how to do this for Windows. It is setup to do the same for all Unix like OS, but the cli.sh script is empty and will have to be filled in by whoever needs it. I'll display and explain the build script (ANT script) and how the cli.bat and cli.sh scripts play their part.

The ANT script (build.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<project name="NBCommandLineExample" default="default" basedir=".">
    <description>Builds, tests, and runs the project NBCommandLineExample.</description>
    <import file="nbproject/build-impl.xml"/>
    <!--

    There exist several targets which are by default empty and which can be 
    used for execution of your tasks. These targets are usually executed 
    before and after some main targets. They are: 

      -pre-init:                 called before initialization of project properties
      -post-init:                called after initialization of project properties
      -pre-compile:              called before javac compilation
      -post-compile:             called after javac compilation
      -pre-compile-single:       called before javac compilation of single file
      -post-compile-single:      called after javac compilation of single file
      -pre-compile-test:         called before javac compilation of JUnit tests
      -post-compile-test:        called after javac compilation of JUnit tests
      -pre-compile-test-single:  called before javac compilation of single JUnit test
      -post-compile-test-single: called after javac compilation of single JUunit test
      -pre-jar:                  called before JAR building
      -post-jar:                 called after JAR building
      -post-clean:               called after cleaning build products

    (Targets beginning with '-' are not intended to be called on their own.)

    Example of inserting an obfuscator after compilation could look like this:

        <target name="-post-compile">
            <obfuscate>
                <fileset dir="${build.classes.dir}"/>
            </obfuscate>
        </target>

    For list of available properties check the imported 
    nbproject/build-impl.xml file. 


    Another way to customize the build is by overriding existing main targets.
    The targets of interest are: 

      -init-macrodef-javac:     defines macro for javac compilation
      -init-macrodef-junit:     defines macro for junit execution
      -init-macrodef-debug:     defines macro for class debugging
      -init-macrodef-java:      defines macro for class execution
      -do-jar-with-manifest:    JAR building (if you are using a manifest)
      -do-jar-without-manifest: JAR building (if you are not using a manifest)
      run:                      execution of project 
      -javadoc-build:           Javadoc generation
      test-report:              JUnit report generation

    An example of overriding the target for project execution could look like this:

        <target name="run" depends="NBCommandLineExample-impl.jar">
            <exec dir="bin" executable="launcher.exe">
                <arg file="${dist.jar}"/>
            </exec>
        </target>

    Notice that the overridden target depends on the jar target and not only on 
    the compile target as the regular run target does. Again, for a list of available 
    properties which you can use, check the target you are overriding in the
    nbproject/build-impl.xml file. 

    -->
    <target name="-post-init">
        <!-- scripts to allow a new console to appear -->
        <property location="cli.bat" name="wincli"/>
        <property location="cli.sh" name="nixcli"/>
        
        <!-- Set thecli to the correct script depending on the type of OS -->
        <condition property="thecli" value="${wincli}">
            <os family="windows"/>
        </condition>
        <condition property="thecli" value="${nixcli}">
            <os family="unix"/>
        </condition>
        
        <!-- 
            Figure out where the Java home is. This is first determined by the platform.home
            as this will be set if the default Java platform is not being used for the project
            otherwise, java.home will be used as this will be a property of the running JVM.
        -->
        <condition property="prep_javaexec" value="${platform.home}/bin/java.exe">
            <and>
                <os family="windows"/>
                <isset property="${platform.home}"/>
            </and>
        </condition>
        <condition property="prep_javaexec" value="${platform.home}/bin/java">
            <and>
                <os family="unix"/>
                <isset property="${platform.home}"/>
            </and>
        </condition>
        <condition property="prep_javaexec" value="${java.home}/bin/java.exe">
            <and>
                <os family="windows"/>
                <not>
                    <isset property="${platform.home}"/>
                </not>
            </and>
        </condition>
        <condition property="prep_javaexec" value="${java.home}/bin/java">
            <and>
                <os family="unix"/>
                <not>
                    <isset property="${platform.home}"/>
                </not>
            </and>
        </condition>
        <property location="." name="local.work.dir"/>
        <condition property="work.dir" value="${local.work.dir}">
            <not>
                <isset property="work.dir"/>
            </not>
        </condition>
        <condition property="run.jvmargs" value="">
            <not>
                <isset property="run.jvmargs"/>
            </not>
        </condition>
        <condition property="application.args" value="">
            <not>
                <isset property="application.args"/>
            </not>
        </condition>
        <fail unless="main.class" message="You must at least set the main Java class." />
        <property location="${prep_javaexec}" name="javaexec"/>
        <path path="${run.classpath}" id="javaexec.run.classpath.prep"/>
        <pathconvert targetos="windows" property="javaexec.windows.run.classpath" refid="javaexec.run.classpath.prep"/>
        <pathconvert targetos="unix" property="javaexec.nix.run.classpath" refid="javaexec.run.classpath.prep"/>
        <condition property="javaexec.run.classpath" value="${javaexec.windows.run.classpath}">
            <os family="windows"/>
        </condition>
        <condition property="javaexec.run.classpath" value="${javaexec.nix.run.classpath}">
            <os family="unix"/>
        </condition>
    </target>
    <target name="run" depends="compile">
        <exec dir="${work.dir}" executable="${thecli}" spawn="true">
            <arg value="${javaexec}"/>
            <arg value="-cp"/>
            <arg value='"${javaexec.run.classpath}"'/>
            <arg value="${run.jvmargs}"/>
            <arg value="${main.class}"/>
            <arg value="${application.args}"/>
        </exec>
    </target>
    
    <target name="debug" depends="compile">
        <echo message="The debugger is running in shared memory at address '${main.class}'"/>
        <!-- 
            Notice the atributes using '', the 's are important as the argument needs the "" 
            to be passed to keep certain characters from being removed.
        -->
        <exec dir="${work.dir}" executable="${thecli}" spawn="true">
            <arg value="${javaexec}"/>
            <arg value="-cp"/>
            <arg value='"${javaexec.run.classpath}"'/>
            <arg value="${run.jvmargs}"/>
            <arg value="-Xdebug"/>
            <!--
                There is a message which will be in the console which explains the debugger is listening
                at a memory address. To make the message go away you can use suspend=n below, however
                the application will start running as soon as it starts without giving you the
                opportunity to set any breakpoints before any user input areas.
            -->
            <arg value='"-Xrunjdwp:transport=dt_shmem,address=${main.class},server=y,suspend=y"'/>
            <arg value="${main.class}"/>
            <arg value="${application.args}"/>
        </exec>
        <nbjpdaconnect name="${main.class}" address="${main.class}" transport="dt_shmem"/>
    </target>
    
</project>


The Windows Batch file (cli.bat)

@echo off
REM A Windows Batch script which is used to launch Java in a separate 
REM console from NetBeans so developers can see exactly how their application 
REM runs on the operating system
set args=

:next
if '%1'=='' goto endloop
set args=%args% %1
shift
goto next
:endloop
start /WAIT %args%
REM If you ever have problems with the application running
REM comment out the above line and then comment in the below line
REM start /WAIT echo %args%

Explanation of build.xml and cli.bat

ANT can run external processes using the <exec> tag. The problem is these tasks are not normally run in a console window, and the input and output of these processes is filtered through ANT. The user never sees an extra window displaying a console. The application runs in the background other than the IO being filtered through ANT and the IDE. Sometimes this makes it hard to see how an application actually behaves, and especially has issues in NB 5.5.x as there is a bug in the way calls such as System.out.print work versus System.out.println. NB 6.0 makes this easier. Regardless, running CLI applications which require user interaction don't look exactly as they will deployed when normally run inside the IDE. This is where the OS scripts and ANT <exec> come in handy.

cli.bat will cause Windows to open a new console window as the java.exe process is a console application. Something similar can be done for all Unix flavors. Probably xterm will need to be used, but I don't have a Mac, so I figured this would be enough to get you going with what you need if needed. I also figure Unix and Linux users will be more proficient with these type things as it is common to write OS scripts and specific source code for the OS. So, I have this done for Windows first. When I have more time I'll come up with a solution on my Linux machine which should work with all (if not most Unix flavors). I'll probably have to have help to make sure it works on Mac OSX correctly.

CLI Caveats

The scripts are pretty self explanatory if you examine them. There are some caveats which you need to be aware. If the caveats are not observed then there will be problems.

  • When adding arguments under Project Properties/Run you need to wrap every argument with double quotes/English quotation marks. If you forget to do this multiple arguments will be passed as a single argument. This applies to both JVM arguments and application arguments
  • In build.xml certain <exec> <arg> sub-elements wrap the value attribute with single quotes/English apostrophe marks. If you remove those marks the parameters will not be passed correctly. The argument for debug "-Xrunjdwp:transport=dt_shmem,address=${main.class},server=y,suspend=y" is a good example. It will be corrupted by ANT or the OS CLI interface if the values are not wrapped. The classpath is another example. If there are spaces in directories they need to be wrapped in quotation marks, and they need to be wrapped anyways as the ';' or ':' will be removed by ANT or the OS CLI interface, the only way to have the actual value contain quotations is to wrap the entire value in apostrophe/single quotes. So, don't remove the single quotes from around the values where they are used.
  • When run, at least on Windows, the build script will complete before the actual application run or debug session is over, and the IDE will output that project execution took X number of seconds. This differs from normal project execution and debug in the NetBeans IDE. The session doesn't end until the application ends or you terminate the debug session and the application.
  • During a debug session, no projects sources will automatically be checked. When the debugger starts you will experience strange breakpoint behavior if the projects the sources are not checked/selected to be used. To select the sources go to Window/Debugging/Sources, locate your project src directory entry, and make sure you check the check box beside it. Breakpoints and single step and step over should then work normally.
  • For debug, the way the script is setup, it will act mostly normal, except for as noted above, and the application is no longer managed by the IDE. Thus, stopping the debug session does not close the application. The CLI application can only be ended by a CTRL-C in the console window or by normal program termination. You can also click the X in the top of the window of the console. Starting a debug session works as usual. Just tell the IDE to debug the main project or a specific project, and the application will be run with a debugger in the JVM. The ANT script tells NetBeans to connect the debugger automatically to the running process.
  • The application, running process, is not managed by the IDE, so the application will not show up under the running processes the IDE knows about. The application can not be terminated by the IDE. It may only be terminated by normal program termination, CTRL-C when in the console window, or some OS specific action.
Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo