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.memory;
018
019import java.io.IOException;
020import java.util.Collections;
021import java.util.Iterator;
022import java.util.LinkedHashMap;
023import java.util.Map;
024import java.util.Map.Entry;
025
026import org.apache.activemq.broker.ConnectionContext;
027import org.apache.activemq.command.ActiveMQDestination;
028import org.apache.activemq.command.Message;
029import org.apache.activemq.command.MessageAck;
030import org.apache.activemq.command.MessageId;
031import org.apache.activemq.store.IndexListener;
032import org.apache.activemq.store.MessageRecoveryListener;
033import org.apache.activemq.store.AbstractMessageStore;
034import org.apache.activemq.store.MessageStoreStatistics;
035
036/**
037 * An implementation of {@link org.apache.activemq.store.MessageStore} which
038 * uses a
039 *
040 *
041 */
042public class MemoryMessageStore extends AbstractMessageStore {
043
044    protected final Map<MessageId, Message> messageTable;
045    protected MessageId lastBatchId;
046    protected long sequenceId;
047
048    public MemoryMessageStore(ActiveMQDestination destination) {
049        this(destination, new LinkedHashMap<MessageId, Message>());
050    }
051
052    public MemoryMessageStore(ActiveMQDestination destination, Map<MessageId, Message> messageTable) {
053        super(destination);
054        this.messageTable = Collections.synchronizedMap(messageTable);
055    }
056
057    @Override
058    public synchronized void addMessage(ConnectionContext context, Message message) throws IOException {
059        synchronized (messageTable) {
060            messageTable.put(message.getMessageId(), message);
061            incMessageStoreStatistics(getMessageStoreStatistics(), message);
062        }
063        message.incrementReferenceCount();
064        message.getMessageId().setFutureOrSequenceLong(sequenceId++);
065        if (indexListener != null) {
066            indexListener.onAdd(new IndexListener.MessageContext(context, message, null));
067        }
068    }
069
070    // public void addMessageReference(ConnectionContext context,MessageId
071    // messageId,long expirationTime,String messageRef)
072    // throws IOException{
073    // synchronized(messageTable){
074    // messageTable.put(messageId,messageRef);
075    // }
076    // }
077
078    @Override
079    public Message getMessage(MessageId identity) throws IOException {
080        return messageTable.get(identity);
081    }
082
083    // public String getMessageReference(MessageId identity) throws IOException{
084    // return (String)messageTable.get(identity);
085    // }
086
087    @Override
088    public void removeMessage(ConnectionContext context, MessageAck ack) throws IOException {
089        removeMessage(ack.getLastMessageId());
090    }
091
092    public void removeMessage(MessageId msgId) throws IOException {
093        synchronized (messageTable) {
094            Message removed = messageTable.remove(msgId);
095            if( removed !=null ) {
096                removed.decrementReferenceCount();
097                decMessageStoreStatistics(getMessageStoreStatistics(), removed);
098            }
099            if ((lastBatchId != null && lastBatchId.equals(msgId)) || messageTable.isEmpty()) {
100                lastBatchId = null;
101            }
102        }
103    }
104
105    @Override
106    public void recover(MessageRecoveryListener listener) throws Exception {
107        // the message table is a synchronizedMap - so just have to synchronize
108        // here
109        synchronized (messageTable) {
110            for (Iterator<Message> iter = messageTable.values().iterator(); iter.hasNext();) {
111                Object msg = iter.next();
112                if (msg.getClass() == MessageId.class) {
113                    listener.recoverMessageReference((MessageId)msg);
114                } else {
115                    listener.recoverMessage((Message)msg);
116                }
117            }
118        }
119    }
120
121    @Override
122    public void removeAllMessages(ConnectionContext context) throws IOException {
123        synchronized (messageTable) {
124            messageTable.clear();
125            getMessageStoreStatistics().reset();
126        }
127    }
128
129    public void delete() {
130        synchronized (messageTable) {
131            messageTable.clear();
132            getMessageStoreStatistics().reset();
133        }
134    }
135
136    @Override
137    public void recoverNextMessages(int maxReturned, MessageRecoveryListener listener) throws Exception {
138        synchronized (messageTable) {
139            boolean pastLackBatch = lastBatchId == null;
140            int count = 0;
141            for (Iterator iter = messageTable.entrySet().iterator(); iter.hasNext();) {
142                Map.Entry entry = (Entry)iter.next();
143                if (pastLackBatch) {
144                    count++;
145                    Object msg = entry.getValue();
146                    lastBatchId = (MessageId)entry.getKey();
147                    if (msg.getClass() == MessageId.class) {
148                        listener.recoverMessageReference((MessageId)msg);
149                    } else {
150                        listener.recoverMessage((Message)msg);
151                    }
152                } else {
153                    pastLackBatch = entry.getKey().equals(lastBatchId);
154                }
155            }
156        }
157    }
158
159    @Override
160    public void resetBatching() {
161        lastBatchId = null;
162    }
163
164    @Override
165    public void setBatch(MessageId messageId) {
166        lastBatchId = messageId;
167    }
168
169    @Override
170    public void updateMessage(Message message) {
171        synchronized (messageTable) {
172            Message original = messageTable.get(message.getMessageId());
173
174            //if can't be found then increment count, else remove old size
175            if (original == null) {
176                getMessageStoreStatistics().getMessageCount().increment();
177            } else {
178                getMessageStoreStatistics().getMessageSize().addSize(-original.getSize());
179            }
180            messageTable.put(message.getMessageId(), message);
181            getMessageStoreStatistics().getMessageSize().addSize(message.getSize());
182        }
183    }
184
185    @Override
186    public void recoverMessageStoreStatistics() throws IOException {
187        synchronized (messageTable) {
188            long size = 0;
189            int count = 0;
190            for (Iterator<Message> iter = messageTable.values().iterator(); iter
191                    .hasNext();) {
192                Message msg = iter.next();
193                size += msg.getSize();
194            }
195
196            getMessageStoreStatistics().reset();
197            getMessageStoreStatistics().getMessageCount().setCount(count);
198            getMessageStoreStatistics().getMessageSize().setTotalSize(size);
199        }
200    }
201
202    protected static final void incMessageStoreStatistics(final MessageStoreStatistics stats, final Message message) {
203        if (stats != null && message != null) {
204            stats.getMessageCount().increment();
205            stats.getMessageSize().addSize(message.getSize());
206        }
207    }
208
209    protected static final void decMessageStoreStatistics(final MessageStoreStatistics stats, final Message message) {
210        if (stats != null && message != null) {
211            stats.getMessageCount().decrement();
212            stats.getMessageSize().addSize(-message.getSize());
213        }
214    }
215
216}