001 package org.apache.fulcrum.yaafi.service.shutdown;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.io.ByteArrayOutputStream;
023 import java.io.File;
024 import java.io.IOException;
025 import java.io.InputStream;
026 import java.io.OutputStream;
027 import java.security.MessageDigest;
028
029 import org.apache.avalon.framework.logger.Logger;
030 import org.apache.fulcrum.yaafi.framework.util.InputStreamLocator;
031
032 /**
033 * Monitors a resource and checks if it has changed
034 *
035 * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
036 */
037
038 public class ShutdownEntry
039 {
040 /** buffer size for copy() */
041 private static final int BUF_SIZE = 1024;
042
043 /** the location to monitor for changes */
044 private String location;
045
046 /** the last message digest of the location */
047 private byte[] digest;
048
049 /** the locator to load the monitored resource */
050 private InputStreamLocator locator;
051
052 /** keep a notice for the very first invocation */
053 private boolean isFirstInvocation;
054
055 /** the logger to be used */
056 private Logger logger;
057
058 /** use System.exit() to shutdown the JVM */
059 private boolean useSystemExit;
060
061 /**
062 * Constructor
063 *
064 * @param logger the logger to use
065 * @param applicationDir the home directory of the application
066 * @param location the location to monitor for changes
067 * @param useSystemExit use System.exit() on shutdown
068 */
069 public ShutdownEntry( Logger logger, File applicationDir, String location, boolean useSystemExit )
070 {
071 this.isFirstInvocation = true;
072 this.useSystemExit = useSystemExit;
073 this.location = location;
074 this.locator = new InputStreamLocator( applicationDir );
075 this.logger = logger;
076 }
077
078 /**
079 * @return has the monitored location changed
080 */
081 public boolean hasChanged()
082 {
083 boolean result = false;
084 InputStream is = null;
085 byte[] currDigest = null;
086
087 try
088 {
089 // get a grip on our resource
090
091 is = this.locate();
092
093 if( is == null )
094 {
095 String msg = "Unable to find the following resource : " + this.getLocation();
096 this.getLogger().warn(msg);
097 }
098 else
099 {
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 }