/* * 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.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.ActiveMQUnsupportedPacketException; import org.apache.activemq.artemis.core.io.SequentialFileFactory; import org.apache.activemq.artemis.core.journal.EncodingSupport; import org.apache.activemq.artemis.core.journal.IOCompletion; import org.apache.activemq.artemis.core.journal.Journal; import org.apache.activemq.artemis.core.journal.JournalLoadInformation; import org.apache.activemq.artemis.core.journal.LoaderCallback; import org.apache.activemq.artemis.core.persistence.Persister; import org.apache.activemq.artemis.core.journal.PreparedTransactionInfo; import org.apache.activemq.artemis.core.journal.RecordInfo; import org.apache.activemq.artemis.core.journal.TransactionFailureCallback; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecord; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalAddRecordTX; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalCompleteRecordTX; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalCompleteRecordTX.TX_RECORD_TYPE; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalDeleteRecord; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalDeleteRecordTX; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalInternalRecord; import org.apache.activemq.artemis.core.journal.impl.dataformat.JournalRollbackRecordTX; import org.apache.activemq.artemis.utils.collections.ConcurrentLongHashMap; /** * Journal used at a replicating backup server during the synchronization of data with the 'live' * server. It just wraps a single {@link JournalFile}. * * Its main purpose is to store the data as a Journal would, but without verifying records. */ public final class FileWrapperJournal extends JournalBase { private final ReentrantLock lockAppend = new ReentrantLock(); private final ConcurrentLongHashMap<AtomicInteger> transactions = new ConcurrentLongHashMap<>(); private final JournalImpl journal; protected volatile JournalFile currentFile; /** * @param journal * @throws Exception */ public FileWrapperJournal(Journal journal) throws Exception { super(journal.getFileFactory().isSupportsCallbacks(), journal.getFileSize()); this.journal = (JournalImpl) journal; currentFile = this.journal.setUpCurrentFile(JournalImpl.SIZE_HEADER); } @Override public void start() throws Exception { throw new UnsupportedOperationException(); } @Override public void stop() throws Exception { if (currentFile.getFile().isOpen()) currentFile.getFile().close(); } @Override public boolean isStarted() { throw new UnsupportedOperationException(); } // ------------------------ // ------------------------ @Override public void appendAddRecord(long id, byte recordType, Persister persister, Object record, boolean sync, IOCompletion callback) throws Exception { JournalInternalRecord addRecord = new JournalAddRecord(true, id, recordType, persister, record); writeRecord(addRecord, sync, callback); } @Override public void flush() throws Exception { } /** * Write the record to the current file. */ private void writeRecord(JournalInternalRecord encoder, final boolean sync, final IOCompletion callback) throws Exception { lockAppend.lock(); try { if (callback != null) { callback.storeLineUp(); } currentFile = journal.switchFileIfNecessary(encoder.getEncodeSize()); encoder.setFileID(currentFile.getRecordID()); if (callback != null) { currentFile.getFile().write(encoder, sync, callback); } else { currentFile.getFile().write(encoder, sync); } } finally { lockAppend.unlock(); } } @Override public void appendDeleteRecord(long id, boolean sync, IOCompletion callback) throws Exception { JournalInternalRecord deleteRecord = new JournalDeleteRecord(id); writeRecord(deleteRecord, sync, callback); } @Override public void appendDeleteRecordTransactional(long txID, long id, EncodingSupport record) throws Exception { count(txID); JournalInternalRecord deleteRecordTX = new JournalDeleteRecordTX(txID, id, record); writeRecord(deleteRecordTX, false, null); } @Override public void appendAddRecordTransactional(long txID, long id, byte recordType, Persister persister, Object record) throws Exception { count(txID); JournalInternalRecord addRecord = new JournalAddRecordTX(true, txID, id, recordType, persister, record); writeRecord(addRecord, false, null); } @Override public void appendUpdateRecord(long id, byte recordType, Persister persister, Object record, boolean sync, IOCompletion callback) throws Exception { JournalInternalRecord updateRecord = new JournalAddRecord(false, id, recordType, persister, record); writeRecord(updateRecord, sync, callback); } @Override public void appendUpdateRecordTransactional(long txID, long id, byte recordType, Persister persister, Object record) throws Exception { count(txID); JournalInternalRecord updateRecordTX = new JournalAddRecordTX(false, txID, id, recordType, persister, record); writeRecord(updateRecordTX, false, null); } @Override public void appendCommitRecord(long txID, boolean sync, IOCompletion callback, boolean lineUpContext) throws Exception { JournalInternalRecord commitRecord = new JournalCompleteRecordTX(TX_RECORD_TYPE.COMMIT, txID, null); AtomicInteger value = transactions.remove(txID); if (value != null) { commitRecord.setNumberOfRecords(value.get()); } writeRecord(commitRecord, true, callback); } @Override public void appendPrepareRecord(long txID, EncodingSupport transactionData, boolean sync, IOCompletion callback) throws Exception { JournalInternalRecord prepareRecord = new JournalCompleteRecordTX(TX_RECORD_TYPE.PREPARE, txID, transactionData); AtomicInteger value = transactions.get(txID); if (value != null) { prepareRecord.setNumberOfRecords(value.get()); } writeRecord(prepareRecord, sync, callback); } private int count(long txID) throws ActiveMQException { AtomicInteger defaultValue = new AtomicInteger(1); AtomicInteger count = transactions.putIfAbsent(txID, defaultValue); if (count != null) { return count.incrementAndGet(); } return defaultValue.get(); } @Override public String toString() { return FileWrapperJournal.class.getName() + "(currentFile=[" + currentFile + "], hash=" + super.toString() + ")"; } @Override public void appendRollbackRecord(long txID, boolean sync, IOCompletion callback) throws Exception { JournalInternalRecord rollbackRecord = new JournalRollbackRecordTX(txID); AtomicInteger value = transactions.remove(txID); if (value != null) { rollbackRecord.setNumberOfRecords(value.get()); } writeRecord(rollbackRecord, sync, callback); } // UNSUPPORTED STUFF @Override public JournalLoadInformation load(LoaderCallback reloadManager) throws Exception { throw new ActiveMQUnsupportedPacketException(); } @Override public JournalLoadInformation loadInternalOnly() throws Exception { throw new ActiveMQUnsupportedPacketException(); } @Override public void lineUpContext(IOCompletion callback) { throw new UnsupportedOperationException(); } @Override public JournalLoadInformation load(List<RecordInfo> committedRecords, List<PreparedTransactionInfo> preparedTransactions, TransactionFailureCallback transactionFailure) throws Exception { throw new ActiveMQUnsupportedPacketException(); } @Override public int getAlignment() throws Exception { throw new ActiveMQUnsupportedPacketException(); } @Override public int getNumberOfRecords() { throw new UnsupportedOperationException(); } @Override public int getUserVersion() { throw new UnsupportedOperationException(); } @Override public void runDirectJournalBlast() throws Exception { throw new UnsupportedOperationException(); } @Override public JournalLoadInformation loadSyncOnly(JournalState state) throws Exception { throw new UnsupportedOperationException(); } @Override public Map<Long, JournalFile> createFilesForBackupSync(long[] fileIds) throws Exception { throw new UnsupportedOperationException(); } @Override public void synchronizationLock() { throw new UnsupportedOperationException(); } @Override public void synchronizationUnlock() { throw new UnsupportedOperationException(); } @Override public void forceMoveNextFile() { throw new UnsupportedOperationException(); } @Override public JournalFile[] getDataFiles() { throw new UnsupportedOperationException(); } @Override void scheduleReclaim() { // no-op } @Override public SequentialFileFactory getFileFactory() { throw new UnsupportedOperationException(); } @Override public void scheduleCompactAndBlock(int timeout) throws Exception { throw new UnsupportedOperationException(); } @Override public void replicationSyncPreserveOldFiles() { throw new UnsupportedOperationException(); } @Override public void replicationSyncFinished() { throw new UnsupportedOperationException(); } }