001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.backends.jeb; 028 029 import com.sleepycat.je.Transaction; 030 import com.sleepycat.je.DatabaseException; 031 import com.sleepycat.je.DatabaseEntry; 032 033 import java.util.*; 034 035 import org.opends.server.types.DirectoryException; 036 037 /** 038 * A buffered index is used to buffer multiple reads or writes to the 039 * same index key into a single read or write. 040 * It can only be used to buffer multiple reads and writes under 041 * the same transaction. The transaction may be null if it is known 042 * that there are no other concurrent updates to the index. 043 */ 044 public class IndexBuffer 045 { 046 private EntryContainer entryContainer; 047 048 /** 049 * The buffered records stored as a map from the record key to the 050 * buffered value for that key for each index. 051 */ 052 private LinkedHashMap<Index, 053 TreeMap<byte[], BufferedIndexValues>> bufferedIndexes; 054 055 /** 056 * The buffered records stored as a set of buffered VLV values 057 * for each index. 058 */ 059 private LinkedHashMap<VLVIndex, BufferedVLVValues> bufferedVLVIndexes; 060 061 /** 062 * A simple class representing a pair of added and deleted indexed IDs. 063 */ 064 public static class BufferedIndexValues { 065 EntryIDSet addedIDs; 066 EntryIDSet deletedIDs; 067 } 068 069 /** 070 * A simple class representing a pair of added and deleted VLV values. 071 */ 072 public static class BufferedVLVValues { 073 TreeSet<SortValues> addedValues; 074 TreeSet<SortValues> deletedValues; 075 } 076 077 /** 078 * Construct a new empty index buffer object. 079 * 080 * @param entryContainer The database entryContainer using this 081 * index buffer. 082 */ 083 public IndexBuffer(EntryContainer entryContainer) 084 { 085 bufferedIndexes = 086 new LinkedHashMap<Index, TreeMap<byte[], BufferedIndexValues>>(); 087 bufferedVLVIndexes = new LinkedHashMap<VLVIndex, BufferedVLVValues>(); 088 this.entryContainer = entryContainer; 089 } 090 091 /** 092 * Get the buffered values for the given index. 093 * 094 * @param index The index with the buffered values to retrieve. 095 * @return The buffered values or <code>null</code> if there are 096 * no buffered values for the specified index. 097 */ 098 public TreeMap<byte[], BufferedIndexValues> getBufferedIndex(Index index) 099 { 100 return bufferedIndexes.get(index); 101 } 102 103 /** 104 * Put the specified buffered index values for the given index. 105 * 106 * @param index The index affected by the buffered values. 107 * @param bufferedValues The buffered values for the index. 108 */ 109 public void putBufferedIndex(Index index, TreeMap<byte[], 110 BufferedIndexValues> bufferedValues) 111 { 112 bufferedIndexes.put(index, bufferedValues); 113 } 114 115 /** 116 * Get the buffered VLV values for the given VLV index. 117 * 118 * @param vlvIndex The VLV index with the buffered values to retrieve. 119 * @return The buffered VLV values or <code>null</code> if there are 120 * no buffered VLV values for the specified VLV index. 121 */ 122 public BufferedVLVValues getVLVIndex(VLVIndex vlvIndex) 123 { 124 return bufferedVLVIndexes.get(vlvIndex); 125 } 126 127 /** 128 * Put the specified buffered VLV values for the given VLV index. 129 * 130 * @param vlvIndex The VLV index affected by the buffered values. 131 * @param bufferedVLVValues The buffered values for the VLV index. 132 */ 133 public void putBufferedVLVIndex(VLVIndex vlvIndex, 134 BufferedVLVValues bufferedVLVValues) 135 { 136 bufferedVLVIndexes.put(vlvIndex, bufferedVLVValues); 137 } 138 139 /** 140 * Flush the buffered index changes until the given transaction to 141 * the database. 142 * 143 * @param txn The database transaction to be used for the updates. 144 * @throws DatabaseException If an error occurs in the JE database. 145 * @throws DirectoryException If a Directory Server error occurs. 146 * @throws JebException If an error occurs in the JE backend. 147 */ 148 public void flush(Transaction txn) 149 throws DatabaseException, DirectoryException, JebException 150 { 151 TreeMap<byte[], BufferedIndexValues> bufferedValues; 152 BufferedVLVValues bufferedVLVValues; 153 byte[] keyBytes; 154 DatabaseEntry key = new DatabaseEntry(); 155 156 for(AttributeIndex attributeIndex : 157 entryContainer.getAttributeIndexes()) 158 { 159 for(Index index : attributeIndex.getAllIndexes()) 160 { 161 bufferedValues = bufferedIndexes.remove(index); 162 163 if(bufferedValues != null) 164 { 165 Iterator<Map.Entry<byte[], BufferedIndexValues>> keyIterator = 166 bufferedValues.entrySet().iterator(); 167 while(keyIterator.hasNext()) 168 { 169 Map.Entry<byte[], BufferedIndexValues> bufferedKey = 170 keyIterator.next(); 171 keyBytes = bufferedKey.getKey(); 172 key.setData(keyBytes); 173 174 index.updateKey(txn, key, bufferedKey.getValue().deletedIDs, 175 bufferedKey.getValue().addedIDs); 176 177 keyIterator.remove(); 178 } 179 } 180 } 181 } 182 183 for(VLVIndex vlvIndex : entryContainer.getVLVIndexes()) 184 { 185 bufferedVLVValues = bufferedVLVIndexes.remove(vlvIndex); 186 187 if(bufferedVLVValues != null) 188 { 189 vlvIndex.updateIndex(txn, bufferedVLVValues.addedValues, 190 bufferedVLVValues.deletedValues); 191 } 192 } 193 194 Index id2children = entryContainer.getID2Children(); 195 bufferedValues = bufferedIndexes.remove(id2children); 196 197 if(bufferedValues != null) 198 { 199 Iterator<Map.Entry<byte[], BufferedIndexValues>> keyIterator = 200 bufferedValues.entrySet().iterator(); 201 while(keyIterator.hasNext()) 202 { 203 Map.Entry<byte[], BufferedIndexValues> bufferedKey = 204 keyIterator.next(); 205 keyBytes = bufferedKey.getKey(); 206 key.setData(keyBytes); 207 208 id2children.updateKey(txn, key, bufferedKey.getValue().deletedIDs, 209 bufferedKey.getValue().addedIDs); 210 211 keyIterator.remove(); 212 } 213 } 214 215 Index id2subtree = entryContainer.getID2Subtree(); 216 bufferedValues = bufferedIndexes.remove(id2subtree); 217 218 if(bufferedValues != null) 219 { 220 Iterator<Map.Entry<byte[], BufferedIndexValues>> keyIterator = 221 bufferedValues.entrySet().iterator(); 222 while(keyIterator.hasNext()) 223 { 224 Map.Entry<byte[], BufferedIndexValues> bufferedKey = 225 keyIterator.next(); 226 keyBytes = bufferedKey.getKey(); 227 key.setData(keyBytes); 228 229 id2subtree.updateKey(txn, key, bufferedKey.getValue().deletedIDs, 230 bufferedKey.getValue().addedIDs); 231 232 keyIterator.remove(); 233 } 234 } 235 } 236 }