001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.store.journal;
018
019import java.io.File;
020import java.io.IOException;
021import java.util.ArrayList;
022import java.util.HashSet;
023import java.util.Iterator;
024import java.util.Set;
025import java.util.concurrent.Callable;
026import java.util.concurrent.ConcurrentHashMap;
027import java.util.concurrent.CountDownLatch;
028import java.util.concurrent.FutureTask;
029import java.util.concurrent.LinkedBlockingQueue;
030import java.util.concurrent.ThreadFactory;
031import java.util.concurrent.ThreadPoolExecutor;
032import java.util.concurrent.TimeUnit;
033import java.util.concurrent.atomic.AtomicBoolean;
034
035import org.apache.activeio.journal.InvalidRecordLocationException;
036import org.apache.activeio.journal.Journal;
037import org.apache.activeio.journal.JournalEventListener;
038import org.apache.activeio.journal.RecordLocation;
039import org.apache.activeio.packet.ByteArrayPacket;
040import org.apache.activeio.packet.Packet;
041import org.apache.activemq.broker.BrokerService;
042import org.apache.activemq.broker.BrokerServiceAware;
043import org.apache.activemq.broker.ConnectionContext;
044import org.apache.activemq.broker.scheduler.JobSchedulerStore;
045import org.apache.activemq.command.ActiveMQDestination;
046import org.apache.activemq.command.ActiveMQQueue;
047import org.apache.activemq.command.ActiveMQTopic;
048import org.apache.activemq.command.DataStructure;
049import org.apache.activemq.command.JournalQueueAck;
050import org.apache.activemq.command.JournalTopicAck;
051import org.apache.activemq.command.JournalTrace;
052import org.apache.activemq.command.JournalTransaction;
053import org.apache.activemq.command.Message;
054import org.apache.activemq.command.MessageAck;
055import org.apache.activemq.command.ProducerId;
056import org.apache.activemq.filter.NonCachedMessageEvaluationContext;
057import org.apache.activemq.openwire.OpenWireFormat;
058import org.apache.activemq.store.MessageStore;
059import org.apache.activemq.store.PersistenceAdapter;
060import org.apache.activemq.store.TopicMessageStore;
061import org.apache.activemq.store.TransactionStore;
062import org.apache.activemq.store.jdbc.JDBCPersistenceAdapter;
063import org.apache.activemq.store.journal.JournalTransactionStore.Tx;
064import org.apache.activemq.store.journal.JournalTransactionStore.TxOperation;
065import org.apache.activemq.thread.Scheduler;
066import org.apache.activemq.thread.Task;
067import org.apache.activemq.thread.TaskRunner;
068import org.apache.activemq.thread.TaskRunnerFactory;
069import org.apache.activemq.usage.SystemUsage;
070import org.apache.activemq.usage.Usage;
071import org.apache.activemq.usage.UsageListener;
072import org.apache.activemq.util.ByteSequence;
073import org.apache.activemq.util.IOExceptionSupport;
074import org.apache.activemq.util.ThreadPoolUtils;
075import org.apache.activemq.wireformat.WireFormat;
076import org.slf4j.Logger;
077import org.slf4j.LoggerFactory;
078
079/**
080 * An implementation of {@link PersistenceAdapter} designed for use with a
081 * {@link Journal} and then check pointing asynchronously on a timeout with some
082 * other long term persistent storage.
083 *
084 * @org.apache.xbean.XBean
085 *
086 */
087public class JournalPersistenceAdapter implements PersistenceAdapter, JournalEventListener, UsageListener, BrokerServiceAware {
088
089    private BrokerService brokerService;
090
091    protected Scheduler scheduler;
092    private static final Logger LOG = LoggerFactory.getLogger(JournalPersistenceAdapter.class);
093
094    private Journal journal;
095    private PersistenceAdapter longTermPersistence;
096
097    private final WireFormat wireFormat = new OpenWireFormat();
098
099    private final ConcurrentHashMap<ActiveMQQueue, JournalMessageStore> queues = new ConcurrentHashMap<ActiveMQQueue, JournalMessageStore>();
100    private final ConcurrentHashMap<ActiveMQTopic, JournalTopicMessageStore> topics = new ConcurrentHashMap<ActiveMQTopic, JournalTopicMessageStore>();
101
102    private SystemUsage usageManager;
103    private long checkpointInterval = 1000 * 60 * 5;
104    private long lastCheckpointRequest = System.currentTimeMillis();
105    private long lastCleanup = System.currentTimeMillis();
106    private int maxCheckpointWorkers = 10;
107    private int maxCheckpointMessageAddSize = 1024 * 1024;
108
109    private final JournalTransactionStore transactionStore = new JournalTransactionStore(this);
110    private ThreadPoolExecutor checkpointExecutor;
111
112    private TaskRunner checkpointTask;
113    private CountDownLatch nextCheckpointCountDownLatch = new CountDownLatch(1);
114    private boolean fullCheckPoint;
115
116    private final AtomicBoolean started = new AtomicBoolean(false);
117
118    private final Runnable periodicCheckpointTask = createPeriodicCheckpointTask();
119
120    private TaskRunnerFactory taskRunnerFactory;
121    private File directory;
122
123    public JournalPersistenceAdapter() {
124    }
125
126    public JournalPersistenceAdapter(Journal journal, PersistenceAdapter longTermPersistence, TaskRunnerFactory taskRunnerFactory) throws IOException {
127        setJournal(journal);
128        setTaskRunnerFactory(taskRunnerFactory);
129        setPersistenceAdapter(longTermPersistence);
130    }
131
132    public void setTaskRunnerFactory(TaskRunnerFactory taskRunnerFactory) {
133        this.taskRunnerFactory = taskRunnerFactory;
134    }
135
136    public void setJournal(Journal journal) {
137        this.journal = journal;
138        journal.setJournalEventListener(this);
139    }
140
141    public void setPersistenceAdapter(PersistenceAdapter longTermPersistence) {
142        this.longTermPersistence = longTermPersistence;
143    }
144
145    final Runnable createPeriodicCheckpointTask() {
146        return new Runnable() {
147            @Override
148            public void run() {
149                long lastTime = 0;
150                synchronized (this) {
151                    lastTime = lastCheckpointRequest;
152                }
153                if (System.currentTimeMillis() > lastTime + checkpointInterval) {
154                    checkpoint(false, true);
155                }
156            }
157        };
158    }
159
160    /**
161     * @param usageManager The UsageManager that is controlling the
162     *                destination's memory usage.
163     */
164    @Override
165    public void setUsageManager(SystemUsage usageManager) {
166        this.usageManager = usageManager;
167        longTermPersistence.setUsageManager(usageManager);
168    }
169
170    @Override
171    public Set<ActiveMQDestination> getDestinations() {
172        Set<ActiveMQDestination> destinations = new HashSet<ActiveMQDestination>(longTermPersistence.getDestinations());
173        destinations.addAll(queues.keySet());
174        destinations.addAll(topics.keySet());
175        return destinations;
176    }
177
178    private MessageStore createMessageStore(ActiveMQDestination destination) throws IOException {
179        if (destination.isQueue()) {
180            return createQueueMessageStore((ActiveMQQueue)destination);
181        } else {
182            return createTopicMessageStore((ActiveMQTopic)destination);
183        }
184    }
185
186    @Override
187    public MessageStore createQueueMessageStore(ActiveMQQueue destination) throws IOException {
188        JournalMessageStore store = queues.get(destination);
189        if (store == null) {
190            MessageStore checkpointStore = longTermPersistence.createQueueMessageStore(destination);
191            store = new JournalMessageStore(this, checkpointStore, destination);
192            queues.put(destination, store);
193        }
194        return store;
195    }
196
197    @Override
198    public TopicMessageStore createTopicMessageStore(ActiveMQTopic destinationName) throws IOException {
199        JournalTopicMessageStore store = topics.get(destinationName);
200        if (store == null) {
201            TopicMessageStore checkpointStore = longTermPersistence.createTopicMessageStore(destinationName);
202            store = new JournalTopicMessageStore(this, checkpointStore, destinationName);
203            topics.put(destinationName, store);
204        }
205        return store;
206    }
207
208    /**
209     * Cleanup method to remove any state associated with the given destination
210     *
211     * @param destination Destination to forget
212     */
213    @Override
214    public void removeQueueMessageStore(ActiveMQQueue destination) {
215        queues.remove(destination);
216    }
217
218    /**
219     * Cleanup method to remove any state associated with the given destination
220     *
221     * @param destination Destination to forget
222     */
223    @Override
224    public void removeTopicMessageStore(ActiveMQTopic destination) {
225        topics.remove(destination);
226    }
227
228    @Override
229    public TransactionStore createTransactionStore() throws IOException {
230        return transactionStore;
231    }
232
233    @Override
234    public long getLastMessageBrokerSequenceId() throws IOException {
235        return longTermPersistence.getLastMessageBrokerSequenceId();
236    }
237
238    @Override
239    public void beginTransaction(ConnectionContext context) throws IOException {
240        longTermPersistence.beginTransaction(context);
241    }
242
243    @Override
244    public void commitTransaction(ConnectionContext context) throws IOException {
245        longTermPersistence.commitTransaction(context);
246    }
247
248    @Override
249    public void rollbackTransaction(ConnectionContext context) throws IOException {
250        longTermPersistence.rollbackTransaction(context);
251    }
252
253    @Override
254    public synchronized void start() throws Exception {
255        if (!started.compareAndSet(false, true)) {
256            return;
257        }
258
259        if( brokerService!=null ) {
260          wireFormat.setVersion(brokerService.getStoreOpenWireVersion());
261        }
262
263        checkpointTask = taskRunnerFactory.createTaskRunner(new Task() {
264            @Override
265            public boolean iterate() {
266                return doCheckpoint();
267            }
268        }, "ActiveMQ Journal Checkpoint Worker");
269
270        checkpointExecutor = new ThreadPoolExecutor(maxCheckpointWorkers, maxCheckpointWorkers, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() {
271            @Override
272            public Thread newThread(Runnable runable) {
273                Thread t = new Thread(runable, "Journal checkpoint worker");
274                t.setPriority(7);
275                return t;
276            }
277        });
278        // checkpointExecutor.allowCoreThreadTimeOut(true);
279
280        this.usageManager.getMemoryUsage().addUsageListener(this);
281
282        if (longTermPersistence instanceof JDBCPersistenceAdapter) {
283            // Disabled periodic clean up as it deadlocks with the checkpoint
284            // operations.
285            ((JDBCPersistenceAdapter)longTermPersistence).setCleanupPeriod(0);
286        }
287
288        longTermPersistence.start();
289        createTransactionStore();
290        recover();
291
292        // Do a checkpoint periodically.
293        this.scheduler = new Scheduler("Journal Scheduler");
294        this.scheduler.start();
295        this.scheduler.executePeriodically(periodicCheckpointTask, checkpointInterval / 10);
296
297    }
298
299    @Override
300    public void stop() throws Exception {
301
302        this.usageManager.getMemoryUsage().removeUsageListener(this);
303        if (!started.compareAndSet(true, false)) {
304            return;
305        }
306
307        this.scheduler.cancel(periodicCheckpointTask);
308        this.scheduler.stop();
309
310        // Take one final checkpoint and stop checkpoint processing.
311        checkpoint(true, true);
312        checkpointTask.shutdown();
313        ThreadPoolUtils.shutdown(checkpointExecutor);
314        checkpointExecutor = null;
315
316        queues.clear();
317        topics.clear();
318
319        IOException firstException = null;
320        try {
321            journal.close();
322        } catch (Exception e) {
323            firstException = IOExceptionSupport.create("Failed to close journals: " + e, e);
324        }
325        longTermPersistence.stop();
326
327        if (firstException != null) {
328            throw firstException;
329        }
330    }
331
332    // Properties
333    // -------------------------------------------------------------------------
334    public PersistenceAdapter getLongTermPersistence() {
335        return longTermPersistence;
336    }
337
338    /**
339     * @return Returns the wireFormat.
340     */
341    public WireFormat getWireFormat() {
342        return wireFormat;
343    }
344
345    // Implementation methods
346    // -------------------------------------------------------------------------
347
348    /**
349     * The Journal give us a call back so that we can move old data out of the
350     * journal. Taking a checkpoint does this for us.
351     *
352     * @see org.apache.activemq.journal.JournalEventListener#overflowNotification(org.apache.activemq.journal.RecordLocation)
353     */
354    @Override
355    public void overflowNotification(RecordLocation safeLocation) {
356        checkpoint(false, true);
357    }
358
359    /**
360     * When we checkpoint we move all the journalled data to long term storage.
361     *
362     */
363    public void checkpoint(boolean sync, boolean fullCheckpoint) {
364        try {
365            if (journal == null) {
366                throw new IllegalStateException("Journal is closed.");
367            }
368
369            long now = System.currentTimeMillis();
370            CountDownLatch latch = null;
371            synchronized (this) {
372                latch = nextCheckpointCountDownLatch;
373                lastCheckpointRequest = now;
374                if (fullCheckpoint) {
375                    this.fullCheckPoint = true;
376                }
377            }
378
379            checkpointTask.wakeup();
380
381            if (sync) {
382                LOG.debug("Waking for checkpoint to complete.");
383                latch.await();
384            }
385        } catch (InterruptedException e) {
386            Thread.currentThread().interrupt();
387            LOG.warn("Request to start checkpoint failed: " + e, e);
388        }
389    }
390
391    @Override
392    public void checkpoint(boolean sync) {
393        checkpoint(sync, sync);
394    }
395
396    /**
397     * This does the actual checkpoint.
398     *
399     * @return
400     */
401    public boolean doCheckpoint() {
402        CountDownLatch latch = null;
403        boolean fullCheckpoint;
404        synchronized (this) {
405            latch = nextCheckpointCountDownLatch;
406            nextCheckpointCountDownLatch = new CountDownLatch(1);
407            fullCheckpoint = this.fullCheckPoint;
408            this.fullCheckPoint = false;
409        }
410        try {
411
412            LOG.debug("Checkpoint started.");
413            RecordLocation newMark = null;
414
415            ArrayList<FutureTask<RecordLocation>> futureTasks = new ArrayList<FutureTask<RecordLocation>>(queues.size() + topics.size());
416
417            //
418            // We do many partial checkpoints (fullCheckpoint==false) to move
419            // topic messages
420            // to long term store as soon as possible.
421            //
422            // We want to avoid doing that for queue messages since removes the
423            // come in the same
424            // checkpoint cycle will nullify the previous message add.
425            // Therefore, we only
426            // checkpoint queues on the fullCheckpoint cycles.
427            //
428            if (fullCheckpoint) {
429                Iterator<JournalMessageStore> iterator = queues.values().iterator();
430                while (iterator.hasNext()) {
431                    try {
432                        final JournalMessageStore ms = iterator.next();
433                        FutureTask<RecordLocation> task = new FutureTask<RecordLocation>(new Callable<RecordLocation>() {
434                            @Override
435                            public RecordLocation call() throws Exception {
436                                return ms.checkpoint();
437                            }
438                        });
439                        futureTasks.add(task);
440                        checkpointExecutor.execute(task);
441                    } catch (Exception e) {
442                        LOG.error("Failed to checkpoint a message store: " + e, e);
443                    }
444                }
445            }
446
447            Iterator<JournalTopicMessageStore> iterator = topics.values().iterator();
448            while (iterator.hasNext()) {
449                try {
450                    final JournalTopicMessageStore ms = iterator.next();
451                    FutureTask<RecordLocation> task = new FutureTask<RecordLocation>(new Callable<RecordLocation>() {
452                        @Override
453                        public RecordLocation call() throws Exception {
454                            return ms.checkpoint();
455                        }
456                    });
457                    futureTasks.add(task);
458                    checkpointExecutor.execute(task);
459                } catch (Exception e) {
460                    LOG.error("Failed to checkpoint a message store: " + e, e);
461                }
462            }
463
464            try {
465                for (Iterator<FutureTask<RecordLocation>> iter = futureTasks.iterator(); iter.hasNext();) {
466                    FutureTask<RecordLocation> ft = iter.next();
467                    RecordLocation mark = ft.get();
468                    // We only set a newMark on full checkpoints.
469                    if (fullCheckpoint) {
470                        if (mark != null && (newMark == null || newMark.compareTo(mark) < 0)) {
471                            newMark = mark;
472                        }
473                    }
474                }
475            } catch (Throwable e) {
476                LOG.error("Failed to checkpoint a message store: " + e, e);
477            }
478
479            if (fullCheckpoint) {
480                try {
481                    if (newMark != null) {
482                        LOG.debug("Marking journal at: " + newMark);
483                        journal.setMark(newMark, true);
484                    }
485                } catch (Exception e) {
486                    LOG.error("Failed to mark the Journal: " + e, e);
487                }
488
489                if (longTermPersistence instanceof JDBCPersistenceAdapter) {
490                    // We may be check pointing more often than the
491                    // checkpointInterval if under high use
492                    // But we don't want to clean up the db that often.
493                    long now = System.currentTimeMillis();
494                    if (now > lastCleanup + checkpointInterval) {
495                        lastCleanup = now;
496                        ((JDBCPersistenceAdapter)longTermPersistence).cleanup();
497                    }
498                }
499            }
500
501            LOG.debug("Checkpoint done.");
502        } finally {
503            latch.countDown();
504        }
505        synchronized (this) {
506            return this.fullCheckPoint;
507        }
508
509    }
510
511    /**
512     * @param location
513     * @return
514     * @throws IOException
515     */
516    public DataStructure readCommand(RecordLocation location) throws IOException {
517        try {
518            Packet packet = journal.read(location);
519            return (DataStructure)wireFormat.unmarshal(toByteSequence(packet));
520        } catch (InvalidRecordLocationException e) {
521            throw createReadException(location, e);
522        } catch (IOException e) {
523            throw createReadException(location, e);
524        }
525    }
526
527    /**
528     * Move all the messages that were in the journal into long term storage. We
529     * just replay and do a checkpoint.
530     *
531     * @throws IOException
532     * @throws IOException
533     * @throws InvalidRecordLocationException
534     * @throws IllegalStateException
535     */
536    private void recover() throws IllegalStateException, InvalidRecordLocationException, IOException, IOException {
537
538        RecordLocation pos = null;
539        int transactionCounter = 0;
540
541        LOG.info("Journal Recovery Started from: " + journal);
542        ConnectionContext context = new ConnectionContext(new NonCachedMessageEvaluationContext());
543
544        // While we have records in the journal.
545        while ((pos = journal.getNextRecordLocation(pos)) != null) {
546            Packet data = journal.read(pos);
547            DataStructure c = (DataStructure)wireFormat.unmarshal(toByteSequence(data));
548
549            if (c instanceof Message) {
550                Message message = (Message)c;
551                JournalMessageStore store = (JournalMessageStore)createMessageStore(message.getDestination());
552                if (message.isInTransaction()) {
553                    transactionStore.addMessage(store, message, pos);
554                } else {
555                    store.replayAddMessage(context, message);
556                    transactionCounter++;
557                }
558            } else {
559                switch (c.getDataStructureType()) {
560                case JournalQueueAck.DATA_STRUCTURE_TYPE: {
561                    JournalQueueAck command = (JournalQueueAck)c;
562                    JournalMessageStore store = (JournalMessageStore)createMessageStore(command.getDestination());
563                    if (command.getMessageAck().isInTransaction()) {
564                        transactionStore.removeMessage(store, command.getMessageAck(), pos);
565                    } else {
566                        store.replayRemoveMessage(context, command.getMessageAck());
567                        transactionCounter++;
568                    }
569                }
570                    break;
571                case JournalTopicAck.DATA_STRUCTURE_TYPE: {
572                    JournalTopicAck command = (JournalTopicAck)c;
573                    JournalTopicMessageStore store = (JournalTopicMessageStore)createMessageStore(command.getDestination());
574                    if (command.getTransactionId() != null) {
575                        transactionStore.acknowledge(store, command, pos);
576                    } else {
577                        store.replayAcknowledge(context, command.getClientId(), command.getSubscritionName(), command.getMessageId());
578                        transactionCounter++;
579                    }
580                }
581                    break;
582                case JournalTransaction.DATA_STRUCTURE_TYPE: {
583                    JournalTransaction command = (JournalTransaction)c;
584                    try {
585                        // Try to replay the packet.
586                        switch (command.getType()) {
587                        case JournalTransaction.XA_PREPARE:
588                            transactionStore.replayPrepare(command.getTransactionId());
589                            break;
590                        case JournalTransaction.XA_COMMIT:
591                        case JournalTransaction.LOCAL_COMMIT:
592                            Tx tx = transactionStore.replayCommit(command.getTransactionId(), command.getWasPrepared());
593                            if (tx == null) {
594                                break; // We may be trying to replay a commit
595                            }
596                            // that
597                            // was already committed.
598
599                            // Replay the committed operations.
600                            tx.getOperations();
601                            for (Iterator iter = tx.getOperations().iterator(); iter.hasNext();) {
602                                TxOperation op = (TxOperation)iter.next();
603                                if (op.operationType == TxOperation.ADD_OPERATION_TYPE) {
604                                    op.store.replayAddMessage(context, (Message)op.data);
605                                }
606                                if (op.operationType == TxOperation.REMOVE_OPERATION_TYPE) {
607                                    op.store.replayRemoveMessage(context, (MessageAck)op.data);
608                                }
609                                if (op.operationType == TxOperation.ACK_OPERATION_TYPE) {
610                                    JournalTopicAck ack = (JournalTopicAck)op.data;
611                                    ((JournalTopicMessageStore)op.store).replayAcknowledge(context, ack.getClientId(), ack.getSubscritionName(), ack.getMessageId());
612                                }
613                            }
614                            transactionCounter++;
615                            break;
616                        case JournalTransaction.LOCAL_ROLLBACK:
617                        case JournalTransaction.XA_ROLLBACK:
618                            transactionStore.replayRollback(command.getTransactionId());
619                            break;
620                        default:
621                            throw new IOException("Invalid journal command type: " + command.getType());
622                        }
623                    } catch (IOException e) {
624                        LOG.error("Recovery Failure: Could not replay: " + c + ", reason: " + e, e);
625                    }
626                }
627                    break;
628                case JournalTrace.DATA_STRUCTURE_TYPE:
629                    JournalTrace trace = (JournalTrace)c;
630                    LOG.debug("TRACE Entry: " + trace.getMessage());
631                    break;
632                default:
633                    LOG.error("Unknown type of record in transaction log which will be discarded: " + c);
634                }
635            }
636        }
637
638        RecordLocation location = writeTraceMessage("RECOVERED", true);
639        journal.setMark(location, true);
640
641        LOG.info("Journal Recovered: " + transactionCounter + " message(s) in transactions recovered.");
642    }
643
644    private IOException createReadException(RecordLocation location, Exception e) {
645        return IOExceptionSupport.create("Failed to read to journal for: " + location + ". Reason: " + e, e);
646    }
647
648    protected IOException createWriteException(DataStructure packet, Exception e) {
649        return IOExceptionSupport.create("Failed to write to journal for: " + packet + ". Reason: " + e, e);
650    }
651
652    protected IOException createWriteException(String command, Exception e) {
653        return IOExceptionSupport.create("Failed to write to journal for command: " + command + ". Reason: " + e, e);
654    }
655
656    protected IOException createRecoveryFailedException(Exception e) {
657        return IOExceptionSupport.create("Failed to recover from journal. Reason: " + e, e);
658    }
659
660    /**
661     * @param command
662     * @param sync
663     * @return
664     * @throws IOException
665     */
666    public RecordLocation writeCommand(DataStructure command, boolean sync) throws IOException {
667        if (started.get()) {
668            try {
669                return journal.write(toPacket(wireFormat.marshal(command)), sync);
670            } catch (IOException ioe) {
671                LOG.error("Cannot write to the journal", ioe);
672                brokerService.handleIOException(ioe);
673                throw ioe;
674            }
675        }
676        throw new IOException("closed");
677    }
678
679    private RecordLocation writeTraceMessage(String message, boolean sync) throws IOException {
680        JournalTrace trace = new JournalTrace();
681        trace.setMessage(message);
682        return writeCommand(trace, sync);
683    }
684
685    @Override
686    public void onUsageChanged(Usage usage, int oldPercentUsage, int newPercentUsage) {
687        newPercentUsage = (newPercentUsage / 10) * 10;
688        oldPercentUsage = (oldPercentUsage / 10) * 10;
689        if (newPercentUsage >= 70 && oldPercentUsage < newPercentUsage) {
690            boolean sync = newPercentUsage >= 90;
691            checkpoint(sync, true);
692        }
693    }
694
695    public JournalTransactionStore getTransactionStore() {
696        return transactionStore;
697    }
698
699    @Override
700    public void deleteAllMessages() throws IOException {
701        try {
702            JournalTrace trace = new JournalTrace();
703            trace.setMessage("DELETED");
704            RecordLocation location = journal.write(toPacket(wireFormat.marshal(trace)), false);
705            journal.setMark(location, true);
706            LOG.info("Journal deleted: ");
707        } catch (IOException e) {
708            throw e;
709        } catch (Throwable e) {
710            throw IOExceptionSupport.create(e);
711        }
712        longTermPersistence.deleteAllMessages();
713    }
714
715    public SystemUsage getUsageManager() {
716        return usageManager;
717    }
718
719    public int getMaxCheckpointMessageAddSize() {
720        return maxCheckpointMessageAddSize;
721    }
722
723    public void setMaxCheckpointMessageAddSize(int maxCheckpointMessageAddSize) {
724        this.maxCheckpointMessageAddSize = maxCheckpointMessageAddSize;
725    }
726
727    public int getMaxCheckpointWorkers() {
728        return maxCheckpointWorkers;
729    }
730
731    public void setMaxCheckpointWorkers(int maxCheckpointWorkers) {
732        this.maxCheckpointWorkers = maxCheckpointWorkers;
733    }
734
735    public long getCheckpointInterval() {
736        return checkpointInterval;
737    }
738
739    public void setCheckpointInterval(long checkpointInterval) {
740        this.checkpointInterval = checkpointInterval;
741    }
742
743    public boolean isUseExternalMessageReferences() {
744        return false;
745    }
746
747    public void setUseExternalMessageReferences(boolean enable) {
748        if (enable) {
749            throw new IllegalArgumentException("The journal does not support message references.");
750        }
751    }
752
753    public Packet toPacket(ByteSequence sequence) {
754        return new ByteArrayPacket(new org.apache.activeio.packet.ByteSequence(sequence.data, sequence.offset, sequence.length));
755    }
756
757    public ByteSequence toByteSequence(Packet packet) {
758        org.apache.activeio.packet.ByteSequence sequence = packet.asByteSequence();
759        return new ByteSequence(sequence.getData(), sequence.getOffset(), sequence.getLength());
760    }
761
762    @Override
763    public void setBrokerName(String brokerName) {
764        longTermPersistence.setBrokerName(brokerName);
765    }
766
767    @Override
768    public String toString() {
769        return "JournalPersistenceAdapter(" + longTermPersistence + ")";
770    }
771
772    @Override
773    public void setDirectory(File dir) {
774        this.directory=dir;
775    }
776
777    @Override
778    public File getDirectory(){
779        return directory;
780    }
781
782    @Override
783    public long size(){
784        return 0;
785    }
786
787    @Override
788    public void setBrokerService(BrokerService brokerService) {
789        this.brokerService = brokerService;
790        PersistenceAdapter pa = getLongTermPersistence();
791        if( pa instanceof BrokerServiceAware ) {
792            ((BrokerServiceAware)pa).setBrokerService(brokerService);
793        }
794    }
795
796    @Override
797    public long getLastProducerSequenceId(ProducerId id) {
798        return -1;
799    }
800
801    @Override
802    public JobSchedulerStore createJobSchedulerStore() throws IOException, UnsupportedOperationException {
803        return longTermPersistence.createJobSchedulerStore();
804    }
805
806}