JUnit + System.exit(-1): Looking for alternatives

Discussion in 'Java' started by Koos Pol, Apr 22, 2008.

  1. Koos Pol

    Koos Pol Guest

    Hi,
     
    Background: I have an application which basically is (although very
    complicated) a batch script. It will run unattend from cron. The nature of
    intermittant problems is unimportant. The batch script fails or it leads to
    a successful result. So it either works, or it doesn't. Due to the many
    possible failure points I created a general ErrorHandler class which dumps
    to log4j and then quits via System.exit(-1).
     
    Now I find that my JUnit tests (3.8.2) no longer work. As already read on
    many forums, JUnit doesn't take care much for System.exit(). That's a real
    bummer because now I have to implement a mucho cluttering and complicated
    try/catch mechanism throughout the whole application which I don't need nor
    care for.
     
    1. Does anyone know of a way to make JUnit not barf on the System.exit(-1)?
    2. Is there a way to gracefully abort besides System.exit(), (which is far
    from gracefully)?
     
    Thanks for any help.

    Koos
    --
    43rd Law of Computing: Anything that can go wr
    fortune: Segmentation violation -- Core dumped
     
    Koos Pol, Apr 22, 2008
    #1
    1. Advertising

  2. Koos Pol

    Koos Pol Guest

    On dinsdag 22 april 2008 21:20 Eric Sosman wrote:

    >> 1. Does anyone know of a way to make JUnit not barf on the
    >> System.exit(-1)? 2. Is there a way to gracefully abort besides
    >> System.exit(), (which is far from gracefully)?


    [snip]

    > Instead of terminating with System.exit(whateverValue), why
    > not throw an unchecked exception? In normal use it will drift
    > all the way out to the JVM's last-ditch catcher and shut your
    > script down


    Whoa, why didn't I think of that. Thanks a lot Eric. Clecver thinking!

    Koos
    --
    43rd Law of Computing: Anything that can go wr
    fortune: Segmentation violation -- Core dumped
     
    Koos Pol, Apr 22, 2008
    #2
    1. Advertising

  3. Koos Pol

    Koos Pol Guest

    On dinsdag 22 april 2008 21:20 Eric Sosman wrote:

    > (Aside: Does anyone know of a system where -1 makes sense
    > as a failure code? "Makes sense" not as in "It works," but as
    > in "Propagates unchanged to the environment and has a well-
    > understood meaning when it gets there.")


    Yes. In my scenario the failure code does exactly what I want it to do. It
    aborts and looks for the emergency exit. The process has failed and there
    is no need whatsoever to keep it well contained in some artificial
    try/catch stack. During a failure a secondary system is triggered from
    anyone of the many possible failure points. So the emergency exit is just
    fine. And makes sense.

    Koos
    --
    43rd Law of Computing: Anything that can go wr
    fortune: Segmentation violation -- Core dumped
     
    Koos Pol, Apr 22, 2008
    #3
  4. Koos Pol

    Koos Pol Guest

    On woensdag 23 april 2008 00:35 Rex Mottram wrote:

    > I think you misunderstood his point, which was specific to the value -1.

    [snip]
    > So yes, we're sure -1 works for you. Not so sure it's the best choice of
    > value.


    Ah. That makes sense. I'll consider changing that. Thanks for clarifying.

    Koos
    --
    43rd Law of Computing: Anything that can go wr
    fortune: Segmentation violation -- Core dumped
     
    Koos Pol, Apr 23, 2008
    #4
  5. Wayne <> wrote:
    > In addition, some systems reserve negative values to indicate
    > termination via a signal.


    I haven't yet seen *such* a system, but I've seen a few, where
    termination through a signal was indicated (through the default-
    signal-handler (from libc?)) as an exit code of (signal number + 128)
    e.g. +139 for SIGSEGV (11). Even if this gets interpreted as a
    signed byte, it's definitely not the same as -SigNr.

    If you really meant "minus the signal number", then please tell
    me/us on what system that was.
     
    Andreas Leitgeb, Apr 24, 2008
    #5
  6. Koos Pol <> writes:

    ....
    > Due to the many
    > possible failure points I created a general ErrorHandler class which dumps
    > to log4j and then quits via System.exit(-1).



    > 1. Does anyone know of a way to make JUnit not barf on the System.exit(-1)?


    Try modifying the TestCase to run with a security manager that
    prevents calling System.exit, then catch the SecurityException.

    Something like (untested!):
    ----
    public class SecurityTestCase extends TestCase {
    private class ExitException extends SecurityException {
    public final int exitCode;
    public ExitException(int code) {
    super("There is no escape!");
    this.exitCode = code;
    }
    }
    private class NoExitSecurityManager extends SecurityManager {
    public checkExit(int status) {
    super.checkExit(); // first call on preventing.
    throw new ExitException(status);
    }
    };

    public void setUp() throws Exception {
    System.setSecurityManager(new NoExitSecurityManager());
    }

    public void tearDown() throws Exception {
    System.setSecurityManager(null);
    }

    }
    ---
    and then make a test like:

    public void testExit() throws Exception {
    try {
    somethingThatExits();
    fail("System.exit() expected");
    } catch (ExitException e) {
    assertEquals("Exit code", 42, e.exitCode);
    }
    }


    Or perhaps you can create a policy file that revokes the permission
    to exit from your own packages.
    See http://java.sun.com/j2se/1.5.0/docs/guide/security/PolicyFiles.html


    > 2. Is there a way to gracefully abort besides System.exit(), (which is far
    > from gracefully)?


    As already said, a custom unchecked exception causes no syntactic
    overhead and won't be caught (unless you catch RuntimeException or
    Exception directly somewhere else, which you really, really shouldn't,
    for pretty much that reason), and won't be lost unless you have
    a finally-block that ends abruptly (which you shouldn't really do either).

    Dying gracefully will also allow finally blocks to release acquired
    resources where necessary.

    /L
    --
    Lasse Reichstein Nielsen -
    DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
    'Faith without judgement merely degrades the spirit divine.'
     
    Lasse Reichstein Nielsen, Apr 24, 2008
    #6
  7. Lasse Reichstein Nielsen <> writes:

    > Try modifying the TestCase to run with a security manager that
    > prevents calling System.exit, then catch the SecurityException.
    >
    > Something like (untested!):


    And now that it has been tested, it turns out to fail in several ways.

    This one seems to work, though:

    ---
    public class NoExitTestCase extends TestCase {

    protected static class ExitException extends SecurityException {
    public final int status;
    public ExitException(int status) {
    super("There is no escape!");
    this.status = status;
    }
    }

    private static class NoExitSecurityManager extends SecurityManager {
    @Override
    public void checkPermission(Permission perm) {
    // allow anything.
    }
    @Override
    public void checkPermission(Permission perm, Object context) {
    // allow anything.
    }
    @Override
    public void checkExit(int status) {
    super.checkExit(status);
    throw new ExitException(status);
    }
    }

    @Override
    protected void setUp() throws Exception {
    super.setUp();
    System.setSecurityManager(new NoExitSecurityManager());
    }

    @Override
    protected void tearDown() throws Exception {
    System.setSecurityManager(null); // or save and restore original
    super.tearDown();
    }

    public void testNoExit() throws Exception {
    System.out.println("Printing works");
    }

    public void testExit() throws Exception {
    try {
    System.exit(42);
    } catch (ExitException e) {
    assertEquals("Exit status", 42, e.status);
    }
    }
    }
    ---
    I'm sure it can be done prettier, but it gives the general idea.

    /L
    --
    Lasse Reichstein Nielsen -
    DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
    'Faith without judgement merely degrades the spirit divine.'
     
    Lasse Reichstein Nielsen, Apr 24, 2008
    #7
  8. Koos Pol

    Koos Pol Guest

    On donderdag 24 april 2008 22:16 Lasse Reichstein Nielsen wrote:

    >> Try modifying the TestCase to run with a security manager that
    >> prevents calling System.exit, then catch the SecurityException.


    > And now that it has been tested, it turns out to fail in several ways.
    > This one seems to work, though:


    This whole SecurityManager thing is new to me. So I have to dig into that
    before I get it. So, "sort of" thanks :)

    Koos
    --
    43rd Law of Computing: Anything that can go wr
    fortune: Segmentation violation -- Core dumped
     
    Koos Pol, Apr 27, 2008
    #8
  9. Wayne wrote:
    > Koos Pol wrote:
    >> On woensdag 23 april 2008 00:35 Rex Mottram wrote:
    >>> I think you misunderstood his point, which was specific to the value -1.

    >> [snip]
    >>> So yes, we're sure -1 works for you. Not so sure it's the best choice of
    >>> value.

    >>
    >> Ah. That makes sense. I'll consider changing that. Thanks for clarifying.

    >
    > I recently looked into this on Unix (SUS/POSIX) and Linux. Turns out
    > the exit status is an int (signed two's complement 4 byte) however only
    > the least significant byte is ever made available to the parent process
    > (the process that "wait"s for the exit status). In addition, some systems
    > reserve negative values to indicate termination via a signal.
    >
    > Of course many systems and applications pay no attention to these
    > standards and conventions. But to be truly portable on all
    > systems you should only use exit status values in the range
    > 0..127 inclusive. If you implement a signal handler (not in
    > Java though, AFAIK) you would return -1 times the signal number.
    > Also, '-1' a.k.a '255' is reserved in POSIX (it has a special
    > meaning with 'xargs').
    >
    > Also the BSD standard values are in /usr/include sysexits.h:
    > #define EX_OK 0 /* successful termination */
    > #define EX_USAGE 64 /* command line usage error */
    > #define EX_DATAERR 65 /* data format error */
    > #define EX_NOINPUT 66 /* cannot open input */
    > #define EX_NOUSER 67 /* addressee unknown */
    > #define EX_NOHOST 68 /* host name unknown */
    > #define EX_UNAVAILABLE 69 /* service unavailable */
    > #define EX_SOFTWARE 70 /* internal software error */
    > #define EX_OSERR 71 /* system error (e.g., can't fork) */
    > #define EX_OSFILE 72 /* critical OS file missing */
    > #define EX_CANTCREAT 73 /* can't create (user) output file */
    > #define EX_IOERR 74 /* input/output error */
    > #define EX_TEMPFAIL 75 /* temp failure; user is invited to
    > retry */
    > #define EX_PROTOCOL 76 /* remote error in protocol */
    > #define EX_NOPERM 77 /* permission denied */
    > #define EX_CONFIG 78 /* configuration error */


    I think you have a narrow understanding of truly portable.

    Portable to *nix style (POSIX) OS's maybe.

    On OpenVMS some values are:

    #define SS$_ACCVIO 12
    #define SS$_BADPARAM 20
    #define SS$_EXQUOTA 28
    #define SS$_NOPRIV 36
    #define SS$_ABORT 44
    #define SS$_BADATTRIB 52
    #define SS$_BADESCAPE 60
    #define SS$_BADIMGHDR 68
    #define SS$_CHANINTLK 76
    #define SS$_CTRLERR 84
    #define SS$_DATACHECK 92
    #define SS$_DEVFOREIGN 100
    #define SS$_DEVMOUNT 108
    #define SS$_DEVNOTMBX 116
    #define SS$_DEVNOTMOUNT 124
    #define SS$_DEVOFFLINE 132
    ....
    #define RMS$_NORMAL 65537
    ....
    #define RMS$_ACT 98906
    #define RMS$_DEL 98914
    #define RMS$_INCOMPSHR 98922
    #define RMS$_DNR 98930
    #define RMS$_EOF 98938
    #define RMS$_FEX 98946
    #define RMS$_FLK 98954
    #define RMS$_FNF 98962
    #define RMS$_PRV 98970
    #define RMS$_REX 98978
    #define RMS$_RLK 98986
    #define RMS$_RNF 98994
    #define RMS$_WLK 99002
    #define RMS$_EXP 99010
    #define RMS$_NMF 99018
    #define RMS$_SUP 99026

    In Windows the exist code only has a range 0-255. The values
    are strictly application specific and typical just sequentially.

    Arne
     
    Arne Vajhøj, May 4, 2008
    #9
  10. Wayne wrote:
    > Arne Vajhøj wrote:
    >> I think you have a narrow understanding of truly portable.
    >>
    >> Portable to *nix style (POSIX) OS's maybe.
    >> ...
    >> In Windows the exist code only has a range 0-255. The values
    >> are strictly application specific and typical just sequentially.

    >
    > I guess for true portability, for POSIX compliant systems anyway,
    > we should stick to EXIT_SUCCESS and EXIT_FAILURE and not
    > use any other values. Too bad, having some defined failure
    > modes seemed like a good idea to me.
    >
    > Maybe I should go back to my old C habit of defining exit statuses
    > (stati?) as an enum. Then those can be mapped in an OS specific way.


    That is the route a lot of software goes. Just good or bad. And
    get the cause communicated some other way.

    Arne
     
    Arne Vajhøj, May 4, 2008
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?U2FuZHk=?=

    Code to Exit Web App and Exit Internet Explorer

    =?Utf-8?B?U2FuZHk=?=, Aug 3, 2005, in forum: ASP .Net
    Replies:
    7
    Views:
    7,976
    =?Utf-8?B?U2FuZHk=?=
    Aug 5, 2005
  2. Joe Smith
    Replies:
    4
    Views:
    66,088
    sandeep1976
    Nov 8, 2006
  3. Replies:
    2
    Views:
    493
    Jeff Epler
    May 31, 2005
  4. QQ
    Replies:
    5
    Views:
    550
    Jonathan Adams
    May 10, 2005
  5. Vicky

    Difference between exit(0) & exit (1)

    Vicky, Aug 8, 2006, in forum: C Programming
    Replies:
    6
    Views:
    731
    Kenneth Brody
    Aug 8, 2006
Loading...

Share This Page