1 package org.apache.fulcrum.yaafi.service.reconfiguration;
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 ReconfigurationEntry
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 list of services to be reconfigured */
47 private String[] serviceList;
48
49 /** the last message digest of the location */
50 private byte[] digest;
51
52 /** the locator to load the monitored resource */
53 private InputStreamLocator locator;
54
55 /** keep a notice for the very first invocation */
56 private boolean isFirstInvocation;
57
58 /** the logger to be used */
59 private Logger logger;
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 serviceList the list of services to be reconfigured
68 */
69 public ReconfigurationEntry( Logger logger, File applicationDir, String location, String[] serviceList )
70 {
71 this.isFirstInvocation = true;
72 this.location = location;
73 this.locator = new InputStreamLocator( applicationDir );
74 this.logger = logger;
75 this.serviceList = serviceList;
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 serviceList.
151 */
152 public String [] getServiceList()
153 {
154 return serviceList;
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 * @return the input stream
184 * @throws IOException the creation failed
185 */
186 public InputStream locate() throws IOException
187 {
188 return this.getLocator().locate(this.getLocation());
189 }
190
191 /**
192 * Creates a message digest.
193 *
194 * @param is the input stream as input for the message digest
195 * @return the message digest
196 * @throws Exception the creation failed
197 */
198 private byte[] getDigest( InputStream is )
199 throws Exception
200 {
201 byte[] result = null;
202 byte[] content = null;
203
204 ByteArrayOutputStream baos = new ByteArrayOutputStream();
205 copy( is, baos );
206 content = baos.toByteArray();
207 baos.close();
208
209 MessageDigest sha1 = MessageDigest.getInstance( "SHA1" );
210 sha1.update( content );
211 result = sha1.digest();
212
213 return result;
214 }
215
216 /**
217 * @param digest The digest to set.
218 */
219 private void setDigest(byte [] digest)
220 {
221 this.digest = digest;
222 }
223
224 /**
225 * Compares two byte[] for equality
226 *
227 * @param lhs the left-hand side
228 * @param rhs the right-hand side
229 * @return true if the byte[] are equal
230 */
231 private static boolean equals(byte[] lhs, byte[] rhs)
232 {
233 if( lhs == rhs )
234 {
235 return true;
236 }
237 else if( lhs.length != rhs.length )
238 {
239 return false;
240 }
241 else
242 {
243 for( int i=0; i<lhs.length; i++ )
244 {
245 if( lhs[i] != rhs[i] )
246 {
247 return false;
248 }
249 }
250 }
251
252 return true;
253 }
254
255 /**
256 * Pumps the input stream to the output stream.
257 *
258 * @param is the source input stream
259 * @param os the target output stream
260 * @throws IOException the copying failed
261 */
262 private static void copy( InputStream is, OutputStream os )
263 throws IOException
264 {
265 byte[] buf = new byte[BUF_SIZE];
266 int n = 0;
267 int total = 0;
268
269 while ((n = is.read(buf)) > 0)
270 {
271 os.write(buf, 0, n);
272 total += n;
273 }
274
275 is.close();
276
277 os.flush();
278 os.close();
279 }
280
281 /**
282 * @return Returns the logger.
283 */
284 private Logger getLogger()
285 {
286 return logger;
287 }
288 }