Package org.tmatesoft.svn.core.internal.io.fs

Source Code of org.tmatesoft.svn.core.internal.io.fs.FSOutputStream

/*
* ====================================================================
* Copyright (c) 2004-2009 TMate Software Ltd.  All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.  The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.io.fs;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.internal.delta.SVNDeltaCombiner;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.io.ISVNDeltaConsumer;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import org.tmatesoft.svn.util.SVNLogType;

/**
* @version 1.3
* @author  TMate Software Ltd.
*/
public class FSOutputStream extends OutputStream implements ISVNDeltaConsumer {

    public static final int SVN_DELTA_WINDOW_SIZE = 102400;
    public static final int WRITE_BUFFER_SIZE = 2*SVN_DELTA_WINDOW_SIZE;

    private boolean isHeaderWritten;
    private CountingOutputStream myTargetFile;
    private long myDeltaStart;
    private long myRepSize;
    private long myRepOffset;
    private InputStream mySourceStream;
    private SVNDeltaGenerator myDeltaGenerator;
    private FSRevisionNode myRevNode;
    private MessageDigest myMD5Digest;
    private MessageDigest mySHA1Digest;
    private FSTransactionRoot myTxnRoot;
    private long mySourceOffset;
    private ByteArrayOutputStream myTextBuffer;
    private boolean myIsClosed;
    private boolean myIsCompress;
    private FSWriteLock myTxnLock;

