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.broker.region.cursors;
018
019import org.apache.activemq.broker.Broker;
020import org.apache.activemq.broker.region.MessageReference;
021import org.apache.activemq.broker.region.Queue;
022import org.apache.activemq.command.Message;
023import org.apache.activemq.command.MessageId;
024import org.apache.activemq.usage.SystemUsage;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028/**
029 * Store based Cursor for Queues
030 */
031public class StoreQueueCursor extends AbstractPendingMessageCursor {
032
033    private static final Logger LOG = LoggerFactory.getLogger(StoreQueueCursor.class);
034    private final Broker broker;
035    private int pendingCount;
036    private final Queue queue;
037    private PendingMessageCursor nonPersistent;
038    private final QueueStorePrefetch persistent;
039    private PendingMessageCursor currentCursor;
040
041    /**
042     * Construct
043     * @param broker
044     * @param queue
045     */
046    public StoreQueueCursor(Broker broker,Queue queue) {
047        super((queue != null ? queue.isPrioritizedMessages():false));
048        this.broker=broker;
049        this.queue = queue;
050        this.persistent = new QueueStorePrefetch(queue, broker);
051        currentCursor = persistent;
052    }
053
054    @Override
055    public synchronized void start() throws Exception {
056        started = true;
057        super.start();
058        if (nonPersistent == null) {
059            if (broker.getBrokerService().isPersistent()) {
060                nonPersistent = new FilePendingMessageCursor(broker,queue.getName(),this.prioritizedMessages);
061            }else {
062                nonPersistent = new VMPendingMessageCursor(this.prioritizedMessages);
063            }
064            nonPersistent.setMaxBatchSize(getMaxBatchSize());
065            nonPersistent.setSystemUsage(systemUsage);
066            nonPersistent.setEnableAudit(isEnableAudit());
067            nonPersistent.setMaxAuditDepth(getMaxAuditDepth());
068            nonPersistent.setMaxProducersToAudit(getMaxProducersToAudit());
069        }
070        nonPersistent.setMessageAudit(getMessageAudit());
071        nonPersistent.start();
072        persistent.setMessageAudit(getMessageAudit());
073        persistent.start();
074        pendingCount = persistent.size() + nonPersistent.size();
075    }
076
077    @Override
078    public synchronized void stop() throws Exception {
079        started = false;
080        if (nonPersistent != null) {
081          nonPersistent.destroy();
082        }
083        persistent.stop();
084        persistent.gc();
085        super.stop();
086        pendingCount = 0;
087    }
088
089    @Override
090    public synchronized boolean tryAddMessageLast(MessageReference node, long maxWait) throws Exception {
091        boolean result = true;
092        if (node != null) {
093            Message msg = node.getMessage();
094            if (started) {
095                pendingCount++;
096                if (!msg.isPersistent()) {
097                    result = nonPersistent.tryAddMessageLast(node, maxWait);
098                }
099            }
100            if (msg.isPersistent()) {
101                result = persistent.addMessageLast(node);
102            }
103        }
104        return result;
105    }
106
107    @Override
108    public synchronized void addMessageFirst(MessageReference node) throws Exception {
109        if (node != null) {
110            Message msg = node.getMessage();
111            if (started) {
112                pendingCount++;
113                if (!msg.isPersistent()) {
114                    nonPersistent.addMessageFirst(node);
115                }
116            }
117            if (msg.isPersistent()) {
118                persistent.addMessageFirst(node);
119            }
120        }
121    }
122
123    @Override
124    public synchronized void clear() {
125        pendingCount = 0;
126    }
127
128    @Override
129    public synchronized boolean hasNext() {
130        try {
131            getNextCursor();
132        } catch (Exception e) {
133            LOG.error("Failed to get current cursor ", e);
134            throw new RuntimeException(e);
135       }
136       return currentCursor != null ? currentCursor.hasNext() : false;
137    }
138
139    @Override
140    public synchronized MessageReference next() {
141        MessageReference result = currentCursor != null ? currentCursor.next() : null;
142        return result;
143    }
144
145    @Override
146    public synchronized void remove() {
147        if (currentCursor != null) {
148            currentCursor.remove();
149        }
150        pendingCount--;
151    }
152
153    @Override
154    public synchronized void remove(MessageReference node) {
155        if (!node.isPersistent()) {
156            nonPersistent.remove(node);
157        } else {
158            persistent.remove(node);
159        }
160        pendingCount--;
161    }
162
163    @Override
164    public synchronized void reset() {
165        nonPersistent.reset();
166        persistent.reset();
167        pendingCount = persistent.size() + nonPersistent.size();
168    }
169
170    @Override
171    public void release() {
172        nonPersistent.release();
173        persistent.release();
174    }
175
176
177    @Override
178    public synchronized int size() {
179        if (pendingCount < 0) {
180            pendingCount = persistent.size() + nonPersistent.size();
181        }
182        return pendingCount;
183    }
184
185    @Override
186    public synchronized long messageSize() {
187        return persistent.messageSize() + nonPersistent.messageSize();
188    }
189
190    @Override
191    public synchronized boolean isEmpty() {
192        // if negative, more messages arrived in store since last reset so non empty
193        return pendingCount == 0;
194    }
195
196    /**
197     * Informs the Broker if the subscription needs to intervention to recover
198     * it's state e.g. DurableTopicSubscriber may do
199     *
200     * @see org.apache.activemq.broker.region.cursors.PendingMessageCursor
201     * @return true if recovery required
202     */
203    @Override
204    public boolean isRecoveryRequired() {
205        return false;
206    }
207
208    /**
209     * @return the nonPersistent Cursor
210     */
211    public PendingMessageCursor getNonPersistent() {
212        return this.nonPersistent;
213    }
214
215    /**
216     * @param nonPersistent cursor to set
217     */
218    public void setNonPersistent(PendingMessageCursor nonPersistent) {
219        this.nonPersistent = nonPersistent;
220    }
221
222    /**
223     * @return the persistent Cursor
224     */
225    public PendingMessageCursor getPersistent() { return  this.persistent; }
226
227    @Override
228    public void setMaxBatchSize(int maxBatchSize) {
229        persistent.setMaxBatchSize(maxBatchSize);
230        if (nonPersistent != null) {
231            nonPersistent.setMaxBatchSize(maxBatchSize);
232        }
233        super.setMaxBatchSize(maxBatchSize);
234    }
235
236
237    @Override
238    public void setMaxProducersToAudit(int maxProducersToAudit) {
239        super.setMaxProducersToAudit(maxProducersToAudit);
240        if (persistent != null) {
241            persistent.setMaxProducersToAudit(maxProducersToAudit);
242        }
243        if (nonPersistent != null) {
244            nonPersistent.setMaxProducersToAudit(maxProducersToAudit);
245        }
246    }
247
248    @Override
249    public void setMaxAuditDepth(int maxAuditDepth) {
250        super.setMaxAuditDepth(maxAuditDepth);
251        if (persistent != null) {
252            persistent.setMaxAuditDepth(maxAuditDepth);
253        }
254        if (nonPersistent != null) {
255            nonPersistent.setMaxAuditDepth(maxAuditDepth);
256        }
257    }
258
259    @Override
260    public void setEnableAudit(boolean enableAudit) {
261        super.setEnableAudit(enableAudit);
262        if (persistent != null) {
263            persistent.setEnableAudit(enableAudit);
264        }
265        if (nonPersistent != null) {
266            nonPersistent.setEnableAudit(enableAudit);
267        }
268    }
269
270    @Override
271    public void rollback(MessageId id) {
272        nonPersistent.rollback(id);
273        persistent.rollback(id);
274    }
275
276    @Override
277    public void setUseCache(boolean useCache) {
278        super.setUseCache(useCache);
279        if (persistent != null) {
280            persistent.setUseCache(useCache);
281        }
282        if (nonPersistent != null) {
283            nonPersistent.setUseCache(useCache);
284        }
285    }
286
287    @Override
288    public void setMemoryUsageHighWaterMark(int memoryUsageHighWaterMark) {
289        super.setMemoryUsageHighWaterMark(memoryUsageHighWaterMark);
290        if (persistent != null) {
291            persistent.setMemoryUsageHighWaterMark(memoryUsageHighWaterMark);
292        }
293        if (nonPersistent != null) {
294            nonPersistent.setMemoryUsageHighWaterMark(memoryUsageHighWaterMark);
295        }
296    }
297
298
299
300    @Override
301    public synchronized void gc() {
302        if (persistent != null) {
303            persistent.gc();
304        }
305        if (nonPersistent != null) {
306            nonPersistent.gc();
307        }
308        pendingCount = persistent.size() + nonPersistent.size();
309    }
310
311    @Override
312    public void setSystemUsage(SystemUsage usageManager) {
313        super.setSystemUsage(usageManager);
314        if (persistent != null) {
315            persistent.setSystemUsage(usageManager);
316        }
317        if (nonPersistent != null) {
318            nonPersistent.setSystemUsage(usageManager);
319        }
320    }
321
322    protected synchronized PendingMessageCursor getNextCursor() throws Exception {
323        if (currentCursor == null || !currentCursor.hasMessagesBufferedToDeliver()) {
324            currentCursor = currentCursor == persistent ? nonPersistent : persistent;
325            // sanity check
326            if (currentCursor.isEmpty()) {
327                currentCursor = currentCursor == persistent ? nonPersistent : persistent;
328            }
329        }
330        return currentCursor;
331    }
332
333    @Override
334    public boolean isCacheEnabled() {
335        boolean cacheEnabled = isUseCache();
336        if (cacheEnabled) {
337            if (persistent != null) {
338                cacheEnabled &= persistent.isCacheEnabled();
339            }
340            if (nonPersistent != null) {
341                cacheEnabled &= nonPersistent.isCacheEnabled();
342            }
343            setCacheEnabled(cacheEnabled);
344        }
345        return cacheEnabled;
346    }
347
348    @Override
349    public void rebase() {
350        persistent.rebase();
351        reset();
352    }
353
354}