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 java.util.Collections;
020import java.util.LinkedList;
021import java.util.List;
022import java.util.Set;
023import org.apache.activemq.ActiveMQMessageAudit;
024import org.apache.activemq.broker.Broker;
025import org.apache.activemq.broker.ConnectionContext;
026import org.apache.activemq.broker.region.BaseDestination;
027import org.apache.activemq.broker.region.Destination;
028import org.apache.activemq.broker.region.MessageReference;
029import org.apache.activemq.broker.region.Subscription;
030import org.apache.activemq.command.MessageId;
031import org.apache.activemq.usage.SystemUsage;
032
033/**
034 * Abstract method holder for pending message (messages awaiting disptach to a
035 * consumer) cursor
036 *
037 *
038 */
039public abstract class AbstractPendingMessageCursor implements PendingMessageCursor {
040    protected int memoryUsageHighWaterMark = 70;
041    protected int maxBatchSize = BaseDestination.MAX_PAGE_SIZE;
042    protected SystemUsage systemUsage;
043    protected int maxProducersToAudit = BaseDestination.MAX_PRODUCERS_TO_AUDIT;
044    protected int maxAuditDepth = BaseDestination.MAX_AUDIT_DEPTH;
045    protected boolean enableAudit=true;
046    protected ActiveMQMessageAudit audit;
047    protected boolean useCache=true;
048    private boolean cacheEnabled=true;
049    private boolean started=false;
050    protected MessageReference last = null;
051    protected final boolean prioritizedMessages;
052
053    public AbstractPendingMessageCursor(boolean prioritizedMessages) {
054        this.prioritizedMessages=prioritizedMessages;
055    }
056
057
058    @Override
059    public synchronized void start() throws Exception  {
060        if (!started && enableAudit && audit==null) {
061            audit= new ActiveMQMessageAudit(maxAuditDepth,maxProducersToAudit);
062        }
063        started=true;
064    }
065
066    @Override
067    public synchronized void stop() throws Exception  {
068        started=false;
069        gc();
070    }
071
072    @Override
073    public void add(ConnectionContext context, Destination destination) throws Exception {
074    }
075
076    @Override
077    @SuppressWarnings("unchecked")
078    public List<MessageReference> remove(ConnectionContext context, Destination destination) throws Exception {
079        return Collections.EMPTY_LIST;
080    }
081
082    @Override
083    public boolean isRecoveryRequired() {
084        return true;
085    }
086
087    @Override
088    public void addMessageFirst(MessageReference node) throws Exception {
089    }
090
091    @Override
092    public boolean addMessageLast(MessageReference node) throws Exception {
093        return tryAddMessageLast(node, INFINITE_WAIT);
094    }
095
096    @Override
097    public boolean tryAddMessageLast(MessageReference node, long maxWaitTime) throws Exception {
098        return true;
099    }
100
101    @Override
102    public void addRecoveredMessage(MessageReference node) throws Exception {
103        addMessageLast(node);
104    }
105
106    @Override
107    public void clear() {
108    }
109
110    @Override
111    public boolean hasNext() {
112        return false;
113    }
114
115    @Override
116    public boolean isEmpty() {
117        return false;
118    }
119
120    @Override
121    public boolean isEmpty(Destination destination) {
122        return isEmpty();
123    }
124
125    @Override
126    public MessageReference next() {
127        return null;
128    }
129
130    @Override
131    public void remove() {
132    }
133
134    @Override
135    public void reset() {
136    }
137
138    @Override
139    public int size() {
140        return 0;
141    }
142
143    @Override
144    public int getMaxBatchSize() {
145        return maxBatchSize;
146    }
147
148    @Override
149    public void setMaxBatchSize(int maxBatchSize) {
150        this.maxBatchSize = maxBatchSize;
151    }
152
153    protected void fillBatch() throws Exception {
154    }
155
156    @Override
157    public void resetForGC() {
158        reset();
159    }
160
161    @Override
162    public void remove(MessageReference node) {
163    }
164
165    @Override
166    public void gc() {
167    }
168
169    @Override
170    public void setSystemUsage(SystemUsage usageManager) {
171        this.systemUsage = usageManager;
172    }
173
174    @Override
175    public boolean hasSpace() {
176        // allow isFull to verify parent usage and otherwise enforce local memoryUsageHighWaterMark
177        return systemUsage != null ? (!isParentFull() && systemUsage.getMemoryUsage().getPercentUsage() < memoryUsageHighWaterMark) : true;
178    }
179
180    boolean parentHasSpace(int waterMark) {
181        boolean result = true;
182        if (systemUsage != null) {
183            if (systemUsage.getMemoryUsage().getParent() != null) {
184                return systemUsage.getMemoryUsage().getParent().getPercentUsage() <= waterMark;
185            }
186        }
187        return result;
188    }
189
190    private boolean isParentFull() {
191        boolean result = false;
192        if (systemUsage != null) {
193            if (systemUsage.getMemoryUsage().getParent() != null) {
194                return systemUsage.getMemoryUsage().getParent().getPercentUsage() >= 100;
195            }
196        }
197        return result;
198    }
199
200    @Override
201    public boolean isFull() {
202        return systemUsage != null ? systemUsage.getMemoryUsage().isFull() : false;
203    }
204
205    @Override
206    public void release() {
207    }
208
209    @Override
210    public boolean hasMessagesBufferedToDeliver() {
211        return false;
212    }
213
214    /**
215     * @return the memoryUsageHighWaterMark
216     */
217    @Override
218    public int getMemoryUsageHighWaterMark() {
219        return memoryUsageHighWaterMark;
220    }
221
222    /**
223     * @param memoryUsageHighWaterMark the memoryUsageHighWaterMark to set
224     */
225    @Override
226    public void setMemoryUsageHighWaterMark(int memoryUsageHighWaterMark) {
227        this.memoryUsageHighWaterMark = memoryUsageHighWaterMark;
228    }
229
230    /**
231     * @return the usageManager
232     */
233    @Override
234    public SystemUsage getSystemUsage() {
235        return this.systemUsage;
236    }
237
238    /**
239     * destroy the cursor
240     *
241     * @throws Exception
242     */
243    @Override
244    public void destroy() throws Exception {
245        stop();
246    }
247
248    /**
249     * Page in a restricted number of messages
250     *
251     * @param maxItems maximum number of messages to return
252     * @return a list of paged in messages
253     */
254    @Override
255    public LinkedList<MessageReference> pageInList(int maxItems) {
256        throw new RuntimeException("Not supported");
257    }
258
259    /**
260     * @return the maxProducersToAudit
261     */
262    @Override
263    public synchronized int getMaxProducersToAudit() {
264        return maxProducersToAudit;
265    }
266
267    /**
268     * @param maxProducersToAudit the maxProducersToAudit to set
269     */
270    @Override
271    public synchronized void setMaxProducersToAudit(int maxProducersToAudit) {
272        this.maxProducersToAudit = maxProducersToAudit;
273        if (audit != null) {
274            audit.setMaximumNumberOfProducersToTrack(maxProducersToAudit);
275        }
276    }
277
278    /**
279     * @return the maxAuditDepth
280     */
281    @Override
282    public synchronized int getMaxAuditDepth() {
283        return maxAuditDepth;
284    }
285
286
287    /**
288     * @param maxAuditDepth the maxAuditDepth to set
289     */
290    @Override
291    public synchronized void setMaxAuditDepth(int maxAuditDepth) {
292        this.maxAuditDepth = maxAuditDepth;
293        if (audit != null) {
294            audit.setAuditDepth(maxAuditDepth);
295        }
296    }
297
298
299    /**
300     * @return the enableAudit
301     */
302    @Override
303    public boolean isEnableAudit() {
304        return enableAudit;
305    }
306
307    /**
308     * @param enableAudit the enableAudit to set
309     */
310    @Override
311    public synchronized void setEnableAudit(boolean enableAudit) {
312        this.enableAudit = enableAudit;
313        if (enableAudit && started && audit==null) {
314            audit= new ActiveMQMessageAudit(maxAuditDepth,maxProducersToAudit);
315        }
316    }
317
318    @Override
319    public boolean isTransient() {
320        return false;
321    }
322
323
324    /**
325     * set the audit
326     * @param audit new audit component
327     */
328    @Override
329    public void setMessageAudit(ActiveMQMessageAudit audit) {
330        this.audit=audit;
331    }
332
333
334    /**
335     * @return the audit
336     */
337    @Override
338    public ActiveMQMessageAudit getMessageAudit() {
339        return audit;
340    }
341
342    @Override
343    public boolean isUseCache() {
344        return useCache;
345    }
346
347    @Override
348    public void setUseCache(boolean useCache) {
349        this.useCache = useCache;
350    }
351
352    public synchronized boolean isDuplicate(MessageId messageId) {
353        boolean unique = recordUniqueId(messageId);
354        rollback(messageId);
355        return !unique;
356    }
357
358    /**
359     * records a message id and checks if it is a duplicate
360     * @param messageId
361     * @return true if id is unique, false otherwise.
362     */
363    public synchronized boolean recordUniqueId(MessageId messageId) {
364        if (!enableAudit || audit==null) {
365            return true;
366        }
367        return !audit.isDuplicate(messageId);
368    }
369
370    @Override
371    public synchronized void rollback(MessageId id) {
372        if (audit != null) {
373            audit.rollback(id);
374        }
375    }
376
377    public synchronized boolean isStarted() {
378        return started;
379    }
380
381    public static boolean isPrioritizedMessageSubscriber(Broker broker,Subscription sub) {
382        boolean result = false;
383        Set<Destination> destinations = broker.getDestinations(sub.getActiveMQDestination());
384        if (destinations != null) {
385            for (Destination dest:destinations) {
386                if (dest.isPrioritizedMessages()) {
387                    result = true;
388                    break;
389                }
390            }
391        }
392        return result;
393
394    }
395
396    @Override
397    public synchronized boolean isCacheEnabled() {
398        return cacheEnabled;
399    }
400
401    public synchronized void setCacheEnabled(boolean val) {
402        cacheEnabled = val;
403    }
404
405    @Override
406    public void rebase() {
407    }
408}