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