1 11 package org.eclipse.core.internal.localstore; 12 13 import java.io.InputStream ; 14 import java.util.*; 15 import org.eclipse.core.filesystem.*; 16 import org.eclipse.core.internal.indexing.*; 17 import org.eclipse.core.internal.properties.IndexedStoreWrapper; 18 import org.eclipse.core.internal.resources.*; 19 import org.eclipse.core.internal.utils.*; 20 import org.eclipse.core.internal.utils.Convert; 21 import org.eclipse.core.resources.*; 22 import org.eclipse.core.runtime.*; 23 import org.eclipse.osgi.util.NLS; 24 25 public class HistoryStore implements IHistoryStore { 26 final static String INDEX_FILE = ".index"; protected BlobStore blobStore; 28 Set blobsToRemove = new HashSet(); 29 IndexedStoreWrapper store; 30 31 protected Workspace workspace; 32 33 public HistoryStore(Workspace workspace, IPath location, int limit) { 34 this.workspace = workspace; 35 this.blobStore = new BlobStore(EFS.getLocalFileSystem().getStore(location), limit); 36 this.store = new IndexedStoreWrapper(location.append(INDEX_FILE)); 37 } 38 39 50 protected void accept(byte[] key, IHistoryStoreVisitor visitor, boolean visitOnPartialMatch, boolean includeLastModTime) { 51 try { 52 IndexCursor cursor = store.getCursor(); 53 cursor.find(key); 54 while (cursor.keyMatches(key)) { 56 byte[] storedKey = cursor.getKey(); 57 58 int bytesToOmit = includeLastModTime ? ILocalStoreConstants.SIZE_COUNTER : ILocalStoreConstants.SIZE_KEY_SUFFIX; 59 if (storedKey.length - bytesToOmit == key.length) { 61 HistoryStoreEntry storedEntry = HistoryStoreEntry.create(store, cursor); 62 if (!visitor.visit(storedEntry)) 63 break; 64 cursor.next(); 65 continue; 66 } 67 68 if (!visitOnPartialMatch) { 70 cursor.next(); 71 continue; 72 } 73 74 byte b = storedKey[key.length]; 79 if (key[key.length - 1] == 47 || b == 47) { 80 HistoryStoreEntry storedEntry = HistoryStoreEntry.create(store, cursor); 81 if (!visitor.visit(storedEntry)) 82 break; 83 } 84 cursor.next(); 85 } 86 cursor.close(); 87 } catch (Exception e) { 88 String message = CompatibilityMessages.history_problemsAccessing; 89 Policy.log(new ResourceStatus(IResourceStatus.FAILED_READ_LOCAL, null, message, e)); 90 } 91 } 92 93 protected void accept(IPath path, IHistoryStoreVisitor visitor, boolean visitOnPartialMatch) { 94 accept(Convert.toUTF8(path.toString()), visitor, visitOnPartialMatch, false); 95 } 96 97 100 public IFileState addState(IPath key, IFileStore localFile, IFileInfo info, boolean moveContents) { 101 long lastModified = info.getLastModified(); 102 if (Policy.DEBUG_HISTORY) 103 System.out.println("History: Adding state for key: " + key + ", file: " + localFile + ", timestamp: " + lastModified + ", size: " + localFile.fetchInfo().getLength()); if (!isValid(localFile)) 105 return null; 106 UniversalUniqueIdentifier uuid = null; 107 try { 108 uuid = blobStore.addBlob(localFile, moveContents); 109 addState(key, uuid, lastModified); 110 store.commit(); 111 } catch (CoreException e) { 112 Policy.log(e); 113 } 114 return new FileState(this, key, lastModified, uuid); 115 } 116 117 124 protected void addState(IPath path, UniversalUniqueIdentifier uuid, long lastModified) { 125 class BitVisitor implements IHistoryStoreVisitor { 127 BitSet bits = new BitSet(); 128 129 public byte useNextClearBit(byte[] key) { 130 int nextBit = bits.length(); 135 if (nextBit > Byte.MAX_VALUE) { 141 if (bits.cardinality() < Byte.MAX_VALUE) { 142 try { 144 IndexCursor cursor = store.getCursor(); 145 byte destCount = (byte) bits.nextClearBit(0); 148 if (destCount < 0) 149 return (byte) -1; 151 byte sourceCount = (byte) bits.nextSetBit(destCount); 156 if (sourceCount < 0) 157 return destCount; 159 byte[] completeKey = new byte[key.length + 1]; 160 System.arraycopy(key, 0, completeKey, 0, key.length); 161 for (; sourceCount >= 0 && destCount >= 0; destCount++) { 162 completeKey[completeKey.length - 1] = sourceCount; 163 cursor.find(completeKey); 164 if (cursor.keyMatches(completeKey)) { 165 HistoryStoreEntry storedEntry = HistoryStoreEntry.create(store, cursor); 166 HistoryStoreEntry entryToInsert = new HistoryStoreEntry(storedEntry.getPath(), storedEntry.getUUID(), storedEntry.getLastModified(), destCount); 167 remove(storedEntry); 168 ObjectID valueID = store.createObject(entryToInsert.valueToBytes()); 169 store.getIndex().insert(entryToInsert.getKey(), valueID); 170 sourceCount = (byte) bits.nextSetBit(sourceCount + 1); 171 } 172 } 173 cursor.close(); 174 return destCount; 175 } catch (Exception e) { 176 String message = CompatibilityMessages.history_problemsAccessing; 177 Policy.log(new ResourceStatus(IResourceStatus.FAILED_READ_LOCAL, null, message, e)); 178 } 179 } else { 180 return (byte) -1; 182 } 183 } 184 return (byte) nextBit; 185 } 186 187 public boolean visit(HistoryStoreEntry entry) { 188 bits.set(entry.getCount()); 189 return true; 190 } 191 } 192 193 byte[] keyPrefix = HistoryStoreEntry.keyPrefixToBytes(path, lastModified); 195 BitVisitor visitor = new BitVisitor(); 196 accept(keyPrefix, visitor, false, true); 197 byte index = visitor.useNextClearBit(keyPrefix); 198 try { 199 if (index < 0) { 200 String message = NLS.bind(CompatibilityMessages.history_tooManySimUpdates, path, new Date(lastModified)); 201 Policy.log(new ResourceStatus(IResourceStatus.FAILED_WRITE_LOCAL, path, message, null)); 202 return; 203 } 204 HistoryStoreEntry entryToInsert = new HistoryStoreEntry(path, uuid, lastModified, index); 205 ObjectID valueID = store.createObject(entryToInsert.valueToBytes()); 207 store.getIndex().insert(entryToInsert.getKey(), valueID); 208 } catch (Exception e) { 209 resetIndexedStore(); 210 String message = NLS.bind(CompatibilityMessages.history_couldNotAdd, path); 211 Policy.log(new ResourceStatus(IResourceStatus.FAILED_WRITE_LOCAL, path, message, e)); 212 } 213 } 214 215 218 public Set allFiles(IPath path, final int depth, IProgressMonitor monitor) { 219 final Set allFiles = new HashSet(); 220 final int pathLength = path.segmentCount(); 221 class PathCollector implements IHistoryStoreVisitor { 222 public boolean visit(HistoryStoreEntry state) { 223 IPath memberPath = state.getPath(); 224 boolean withinDepthRange = false; 225 switch (depth) { 226 case IResource.DEPTH_ZERO : 227 withinDepthRange = memberPath.segmentCount() == pathLength; 228 break; 229 case IResource.DEPTH_ONE : 230 withinDepthRange = memberPath.segmentCount() <= pathLength + 1; 231 break; 232 case IResource.DEPTH_INFINITE : 233 withinDepthRange = true; 234 break; 235 } 236 if (withinDepthRange) { 237 allFiles.add(memberPath); 238 } 239 return withinDepthRange; 241 } 242 } 243 accept(path, new PathCollector(), true); 244 return allFiles; 245 } 246 247 250 public void clean(IProgressMonitor monitor) { 251 long start = System.currentTimeMillis(); 252 int entryCount = 0; 253 IWorkspaceDescription description = workspace.internalGetDescription(); 254 long minimumTimestamp = System.currentTimeMillis() - description.getFileStateLongevity(); 255 int max = description.getMaxFileStates(); 256 IPath current = null; 257 List result = new ArrayList(Math.min(max, 1000)); 258 try { 259 IndexCursor cursor = store.getCursor(); 260 cursor.findFirstEntry(); 261 while (cursor.isSet()) { 262 entryCount++; 263 HistoryStoreEntry entry = HistoryStoreEntry.create(store, cursor); 264 if (entry.getLastModified() < minimumTimestamp) { 266 remove(entry); 267 continue; 268 } 269 if (!entry.getPath().equals(current)) { 270 removeOldestEntries(result, max); 271 result.clear(); 272 current = entry.getPath(); 273 } 274 result.add(entry); 275 cursor.next(); 276 } 277 removeOldestEntries(result, max); 278 cursor.close(); 279 store.commit(); 280 if (Policy.DEBUG_HISTORY) { 281 Policy.debug("Time to apply history store policies: " + (System.currentTimeMillis() - start) + "ms."); Policy.debug("Total number of history store entries: " + entryCount); } 284 start = System.currentTimeMillis(); 285 blobStore.deleteBlobs(blobsToRemove); 287 if (Policy.DEBUG_HISTORY) 288 Policy.debug("Time to remove " + blobsToRemove.size() + " unreferenced blobs: " + (System.currentTimeMillis() - start) + "ms."); blobsToRemove = new HashSet(); 290 } catch (Exception e) { 291 String message = CompatibilityMessages.history_problemsCleaning; 292 Policy.log(new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, null, message, e)); 293 } 294 } 295 296 299 public void closeHistoryStore(IResource resource) { 300 } 302 303 307 public void copyHistory(final IResource sourceResource, final IResource destinationResource, boolean moving) { 308 313 if (sourceResource == null || destinationResource == null) { 316 String message = CompatibilityMessages.history_copyToNull; 317 Policy.log(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, null, message, null)); 318 return; 319 } 320 if (sourceResource.equals(destinationResource)) { 321 String message = CompatibilityMessages.history_copyToSelf; 322 Policy.log(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, sourceResource.getFullPath(), message, null)); 323 return; 324 } 325 326 final IPath source = sourceResource.getFullPath(); 327 final IPath destination = destinationResource.getFullPath(); 328 333 final Set matches = new HashSet(); 336 337 IHistoryStoreVisitor visitor = new IHistoryStoreVisitor() { 338 public boolean visit(HistoryStoreEntry entry) { 339 IPath path = entry.getPath(); 340 int prefixSegments = source.matchingFirstSegments(path); 341 if (prefixSegments == 0) { 344 String message = NLS.bind(CompatibilityMessages.history_interalPathErrors, source, path); 345 Policy.log(new ResourceStatus(IResourceStatus.INTERNAL_ERROR, source, message, null)); 346 return false; 347 } 348 path = destination.append(path.removeFirstSegments(prefixSegments)); 349 if (!stateAlreadyExists(path, entry.getUUID())) { 350 matches.add(path); 351 addState(path, entry.getUUID(), entry.getLastModified()); 352 } 353 return true; 354 } 355 }; 356 357 accept(source, visitor, true); 360 361 IWorkspaceDescription description = workspace.internalGetDescription(); 364 int maxFileStates = description.getMaxFileStates(); 365 try { 366 for (Iterator i = matches.iterator(); i.hasNext();) { 367 List removeEntries = new LinkedList(); 368 IndexCursor cursor = store.getCursor(); 369 IPath path = (IPath) i.next(); 370 byte key[] = Convert.toUTF8(path.toString()); 371 cursor.find(key); 372 while (cursor.keyMatches(key)) { 377 removeEntries.add(HistoryStoreEntry.create(store, cursor)); 378 cursor.next(); 379 } 380 cursor.close(); 381 removeOldestEntries(removeEntries, maxFileStates); 382 } 383 } catch (IndexedStoreException e) { 384 String message = NLS.bind(CompatibilityMessages.history_problemsPurging, source, destination); 385 ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_WRITE_METADATA, source, message, e); 386 Policy.log(status); 387 } catch (CoreException e) { 388 String message = NLS.bind(CompatibilityMessages.history_problemsPurging, source, destination); 389 ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_WRITE_METADATA, source, message, e); 390 Policy.log(status); 391 } 392 393 try { 396 store.commit(); 397 } catch (CoreException e) { 398 String message = NLS.bind(CompatibilityMessages.history_problemCopying, source, destination); 399 ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_WRITE_METADATA, source, message, e); 400 Policy.log(status); 401 } 402 } 403 404 407 public boolean exists(IFileState target) { 408 return blobStore.fileFor(((FileState) target).getUUID()).fetchInfo().exists(); 409 } 410 411 414 public InputStream getContents(IFileState target) throws CoreException { 415 if (!target.exists()) { 416 String message = CompatibilityMessages.history_notValid; 417 throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL, target.getFullPath(), message, null); 418 } 419 return blobStore.getBlob(((FileState) target).getUUID()); 420 } 421 422 425 public IFileState[] getStates(final IPath key, IProgressMonitor monitor) { 426 final int max = workspace.internalGetDescription().getMaxFileStates(); 427 final List result = new ArrayList(max); 428 IHistoryStoreVisitor visitor = new IHistoryStoreVisitor() { 429 public boolean visit(HistoryStoreEntry entry) { 430 result.add(new FileState(HistoryStore.this, key, entry.getLastModified(), entry.getUUID())); 431 return true; 432 } 433 }; 434 accept(key, visitor, false); 435 if (result.isEmpty()) 436 return ICoreConstants.EMPTY_FILE_STATES; 437 IFileState[] states = new IFileState[result.size()]; 439 for (int i = 0; i < states.length; i++) 440 states[i] = (IFileState) result.get(result.size() - (i + 1)); 441 return states; 442 } 443 444 453 private boolean isValid(IFileStore localFile) { 454 WorkspaceDescription description = workspace.internalGetDescription(); 455 long length = localFile.fetchInfo().getLength(); 456 boolean result = length <= description.getMaxFileStateSize(); 457 if (Policy.DEBUG_HISTORY && !result) 458 System.out.println("History: Ignoring file (too large). File: " + localFile.toString() + ", size: " + length + ", max: " + description.getMaxFileStateSize()); return result; 462 } 463 464 protected void remove(HistoryStoreEntry entry) throws IndexedStoreException { 465 try { 466 Vector objectIds = store.getIndex().getObjectIdentifiersMatching(entry.getKey()); 467 if (objectIds.size() == 1) { 468 store.removeObject((ObjectID) objectIds.get(0)); 469 } else if (objectIds.size() > 1) { 470 String message = NLS.bind(CompatibilityMessages.history_tooManySimUpdates, entry.getPath(), new Date(entry.getLastModified())); 473 ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, entry.getPath(), message, null); 474 Policy.log(status); 475 } 476 } catch (Exception e) { 477 String [] messageArgs = {entry.getPath().toString(), new Date(entry.getLastModified()).toString(), entry.getUUID().toString()}; 478 String message = NLS.bind(CompatibilityMessages.history_specificProblemsCleaning, messageArgs); 479 ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, null, message, e); 480 Policy.log(status); 481 } 482 blobsToRemove.add(entry.getUUID()); 485 entry.remove(); 486 } 487 488 491 public void remove(IPath path, IProgressMonitor monitor) { 492 if (Path.ROOT.equals(path)) { 493 removeAll(); 494 return; 495 } 496 try { 497 IndexCursor cursor = store.getCursor(); 498 byte[] key = Convert.toUTF8(path.toString()); 499 cursor.find(key); 500 while (cursor.keyMatches(key)) { 501 HistoryStoreEntry entry = HistoryStoreEntry.create(store, cursor); 502 remove(entry); 503 } 504 cursor.close(); 505 store.commit(); 506 } catch (Exception e) { 507 String message = NLS.bind(CompatibilityMessages.history_problemsRemoving, path); 508 ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, path, message, e); 509 Policy.log(status); 510 } 511 } 512 513 516 private void removeAll() { 517 try { 519 IndexCursor cursor = store.getCursor(); 520 cursor.findFirstEntry(); 521 while (cursor.isSet()) { 522 HistoryStoreEntry entry = HistoryStoreEntry.create(store, cursor); 523 remove(entry); 524 } 525 cursor.close(); 526 store.commit(); 527 } catch (Exception e) { 528 String message = NLS.bind(CompatibilityMessages.history_problemsRemoving, workspace.getRoot().getFullPath()); 529 ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, workspace.getRoot().getFullPath(), message, e); 530 Policy.log(status); 531 } 532 } 533 534 541 public void removeGarbage() { 542 try { 543 IndexCursor cursor = store.getCursor(); 544 cursor.findFirstEntry(); 545 while (!blobsToRemove.isEmpty() && cursor.isSet()) { 546 HistoryStoreEntry entry = HistoryStoreEntry.create(store, cursor); 547 blobsToRemove.remove(entry.getUUID()); 548 cursor.next(); 549 } 550 cursor.close(); 551 blobStore.deleteBlobs(blobsToRemove); 552 blobsToRemove = new HashSet(); 553 } catch (Exception e) { 554 String message = CompatibilityMessages.history_problemsCleaning; 555 ResourceStatus status = new ResourceStatus(IResourceStatus.FAILED_DELETE_LOCAL, null, message, e); 556 Policy.log(status); 557 } 558 } 559 560 protected void removeOldestEntries(List entries, int maxEntries) throws IndexedStoreException { 561 if (entries.size() <= maxEntries) 563 return; 564 int limit = entries.size() - maxEntries; 565 for (int i = 0; i < limit; i++) 566 remove((HistoryStoreEntry) entries.get(i)); 567 } 568 569 protected void resetIndexedStore() { 570 store.reset(); 571 java.io.File target = workspace.getMetaArea().getHistoryStoreLocation().toFile(); 572 Workspace.clear(target); 573 target.mkdirs(); 574 String message = CompatibilityMessages.history_corrupt; 575 ResourceStatus status = new ResourceStatus(IResourceStatus.INTERNAL_ERROR, null, message, null); 576 Policy.log(status); 577 } 578 579 582 public void shutdown(IProgressMonitor monitor) { 583 if (store == null) 584 return; 585 store.close(); 586 } 587 588 591 public void startup(IProgressMonitor monitor) { 592 } 594 595 boolean stateAlreadyExists(IPath path, final UniversalUniqueIdentifier uuid) { 596 final boolean[] rc = new boolean[] {false}; 597 IHistoryStoreVisitor visitor = new IHistoryStoreVisitor() { 598 public boolean visit(HistoryStoreEntry entry) { 599 if (rc[0] || uuid.equals(entry.getUUID())) { 600 rc[0] = true; 601 return false; 602 } 603 return true; 604 } 605 }; 606 accept(path, visitor, false); 607 return rc[0]; 608 } 609 } 610 | Popular Tags |