View Javadoc

1   package org.apache.fulcrum.yaafi.service.shutdown;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.security.MessageDigest;
28  
29  import org.apache.avalon.framework.logger.Logger;
30  import org.apache.fulcrum.yaafi.framework.util.InputStreamLocator;
31  
32  /**
33   * Monitors a resource and checks if it has changed
34   *
35   * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
36   */
37  
38  public class ShutdownEntry
39  {
40      /** buffer size for copy() */
41      private static final int BUF_SIZE = 1024;
42  
43      /** the location to monitor for changes */
44      private String location;
45  
46      /** the last message digest of the location */
47      private byte[] digest;
48  
49      /** the locator to load the monitored resource */
50      private InputStreamLocator locator;
51  
52      /** keep a notice for the very first invocation */
53      private boolean isFirstInvocation;
54  
55      /** the logger to be used */
56      private Logger logger;
57  
58      /** use System.exit() to shutdown the JVM */
59      private boolean useSystemExit;
60  
61      /**
62       * Constructor
63       *
64       * @param logger the logger to use
65       * @param applicationDir the home directory of the application
66       * @param location the location to monitor for changes
67       * @param useSystemExit use System.exit() on shutdown
68       */
69      public ShutdownEntry( Logger logger, File applicationDir, String location, boolean useSystemExit )
70      {
71          this.isFirstInvocation = true;
72          this.useSystemExit = useSystemExit;
73          this.location = location;
74          this.locator  = new InputStreamLocator( applicationDir );
75          this.logger = logger;
76      }
77  
78      /**
79       * @return has the monitored location changed
80       */
81      public boolean hasChanged()
82      {
83          boolean result = false;
84          InputStream is = null;
85          byte[] currDigest = null;
86  
87          try
88          {
89              // get a grip on our resource
90  
91              is = this.locate();
92  
93              if( is == null )
94              {
95                  String msg = "Unable to find the following resource : " + this.getLocation();
96                  this.getLogger().warn(msg);
97              }
98              else
99              {
100                 // calculate a SHA-1 digest
101 
102                 currDigest = this.getDigest(is);
103                 is.close();
104                 is = null;
105 
106                 if( this.isFirstInvocation() == true )
107                 {
108                     isFirstInvocation = false;
109                     this.getLogger().debug( "Storing SHA-1 digest of " + this.getLocation() );
110                     this.setDigest( currDigest );
111                 }
112                 else
113                 {
114                     if( equals( this.digest, currDigest ) == false )
115                     {
116                         this.getLogger().debug( "The following resource has changed : " + this.getLocation() );
117                         this.setDigest( currDigest );
118                         result = true;
119                     }
120                 }
121             }
122 
123             return result;
124         }
125         catch(Exception e)
126         {
127             String msg = "The ShutdownService encountered an internal error";
128             this.getLogger().error(msg,e);
129             return false;
130         }
131         finally
132         {
133             if( is != null )
134             {
135                 try
136                 {
137                     is.close();
138                 }
139                 catch (Exception e)
140                 {
141                     String msg = "Can't close the InputStream during error recovery";
142                     this.getLogger().error(msg,e);
143                 }
144             }
145         }
146 
147     }
148 
149     /**
150      * @return Returns the useSystemExit.
151      */
152     public boolean isUseSystemExit()
153     {
154         return useSystemExit;
155     }
156 
157     /**
158      * @return Returns the isFirstInvocation.
159      */
160     private boolean isFirstInvocation()
161     {
162         return isFirstInvocation;
163     }
164 
165     /**
166      * @return Returns the location.
167      */
168     private String getLocation()
169     {
170         return location;
171     }
172 
173     /**
174      * @return Returns the locator.
175      */
176     private InputStreamLocator getLocator()
177     {
178         return locator;
179     }
180 
181     /**
182      * Creates an InputStream
183      */
184     public InputStream locate() throws IOException
185     {
186         return this.getLocator().locate(this.getLocation());
187     }
188 
189     /**
190      * Creates a message digest
191      */
192     private byte[] getDigest( InputStream is )
193         throws Exception
194     {
195         byte[] result = null;
196         byte[] content = null;
197 
198         ByteArrayOutputStream baos = new ByteArrayOutputStream();
199         copy( is, baos );
200         content = baos.toByteArray();
201         baos.close();
202 
203         MessageDigest sha1 = MessageDigest.getInstance( "SHA1" );
204         sha1.update( content );
205         result = sha1.digest();
206 
207         return result;
208     }
209 
210     /**
211      * @param digest The digest to set.
212      */
213     private void setDigest(byte [] digest)
214     {
215         this.digest = digest;
216     }
217 
218     /**
219      * Compares two byte[] for equality
220      */
221     private static boolean equals(byte[] lhs, byte[] rhs)
222     {
223         if( lhs == rhs )
224         {
225             return true;
226         }
227         else if( lhs.length != rhs.length )
228         {
229             return false;
230         }
231         else
232         {
233             for( int i=0; i<lhs.length; i++ )
234             {
235                 if( lhs[i] != rhs[i] )
236                 {
237                     return false;
238                 }
239             }
240         }
241 
242         return true;
243     }
244 
245     /**
246      * Pumps the input stream to the output stream.
247      *
248      * @param is the source input stream
249      * @param os the target output stream
250      * @throws IOException the copying failed
251      */
252     private static void copy( InputStream is, OutputStream os )
253         throws IOException
254     {
255         byte[] buf = new byte[BUF_SIZE];
256         int n = 0;
257         int total = 0;
258 
259         while ((n = is.read(buf)) > 0)
260         {
261             os.write(buf, 0, n);
262             total += n;
263         }
264 
265         is.close();
266 
267         os.flush();
268         os.close();
269     }
270 
271     /**
272      * @return Returns the logger.
273      */
274     private Logger getLogger()
275     {
276         return logger;
277     }
278 }