Monday, March 22, 2010

Eclipse Super Clean TM

For the past couple of years, I've regularly had issues where Eclipse reports unresolved references for projects that were fine when I closed down the night before; sometimes they'll show up right after I do an update from SVN making me wonder if I had a bad update or someone committed bad code.

Through some trial and error, I've found a sequence of steps that I follow that invariably solves the problem.
  • Turn off Project Build Automatically
  • Choose Project Clean and make sure that the “start build immediately” check box is unselected
  • Clean the project(s) affected
  • Close the projects in the IDE
  • Shut down Eclipse
  • Re-start Eclipse
  • Re-open the projects
  • Turn on Project Build Automatically
 That should do it.

Sunday, March 21, 2010

Running Multiple Nodes (Non-Clustered) Simultaneously Using JBoss 4.2.3

On a recent engagement for a health care client, JBoss was chosen as the implementation technology.

Because the stack included the application server, service bus, bpm and the operational network components, plus some of our own proprietary IP, I created 3 different nodes

  1. Web Application
  2. Service Bus BPM and Rules
  3. Operational Network, plus centralized exception handling and alerting
With JBoss 5.x, the -Djboss.service.bindingset=ports-0x works beautifully. Unfortunately, for compatibility reasons, we needed the v4.2 release.
I had read about the option for using the sample-bindings.xml file, but at 1940 lines of XML, I wanted to try something that didn't involve going through all those ports to make each unique.

Through testing, I ended up changing the following ports:

Port Modification

jboss-service.xml
3873
8083
1099
1098
4444
4445
4446

server.xml
7080
7009
7443

remoting-bisocket-service.xml
4457
4458


Note that some of these ports appear in multiple files and to be safe you should search all your .xml files in the server/xxxxx folder to make sure you have changed all the relevant values.

Configuring Eclipse
Each launch configuration should reference a separate node. For our purposes, the 3 we created were named app, default (esb) and ops.

Below are the program arguments for each of the launch configurations:

--configuration=app  -b localhost
--configuration=default  -b localhost
--configuration=ops  -b localhost

Launching JBoss as a Service

Now Eclipse can launch each of the nodes on the same computer, but using different and non-conflicting port bindings. While this is fine for development, if you wish to launch these same nodes in a development or test environment as a Windows service, there is some additional work that is needed.

First, install the native libraries for JBoss. From this link you'll find the actual download link.

Secondly, copy the run.bat, service.bat and shutdown.bat files and name each uniquely for the node in question. Mine looked like the following when I was done:

run-app.bat
shutdown-app.bat
service-app.bat

and so on.

The service-xxx.bat file is used to create the NT Service. Give the service a unique name since there will be multiple JBoss services running.

service-app.bat
Below appears the service-app.bat in its entirety. Notice the addition of the "-app" prefix to the .lock files. Failing to add the prefix will result in service conflicts.


I've highlighted some of the relevant areas to make note of.


@echo off
REM JBoss, the OpenSource webOS
REM
REM Distributable under LGPL license.
REM See terms of license at gnu.org.
REM
REM -------------------------------------------------------------------------
REM JBoss Service Script for Windows
REM -------------------------------------------------------------------------


@if not "%ECHO%" == "" echo %ECHO%
@if "%OS%" == "Windows_NT" setlocal
set DIRNAME=%CD%

REM
REM VERSION, VERSION_MAJOR and VERSION_MINOR are populated
REM during the build with ant filter.
REM
set SVCNAME=JBASAPP
set SVCDISP=JBoss AS App Node
set SVCDESC=JBoss Application Server (App) 4.2.3 GA/Platform: Windows x86
set NOPAUSE=Y

REM Suppress killing service on logoff event
set JAVA_OPTS=-Xrs

REM Figure out the running mode

if /I "%1" == "install"   goto cmdInstall
if /I "%1" == "uninstall" goto cmdUninstall
if /I "%1" == "start"     goto cmdStart
if /I "%1" == "stop"      goto cmdStop
if /I "%1" == "restart"   goto cmdRestart
if /I "%1" == "signal"    goto cmdSignal
echo Usage: service install^|uninstall^|start^|stop^|restart^|signal
goto cmdEnd

REM jbosssvc retun values
REM ERR_RET_USAGE           1
REM ERR_RET_VERSION         2
REM ERR_RET_INSTALL         3
REM ERR_RET_REMOVE          4
REM ERR_RET_PARAMS          5
REM ERR_RET_MODE            6

