View Javadoc

1   package org.apache.tomcat.maven.runner;
2   /*
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   */
20  
21  import org.apache.catalina.Context;
22  import org.apache.catalina.Host;
23  import org.apache.catalina.connector.Connector;
24  import org.apache.catalina.core.StandardContext;
25  import org.apache.catalina.startup.Catalina;
26  import org.apache.catalina.startup.ContextConfig;
27  import org.apache.catalina.startup.Tomcat;
28  import org.apache.catalina.valves.AccessLogValve;
29  import org.apache.juli.ClassLoaderLogManager;
30  import org.apache.tomcat.util.ExceptionUtils;
31  import org.apache.tomcat.util.http.fileupload.FileUtils;
32  
33  import java.io.BufferedOutputStream;
34  import java.io.File;
35  import java.io.FileInputStream;
36  import java.io.FileNotFoundException;
37  import java.io.FileOutputStream;
38  import java.io.IOException;
39  import java.io.InputStream;
40  import java.lang.reflect.InvocationTargetException;
41  import java.lang.reflect.Method;
42  import java.net.URISyntaxException;
43  import java.net.URL;
44  import java.security.AccessController;
45  import java.security.PrivilegedAction;
46  import java.util.HashMap;
47  import java.util.Map;
48  import java.util.Properties;
49  import java.util.StringTokenizer;
50  import java.util.logging.LogManager;
51  
52  /**
53   * FIXME add junit for that but when https://issues.apache.org/bugzilla/show_bug.cgi?id=52028 fixed
54   * Main class used to run the standalone wars in a Apache Tomcat instance.
55   *
56   * @author Olivier Lamy
57   * @since 2.0
58   */
59  public class Tomcat7Runner
60  {
61      // true/false to use the server.xml located in the jar /conf/server.xml
62      public static final String USE_SERVER_XML_KEY = "useServerXml";
63  
64      // contains war name wars=foo.war,bar.war
65      public static final String WARS_KEY = "wars";
66  
67      public static final String ARCHIVE_GENERATION_TIMESTAMP_KEY = "generationTimestamp";
68  
69      public static final String ENABLE_NAMING_KEY = "enableNaming";
70  
71      public static final String ACCESS_LOG_VALVE_FORMAT_KEY = "accessLogValveFormat";
72  
73      public static final String CODE_SOURCE_CONTEXT_PATH = "codeSourceContextPath";
74  
75      /**
76       * key of the property which contains http protocol : HTTP/1.1 or org.apache.coyote.http11.Http11NioProtocol
77       */
78      public static final String HTTP_PROTOCOL_KEY = "connectorhttpProtocol";
79  
80      /**
81       * key for default http port defined in the plugin
82       */
83      public static final String HTTP_PORT_KEY = "httpPort";
84  
85  
86      public int httpPort;
87  
88      public int httpsPort;
89  
90      public int ajpPort;
91  
92      public String serverXmlPath;
93  
94      public Properties runtimeProperties;
95  
96      public boolean resetExtract;
97  
98      public boolean debug = false;
99  
100     public String clientAuth = "false";
101 
102     public String keyAlias = null;
103 
104     public String httpProtocol;
105 
106     public String extractDirectory = ".extract";
107 
108     public File extractDirectoryFile;
109 
110     public String codeSourceContextPath = null;
111 
112     public File codeSourceWar = null;
113 
114     public String loggerName;
115 
116     Catalina container;
117 
118     Tomcat tomcat;
119 
120     String uriEncoding = "ISO-8859-1";
121 
122     /**
123      * key = context of the webapp, value = war path on file system
124      */
125     Map<String, String> webappWarPerContext = new HashMap<String, String>();
126 
127     public Tomcat7Runner()
128     {
129         // no op
130     }
131 
132     public void run()
133         throws Exception
134     {
135 
136         PasswordUtil.deobfuscateSystemProps();
137 
138         if ( loggerName != null && loggerName.length() > 0 )
139         {
140             installLogger( loggerName );
141         }
142 
143         this.extractDirectoryFile = new File( this.extractDirectory );
144 
145         debugMessage( "use extractDirectory:" + extractDirectoryFile.getPath() );
146 
147         boolean archiveTimestampChanged = false;
148 
149         // compare timestamp stored during previous run if exists
150         File timestampFile = new File( extractDirectoryFile, ".tomcat_executable_archive.timestamp" );
151 
152         Properties timestampProps = loadProperties( timestampFile );
153 
154         if ( timestampFile.exists() )
155         {
156             String timestampValue = timestampProps.getProperty( Tomcat7Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY );
157             if ( timestampValue != null )
158             {
159                 long timestamp = Long.parseLong( timestampValue );
160                 archiveTimestampChanged =
161                     Long.parseLong( runtimeProperties.getProperty( Tomcat7Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY ) )
162                         > timestamp;
163 
164                 debugMessage( "read timestamp from file " + timestampValue + ", archiveTimestampChanged: "
165                                   + archiveTimestampChanged );
166             }
167 
168         }
169 
170         codeSourceContextPath = runtimeProperties.getProperty( CODE_SOURCE_CONTEXT_PATH );
171         if ( codeSourceContextPath != null && !codeSourceContextPath.isEmpty() )
172         {
173             codeSourceWar = AccessController.doPrivileged( new PrivilegedAction<File>()
174             {
175                 public File run()
176                 {
177                     try
178                     {
179                         File src =
180                             new File( Tomcat7Runner.class.getProtectionDomain().getCodeSource().getLocation().toURI() );
181                         if ( src.getName().endsWith( ".war" ) )
182                         {
183                             return src;
184                         }
185                         else
186                         {
187                             debugMessage( "ERROR: Code source is not a war file, ignoring." );
188                         }
189                     }
190                     catch ( URISyntaxException e )
191                     {
192                         debugMessage( "ERROR: Could not find code source. " + e.getMessage() );
193 
194                     }
195                     return null;
196                 }
197             } );
198         }
199 
200         // do we have to extract content
201         {
202             if ( !extractDirectoryFile.exists() || resetExtract || archiveTimestampChanged )
203             {
204                 extract();
205                 //if archiveTimestampChanged or timestamp file not exists store the last timestamp from the archive
206                 if ( archiveTimestampChanged || !timestampFile.exists() )
207                 {
208                     timestampProps.put( Tomcat7Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY, runtimeProperties.getProperty(
209                         Tomcat7Runner.ARCHIVE_GENERATION_TIMESTAMP_KEY ) );
210                     saveProperties( timestampProps, timestampFile );
211                 }
212             }
213             else
214             {
215                 String wars = runtimeProperties.getProperty( WARS_KEY );
216                 populateWebAppWarPerContext( wars );
217             }
218         }
219 
220         // create tomcat various paths
221         new File( extractDirectory, "conf" ).mkdirs();
222         new File( extractDirectory, "logs" ).mkdirs();
223         new File( extractDirectory, "webapps" ).mkdirs();
224         new File( extractDirectory, "work" ).mkdirs();
225         File tmpDir = new File( extractDirectory, "temp" );
226         tmpDir.mkdirs();
227 
228         System.setProperty( "java.io.tmpdir", tmpDir.getAbsolutePath() );
229 
230         System.setProperty( "catalina.base", extractDirectoryFile.getAbsolutePath() );
231         System.setProperty( "catalina.home", extractDirectoryFile.getAbsolutePath() );
232 
233         // start with a server.xml
234         if ( serverXmlPath != null || useServerXml() )
235         {
236             container = new Catalina();
237             container.setUseNaming( this.enableNaming() );
238             if ( serverXmlPath != null && new File( serverXmlPath ).exists() )
239             {
240                 container.setConfig( serverXmlPath );
241             }
242             else
243             {
244                 container.setConfig( new File( extractDirectory, "conf/server.xml" ).getAbsolutePath() );
245             }
246             container.start();
247         }
248         else
249         {
250             tomcat = new Tomcat()
251             {
252                 public Context addWebapp( Host host, String url, String name, String path )
253                 {
254 
255                     Context ctx = new StandardContext();
256                     ctx.setName( name );
257                     ctx.setPath( url );
258                     ctx.setDocBase( path );
259 
260                     ContextConfig ctxCfg = new ContextConfig();
261                     ctx.addLifecycleListener( ctxCfg );
262 
263                     ctxCfg.setDefaultWebXml( new File( extractDirectory, "conf/web.xml" ).getAbsolutePath() );
264 
265                     if ( host == null )
266                     {
267                         getHost().addChild( ctx );
268                     }
269                     else
270                     {
271                         host.addChild( ctx );
272                     }
273 
274                     return ctx;
275                 }
276             };
277 
278             if ( this.enableNaming() )
279             {
280                 System.setProperty( "catalina.useNaming", "true" );
281                 tomcat.enableNaming();
282             }
283 
284             tomcat.getHost().setAppBase( new File( extractDirectory, "webapps" ).getAbsolutePath() );
285 
286             String connectorHttpProtocol = runtimeProperties.getProperty( HTTP_PROTOCOL_KEY );
287 
288             if ( httpProtocol != null && httpProtocol.trim().length() > 0 )
289             {
290                 connectorHttpProtocol = httpProtocol;
291             }
292 
293             debugMessage( "use connectorHttpProtocol:" + connectorHttpProtocol );
294 
295             if ( httpPort > 0 )
296             {
297                 Connector connector = new Connector( connectorHttpProtocol );
298                 connector.setPort( httpPort );
299 
300                 if ( httpsPort > 0 )
301                 {
302                     connector.setRedirectPort( httpsPort );
303                 }
304                 connector.setURIEncoding( uriEncoding );
305 
306                 tomcat.getService().addConnector( connector );
307 
308                 tomcat.setConnector( connector );
309             }
310 
311             // add a default acces log valve
312             AccessLogValve alv = new AccessLogValve();
313             alv.setDirectory( new File( extractDirectory, "logs" ).getAbsolutePath() );
314             alv.setPattern( runtimeProperties.getProperty( Tomcat7Runner.ACCESS_LOG_VALVE_FORMAT_KEY ) );
315             tomcat.getHost().getPipeline().addValve( alv );
316 
317             // create https connector
318             if ( httpsPort > 0 )
319             {
320                 Connector httpsConnector = new Connector( connectorHttpProtocol );
321                 httpsConnector.setPort( httpsPort );
322                 httpsConnector.setSecure( true );
323                 httpsConnector.setProperty( "SSLEnabled", "true" );
324                 httpsConnector.setProperty( "sslProtocol", "TLS" );
325                 httpsConnector.setURIEncoding( uriEncoding );
326 
327                 String keystoreFile = System.getProperty( "javax.net.ssl.keyStore" );
328                 String keystorePass = System.getProperty( "javax.net.ssl.keyStorePassword" );
329                 String keystoreType = System.getProperty( "javax.net.ssl.keyStoreType", "jks" );
330 
331                 if ( keystoreFile != null )
332                 {
333                     httpsConnector.setAttribute( "keystoreFile", keystoreFile );
334                 }
335                 if ( keystorePass != null )
336                 {
337                     httpsConnector.setAttribute( "keystorePass", keystorePass );
338                 }
339                 httpsConnector.setAttribute( "keystoreType", keystoreType );
340 
341                 String truststoreFile = System.getProperty( "javax.net.ssl.trustStore" );
342                 String truststorePass = System.getProperty( "javax.net.ssl.trustStorePassword" );
343                 String truststoreType = System.getProperty( "javax.net.ssl.trustStoreType", "jks" );
344                 if ( truststoreFile != null )
345                 {
346                     httpsConnector.setAttribute( "truststoreFile", truststoreFile );
347                 }
348                 if ( truststorePass != null )
349                 {
350                     httpsConnector.setAttribute( "truststorePass", truststorePass );
351                 }
352                 httpsConnector.setAttribute( "truststoreType", truststoreType );
353 
354                 httpsConnector.setAttribute( "clientAuth", clientAuth );
355                 httpsConnector.setAttribute( "keyAlias", keyAlias );
356 
357                 tomcat.getService().addConnector( httpsConnector );
358 
359                 if ( httpPort <= 0 )
360                 {
361                     tomcat.setConnector( httpsConnector );
362                 }
363             }
364 
365             // create ajp connector
366             if ( ajpPort > 0 )
367             {
368                 Connector ajpConnector = new Connector( "org.apache.coyote.ajp.AjpProtocol" );
369                 ajpConnector.setPort( ajpPort );
370                 ajpConnector.setURIEncoding( uriEncoding );
371                 tomcat.getService().addConnector( ajpConnector );
372             }
373 
374             // add webapps
375             for ( Map.Entry<String, String> entry : this.webappWarPerContext.entrySet() )
376             {
377                 String baseDir = null;
378                 Context context = null;
379                 if ( entry.getKey().equals( "/" ) )
380                 {
381                     baseDir = new File( extractDirectory, "webapps/ROOT.war" ).getAbsolutePath();
382                     context = tomcat.addWebapp( "", baseDir );
383                 }
384                 else
385                 {
386                     baseDir = new File( extractDirectory, "webapps/" + entry.getValue() ).getAbsolutePath();
387                     context = tomcat.addWebapp( entry.getKey(), baseDir );
388                 }
389 
390                 URL contextFileUrl = getContextXml( baseDir );
391                 if ( contextFileUrl != null )
392                 {
393                     context.setConfigFile( contextFileUrl );
394                 }
395             }
396 
397             if ( codeSourceWar != null )
398             {
399                 String baseDir = new File( extractDirectory, "webapps/" + codeSourceWar.getName() ).getAbsolutePath();
400                 Context context = tomcat.addWebapp( codeSourceContextPath, baseDir );
401                 URL contextFileUrl = getContextXml( baseDir );
402                 if ( contextFileUrl != null )
403                 {
404                     context.setConfigFile( contextFileUrl );
405                 }
406             }
407 
408             tomcat.start();
409 
410             Runtime.getRuntime().addShutdownHook( new TomcatShutdownHook() );
411 
412         }
413 
414         waitIndefinitely();
415 
416     }
417 
418     protected class TomcatShutdownHook
419         extends Thread
420     {
421 
422         protected TomcatShutdownHook()
423         {
424             // no op
425         }
426 
427         @Override
428         public void run()
429         {
430             try
431             {
432                 Tomcat7Runner.this.stop();
433             }
434             catch ( Throwable ex )
435             {
436                 ExceptionUtils.handleThrowable( ex );
437                 System.out.println( "fail to properly shutdown Tomcat:" + ex.getMessage() );
438             }
439             finally
440             {
441                 // If JULI is used, shut JULI down *after* the server shuts down
442                 // so log messages aren't lost
443                 LogManager logManager = LogManager.getLogManager();
444                 if ( logManager instanceof ClassLoaderLogManager )
445                 {
446                     ( (ClassLoaderLogManager) logManager ).shutdown();
447                 }
448             }
449         }
450     }
451 
452     private URL getContextXml( String warPath )
453         throws IOException
454     {
455         InputStream inputStream = null;
456         try
457         {
458             String urlStr = "jar:file:" + warPath + "!/META-INF/context.xml";
459             debugMessage( "search context.xml in url:'" + urlStr + "'" );
460             URL url = new URL( urlStr );
461             inputStream = url.openConnection().getInputStream();
462             if ( inputStream != null )
463             {
464                 return url;
465             }
466         }
467         catch ( FileNotFoundException e )
468         {
469             return null;
470         }
471         finally
472         {
473             closeQuietly( inputStream );
474         }
475         return null;
476     }
477 
478     private static void closeQuietly( InputStream inputStream )
479     {
480         if ( inputStream == null )
481         {
482             return;
483         }
484         try
485         {
486             inputStream.close();
487         }
488         catch ( IOException e )
489         {
490             // ignore exception here
491         }
492     }
493 
494     private void waitIndefinitely()
495     {
496         Object lock = new Object();
497 
498         synchronized ( lock )
499         {
500             try
501             {
502                 lock.wait();
503             }
504             catch ( InterruptedException exception )
505             {
506                 throw new Error( "InterruptedException on wait Indefinitely lock:" + exception.getMessage(),
507                                  exception );
508             }
509         }
510     }
511 
512     public void stop()
513         throws Exception
514     {
515         if ( container != null )
516         {
517             container.stop();
518         }
519         if ( tomcat != null )
520         {
521             tomcat.stop();
522         }
523     }
524 
525     protected void extract()
526         throws Exception
527     {
528 
529         if ( extractDirectoryFile.exists() )
530         {
531             debugMessage( "delete extractDirectory:" + extractDirectoryFile.getAbsolutePath() );
532             FileUtils.deleteDirectory( extractDirectoryFile );
533         }
534 
535         if ( !this.extractDirectoryFile.exists() )
536         {
537             boolean created = this.extractDirectoryFile.mkdirs();
538             if ( !created )
539             {
540                 throw new Exception( "FATAL: impossible to create directory:" + this.extractDirectoryFile.getPath() );
541             }
542         }
543 
544         // ensure webapp dir is here
545         boolean created = new File( extractDirectory, "webapps" ).mkdirs();
546         if ( !created )
547         {
548             throw new Exception(
549                 "FATAL: impossible to create directory:" + this.extractDirectoryFile.getPath() + "/webapps" );
550 
551         }
552 
553         String wars = runtimeProperties.getProperty( WARS_KEY );
554         populateWebAppWarPerContext( wars );
555 
556         for ( Map.Entry<String, String> entry : webappWarPerContext.entrySet() )
557         {
558             debugMessage( "webappWarPerContext entry key/value: " + entry.getKey() + "/" + entry.getValue() );
559             InputStream inputStream = null;
560             try
561             {
562                 File expandFile = null;
563                 inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( entry.getValue() );
564                 if ( !useServerXml() )
565                 {
566                     if ( entry.getKey().equals( "/" ) )
567                     {
568                         expandFile = new File( extractDirectory, "webapps/ROOT.war" );
569                     }
570                     else
571                     {
572                         expandFile = new File( extractDirectory, "webapps/" + entry.getValue() );
573                     }
574                 }
575                 else
576                 {
577                     expandFile = new File( extractDirectory, "webapps/" + entry.getValue() );
578                 }
579 
580                 debugMessage( "expand to file:" + expandFile.getPath() );
581 
582                 // MTOMCAT-211 ensure parent directories created
583 
584                 if ( !expandFile.getParentFile().mkdirs() )
585                 {
586                     throw new Exception( "FATAL: impossible to create directories:" + expandFile.getParentFile() );
587                 }
588 
589                 expand( inputStream, expandFile );
590 
591             }
592             finally
593             {
594                 if ( inputStream != null )
595                 {
596                     inputStream.close();
597                 }
598             }
599         }
600 
601         //Copy code source to webapps folder
602         if ( codeSourceWar != null )
603         {
604             FileInputStream inputStream = null;
605             try
606             {
607                 File expandFile = new File( extractDirectory, "webapps/" + codeSourceContextPath + ".war" );
608                 inputStream = new FileInputStream( codeSourceWar );
609                 debugMessage( "move code source to file:" + expandFile.getPath() );
610                 expand( inputStream, expandFile );
611             }
612             finally
613             {
614                 if ( inputStream != null )
615                 {
616                     inputStream.close();
617                 }
618             }
619         }
620 
621         // expand tomcat configuration files if there
622         expandConfigurationFile( "catalina.properties", extractDirectoryFile );
623         expandConfigurationFile( "logging.properties", extractDirectoryFile );
624         expandConfigurationFile( "tomcat-users.xml", extractDirectoryFile );
625         expandConfigurationFile( "catalina.policy", extractDirectoryFile );
626         expandConfigurationFile( "context.xml", extractDirectoryFile );
627         expandConfigurationFile( "server.xml", extractDirectoryFile );
628         expandConfigurationFile( "web.xml", extractDirectoryFile );
629 
630     }
631 
632     private static void expandConfigurationFile( String fileName, File extractDirectory )
633         throws Exception
634     {
635         InputStream inputStream = null;
636         try
637         {
638             inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream( "conf/" + fileName );
639             if ( inputStream != null )
640             {
641                 File confDirectory = new File( extractDirectory, "conf" );
642                 if ( !confDirectory.exists() )
643                 {
644                     confDirectory.mkdirs();
645                 }
646                 expand( inputStream, new File( confDirectory, fileName ) );
647             }
648         }
649         finally
650         {
651             if ( inputStream != null )
652             {
653                 inputStream.close();
654             }
655         }
656 
657     }
658 
659     /**
660      * @param warsValue we can value in format: wars=foo.war|contextpath;bar.war  ( |contextpath is optionnal if empty use the war name)
661      *                  so here we return war file name and populate webappWarPerContext
662      */
663     private void populateWebAppWarPerContext( String warsValue )
664     {
665         if ( warsValue == null )
666         {
667             return;
668         }
669 
670         StringTokenizer st = new StringTokenizer( warsValue, ";" );
671         while ( st.hasMoreTokens() )
672         {
673             String warValue = st.nextToken();
674             debugMessage( "populateWebAppWarPerContext warValue:" + warValue );
675             String warFileName = "";
676             String contextValue = "";
677             int separatorIndex = warValue.indexOf( "|" );
678             if ( separatorIndex >= 0 )
679             {
680                 warFileName = warValue.substring( 0, separatorIndex );
681                 contextValue = warValue.substring( separatorIndex + 1, warValue.length() );
682 
683             }
684             else
685             {
686                 warFileName = contextValue;
687             }
688             debugMessage( "populateWebAppWarPerContext contextValue/warFileName:" + contextValue + "/" + warFileName );
689             this.webappWarPerContext.put( contextValue, warFileName );
690         }
691     }
692 
693 
694     /**
695      * Expand the specified input stream into the specified file.
696      *
697      * @param input InputStream to be copied
698      * @param file  The file to be created
699      * @throws java.io.IOException if an input/output error occurs
700      */
701     private static void expand( InputStream input, File file )
702         throws IOException
703     {
704         BufferedOutputStream output = null;
705         try
706         {
707             output = new BufferedOutputStream( new FileOutputStream( file ) );
708             byte buffer[] = new byte[2048];
709             while ( true )
710             {
711                 int n = input.read( buffer );
712                 if ( n <= 0 )
713                 {
714                     break;
715                 }
716                 output.write( buffer, 0, n );
717             }
718         }
719         finally
720         {
721             if ( output != null )
722             {
723                 try
724                 {
725                     output.close();
726                 }
727                 catch ( IOException e )
728                 {
729                     // Ignore
730                 }
731             }
732         }
733     }
734 
735     public boolean useServerXml()
736     {
737         return Boolean.parseBoolean( runtimeProperties.getProperty( USE_SERVER_XML_KEY, Boolean.FALSE.toString() ) );
738     }
739 
740 
741     public void debugMessage( String message )
742     {
743         if ( debug )
744         {
745             System.out.println( message );
746         }
747     }
748 
749 
750     public boolean enableNaming()
751     {
752         return Boolean.parseBoolean( runtimeProperties.getProperty( ENABLE_NAMING_KEY, Boolean.FALSE.toString() ) );
753     }
754 
755     private void installLogger( String loggerName )
756         throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException,
757         InvocationTargetException
758     {
759         if ( "slf4j".equals( loggerName ) )
760         {
761 
762             try
763             {
764                 // Check class is available
765 
766                 //final Class<?> clazz = Class.forName( "org.slf4j.bridge.SLF4JBridgeHandler" );
767                 final Class<?> clazz =
768                     Thread.currentThread().getContextClassLoader().loadClass( "org.slf4j.bridge.SLF4JBridgeHandler" );
769 
770                 // Remove all JUL handlers
771                 java.util.logging.LogManager.getLogManager().reset();
772 
773                 // Install slf4j bridge handler
774                 final Method method = clazz.getMethod( "install", null );
775                 method.invoke( null );
776             }
777             catch ( ClassNotFoundException e )
778             {
779                 System.out.println( "WARNING: issue configuring slf4j jul bridge, skip it" );
780             }
781         }
782         else
783         {
784             System.out.println( "WARNING: loggerName " + loggerName + " not supported, skip it" );
785         }
786     }
787 
788     private Properties loadProperties( File file )
789         throws FileNotFoundException, IOException
790     {
791         Properties properties = new Properties();
792         if ( file.exists() )
793         {
794 
795             FileInputStream fileInputStream = new FileInputStream( file );
796             try
797             {
798                 properties.load( fileInputStream );
799             }
800             finally
801             {
802                 fileInputStream.close();
803             }
804 
805         }
806         return properties;
807     }
808 
809     private void saveProperties( Properties properties, File file )
810         throws FileNotFoundException, IOException
811     {
812         FileOutputStream fileOutputStream = new FileOutputStream( file );
813         try
814         {
815             properties.store( fileOutputStream, "Timestamp file for executable war/jar" );
816         }
817         finally
818         {
819             fileOutputStream.close();
820         }
821     }
822 }