use Kaha MapContainer for message storage instead of the Kaha ListContainer

git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@503680 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Robert Davies 2007-02-05 13:57:43 +00:00
parent 5b49189cfe
commit 0859f30a91
26 changed files with 837 additions and 362 deletions

View File

@ -278,7 +278,7 @@ public class FilePendingMessageCursor extends AbstractPendingMessageCursor imple
protected ListContainer getDiskList(){ protected ListContainer getDiskList(){
if(diskList==null){ if(diskList==null){
try{ try{
diskList=store.getListContainer(name,"TopicSubscription",Store.IndexType.PERSISTENT); diskList=store.getListContainer(name,"TopicSubscription",true);
diskList.setMarshaller(new CommandMarshaller(new OpenWireFormat())); diskList.setMarshaller(new CommandMarshaller(new OpenWireFormat()));
}catch(IOException e){ }catch(IOException e){
e.printStackTrace(); e.printStackTrace();

View File

@ -23,7 +23,7 @@ package org.apache.activemq.command;
* @openwire:marshaller code="120" * @openwire:marshaller code="120"
* @version $Revision$ * @version $Revision$
*/ */
public class ConnectionId implements DataStructure { public class ConnectionId implements DataStructure, Comparable<ConnectionId> {
public static final byte DATA_STRUCTURE_TYPE=CommandTypes.CONNECTION_ID; public static final byte DATA_STRUCTURE_TYPE=CommandTypes.CONNECTION_ID;
@ -86,4 +86,10 @@ public class ConnectionId implements DataStructure {
public boolean isMarshallAware() { public boolean isMarshallAware() {
return false; return false;
} }
public int compareTo(ConnectionId o){
return value.compareTo(o.value);
}
} }

View File

@ -21,7 +21,7 @@ package org.apache.activemq.command;
* @openwire:marshaller code="111" * @openwire:marshaller code="111"
* @version $Revision: 1.11 $ * @version $Revision: 1.11 $
*/ */
public class LocalTransactionId extends TransactionId { public class LocalTransactionId extends TransactionId implements Comparable<LocalTransactionId>{
public static final byte DATA_STRUCTURE_TYPE=CommandTypes.ACTIVEMQ_LOCAL_TRANSACTION_ID; public static final byte DATA_STRUCTURE_TYPE=CommandTypes.ACTIVEMQ_LOCAL_TRANSACTION_ID;
@ -79,6 +79,19 @@ public class LocalTransactionId extends TransactionId {
&& connectionId.equals(tx.connectionId); && connectionId.equals(tx.connectionId);
} }
/**
* @param o
* @return
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(LocalTransactionId o){
int result = connectionId.compareTo(o.connectionId);
if (result==0) {
result = (int) (value - o.value);
}
return result;
}
/** /**
* @openwire:property version=1 * @openwire:property version=1
*/ */
@ -99,5 +112,7 @@ public class LocalTransactionId extends TransactionId {
this.connectionId = connectionId; this.connectionId = connectionId;
} }
} }

View File

@ -22,7 +22,7 @@ package org.apache.activemq.command;
* @openwire:marshaller code="110" * @openwire:marshaller code="110"
* @version $Revision: 1.12 $ * @version $Revision: 1.12 $
*/ */
public class MessageId implements DataStructure { public class MessageId implements DataStructure, Comparable<MessageId> {
public static final byte DATA_STRUCTURE_TYPE=CommandTypes.MESSAGE_ID; public static final byte DATA_STRUCTURE_TYPE=CommandTypes.MESSAGE_ID;
@ -145,5 +145,18 @@ public class MessageId implements DataStructure {
copy.key = key; copy.key = key;
copy.brokerSequenceId = brokerSequenceId ; copy.brokerSequenceId = brokerSequenceId ;
return copy; return copy;
}
/**
* @param o
* @return
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(MessageId other){
int result = -1;
if (other != null) {
result = this.toString().compareTo(other.toString());
}
return result;
} }
} }

View File

@ -138,7 +138,7 @@ public interface ListContainer<V> extends List<V>{
public StoreEntry getFirst(); public StoreEntry getFirst();
/** /**
* Get yjr StoreEntry for the last item of the list * Get the StoreEntry for the last item of the list
* *
* @return the last StoreEntry or null if the list is empty * @return the last StoreEntry or null if the list is empty
*/ */

View File

@ -171,5 +171,41 @@ public interface MapContainer<K, V> extends Map<K, V>{
*/ */
public V getValue(StoreEntry Valuelocation); public V getValue(StoreEntry Valuelocation);
/** Get the StoreEntry for the first value in the Map
*
* @return the first StoreEntry or null if the map is empty
*/
public StoreEntry getFirst();
/**
* Get the StoreEntry for the last value item of the Map
*
* @return the last StoreEntry or null if the list is empty
*/
public StoreEntry getLast();
/**
* Get the next StoreEntry value from the map
*
* @param entry
* @return the next StoreEntry or null
*/
public StoreEntry getNext(StoreEntry entry);
/**
* Get the previous StoreEntry from the map
*
* @param entry
* @return the previous store entry or null
*/
public StoreEntry getPrevious(StoreEntry entry);
/**
* It's possible that a StoreEntry could be come stale
* this will return an upto date entry for the StoreEntry position
* @param entry old entry
* @return a refreshed StoreEntry
*/
public StoreEntry refresh(StoreEntry entry);
} }

View File

@ -0,0 +1,51 @@
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.kaha;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.activemq.command.MessageId;
/**
* Implementation of a Marshaller for MessageIds
*
* @version $Revision: 1.2 $
*/
public class MessageIdMarshaller implements Marshaller<MessageId> {
/**
* Write the payload of this entry to the RawContainer
*
* @param object
* @param dataOut
* @throws IOException
*/
public void writePayload(MessageId object,DataOutput dataOut) throws IOException{
dataOut.writeUTF(object.toString());
}
/**
* Read the entry from the RawContainer
*
* @param dataIn
* @return unmarshalled object
* @throws IOException
*/
public MessageId readPayload(DataInput dataIn) throws IOException{
return new MessageId(dataIn.readUTF());
}
}

View File

@ -0,0 +1,67 @@
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.kaha;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.activemq.command.Message;
import org.apache.activemq.util.ByteSequence;
import org.apache.activemq.wireformat.WireFormat;
/**
* Implementation of a Marshaller for MessageIds
*
* @version $Revision: 1.2 $
*/
public class MessageMarshaller implements Marshaller<Message> {
private WireFormat wireFormat;
/**
* Constructor
* @param wireFormat
*/
public MessageMarshaller(WireFormat wireFormat) {
this.wireFormat=wireFormat;
}
/**
* Write the payload of this entry to the RawContainer
*
* @param message
* @param dataOut
* @throws IOException
*/
public void writePayload(Message message,DataOutput dataOut) throws IOException{
ByteSequence packet = wireFormat.marshal(message);
dataOut.writeInt(packet.length);
dataOut.write(packet.data, packet.offset, packet.length);
}
/**
* Read the entry from the RawContainer
*
* @param dataIn
* @return unmarshalled object
* @throws IOException
*/
public Message readPayload(DataInput dataIn) throws IOException{
int size=dataIn.readInt();
byte[] data=new byte[size];
dataIn.readFully(data);
return (Message)wireFormat.unmarshal(new ByteSequence(data));
}
}

View File

@ -29,21 +29,7 @@ public interface Store{
* Defauly container name * Defauly container name
*/ */
public static final String DEFAULT_CONTAINER_NAME="kaha"; public static final String DEFAULT_CONTAINER_NAME="kaha";
/**
* Index Types
*
*/
public static enum IndexType{
/**
* Map Index held in memory
*/
VM,
/**
* Map index persistent
*/
PERSISTENT
}
/** /**
* Byte Marshaller * Byte Marshaller
*/ */
@ -130,11 +116,11 @@ public interface Store{
* *
* @param id * @param id
* @param containerName * @param containerName
* @param indexType * @param persistentIndex
* @return container for the associated id or null if it doesn't exist * @return container for the associated id or null if it doesn't exist
* @throws IOException * @throws IOException
*/ */
public MapContainer getMapContainer(Object id,String containerName,Store.IndexType indexType) throws IOException; public MapContainer getMapContainer(Object id,String containerName,boolean persistentIndex) throws IOException;
/** /**
* delete a container from the default container * delete a container from the default container
@ -204,11 +190,11 @@ public interface Store{
* *
* @param id * @param id
* @param containerName * @param containerName
* @param indexType * @param persistentIndex
* @return container for the associated id or null if it doesn't exist * @return container for the associated id or null if it doesn't exist
* @throws IOException * @throws IOException
*/ */
public ListContainer getListContainer(Object id,String containerName,Store.IndexType indexType) throws IOException; public ListContainer getListContainer(Object id,String containerName,boolean persistentIndex) throws IOException;
/** /**
* delete a ListContainer from the default container * delete a ListContainer from the default container

View File

@ -76,7 +76,7 @@ public class KahaStore implements Store{
private boolean useAsyncDataManager=false; private boolean useAsyncDataManager=false;
private long maxDataFileLength=1024*1024*32; private long maxDataFileLength=1024*1024*32;
private FileLock lock; private FileLock lock;
private IndexType indexType=IndexType.PERSISTENT; private boolean persistentIndex=true;
public KahaStore(String name,String mode) throws IOException{ public KahaStore(String name,String mode) throws IOException{
this.mode=mode; this.mode=mode;
@ -183,10 +183,10 @@ public class KahaStore implements Store{
} }
public MapContainer getMapContainer(Object id,String containerName) throws IOException{ public MapContainer getMapContainer(Object id,String containerName) throws IOException{
return getMapContainer(id,containerName,indexType); return getMapContainer(id,containerName,persistentIndex);
} }
public synchronized MapContainer getMapContainer(Object id,String containerName,IndexType indexType) public synchronized MapContainer getMapContainer(Object id,String containerName,boolean persistentIndex)
throws IOException{ throws IOException{
initialize(); initialize();
ContainerId containerId=new ContainerId(); ContainerId containerId=new ContainerId();
@ -200,7 +200,7 @@ public class KahaStore implements Store{
if(root==null){ if(root==null){
root=mapsContainer.addRoot(im,containerId); root=mapsContainer.addRoot(im,containerId);
} }
result=new MapContainerImpl(directory,containerId,root,im,dm,indexType); result=new MapContainerImpl(directory,containerId,root,im,dm,persistentIndex);
maps.put(containerId,result); maps.put(containerId,result);
} }
return result; return result;
@ -249,10 +249,10 @@ public class KahaStore implements Store{
} }
public ListContainer getListContainer(Object id,String containerName) throws IOException{ public ListContainer getListContainer(Object id,String containerName) throws IOException{
return getListContainer(id,containerName,indexType); return getListContainer(id,containerName,persistentIndex);
} }
public synchronized ListContainer getListContainer(Object id,String containerName,IndexType indexType) public synchronized ListContainer getListContainer(Object id,String containerName,boolean persistentIndex)
throws IOException{ throws IOException{
initialize(); initialize();
ContainerId containerId=new ContainerId(); ContainerId containerId=new ContainerId();
@ -267,7 +267,7 @@ public class KahaStore implements Store{
if(root==null){ if(root==null){
root=listsContainer.addRoot(im,containerId); root=listsContainer.addRoot(im,containerId);
} }
result=new ListContainerImpl(containerId,root,im,dm,indexType); result=new ListContainerImpl(containerId,root,im,dm,persistentIndex);
lists.put(containerId,result); lists.put(containerId,result);
} }
return result; return result;
@ -387,7 +387,7 @@ public class KahaStore implements Store{
* @return the default index type * @return the default index type
*/ */
public synchronized String getIndexTypeAsString(){ public synchronized String getIndexTypeAsString(){
return indexType==IndexType.PERSISTENT ? "PERSISTENT":"VM"; return persistentIndex ? "PERSISTENT":"VM";
} }
/** /**
@ -398,9 +398,9 @@ public class KahaStore implements Store{
*/ */
public synchronized void setIndexTypeAsString(String type){ public synchronized void setIndexTypeAsString(String type){
if(type.equalsIgnoreCase("VM")){ if(type.equalsIgnoreCase("VM")){
indexType=IndexType.VM; persistentIndex=false;
}else{ }else{
indexType=IndexType.PERSISTENT; persistentIndex=true;
} }
} }

View File

@ -50,15 +50,15 @@ public abstract class BaseContainerImpl{
protected boolean loaded=false; protected boolean loaded=false;
protected boolean closed=false; protected boolean closed=false;
protected boolean initialized=false; protected boolean initialized=false;
protected Store.IndexType indexType; protected boolean persistentIndex;
protected BaseContainerImpl(ContainerId id,IndexItem root,IndexManager indexManager, protected BaseContainerImpl(ContainerId id,IndexItem root,IndexManager indexManager,
DataManager dataManager,Store.IndexType indexType){ DataManager dataManager,boolean persistentIndex){
this.containerId=id; this.containerId=id;
this.root=root; this.root=root;
this.indexManager=indexManager; this.indexManager=indexManager;
this.dataManager=dataManager; this.dataManager=dataManager;
this.indexType = indexType; this.persistentIndex = persistentIndex;
} }
public ContainerId getContainerId(){ public ContainerId getContainerId(){
@ -70,7 +70,7 @@ public abstract class BaseContainerImpl{
if(!initialized){ if(!initialized){
initialized=true; initialized=true;
if(this.indexList==null){ if(this.indexList==null){
if(indexType.equals(Store.IndexType.PERSISTENT)){ if(persistentIndex){
this.indexList=new DiskIndexLinkedList(indexManager,root); this.indexList=new DiskIndexLinkedList(indexManager,root);
}else{ }else{
this.indexList=new VMIndexLinkedList(root); this.indexList=new VMIndexLinkedList(root);

View File

@ -46,8 +46,8 @@ public class ListContainerImpl extends BaseContainerImpl implements ListContaine
public ListContainerImpl(ContainerId id,IndexItem root,IndexManager indexManager,DataManager dataManager, public ListContainerImpl(ContainerId id,IndexItem root,IndexManager indexManager,DataManager dataManager,
Store.IndexType indexType) throws IOException{ boolean persistentIndex) throws IOException{
super(id,root,indexManager,dataManager,indexType); super(id,root,indexManager,dataManager,persistentIndex);
} }
/* /*
@ -697,7 +697,7 @@ public class ListContainerImpl extends BaseContainerImpl implements ListContaine
} }
/** /**
* Get yjr StoreEntry for the last item of the list * Get the StoreEntry for the last item of the list
* *
* @return the last StoreEntry or null if the list is empty * @return the last StoreEntry or null if the list is empty
*/ */

View File

@ -50,19 +50,20 @@ public final class MapContainerImpl extends BaseContainerImpl implements MapCont
protected Marshaller valueMarshaller=Store.ObjectMarshaller; protected Marshaller valueMarshaller=Store.ObjectMarshaller;
protected File directory; protected File directory;
public MapContainerImpl(File directory,ContainerId id,IndexItem root,IndexManager indexManager,DataManager dataManager, public MapContainerImpl(File directory,ContainerId id,IndexItem root,IndexManager indexManager,
Store.IndexType indexType){ DataManager dataManager,boolean persistentIndex){
super(id,root,indexManager,dataManager,indexType); super(id,root,indexManager,dataManager,persistentIndex);
this.directory = directory; this.directory=directory;
} }
public synchronized void init() { public synchronized void init(){
super.init(); super.init();
if(index==null){ if(index==null){
if(indexType.equals(Store.IndexType.PERSISTENT)){ if(persistentIndex){
String name = containerId.getDataContainerName() + "_" + containerId.getKey(); String name=containerId.getDataContainerName()+"_"+containerId.getKey();
name=name.replaceAll("[^a-zA-Z0-9\\.\\_\\-]", "_");
try{ try{
this.index=new HashIndex(directory, name , indexManager); this.index=new HashIndex(directory,name,indexManager);
}catch(IOException e){ }catch(IOException e){
log.error("Failed to create HashIndex",e); log.error("Failed to create HashIndex",e);
throw new RuntimeException(e); throw new RuntimeException(e);
@ -182,7 +183,7 @@ public final class MapContainerImpl extends BaseContainerImpl implements MapCont
Object result=null; Object result=null;
StoreEntry item=null; StoreEntry item=null;
try{ try{
item=(StoreEntry)index.get(key); item=index.get(key);
}catch(IOException e){ }catch(IOException e){
log.error("Failed trying to get key: "+key,e); log.error("Failed trying to get key: "+key,e);
throw new RuntimeException(e); throw new RuntimeException(e);
@ -288,11 +289,10 @@ public final class MapContainerImpl extends BaseContainerImpl implements MapCont
load(); load();
try{ try{
Object result=null; Object result=null;
IndexItem item=(IndexItem)index.get(key); IndexItem item=(IndexItem)index.remove(key);
if(item!=null){ if(item!=null){
// refresh the index // refresh the index
item=(IndexItem)indexList.refreshEntry(item); item=(IndexItem)indexList.refreshEntry(item);
index.remove(key);
result=getValue(item); result=getValue(item);
IndexItem prev=indexList.getPrevEntry(item); IndexItem prev=indexList.getPrevEntry(item);
IndexItem next=indexList.getNextEntry(item); IndexItem next=indexList.getNextEntry(item);
@ -365,15 +365,13 @@ public final class MapContainerImpl extends BaseContainerImpl implements MapCont
public synchronized StoreEntry place(Object key,Object value){ public synchronized StoreEntry place(Object key,Object value){
load(); load();
try{ try{
if(index.containsKey(key)){ remove(key);
remove(key);
}
IndexItem item=write(key,value); IndexItem item=write(key,value);
index.store(key,item); index.store(key,item);
indexList.add(item); indexList.add(item);
return item; return item;
}catch(IOException e){ }catch(IOException e){
log.error("Failed trying to palce key: "+key,e); log.error("Failed trying to place key: "+key,e);
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@ -402,6 +400,33 @@ public final class MapContainerImpl extends BaseContainerImpl implements MapCont
} }
} }
public synchronized StoreEntry getFirst(){
load();
return indexList.getFirst();
}
public synchronized StoreEntry getLast(){
load();
return indexList.getLast();
}
public synchronized StoreEntry getNext(StoreEntry entry){
load();
IndexItem item=(IndexItem)entry;
return indexList.getNextEntry(item);
}
public synchronized StoreEntry getPrevious(StoreEntry entry){
load();
IndexItem item=(IndexItem)entry;
return indexList.getPrevEntry(item);
}
public synchronized StoreEntry refresh(StoreEntry entry){
load();
return indexList.getEntry(entry);
}
/** /**
* Get the value from it's location * Get the value from it's location
* *

View File

@ -146,7 +146,8 @@ class HashBin{
} }
} }
void remove(HashEntry entry) throws IOException{ HashEntry remove(HashEntry entry) throws IOException{
HashEntry result = null;
try{ try{
int low=0; int low=0;
int high=size()-1; int high=size()-1;
@ -155,6 +156,7 @@ class HashBin{
HashEntry te=getHashEntry(mid); HashEntry te=getHashEntry(mid);
int cmp=te.compareTo(entry); int cmp=te.compareTo(entry);
if(cmp==0){ if(cmp==0){
result =te;
removeHashEntry(mid); removeHashEntry(mid);
size--; size--;
break; break;
@ -167,6 +169,7 @@ class HashBin{
}finally{ }finally{
end(); end();
} }
return result;
} }
private void addHashEntry(int index,HashEntry entry) throws IOException{ private void addHashEntry(int index,HashEntry entry) throws IOException{

View File

@ -24,6 +24,7 @@ import org.apache.activemq.kaha.impl.index.Index;
import org.apache.activemq.kaha.impl.index.IndexManager; import org.apache.activemq.kaha.impl.index.IndexManager;
import org.apache.activemq.util.DataByteArrayInputStream; import org.apache.activemq.util.DataByteArrayInputStream;
import org.apache.activemq.util.DataByteArrayOutputStream; import org.apache.activemq.util.DataByteArrayOutputStream;
import org.apache.activemq.util.LRUCache;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -34,7 +35,7 @@ import org.apache.commons.logging.LogFactory;
*/ */
public class HashIndex implements Index{ public class HashIndex implements Index{
private static final String NAME_PREFIX="tree-index-"; private static final String NAME_PREFIX="hash-index-";
private static final int DEFAULT_PAGE_SIZE; private static final int DEFAULT_PAGE_SIZE;
private static final int DEFAULT_KEY_SIZE; private static final int DEFAULT_KEY_SIZE;
private static final Log log=LogFactory.getLog(HashIndex.class); private static final Log log=LogFactory.getLog(HashIndex.class);
@ -55,6 +56,9 @@ public class HashIndex implements Index{
private HashPage firstFree; private HashPage firstFree;
private HashPage lastFree; private HashPage lastFree;
private AtomicBoolean loaded=new AtomicBoolean(); private AtomicBoolean loaded=new AtomicBoolean();
private LRUCache<Long,HashPage> pageCache;
private boolean enablePageCaching=false;
private int pageCacheSize=10;
/** /**
* Constructor * Constructor
@ -86,6 +90,7 @@ public class HashIndex implements Index{
capacity<<=1; capacity<<=1;
this.bins=new HashBin[capacity]; this.bins=new HashBin[capacity];
openIndexFile(); openIndexFile();
pageCache=new LRUCache<Long,HashPage>(pageCacheSize,pageCacheSize,0.75f,true);
} }
/** /**
@ -130,6 +135,37 @@ public class HashIndex implements Index{
} }
this.pageSize=pageSize; this.pageSize=pageSize;
} }
/**
* @return the enablePageCaching
*/
public boolean isEnablePageCaching(){
return this.enablePageCaching;
}
/**
* @param enablePageCaching the enablePageCaching to set
*/
public void setEnablePageCaching(boolean enablePageCaching){
this.enablePageCaching=enablePageCaching;
}
/**
* @return the pageCacheSize
*/
public int getPageCacheSize(){
return this.pageCacheSize;
}
/**
* @param pageCacheSize the pageCacheSize to set
*/
public void setPageCacheSize(int pageCacheSize){
this.pageCacheSize=pageCacheSize;
pageCache.setMaxCacheSize(pageCacheSize);
}
public boolean isTransient(){ public boolean isTransient(){
return false; return false;
@ -187,6 +223,7 @@ public class HashIndex implements Index{
} }
public void store(Object key,StoreEntry value) throws IOException{ public void store(Object key,StoreEntry value) throws IOException{
load();
HashEntry entry=new HashEntry(); HashEntry entry=new HashEntry();
entry.setKey((Comparable)key); entry.setKey((Comparable)key);
entry.setIndexOffset(value.getOffset()); entry.setIndexOffset(value.getOffset());
@ -194,16 +231,19 @@ public class HashIndex implements Index{
} }
public StoreEntry get(Object key) throws IOException{ public StoreEntry get(Object key) throws IOException{
load();
HashEntry entry=new HashEntry(); HashEntry entry=new HashEntry();
entry.setKey((Comparable)key); entry.setKey((Comparable)key);
HashEntry result=getBin(key).find(entry); HashEntry result=getBin(key).find(entry);
return result!=null?indexManager.getIndex(result.getIndexOffset()):null; return result!=null?indexManager.getIndex(result.getIndexOffset()):null;
} }
public void remove(Object key) throws IOException{ public StoreEntry remove(Object key) throws IOException{
load();
HashEntry entry=new HashEntry(); HashEntry entry=new HashEntry();
entry.setKey((Comparable)key); entry.setKey((Comparable)key);
getBin(key).remove(entry); HashEntry result = getBin(key).remove(entry);
return result!=null?indexManager.getIndex(result.getIndexOffset()):null;
} }
public boolean containsKey(Object key) throws IOException{ public boolean containsKey(Object key) throws IOException{
@ -228,11 +268,15 @@ public class HashIndex implements Index{
HashPage lookupPage(long pageId) throws IOException{ HashPage lookupPage(long pageId) throws IOException{
HashPage result=null; HashPage result=null;
if(pageId>=0){ if(pageId>=0){
result=getFullPage(pageId); result=getFromCache(pageId);
if(result!=null){ if(result==null){
if(result.isActive()){ result=getFullPage(pageId);
}else{ if(result!=null){
throw new IllegalStateException("Trying to access an inactive page: "+pageId); if(result.isActive()){
addToCache(result);
}else{
throw new IllegalStateException("Trying to access an inactive page: "+pageId);
}
} }
} }
} }
@ -251,10 +295,12 @@ public class HashIndex implements Index{
indexFile.seek(length); indexFile.seek(length);
indexFile.write(HashEntry.NOT_SET); indexFile.write(HashEntry.NOT_SET);
} }
addToCache(result);
return result; return result;
} }
void releasePage(HashPage page) throws IOException{ void releasePage(HashPage page) throws IOException{
removeFromCache(page);
page.reset(); page.reset();
page.setActive(false); page.setActive(false);
if(lastFree==null){ if(lastFree==null){
@ -349,6 +395,26 @@ public class HashIndex implements Index{
int i=indexFor(hash,bins.length); int i=indexFor(hash,bins.length);
return getBin(i); return getBin(i);
} }
private HashPage getFromCache(long pageId){
HashPage result=null;
if(enablePageCaching){
result=pageCache.get(pageId);
}
return result;
}
private void addToCache(HashPage page){
if(enablePageCaching){
pageCache.put(page.getId(),page);
}
}
private void removeFromCache(HashPage page){
if(enablePageCaching){
pageCache.remove(page.getId());
}
}
static int hash(Object x){ static int hash(Object x){
int h=x.hashCode(); int h=x.hashCode();

View File

@ -225,10 +225,11 @@ public class TreeIndex implements Index{
return result!=null?indexManager.getIndex(result.getIndexOffset()):null; return result!=null?indexManager.getIndex(result.getIndexOffset()):null;
} }
public void remove(Object key) throws IOException{ public StoreEntry remove(Object key) throws IOException{
TreeEntry entry=new TreeEntry(); TreeEntry entry=new TreeEntry();
entry.setKey((Comparable)key); entry.setKey((Comparable)key);
root.remove(entry); TreeEntry result = root.remove(entry);
return result!=null?indexManager.getIndex(result.getIndexOffset()):null;
} }
public boolean containsKey(Object key) throws IOException{ public boolean containsKey(Object key) throws IOException{

View File

@ -268,14 +268,16 @@ class TreePage{
return result; return result;
} }
void remove(TreeEntry entry) throws IOException{ TreeEntry remove(TreeEntry entry) throws IOException{
TreeEntry result = null;
if(isRoot()){ if(isRoot()){
if(!isEmpty()){ if(!isEmpty()){
doRemove(entry); result = doRemove(entry);
} }
}else{ }else{
throw new IllegalStateException("remove() should not be called on non root page"); throw new IllegalStateException("remove() should not be called on non root page");
} }
return result;
} }
private TreeEntry doInsert(Flavour flavour,TreeEntry newEntry) throws IOException{ private TreeEntry doInsert(Flavour flavour,TreeEntry newEntry) throws IOException{
@ -369,7 +371,8 @@ class TreePage{
return result; return result;
} }
private void doRemove(TreeEntry entry) throws IOException{ private TreeEntry doRemove(TreeEntry entry) throws IOException{
TreeEntry result = null;
TreePageEntry closest=findClosestEntry(entry); TreePageEntry closest=findClosestEntry(entry);
if(closest!=null){ if(closest!=null){
TreeEntry closestEntry=closest.getTreeEntry(); TreeEntry closestEntry=closest.getTreeEntry();
@ -377,7 +380,7 @@ class TreePage{
TreePage closestPage=closest.getTreePage(); TreePage closestPage=closest.getTreePage();
int cmp=closestEntry.compareTo(entry); int cmp=closestEntry.compareTo(entry);
if(cmp==0){ if(cmp==0){
TreeEntry result=closest.getTreeEntry(); result=closest.getTreeEntry();
int index=closest.getIndex(); int index=closest.getIndex();
removeTreeEntry(index); removeTreeEntry(index);
save(); save();
@ -388,6 +391,7 @@ class TreePage{
} }
} }
} }
return result;
} }
/** /**

View File

@ -20,7 +20,6 @@ package org.apache.activemq.store.kahadaptor;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import org.apache.activemq.kaha.Marshaller; import org.apache.activemq.kaha.Marshaller;
import org.apache.activemq.util.ByteSequence; import org.apache.activemq.util.ByteSequence;
import org.apache.activemq.wireformat.WireFormat; import org.apache.activemq.wireformat.WireFormat;

View File

@ -1,151 +1,102 @@
/** /**
* *
* Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* contributor license agreements. See the NOTICE file distributed with * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* this work for additional information regarding copyright ownership. * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* The ASF licenses this file to You under the Apache License, Version 2.0 * License. You may obtain a copy of the License at
* (the "License"); you may not use this file except in compliance with *
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* distributed under the License is distributed on an "AS IS" BASIS, * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * specific language governing permissions and limitations under the License.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.apache.activemq.store.kahadaptor; package org.apache.activemq.store.kahadaptor;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator;
import org.apache.activemq.broker.ConnectionContext; import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.Message; import org.apache.activemq.command.Message;
import org.apache.activemq.command.MessageAck; import org.apache.activemq.command.MessageAck;
import org.apache.activemq.command.MessageId; import org.apache.activemq.command.MessageId;
import org.apache.activemq.kaha.ListContainer; import org.apache.activemq.kaha.MapContainer;
import org.apache.activemq.kaha.StoreEntry; import org.apache.activemq.kaha.StoreEntry;
import org.apache.activemq.memory.UsageListener;
import org.apache.activemq.memory.UsageManager; import org.apache.activemq.memory.UsageManager;
import org.apache.activemq.store.MessageRecoveryListener; import org.apache.activemq.store.MessageRecoveryListener;
import org.apache.activemq.store.MessageStore; import org.apache.activemq.store.MessageStore;
import org.apache.activemq.util.LRUCache;
/** /**
* An implementation of {@link org.apache.activemq.store.MessageStore} which uses a JPS Container * An implementation of {@link org.apache.activemq.store.MessageStore} which uses a JPS Container
* *
* @version $Revision: 1.7 $ * @version $Revision: 1.7 $
*/ */
public class KahaMessageStore implements MessageStore, UsageListener{ public class KahaMessageStore implements MessageStore{
protected final ActiveMQDestination destination;
protected final ListContainer<Object> messageContainer;
protected StoreEntry batchEntry = null;
protected final LRUCache<MessageId, StoreEntry> cache;
protected UsageManager usageManager;
public KahaMessageStore(ListContainer<Object> container,ActiveMQDestination destination, int maximumCacheSize) throws IOException{ protected final ActiveMQDestination destination;
protected final MapContainer<MessageId,Message> messageContainer;
protected StoreEntry batchEntry=null;
public KahaMessageStore(MapContainer<MessageId,Message> container,ActiveMQDestination destination) throws IOException{
this.messageContainer=container; this.messageContainer=container;
this.destination=destination; this.destination=destination;
this.cache=new LRUCache<MessageId, StoreEntry>(maximumCacheSize,maximumCacheSize,0.75f,false);
// populate the cache
StoreEntry entry=messageContainer.getFirst();
int count = 0;
if(entry!=null){
do{
MessageId id = getMessageId(messageContainer.get(entry));
cache.put(id,entry);
entry = messageContainer.getNext(entry);
count++;
}while(entry!=null && count < maximumCacheSize);
}
} }
protected MessageId getMessageId(Object object) {
return ((Message)object).getMessageId();
}
public Object getId(){ protected MessageId getMessageId(Object object){
return ((Message)object).getMessageId();
}
public Object getId(){
return messageContainer.getId(); return messageContainer.getId();
} }
public synchronized void addMessage(ConnectionContext context,Message message) throws IOException{ public synchronized void addMessage(ConnectionContext context,Message message) throws IOException{
StoreEntry item=messageContainer.placeLast(message); messageContainer.put(message.getMessageId(),message);
// TODO: we should do the following but it is not need if the message is being added within a persistence // TODO: we should do the following but it is not need if the message is being added within a persistence
// transaction // transaction
// but since I can't tell if one is running right now.. I'll leave this out for now. // but since I can't tell if one is running right now.. I'll leave this out for now.
// if( message.isResponseRequired() ) { // if( message.isResponseRequired() ) {
// messageContainer.force(); // messageContainer.force();
// } // }
cache.put(message.getMessageId(),item);
} }
public synchronized Message getMessage(MessageId identity) throws IOException{ public synchronized Message getMessage(MessageId identity) throws IOException{
Message result=null; Message result=messageContainer.get(identity);
StoreEntry entry=cache.get(identity);
if(entry!=null){
entry = messageContainer.refresh(entry);
result = (Message)messageContainer.get(entry);
}else{
for (entry = messageContainer.getFirst();entry != null; entry = messageContainer.getNext(entry)) {
Message msg=(Message)messageContainer.get(entry);
if(msg.getMessageId().equals(identity)){
result=msg;
cache.put(identity,entry);
break;
}
}
}
return result; return result;
} }
protected void recover(MessageRecoveryListener listener, Object msg) throws Exception { protected void recover(MessageRecoveryListener listener,Object msg) throws Exception{
listener.recoverMessage((Message)msg); listener.recoverMessage((Message)msg);
} }
public void removeMessage(ConnectionContext context,MessageAck ack) throws IOException{ public void removeMessage(ConnectionContext context,MessageAck ack) throws IOException{
removeMessage(ack.getLastMessageId()); removeMessage(ack.getLastMessageId());
} }
public synchronized void removeMessage(MessageId msgId) throws IOException{ public synchronized void removeMessage(MessageId msgId) throws IOException{
StoreEntry entry=cache.remove(msgId); messageContainer.remove(msgId);
if(entry!=null){ if(messageContainer.isEmpty()){
entry = messageContainer.refresh(entry);
messageContainer.remove(entry);
}else{
for (entry = messageContainer.getFirst();entry != null; entry = messageContainer.getNext(entry)) {
MessageId id=getMessageId(messageContainer.get(entry));
if(id.equals(msgId)){
messageContainer.remove(entry);
break;
}
}
}
if (messageContainer.isEmpty()) {
resetBatching(); resetBatching();
} }
} }
public synchronized void recover(MessageRecoveryListener listener) throws Exception{ public synchronized void recover(MessageRecoveryListener listener) throws Exception{
for(Iterator iter=messageContainer.iterator();iter.hasNext();){ for(StoreEntry entry=messageContainer.getFirst();entry!=null;entry=messageContainer.getNext(entry)){
recover(listener, iter.next()); Message msg=(Message)messageContainer.getValue(entry);
recover(listener,msg);
} }
listener.finished(); listener.finished();
} }
public void start() { public void start(){
if( this.usageManager != null )
this.usageManager.addUsageListener(this);
} }
public void stop() { public void stop(){
if( this.usageManager != null )
this.usageManager.removeUsageListener(this);
} }
public synchronized void removeAllMessages(ConnectionContext context) throws IOException{ public synchronized void removeAllMessages(ConnectionContext context) throws IOException{
messageContainer.clear(); messageContainer.clear();
cache.clear();
} }
public ActiveMQDestination getDestination(){ public ActiveMQDestination getDestination(){
@ -154,14 +105,12 @@ public class KahaMessageStore implements MessageStore, UsageListener{
public synchronized void delete(){ public synchronized void delete(){
messageContainer.clear(); messageContainer.clear();
cache.clear();
} }
/** /**
* @param usageManager The UsageManager that is controlling the destination's memory usage. * @param usageManager The UsageManager that is controlling the destination's memory usage.
*/ */
public void setUsageManager(UsageManager usageManager) { public void setUsageManager(UsageManager usageManager) {
this.usageManager = usageManager;
} }
/** /**
@ -169,7 +118,7 @@ public class KahaMessageStore implements MessageStore, UsageListener{
* @see org.apache.activemq.store.MessageStore#getMessageCount() * @see org.apache.activemq.store.MessageStore#getMessageCount()
*/ */
public int getMessageCount(){ public int getMessageCount(){
return messageContainer.size(); return messageContainer.size();
} }
/** /**
@ -187,30 +136,30 @@ public class KahaMessageStore implements MessageStore, UsageListener{
* @param maxReturned * @param maxReturned
* @param listener * @param listener
* @throws Exception * @throws Exception
* @see org.apache.activemq.store.MessageStore#recoverNextMessages(org.apache.activemq.command.MessageId, int, org.apache.activemq.store.MessageRecoveryListener) * @see org.apache.activemq.store.MessageStore#recoverNextMessages(org.apache.activemq.command.MessageId, int,
* org.apache.activemq.store.MessageRecoveryListener)
*/ */
public synchronized void recoverNextMessages(int maxReturned,MessageRecoveryListener listener) throws Exception{ public synchronized void recoverNextMessages(int maxReturned,MessageRecoveryListener listener) throws Exception{
StoreEntry entry = batchEntry; StoreEntry entry=batchEntry;
if (entry == null) { if(entry==null){
entry= messageContainer.getFirst(); entry=messageContainer.getFirst();
}else { }else{
entry=messageContainer.refresh(entry); entry=messageContainer.refresh(entry);
entry=messageContainer.getNext(entry); entry=messageContainer.getNext(entry);
} }
if(entry!=null){ if(entry!=null){
int count = 0; int count=0;
do{ do{
Object msg=messageContainer.get(entry); Object msg=messageContainer.getValue(entry);
if(msg!=null){ if(msg!=null){
recover(listener, msg); recover(listener,msg);
count++; count++;
} }
batchEntry = entry; batchEntry=entry;
entry=messageContainer.getNext(entry); entry=messageContainer.getNext(entry);
}while(entry!=null&&count<maxReturned&&listener.hasSpace()); }while(entry!=null&&count<maxReturned&&listener.hasSpace());
} }
listener.finished(); listener.finished();
} }
/** /**
@ -218,27 +167,15 @@ public class KahaMessageStore implements MessageStore, UsageListener{
* @see org.apache.activemq.store.MessageStore#resetBatching(org.apache.activemq.command.MessageId) * @see org.apache.activemq.store.MessageStore#resetBatching(org.apache.activemq.command.MessageId)
*/ */
public void resetBatching(){ public void resetBatching(){
batchEntry = null; batchEntry=null;
} }
/** /**
* @return true if the store supports cursors * @return true if the store supports cursors
*/ */
public boolean isSupportForCursors() { public boolean isSupportForCursors(){
return true; return true;
} }
/**
* @param memoryManager
* @param oldPercentUsage
* @param newPercentUsage
* @see org.apache.activemq.memory.UsageListener#onMemoryUseChanged(org.apache.activemq.memory.UsageManager, int, int)
*/
public synchronized void onMemoryUseChanged(UsageManager memoryManager,int oldPercentUsage,int newPercentUsage){
if(newPercentUsage==100){
cache.clear();
}
}
} }

View File

@ -25,9 +25,13 @@ import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic; import org.apache.activemq.command.ActiveMQTopic;
import org.apache.activemq.command.Command;
import org.apache.activemq.command.Message;
import org.apache.activemq.command.MessageId;
import org.apache.activemq.kaha.ListContainer; import org.apache.activemq.kaha.ListContainer;
import org.apache.activemq.kaha.MapContainer; import org.apache.activemq.kaha.MapContainer;
import org.apache.activemq.kaha.Marshaller; import org.apache.activemq.kaha.Marshaller;
import org.apache.activemq.kaha.MessageMarshaller;
import org.apache.activemq.kaha.Store; import org.apache.activemq.kaha.Store;
import org.apache.activemq.kaha.StoreFactory; import org.apache.activemq.kaha.StoreFactory;
import org.apache.activemq.kaha.StringMarshaller; import org.apache.activemq.kaha.StringMarshaller;
@ -56,7 +60,7 @@ public class KahaPersistenceAdapter implements PersistenceAdapter{
ConcurrentHashMap<ActiveMQDestination, MessageStore> messageStores=new ConcurrentHashMap<ActiveMQDestination, MessageStore>(); ConcurrentHashMap<ActiveMQDestination, MessageStore> messageStores=new ConcurrentHashMap<ActiveMQDestination, MessageStore>();
protected OpenWireFormat wireFormat=new OpenWireFormat(); protected OpenWireFormat wireFormat=new OpenWireFormat();
private long maxDataFileLength=32*1024*1024; private long maxDataFileLength=32*1024*1024;
protected int maximumDestinationCacheSize=10000;
private File dir; private File dir;
private Store theStore; private Store theStore;
@ -89,7 +93,7 @@ public class KahaPersistenceAdapter implements PersistenceAdapter{
public synchronized MessageStore createQueueMessageStore(ActiveMQQueue destination) throws IOException{ public synchronized MessageStore createQueueMessageStore(ActiveMQQueue destination) throws IOException{
MessageStore rc=queues.get(destination); MessageStore rc=queues.get(destination);
if(rc==null){ if(rc==null){
rc=new KahaMessageStore(getListContainer(destination,"queue-data"),destination,maximumDestinationCacheSize); rc=new KahaMessageStore(getMapContainer(destination,"queue-data"),destination);
messageStores.put(destination,rc); messageStores.put(destination,rc);
if(transactionStore!=null){ if(transactionStore!=null){
rc=transactionStore.proxy(rc); rc=transactionStore.proxy(rc);
@ -103,11 +107,11 @@ public class KahaPersistenceAdapter implements PersistenceAdapter{
TopicMessageStore rc=topics.get(destination); TopicMessageStore rc=topics.get(destination);
if(rc==null){ if(rc==null){
Store store=getStore(); Store store=getStore();
ListContainer messageContainer=getListContainer(destination,"topic-data"); MapContainer messageContainer=getMapContainer(destination,"topic-data");
MapContainer subsContainer=getMapContainer(destination.toString()+"-Subscriptions","topic-subs"); MapContainer subsContainer=getSubsMapContainer(destination.toString()+"-Subscriptions","topic-subs");
ListContainer ackContainer=store.getListContainer(destination.toString(),"topic-acks"); ListContainer<TopicSubAck> ackContainer=store.getListContainer(destination.toString(),"topic-acks");
ackContainer.setMarshaller(new TopicSubAckMarshaller()); ackContainer.setMarshaller(new TopicSubAckMarshaller());
rc=new KahaTopicMessageStore(store,messageContainer,ackContainer,subsContainer,destination,maximumDestinationCacheSize); rc=new KahaTopicMessageStore(store,messageContainer,ackContainer,subsContainer,destination);
messageStores.put(destination,rc); messageStores.put(destination,rc);
if(transactionStore!=null){ if(transactionStore!=null){
rc=transactionStore.proxy(rc); rc=transactionStore.proxy(rc);
@ -171,11 +175,20 @@ public class KahaPersistenceAdapter implements PersistenceAdapter{
} }
} }
protected MapContainer<String, Object> getMapContainer(Object id,String containerName) throws IOException{ protected MapContainer<MessageId,Message> getMapContainer(Object id,String containerName) throws IOException{
Store store=getStore();
MapContainer<MessageId, Message> container=store.getMapContainer(id,containerName);
container.setKeyMarshaller(new MessageIdMarshaller());
container.setValueMarshaller(new MessageMarshaller(wireFormat));
container.load();
return container;
}
protected MapContainer<String,Object> getSubsMapContainer(Object id,String containerName) throws IOException{
Store store=getStore(); Store store=getStore();
MapContainer<String, Object> container=store.getMapContainer(id,containerName); MapContainer<String, Object> container=store.getMapContainer(id,containerName);
container.setKeyMarshaller(new StringMarshaller()); container.setKeyMarshaller(Store.StringMarshaller);
container.setValueMarshaller(new CommandMarshaller(wireFormat)); container.setValueMarshaller(createMessageMarshaller());
container.load(); container.load();
return container; return container;
} }
@ -214,21 +227,7 @@ public class KahaPersistenceAdapter implements PersistenceAdapter{
this.maxDataFileLength=maxDataFileLength; this.maxDataFileLength=maxDataFileLength;
} }
/**
* @return the maximumDestinationCacheSize
*/
public int getMaximumDestinationCacheSize(){
return this.maximumDestinationCacheSize;
}
/**
* @param maximumDestinationCacheSize the maximumDestinationCacheSize to set
*/
public void setMaximumDestinationCacheSize(int maximumDestinationCacheSize){
this.maximumDestinationCacheSize=maximumDestinationCacheSize;
}
protected synchronized Store getStore() throws IOException{ protected synchronized Store getStore() throws IOException{
if(theStore==null){ if(theStore==null){

View File

@ -19,71 +19,96 @@ package org.apache.activemq.store.kahadaptor;
import java.io.IOException; import java.io.IOException;
import java.util.Set; import java.util.Set;
import org.apache.activemq.broker.ConnectionContext; import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.Message; import org.apache.activemq.command.Message;
import org.apache.activemq.command.MessageAck;
import org.apache.activemq.command.MessageId; import org.apache.activemq.command.MessageId;
import org.apache.activemq.kaha.ListContainer;
import org.apache.activemq.kaha.MapContainer; import org.apache.activemq.kaha.MapContainer;
import org.apache.activemq.kaha.StoreEntry; import org.apache.activemq.kaha.StoreEntry;
import org.apache.activemq.memory.UsageManager;
import org.apache.activemq.store.MessageRecoveryListener; import org.apache.activemq.store.MessageRecoveryListener;
import org.apache.activemq.store.ReferenceStore; import org.apache.activemq.store.ReferenceStore;
import org.apache.activemq.store.kahadaptor.KahaReferenceStoreAdapter.ReferenceRecord;
public class KahaReferenceStore extends KahaMessageStore implements ReferenceStore { public class KahaReferenceStore implements ReferenceStore {
private final MapContainer<Integer, Integer> fileReferences; protected final ActiveMQDestination destination;
protected final MapContainer<MessageId,ReferenceRecord> messageContainer;
protected StoreEntry batchEntry=null;
public KahaReferenceStore(ListContainer container, ActiveMQDestination destination, int maximumCacheSize, MapContainer<Integer, Integer> fileReferences) throws IOException { public KahaReferenceStore(MapContainer container, ActiveMQDestination destination) throws IOException {
super(container, destination, maximumCacheSize); this.messageContainer=container;
this.fileReferences = fileReferences; this.destination=destination;
} }
public void start(){
}
@Override public void stop(){
}
protected MessageId getMessageId(Object object) { protected MessageId getMessageId(Object object) {
return new MessageId(((ReferenceRecord)object).messageId); return new MessageId(((ReferenceRecord)object).messageId);
} }
@Override
public synchronized void addMessage(ConnectionContext context, Message message) throws IOException { public synchronized void addMessage(ConnectionContext context, Message message) throws IOException {
throw new RuntimeException("Use addMessageReference instead"); throw new RuntimeException("Use addMessageReference instead");
} }
@Override
public synchronized Message getMessage(MessageId identity) throws IOException { public synchronized Message getMessage(MessageId identity) throws IOException {
throw new RuntimeException("Use addMessageReference instead"); throw new RuntimeException("Use addMessageReference instead");
} }
@Override
protected void recover(MessageRecoveryListener listener, Object msg) throws Exception { protected void recover(MessageRecoveryListener listener, Object msg) throws Exception {
ReferenceRecord record = (ReferenceRecord) msg; ReferenceRecord record = (ReferenceRecord) msg;
listener.recoverMessageReference(new MessageId(record.messageId)); listener.recoverMessageReference(new MessageId(record.messageId));
} }
public synchronized void recover(MessageRecoveryListener listener) throws Exception{
for(StoreEntry entry=messageContainer.getFirst();entry!=null;entry=messageContainer.getNext(entry)){
ReferenceRecord record=messageContainer.getValue(entry);
recover(listener,new MessageId(record.messageId));
}
listener.finished();
}
public synchronized void recoverNextMessages(int maxReturned,MessageRecoveryListener listener) throws Exception{
StoreEntry entry=batchEntry;
if(entry==null){
entry=messageContainer.getFirst();
}else{
entry=messageContainer.refresh(entry);
entry=messageContainer.getNext(entry);
}
if(entry!=null){
int count=0;
do{
Object msg=messageContainer.getValue(entry);
if(msg!=null){
recover(listener,msg);
count++;
}
batchEntry=entry;
entry=messageContainer.getNext(entry);
}while(entry!=null&&count<maxReturned&&listener.hasSpace());
}
listener.finished();
}
public void addMessageReference(ConnectionContext context, MessageId messageId, ReferenceData data) throws IOException { public void addMessageReference(ConnectionContext context, MessageId messageId, ReferenceData data) throws IOException {
ReferenceRecord record = new ReferenceRecord(messageId.toString(), data); ReferenceRecord record = new ReferenceRecord(messageId.toString(), data);
StoreEntry item=messageContainer.placeLast(record); messageContainer.put(messageId,record);
cache.put(messageId,item);
} }
public ReferenceData getMessageReference(MessageId identity) throws IOException { public ReferenceData getMessageReference(MessageId identity) throws IOException {
ReferenceRecord result=null; ReferenceRecord result=messageContainer.get(identity);
StoreEntry entry=cache.get(identity);
if(entry!=null){
entry = messageContainer.refresh(entry);
result = (ReferenceRecord)messageContainer.get(entry);
}else{
for (entry = messageContainer.getFirst();entry != null; entry = messageContainer.getNext(entry)) {
ReferenceRecord msg=(ReferenceRecord)messageContainer.get(entry);
if(msg.messageId.equals(identity.toString())){
result=msg;
cache.put(identity,entry);
break;
}
}
}
if( result == null ) if( result == null )
return null; return null;
return result.data; return result.data;
@ -95,6 +120,44 @@ public class KahaReferenceStore extends KahaMessageStore implements ReferenceSto
rc.add(msg.data.getFileId()); rc.add(msg.data.getFileId());
} }
} }
public void removeMessage(ConnectionContext context,MessageAck ack) throws IOException{
removeMessage(ack.getLastMessageId());
}
public synchronized void removeMessage(MessageId msgId) throws IOException{
messageContainer.remove(msgId);
if(messageContainer.isEmpty()){
resetBatching();
}
}
public synchronized void removeAllMessages(ConnectionContext context) throws IOException{
messageContainer.clear();
}
public ActiveMQDestination getDestination(){
return destination;
}
public synchronized void delete(){
messageContainer.clear();
}
public void resetBatching(){
batchEntry=null;
}
public int getMessageCount(){
return messageContainer.size();
}
public void setUsageManager(UsageManager usageManager) {
}
public boolean isSupportForCursors(){
return true;
}
} }

View File

@ -27,9 +27,12 @@ import java.util.Set;
import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.ActiveMQQueue; import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic; import org.apache.activemq.command.ActiveMQTopic;
import org.apache.activemq.command.Message;
import org.apache.activemq.command.MessageId;
import org.apache.activemq.kaha.ListContainer; import org.apache.activemq.kaha.ListContainer;
import org.apache.activemq.kaha.MapContainer; import org.apache.activemq.kaha.MapContainer;
import org.apache.activemq.kaha.Marshaller; import org.apache.activemq.kaha.Marshaller;
import org.apache.activemq.kaha.MessageMarshaller;
import org.apache.activemq.kaha.Store; import org.apache.activemq.kaha.Store;
import org.apache.activemq.store.MessageStore; import org.apache.activemq.store.MessageStore;
import org.apache.activemq.store.ReferenceStore; import org.apache.activemq.store.ReferenceStore;
@ -65,44 +68,12 @@ public class KahaReferenceStoreAdapter extends KahaPersistenceAdapter implements
fileReferences.load(); fileReferences.load();
} }
public static class ReferenceRecord {
public String messageId;
public ReferenceData data;
public ReferenceRecord() {
}
public ReferenceRecord(String messageId, ReferenceData data) {
this.messageId = messageId;
this.data = data;
}
}
protected Marshaller<Object> createMessageMarshaller() {
return new Marshaller<Object>() {
public void writePayload(Object object,DataOutput dataOut) throws IOException{
ReferenceRecord rr = (ReferenceRecord) object;
dataOut.writeUTF(rr.messageId);
dataOut.writeInt(rr.data.getFileId());
dataOut.writeInt(rr.data.getOffset());
dataOut.writeLong(rr.data.getExpiration());
}
public Object readPayload(DataInput dataIn) throws IOException{
ReferenceRecord rr = new ReferenceRecord();
rr.messageId = dataIn.readUTF();
rr.data = new ReferenceData();
rr.data.setFileId(dataIn.readInt());
rr.data.setOffset(dataIn.readInt());
rr.data.setExpiration(dataIn.readLong());
return rr;
}
};
}
public ReferenceStore createQueueReferenceStore(ActiveMQQueue destination) throws IOException { public ReferenceStore createQueueReferenceStore(ActiveMQQueue destination) throws IOException {
ReferenceStore rc=(ReferenceStore)queues.get(destination); ReferenceStore rc=(ReferenceStore)queues.get(destination);
if(rc==null){ if(rc==null){
rc=new KahaReferenceStore(getListContainer(destination,"queue-data"),destination,maximumDestinationCacheSize, fileReferences); rc=new KahaReferenceStore(getMapReferenceContainer(destination,"queue-data"),destination);
messageStores.put(destination,rc); messageStores.put(destination,rc);
// if(transactionStore!=null){ // if(transactionStore!=null){
// rc=transactionStore.proxy(rc); // rc=transactionStore.proxy(rc);
@ -116,11 +87,11 @@ public class KahaReferenceStoreAdapter extends KahaPersistenceAdapter implements
TopicReferenceStore rc=(TopicReferenceStore)topics.get(destination); TopicReferenceStore rc=(TopicReferenceStore)topics.get(destination);
if(rc==null){ if(rc==null){
Store store=getStore(); Store store=getStore();
ListContainer messageContainer=getListContainer(destination,"topic-data"); MapContainer messageContainer=getMapReferenceContainer(destination,"topic-data");
MapContainer subsContainer=getMapContainer(destination.toString()+"-Subscriptions","topic-subs"); MapContainer subsContainer=getSubsMapContainer(destination.toString()+"-Subscriptions","topic-subs");
ListContainer ackContainer=store.getListContainer(destination.toString(),"topic-acks"); ListContainer ackContainer=store.getListContainer(destination.toString(),"topic-acks");
ackContainer.setMarshaller(new TopicSubAckMarshaller()); ackContainer.setMarshaller(new TopicSubAckMarshaller());
rc=new KahaTopicReferenceStore(store,messageContainer,ackContainer,subsContainer,destination,maximumDestinationCacheSize, fileReferences); rc=new KahaTopicReferenceStore(store,messageContainer,ackContainer,subsContainer,destination);
messageStores.put(destination,rc); messageStores.put(destination,rc);
// if(transactionStore!=null){ // if(transactionStore!=null){
// rc=transactionStore.proxy(rc); // rc=transactionStore.proxy(rc);
@ -148,6 +119,16 @@ public class KahaReferenceStoreAdapter extends KahaPersistenceAdapter implements
return rc; return rc;
} }
protected MapContainer<MessageId,ReferenceRecord> getMapReferenceContainer(Object id,String containerName) throws IOException{
Store store=getStore();
MapContainer<MessageId, ReferenceRecord> container=store.getMapContainer(id,containerName);
container.setKeyMarshaller(new MessageIdMarshaller());
container.setValueMarshaller(new ReferenceRecordMarshaller());
container.load();
return container;
}
} }

View File

@ -30,21 +30,20 @@ import org.apache.activemq.kaha.Store;
import org.apache.activemq.kaha.StoreEntry; import org.apache.activemq.kaha.StoreEntry;
import org.apache.activemq.store.MessageRecoveryListener; import org.apache.activemq.store.MessageRecoveryListener;
import org.apache.activemq.store.TopicMessageStore; import org.apache.activemq.store.TopicMessageStore;
import org.apache.activemq.store.kahadaptor.KahaReferenceStoreAdapter.ReferenceRecord;
/** /**
* @version $Revision: 1.5 $ * @version $Revision: 1.5 $
*/ */
public class KahaTopicMessageStore extends KahaMessageStore implements TopicMessageStore{ public class KahaTopicMessageStore extends KahaMessageStore implements TopicMessageStore{
protected ListContainer ackContainer; protected ListContainer<TopicSubAck> ackContainer;
private Map subscriberContainer; private Map subscriberContainer;
private Store store; private Store store;
protected Map subscriberMessages=new ConcurrentHashMap(); protected Map subscriberMessages=new ConcurrentHashMap();
public KahaTopicMessageStore(Store store,ListContainer messageContainer,ListContainer ackContainer, public KahaTopicMessageStore(Store store,MapContainer messageContainer,ListContainer<TopicSubAck> ackContainer,
MapContainer subsContainer,ActiveMQDestination destination,int maximumCacheSize) throws IOException{ MapContainer subsContainer,ActiveMQDestination destination) throws IOException{
super(messageContainer,destination,maximumCacheSize); super(messageContainer,destination);
this.store=store; this.store=store;
this.ackContainer=ackContainer; this.ackContainer=ackContainer;
subscriberContainer=subsContainer; subscriberContainer=subsContainer;
@ -59,7 +58,7 @@ public class KahaTopicMessageStore extends KahaMessageStore implements TopicMess
public synchronized void addMessage(ConnectionContext context,Message message) throws IOException{ public synchronized void addMessage(ConnectionContext context,Message message) throws IOException{
int subscriberCount=subscriberMessages.size(); int subscriberCount=subscriberMessages.size();
if(subscriberCount>0){ if(subscriberCount>0){
StoreEntry messageEntry=messageContainer.placeLast(message); StoreEntry messageEntry=messageContainer.place(message.getMessageId(),message);
TopicSubAck tsa=new TopicSubAck(); TopicSubAck tsa=new TopicSubAck();
tsa.setCount(subscriberCount); tsa.setCount(subscriberCount);
tsa.setMessageEntry(messageEntry); tsa.setMessageEntry(messageEntry);
@ -166,7 +165,7 @@ public class KahaTopicMessageStore extends KahaMessageStore implements TopicMess
if(entry!=null){ if(entry!=null){
do{ do{
ConsumerMessageRef consumerRef=container.get(entry); ConsumerMessageRef consumerRef=container.get(entry);
Object msg=messageContainer.get(consumerRef.getMessageEntry()); Object msg=messageContainer.getValue(consumerRef.getMessageEntry());
if(msg!=null){ if(msg!=null){
recover(listener, msg); recover(listener, msg);
count++; count++;
@ -227,7 +226,7 @@ public class KahaTopicMessageStore extends KahaMessageStore implements TopicMess
public int getMessageCount(String clientId,String subscriberName) throws IOException{ public int getMessageCount(String clientId,String subscriberName) throws IOException{
String key=getSubscriptionKey(clientId,subscriberName); String key=getSubscriptionKey(clientId,subscriberName);
TopicSubContainer container=(TopicSubContainer)subscriberMessages.get(key); TopicSubContainer container=(TopicSubContainer)subscriberMessages.get(key);
return container.size(); return container != null ? container.size() : 0;
} }
/** /**

View File

@ -1,75 +1,80 @@
/** /**
* *
* Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* contributor license agreements. See the NOTICE file distributed with * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* this work for additional information regarding copyright ownership. * to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* The ASF licenses this file to You under the Apache License, Version 2.0 * License. You may obtain a copy of the License at
* (the "License"); you may not use this file except in compliance with *
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* distributed under the License is distributed on an "AS IS" BASIS, * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * specific language governing permissions and limitations under the License.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.apache.activemq.store.kahadaptor; package org.apache.activemq.store.kahadaptor;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.activemq.broker.ConnectionContext; import org.apache.activemq.broker.ConnectionContext;
import org.apache.activemq.command.ActiveMQDestination; import org.apache.activemq.command.ActiveMQDestination;
import org.apache.activemq.command.Message; import org.apache.activemq.command.Message;
import org.apache.activemq.command.MessageId; import org.apache.activemq.command.MessageId;
import org.apache.activemq.command.SubscriptionInfo;
import org.apache.activemq.kaha.ListContainer; import org.apache.activemq.kaha.ListContainer;
import org.apache.activemq.kaha.MapContainer; import org.apache.activemq.kaha.MapContainer;
import org.apache.activemq.kaha.Marshaller;
import org.apache.activemq.kaha.Store; import org.apache.activemq.kaha.Store;
import org.apache.activemq.kaha.StoreEntry; import org.apache.activemq.kaha.StoreEntry;
import org.apache.activemq.store.MessageRecoveryListener; import org.apache.activemq.store.MessageRecoveryListener;
import org.apache.activemq.store.TopicReferenceStore; import org.apache.activemq.store.TopicReferenceStore;
import org.apache.activemq.store.kahadaptor.KahaReferenceStoreAdapter.ReferenceRecord;
public class KahaTopicReferenceStore extends KahaTopicMessageStore implements TopicReferenceStore { public class KahaTopicReferenceStore extends KahaReferenceStore implements TopicReferenceStore{
private final MapContainer<Integer, Integer> fileReferences; protected ListContainer<TopicSubAck> ackContainer;
private Map subscriberContainer;
private Store store;
protected Map subscriberMessages=new ConcurrentHashMap();
public KahaTopicReferenceStore(Store store, ListContainer messageContainer, ListContainer ackContainer, MapContainer subsContainer, ActiveMQDestination destination, int maximumCacheSize, MapContainer<Integer, Integer> fileReferences) throws IOException { public KahaTopicReferenceStore(Store store,MapContainer messageContainer,ListContainer ackContainer,
super(store, messageContainer, ackContainer, subsContainer, destination, maximumCacheSize); MapContainer subsContainer,ActiveMQDestination destination) throws IOException{
this.fileReferences = fileReferences; super(messageContainer,destination);
} this.store=store;
this.ackContainer=ackContainer;
subscriberContainer=subsContainer;
// load all the Ack containers
for(Iterator i=subscriberContainer.keySet().iterator();i.hasNext();){
Object key=i.next();
addSubscriberMessageContainer(key);
}
}
@Override protected MessageId getMessageId(Object object){
protected MessageId getMessageId(Object object) { return new MessageId(((ReferenceRecord)object).messageId);
return new MessageId(((ReferenceRecord)object).messageId); }
}
@Override public synchronized void addMessage(ConnectionContext context,Message message) throws IOException{
public synchronized void addMessage(ConnectionContext context, Message message) throws IOException { throw new RuntimeException("Use addMessageReference instead");
throw new RuntimeException("Use addMessageReference instead"); }
}
@Override
public synchronized Message getMessage(MessageId identity) throws IOException {
throw new RuntimeException("Use addMessageReference instead");
}
@Override
protected void recover(MessageRecoveryListener listener, Object msg) throws Exception {
ReferenceRecord record = (ReferenceRecord) msg;
listener.recoverMessageReference(new MessageId(record.messageId));
}
public void addMessageReference(ConnectionContext context, MessageId messageId, ReferenceData data) throws IOException { public synchronized Message getMessage(MessageId identity) throws IOException{
throw new RuntimeException("Use addMessageReference instead");
ReferenceRecord record = new ReferenceRecord(messageId.toString(), data); }
protected void recover(MessageRecoveryListener listener,Object msg) throws Exception{
ReferenceRecord record=(ReferenceRecord)msg;
listener.recoverMessageReference(new MessageId(record.messageId));
}
public void addMessageReference(ConnectionContext context,MessageId messageId,ReferenceData data)
throws IOException{
ReferenceRecord record=new ReferenceRecord(messageId.toString(),data);
int subscriberCount=subscriberMessages.size(); int subscriberCount=subscriberMessages.size();
if(subscriberCount>0){ if(subscriberCount>0){
StoreEntry messageEntry=messageContainer.placeLast(record); StoreEntry messageEntry=messageContainer.place(messageId,record);
TopicSubAck tsa=new TopicSubAck(); TopicSubAck tsa=new TopicSubAck();
tsa.setCount(subscriberCount); tsa.setCount(subscriberCount);
tsa.setMessageEntry(messageEntry); tsa.setMessageEntry(messageEntry);
@ -82,40 +87,181 @@ public class KahaTopicReferenceStore extends KahaTopicMessageStore implements To
container.add(ref); container.add(ref);
} }
} }
}
}
public ReferenceData getMessageReference(MessageId identity) throws IOException { public ReferenceData getMessageReference(MessageId identity) throws IOException{
ReferenceRecord result=messageContainer.get(identity);
ReferenceRecord result=null; if(result==null)
StoreEntry entry=(StoreEntry)cache.get(identity); return null;
if(entry!=null){ return result.data;
entry = messageContainer.refresh(entry); }
result = (ReferenceRecord)messageContainer.get(entry);
}else{ public void addReferenceFileIdsInUse(Set<Integer> rc){
for (entry = messageContainer.getFirst();entry != null; entry = messageContainer.getNext(entry)) { for(StoreEntry entry=ackContainer.getFirst();entry!=null;entry=ackContainer.getNext(entry)){
ReferenceRecord msg=(ReferenceRecord)messageContainer.get(entry); TopicSubAck subAck=(TopicSubAck)ackContainer.get(entry);
if(msg.messageId.equals(identity.toString())){ if(subAck.getCount()>0){
result=msg; ReferenceRecord rr=(ReferenceRecord)messageContainer.get(subAck.getMessageEntry());
cache.put(identity,entry); rc.add(rr.data.getFileId());
break; }
}
}
protected ListContainer addSubscriberMessageContainer(Object key) throws IOException{
ListContainer container=store.getListContainer(key,"topic-subs");
Marshaller marshaller=new ConsumerMessageRefMarshaller();
container.setMarshaller(marshaller);
TopicSubContainer tsc=new TopicSubContainer(container);
subscriberMessages.put(key,tsc);
return container;
}
public synchronized void acknowledge(ConnectionContext context,String clientId,String subscriptionName,
MessageId messageId) throws IOException{
String subcriberId=getSubscriptionKey(clientId,subscriptionName);
TopicSubContainer container=(TopicSubContainer)subscriberMessages.get(subcriberId);
if(container!=null){
ConsumerMessageRef ref=container.remove();
if(container.isEmpty()){
container.reset();
}
if(ref!=null){
TopicSubAck tsa=(TopicSubAck)ackContainer.get(ref.getAckEntry());
if(tsa!=null){
if(tsa.decrementCount()<=0){
ackContainer.remove(ref.getAckEntry());
messageContainer.remove(tsa.getMessageEntry());
}else{
ackContainer.update(ref.getAckEntry(),tsa);
}
} }
} }
} }
if( result == null ) }
return null;
return result.data;
}
public void addReferenceFileIdsInUse(Set<Integer> rc) { public synchronized void addSubsciption(String clientId,String subscriptionName,String selector,boolean retroactive)
for (StoreEntry entry = ackContainer.getFirst();entry != null; entry = ackContainer.getNext(entry)) { throws IOException{
TopicSubAck subAck=(TopicSubAck)ackContainer.get(entry); SubscriptionInfo info=new SubscriptionInfo();
if( subAck.getCount() > 0 ) { info.setDestination(destination);
ReferenceRecord rr = (ReferenceRecord)messageContainer.get(subAck.getMessageEntry()); info.setClientId(clientId);
rc.add(rr.data.getFileId()); info.setSelector(selector);
} info.setSubcriptionName(subscriptionName);
String key=getSubscriptionKey(clientId,subscriptionName);
// if already exists - won't add it again as it causes data files
// to hang around
if(!subscriberContainer.containsKey(key)){
subscriberContainer.put(key,info);
} }
} // add the subscriber
ListContainer container=addSubscriberMessageContainer(key);
if(retroactive){
for(StoreEntry entry=ackContainer.getFirst();entry!=null;){
TopicSubAck tsa=(TopicSubAck)ackContainer.get(entry);
ConsumerMessageRef ref=new ConsumerMessageRef();
ref.setAckEntry(entry);
ref.setMessageEntry(tsa.getMessageEntry());
container.add(ref);
}
}
}
public synchronized void deleteSubscription(String clientId,String subscriptionName) throws IOException{
String key=getSubscriptionKey(clientId,subscriptionName);
removeSubscriberMessageContainer(key);
}
public SubscriptionInfo[] getAllSubscriptions() throws IOException{
return (SubscriptionInfo[])subscriberContainer.values().toArray(
new SubscriptionInfo[subscriberContainer.size()]);
}
public int getMessageCount(String clientId,String subscriberName) throws IOException{
String key=getSubscriptionKey(clientId,subscriberName);
TopicSubContainer container=(TopicSubContainer)subscriberMessages.get(key);
return container.size();
}
public SubscriptionInfo lookupSubscription(String clientId,String subscriptionName) throws IOException{
return (SubscriptionInfo)subscriberContainer.get(getSubscriptionKey(clientId,subscriptionName));
}
public void recoverNextMessages(String clientId,String subscriptionName,int maxReturned,
MessageRecoveryListener listener) throws Exception{
String key=getSubscriptionKey(clientId,subscriptionName);
TopicSubContainer container=(TopicSubContainer)subscriberMessages.get(key);
if(container!=null){
int count=0;
StoreEntry entry=container.getBatchEntry();
if(entry==null){
entry=container.getEntry();
}else{
entry=container.refreshEntry(entry);
if(entry!=null){
entry=container.getNextEntry(entry);
}
}
if(entry!=null){
do{
ConsumerMessageRef consumerRef=container.get(entry);
Object msg=messageContainer.getValue(consumerRef.getMessageEntry());
if(msg!=null){
recover(listener,msg);
count++;
}
container.setBatchEntry(entry);
entry=container.getNextEntry(entry);
}while(entry!=null&&count<maxReturned&&listener.hasSpace());
}
}
listener.finished();
}
public void recoverSubscription(String clientId,String subscriptionName,MessageRecoveryListener listener)
throws Exception{
String key=getSubscriptionKey(clientId,subscriptionName);
TopicSubContainer container=(TopicSubContainer)subscriberMessages.get(key);
if(container!=null){
for(Iterator i=container.iterator();i.hasNext();){
ConsumerMessageRef ref=(ConsumerMessageRef)i.next();
Object msg=messageContainer.get(ref.getMessageEntry());
if(msg!=null){
recover(listener,msg);
}
}
}
listener.finished();
}
public synchronized void resetBatching(String clientId,String subscriptionName){
String key=getSubscriptionKey(clientId,subscriptionName);
TopicSubContainer topicSubContainer=(TopicSubContainer)subscriberMessages.get(key);
if(topicSubContainer!=null){
topicSubContainer.reset();
}
}
protected void removeSubscriberMessageContainer(Object key) throws IOException{
subscriberContainer.remove(key);
TopicSubContainer container=(TopicSubContainer)subscriberMessages.remove(key);
for(Iterator i=container.iterator();i.hasNext();){
ConsumerMessageRef ref=(ConsumerMessageRef)i.next();
if(ref!=null){
TopicSubAck tsa=(TopicSubAck)ackContainer.get(ref.getAckEntry());
if(tsa!=null){
if(tsa.decrementCount()<=0){
ackContainer.remove(ref.getAckEntry());
messageContainer.remove(tsa.getMessageEntry());
}else{
ackContainer.update(ref.getAckEntry(),tsa);
}
}
}
}
store.deleteListContainer(key,"topic-subs");
}
protected String getSubscriptionKey(String clientId,String subscriberName){
String result=clientId+":";
result+=subscriberName!=null?subscriberName:"NOT_SET";
return result;
}
} }

View File

@ -0,0 +1,31 @@
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.apache.activemq.store.kahadaptor;
import org.apache.activemq.store.ReferenceStore.ReferenceData;
public class ReferenceRecord{
public String messageId;
public ReferenceData data;
public ReferenceRecord(){
}
public ReferenceRecord(String messageId,ReferenceData data){
this.messageId=messageId;
this.data=data;
}
}

View File

@ -0,0 +1,47 @@
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.apache.activemq.store.kahadaptor;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.activemq.kaha.Marshaller;
import org.apache.activemq.store.ReferenceStore.ReferenceData;
public class ReferenceRecordMarshaller implements Marshaller<ReferenceRecord>{
public ReferenceRecord readPayload(DataInput dataIn) throws IOException{
ReferenceRecord rr=new ReferenceRecord();
rr.messageId=dataIn.readUTF();
rr.data=new ReferenceData();
rr.data.setFileId(dataIn.readInt());
rr.data.setOffset(dataIn.readInt());
rr.data.setExpiration(dataIn.readLong());
return rr;
}
/**
* @param object
* @param dataOut
* @throws IOException
* @see org.apache.activemq.kaha.Marshaller#writePayload(java.lang.Object, java.io.DataOutput)
*/
public void writePayload(ReferenceRecord rr,DataOutput dataOut) throws IOException{
dataOut.writeUTF(rr.messageId);
dataOut.writeInt(rr.data.getFileId());
dataOut.writeInt(rr.data.getOffset());
dataOut.writeLong(rr.data.getExpiration());
}
}