001 package org.apache.fulcrum.yaafi.service.reconfiguration;
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 ReconfigurationEntry
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 list of services to be reconfigured */
047 private String[] serviceList;
048
049 /** the last message digest of the location */
050 private byte[] digest;
051
052 /** the locator to load the monitored resource */
053 private InputStreamLocator locator;
054
055 /** keep a notice for the very first invocation */
056 private boolean isFirstInvocation;
057
058 /** the logger to be used */
059 private Logger logger;
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 serviceList the list of services to be reconfigured
068 */
069 public ReconfigurationEntry( Logger logger, File applicationDir, String location, String[] serviceList )
070 {
071 this.isFirstInvocation = true;
072 this.location = location;
073 this.locator = new InputStreamLocator( applicationDir );
074 this.logger = logger;
075 this.serviceList = serviceList;
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 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 }