Java Launcher for Field Debug

From NSIS Wiki
Jump to navigationJump to search
Author: Jmay (talk, contrib)


Description

This is a slightly more advanced Java launcher that builds on the ideas of the other launchers here. I'm indebted to their authors for coming up with the idea of using NSIS to create a Java launcher. My script just puts a bit of gloss on the basic idea. It solves a couple of problems for field-debugging of deployed applications:

1. As Pac has said with A slightly better Java Launcher, when everything is working fine, it's better not to have a command prompt window showing as when running with java.exe. However, if you need to debug your application in the field, it is often very useful to have this console so you can quickly look at Log4j or System.out.println() trace. Of course, you can manually invoke your application with a batch file, but it gets rather tiresome having to specify the classpath etc. So, this launcher lets you specify '-console' as an argument to the executable. This invokes java.exe rather than javaw.exe and so causes the console to appear.

2. Again, when debugging built applications, it is very useful to be able to attach a remote debug session to the deployed application, e.g. from Eclipse. However, in order to put the JVM into remote debug mode, you need to specify, for example:

–Xdebug –Xnoagent –Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000

These arguments must come before the -classpath tag, but the other launchers here simply pass all arguments to the Java application. This launcher will pass all arguments that start with '-X' to the JVM itself.

This script incorporates the 'Strtok' by bigmac666 and uses it for the command line parsing parts.

There are various calls to MessageBox in the script so you can see what each bit does. Comment them in or out as necessary.

Links

LauncherForFieldDebug.zip (25 KB)

This zip contains a compiled version of the script, together with a sample Java Swing application to show the principles of operation. See the readme.txt file in the zip for details.

The Script

# ------------------------------------------------------------------------------
# Advanced Java Launcher
# Author: Jmay
# Based on: Java Launcher
# 
# Thanks to bigmac666 for StrTok and all the other NSIS Java launcher
# developers, on whose work this is substantially based.
#
# Specify '-console' as argument to launch with java.exe, otherwise javaw.exe 
# will be used.
#
# All arguments starting '-X' will be passed to the JVM, all other arguments 
# will be passed to the Java application.
# ------------------------------------------------------------------------------
 
# You want to change the next four lines
Name "YourProgramName"
Caption "Java Launcher"
Icon "YourIcon.ico"
OutFile "YourProgram.exe"
 
SilentInstall silent
AutoCloseWindow true
ShowInstDetails nevershow
 
# You want to change the next two lines too
!define CLASSPATH "."
!define CLASS "Test"
 
!define TRUE "true"
!define FALSE "false"
 
# defines for console or windowed usage
!define USE_CONSOLE_ARG "-console"
!define JAVA_CONSOLE_EXE "java.exe"
!define JAVA_WINDOWED_EXE "javaw.exe"
 
Var VmArgs         # JVM arguments, e.g. -Xdebug -Xnoagent, etc.
Var ProgramArgs    # your program arguments (if you use them), e.g. -enablefoo, etc.
Var C0             # temporary variable used in testing for quoted executable path
Var X0             # temporary variable used in testing for VM arguments
Var BreakLoop      # essentially a boolean to indicate loop processing should finish
Var JavaExe        # the Java exe to use - either java.exe or javaw.exe
 
 
Section "1"
 
  StrCpy $BreakLoop ${FALSE}
 
  # set to javaw.exe by default
  StrCpy $JavaExe ${JAVA_WINDOWED_EXE}
 
  Call ParseArgs
  Call GetJRE
  Pop $R0
 
  StrCpy $0 '"$R0" $VmArgs -classpath "${CLASSPATH}" ${CLASS} $ProgramArgs'
 
  ; MessageBox MB_OK|MB_ICONINFORMATION "VmArgs: $VmArgs"
  ; MessageBox MB_OK|MB_ICONINFORMATION "ProgramArgs: $ProgramArgs"
  MessageBox MB_OK|MB_ICONINFORMATION "Final cmdline: $0"
 
  SetOutPath $EXEDIR
  ExecWait $0
SectionEnd
 
