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 boolean started;
040    private PendingMessageCursor currentCursor;
041
042    /**
043     * Construct
044     * @param broker
045     * @param queue
046     */
047    public StoreQueueCursor(Broker broker,Queue queue) {
048        super((queue != null ? queue.isPrioritizedMessages():false));
049        this.broker=broker;
050        this.queue = queue;
051        this.persistent = new QueueStorePrefetch(queue, broker);
052        currentCursor = persistent;
053    }
054
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    public synchronized void stop() throws Exception {
078        started = false;
079        if (nonPersistent != null) {
080          nonPersistent.destroy();
081        }
082        persistent.stop();
083        persistent.gc();
084        super.stop();
085        pendingCount = 0;
086    }
087
088    public synchronized boolean addMessageLast(MessageReference node) throws Exception {
089        boolean result = true;
090        if (node != null) {
091            Message msg = node.getMessage();
092            if (started) {
093                pendingCount++;
094                if (!msg.isPersistent()) {
095                    nonPersistent.addMessageLast(node);
096                }
097            }
098            if (msg.isPersistent()) {
099                result = persistent.addMessageLast(node);
100            }
101        }
102        return result;
103    }
104
105    public synchronized void addMessageFirst(MessageReference node) throws Exception {
106        if (node != null) {
107            Message msg = node.getMessage();
108            if (started) {
109                pendingCount++;
110                if (!msg.isPersistent()) {
111                    nonPersistent.addMessageFirst(node);
112                }
113            }
114            if (msg.isPersistent()) {
115                persistent.addMessageFirst(node);
116            }
117        }
118    }
119
120    public synchronized void clear() {
121        pendingCount = 0;
122    }
123
124    public synchronized boolean hasNext() {
125        try {
126            getNextCursor();
127        } catch (Exception e) {
128            LOG.error("Failed to get current cursor ", e);
129            throw new RuntimeException(e);
130       }
131       return currentCursor != null ? currentCursor.hasNext() : false;
132    }
133
134    public synchronized MessageReference next() {
135        MessageReference result = currentCursor != null ? currentCursor.next() : null;
136        return result;
137    }
138
139    public synchronized void remove() {
140        if (currentCursor != null) {
141            currentCursor.remove();
142        }
143        pendingCount--;
144    }
145
146    public synchronized void remove(MessageReference node) {
147        if (!node.isPersistent()) {
148            nonPersistent.remove(node);
149        } else {
150            persistent.remove(node);
151        }
152        pendingCount--;
153    }
154
155    public synchronized void reset() {
156        nonPersistent.reset();
157        persistent.reset();
158        pendingCount = persistent.size() + nonPersistent.size();
159    }
160
161    public void release() {
162        nonPersistent.release();
163        persistent.release();
164    }
165
166
167    public synchronized int size() {
168        if (pendingCount < 0) {
169            pendingCount = persistent.size() + nonPersistent.size();
170        }
171        return pendingCount;
172    }
173
174    public synchronized boolean isEmpty() {
175        // if negative, more messages arrived in store since last reset so non empty
176        return pendingCount == 0;
177    }
178
179    /**
180     * Informs the Broker if the subscription needs to intervention to recover
181     * it's state e.g. DurableTopicSubscriber may do
182     *
183     * @see org.apache.activemq.broker.region.cursors.PendingMessageCursor
184     * @return true if recovery required
185     */
186    public boolean isRecoveryRequired() {
187        return false;
188    }
189
190    /**
191     * @return the nonPersistent Cursor
192     */
193    public PendingMessageCursor getNonPersistent() {
194        return this.nonPersistent;
195    }
196
197    /**
198     * @param nonPersistent cursor to set
199     */
200    public void setNonPersistent(PendingMessageCursor nonPersistent) {
201        this.nonPersistent = nonPersistent;
202    }
203
204    public void setMaxBatchSize(int maxBatchSize) {
205        persistent.setMaxBatchSize(maxBatchSize);
206        if (nonPersistent != null) {
207            nonPersistent.setMaxBatchSize(maxBatchSize);
208        }
209        super.setMaxBatchSize(maxBatchSize);
210    }
211
212
213    public void setMaxProducersToAudit(int maxProducersToAudit) {
214        super.setMaxProducersToAudit(maxProducersToAudit);
215        if (persistent != null) {
216            persistent.setMaxProducersToAudit(maxProducersToAudit);
217        }
218        if (nonPersistent != null) {
219            nonPersistent.setMaxProducersToAudit(maxProducersToAudit);
220        }
221    }
222
223    public void setMaxAuditDepth(int maxAuditDepth) {
224        super.setMaxAuditDepth(maxAuditDepth);
225        if (persistent != null) {
226            persistent.setMaxAuditDepth(maxAuditDepth);
227        }
228        if (nonPersistent != null) {
229            nonPersistent.setMaxAuditDepth(maxAuditDepth);
230        }
231    }
232
233    public void setEnableAudit(boolean enableAudit) {
234        super.setEnableAudit(enableAudit);
235        if (persistent != null) {
236            persistent.setEnableAudit(enableAudit);
237        }
238        if (nonPersistent != null) {
239            nonPersistent.setEnableAudit(enableAudit);
240        }
241    }
242
243    @Override
244    public void rollback(MessageId id) {
245        nonPersistent.rollback(id);
246        persistent.rollback(id);
247    }
248
249    @Override
250    public void setUseCache(boolean useCache) {
251        super.setUseCache(useCache);
252        if (persistent != null) {
253            persistent.setUseCache(useCache);
254        }
255        if (nonPersistent != null) {
256            nonPersistent.setUseCache(useCache);
257        }
258    }
259
260    @Override
261    public void setMemoryUsageHighWaterMark(int memoryUsageHighWaterMark) {
262        super.setMemoryUsageHighWaterMark(memoryUsageHighWaterMark);
263        if (persistent != null) {
264            persistent.setMemoryUsageHighWaterMark(memoryUsageHighWaterMark);
265        }
266        if (nonPersistent != null) {
267            nonPersistent.setMemoryUsageHighWaterMark(memoryUsageHighWaterMark);
268        }
269    }
270
271
272
273    public synchronized void gc() {
274        if (persistent != null) {
275            persistent.gc();
276        }
277        if (nonPersistent != null) {
278            nonPersistent.gc();
279        }
280        pendingCount = persistent.size() + nonPersistent.size();
281    }
282
283    public void setSystemUsage(SystemUsage usageManager) {
284        super.setSystemUsage(usageManager);
285        if (persistent != null) {
286            persistent.setSystemUsage(usageManager);
287        }
288        if (nonPersistent != null) {
289            nonPersistent.setSystemUsage(usageManager);
290        }
291    }
292
293    protected synchronized PendingMessageCursor getNextCursor() throws Exception {
294        if (currentCursor == null || !currentCursor.hasMessagesBufferedToDeliver()) {
295            currentCursor = currentCursor == persistent ? nonPersistent : persistent;
296            // sanity check
297            if (currentCursor.isEmpty()) {
298                currentCursor = currentCursor == persistent ? nonPersistent : persistent;
299            }
300        }
301        return currentCursor;
302    }
303
304    @Override
305    public boolean isCacheEnabled() {
306        boolean cacheEnabled = isUseCache();
307        if (cacheEnabled) {
308            if (persistent != null) {
309                cacheEnabled &= persistent.isCacheEnabled();
310            }
311            if (nonPersistent != null) {
312                cacheEnabled &= nonPersistent.isCacheEnabled();
313            }
314            setCacheEnabled(cacheEnabled);
315        }
316        return cacheEnabled;
317    }
318
319    @Override
320    public void rebase() {
321        persistent.rebase();
322        reset();
323    }
324
325}