1 package org.apache.fulcrum.yaafi.service.shutdown;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
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 }