Function ParseArgs
  # first argument is executable name; check for double quote at start, e.g. if
  # path contains spaces
  StrCpy $C0 $CMDLINE 1
  StrCmpS $C0 '"' quoted unquoted
 
  quoted:
    # tokenize using '"' as the delimiter; this fixes up the problem where paths
	# containing spaces break the later tokenization of arguments using ' ' as a delimiter
 
    ; MessageBox MB_OK|MB_ICONINFORMATION "Quoted string" 
    ; MessageBox MB_OK|MB_ICONINFORMATION "Cmdline: $CMDLINE" 
 
	# push $CMDLINE, tokenize and ignore as first token will be empty as string starts with delimiter
    Push $CMDLINE	
    Push '"'
    Call StrTok
 
    Pop $R0  # first token
    Pop $R1  # remainder of string
 
 
    # push remainder of string, tokenize and use first token as the initial argument in $ProgramArgs
    Push $R1
    Push '"'
    Call StrTok
 
    Pop $R0
    Pop $R1
 
    Strcpy $ProgramArgs '"$R0"'
    GoTo appendspace
 
  unquoted:
    # no special processing needed for unquoted path	
    ; MessageBox MB_OK|MB_ICONINFORMATION "Un-quoted string" 
 
  appendspace:
    # append space to create trailing delimiter so can get final token, 
	# otherwise StrTok will finish prematurely
    StrCpy $R1 "$R1 "
 
  # main processing loop, uses StrTok to extract each argument in turn
  loop:
    # check loop continue flag
    StrCmp $BreakLoop ${TRUE} finished
 
    Push $R1
    Push " "
    Call StrTok
 
    Pop $R0
    Pop $R1
 
	# if token is empty string then set flag to break out of loop
    StrCmp $R1 "" 0 moretodo
    StrCpy $BreakLoop ${TRUE}
 
  moretodo:
 
    ; MessageBox MB_OK|MB_ICONINFORMATION "loop - R0: $R0"
    ; MessageBox MB_OK|MB_ICONINFORMATION "loop - R1: $R1"
 
    # check whether user specified '-console'
    StrCmpS $R0 "-console" usingconsole vmorprogramarg
 
  usingconsole:
    # set to java.exe
    StrCpy $JavaExe ${JAVA_CONSOLE_EXE}
    GoTo loop
 
  vmorprogramarg:
    # check start of arg to see if is a VM arg or program arg
    StrCpy $X0 $R0 2
    StrCmpS $X0 "-X" isvmarg isprogramarg
 
  isvmarg:
    # is a VM arg; append to $VmArgs
    StrCpy $VmArgs "$VmArgs $R0"
    GoTo loop
 
  isprogramarg:
    # is a program arg; append to $ProgramArgs
    StrCpy $ProgramArgs "$ProgramArgs $R0"  
    GoTo loop
 
  finished:
    ClearErrors
 
FunctionEnd
 
# Find JRE (java(w).exe)
# 1 - in .\jre directory (JRE Installed with application)
# 2 - in JAVA_HOME environment variable
# 3 - in the registry
# 4 - assume java(w).exe in current dir or PATH
Function GetJRE
 
  Push $R0
  Push $R1
 
  ClearErrors
 
  StrCpy $R0 "$EXEDIR\jre\bin\$JavaExe"
  ; MessageBox MB_OK|MB_ICONINFORMATION "Looking in JRE for: $R0"
  IfFileExists $R0 JreFound
  StrCpy $R0 ""
 
  ClearErrors
  ReadEnvStr $R0 "JAVA_HOME"
  StrCpy $R0 "$R0\bin\$JavaExe"
  IfErrors 0 JreFound
 
  ClearErrors
  ReadRegStr $R1 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment" "CurrentVersion"
  ReadRegStr $R0 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment\$R1" "JavaHome"
  StrCpy $R0 "$R0\bin\$JavaExe"
 
  IfErrors 0 JreFound
  StrCpy $R0 "$JavaExe"
 
 JreFound:
  Pop $R1
  Exch $R0
FunctionEnd
 
# C-style StrTok function
# Author: bigmac666
Function StrTok
  Exch $R1
  Exch 1
  Exch $R0
  Push $R2
  Push $R3
  Push $R4
  Push $R5
 
  #R0 fullstring
  #R1 tokens
  #R2 len of fullstring
  #R3 len of tokens
  #R4 char from string
  #R5 testchar
 
  StrLen $R2 $R0
  IntOp $R2 $R2 + 1
 
  loop1:
    IntOp $R2 $R2 - 1
    IntCmp $R2 0 exit
 
    StrCpy $R4 $R0 1 -$R2
 
    StrLen $R3 $R1
    IntOp $R3 $R3 + 1
 
    loop2:
      IntOp $R3 $R3 - 1
      IntCmp $R3 0 loop1
 
      StrCpy $R5 $R1 1 -$R3
 
      StrCmp $R4 $R5 Found
    Goto loop2
  Goto loop1
 
  exit:
  # Not found!!!
  StrCpy $R1 ""
  StrCpy $R0 ""
  Goto Cleanup
 
  Found:
  StrLen $R3 $R0
  IntOp $R3 $R3 - $R2
  StrCpy $R1 $R0 $R3
 
  IntOp $R2 $R2 - 1
  IntOp $R3 $R3 + 1
  StrCpy $R0 $R0 $R2 $R3
 
  Cleanup:
  Pop $R5
  Pop $R4
  Pop $R3
  Pop $R2
  Exch $R0
  Exch 1
  Exch $R1
 
FunctionEnd