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 legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2009-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.server.backends.jeb;
028
029import java.util.Collection;
030import java.util.Map;
031
032import org.forgerock.i18n.LocalizableMessageBuilder;
033import org.forgerock.opendj.ldap.ByteSequence;
034import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
035import org.forgerock.opendj.ldap.spi.IndexingOptions;
036import org.opends.server.types.AttributeType;
037
038import com.sleepycat.je.DatabaseEntry;
039import com.sleepycat.je.LockMode;
040
041import static org.opends.messages.BackendMessages.*;
042
043/**
044 * This class is an implementation of IndexQueryFactory which creates
045 * IndexQuery objects as part of the query of the JEB index.
046 */
047public final class IndexQueryFactoryImpl implements
048    IndexQueryFactory<IndexQuery>
049{
050
051  private static final String PRESENCE_INDEX_KEY = "presence";
052
053  /**
054   * The Map containing the string type identifier and the corresponding index.
055   */
056  private final Map<String, Index> indexMap;
057  private final IndexingOptions indexingOptions;
058  private final AttributeType attribute;
059
060  /**
061   * Creates a new IndexQueryFactoryImpl object.
062   *
063   * @param indexMap
064   *          A map containing the index id and the corresponding index.
065   * @param indexingOptions
066   *          The options to use for indexing
067   * @param attribute
068   *          The Attribute type of this index, for error messages
069   */
070  public IndexQueryFactoryImpl(Map<String, Index> indexMap, IndexingOptions indexingOptions, AttributeType attribute)
071  {
072    this.indexMap = indexMap;
073    this.indexingOptions = indexingOptions;
074    this.attribute = attribute;
075  }
076
077
078
079  /** {@inheritDoc} */
080  @Override
081  public IndexQuery createExactMatchQuery(final String indexID, final ByteSequence value)
082  {
083    return new IndexQuery()
084      {
085
086        @Override
087        public EntryIDSet evaluate(LocalizableMessageBuilder debugMessage)
088        {
089          // Read the database and get Record for the key.
090          DatabaseEntry key = new DatabaseEntry(value.toByteArray());
091
092          // Select the right index to be used.
093          Index index = indexMap.get(indexID);
094          if (index == null)
095          {
096            if(debugMessage != null)
097            {
098              debugMessage.append(INFO_INDEX_FILTER_INDEX_TYPE_DISABLED.get(indexID, attribute.getNameOrOID()));
099            }
100            return createMatchAllQuery().evaluate(debugMessage);
101          }
102          EntryIDSet entrySet = index.readKey(key, null, LockMode.DEFAULT);
103          if(debugMessage != null && !entrySet.isDefined())
104          {
105            updateStatsUndefinedResults(debugMessage, index);
106          }
107          return entrySet;
108        }
109      };
110  }
111
112
113
114  /** {@inheritDoc} */
115  @Override
116  public IndexQuery createRangeMatchQuery(final String indexID,
117      final ByteSequence lowerBound, final ByteSequence upperBound,
118      final boolean includeLowerBound, final boolean includeUpperBound)
119  {
120    return new IndexQuery()
121      {
122
123        @Override
124        public EntryIDSet evaluate(LocalizableMessageBuilder debugMessage)
125        {
126          // Find the right index.
127          Index index = indexMap.get(indexID);
128          if (index == null)
129          {
130            if(debugMessage != null)
131            {
132              debugMessage.append(INFO_INDEX_FILTER_INDEX_TYPE_DISABLED.get(indexID, attribute.getNameOrOID()));
133            }
134            return createMatchAllQuery().evaluate(debugMessage);
135          }
136          EntryIDSet entrySet = index.readRange(lowerBound.toByteArray(), upperBound.toByteArray(),
137              includeLowerBound, includeUpperBound);
138          if(debugMessage != null && !entrySet.isDefined())
139          {
140            updateStatsUndefinedResults(debugMessage, index);
141          }
142          return entrySet;
143        }
144      };
145  }
146
147
148
149  /** {@inheritDoc} */
150  @Override
151  public IndexQuery createIntersectionQuery(Collection<IndexQuery> subqueries)
152  {
153    return IndexQuery.createIntersectionIndexQuery(subqueries);
154  }
155
156
157
158  /** {@inheritDoc} */
159  @Override
160  public IndexQuery createUnionQuery(Collection<IndexQuery> subqueries)
161  {
162    return IndexQuery.createUnionIndexQuery(subqueries);
163  }
164
165
166
167  /**
168   * {@inheritDoc}
169   * <p>
170   * It returns an empty EntryIDSet object when either all or no record
171   * sets are requested.
172   */
173  @Override
174  public IndexQuery createMatchAllQuery()
175  {
176    return new IndexQuery()
177      {
178        @Override
179        public EntryIDSet evaluate(LocalizableMessageBuilder debugMessage)
180        {
181        final String indexID = PRESENCE_INDEX_KEY;
182        final Index index = indexMap.get(indexID);
183          if (index == null)
184          {
185            if(debugMessage != null)
186            {
187              debugMessage.append(INFO_INDEX_FILTER_INDEX_TYPE_DISABLED.get(indexID, attribute.getNameOrOID()));
188            }
189            return new EntryIDSet();
190          }
191
192          EntryIDSet entrySet = index.readKey(JEBUtils.presenceKey, null, LockMode.DEFAULT);
193          if (debugMessage != null && !entrySet.isDefined())
194          {
195            updateStatsUndefinedResults(debugMessage, index);
196          }
197          return entrySet;
198        }
199      };
200  }
201
202  private static void updateStatsUndefinedResults(LocalizableMessageBuilder debugMessage, Index index)
203  {
204    if (!index.isTrusted())
205    {
206      debugMessage.append(INFO_INDEX_FILTER_INDEX_NOT_TRUSTED.get(index.getName()));
207    }
208    else if (index.isRebuildRunning())
209    {
210      debugMessage.append(INFO_INDEX_FILTER_INDEX_REBUILD_IN_PROGRESS.get(index.getName()));
211    }
212    else
213    {
214      debugMessage.append(INFO_INDEX_FILTER_INDEX_LIMIT_EXCEEDED.get(index.getName()));
215    }
216  }
217
218  /** {@inheritDoc} */
219  @Override
220  public IndexingOptions getIndexingOptions()
221  {
222    return indexingOptions;
223  }
224}