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    }