001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.io.delta;
016    
017    import java.io.IOException;
018    
019    import java.nio.ByteBuffer;
020    import java.nio.channels.FileChannel;
021    import java.nio.channels.WritableByteChannel;
022    
023    /**
024     * @author Connor McKay
025     */
026    public class Patcher {
027    
028            public void patch(
029                            FileChannel originalFileChannel,
030                            WritableByteChannel patchedWritableByteChannel,
031                            ByteChannelReader deltaByteChannelReader)
032                    throws IOException {
033    
034                    deltaByteChannelReader.resizeBuffer(5);
035    
036                    ByteBuffer deltaByteBuffer = deltaByteChannelReader.getBuffer();
037    
038                    deltaByteChannelReader.ensureData(5);
039    
040                    if (DeltaUtil.PROTOCOL_VERSION != deltaByteBuffer.get()) {
041                            throw new IOException("Unknown protocol version");
042                    }
043    
044                    int blockLength = deltaByteBuffer.getInt();
045    
046                    deltaByteChannelReader.resizeBuffer(
047                            blockLength * DeltaUtil.BUFFER_FACTOR + 5);
048    
049                    deltaByteBuffer = deltaByteChannelReader.getBuffer();
050    
051                    while (true) {
052                            deltaByteChannelReader.ensureData(1);
053    
054                            byte key = deltaByteBuffer.get();
055    
056                            if (key == DeltaUtil.REFERENCE_RANGE_KEY) {
057                                    deltaByteChannelReader.ensureData(9);
058    
059                                    int firstBlockNumber = deltaByteBuffer.getInt();
060                                    int lastBlockNumber = deltaByteBuffer.getInt();
061    
062                                    long position = firstBlockNumber * (long)blockLength;
063                                    long length =
064                                            (lastBlockNumber - firstBlockNumber + 1) *
065                                                    (long)blockLength;
066    
067                                    transfer(
068                                            originalFileChannel, patchedWritableByteChannel, position,
069                                            length);
070                            }
071                            else if (key == DeltaUtil.REFERENCE_KEY) {
072                                    deltaByteChannelReader.ensureData(4);
073    
074                                    int blockNumber = deltaByteBuffer.getInt();
075    
076                                    long position = blockNumber * (long)blockLength;
077    
078                                    transfer(
079                                            originalFileChannel, patchedWritableByteChannel, position,
080                                            blockLength);
081                            }
082                            else if (key == DeltaUtil.DATA_KEY) {
083                                    deltaByteChannelReader.ensureData(4);
084    
085                                    int length = deltaByteBuffer.getInt();
086    
087                                    deltaByteChannelReader.ensureData(length);
088    
089                                    int oldLimit = deltaByteBuffer.limit();
090    
091                                    deltaByteBuffer.limit(deltaByteBuffer.position() + length);
092    
093                                    patchedWritableByteChannel.write(deltaByteBuffer);
094    
095                                    deltaByteBuffer.limit(oldLimit);
096                            }
097                            else if (key == DeltaUtil.EOF_KEY) {
098                                    return;
099                            }
100                            else {
101                                    throw new IOException("Invalid key");
102                            }
103                    }
104            }
105    
106            protected void transfer(
107                            FileChannel source, WritableByteChannel destination, long position,
108                            long length)
109                    throws IOException {
110    
111                    if (length > _NATIVE_TRANSFER_THRESHOLD) {
112                            source.transferTo(position, length, destination);
113                    }
114                    else {
115                            _transferByteBuffer.clear();
116                            _transferByteBuffer.limit((int)length);
117    
118                            source.read(_transferByteBuffer, position);
119    
120                            _transferByteBuffer.flip();
121    
122                            destination.write(_transferByteBuffer);
123                    }
124            }
125    
126            private static final int _NATIVE_TRANSFER_THRESHOLD = 1000000;
127    
128            private ByteBuffer _transferByteBuffer = ByteBuffer.allocate(
129                    _NATIVE_TRANSFER_THRESHOLD);
130    
131    }