1 package org.apache.tomcat.maven.runner;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import org.apache.catalina.Context;
22 import org.apache.catalina.connector.Connector;
23 import org.apache.catalina.startup.Catalina;
24 import org.apache.catalina.startup.Tomcat;
25 import org.apache.catalina.valves.AccessLogValve;
26 import org.apache.tomcat.util.http.fileupload.FileUtils;
27 import org.apache.tomcat.util.http.fileupload.IOUtils;
28
29 import java.io.BufferedOutputStream;
30 import java.io.File;
31 import java.io.FileNotFoundException;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.lang.reflect.InvocationTargetException;
36 import java.lang.reflect.Method;
37 import java.net.URL;
38 import java.util.HashMap;
39 import java.util.Map;
40 import java.util.Properties;
41 import java.util.StringTokenizer;
42
43
44
45
46
47
48
49
50 public class Tomcat7Runner
51 {
52
53 public static final String USE_SERVER_XML_KEY = "useServerXml";
54
55
56 public static final String WARS_KEY = "wars";
57
58 public static final String ENABLE_NAMING_KEY = "enableNaming";
59
60 public static final String ACCESS_LOG_VALVE_FORMAT_KEY = "accessLogValveFormat";
61
62
63
64
65 public static final String HTTP_PROTOCOL_KEY = "connectorhttpProtocol";
66
67
68 public int httpPort;
69
70 public int httpsPort;
71
72 public int ajpPort;
73
74 public String serverXmlPath;
75
76 public Properties runtimeProperties;
77
78 public boolean resetExtract;
79
80 public boolean debug = false;
81
82 public boolean clientAuth = false;
83
84 public String keyAlias = null;
85
86 public String httpProtocol;
87
88 public String extractDirectory = ".extract";
89
90 public File extractDirectoryFile;
91
92 public String loggerName;
93
94 Catalina container;
95
96 Tomcat tomcat;
97
98 String uriEncoding = "ISO-8859-1";
99
100
101
102
103 Map<String, String> webappWarPerContext = new HashMap<String, String>();
104
105 public Tomcat7Runner()
106 {
107
108 }
109
110 public void run()
111 throws Exception
112 {
113
114 PasswordUtil.deobfuscateSystemProps();
115
116 if ( loggerName != null && loggerName.length() > 0 )
117 {
118 installLogger( loggerName );
119 }
120
121 this.extractDirectoryFile = new File( this.extractDirectory );
122
123 debugMessage( "use extractDirectory:" + extractDirectoryFile.getPath() );
124
125
126 if ( !extractDirectoryFile.exists() || resetExtract )
127 {
128 extract();
129 }
130 else
131 {
132 String wars = runtimeProperties.getProperty( WARS_KEY );
133 populateWebAppWarPerContext( wars );
134 }
135
136
137 new File( extractDirectory, "conf" ).mkdirs();
138 new File( extractDirectory, "logs" ).mkdirs();
139 new File( extractDirectory, "webapps" ).mkdirs();
140 new File( extractDirectory, "work" ).mkdirs();
141 File tmpDir = new File( extractDirectory, "temp" );
142 tmpDir.mkdirs();
143
144 System.setProperty( "java.io.tmpdir", tmpDir.getAbsolutePath() );
145
146 System.setProperty( "catalina.base", extractDirectoryFile.getAbsolutePath() );
147 System.setProperty( "catalina.home", extractDirectoryFile.getAbsolutePath() );
148
149
150 if ( serverXmlPath != null || useServerXml() )
151 {
152 container = new Catalina();
153 container.setUseNaming( this.enableNaming() );
154 if ( serverXmlPath != null && new File( serverXmlPath ).exists() )
155 {
156 container.setConfig( serverXmlPath );
157 }
158 else
159 {
160 container.setConfig( new File( extractDirectory, "conf/server.xml" ).getAbsolutePath() );
161 }
162 container.start();
163 }
164 else
165 {
166 tomcat = new Tomcat();
167
168 if ( this.enableNaming() )
169 {
170 System.setProperty( "catalina.useNaming", "true" );
171 tomcat.enableNaming();
172 }
173
174 tomcat.getHost().setAppBase( new File( extractDirectory, "webapps" ).getAbsolutePath() );
175
176 String connectorHttpProtocol = runtimeProperties.getProperty( HTTP_PROTOCOL_KEY );
177
178 if ( httpProtocol != null && httpProtocol.trim().length() > 0 )
179 {
180 connectorHttpProtocol = httpProtocol;
181 }
182
183 debugMessage( "use connectorHttpProtocol:" + connectorHttpProtocol );
184
185 if ( httpPort > 0 )
186 {
187 Connector connector = new Connector( connectorHttpProtocol );
188 connector.setPort( httpPort );
189
190 if ( httpsPort > 0 )
191 {
192 connector.setRedirectPort( httpsPort );
193 }
194 connector.setURIEncoding( uriEncoding );
195
196 tomcat.getService().addConnector( connector );
197
198 tomcat.setConnector( connector );
199 }
200
201
202 AccessLogValve alv = new AccessLogValve();
203 alv.setDirectory( new File( extractDirectory, "logs" ).getAbsolutePath() );
204 alv.setPattern( runtimeProperties.getProperty( Tomcat7Runner.ACCESS_LOG_VALVE_FORMAT_KEY ) );
205 tomcat.getHost().getPipeline().addValve( alv );
206
207
208 if ( httpsPort > 0 )
209 {
210 Connector httpsConnector = new Connector( connectorHttpProtocol );
211 httpsConnector.setPort( httpsPort );
212 httpsConnector.setSecure( true );
213 httpsConnector.setProperty( "SSLEnabled", "true" );
214 httpsConnector.setProperty( "sslProtocol", "TLS" );
215 httpsConnector.setURIEncoding( uriEncoding );
216
217 String keystoreFile = System.getProperty( "javax.net.ssl.keyStore" );
218 String keystorePass = System.getProperty( "javax.net.ssl.keyStorePassword" );
219 String keystoreType = System.getProperty( "javax.net.ssl.keyStoreType", "jks" );
220
221 if ( keystoreFile != null )
222 {
223 httpsConnector.setAttribute( "keystoreFile", keystoreFile );
224 }
225 if ( keystorePass != null )
226 {
227 httpsConnector.setAttribute( "keystorePass", keystorePass );
228 }
229 httpsConnector.setAttribute( "keystoreType", keystoreType );
230
231 String truststoreFile = System.getProperty( "javax.net.ssl.trustStore" );
232 String truststorePass = System.getProperty( "javax.net.ssl.trustStorePassword" );
233 String truststoreType = System.getProperty( "javax.net.ssl.trustStoreType", "jks" );
234 if ( truststoreFile != null )
235 {
236 httpsConnector.setAttribute( "truststoreFile", truststoreFile );
237 }
238 if ( truststorePass != null )
239 {
240 httpsConnector.setAttribute( "truststorePass", truststorePass );
241 }
242 httpsConnector.setAttribute( "truststoreType", truststoreType );
243
244 httpsConnector.setAttribute( "clientAuth", clientAuth );
245 httpsConnector.setAttribute( "keyAlias", keyAlias );
246
247 tomcat.getService().addConnector( httpsConnector );
248
249 if ( httpPort <= 0 )
250 {
251 tomcat.setConnector( httpsConnector );
252 }
253 }
254
255
256 if ( ajpPort > 0 )
257 {
258 Connector ajpConnector = new Connector( "org.apache.coyote.ajp.AjpProtocol" );
259 ajpConnector.setPort( ajpPort );
260 ajpConnector.setURIEncoding( uriEncoding );
261 tomcat.getService().addConnector( ajpConnector );
262 }
263
264
265 for ( Map.Entry<String, String> entry : this.webappWarPerContext.entrySet() )
266 {
267 String baseDir = null;
268 if ( entry.getKey().equals( "/" ) )
269 {
270 baseDir = new File( extractDirectory, "webapps/ROOT.war" ).getAbsolutePath();
271 }
272 else
273 {
274 baseDir = new File( extractDirectory, "webapps/" + entry.getValue() ).getAbsolutePath();
275 }
276 Context context = tomcat.addWebapp( entry.getKey(), baseDir );
277 URL contextFileUrl = getContextXml( baseDir );
278 if ( contextFileUrl != null )
279 {
280 context.setConfigFile( contextFileUrl );
281 }
282 }
283
284 tomcat.start();
285 }
286
287 waitIndefinitely();
288
289 }
290
291 private URL getContextXml( String warPath )
292 throws IOException
293 {
294 InputStream inputStream = null;
295 try
296 {
297 String urlStr = "jar:file:" + warPath + "!/META-INF/context.xml";
298 debugMessage( "search context.xml in url:'" + urlStr + "'" );
299 URL url = new URL( urlStr );
300 inputStream = url.openConnection().getInputStream();
301 if ( inputStream != null )
302 {
303 return url;
304 }
305 }
306 catch ( FileNotFoundException e )
307 {
308 return null;
309 }
310 finally
311 {
312 IOUtils.closeQuietly( inputStream );
313 }
314 return null;
315 }
316
317 private void waitIndefinitely()
318 {
319 Object lock = new Object();
320
321 synchronized ( lock )
322 {
323 try
324 {
325 lock.wait();
326 }
327 catch ( InterruptedException exception )
328 {
329 throw new Error( "InterruptedException on wait Indefinitely lock:" + exception.getMessage(),
330 exception );
331 }
332 }
333 }
334
335 public void stop()
336 throws Exception
337 {
338 if ( container != null )
339 {
340 container.stop();
341 }
342 if ( tomcat != null )
343 {
344 tomcat.stop();
345 }
346 }
347
348 protected void extract()
349 throws Exception
350 {
351
352 if ( extractDirectoryFile.exists() )
353 {
354 FileUtils.deleteDirectory( extractDirectoryFile );
355 }
356
357 if ( !this.extractDirectoryFile.exists() )
358 {
359 boolean created = this.extractDirectoryFile.mkdirs();
360 if ( !created )
361 {
362 throw new Exception( "FATAL: impossible to create directory:" + this.extractDirectoryFile.getPath() );
363 }
364 }
365
366
367 boolean created = new File( extractDirectory, "webapps" ).mkdirs();
368 if ( !created )
369 {
370 throw new Exception(
371 "FATAL: impossible to create directory:" + this.extractDirectoryFile.getPath() + "/webapps" );
372
373 }
374
375 String wars = runtimeProperties.getProperty( WARS_KEY );
376 populateWebAppWarPerContext( wars );
377
378 for ( Map.Entry<String, String> entry : webappWarPerContext.entrySet() )
379 {
380 debugMessage( "webappWarPerContext entry key/value: " + entry.getKey() + "/" + entry.getValue() );
381 InputStream inputStream = null;
382 try
383 {
384 inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( entry.getValue() );
385 if ( !useServerXml() )
386 {
387 if ( entry.getKey().equals( "/" ) )
388 {
389 File expandFile = new File( extractDirectory, "webapps/ROOT.war" );
390 debugMessage( "expand to file:" + expandFile.getPath() );
391 expand( inputStream, expandFile );
392 }
393 else
394 {
395 File expandFile = new File( extractDirectory, "webapps/" + entry.getValue() );
396 debugMessage( "expand to file:" + expandFile.getPath() );
397 expand( inputStream, expandFile );
398 }
399 }
400 else
401 {
402 File expandFile = new File( extractDirectory, "webapps/" + entry.getValue() );
403 debugMessage( "expand to file:" + expandFile.getPath() );
404 expand( inputStream, new File( extractDirectory, "webapps/" + entry.getValue() ) );
405 }
406 }
407 finally
408 {
409 if ( inputStream != null )
410 {
411 inputStream.close();
412 }
413 }
414 }
415
416
417 expandConfigurationFile( "catalina.properties", extractDirectoryFile );
418 expandConfigurationFile( "logging.properties", extractDirectoryFile );
419 expandConfigurationFile( "tomcat-users.xml", extractDirectoryFile );
420 expandConfigurationFile( "catalina.policy", extractDirectoryFile );
421 expandConfigurationFile( "context.xml", extractDirectoryFile );
422 expandConfigurationFile( "server.xml", extractDirectoryFile );
423 expandConfigurationFile( "web.xml", extractDirectoryFile );
424
425 }
426
427 private static void expandConfigurationFile( String fileName, File extractDirectory )
428 throws Exception
429 {
430 InputStream inputStream = null;
431 try
432 {
433 inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( "conf/" + fileName );
434 if ( inputStream != null )
435 {
436 File confDirectory = new File( extractDirectory, "conf" );
437 if ( !confDirectory.exists() )
438 {
439 confDirectory.mkdirs();
440 }
441 expand( inputStream, new File( confDirectory, fileName ) );
442 }
443 }
444 finally
445 {
446 if ( inputStream != null )
447 {
448 inputStream.close();
449 }
450 }
451
452 }
453
454
455
456
457
458 private void populateWebAppWarPerContext( String warsValue )
459 {
460 StringTokenizer st = new StringTokenizer( warsValue, ";" );
461 while ( st.hasMoreTokens() )
462 {
463 String warValue = st.nextToken();
464 debugMessage( "populateWebAppWarPerContext warValue:" + warValue );
465 String warFileName = "";
466 String contextValue = "";
467 int separatorIndex = warValue.indexOf( "|" );
468 if ( separatorIndex >= 0 )
469 {
470 warFileName = warValue.substring( 0, separatorIndex );
471 contextValue = warValue.substring( separatorIndex + 1, warValue.length() );
472
473 }
474 else
475 {
476 warFileName = contextValue;
477 }
478 debugMessage( "populateWebAppWarPerContext contextValue/warFileName:" + contextValue + "/" + warFileName );
479 this.webappWarPerContext.put( contextValue, warFileName );
480 }
481 }
482
483
484
485
486
487
488
489
490
491 private static void expand( InputStream input, File file )
492 throws IOException
493 {
494 BufferedOutputStream output = null;
495 try
496 {
497 output = new BufferedOutputStream( new FileOutputStream( file ) );
498 byte buffer[] = new byte[2048];
499 while ( true )
500 {
501 int n = input.read( buffer );
502 if ( n <= 0 )
503 {
504 break;
505 }
506 output.write( buffer, 0, n );
507 }
508 }
509 finally
510 {
511 if ( output != null )
512 {
513 try
514 {
515 output.close();
516 }
517 catch ( IOException e )
518 {
519
520 }
521 }
522 }
523 }
524
525 public boolean useServerXml()
526 {
527 return Boolean.parseBoolean( runtimeProperties.getProperty( USE_SERVER_XML_KEY, Boolean.FALSE.toString() ) );
528 }
529
530
531 public void debugMessage( String message )
532 {
533 if ( debug )
534 {
535 System.out.println( message );
536 }
537 }
538
539
540 public boolean enableNaming()
541 {
542 return Boolean.parseBoolean( runtimeProperties.getProperty( ENABLE_NAMING_KEY, Boolean.FALSE.toString() ) );
543 }
544
545 private void installLogger( String loggerName )
546 throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
547 InvocationTargetException
548 {
549 if ( "slf4j".equals( loggerName ) )
550 {
551
552 try
553 {
554
555 final Class<?> clazz = Class.forName( "org.slf4j.bridge.SLF4JBridgeHandler" );
556
557
558 java.util.logging.LogManager.getLogManager().reset();
559
560
561 final Method method = clazz.getMethod( "install", null );
562 method.invoke( null );
563 }
564 catch ( ClassNotFoundException e )
565 {
566 System.out.println( "WARNING: issue configuring slf4j jul bridge, skip it" );
567 }
568 }
569 else
570 {
571 System.out.println( "WARNING: loggerName " + loggerName + " not supported, skip it" );
572 }
573 }
574 }