:errExplain
if errorlevel 1 echo Invalid command line parameters
if errorlevel 2 echo Failed installing %SVCDISP%
if errorlevel 4 echo Failed removing %SVCDISP%
if errorlevel 6 echo Unknown service mode for %SVCDISP%
goto cmdEnd

:cmdInstall
jbosssvc.exe -imwdc %SVCNAME% "%DIRNAME%" "%SVCDISP%" "%SVCDESC%" service-app.bat
if not errorlevel 0 goto errExplain
echo Service %SVCDISP% installed
goto cmdEnd

:cmdUninstall
jbosssvc.exe -u %SVCNAME%
if not errorlevel 0 goto errExplain
echo Service %SVCDISP% removed
goto cmdEnd

:cmdStart
REM Executed on service start
del .r-app.lock 2>&1 | findstr /C:"being used" > nul
if not errorlevel 1 (
  echo Could not continue. Locking file already in use.
  goto cmdEnd
)
echo Y > .r-app.lock
jbosssvc.exe -p 1 "Starting %SVCDISP%" > run-app.log
call run-app.bat < .r-app.lock >> run-app.log 2>&1
jbosssvc.exe -p 1 "Shutdown %SVCDISP% service" >> run-app.log
del .r-app.lock
goto cmdEnd

:cmdStop
REM Executed on service stop
echo Y > .s-app.lock
jbosssvc.exe -p 1 "Shutting down %SVCDISP%" > shutdown-app.log
call shutdown-app -S < .s-app.lock >> shutdown-app.log 2>&1
jbosssvc.exe -p 1 "Shutdown %SVCDISP% service" >> shutdown-app.log
del .s-app.lock
goto cmdEnd

:cmdRestart
REM Executed manually from command line
REM Note: We can only stop and start
echo Y > .s-app.lock
jbosssvc.exe -p 1 "Shutting down %SVCDISP%" >> shutdown-app.log
call shutdown-app -S < .s-app.lock >> shutdown-app.log 2>&1
del .s-app.lock
:waitRun
REM Delete lock file
del .r-app.lock > nul 2>&1
REM Wait one second if lock file exist
jbosssvc.exe -s 1
if exist ".r-app.lock" goto waitRun
echo Y > .r-app.lock
jbosssvc.exe -p 1 "Restarting %SVCDISP%" >> run-app.log
call run-app.bat < .r-app.lock >> run-app.log 2>&1
jbosssvc.exe -p 1 "Shutdown %SVCDISP% service" >> run-app.log
del .r-app.lock
goto cmdEnd

:cmdSignal
REM Send signal to the service.
REM Requires jbosssch.dll to be loaded in JVM
@if not ""%2"" == """" goto execSignal
echo Missing signal parameter.
echo Usage: service signal [0...9]
goto cmdEnd
:execSignal
jbosssvc.exe -k%2 %SVCNAME%
goto cmdEnd

:cmdEnd


run-app.bat
Rather than include the entire file, just the sections that need modification are shown:



if "%OS%" == "Windows_NT" set DIRNAME=%~dp0%
set PROGNAME=run-app.bat
if "%OS%" == "Windows_NT" set PROGNAME=%~nx0%

:RESTART
"%JAVA%" %JAVA_OPTS% ^
   -Djava.endorsed.dirs="%JBOSS_ENDORSED_DIRS%" ^
   -classpath "%JBOSS_CLASSPATH%" ^
   org.jboss.Main -c app -b 0.0.0.0
  
This last change specifies the node name. The -b option allows JBoss to accept requests from any address. If you desire only localhost communication, then change 0.0.0.0 to 127.0.0.1.


shutdown-app.bat
As with run-app.bat, only the relevant sections are shown:


set DIRNAME=.\
if "%OS%" == "Windows_NT" set DIRNAME=%~dp0%
set PROGNAME=run-app.bat
if "%OS%" == "Windows_NT" set PROGNAME=%~nx0%



"%JAVA%" %JAVA_OPTS% -classpath "%JBOSS_CLASSPATH%" %MAIN_CLASS% -s jnp://localhost:1099


Make sure that the port number is correct for that node.



Now, simply repeat this process for each of your JBoss nodes and you should be able to run service-xxx.bat for each node to create a Windows service.


Summary

Wherever feasible, I'd definitely recommend using JBoss AS 5.x, but if you need to use 4.2.x, I hope these instructions are helpful to you.