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}