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 }