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 }