    private FSOutputStream(FSRevisionNode revNode, CountingOutputStream file, InputStream source, long deltaStart,
            long repSize, long repOffset, FSTransactionRoot txnRoot, boolean compress, FSWriteLock txnLock) throws SVNException {
        myTxnRoot = txnRoot;
        myTargetFile = file;
        mySourceStream = source;
        myDeltaStart = deltaStart;
        myRepSize = repSize;
        myRepOffset = repOffset;
        isHeaderWritten = false;
        myRevNode = revNode;
        mySourceOffset = 0;
        myIsClosed = false;
        myTxnLock = txnLock;
        myDeltaGenerator = new SVNDeltaGenerator(SVN_DELTA_WINDOW_SIZE);
        myTextBuffer = new ByteArrayOutputStream();

        try {
            myMD5Digest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException nsae) {
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "MD5 implementation not found: {0}", nsae.getLocalizedMessage());
            SVNErrorManager.error(err, nsae, SVNLogType.FSFS);
        }
        try {
            mySHA1Digest = MessageDigest.getInstance("SHA1");
        } catch (NoSuchAlgorithmException nsae) {
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, "SHA1 implementation not found: {0}", nsae.getLocalizedMessage());
            SVNErrorManager.error(err, nsae, SVNLogType.FSFS);
        }

        myIsCompress = compress;
    }

    private void reset(FSRevisionNode revNode, CountingOutputStream file, InputStream source, long deltaStart, long repSize, long repOffset, FSTransactionRoot txnRoot, FSWriteLock txnLock) {
        myTxnRoot = txnRoot;
        myTargetFile = file;
        mySourceStream = source;
        myDeltaStart = deltaStart;
        myRepSize = repSize;
        myRepOffset = repOffset;
        isHeaderWritten = false;
        myRevNode = revNode;
        mySourceOffset = 0;
        myIsClosed = false;
        myMD5Digest.reset();
        mySHA1Digest.reset();
        myTextBuffer.reset();
        myTxnLock = txnLock;
    }

    public static OutputStream createStream(FSRevisionNode revNode, FSTransactionRoot txnRoot, OutputStream dstStream, boolean compress) throws SVNException {
        if (revNode.getType() != SVNNodeKind.FILE) {
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FILE, "Attempted to set textual contents of a *non*-file node");
            SVNErrorManager.error(err, SVNLogType.FSFS);
        }

        if (!revNode.getId().isTxn()) {
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_MUTABLE, "Attempted to set textual contents of an immutable node");
            SVNErrorManager.error(err, SVNLogType.FSFS);
        }

        OutputStream targetOS = null;
        InputStream sourceStream = null;
        long offset = -1;
        long deltaStart = -1;
        FSWriteLock txnLock = null;
        try {
            txnLock = FSWriteLock.getWriteLockForTxn(txnRoot.getTxnID(), txnRoot.getOwner());
            txnLock.lock();
           
            File targetFile = txnRoot.getTransactionProtoRevFile();
            offset = targetFile.length();
            targetOS = SVNFileUtil.openFileForWriting(targetFile, true);
            CountingOutputStream revWriter = new CountingOutputStream(targetOS, offset);

            FSRepresentation baseRep = revNode.chooseDeltaBase(txnRoot.getOwner());
            sourceStream = FSInputStream.createDeltaStream(new SVNDeltaCombiner(), baseRep, txnRoot.getOwner());
            String header;

            if (baseRep != null) {
                header = FSRepresentation.REP_DELTA + " " + baseRep.getRevision() + " " + baseRep.getOffset() + " " + baseRep.getSize() + "\n";
            } else {
                header = FSRepresentation.REP_DELTA + "\n";
            }

            revWriter.write(header.getBytes("UTF-8"));
            deltaStart = revWriter.getPosition();

            if (dstStream instanceof FSOutputStream) {
                FSOutputStream fsOS = (FSOutputStream) dstStream;
                fsOS.reset(revNode, revWriter, sourceStream, deltaStart, 0, offset, txnRoot, txnLock);
                return dstStream;
            }

            return new FSOutputStream(revNode, revWriter, sourceStream, deltaStart, 0, offset, txnRoot,
                    compress, txnLock);

        } catch (IOException ioe) {
            SVNFileUtil.closeFile(targetOS);
            SVNFileUtil.closeFile(sourceStream);
            txnLock.unlock();
            FSWriteLock.release(txnLock);
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, ioe.getLocalizedMessage());
            SVNErrorManager.error(err, ioe, SVNLogType.FSFS);
        } catch (SVNException svne) {
            if (txnLock != null) {
                txnLock.unlock();
                FSWriteLock.release(txnLock);
            }
            SVNFileUtil.closeFile(targetOS);
            SVNFileUtil.closeFile(sourceStream);
            throw svne;
        }
        return null;
    }

    public void write(int b) throws IOException {
        write(new byte[] {
            (byte) (b & 0xFF)
        }, 0, 1);
    }

    public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
    }

    public void write(byte[] b, int off, int len) throws IOException {
        myMD5Digest.update(b, off, len);
        mySHA1Digest.update(b, off, len);
        myRepSize += len;
        int toWrite = 0;
        while (len > 0) {
            toWrite = len;
            myTextBuffer.write(b, off, toWrite);
            if (myTextBuffer.size() >= WRITE_BUFFER_SIZE) {
                try {
                    ByteArrayInputStream target = new ByteArrayInputStream(myTextBuffer.toByteArray());
                    myDeltaGenerator.sendDelta(null, mySourceStream, mySourceOffset, target, this, false);
                } catch (SVNException svne) {
                    throw new IOException(svne.getMessage());
                }
                myTextBuffer.reset();
            }
            off += toWrite;
            len -= toWrite;
        }
    }

    public void close() throws IOException {
        if (myIsClosed) {
            return;
        }
        myIsClosed = true;

        try {
            ByteArrayInputStream target = new ByteArrayInputStream(myTextBuffer.toByteArray());
            myDeltaGenerator.sendDelta(null, mySourceStream, mySourceOffset, target, this, false);

            FSRepresentation rep = new FSRepresentation();
            rep.setOffset(myRepOffset);

            long offset = myTargetFile.getPosition();

            rep.setSize(offset - myDeltaStart);
            rep.setExpandedSize(myRepSize);
            rep.setTxnId(myRevNode.getId().getTxnID());
            String uniqueSuffix = myTxnRoot.getNewTxnNodeId();
            String uniquifier = rep.getTxnId() + '/' + uniqueSuffix;
            rep.setUniquifier(uniquifier);
            rep.setRevision(SVNRepository.INVALID_REVISION);

            rep.setMD5HexDigest(SVNFileUtil.toHexDigest(myMD5Digest));
            rep.setSHA1HexDigest(SVNFileUtil.toHexDigest(mySHA1Digest));
           
            myTargetFile.write("ENDREP\n".getBytes("UTF-8"));
            myRevNode.setTextRepresentation(rep);
            myRevNode.setIsFreshTxnRoot(false);
            myTxnRoot.getOwner().putTxnRevisionNode(myRevNode.getId(), myRevNode);
        } catch (SVNException svne) {
            throw new IOException(svne.getMessage());
        } finally {
            closeStreams();
            try {
                myTxnLock.unlock();
            } catch (SVNException e) {
                //
            }
            FSWriteLock.release(myTxnLock);
        }
    }

    public void closeStreams() {
        SVNFileUtil.closeFile(myTargetFile);
        SVNFileUtil.closeFile(mySourceStream);
    }

    public FSRevisionNode getRevisionNode() {
        return myRevNode;
    }

    public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException {
        mySourceOffset += diffWindow.getInstructionsLength();
        try {
            diffWindow.writeTo(myTargetFile, !isHeaderWritten, myIsCompress);
            isHeaderWritten = true;
        } catch (IOException ioe) {
            SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, ioe.getLocalizedMessage());
            SVNErrorManager.error(err, ioe, SVNLogType.FSFS);
        }
        return SVNFileUtil.DUMMY_OUT;
    }

    public void textDeltaEnd(String path) throws SVNException {
    }

    public void applyTextDelta(String path, String baseChecksum) throws SVNException {
    }
}
TOP

Related Classes of org.tmatesoft.svn.core.internal.io.fs.FSOutputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.