You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ObjectDirectory.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. /*
  2. * Copyright (C) 2009, Google Inc. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.internal.storage.file;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
  13. import static org.eclipse.jgit.internal.storage.pack.PackExt.BITMAP_INDEX;
  14. import static org.eclipse.jgit.internal.storage.pack.PackExt.INDEX;
  15. import java.io.BufferedReader;
  16. import java.io.File;
  17. import java.io.FileNotFoundException;
  18. import java.io.IOException;
  19. import java.nio.file.Files;
  20. import java.text.MessageFormat;
  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.Collections;
  24. import java.util.HashSet;
  25. import java.util.List;
  26. import java.util.Objects;
  27. import java.util.Set;
  28. import java.util.concurrent.atomic.AtomicReference;
  29. import org.eclipse.jgit.internal.JGitText;
  30. import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
  31. import org.eclipse.jgit.internal.storage.pack.PackExt;
  32. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  33. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  34. import org.eclipse.jgit.lib.AnyObjectId;
  35. import org.eclipse.jgit.lib.Config;
  36. import org.eclipse.jgit.lib.Constants;
  37. import org.eclipse.jgit.lib.ObjectDatabase;
  38. import org.eclipse.jgit.lib.ObjectId;
  39. import org.eclipse.jgit.lib.ObjectLoader;
  40. import org.eclipse.jgit.lib.RepositoryCache;
  41. import org.eclipse.jgit.lib.RepositoryCache.FileKey;
  42. import org.eclipse.jgit.util.FS;
  43. import org.eclipse.jgit.util.FileUtils;
  44. /**
  45. * Traditional file system based {@link org.eclipse.jgit.lib.ObjectDatabase}.
  46. * <p>
  47. * This is the classical object database representation for a Git repository,
  48. * where objects are stored loose by hashing them into directories by their
  49. * {@link org.eclipse.jgit.lib.ObjectId}, or are stored in compressed containers
  50. * known as {@link org.eclipse.jgit.internal.storage.file.Pack}s.
  51. * <p>
  52. * Optionally an object database can reference one or more alternates; other
  53. * ObjectDatabase instances that are searched in addition to the current
  54. * database.
  55. * <p>
  56. * Databases are divided into two halves: a half that is considered to be fast
  57. * to search (the {@code PackFile}s), and a half that is considered to be slow
  58. * to search (loose objects). When alternates are present the fast half is fully
  59. * searched (recursively through all alternates) before the slow half is
  60. * considered.
  61. */
  62. public class ObjectDirectory extends FileObjectDatabase {
  63. /** Maximum number of candidates offered as resolutions of abbreviation. */
  64. private static final int RESOLVE_ABBREV_LIMIT = 256;
  65. private final AlternateHandle handle = new AlternateHandle(this);
  66. private final Config config;
  67. private final File objects;
  68. private final File infoDirectory;
  69. private final LooseObjects loose;
  70. private final PackDirectory packed;
  71. private final PackDirectory preserved;
  72. private final File alternatesFile;
  73. private final FS fs;
  74. private final AtomicReference<AlternateHandle[]> alternates;
  75. private final File shallowFile;
  76. private FileSnapshot shallowFileSnapshot = FileSnapshot.DIRTY;
  77. private Set<ObjectId> shallowCommitsIds;
  78. /**
  79. * Initialize a reference to an on-disk object directory.
  80. *
  81. * @param cfg
  82. * configuration this directory consults for write settings.
  83. * @param dir
  84. * the location of the <code>objects</code> directory.
  85. * @param alternatePaths
  86. * a list of alternate object directories
  87. * @param fs
  88. * the file system abstraction which will be necessary to perform
  89. * certain file system operations.
  90. * @param shallowFile
  91. * file which contains IDs of shallow commits, null if shallow
  92. * commits handling should be turned off
  93. * @throws java.io.IOException
  94. * an alternate object cannot be opened.
  95. */
  96. public ObjectDirectory(final Config cfg, final File dir,
  97. File[] alternatePaths, FS fs, File shallowFile) throws IOException {
  98. config = cfg;
  99. objects = dir;
  100. infoDirectory = new File(objects, "info"); //$NON-NLS-1$
  101. File packDirectory = new File(objects, "pack"); //$NON-NLS-1$
  102. File preservedDirectory = new File(packDirectory, "preserved"); //$NON-NLS-1$
  103. alternatesFile = new File(objects, Constants.INFO_ALTERNATES);
  104. loose = new LooseObjects(objects);
  105. packed = new PackDirectory(config, packDirectory);
  106. preserved = new PackDirectory(config, preservedDirectory);
  107. this.fs = fs;
  108. this.shallowFile = shallowFile;
  109. alternates = new AtomicReference<>();
  110. if (alternatePaths != null) {
  111. AlternateHandle[] alt;
  112. alt = new AlternateHandle[alternatePaths.length];
  113. for (int i = 0; i < alternatePaths.length; i++)
  114. alt[i] = openAlternate(alternatePaths[i]);
  115. alternates.set(alt);
  116. }
  117. }
  118. /** {@inheritDoc} */
  119. @Override
  120. public final File getDirectory() {
  121. return loose.getDirectory();
  122. }
  123. /**
  124. * <p>Getter for the field <code>packDirectory</code>.</p>
  125. *
  126. * @return the location of the <code>pack</code> directory.
  127. */
  128. public final File getPackDirectory() {
  129. return packed.getDirectory();
  130. }
  131. /**
  132. * <p>Getter for the field <code>preservedDirectory</code>.</p>
  133. *
  134. * @return the location of the <code>preserved</code> directory.
  135. */
  136. public final File getPreservedDirectory() {
  137. return preserved.getDirectory();
  138. }
  139. /** {@inheritDoc} */
  140. @Override
  141. public boolean exists() {
  142. return fs.exists(objects);
  143. }
  144. /** {@inheritDoc} */
  145. @Override
  146. public void create() throws IOException {
  147. loose.create();
  148. FileUtils.mkdir(infoDirectory);
  149. packed.create();
  150. }
  151. /** {@inheritDoc} */
  152. @Override
  153. public ObjectDirectoryInserter newInserter() {
  154. return new ObjectDirectoryInserter(this, config);
  155. }
  156. /**
  157. * Create a new inserter that inserts all objects as pack files, not loose
  158. * objects.
  159. *
  160. * @return new inserter.
  161. */
  162. public PackInserter newPackInserter() {
  163. return new PackInserter(this);
  164. }
  165. /** {@inheritDoc} */
  166. @Override
  167. public void close() {
  168. loose.close();
  169. packed.close();
  170. // Fully close all loaded alternates and clear the alternate list.
  171. AlternateHandle[] alt = alternates.get();
  172. if (alt != null && alternates.compareAndSet(alt, null)) {
  173. for(AlternateHandle od : alt)
  174. od.close();
  175. }
  176. }
  177. /** {@inheritDoc} */
  178. @Override
  179. public Collection<Pack> getPacks() {
  180. return packed.getPacks();
  181. }
  182. /**
  183. * {@inheritDoc}
  184. * <p>
  185. * Add a single existing pack to the list of available pack files.
  186. */
  187. @Override
  188. public Pack openPack(File pack) throws IOException {
  189. PackFile pf;
  190. try {
  191. pf = new PackFile(pack);
  192. } catch (IllegalArgumentException e) {
  193. throw new IOException(
  194. MessageFormat.format(JGitText.get().notAValidPack, pack),
  195. e);
  196. }
  197. String p = pf.getName();
  198. // TODO(nasserg): See if PackFile can do these checks instead
  199. if (p.length() != 50 || !p.startsWith("pack-") //$NON-NLS-1$
  200. || !pf.getPackExt().equals(PACK)) {
  201. throw new IOException(
  202. MessageFormat.format(JGitText.get().notAValidPack, pack));
  203. }
  204. PackFile bitmapIdx = pf.create(BITMAP_INDEX);
  205. Pack res = new Pack(pack, bitmapIdx.exists() ? bitmapIdx : null);
  206. packed.insert(res);
  207. return res;
  208. }
  209. /** {@inheritDoc} */
  210. @Override
  211. public String toString() {
  212. return "ObjectDirectory[" + getDirectory() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
  213. }
  214. /** {@inheritDoc} */
  215. @Override
  216. public boolean has(AnyObjectId objectId) {
  217. return loose.hasCached(objectId)
  218. || hasPackedOrLooseInSelfOrAlternate(objectId)
  219. || (restoreFromSelfOrAlternate(objectId, null)
  220. && hasPackedOrLooseInSelfOrAlternate(objectId));
  221. }
  222. private boolean hasPackedOrLooseInSelfOrAlternate(AnyObjectId objectId) {
  223. return hasPackedInSelfOrAlternate(objectId, null)
  224. || hasLooseInSelfOrAlternate(objectId, null);
  225. }
  226. private boolean hasPackedInSelfOrAlternate(AnyObjectId objectId,
  227. Set<AlternateHandle.Id> skips) {
  228. if (hasPackedObject(objectId)) {
  229. return true;
  230. }
  231. skips = addMe(skips);
  232. for (AlternateHandle alt : myAlternates()) {
  233. if (!skips.contains(alt.getId())) {
  234. if (alt.db.hasPackedInSelfOrAlternate(objectId, skips)) {
  235. return true;
  236. }
  237. }
  238. }
  239. return false;
  240. }
  241. private boolean hasLooseInSelfOrAlternate(AnyObjectId objectId,
  242. Set<AlternateHandle.Id> skips) {
  243. if (loose.has(objectId)) {
  244. return true;
  245. }
  246. skips = addMe(skips);
  247. for (AlternateHandle alt : myAlternates()) {
  248. if (!skips.contains(alt.getId())) {
  249. if (alt.db.hasLooseInSelfOrAlternate(objectId, skips)) {
  250. return true;
  251. }
  252. }
  253. }
  254. return false;
  255. }
  256. boolean hasPackedObject(AnyObjectId objectId) {
  257. return packed.has(objectId);
  258. }
  259. @Override
  260. void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
  261. throws IOException {
  262. resolve(matches, id, null);
  263. }
  264. private void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
  265. Set<AlternateHandle.Id> skips)
  266. throws IOException {
  267. if (!packed.resolve(matches, id, RESOLVE_ABBREV_LIMIT))
  268. return;
  269. if (!loose.resolve(matches, id, RESOLVE_ABBREV_LIMIT))
  270. return;
  271. skips = addMe(skips);
  272. for (AlternateHandle alt : myAlternates()) {
  273. if (!skips.contains(alt.getId())) {
  274. alt.db.resolve(matches, id, skips);
  275. if (matches.size() > RESOLVE_ABBREV_LIMIT) {
  276. return;
  277. }
  278. }
  279. }
  280. }
  281. @Override
  282. ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId)
  283. throws IOException {
  284. ObjectLoader ldr = openObjectWithoutRestoring(curs, objectId);
  285. if (ldr == null && restoreFromSelfOrAlternate(objectId, null)) {
  286. ldr = openObjectWithoutRestoring(curs, objectId);
  287. }
  288. return ldr;
  289. }
  290. private ObjectLoader openObjectWithoutRestoring(WindowCursor curs, AnyObjectId objectId)
  291. throws IOException {
  292. if (loose.hasCached(objectId)) {
  293. ObjectLoader ldr = openLooseObject(curs, objectId);
  294. if (ldr != null) {
  295. return ldr;
  296. }
  297. }
  298. ObjectLoader ldr = openPackedFromSelfOrAlternate(curs, objectId, null);
  299. if (ldr != null) {
  300. return ldr;
  301. }
  302. return openLooseFromSelfOrAlternate(curs, objectId, null);
  303. }
  304. private ObjectLoader openPackedFromSelfOrAlternate(WindowCursor curs,
  305. AnyObjectId objectId, Set<AlternateHandle.Id> skips) {
  306. ObjectLoader ldr = openPackedObject(curs, objectId);
  307. if (ldr != null) {
  308. return ldr;
  309. }
  310. skips = addMe(skips);
  311. for (AlternateHandle alt : myAlternates()) {
  312. if (!skips.contains(alt.getId())) {
  313. ldr = alt.db.openPackedFromSelfOrAlternate(curs, objectId, skips);
  314. if (ldr != null) {
  315. return ldr;
  316. }
  317. }
  318. }
  319. return null;
  320. }
  321. private ObjectLoader openLooseFromSelfOrAlternate(WindowCursor curs,
  322. AnyObjectId objectId, Set<AlternateHandle.Id> skips)
  323. throws IOException {
  324. ObjectLoader ldr = openLooseObject(curs, objectId);
  325. if (ldr != null) {
  326. return ldr;
  327. }
  328. skips = addMe(skips);
  329. for (AlternateHandle alt : myAlternates()) {
  330. if (!skips.contains(alt.getId())) {
  331. ldr = alt.db.openLooseFromSelfOrAlternate(curs, objectId, skips);
  332. if (ldr != null) {
  333. return ldr;
  334. }
  335. }
  336. }
  337. return null;
  338. }
  339. ObjectLoader openPackedObject(WindowCursor curs, AnyObjectId objectId) {
  340. return packed.open(curs, objectId);
  341. }
  342. @Override
  343. ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
  344. throws IOException {
  345. return loose.open(curs, id);
  346. }
  347. @Override
  348. long getObjectSize(WindowCursor curs, AnyObjectId id) throws IOException {
  349. long sz = getObjectSizeWithoutRestoring(curs, id);
  350. if (0 > sz && restoreFromSelfOrAlternate(id, null)) {
  351. sz = getObjectSizeWithoutRestoring(curs, id);
  352. }
  353. return sz;
  354. }
  355. private long getObjectSizeWithoutRestoring(WindowCursor curs,
  356. AnyObjectId id) throws IOException {
  357. if (loose.hasCached(id)) {
  358. long len = loose.getSize(curs, id);
  359. if (0 <= len) {
  360. return len;
  361. }
  362. }
  363. long len = getPackedSizeFromSelfOrAlternate(curs, id, null);
  364. if (0 <= len) {
  365. return len;
  366. }
  367. return getLooseSizeFromSelfOrAlternate(curs, id, null);
  368. }
  369. private long getPackedSizeFromSelfOrAlternate(WindowCursor curs,
  370. AnyObjectId id, Set<AlternateHandle.Id> skips) {
  371. long len = packed.getSize(curs, id);
  372. if (0 <= len) {
  373. return len;
  374. }
  375. skips = addMe(skips);
  376. for (AlternateHandle alt : myAlternates()) {
  377. if (!skips.contains(alt.getId())) {
  378. len = alt.db.getPackedSizeFromSelfOrAlternate(curs, id, skips);
  379. if (0 <= len) {
  380. return len;
  381. }
  382. }
  383. }
  384. return -1;
  385. }
  386. private long getLooseSizeFromSelfOrAlternate(WindowCursor curs,
  387. AnyObjectId id, Set<AlternateHandle.Id> skips) throws IOException {
  388. long len = loose.getSize(curs, id);
  389. if (0 <= len) {
  390. return len;
  391. }
  392. skips = addMe(skips);
  393. for (AlternateHandle alt : myAlternates()) {
  394. if (!skips.contains(alt.getId())) {
  395. len = alt.db.getLooseSizeFromSelfOrAlternate(curs, id, skips);
  396. if (0 <= len) {
  397. return len;
  398. }
  399. }
  400. }
  401. return -1;
  402. }
  403. @Override
  404. void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
  405. WindowCursor curs) throws IOException {
  406. selectObjectRepresentation(packer, otp, curs, null);
  407. }
  408. private void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
  409. WindowCursor curs, Set<AlternateHandle.Id> skips) throws IOException {
  410. packed.selectRepresentation(packer, otp, curs);
  411. skips = addMe(skips);
  412. for (AlternateHandle h : myAlternates()) {
  413. if (!skips.contains(h.getId())) {
  414. h.db.selectObjectRepresentation(packer, otp, curs, skips);
  415. }
  416. }
  417. }
  418. private boolean restoreFromSelfOrAlternate(AnyObjectId objectId,
  419. Set<AlternateHandle.Id> skips) {
  420. if (restoreFromSelf(objectId)) {
  421. return true;
  422. }
  423. skips = addMe(skips);
  424. for (AlternateHandle alt : myAlternates()) {
  425. if (!skips.contains(alt.getId())) {
  426. if (alt.db.restoreFromSelfOrAlternate(objectId, skips)) {
  427. return true;
  428. }
  429. }
  430. }
  431. return false;
  432. }
  433. private boolean restoreFromSelf(AnyObjectId objectId) {
  434. Pack preservedPack = preserved.getPack(objectId);
  435. if (preservedPack == null) {
  436. return false;
  437. }
  438. PackFile preservedFile = new PackFile(preservedPack.getPackFile());
  439. // Restore the index last since the set will be considered for use once
  440. // the index appears.
  441. for (PackExt ext : PackExt.values()) {
  442. if (!INDEX.equals(ext)) {
  443. restore(preservedFile.create(ext));
  444. }
  445. }
  446. restore(preservedFile.create(INDEX));
  447. return true;
  448. }
  449. private boolean restore(PackFile preservedPack) {
  450. PackFile restored = preservedPack
  451. .createForDirectory(packed.getDirectory());
  452. try {
  453. Files.createLink(restored.toPath(), preservedPack.toPath());
  454. } catch (IOException e) {
  455. return false;
  456. }
  457. return true;
  458. }
  459. @Override
  460. InsertLooseObjectResult insertUnpackedObject(File tmp, ObjectId id,
  461. boolean createDuplicate) throws IOException {
  462. // If the object is already in the repository, remove temporary file.
  463. //
  464. if (loose.hasCached(id)) {
  465. FileUtils.delete(tmp, FileUtils.RETRY);
  466. return InsertLooseObjectResult.EXISTS_LOOSE;
  467. }
  468. if (!createDuplicate && has(id)) {
  469. FileUtils.delete(tmp, FileUtils.RETRY);
  470. return InsertLooseObjectResult.EXISTS_PACKED;
  471. }
  472. return loose.insert(tmp, id);
  473. }
  474. @Override
  475. Config getConfig() {
  476. return config;
  477. }
  478. @Override
  479. FS getFS() {
  480. return fs;
  481. }
  482. @Override
  483. Set<ObjectId> getShallowCommits() throws IOException {
  484. if (shallowFile == null || !shallowFile.isFile())
  485. return Collections.emptySet();
  486. if (shallowFileSnapshot == null
  487. || shallowFileSnapshot.isModified(shallowFile)) {
  488. shallowCommitsIds = new HashSet<>();
  489. try (BufferedReader reader = open(shallowFile)) {
  490. String line;
  491. while ((line = reader.readLine()) != null) {
  492. try {
  493. shallowCommitsIds.add(ObjectId.fromString(line));
  494. } catch (IllegalArgumentException ex) {
  495. throw new IOException(MessageFormat
  496. .format(JGitText.get().badShallowLine, line),
  497. ex);
  498. }
  499. }
  500. }
  501. shallowFileSnapshot = FileSnapshot.save(shallowFile);
  502. }
  503. return shallowCommitsIds;
  504. }
  505. void closeAllPackHandles(File packFile) {
  506. // if the packfile already exists (because we are rewriting a
  507. // packfile for the same set of objects maybe with different
  508. // PackConfig) then make sure we get rid of all handles on the file.
  509. // Windows will not allow for rename otherwise.
  510. if (packFile.exists()) {
  511. for (Pack p : packed.getPacks()) {
  512. if (packFile.getPath().equals(p.getPackFile().getPath())) {
  513. p.close();
  514. break;
  515. }
  516. }
  517. }
  518. }
  519. AlternateHandle[] myAlternates() {
  520. AlternateHandle[] alt = alternates.get();
  521. if (alt == null) {
  522. synchronized (alternates) {
  523. alt = alternates.get();
  524. if (alt == null) {
  525. try {
  526. alt = loadAlternates();
  527. } catch (IOException e) {
  528. alt = new AlternateHandle[0];
  529. }
  530. alternates.set(alt);
  531. }
  532. }
  533. }
  534. return alt;
  535. }
  536. Set<AlternateHandle.Id> addMe(Set<AlternateHandle.Id> skips) {
  537. if (skips == null) {
  538. skips = new HashSet<>();
  539. }
  540. skips.add(handle.getId());
  541. return skips;
  542. }
  543. private AlternateHandle[] loadAlternates() throws IOException {
  544. final List<AlternateHandle> l = new ArrayList<>(4);
  545. try (BufferedReader br = open(alternatesFile)) {
  546. String line;
  547. while ((line = br.readLine()) != null) {
  548. l.add(openAlternate(line));
  549. }
  550. }
  551. return l.toArray(new AlternateHandle[0]);
  552. }
  553. private static BufferedReader open(File f)
  554. throws IOException, FileNotFoundException {
  555. return Files.newBufferedReader(f.toPath(), UTF_8);
  556. }
  557. private AlternateHandle openAlternate(String location)
  558. throws IOException {
  559. final File objdir = fs.resolve(objects, location);
  560. return openAlternate(objdir);
  561. }
  562. private AlternateHandle openAlternate(File objdir) throws IOException {
  563. final File parent = objdir.getParentFile();
  564. if (FileKey.isGitRepository(parent, fs)) {
  565. FileKey key = FileKey.exact(parent, fs);
  566. FileRepository db = (FileRepository) RepositoryCache.open(key);
  567. return new AlternateRepository(db);
  568. }
  569. ObjectDirectory db = new ObjectDirectory(config, objdir, null, fs, null);
  570. return new AlternateHandle(db);
  571. }
  572. /**
  573. * Compute the location of a loose object file.
  574. */
  575. @Override
  576. public File fileFor(AnyObjectId objectId) {
  577. return loose.fileFor(objectId);
  578. }
  579. static class AlternateHandle {
  580. static class Id {
  581. String alternateId;
  582. public Id(File object) {
  583. try {
  584. this.alternateId = object.getCanonicalPath();
  585. } catch (Exception e) {
  586. alternateId = null;
  587. }
  588. }
  589. @Override
  590. public boolean equals(Object o) {
  591. if (o == this) {
  592. return true;
  593. }
  594. if (o == null || !(o instanceof Id)) {
  595. return false;
  596. }
  597. Id aId = (Id) o;
  598. return Objects.equals(alternateId, aId.alternateId);
  599. }
  600. @Override
  601. public int hashCode() {
  602. if (alternateId == null) {
  603. return 1;
  604. }
  605. return alternateId.hashCode();
  606. }
  607. }
  608. final ObjectDirectory db;
  609. AlternateHandle(ObjectDirectory db) {
  610. this.db = db;
  611. }
  612. void close() {
  613. db.close();
  614. }
  615. public Id getId(){
  616. return db.getAlternateId();
  617. }
  618. }
  619. static class AlternateRepository extends AlternateHandle {
  620. final FileRepository repository;
  621. AlternateRepository(FileRepository r) {
  622. super(r.getObjectDatabase());
  623. repository = r;
  624. }
  625. @Override
  626. void close() {
  627. repository.close();
  628. }
  629. }
  630. /** {@inheritDoc} */
  631. @Override
  632. public ObjectDatabase newCachedDatabase() {
  633. return newCachedFileObjectDatabase();
  634. }
  635. CachedObjectDirectory newCachedFileObjectDatabase() {
  636. return new CachedObjectDirectory(this);
  637. }
  638. AlternateHandle.Id getAlternateId() {
  639. return new AlternateHandle.Id(objects);
  640. }
  641. }