aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java')
-rw-r--r--org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java129
1 files changed, 129 insertions, 0 deletions
diff --git a/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java
new file mode 100644
index 0000000000..009250294e
--- /dev/null
+++ b/org.eclipse.jgit.lfs/src/org/eclipse/jgit/lfs/internal/AtomicObjectOutputStream.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015, Matthias Sohn <matthias.sohn@sap.com> and others
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Distribution License v. 1.0 which is available at
+ * https://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+package org.eclipse.jgit.lfs.internal;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Path;
+import java.security.DigestOutputStream;
+import java.text.MessageFormat;
+
+import org.eclipse.jgit.annotations.Nullable;
+import org.eclipse.jgit.internal.storage.file.LockFile;
+import org.eclipse.jgit.lfs.errors.CorruptLongObjectException;
+import org.eclipse.jgit.lfs.lib.AnyLongObjectId;
+import org.eclipse.jgit.lfs.lib.Constants;
+import org.eclipse.jgit.lfs.lib.LongObjectId;
+
+/**
+ * Output stream writing content to a
+ * {@link org.eclipse.jgit.internal.storage.file.LockFile} which is committed on
+ * close(). The stream checks if the hash of the stream content matches the id.
+ */
+public class AtomicObjectOutputStream extends OutputStream {
+
+ private LockFile locked;
+
+ private DigestOutputStream out;
+
+ private boolean aborted;
+
+ private AnyLongObjectId id;
+
+ /**
+ * Constructor for AtomicObjectOutputStream.
+ *
+ * @param path
+ * a {@link java.nio.file.Path} object.
+ * @param id
+ * a {@link org.eclipse.jgit.lfs.lib.AnyLongObjectId} object.
+ * @throws java.io.IOException
+ * if an IO error occurred
+ */
+ public AtomicObjectOutputStream(Path path, AnyLongObjectId id)
+ throws IOException {
+ locked = new LockFile(path.toFile());
+ locked.lock();
+ this.id = id;
+ out = new DigestOutputStream(locked.getOutputStream(),
+ Constants.newMessageDigest());
+ }
+
+ /**
+ * Constructor for AtomicObjectOutputStream.
+ *
+ * @param path
+ * a {@link java.nio.file.Path} object.
+ * @throws java.io.IOException
+ * if an IO error occurred
+ */
+ public AtomicObjectOutputStream(Path path) throws IOException {
+ this(path, null);
+ }
+
+ /**
+ * Get the <code>id</code>.
+ *
+ * @return content hash of the object which was streamed through this
+ * stream. May return {@code null} if called before closing this
+ * stream.
+ */
+ @Nullable
+ public AnyLongObjectId getId() {
+ return id;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ out.write(b);
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ out.write(b);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ out.write(b, off, len);
+ }
+
+ @Override
+ public void close() throws IOException {
+ out.close();
+ if (!aborted) {
+ if (id != null) {
+ verifyHash();
+ } else {
+ id = LongObjectId.fromRaw(out.getMessageDigest().digest());
+ }
+ locked.commit();
+ }
+ }
+
+ private void verifyHash() {
+ AnyLongObjectId contentHash = LongObjectId
+ .fromRaw(out.getMessageDigest().digest());
+ if (!contentHash.equals(id)) {
+ abort();
+ throw new CorruptLongObjectException(id, contentHash,
+ MessageFormat.format(LfsText.get().corruptLongObject,
+ contentHash, id));
+ }
+ }
+
+ /**
+ * Aborts the stream. Temporary file will be deleted
+ */
+ public void abort() {
+ locked.unlock();
+ aborted = true;
+ }
+}