001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/search/tags/sakai_2-4-1/search-impl/impl/src/java/org/sakaiproject/search/index/impl/SegmentState.java $
003: * $Id: SegmentState.java 29635 2007-04-26 14:44:09Z ajpoland@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006, 2007 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.search.index.impl;
021:
022: import java.io.BufferedReader;
023: import java.io.File;
024: import java.io.FileInputStream;
025: import java.io.FileReader;
026: import java.io.FileWriter;
027: import java.io.IOException;
028: import java.io.InputStream;
029: import java.security.MessageDigest;
030: import java.security.NoSuchAlgorithmException;
031: import java.util.Date;
032: import java.util.HashMap;
033: import java.util.Iterator;
034: import java.util.Map;
035:
036: import org.apache.commons.logging.Log;
037: import org.apache.commons.logging.LogFactory;
038: import org.sakaiproject.search.index.SegmentInfo;
039:
040: /**
041: * @author ieb
042: */
043:
044: public class SegmentState {
045:
046: private static final String VERSION = "1.0";
047:
048: private static final Log log = LogFactory
049: .getLog(SegmentState.class);
050:
051: private Map<String, FileRecord> fileRecords;
052:
053: private long timeStamp = System.currentTimeMillis();
054:
055: private String name;
056:
057: /**
058: * @param timestampFile
059: * @throws IOException
060: * @throws IOException
061: */
062: public SegmentState(SegmentInfo segInfo, File timestampFile)
063: throws IOException {
064: name = segInfo.getName();
065: if (timestampFile == null) {
066: analyze(segInfo);
067: } else {
068: load(timestampFile);
069: }
070: }
071:
072: /**
073: * @param timestampFile
074: * @throws IOException
075: */
076: public void save(File checksumFile) throws IOException {
077: File tmpFile = new File(checksumFile.getAbsolutePath() + ".tmp");
078: FileWriter fw = new FileWriter(tmpFile);
079: fw.append(VERSION).append("\n");
080: fw.append(String.valueOf(timeStamp)).append("\n");
081: for (Iterator<FileRecord> i = fileRecords.values().iterator(); i
082: .hasNext();) {
083: FileRecord fr = i.next();
084: fw.append(fr.path).append(";");
085: fw.append(fr.checksum).append(";");
086: fw.append(String.valueOf(fr.length)).append(";");
087: fw.append(String.valueOf(fr.lastMod)).append(";\n");
088: }
089: fw.close();
090: tmpFile.renameTo(checksumFile);
091: }
092:
093: /**
094: * @param timestampFile
095: * @throws IOException
096: */
097: private void load(File checksumFile) throws IOException {
098: fileRecords = new HashMap<String, FileRecord>();
099: BufferedReader fr = new BufferedReader(new FileReader(
100: checksumFile));
101: String version = fr.readLine();
102: if (VERSION.equals(version)) {
103: String ts = fr.readLine();
104: timeStamp = Long.parseLong(ts);
105: for (String line = fr.readLine(); line != null; line = fr
106: .readLine()) {
107: String[] elements = line.split(";");
108: FileRecord infr = new FileRecord();
109: infr.path = elements[0];
110: infr.checksum = elements[1];
111: infr.length = Long.parseLong(elements[2]);
112: infr.lastMod = Long.parseLong(elements[3]);
113: fileRecords.put(infr.path, infr);
114: }
115: } else {
116: log.warn("Unrecognized version number " + version);
117: }
118: fr.close();
119: }
120:
121: /**
122: * @param segInfo
123: */
124: public void analyze(SegmentInfo segInfo) {
125: File[] files = segInfo.getSegmentLocation().listFiles();
126: String basePath = segInfo.getSegmentLocation()
127: .getAbsolutePath();
128: fileRecords = new HashMap<String, FileRecord>();
129: MessageDigest md5 = null;
130: try {
131: md5 = MessageDigest.getInstance("MD5");
132: } catch (NoSuchAlgorithmException e) {
133: log.error("MD5 not available ", e);
134: }
135: byte[] buffer = new byte[4096];
136: if (files != null) {
137: for (int i = 0; i < files.length; i++) {
138: try {
139: String echecksum = "none";
140: if (md5 != null) {
141: InputStream fin = new FileInputStream(files[i]);
142: int len = 0;
143: md5.reset();
144: while ((len = fin.read(buffer)) > 0) {
145: md5.update(buffer, 0, len);
146: }
147: fin.close();
148: char[] encoding = "0123456789ABCDEF"
149: .toCharArray();
150: byte[] checksum = md5.digest();
151: char[] hexchecksum = new char[checksum.length * 2];
152: for (int j = 0; j < checksum.length; j++) {
153: int lo = checksum[j] & 0x0f;
154: int hi = (checksum[j] >> 4) & 0x0f;
155: hexchecksum[j * 2] = encoding[lo];
156: hexchecksum[j * 2 + 1] = encoding[hi];
157: }
158: echecksum = new String(hexchecksum);
159: }
160: FileRecord fr = new FileRecord();
161: fr.checksum = echecksum;
162: fr.path = files[i].getAbsolutePath().substring(
163: basePath.length());
164: fr.lastMod = files[i].lastModified();
165: fr.length = files[i].length();
166: fileRecords.put(fr.path, fr);
167: } catch (Exception ex) {
168: log.error("Failed to generate checksum of "
169: + files[i].getAbsolutePath(), ex);
170: }
171: }
172: }
173:
174: }
175:
176: public class FileRecord {
177:
178: public long length;
179:
180: public long lastMod;
181:
182: public String path;
183:
184: public String checksum = "none";
185:
186: /**
187: * @param sfr
188: * @return
189: */
190: public String diff(FileRecord sfr) {
191: StringBuilder sb = new StringBuilder();
192: if (sfr == null) {
193: return "new file";
194:
195: }
196: if (!path.equals(sfr.path)) {
197: return "[not the same file]";
198: }
199: int mod = 0;
200: if (!checksum.equals(sfr.checksum)) {
201: sb.append("content changed,");
202: mod++;
203: }
204: if (lastMod > sfr.lastMod) {
205: sb.append("newer;");
206: mod++;
207: } else if (lastMod < sfr.lastMod) {
208: sb.append("older;");
209: mod++;
210: } else {
211: sb.append("same age;");
212: }
213: if (length > sfr.length) {
214: sb.append("larger;");
215: mod++;
216: } else if (length < sfr.length) {
217: sb.append("smaller;");
218: mod++;
219: } else {
220: sb.append("same size;");
221: }
222: if (mod != 0) {
223: return sb.toString();
224: }
225: return "identical";
226: }
227:
228: /* (non-Javadoc)
229: * @see java.lang.Object#toString()
230: */
231: @Override
232: public String toString() {
233: return path + ";" + new Date(lastMod) + ";" + length + ";";
234: }
235:
236: }
237:
238: /**
239: * @return the timeStamp
240: */
241: public long getTimeStamp() {
242: return timeStamp;
243: }
244:
245: /**
246: * @param timeStamp
247: * the timeStamp to set
248: */
249: public void setTimeStamp(long timeStamp) {
250: this .timeStamp = timeStamp;
251: }
252:
253: /**
254: * Check the validity of this segment against the stored segment
255: *
256: * @param message
257: * @param logging
258: * @param storedSegmentState
259: */
260: public boolean checkValidity(boolean logging, String message,
261: SegmentState storedSegmentState) {
262: if (storedSegmentState == null) {
263: if (logging) {
264: log
265: .info(" The segment has no stored state, it may be new or it could be dammaged ");
266: }
267: return true;
268: }
269: StringBuilder sb = new StringBuilder();
270: if (timeStamp > storedSegmentState.getTimeStamp()) {
271: sb.append(" This Segment has been modified ").append(name)
272: .append("\n");
273: }
274: for (Iterator<FileRecord> i = fileRecords.values().iterator(); i
275: .hasNext();) {
276: FileRecord fr = i.next();
277: FileRecord sfr = storedSegmentState.getFileRecord(fr.path);
278: String differences = fr.diff(sfr);
279:
280: sb.append(" Checking [").append(fr).append("]==[")
281: .append(sfr).append("] ").append(differences)
282: .append("\n");
283: }
284: for (Iterator<FileRecord> i = storedSegmentState.iterator(); i
285: .hasNext();) {
286: FileRecord sfr = i.next();
287: FileRecord fr = fileRecords.get(sfr.path);
288: if (fr == null) {
289: sb.append(" Dropped ").append(fr).append("\n");
290: }
291: }
292: if (logging) {
293: log.info("Checked " + name + "\n" + sb.toString());
294: }
295: return true;
296: }
297:
298: /**
299: * @return
300: */
301: private Iterator<FileRecord> iterator() {
302: return fileRecords.values().iterator();
303: }
304:
305: /**
306: * @param path
307: * @return
308: */
309: private FileRecord getFileRecord(String path) {
310: return fileRecords.get(path);
311: }
312:
313: }
|