/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.activemq.artemis.core.journal.impl;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
import org.apache.activemq.artemis.api.core.ActiveMQBuffers;
import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.core.io.SequentialFile;
import org.apache.activemq.artemis.core.io.SequentialFileFactory;
import org.apache.activemq.artemis.core.journal.EncoderPersister;
import org.apache.activemq.artemis.core.journal.impl.dataformat.ByteArrayEncoding;
import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecord;
import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalInternalRecord;
import org.apache.activemq.artemis.utils.collections.ConcurrentLongHashSet;
/**
* Super class for Journal maintenances such as clean up and Compactor
*/
public abstract class AbstractJournalUpdateTask implements JournalReaderCallback {
// Constants -----------------------------------------------------
// Attributes ----------------------------------------------------
protected static final String FILE_COMPACT_CONTROL = "journal-rename-control.ctr";
protected final JournalImpl journal;
protected final SequentialFileFactory fileFactory;
protected JournalFile currentFile;
protected SequentialFile sequentialFile;
protected final JournalFilesRepository filesRepository;
protected long nextOrderingID;
private ActiveMQBuffer writingChannel;
private final ConcurrentLongHashSet recordsSnapshot;
protected final List<JournalFile> newDataFiles = new ArrayList<>();
// Static --------------------------------------------------------
// Constructors --------------------------------------------------
protected AbstractJournalUpdateTask(final SequentialFileFactory fileFactory,
final JournalImpl journal,
final JournalFilesRepository filesRepository,
final ConcurrentLongHashSet recordsSnapshot,
final long nextOrderingID) {
super();
this.journal = journal;
this.filesRepository = filesRepository;
this.fileFactory = fileFactory;
this.nextOrderingID = nextOrderingID;
this.recordsSnapshot = recordsSnapshot;
}
// Public --------------------------------------------------------
public static SequentialFile writeControlFile(final SequentialFileFactory fileFactory,
final List<JournalFile> files,
final List<JournalFile> newFiles,
final List<Pair<String, String>> renames) throws Exception {
SequentialFile controlFile = fileFactory.createSequentialFile(AbstractJournalUpdateTask.FILE_COMPACT_CONTROL);
try {
controlFile.open(1, false);
JournalImpl.initFileHeader(fileFactory, controlFile, 0, 0);
ActiveMQBuffer filesToRename = ActiveMQBuffers.dynamicBuffer(1);
// DataFiles first
if (files == null) {
filesToRename.writeInt(0);
} else {
filesToRename.writeInt(files.size());
for (JournalFile file : files) {
filesToRename.writeUTF(file.getFile().getFileName());
}
}
// New Files second
if (newFiles == null) {
filesToRename.writeInt(0);
} else {
filesToRename.writeInt(newFiles.size());
for (JournalFile file : newFiles) {
filesToRename.writeUTF(file.getFile().getFileName());
}
}
// Renames from clean up third
if (renames == null) {
filesToRename.writeInt(0);
} else {
filesToRename.writeInt(renames.size());
for (Pair<String, String> rename : renames) {
filesToRename.writeUTF(rename.getA());
filesToRename.writeUTF(rename.getB());
}
}
JournalInternalRecord controlRecord = new JournalAddRecord(true, 1, (byte) 0, EncoderPersister.getInstance(), new ByteArrayEncoding(filesToRename.toByteBuffer().array()));
ActiveMQBuffer renameBuffer = ActiveMQBuffers.dynamicBuffer(filesToRename.writerIndex());
controlRecord.setFileID(0);
controlRecord.encode(renameBuffer);
ByteBuffer writeBuffer = fileFactory.newBuffer(renameBuffer.writerIndex());
writeBuffer.put(renameBuffer.toByteBuffer().array(), 0, renameBuffer.writerIndex());
writeBuffer.rewind();
controlFile.writeDirect(writeBuffer, true);
return controlFile;
} finally {
controlFile.close();
}
}
/**
* Write pending output into file
*/
public void flush() throws Exception {
if (writingChannel != null) {
sequentialFile.position(0);
// To Fix the size of the file
writingChannel.writerIndex(writingChannel.capacity());
sequentialFile.writeDirect(writingChannel.toByteBuffer(), true);
sequentialFile.close();
newDataFiles.add(currentFile);
}
writingChannel = null;
}
public boolean lookupRecord(final long id) {
return recordsSnapshot.contains(id);
}
// Package protected ---------------------------------------------
// Protected -----------------------------------------------------
/**
* @throws Exception
*/
protected void openFile() throws Exception {
flush();
ByteBuffer bufferWrite = fileFactory.newBuffer(journal.getFileSize());
writingChannel = ActiveMQBuffers.wrappedBuffer(bufferWrite);
currentFile = filesRepository.takeFile(false, false, false, true);
sequentialFile = currentFile.getFile();
sequentialFile.open(1, false);
currentFile = new JournalFileImpl(sequentialFile, nextOrderingID++, JournalImpl.FORMAT_VERSION);
JournalImpl.writeHeader(writingChannel, journal.getUserVersion(), currentFile.getFileID());
}
protected void addToRecordsSnaptshot(final long id) {
recordsSnapshot.add(id);
}
/**
* @return the writingChannel
*/
protected ActiveMQBuffer getWritingChannel() {
return writingChannel;
}
protected void writeEncoder(final JournalInternalRecord record) throws Exception {
record.setFileID(currentFile.getRecordID());
record.encode(getWritingChannel());
}
protected void writeEncoder(final JournalInternalRecord record, final int txcounter) throws Exception {
record.setNumberOfRecords(txcounter);
writeEncoder(record);
}
// Private -------------------------------------------------------
// Inner classes -------------------------------------------------
}