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 }