View Javadoc
1   package org.apache.fulcrum.yaafi.service.shutdown;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.security.MessageDigest;
26  import java.util.Arrays;
27  
28  import org.apache.avalon.framework.logger.Logger;
29  import org.apache.commons.io.IOUtils;
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  	/** the location to monitor for changes */
41  	private String location;
42  
43  	/** the last message digest of the location */
44  	private byte[] digest;
45  
46  	/** the locator to load the monitored resource */
47  	private InputStreamLocator locator;
48  
49  	/** keep a notice for the very first invocation */
50  	private boolean isFirstInvocation;
51  
52  	/** the logger to be used */
53  	private Logger logger;
54  
55  	/** use System.exit() to shutdown the JVM */
56  	private boolean useSystemExit;
57  
58  	/**
59  	 * Constructor
60  	 *
61  	 * @param logger         the logger to use
62  	 * @param applicationDir the home directory of the application
63  	 * @param location       the location to monitor for changes
64  	 * @param useSystemExit  use System.exit() on shutdown
65  	 */
66  	public ShutdownEntry(Logger logger, File applicationDir, String location, boolean useSystemExit) {
67  		this.isFirstInvocation = true;
68  		this.useSystemExit = useSystemExit;
69  		this.location = location;
70  		this.locator = new InputStreamLocator(applicationDir);
71  		this.logger = logger;
72  	}
73  
74  	/**
75  	 * @return has the monitored location changed
76  	 */
77  	public boolean hasChanged() {
78  		boolean result = false;
79  		InputStream is = null;
80  		byte[] currDigest = null;
81  
82  		try {
83  			// get a grip on our resource
84  
85  			is = this.locate();
86  
87  			if (is == null) {
88  				String msg = "Unable to find the following resource : " + this.getLocation();
89  				this.logger.warn(msg);
90  			} else {
91  				// calculate a SHA-1 digest
92  
93  				currDigest = this.getDigest(is);
94  				is.close();
95  				is = null;
96  
97  				if (this.isFirstInvocation() == true) {
98  					isFirstInvocation = false;
99  					this.logger.debug("Storing SHA-1 digest of " + this.getLocation());
100 					this.setDigest(currDigest);
101 				} else {
102 					if (equals(this.digest, currDigest) == false) {
103 						this.logger.debug("The following resource has changed : " + this.getLocation());
104 						this.setDigest(currDigest);
105 						result = true;
106 					}
107 				}
108 			}
109 
110 			return result;
111 		} catch (Exception e) {
112 			String msg = "The ShutdownService encountered an internal error";
113 			this.logger.error(msg, e);
114 			return false;
115 		} finally {
116 			if (is != null) {
117 				try {
118 					is.close();
119 				} catch (Exception e) {
120 					String msg = "Can't close the InputStream during error recovery";
121 					this.logger.error(msg, e);
122 				}
123 			}
124 		}
125 
126 	}
127 
128 	/**
129 	 * @return Returns the useSystemExit.
130 	 */
131 	public boolean isUseSystemExit() {
132 		return useSystemExit;
133 	}
134 
135 	/**
136 	 * @return Returns the isFirstInvocation.
137 	 */
138 	private boolean isFirstInvocation() {
139 		return isFirstInvocation;
140 	}
141 
142 	/**
143 	 * @return Returns the location.
144 	 */
145 	private String getLocation() {
146 		return location;
147 	}
148 
149 	/**
150 	 * Creates an InputStream
151 	 * 
152 	 * @return InputStream of the location
153 	 * @throws IOException if unable to open the stream
154 	 */
155 	public InputStream locate() throws IOException {
156 		return this.locator.locate(this.getLocation());
157 	}
158 
159 	/**
160 	 * Creates a message digest
161 	 * 
162 	 * @param is Input stream
163 	 * @return byte array of the input stream
164 	 */
165 	private byte[] getDigest(InputStream is) throws Exception {
166 		byte[] result = null;
167 		byte[] content = null;
168 
169 		// convert to byte array
170 		content = IOUtils.toByteArray(is);
171 
172 		MessageDigest sha1 = MessageDigest.getInstance("SHA1");
173 		sha1.update(content);
174 		result = sha1.digest();
175 
176 		return result;
177 	}
178 
179 	/**
180 	 * @param digest The digest to set.
181 	 */
182 	private void setDigest(byte[] digest) {
183 		this.digest = digest;
184 	}
185 
186 	/**
187 	 * Compares two byte[] for equality
188 	 */
189 	private static boolean equals(byte[] lhs, byte[] rhs) {
190 		// JDK provided method
191 		return Arrays.equals(lhs, rhs);
192 	}
193 
194 }