1 { config, lib, pkgs, ... }:
5 cfg = config.services.tomcat;
11 maintainers = with lib.maintainers; [ danbst ];
18 enable = lib.mkEnableOption (lib.mdDoc "Apache Tomcat");
20 package = lib.mkPackageOptionMD pkgs "tomcat9" { };
22 purifyOnStart = lib.mkOption {
23 type = lib.types.bool;
25 description = lib.mdDoc ''
26 On startup, the `baseDir` directory is populated with various files,
27 subdirectories and symlinks. If this option is enabled, these items
28 (except for the `logs` and `work` subdirectories) are first removed.
29 This prevents interference from remainders of an old configuration
30 (libraries, webapps, etc.), so it's recommended to enable this option.
34 baseDir = lib.mkOption {
35 type = lib.types.path;
36 default = "/var/tomcat";
37 description = lib.mdDoc ''
38 Location where Tomcat stores configuration files, web applications
39 and logfiles. Note that it is partially cleared on each service startup
40 if `purifyOnStart` is enabled.
44 logDirs = lib.mkOption {
46 type = lib.types.listOf lib.types.path;
47 description = lib.mdDoc "Directories to create in baseDir/logs/";
50 extraConfigFiles = lib.mkOption {
52 type = lib.types.listOf lib.types.path;
53 description = lib.mdDoc "Extra configuration files to pull into the tomcat conf directory";
56 extraEnvironment = lib.mkOption {
57 type = lib.types.listOf lib.types.str;
59 example = [ "ENVIRONMENT=production" ];
60 description = lib.mdDoc "Environment Variables to pass to the tomcat service";
63 extraGroups = lib.mkOption {
65 type = lib.types.listOf lib.types.str;
66 example = [ "users" ];
67 description = lib.mdDoc "Defines extra groups to which the tomcat user belongs.";
73 description = lib.mdDoc "User account under which Apache Tomcat runs.";
76 group = lib.mkOption {
79 description = lib.mdDoc "Group account under which Apache Tomcat runs.";
82 javaOpts = lib.mkOption {
83 type = lib.types.either (lib.types.listOf lib.types.str) lib.types.str;
85 description = lib.mdDoc "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat";
88 catalinaOpts = lib.mkOption {
89 type = lib.types.either (lib.types.listOf lib.types.str) lib.types.str;
91 description = lib.mdDoc "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container";
94 sharedLibs = lib.mkOption {
95 type = lib.types.listOf lib.types.str;
97 description = lib.mdDoc "List containing JAR files or directories with JAR files which are libraries shared by the web applications";
100 serverXml = lib.mkOption {
101 type = lib.types.lines;
103 description = lib.mdDoc ''
104 Verbatim server.xml configuration.
105 This is mutually exclusive with the virtualHosts options.
109 commonLibs = lib.mkOption {
110 type = lib.types.listOf lib.types.str;
112 description = lib.mdDoc "List containing JAR files or directories with JAR files which are libraries shared by the web applications and the servlet container";
115 webapps = lib.mkOption {
116 type = lib.types.listOf lib.types.path;
117 default = [ tomcat.webapps ];
118 defaultText = lib.literalExpression "[ config.services.tomcat.package.webapps ]";
119 description = lib.mdDoc "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat";
122 virtualHosts = lib.mkOption {
123 type = lib.types.listOf (lib.types.submodule {
125 name = lib.mkOption {
126 type = lib.types.str;
127 description = lib.mdDoc "name of the virtualhost";
129 aliases = lib.mkOption {
130 type = lib.types.listOf lib.types.str;
131 description = lib.mdDoc "aliases of the virtualhost";
134 webapps = lib.mkOption {
135 type = lib.types.listOf lib.types.path;
136 description = lib.mdDoc ''
137 List containing web application WAR files and/or directories containing
138 web applications and configuration files for the virtual host.
145 description = lib.mdDoc "List consisting of a virtual host name and a list of web applications to deploy on each virtual host";
148 logPerVirtualHost = lib.mkOption {
149 type = lib.types.bool;
151 description = lib.mdDoc "Whether to enable logging per virtual host.";
154 jdk = lib.mkPackageOptionMD pkgs "jdk" { };
157 enable = lib.mkEnableOption "Apache Axis2 container";
159 services = lib.mkOption {
161 type = lib.types.listOf lib.types.str;
162 description = lib.mdDoc "List containing AAR files or directories with AAR files which are web services to be deployed on Axis2";
168 ###### implementation
170 config = lib.mkIf config.services.tomcat.enable {
172 users.groups.tomcat.gid = config.ids.gids.tomcat;
176 uid = config.ids.uids.tomcat;
177 description = "Tomcat user";
178 home = "/homeless-shelter";
180 extraGroups = cfg.extraGroups;
183 systemd.services.tomcat = {
184 description = "Apache Tomcat server";
185 wantedBy = [ "multi-user.target" ];
186 after = [ "network.target" ];
189 ${lib.optionalString cfg.purifyOnStart ''
190 # Delete most directories/symlinks we create from the existing base directory,
191 # to get rid of remainders of an old configuration.
192 # The list of directories to delete is taken from the "mkdir" command below,
193 # excluding "logs" (because logs are valuable) and "work" (because normally
194 # session files are there), and additionally including "bin".
195 rm -rf ${cfg.baseDir}/{conf,virtualhosts,temp,lib,shared/lib,webapps,bin}
198 # Create the base directory
200 ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
201 chown ${cfg.user}:${cfg.group} \
202 ${cfg.baseDir}/{conf,virtualhosts,logs,temp,lib,shared/lib,webapps,work}
204 # Create a symlink to the bin directory of the tomcat component
205 ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin
207 # Symlink the config files in the conf/ directory (except for catalina.properties and server.xml)
208 for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml); do
209 ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
212 ${lib.optionalString (cfg.extraConfigFiles != []) ''
213 for i in ${toString cfg.extraConfigFiles}; do
214 ln -sfn $i ${cfg.baseDir}/conf/`basename $i`
218 # Create a modified catalina.properties file
219 # Change all references from CATALINA_HOME to CATALINA_BASE and add support for shared libraries
220 sed -e 's|''${catalina.home}|''${catalina.base}|g' \
221 -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
222 ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
224 ${if cfg.serverXml != "" then ''
225 cp -f ${pkgs.writeTextDir "server.xml" cfg.serverXml}/* ${cfg.baseDir}/conf/
228 hostElementForVirtualHost = virtualHost: ''
229 <Host name="${virtualHost.name}" appBase="virtualhosts/${virtualHost.name}/webapps"
230 unpackWARs="true" autoDeploy="true" xmlValidation="false" xmlNamespaceAware="false">
231 '' + lib.concatStrings (innerElementsForVirtualHost virtualHost) + ''
234 innerElementsForVirtualHost = virtualHost:
236 <Alias>${alias}</Alias>
237 '') virtualHost.aliases)
238 ++ (lib.optional cfg.logPerVirtualHost ''
239 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs/${virtualHost.name}"
240 prefix="${virtualHost.name}_access_log." pattern="combined" resolveHosts="false"/>
242 hostElementsString = lib.concatMapStringsSep "\n" hostElementForVirtualHost cfg.virtualHosts;
243 hostElementsSedString = lib.replaceStrings ["\n"] ["\\\n"] hostElementsString;
245 # Create a modified server.xml which also includes all virtual hosts
246 sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\\"${lib.escapeShellArg hostElementsSedString} \
247 ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
250 ${lib.optionalString (cfg.logDirs != []) ''
251 for i in ${toString cfg.logDirs}; do
252 mkdir -p ${cfg.baseDir}/logs/$i
253 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/$i
256 ${lib.optionalString cfg.logPerVirtualHost (toString (map (h: ''
257 mkdir -p ${cfg.baseDir}/logs/${h.name}
258 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
259 '') cfg.virtualHosts))}
261 # Symlink all the given common libs files or paths into the lib/ directory
262 for i in ${tomcat} ${toString cfg.commonLibs}; do
264 # If the given web application is a file, symlink it into the common/lib/ directory
265 ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
267 # If the given web application is a directory, then iterate over the files
268 # in the special purpose directories and symlink them into the tomcat tree
270 for j in $i/lib/*; do
271 ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
276 # Symlink all the given shared libs files or paths into the shared/lib/ directory
277 for i in ${toString cfg.sharedLibs}; do
279 # If the given web application is a file, symlink it into the common/lib/ directory
280 ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
282 # If the given web application is a directory, then iterate over the files
283 # in the special purpose directories and symlink them into the tomcat tree
285 for j in $i/shared/lib/*; do
286 ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
291 # Symlink all the given web applications files or paths into the webapps/ directory
292 for i in ${toString cfg.webapps}; do
294 # If the given web application is a file, symlink it into the webapps/ directory
295 ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
297 # If the given web application is a directory, then iterate over the files
298 # in the special purpose directories and symlink them into the tomcat tree
300 for j in $i/webapps/*; do
301 ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
304 # Also symlink the configuration files if they are included
305 if [ -d $i/conf/Catalina ]; then
306 for j in $i/conf/Catalina/*; do
307 mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
308 ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
314 ${toString (map (virtualHost: ''
315 # Create webapps directory for the virtual host
316 mkdir -p ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
319 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
321 # Symlink all the given web applications files or paths into the webapps/ directory
322 # of this virtual host
323 for i in "${lib.optionalString (virtualHost ? webapps) (toString virtualHost.webapps)}"; do
325 # If the given web application is a file, symlink it into the webapps/ directory
326 ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
328 # If the given web application is a directory, then iterate over the files
329 # in the special purpose directories and symlink them into the tomcat tree
331 for j in $i/webapps/*; do
332 ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
335 # Also symlink the configuration files if they are included
336 if [ -d $i/conf/Catalina ]; then
337 for j in $i/conf/Catalina/*; do
338 mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
339 ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
344 '') cfg.virtualHosts)}
346 ${lib.optionalString cfg.axis2.enable ''
347 # Copy the Axis2 web application
348 cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
350 # Turn off addressing, which causes many errors
351 sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
353 # Modify permissions on the Axis2 application
354 chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
356 # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
357 for i in ${toString cfg.axis2.services}; do
359 # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
360 ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
362 # If the given web application is a directory, then iterate over the files
363 # in the special purpose directories and symlink them into the tomcat tree
365 for j in $i/webapps/axis2/WEB-INF/services/*; do
366 ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
369 # Also symlink the configuration files if they are included
370 if [ -d $i/conf/Catalina ]; then
371 for j in $i/conf/Catalina/*; do
372 ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
382 PermissionsStartOnly = true;
383 PIDFile = "/run/tomcat/tomcat.pid";
384 RuntimeDirectory = "tomcat";
387 "CATALINA_BASE=${cfg.baseDir}"
388 "CATALINA_PID=/run/tomcat/tomcat.pid"
389 "JAVA_HOME='${cfg.jdk}'"
390 "JAVA_OPTS='${builtins.toString cfg.javaOpts}'"
391 "CATALINA_OPTS='${builtins.toString cfg.catalinaOpts}'"
392 ] ++ cfg.extraEnvironment;
393 ExecStart = "${tomcat}/bin/startup.sh";
394 ExecStop = "${tomcat}/bin/shutdown.sh";