1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package org.utgenome.shell.tomcat;
26
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.FileOutputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.net.InetAddress;
33 import java.net.URISyntaxException;
34 import java.net.URL;
35 import java.nio.channels.FileChannel;
36 import java.util.List;
37
38 import org.apache.catalina.Context;
39 import org.apache.catalina.Engine;
40 import org.apache.catalina.LifecycleException;
41 import org.apache.catalina.core.StandardHost;
42 import org.apache.catalina.startup.Embedded;
43 import org.apache.catalina.startup.HostConfig;
44 import org.xerial.core.XerialErrorCode;
45 import org.xerial.core.XerialException;
46 import org.xerial.util.FileResource;
47 import org.xerial.util.io.VirtualFile;
48 import org.xerial.util.log.Logger;
49 import org.xerial.util.opt.Option;
50 import org.xerial.util.opt.OptionParser;
51 import org.xerial.util.opt.OptionParserException;
52
53
54
55
56
57
58
59 public class TomcatServer {
60 private static Logger _logger = Logger.getLogger(TomcatServer.class);
61
62 private TomcatServerConfiguration configuration;
63 private Embedded embeddedTomcat = null;
64 private Engine tomcatEngine = null;
65 private StandardHost tomcatHost = null;
66
67 static {
68 }
69
70 public static class Opt {
71 @Option(symbol = "h", longName = "help", description = "display help message")
72 boolean displayHelp = false;
73
74 @Option(symbol = "p", longName = "port", varName = "PORT", description = "server port number. default=8989")
75 int port = 8989;
76
77 @Option(symbol = "t", longName = "tomcat_home", varName = "TOMCAT_HOME", description = "set the home directory of the Tomcat engine")
78 String catalinaBase = null;
79
80 @Option(symbol = "c", longName = "contextPath", varName = "path", description = "/path: URL path of the web application")
81 String contextPath = null;
82
83 @Option(symbol = "d", longName = "docBase", varName = "(WAR or docBase)", description = "path to the war file or docBase to deploy")
84 String docBase = null;
85 }
86
87
88
89
90
91
92 public static void main(String[] args) {
93
94 Opt opt = new Opt();
95 final OptionParser optionParser = new OptionParser(opt);
96 final TomcatServerConfiguration config = new TomcatServerConfiguration();
97
98 try {
99 optionParser.parse(args);
100
101 config.setPort(opt.port);
102
103 if (opt.displayHelp) {
104 throw new OptionParserException(XerialErrorCode.MISSING_ARGUMENT, "help");
105 }
106
107 if (opt.catalinaBase != null)
108 config.setCatalinaBase(opt.catalinaBase);
109
110
111 final TomcatServer server = new TomcatServer(config);
112 server.start();
113
114
115 if (opt.contextPath != null && opt.docBase != null) {
116 File docBase = new File(opt.docBase);
117 server.addContext(opt.contextPath, docBase.getAbsolutePath());
118 }
119
120 Runtime.getRuntime().addShutdownHook(new Thread() {
121 @Override
122 public void run() {
123 try {
124 server.stop();
125 }
126 catch (XerialException e) {
127 _logger.error(e);
128 }
129 }
130 });
131
132
133 while (true) {
134 Thread.sleep(10000000000000000L);
135 }
136
137 }
138 catch (OptionParserException e) {
139 if (e.getMessage().equals("help")) {
140 System.out.println("> TomcatServer [option]");
141 optionParser.printUsage();
142 }
143 else
144 System.err.println(e.getMessage());
145 return;
146 }
147 catch (XerialException e) {
148 _logger.error(e);
149 }
150 catch (InterruptedException e) {
151 _logger.error(e);
152 }
153
154 }
155
156
157
158
159
160
161
162
163 public TomcatServer(int port) throws XerialException {
164 this(TomcatServerConfiguration.newInstance(port));
165 }
166
167
168
169
170
171
172
173
174 public TomcatServer(TomcatServerConfiguration configuration) throws XerialException {
175 setConfiguration(configuration);
176
177 _logger.debug("port: " + configuration.getPort());
178 _logger.debug("catalina base: " + configuration.getCatalinaBase());
179
180 try {
181 prepareScaffold(configuration.getCatalinaBase());
182 }
183 catch (IOException e) {
184 throw new XerialException(XerialErrorCode.IO_EXCEPTION, e);
185 }
186
187
188 String logConfigPath = new File(configuration.getCatalinaBase(), "conf/logging.properties").getPath();
189 System.setProperty("catalina.base", configuration.getCatalinaBase());
190 System.setProperty("java.util.logging.manager", "org.apache.juli.ClassLoaderLogManager");
191 System.setProperty("java.util.logging.config.file", logConfigPath);
192
193 for (String p : new String[] { "catalina.base", "java.util.logging.manager", "java.util.logging.config.file" })
194 _logger.info(String.format("%s = %s", p, System.getProperty(p)));
195
196 if (_logger.isDebugEnabled())
197 _logger.debug("juli log config file: " + logConfigPath);
198
199 }
200
201 public void setConfiguration(TomcatServerConfiguration configuration) {
202 this.configuration = configuration;
203 }
204
205 private ClassLoader getExtensionClassLoader() {
206 ClassLoader cl = Thread.currentThread().getContextClassLoader();
207 if (cl != null) {
208 ClassLoader parent = cl.getParent();
209 if (parent != null)
210 cl = parent;
211 }
212
213 return cl;
214
215 }
216
217
218
219
220
221
222
223 public void start() throws XerialException {
224
225
226 embeddedTomcat = new Embedded();
227 embeddedTomcat.setAwait(true);
228 embeddedTomcat.setCatalinaBase(configuration.getCatalinaBase());
229
230
231 tomcatEngine = embeddedTomcat.createEngine();
232 tomcatEngine.setName("utgb");
233 tomcatEngine.setDefaultHost("localhost");
234 tomcatEngine.setParentClassLoader(getExtensionClassLoader());
235
236
237 String appBase = configuration.getCatalinaBase() + "/webapps";
238 _logger.debug("appBase: " + appBase);
239 tomcatHost = (StandardHost) embeddedTomcat.createHost("localhost", appBase);
240
241
242 HostConfig hostConfig = new HostConfig();
243 tomcatHost.addLifecycleListener(hostConfig);
244
245
246 tomcatEngine.addChild(tomcatHost);
247 tomcatEngine.setDefaultHost(tomcatHost.getName());
248
249
250 embeddedTomcat.addEngine(tomcatEngine);
251
252
253 InetAddress nullAddr = null;
254 org.apache.catalina.connector.Connector conn = embeddedTomcat.createConnector(nullAddr, configuration.getPort(), false);
255 conn.setEnableLookups(true);
256
257 embeddedTomcat.addConnector(conn);
258
259
260
261
262 try {
263 org.apache.catalina.connector.Connector ajp13connector = new org.apache.catalina.connector.Connector("org.apache.jk.server.JkCoyoteHandler");
264 ajp13connector.setPort(configuration.getAjp13port());
265 ajp13connector.setProtocol("AJP/1.3");
266 ajp13connector.setRedirectPort(8443);
267 embeddedTomcat.addConnector(ajp13connector);
268 }
269 catch (Exception e1) {
270 throw new XerialException(XerialErrorCode.INVALID_STATE, e1);
271 }
272
273
274
275
276
277
278
279
280
281
282
283
284 try {
285 embeddedTomcat.start();
286 }
287 catch (LifecycleException e) {
288 _logger.error(e);
289 String m = e.getMessage();
290 if (m != null && m.indexOf("already in use") != -1)
291 m = "port " + configuration.getPort() + " is already in use. You probably have another tomcat listening the same port";
292
293
294
295 throw new XerialException(XerialErrorCode.INVALID_STATE, e);
296 }
297
298 }
299
300 public void registerWAR(String contextPath, String pathToTheWarFile) throws XerialException {
301 addContext(contextPath, pathToTheWarFile);
302 }
303
304 public void addContext(String contextPath, String docBase) throws XerialException {
305 if (embeddedTomcat == null)
306 throw new XerialException(XerialErrorCode.INVALID_STATE, "tomcat server is not started yet.");
307
308 _logger.debug("deploy: contextPath=" + contextPath + ", docBase=" + docBase);
309
310 Context context = embeddedTomcat.createContext(contextPath, docBase);
311
312 context.setConfigFile(docBase + "/META-INF/context.xml");
313 tomcatHost.addChild(context);
314 }
315
316
317
318
319
320
321
322
323
324
325
326 public void stop() throws XerialException {
327 if (embeddedTomcat != null) {
328 try {
329 embeddedTomcat.stop();
330 embeddedTomcat = null;
331 }
332 catch (LifecycleException e) {
333 throw new XerialException(XerialErrorCode.INVALID_STATE, e);
334 }
335 finally {
336
337 }
338 }
339 }
340
341
342
343
344
345
346
347 private void prepareScaffold(String catalinaBase) throws IOException {
348
349 File tomcatBase = new File(catalinaBase);
350
351 List<VirtualFile> tomcatResources = FileResource.listResources("org.utgenome.shell.tomcat.scaffold");
352
353 if (tomcatResources.size() <= 0)
354 throw new IllegalStateException("org.utgenome.shell.tomcat.scaffold is not found");
355
356
357 for (VirtualFile vf : tomcatResources) {
358 String srcLogicalPath = vf.getLogicalPath();
359 File targetFile = new File(tomcatBase, srcLogicalPath);
360
361 if (vf.isDirectory()) {
362 targetFile.mkdirs();
363 }
364 else {
365 _logger.debug("rsync: src=" + vf.getLogicalPath() + " target=" + targetFile.getPath());
366
367 File parentFolder = targetFile.getParentFile();
368 parentFolder.mkdirs();
369
370
371 if (!targetFile.exists()) {
372 InputStream reader = vf.getURL().openStream();
373 FileOutputStream writer = new FileOutputStream(targetFile);
374 byte[] buffer = new byte[1024];
375 int bytesRead = 0;
376 while ((bytesRead = reader.read(buffer)) > 0) {
377 writer.write(buffer, 0, bytesRead);
378 }
379 }
380 }
381 }
382
383 }
384
385 private static String packagePath(String packageName) {
386 String packageAsPath = packageName.replaceAll("\\.", "/");
387 return packageAsPath.endsWith("/") ? packageAsPath : packageAsPath + "/";
388 }
389
390 public File findDir(String resourceName) {
391 String resourcePath = packagePath(resourceName);
392 if (!resourcePath.startsWith("/"))
393 resourcePath = "/" + resourcePath;
394 URL resourceURL = this.getClass().getResource(resourcePath);
395 if (resourceURL != null) {
396 _logger.debug("found resource:" + resourceURL);
397 String protocol = resourceURL.getProtocol();
398 if (protocol.equals("file")) {
399 try {
400 return new File(resourceURL.toURI());
401 }
402 catch (URISyntaxException e) {
403 _logger.error(e);
404 }
405 }
406
407 return new File(resourceURL.toString());
408 }
409 return null;
410 }
411
412
413
414
415
416
417
418
419 private void rsync(File srcDir, File targetDir) throws IOException {
420 _logger.trace("rsync: src=" + srcDir + ", dest=" + targetDir);
421
422
423
424
425
426 if (srcDir.getName() == ".svn")
427 return;
428
429
430 targetDir.mkdirs();
431
432 for (File file : srcDir.listFiles()) {
433 String fileName = file.getName();
434 if (file.isDirectory()) {
435 rsync(file, new File(targetDir, fileName));
436 continue;
437 }
438
439 if (fileName.startsWith("~"))
440 continue;
441
442 File newFile = new File(targetDir, fileName);
443
444 copyWithNoOverwrite(file, newFile);
445 }
446 }
447
448 private void copyWithNoOverwrite(File from, File to) throws IOException {
449 if (to.exists())
450 return;
451 FileChannel srcChannel = new FileInputStream(from).getChannel();
452 FileChannel destChannel = new FileOutputStream(to).getChannel();
453 srcChannel.transferTo(0, srcChannel.size(), destChannel);
454 srcChannel.close();
455 destChannel.close();
456 }
457
458 }