I'm planning a series on Lucene/Solr integration with NHibernate & Telerik Extensions for ASP.NET MVC in the coming weeks but thought this might interest those planning to use Solr on Windows.
Background
We're using Solr to meet search requirements for an enterprise sized application. Tightly coupled to our ORM (NHibernate) event listeners it's availability and readiness, for both search queries and data inserts/updates, is paramount.
Although our Solr instances run on linux boxes I wanted to give us the option of deploying to Windows boxes in a production environment, not to mention give our team one less thing to do in the morning - start it!
Considerations
- Fully-fledged Windows service with stop/start/restart
- Must be able to start upon boot (without user interaction)
- Handle unexpected shut-downs/hangs properly (& release javaw32.exe)
- Automatically restart in the event of failure
- Log output from the application console
- Some kind of notification in the event of failure (email)
Java Service Wrapper
The prospect of having to write this ourselves was quite daunting as the time/resource would quite frankly outweigh slapping another linux server in the data-centre. However after some searching I came across the Java Service Wrapper. Available in in many flavours (including a free community version) it both meets our needs and brings some extra features welcome in a production environment.
Setup
The standard Solr installation seems to require that the application is started from it's relative root, I presume for discovery reasons. Unfortunately this left me with a rather unsatisfactory directory structure.
Furthermore lack of command-line args for overriding configuration meant that I was forced to bake the library paths in the configuration file. As we're potentially deploying to 32bit and 64bit environments this meant two configuration files.
Configuration
This is the standard 'src' configuration file with changes for solr and the new directory structure.
wrapper/conf/solr32.conf
#********************************************************************
# Wrapper License Properties (Ignored by Community Edition)
#********************************************************************
# Include file problems can be debugged by removing the first '#'
# from the following line:
##include.debug
#include conf/wrapper-license.conf
#include conf/wrapper-license-%WRAPPER_HOST_NAME%.conf
#********************************************************************
# Wrapper Java Properties
#********************************************************************
# Java Application
wrapper.java.command=java
# Tell the Wrapper to log the full generated Java command line.
wrapper.java.command.loglevel=INFO
# Java Main class. This class must implement the WrapperListener interface
# or guarantee that the WrapperManager class is initialized. Helper
# classes are provided to do this for you. See the Integration section
# of the documentation for details.
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperJarApp
# Java Classpath (include wrapper.jar) Add class path elements as
# needed starting from 1
wrapper.java.classpath.1=wrapper/lib32/wrapper.jar
# Java Library Path (location of Wrapper.DLL or libwrapper.so)
wrapper.java.library.path.1=wrapper/lib32
# Java Bits. On applicable platforms, tells the JVM to run in 32 or 64-bit mode.
wrapper.java.additional.auto_bits=TRUE
# Java Additional Parameters
#wrapper.java.additional.1=
# Initial Java Heap Size (in MB)
#wrapper.java.initmemory=3
# Maximum Java Heap Size (in MB)
#wrapper.java.maxmemory=64
# Application parameters. Add parameters as needed starting from 1
wrapper.app.parameter.1=start.jar
#********************************************************************
# Wrapper Logging Properties
#********************************************************************
# Enables Debug output from the Wrapper.
# wrapper.debug=TRUE
# Format of output for the console. (See docs for formats)
wrapper.console.format=PM
# Log Level for console output. (See docs for log levels)
wrapper.console.loglevel=INFO
# Log file to use for wrapper output logging.
wrapper.logfile=wrapper/logs/wrapper.log
# Format of output for the log file. (See docs for formats)
wrapper.logfile.format=LPTM
# Log Level for log file output. (See docs for log levels)
wrapper.logfile.loglevel=INFO
# Maximum size that the log file will be allowed to grow to before
# the log is rolled. Size is specified in bytes. The default value
# of 0, disables log rolling. May abbreviate with the 'k' (kb) or
# 'm' (mb) suffix. For example: 10m = 10 megabytes.
wrapper.logfile.maxsize=0
# Maximum number of rolled log files which will be allowed before old
# files are deleted. The default value of 0 implies no limit.
wrapper.logfile.maxfiles=0
# Log Level for sys/event log output. (See docs for log levels)
wrapper.syslog.loglevel=NONE
#********************************************************************
# Wrapper General Properties
#********************************************************************
# Allow for the use of non-contiguous numbered properties
wrapper.ignore_sequence_gaps=TRUE
# title to use when running as a console
wrapper.console.title=Solr
#********************************************************************
# Wrapper Windows NT/2000/XP Service Properties
#********************************************************************
# WARNING - Do not modify any of these properties when an application
# using this configuration file has been installed as a service.
# Please uninstall the service before modifying this section. The
# service can then be reinstalled.
# Name of the service
wrapper.name=Solr
# Display name of the service
wrapper.displayname=Solr Engine
# Description of the service
wrapper.description=Solr Engine
# Service dependencies. Add dependencies as needed starting from 1
wrapper.ntservice.dependency.1=
# Mode in which the service is installed. AUTO_START, DELAY_START or DEMAND_START
wrapper.ntservice.starttype=AUTO_START
# Allow the service to interact with the desktop.
wrapper.ntservice.interactive=true
Installing the service
To install as a Windows service run the appropriate batch file; however be aware that:
- You must change the 'Log on as' of the service to a local administrator or user with sufficient privileges. I found (to my cost) that trying to run the JavaVM under "Local System" simply doesn't work and causes a shed load of errors.
- If you plan to change your configuration I'd recommend uninstalling the existing service first, otherwise it may fail.
- Although the service will be set to 'Automatic' start you will need to start it manually immediately after installation.
InstallWrapper-NT-32
@echo off
setlocal
rem Copyright (c) 1999, 2009 Tanuki Software, Ltd.
rem http://www.tanukisoftware.com
rem All rights reserved.
rem
rem This software is the proprietary information of Tanuki Software.
rem You shall use it only in accordance with the terms of the
rem license agreement you entered into with Tanuki Software.
rem http://wrapper.tanukisoftware.org/doc/english/licenseOverview.html
rem
rem Java Service Wrapper general NT service uninstall script.
rem Optimized for use with version 3.3.9 of the Wrapper.
rem
if "%OS%"=="Windows_NT" goto nt
echo This script only works with NT-based versions of Windows.
goto :eof
:nt
rem
rem Find the application home.
rem
rem %~dp0 is location of current script under NT
set _REALPATH=%~dp0
rem Decide on the wrapper binary.
set _WRAPPER_BASE=wrapper
set _WRAPPER_EXE=%_REALPATH%..%_WRAPPER_BASE%-windows-x86-32.exe
if exist "%_WRAPPER_EXE%" goto conf
set _WRAPPER_EXE=%_REALPATH%..%_WRAPPER_BASE%.exe
if exist "%_WRAPPER_EXE%" goto conf
echo Unable to locate a Wrapper executable using any of the following names:
echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe
pause
goto :eof
rem
rem Find the wrapper.conf
rem
:conf
set _WRAPPER_CONF="%~f1"
if not %_WRAPPER_CONF%=="" goto startup
set _WRAPPER_CONF="%_REALPATH%confsolr32.conf"
rem
rem Install the Wrapper as an NT service.
rem
:startup
"%_WRAPPER_EXE%" -i %_WRAPPER_CONF%
if not errorlevel 1 goto :eof
pause
UninstallWrapper-NT-32
@echo off
setlocal
rem Copyright (c) 1999, 2009 Tanuki Software, Ltd.
rem http://www.tanukisoftware.com
rem All rights reserved.
rem
rem This software is the proprietary information of Tanuki Software.
rem You shall use it only in accordance with the terms of the
rem license agreement you entered into with Tanuki Software.
rem http://wrapper.tanukisoftware.org/doc/english/licenseOverview.html
rem
rem Java Service Wrapper general NT service uninstall script.
rem Optimized for use with version 3.3.9 of the Wrapper.
rem
if "%OS%"=="Windows_NT" goto nt
echo This script only works with NT-based versions of Windows.
goto :eof
:nt
rem
rem Find the application home.
rem
rem %~dp0 is location of current script under NT
set _REALPATH=%~dp0
rem Decide on the wrapper binary.
set _WRAPPER_BASE=wrapper
set _WRAPPER_EXE=%_REALPATH%..%_WRAPPER_BASE%-windows-x86-32.exe
if exist "%_WRAPPER_EXE%" goto conf
set _WRAPPER_EXE=%_REALPATH%..%_WRAPPER_BASE%.exe
if exist "%_WRAPPER_EXE%" goto conf
echo Unable to locate a Wrapper executable using any of the following names:
echo %_REALPATH%%_WRAPPER_BASE%-windows-x86-32.exe
pause
goto :eof
rem
rem Find the wrapper.conf
rem
:conf
set _WRAPPER_CONF="%~f1"
if not %_WRAPPER_CONF%=="" goto startup
set _WRAPPER_CONF="%_REALPATH%confsolr32.conf"
rem
rem Uninstall the Wrapper as an NT service.
rem
:startup
"%_WRAPPER_EXE%" -r %_WRAPPER_CONF%
if not errorlevel 1 goto :eof
pause
Finally..
As the wrapper will constantly restart upon failure if you have full logging enabled (as I do here) you will soon end up with GB's of log entries. The errors caused by neglecting to change the 'Log on as' service user is a fine example of this.
If you have any feedback/improvements on the above, please do comment :o)