/**
 *
 * Copyright 2004 Hiram Chirino
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **/
package org.codehaus.activemq.journal.impl;

import org.codehaus.activemq.journal.InvalidRecordLocationException;
import org.codehaus.activemq.journal.Journal;
import org.codehaus.activemq.journal.JournalEventListener;
import org.codehaus.activemq.journal.RecordLocation;
import org.codehaus.activemq.util.LongSequenceGenerator;

import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;

/**
 * A high speed Journal implementation.  Inspired by the ideas of the
 * <a href="http://howl.objectweb.org/">Howl</a> project but tailored to the needs
 * of ActiveMQ.
 * <p/>
 * This Journal provides the following features:
 * <ul>
 * <li> Conncurrent writes are batched into a single write/force done by a background thread.</li>
 * <li> Uses preallocated logs to avoid disk fragmentation and performance degregation. </li>
 * <li> The number and size of the preallocated logs are configurable. </li>
 * <li> Uses direct ByteBuffers to write data to log files. </li>
 * <li> Allows logs to grow in case of an overflow condition so that overflow exceptions are not
 * not thrown.  Grown logs that are inactivated (due to a new mark) are resized to
 * thier original size.</li>
 * <li> No limit on the size of the record written to the journal</li>
 * <li> Should be possible to extend so that multiple physical disk are used concurrently to
 * increase throughput and decrease latency.</li>
 * </ul>
 * <p/>
 * This class is a thing wrapper around a LogFileManager.  It may be possible to extends this class so
 * that it manages multiple LogFileManager objects to allow logging to multiple physical disks.
 *
 * @version $Revision: 1.1 $
 */
public class JournalImpl implements Journal {

    LogFileManager manager;
    LongSequenceGenerator sequenceGenerator = new LongSequenceGenerator();

    public JournalImpl() throws IOException {
        this(new File("logs"));
    }

    public JournalImpl(File logDirectory) throws IOException {
        manager = new LogFileManager((byte) 0, sequenceGenerator, new LogFile(logDirectory));
        initSequenceId();
    }

    public JournalImpl(File logDirectory, int segments, int segmentSize) throws IOException {
        manager = new LogFileManager((byte) 0, sequenceGenerator, new LogFile(logDirectory, segments, segmentSize));
        initSequenceId();
    }


    private void initSequenceId() {
        long id = manager.getLastSequenceId();
        if (id == -1) {
            id = 0;
        }
        sequenceGenerator.setLastSequenceId(id);
    }

    /**
     * @throws IOException
     * @see org.codehaus.activemq.journal.Journal#write(byte[], boolean)
     */
    public RecordLocation write(byte[] data, boolean sync) throws IOException {
        return manager.write(data, sync);
    }

    /**
     * @see org.codehaus.activemq.journal.Journal#setMark(org.codehaus.activemq.journal.RecordLocation,
            *      boolean)
     */
    public void setMark(RecordLocation recordLocator, boolean force)
            throws InvalidRecordLocationException, IOException {
        RecordLocationImpl rl = (RecordLocationImpl) recordLocator;
        try {
            manager.setMark(rl, force);
        }
        catch (InterruptedException e) {
            throw new InterruptedIOException();
        }
    }

    /**
     * @see org.codehaus.activemq.journal.Journal#getMark()
     */
    public RecordLocation getMark() {
        return manager.getMark();
    }

    /**
     * @see org.codehaus.activemq.journal.Journal#close()
     */
    public void close() throws IOException {
        manager.close();
    }

    /**
     * @see org.codehaus.activemq.journal.Journal#setJournalEventListener(org.codehaus.activemq.journal.JournalEventListener)
     */
    public void setJournalEventListener(JournalEventListener eventListener) {
        manager.setJournalEventListener(eventListener);
    }

    /**
     * @throws InvalidRecordLocationException
     * @throws IOException
     * @see org.codehaus.activemq.journal.Journal#getNextRecordLocation(org.codehaus.activemq.journal.RecordLocation)
     */
    public RecordLocation getNextRecordLocation(RecordLocation lastLocation)
            throws IOException, InvalidRecordLocationException {

        RecordLocationImpl rl = (RecordLocationImpl) lastLocation;
        return manager.getNextRecordLocation(rl);
    }

    /**
     * @see org.codehaus.activemq.journal.Journal#getNextRecordLocation(org.codehaus.activemq.journal.RecordLocation)
     */
    public byte[] read(RecordLocation location)
            throws InvalidRecordLocationException, IOException {

        RecordLocationImpl rl = (RecordLocationImpl) location;
        return manager.read(rl);
    }

    /**
     * @see java.lang.Object#toString()
     */
    public String toString() {
        return "JournalImpl at '" + manager.getLogDirectory() + "' using " + manager.getTotalSegements() + " x " + ((float) manager.getInitialSegmentSize() / ((float) 1024 * 1024)) + " Meg log files.";
    }
}