001 /**
002 *
003 * Copyright 2004 Hiram Chirino
004 * Copyright 2004 Protique Ltd
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 *
018 **/
019 package org.activemq.store.journal;
020
021 import java.util.HashMap;
022 import java.util.Iterator;
023
024 import javax.jms.JMSException;
025
026 import org.activeio.journal.RecordLocation;
027 import org.activemq.message.ConsumerInfo;
028 import org.activemq.service.MessageIdentity;
029 import org.activemq.service.SubscriberEntry;
030 import org.activemq.service.Transaction;
031 import org.activemq.service.TransactionManager;
032 import org.activemq.service.TransactionTask;
033 import org.activemq.store.RecoveryListener;
034 import org.activemq.store.TopicMessageStore;
035 import org.activemq.util.Callback;
036 import org.apache.commons.logging.Log;
037 import org.apache.commons.logging.LogFactory;
038
039 /**
040 * A MessageStore that uses a Journal to store it's messages.
041 *
042 * @version $Revision: 1.1 $
043 */
044 public class JournalTopicMessageStore extends JournalMessageStore implements TopicMessageStore {
045 private static final Log log = LogFactory.getLog(JournalTopicMessageStore.class);
046
047 private TopicMessageStore longTermStore;
048 private HashMap ackedLastAckLocations = new HashMap();
049
050 public JournalTopicMessageStore(JournalPersistenceAdapter adapter, TopicMessageStore checkpointStore, String destinationName) {
051 super(adapter, checkpointStore, destinationName);
052 this.longTermStore = checkpointStore;
053 }
054
055 public void recoverSubscription(String subscriptionId, MessageIdentity lastDispatchedMessage, RecoveryListener listener) throws JMSException {
056 peristenceAdapter.checkpoint(true);
057 longTermStore.recoverSubscription(subscriptionId, lastDispatchedMessage, listener);
058 }
059
060 public SubscriberEntry getSubscriberEntry(ConsumerInfo info) throws JMSException {
061 return longTermStore.getSubscriberEntry(info);
062 }
063
064 public void setSubscriberEntry(ConsumerInfo info, SubscriberEntry subscriberEntry) throws JMSException {
065 peristenceAdapter.checkpoint(true);
066 longTermStore.setSubscriberEntry(info, subscriberEntry);
067 }
068
069 public MessageIdentity getLastestMessageIdentity() throws JMSException {
070 return longTermStore.getLastestMessageIdentity();
071 }
072
073 public void incrementMessageCount(MessageIdentity messageId) throws JMSException {
074 longTermStore.incrementMessageCount(messageId);
075 }
076
077 public void decrementMessageCountAndMaybeDelete(MessageIdentity messageId) throws JMSException {
078 longTermStore.decrementMessageCountAndMaybeDelete(messageId);
079 }
080
081 /**
082 */
083 public void setLastAcknowledgedMessageIdentity(final String subscription, final MessageIdentity messageIdentity) throws JMSException {
084 final boolean debug = log.isDebugEnabled();
085 final RecordLocation location = peristenceAdapter.writePacket(destinationName, subscription, messageIdentity, false);
086 if( !TransactionManager.isCurrentTransaction() ) {
087 if( debug )
088 log.debug("Journalled acknowledge: "+messageIdentity.getMessageID()+" at "+location);
089 acknowledge(subscription, messageIdentity, location);
090 } else {
091 if( debug )
092 log.debug("Journalled in flight acknowledge: "+messageIdentity.getMessageID()+" at "+location);
093
094 synchronized (this) {
095 inFlightTxLocations.add(location);
096 }
097 final Transaction tx = TransactionManager.getContexTransaction();
098 JournalAck ack = new JournalAck(destinationName,subscription,messageIdentity.getMessageID(), tx.getTransactionId());
099 transactionStore.acknowledge(this, ack, location);
100 tx.addPostCommitTask(new TransactionTask(){
101 public void execute() throws Throwable {
102 if( debug )
103 log.debug("In flight acknowledge commit: "+messageIdentity.getMessageID()+" at "+location);
104
105 synchronized (JournalTopicMessageStore.this) {
106 inFlightTxLocations.remove(location);
107 acknowledge(subscription, messageIdentity, location);
108 }
109 }
110 });
111 tx.addPostRollbackTask(new TransactionTask(){
112 public void execute() throws Throwable {
113 if( debug )
114 log.debug("In flight acknowledge rollback: "+messageIdentity.getMessageID()+" at "+location);
115 // TODO Auto-generated method stub
116 synchronized (JournalTopicMessageStore.this) {
117 inFlightTxLocations.remove(location);
118 }
119 }
120 });
121
122 }
123 }
124
125 private void acknowledge(String subscription, MessageIdentity messageIdentity, RecordLocation location) {
126 synchronized(this) {
127 lastLocation = location;
128 ackedLastAckLocations.put(subscription,messageIdentity);
129 }
130 }
131
132 public RecordLocation checkpoint() throws JMSException {
133
134 // swap the acks before check pointing the added messages since we don't want to ack
135 // a message that has not been checkpointed yet.
136 final HashMap cpAckedLastAckLocations;
137 synchronized(this) {
138 cpAckedLastAckLocations = this.ackedLastAckLocations;
139 this.ackedLastAckLocations = new HashMap();
140 }
141
142 // Check point the added messages.
143 RecordLocation rc = super.checkpoint();
144
145 if( log.isDebugEnabled() ) {
146 log.debug("Checkpoint acknowledgments: "+cpAckedLastAckLocations);
147 }
148
149 transactionTemplate.run(new Callback() {
150 public void execute() throws Throwable {
151
152 // Checkpoint the acknowledged messages.
153 Iterator iterator = cpAckedLastAckLocations.keySet().iterator();
154 while (iterator.hasNext()) {
155 String subscription = (String) iterator.next();
156 MessageIdentity identity = (MessageIdentity) cpAckedLastAckLocations.get(subscription);
157 longTermStore.setLastAcknowledgedMessageIdentity(subscription, identity);
158 }
159
160 }
161
162 });
163
164 return rc;
165 }
166
167 /**
168 * @return Returns the longTermStore.
169 */
170 public TopicMessageStore getLongTermTopicMessageStore() {
171 return longTermStore;
172 }
173
174 public void deleteSubscription(String subscription) throws JMSException {
175 peristenceAdapter.checkpoint(true);
176 longTermStore.deleteSubscription(subscription);
177 }
178
179 public void replayAcknowledge(String subscription, MessageIdentity identity) {
180 try {
181 longTermStore.setLastAcknowledgedMessageIdentity(subscription,identity);
182 }
183 catch (Throwable e) {
184 log.debug("Could not replay acknowledge for message '"+identity.getMessageID()+"'. Message may have already been acknowledged. reason: " + e);
185 }
186 }
187
188 }