1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.services.tomcat;
8 tomcat = cfg.package;
9in
10
11{
12
13 meta = {
14 maintainers = with maintainers; [ danbst ];
15 };
16
17 ###### interface
18
19 options = {
20
21 services.tomcat = {
22
23 enable = mkOption {
24 default = false;
25 description = "Whether to enable Apache Tomcat";
26 };
27
28 package = mkOption {
29 type = types.package;
30 default = pkgs.tomcat85;
31 defaultText = "pkgs.tomcat85";
32 example = lib.literalExample "pkgs.tomcatUnstable";
33 description = ''
34 Which tomcat package to use.
35 '';
36 };
37
38 baseDir = mkOption {
39 default = "/var/tomcat";
40 description = "Location where Tomcat stores configuration files, webapplications and logfiles";
41 };
42
43 extraGroups = mkOption {
44 default = [];
45 example = [ "users" ];
46 description = "Defines extra groups to which the tomcat user belongs.";
47 };
48
49 user = mkOption {
50 default = "tomcat";
51 description = "User account under which Apache Tomcat runs.";
52 };
53
54 group = mkOption {
55 default = "tomcat";
56 description = "Group account under which Apache Tomcat runs.";
57 };
58
59 javaOpts = mkOption {
60 default = "";
61 description = "Parameters to pass to the Java Virtual Machine which spawns Apache Tomcat";
62 };
63
64 catalinaOpts = mkOption {
65 default = "";
66 description = "Parameters to pass to the Java Virtual Machine which spawns the Catalina servlet container";
67 };
68
69 sharedLibs = mkOption {
70 default = [];
71 description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications";
72 };
73
74 commonLibs = mkOption {
75 default = [];
76 description = "List containing JAR files or directories with JAR files which are libraries shared by the web applications and the servlet container";
77 };
78
79 webapps = mkOption {
80 type = types.listOf types.package;
81 default = [ tomcat.webapps ];
82 defaultText = "[ tomcat.webapps ]";
83 description = "List containing WAR files or directories with WAR files which are web applications to be deployed on Tomcat";
84 };
85
86 virtualHosts = mkOption {
87 default = [];
88 description = "List consisting of a virtual host name and a list of web applications to deploy on each virtual host";
89 };
90
91 logPerVirtualHost = mkOption {
92 default = false;
93 description = "Whether to enable logging per virtual host.";
94 };
95
96 jdk = mkOption {
97 type = types.package;
98 default = pkgs.jdk;
99 defaultText = "pkgs.jdk";
100 description = "Which JDK to use.";
101 };
102
103 axis2 = {
104
105 enable = mkOption {
106 default = false;
107 description = "Whether to enable an Apache Axis2 container";
108 };
109
110 services = mkOption {
111 default = [];
112 description = "List containing AAR files or directories with AAR files which are web services to be deployed on Axis2";
113 };
114
115 };
116
117 };
118
119 };
120
121
122 ###### implementation
123
124 config = mkIf config.services.tomcat.enable {
125
126 users.extraGroups = singleton
127 { name = "tomcat";
128 gid = config.ids.gids.tomcat;
129 };
130
131 users.extraUsers = singleton
132 { name = "tomcat";
133 uid = config.ids.uids.tomcat;
134 description = "Tomcat user";
135 home = "/homeless-shelter";
136 extraGroups = cfg.extraGroups;
137 };
138
139 systemd.services.tomcat = {
140 description = "Apache Tomcat server";
141 wantedBy = [ "multi-user.target" ];
142 after = [ "network.target" ];
143 serviceConfig.Type = "oneshot";
144 serviceConfig.RemainAfterExit = true;
145
146 preStart = ''
147 # Create the base directory
148 mkdir -p ${cfg.baseDir}
149
150 # Create a symlink to the bin directory of the tomcat component
151 ln -sfn ${tomcat}/bin ${cfg.baseDir}/bin
152
153 # Create a conf/ directory
154 mkdir -p ${cfg.baseDir}/conf
155 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/conf
156
157 # Symlink the config files in the conf/ directory (except for catalina.properties and server.xml)
158 for i in $(ls ${tomcat}/conf | grep -v catalina.properties | grep -v server.xml)
159 do
160 ln -sfn ${tomcat}/conf/$i ${cfg.baseDir}/conf/`basename $i`
161 done
162
163 # Create subdirectory for virtual hosts
164 mkdir -p ${cfg.baseDir}/virtualhosts
165
166 # Create a modified catalina.properties file
167 # Change all references from CATALINA_HOME to CATALINA_BASE and add support for shared libraries
168 sed -e 's|''${catalina.home}|''${catalina.base}|g' \
169 -e 's|shared.loader=|shared.loader=''${catalina.base}/shared/lib/*.jar|' \
170 ${tomcat}/conf/catalina.properties > ${cfg.baseDir}/conf/catalina.properties
171
172 # Create a modified server.xml which also includes all virtual hosts
173 sed -e "/<Engine name=\"Catalina\" defaultHost=\"localhost\">/a\ ${
174 toString (map (virtualHost: ''<Host name=\"${virtualHost.name}\" appBase=\"virtualhosts/${virtualHost.name}/webapps\" unpackWARs=\"true\" autoDeploy=\"true\" xmlValidation=\"false\" xmlNamespaceAware=\"false\" >${if cfg.logPerVirtualHost then ''<Valve className=\"org.apache.catalina.valves.AccessLogValve\" directory=\"logs/${virtualHost.name}\" prefix=\"${virtualHost.name}_access_log.\" pattern=\"combined\" resolveHosts=\"false\"/>'' else ""}</Host>'') cfg.virtualHosts)}" \
175 ${tomcat}/conf/server.xml > ${cfg.baseDir}/conf/server.xml
176
177 # Create a logs/ directory
178 mkdir -p ${cfg.baseDir}/logs
179 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs
180 ${if cfg.logPerVirtualHost then
181 toString (map (h: ''
182 mkdir -p ${cfg.baseDir}/logs/${h.name}
183 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/logs/${h.name}
184 '') cfg.virtualHosts) else ''''}
185
186 # Create a temp/ directory
187 mkdir -p ${cfg.baseDir}/temp
188 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/temp
189
190 # Create a lib/ directory
191 mkdir -p ${cfg.baseDir}/lib
192 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/lib
193
194 # Create a shared/lib directory
195 mkdir -p ${cfg.baseDir}/shared/lib
196 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/shared/lib
197
198 # Create a webapps/ directory
199 mkdir -p ${cfg.baseDir}/webapps
200 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps
201
202 # Symlink all the given common libs files or paths into the lib/ directory
203 for i in ${tomcat} ${toString cfg.commonLibs}
204 do
205 if [ -f $i ]
206 then
207 # If the given web application is a file, symlink it into the common/lib/ directory
208 ln -sfn $i ${cfg.baseDir}/lib/`basename $i`
209 elif [ -d $i ]
210 then
211 # If the given web application is a directory, then iterate over the files
212 # in the special purpose directories and symlink them into the tomcat tree
213
214 for j in $i/lib/*
215 do
216 ln -sfn $j ${cfg.baseDir}/lib/`basename $j`
217 done
218 fi
219 done
220
221 # Symlink all the given shared libs files or paths into the shared/lib/ directory
222 for i in ${toString cfg.sharedLibs}
223 do
224 if [ -f $i ]
225 then
226 # If the given web application is a file, symlink it into the common/lib/ directory
227 ln -sfn $i ${cfg.baseDir}/shared/lib/`basename $i`
228 elif [ -d $i ]
229 then
230 # If the given web application is a directory, then iterate over the files
231 # in the special purpose directories and symlink them into the tomcat tree
232
233 for j in $i/shared/lib/*
234 do
235 ln -sfn $j ${cfg.baseDir}/shared/lib/`basename $j`
236 done
237 fi
238 done
239
240 # Symlink all the given web applications files or paths into the webapps/ directory
241 for i in ${toString cfg.webapps}
242 do
243 if [ -f $i ]
244 then
245 # If the given web application is a file, symlink it into the webapps/ directory
246 ln -sfn $i ${cfg.baseDir}/webapps/`basename $i`
247 elif [ -d $i ]
248 then
249 # If the given web application is a directory, then iterate over the files
250 # in the special purpose directories and symlink them into the tomcat tree
251
252 for j in $i/webapps/*
253 do
254 ln -sfn $j ${cfg.baseDir}/webapps/`basename $j`
255 done
256
257 # Also symlink the configuration files if they are included
258 if [ -d $i/conf/Catalina ]
259 then
260 for j in $i/conf/Catalina/*
261 do
262 mkdir -p ${cfg.baseDir}/conf/Catalina/localhost
263 ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
264 done
265 fi
266 fi
267 done
268
269 ${toString (map (virtualHost: ''
270 # Create webapps directory for the virtual host
271 mkdir -p ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
272
273 # Modify ownership
274 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps
275
276 # Symlink all the given web applications files or paths into the webapps/ directory
277 # of this virtual host
278 for i in "${if virtualHost ? webapps then toString virtualHost.webapps else ""}"
279 do
280 if [ -f $i ]
281 then
282 # If the given web application is a file, symlink it into the webapps/ directory
283 ln -sfn $i ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $i`
284 elif [ -d $i ]
285 then
286 # If the given web application is a directory, then iterate over the files
287 # in the special purpose directories and symlink them into the tomcat tree
288
289 for j in $i/webapps/*
290 do
291 ln -sfn $j ${cfg.baseDir}/virtualhosts/${virtualHost.name}/webapps/`basename $j`
292 done
293
294 # Also symlink the configuration files if they are included
295 if [ -d $i/conf/Catalina ]
296 then
297 for j in $i/conf/Catalina/*
298 do
299 mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name}
300 ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j`
301 done
302 fi
303 fi
304 done
305
306 ''
307 ) cfg.virtualHosts) }
308
309 # Create a work/ directory
310 mkdir -p ${cfg.baseDir}/work
311 chown ${cfg.user}:${cfg.group} ${cfg.baseDir}/work
312
313 ${if cfg.axis2.enable then
314 ''
315 # Copy the Axis2 web application
316 cp -av ${pkgs.axis2}/webapps/axis2 ${cfg.baseDir}/webapps
317
318 # Turn off addressing, which causes many errors
319 sed -i -e 's%<module ref="addressing"/>%<!-- <module ref="addressing"/> -->%' ${cfg.baseDir}/webapps/axis2/WEB-INF/conf/axis2.xml
320
321 # Modify permissions on the Axis2 application
322 chown -R ${cfg.user}:${cfg.group} ${cfg.baseDir}/webapps/axis2
323
324 # Symlink all the given web service files or paths into the webapps/axis2/WEB-INF/services directory
325 for i in ${toString cfg.axis2.services}
326 do
327 if [ -f $i ]
328 then
329 # If the given web service is a file, symlink it into the webapps/axis2/WEB-INF/services
330 ln -sfn $i ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $i`
331 elif [ -d $i ]
332 then
333 # If the given web application is a directory, then iterate over the files
334 # in the special purpose directories and symlink them into the tomcat tree
335
336 for j in $i/webapps/axis2/WEB-INF/services/*
337 do
338 ln -sfn $j ${cfg.baseDir}/webapps/axis2/WEB-INF/services/`basename $j`
339 done
340
341 # Also symlink the configuration files if they are included
342 if [ -d $i/conf/Catalina ]
343 then
344 for j in $i/conf/Catalina/*
345 do
346 ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j`
347 done
348 fi
349 fi
350 done
351 ''
352 else ""}
353 '';
354
355 script = ''
356 ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${tomcat}/bin/startup.sh'
357 '';
358
359 preStop = ''
360 echo "Stopping tomcat..."
361 CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${cfg.jdk} ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c ${tomcat}/bin/shutdown.sh
362 '';
363
364 };
365
366 };
367
368}