From ed270f32bf132018992ab8a9c5978f7dbf33f263 Mon Sep 17 00:00:00 2001
From: sin <sin@localhost>
Date: Thu, 02 Jul 2009 17:40:53 +0000
Subject: [PATCH] Issue 4075 : Provide implementations for the relative and partial time matching rules
---
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/TimeBasedMatchingRuleTest.java | 508 ++++++++++++++
opendj-sdk/opends/resource/config/config.ldif | 8
opendj-sdk/opends/src/messages/messages/schema.properties | 36 +
opendj-sdk/opends/src/server/org/opends/server/schema/TimeBasedMatchingRuleFactory.java | 1366 ++++++++++++++++++++++++++++++++++++++++
opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java | 8
opendj-sdk/opends/src/server/org/opends/server/schema/SchemaConstants.java | 86 ++
opendj-sdk/opends/resource/schema/02-config.ldif | 5
7 files changed, 2,016 insertions(+), 1 deletions(-)
diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index aea468a..0e486b7 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -1303,6 +1303,14 @@
ds-cfg-java-class: org.opends.server.schema.TelephoneNumberSubstringMatchingRuleFactory
ds-cfg-enabled: true
+dn: cn=Time Based Matching Rule,cn=Matching Rules,cn=config
+objectClass: top
+objectClass: ds-cfg-matching-rule
+objectClass: ds-cfg-extensible-matching-rule
+cn: Time Based Matching Rule
+ds-cfg-java-class: org.opends.server.schema.TimeBasedMatchingRuleFactory
+ds-cfg-enabled: true
+
dn: cn=Unique Member Equality Matching Rule,cn=Matching Rules,cn=config
objectClass: top
objectClass: ds-cfg-matching-rule
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index cdea6f0..6aba8b1 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -4049,4 +4049,9 @@
SUP ds-cfg-virtual-attribute
STRUCTURAL
X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.231
+ NAME 'ds-cfg-extensible-matching-rule'
+ SUP ds-cfg-matching-rule
+ STRUCTURAL
+ X-ORIGIN 'OpenDS Directory Server' )
diff --git a/opendj-sdk/opends/src/messages/messages/schema.properties b/opendj-sdk/opends/src/messages/messages/schema.properties
index 7f2e681..bf4c5a7 100644
--- a/opendj-sdk/opends/src/messages/messages/schema.properties
+++ b/opendj-sdk/opends/src/messages/messages/schema.properties
@@ -960,3 +960,39 @@
value could not be parsed as a valid superior object class definition because \
definition for the objectclass with OID %s has already declared a superior objectclass with an OID \
of %s. Multiple inheritance of objectclasses is not yet supported
+MILD_WARN_ATTR_INVALID_RELATIVE_TIME_ASSERTION_FORMAT_294=The provided \
+ value "%s" could not be parsed as a valid assertion value because the \
+ character '%c' is not allowed. The acceptable values are s(second),m(minute), \
+ ,h(hour),d(day) and w(week)
+MILD_WARN_ATTR_INVALID_PARTIAL_TIME_ASSERTION_FORMAT_295=The provided \
+ value "%s" could not be parsed as a valid assertion value because the \
+ character '%c' is not allowed. The acceptable values are DD (date),MM(month) \
+ and YYYY(year)
+MILD_WARN_ATTR_MISSING_CHAR_PARTIAL_TIME_ASSERTION_FORMAT_296=The provided \
+ value "%s" could not be parsed as a valid assertion value because an invalid \
+ character '%c' is found instead of '%c' at position %d
+MILD_WARN_ATTR_INVALID_DATE_ASSERTION_FORMAT_297=The provided \
+ value "%s" could not be parsed as a valid assertion value because "%d" is not \
+ a valid date specification
+MILD_WARN_ATTR_INVALID_MONTH_ASSERTION_FORMAT_298=The provided \
+ value "%s" could not be parsed as a valid assertion value because "%d" is not \
+ a valid month specification
+MILD_WARN_ATTR_INVALID_YEAR_ASSERTION_FORMAT_299=The provided \
+ value "%s" could not be parsed as a valid assertion value because "%d" is not \
+ a valid year specification
+MILD_WARN_ATTR_DUPLICATE_DATE_ASSERTION_FORMAT_300=The provided \
+ value "%s" could not be parsed as a valid assertion value because there is \
+ conflicting value "%d" for DD(Date) specification
+MILD_WARN_ATTR_DUPLICATE_MONTH_ASSERTION_FORMAT_301=The provided \
+ value "%s" could not be parsed as a valid assertion value because there is \
+ conflicting value "%d" for MM(Month) specification
+MILD_WARN_ATTR_DUPLICATE_YEAR_ASSERTION_FORMAT_302=The provided \
+ value "%s" could not be parsed as a valid assertion value because there is \
+ conflicting value "%d" for YYYY(Year) specification
+MILD_WARN_ATTR_MISSING_YEAR_PARTIAL_TIME_ASSERTION_FORMAT_303=The provided \
+ value "%s" could not be parsed as a valid assertion value because it does \
+ not contain year in YYYY format
+MILD_WARN_ATTR_CONFLICTING_ASSERTION_FORMAT_304=The provided \
+ value "%s" could not be parsed as a valid assertion value because more than \
+ one time units are not allowed
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/schema/SchemaConstants.java b/opendj-sdk/opends/src/server/org/opends/server/schema/SchemaConstants.java
index 348092f..9e51258 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/schema/SchemaConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/schema/SchemaConstants.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Copyright 2006-2008 Sun Microsystems, Inc.
+ * Copyright 2006-2009 Sun Microsystems, Inc.
*/
package org.opends.server.schema;
@@ -1906,5 +1906,89 @@
* The OID for the "top" objectclass.
*/
public static final String TOP_OBJECTCLASS_OID = "2.5.6.0";
+
+
+
+ /**
+ * The name for the relative time greater-than extensible ordering matching
+ * rule.
+ */
+ public static final String EXT_OMR_RELATIVE_TIME_GT_NAME =
+ "relativeTimeGTOrderingMatch";
+
+
+
+ /**
+ * The alternative name for the relative time greater-than extensible
+ * ordering matching rule.
+ */
+ public static final String EXT_OMR_RELATIVE_TIME_GT_ALT_NAME =
+ "relativeTimeOrderingMatch.gt";
+
+
+
+ /**
+ * The OID for the relative time greater-than extensible ordering matching
+ * rule.
+ */
+ public static final String EXT_OMR_RELATIVE_TIME_GT_OID =
+ "1.3.6.1.4.1.26027.1.4.5";
+
+
+
+ /**
+ * The name for the relative time less-than extensible ordering matching
+ * rule.
+ */
+ public static final String EXT_OMR_RELATIVE_TIME_LT_NAME =
+ "relativeTimeLTOrderingMatch";
+
+
+
+ /**
+ * The alternative name for the relative time less-than extensible ordering
+ * matching rule.
+ */
+ public static final String EXT_OMR_RELATIVE_TIME_LT_ALT_NAME =
+ "relativeTimeOrderingMatch.lt";
+
+
+
+ /**
+ * The OID for the relative time less-than extensible ordering matching rule.
+ */
+ public static final String EXT_OMR_RELATIVE_TIME_LT_OID =
+ "1.3.6.1.4.1.26027.1.4.6";
+
+
+
+ /**
+ * The OID for the partial date and time extensible matching rule.
+ */
+ public static final String EXT_PARTIAL_DATE_TIME_OID =
+ "1.3.6.1.4.1.26027.1.4.7";
+
+
+
+ /**
+ * The name for the partial date and time extensible rule.
+ */
+ public static final String EXT_PARTIAL_DATE_TIME_NAME =
+ "partialDateAndTimeMatchingRule";
+
+
+
+ /**
+ * The preferred index name for partial date and time matching rule.
+ */
+ public static final String PARTIAL_DATE_TIME_INDEX_NAME = "pdt";
+
+
+
+ /**
+ * The preferred index name for partial date and time matching rule.
+ */
+ public static final String RELATIVE_TIME_INDEX_NAME = "rt";
+
}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/schema/TimeBasedMatchingRuleFactory.java b/opendj-sdk/opends/src/server/org/opends/server/schema/TimeBasedMatchingRuleFactory.java
new file mode 100644
index 0000000..70d5486
--- /dev/null
+++ b/opendj-sdk/opends/src/server/org/opends/server/schema/TimeBasedMatchingRuleFactory.java
@@ -0,0 +1,1366 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.
+ */
+
+
+package org.opends.server.schema;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+import org.opends.messages.Message;
+import org.opends.server.api.ExtensibleIndexer;
+import org.opends.server.api.IndexQueryFactory;
+import org.opends.server.api.MatchingRule;
+import org.opends.server.api.MatchingRuleFactory;
+import org.opends.server.admin.std.server.MatchingRuleCfg;
+import org.opends.server.api.AbstractMatchingRule;
+import org.opends.server.api.ExtensibleMatchingRule;
+import org.opends.server.api.OrderingMatchingRule;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.types.AttributeValue;
+import org.opends.server.types.ByteSequence;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.ConditionResult;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.IndexConfig;
+import org.opends.server.types.InitializationException;
+import org.opends.server.types.ResultCode;
+import static org.opends.server.util.StaticUtils.*;
+import static org.opends.server.util.TimeThread.*;
+
+import static org.opends.messages.SchemaMessages.*;
+import static org.opends.server.loggers.ErrorLogger.*;
+import static org.opends.server.schema.SchemaConstants.*;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.schema.GeneralizedTimeSyntax.*;
+
+
+
+/**
+ * This class acts as a factory for time-based matching rules.
+ */
+public final class TimeBasedMatchingRuleFactory
+ extends MatchingRuleFactory<MatchingRuleCfg>
+{
+ //Greater-than RelativeTimeMatchingRule.
+ private MatchingRule greaterThanRTMRule;
+
+
+ //Less-than RelativeTimeMatchingRule.
+ private MatchingRule lessThanRTMRule;
+
+
+ //PartialDayAndTimeMatchingRule.
+ private MatchingRule partialDTMatchingRule;
+
+
+ //A Collection of matching rules managed by this factory.
+ private Set<MatchingRule> matchingRules;
+
+
+ private static final TimeZone TIME_ZONE_UTC_OBJ =
+ TimeZone.getTimeZone(TIME_ZONE_UTC);
+
+
+ //Constants for months.
+ private static final byte[] JAN = {'j','a','n' };
+
+
+ private static final byte[] FEB = {'f','e','b'};
+
+
+ private static final byte[] MAR = {'m','a','r'};
+
+
+ private static final byte[] APR = {'a','p','r'};
+
+
+ private static final byte[] MAY = {'m','a','y'};
+
+
+ private static final byte[] JUN = {'j','u','n'};
+
+
+ private static final byte[] JUL = {'j','u','l'};
+
+
+ private static final byte[] AUG = {'a','u','g'};
+
+
+ private static final byte[] SEP = {'s','e','p'};
+
+
+ private static final byte[] OCT = {'o','c','t'};
+
+
+ private static final byte[] NOV = {'n','o','v'};
+
+
+ private static final byte[] DEC = {'d','e','c'};
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void initializeMatchingRule(MatchingRuleCfg configuration)
+ throws ConfigException, InitializationException
+ {
+ matchingRules = new HashSet<MatchingRule>();
+ greaterThanRTMRule = new RelativeTimeGTOrderingMatchingRule();
+ matchingRules.add(greaterThanRTMRule);
+ lessThanRTMRule = new RelativeTimeLTOrderingMatchingRule();
+ matchingRules.add(lessThanRTMRule);
+ partialDTMatchingRule = new PartialDateAndTimeMatchingRule();
+ matchingRules.add(partialDTMatchingRule);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Collection<MatchingRule> getMatchingRules()
+ {
+ return Collections.unmodifiableCollection(matchingRules);
+ }
+
+
+
+ /**
+ * This class defines a matching rule which is used for time-based searches.
+ */
+ private abstract class TimeBasedMatchingRule extends AbstractMatchingRule
+ implements ExtensibleMatchingRule
+ {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDescription()
+ {
+ //There is no standard definition.
+ return null;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSyntaxOID()
+ {
+ return SYNTAX_GENERALIZED_TIME_OID;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ByteString normalizeValue(ByteSequence value)
+ throws DirectoryException
+ {
+ try
+ {
+ long timestamp = decodeGeneralizedTimeValue(value);
+ return ByteString.valueOf(timestamp);
+ }
+ catch (DirectoryException de)
+ {
+ switch (DirectoryServer.getSyntaxEnforcementPolicy())
+ {
+ case REJECT:
+ throw de;
+
+ case WARN:
+ logError(de.getMessageObject());
+ return value.toByteString();
+
+ default:
+ return value.toByteString();
+ }
+ }
+ }
+ }
+
+
+
+ /**
+ * This class defines a matching rule which matches the relative time for
+ * time-based searches.
+ */
+ private abstract class RelativeTimeOrderingMatchingRule
+ extends TimeBasedMatchingRule
+ implements OrderingMatchingRule
+ {
+ /**
+ * The serial version identifier required to satisfy the compiler because
+ * this class implements the <CODE>java.io.Serializable</CODE> interface.
+ * This value was generated using the <CODE>serialver</CODE> command-line
+ * utility included with the Java SDK.
+ */
+ private static final long serialVersionUID = -3501812894473163490L;
+
+
+
+ /**
+ * Indexer associated with this instance.
+ */
+ protected ExtensibleIndexer indexer;
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ByteString normalizeAssertionValue(ByteSequence value)
+ throws DirectoryException
+ {
+ /**
+ An assertion value may contain one of the following:
+ s = second
+ m = minute
+ h = hour
+ d = day
+ w = week
+
+ An example assertion is OID:=(-)1d, where a '-' means that the user
+ intends to search only the expired events. In this example we are
+ searching for an event expired 1 day back.
+
+ Use this method to parse, validate and normalize the assertion value
+ into a format to be recognized by the valuesMatch routine. This method
+ takes the assertion value, adds/substracts it to/from the current time
+ and calculates a time which will be used as a relative time by inherited
+ rules.
+ */
+
+ int index = 0;
+ boolean signed = false;
+ byte firstByte = value.byteAt(0);
+
+ if(firstByte == '-')
+ {
+ //Turn the sign on to go back in past.
+ signed = true;
+ index = 1;
+ }
+ else if(firstByte == '+')
+ {
+ //'+" is not required but we won't reject it either.
+ index = 1;
+ }
+
+ long second = 0;
+ long minute = 0;
+ long hour = 0;
+ long day = 0;
+ long week = 0;
+
+ boolean containsTimeUnit = false;
+ long number = 0;
+
+ for(; index<value.length(); index++)
+ {
+ byte b = value.byteAt(index);
+ if(isDigit((char)b))
+ {
+ switch (value.byteAt(index))
+ {
+ case '0':
+ number = (number * 10);
+ break;
+
+ case '1':
+ number = (number * 10) + 1;
+ break;
+
+ case '2':
+ number = (number * 10) + 2;
+ break;
+
+ case '3':
+ number = (number * 10) + 3;
+ break;
+
+ case '4':
+ number = (number * 10) + 4;
+ break;
+
+ case '5':
+ number = (number * 10) + 5;
+ break;
+
+ case '6':
+ number = (number * 10) + 6;
+ break;
+
+ case '7':
+ number = (number * 10) + 7;
+ break;
+
+ case '8':
+ number = (number * 10) + 8;
+ break;
+
+ case '9':
+ number = (number * 10) + 9;
+ break;
+ }
+ }
+ else
+ {
+ Message message = null;
+ if(containsTimeUnit)
+ {
+ //We already have time unit found by now.
+ message = WARN_ATTR_CONFLICTING_ASSERTION_FORMAT.
+ get(value.toString());
+ }
+ else
+ {
+ switch(value.byteAt(index))
+ {
+ case 's':
+ second = number;
+ break;
+ case 'm':
+ minute = number;
+ break;
+ case 'h':
+ hour = number;
+ break;
+ case 'd':
+ day = number;
+ break;
+ case 'w':
+ week = number;
+ break;
+ default:
+ message =
+ WARN_ATTR_INVALID_RELATIVE_TIME_ASSERTION_FORMAT.
+ get(value.toString(),(char)value.byteAt(index));
+ }
+ }
+ if(message !=null)
+ {
+ //Log the message and throw an exception.
+ logError(message);
+ throw new DirectoryException(
+ ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
+ }
+ else
+ {
+ containsTimeUnit = true;
+ number = 0;
+ }
+ }
+ }
+
+ if(!containsTimeUnit)
+ {
+ //There was no time unit so assume it is seconds.
+ second = number;
+ }
+
+ long delta = (second + minute*60 + hour*3600 + day*24*3600 +
+ week*7*24*3600)*1000 ;
+ long now = getTime();
+ return signed?ByteString.valueOf(now-delta):
+ ByteString.valueOf(now+delta);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public int compareValues(ByteSequence value1, ByteSequence value2)
+ {
+ return value1.compareTo(value2);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public int compare(byte[] arg0, byte[] arg1)
+ {
+ return compare(arg0, arg1);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<ExtensibleIndexer> getIndexers(IndexConfig config)
+ {
+ if(indexer == null)
+ {
+ indexer = new RelativeTimeExtensibleIndexer(this);
+ }
+ return Collections.singletonList(indexer);
+ }
+ }
+
+
+
+ /**
+ * This class defines a matching rule which calculates the "greater-than"
+ * relative time for time-based searches.
+ */
+ private final class RelativeTimeGTOrderingMatchingRule
+ extends RelativeTimeOrderingMatchingRule
+ {
+ //All the names for this matching rule.
+ private final List<String> names;
+
+
+
+ /**
+ * The serial version identifier required to satisfy the compiler because
+ * this class implements the <CODE>java.io.Serializable</CODE> interface.
+ * This value was generated using the <CODE>serialver</CODE> command-line
+ * utility included with the Java SDK.
+ */
+ private static final long serialVersionUID = 7247241496402474136L;
+
+
+ RelativeTimeGTOrderingMatchingRule()
+ {
+ names = new ArrayList<String>();
+ names.add(EXT_OMR_RELATIVE_TIME_GT_NAME);
+ names.add(EXT_OMR_RELATIVE_TIME_GT_ALT_NAME);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName()
+ {
+ return EXT_OMR_RELATIVE_TIME_GT_NAME;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Collection<String> getAllNames()
+ {
+ return Collections.unmodifiableList(names);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getOID()
+ {
+ return EXT_OMR_RELATIVE_TIME_GT_OID;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ConditionResult valuesMatch(ByteSequence attributeValue,
+ ByteSequence assertionValue)
+ {
+ int ret = compareValues(attributeValue, assertionValue);
+
+ if (ret > 0)
+ {
+ return ConditionResult.TRUE;
+ }
+ else
+ {
+ return ConditionResult.FALSE;
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> T createIndexQuery(ByteSequence assertionValue,
+ IndexQueryFactory<T> factory) throws DirectoryException
+ {
+ return factory.createRangeMatchQuery(indexer
+ .getExtensibleIndexID(), normalizeAssertionValue(assertionValue),
+ ByteString.empty(), false, false);
+ }
+ }
+
+
+
+ /**
+ * This class defines a matching rule which calculates the "less-than"
+ * relative time for time-based searches.
+ */
+ private final class RelativeTimeLTOrderingMatchingRule
+ extends RelativeTimeOrderingMatchingRule
+ {
+ //All the names for this matching rule.
+ private final List<String> names;
+
+
+
+ /**
+ * The serial version identifier required to satisfy the compiler because
+ * this class implements the <CODE>java.io.Serializable</CODE> interface.
+ * This value was generated using the <CODE>serialver</CODE> command-line
+ * utility included with the Java SDK.
+ */
+ private static final long serialVersionUID = -5122459830973558441L;
+
+
+
+ RelativeTimeLTOrderingMatchingRule()
+ {
+ names = new ArrayList<String>();
+ names.add(EXT_OMR_RELATIVE_TIME_LT_NAME);
+ names.add(EXT_OMR_RELATIVE_TIME_LT_ALT_NAME);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName()
+ {
+ return EXT_OMR_RELATIVE_TIME_LT_NAME;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Collection<String> getAllNames()
+ {
+ return Collections.unmodifiableList(names);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getOID()
+ {
+ return EXT_OMR_RELATIVE_TIME_LT_OID;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ConditionResult valuesMatch(ByteSequence attributeValue,
+ ByteSequence assertionValue)
+ {
+ int ret = compareValues(attributeValue, assertionValue);
+
+ if (ret < 0)
+ {
+ return ConditionResult.TRUE;
+ }
+ else
+ {
+ return ConditionResult.FALSE;
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> T createIndexQuery(ByteSequence assertionValue,
+ IndexQueryFactory<T> factory) throws DirectoryException
+ {
+ return factory.createRangeMatchQuery(indexer
+ .getExtensibleIndexID(), ByteString.empty(),
+ normalizeAssertionValue(assertionValue),false, false);
+ }
+ }
+
+
+
+ /**
+ * Extensible Indexer class for Relative Time Matching rules which share
+ * the same index. This Indexer is shared by both greater than and less than
+ * Relative Time Matching Rules.
+ */
+ private final class RelativeTimeExtensibleIndexer extends
+ ExtensibleIndexer
+ {
+
+ /**
+ * The Extensible Matching Rule.
+ */
+ private final RelativeTimeOrderingMatchingRule matchingRule;
+
+
+
+ /**
+ * Creates a new instance of RelativeTimeExtensibleIndexer.
+ *
+ * @param matchingRule The relative time Matching Rule.
+ */
+ private RelativeTimeExtensibleIndexer(
+ RelativeTimeOrderingMatchingRule matchingRule)
+ {
+ this.matchingRule = matchingRule;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getExtensibleIndexID()
+ {
+ return EXTENSIBLE_INDEXER_ID_DEFAULT;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final void getKeys(AttributeValue value, Set<byte[]> keys)
+ {
+ ByteString key;
+ try
+ {
+ key = matchingRule.normalizeValue(value.getValue());
+ keys.add(key.toByteArray());
+ }
+ catch (DirectoryException de)
+ {
+ //don't do anything.
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final void getKeys(AttributeValue value,
+ Map<byte[], Boolean> modifiedKeys, Boolean insert)
+ {
+ Set<byte[]> keys = new HashSet<byte[]>();
+ getKeys(value, keys);
+ for (byte[] key : keys)
+ {
+ Boolean cInsert = modifiedKeys.get(key);
+ if (cInsert == null)
+ {
+ modifiedKeys.put(key, insert);
+ }
+ else if (!cInsert.equals(insert))
+ {
+ modifiedKeys.remove(key);
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPreferredIndexName()
+ {
+ return RELATIVE_TIME_INDEX_NAME;
+ }
+ }
+
+
+
+ /**
+ * This class performs the partial date and time matching capabilities.
+ */
+ private final class PartialDateAndTimeMatchingRule
+ extends TimeBasedMatchingRule
+ implements ExtensibleMatchingRule
+ {
+ /**
+ * Indexer associated with this instance.
+ */
+ private ExtensibleIndexer indexer;
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName()
+ {
+ return EXT_PARTIAL_DATE_TIME_NAME;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getOID()
+ {
+ return EXT_PARTIAL_DATE_TIME_OID;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Collection<String> getAllNames()
+ {
+ return Collections.singleton(getName());
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ByteString normalizeAssertionValue(ByteSequence value)
+ throws DirectoryException
+ {
+ /**
+ An assertion value may contain one or all of the following:
+ DD = day
+ MM = month
+ YYYY = year
+
+ An example assertion is OID:=04MM. In this example we are
+ searching for entries corresponding to month of april.
+
+ Use this method to parse, validate and normalize the assertion value
+ into a format to be recognized by the compare routine. The normalized
+ value is actually the format of : DDMMYYYY.
+ */
+ int date = 0;
+ int year = 0;
+ int number = 0;
+ int month = -1;
+
+ int length = value.length();
+ for(int index=0; index<length; index++)
+ {
+ byte b = value.byteAt(index);
+ if(isDigit((char)b))
+ {
+ switch (value.byteAt(index))
+ {
+ case '0':
+ number = (number * 10);
+ break;
+
+ case '1':
+ number = (number * 10) + 1;
+ break;
+
+ case '2':
+ number = (number * 10) + 2;
+ break;
+
+ case '3':
+ number = (number * 10) + 3;
+ break;
+
+ case '4':
+ number = (number * 10) + 4;
+ break;
+
+ case '5':
+ number = (number * 10) + 5;
+ break;
+
+ case '6':
+ number = (number * 10) + 6;
+ break;
+
+ case '7':
+ number = (number * 10) + 7;
+ break;
+
+ case '8':
+ number = (number * 10) + 8;
+ break;
+
+ case '9':
+ number = (number * 10) + 9;
+ break;
+ }
+ }
+ else
+ {
+ Message message = null;
+ switch(value.byteAt(index))
+ {
+ case 'D':
+ if(!(index < length-1) || value.byteAt(index+1) !='D')
+ {
+ //the acceptable format is 'DD'.
+ message =
+ WARN_ATTR_MISSING_CHAR_PARTIAL_TIME_ASSERTION_FORMAT.
+ get(value.toString(),
+ (char)value.byteAt(index),'D',index+1);
+ }
+ else if(number == 0)
+ {
+ message =
+ WARN_ATTR_INVALID_DATE_ASSERTION_FORMAT.get(
+ value.toString(), number);
+ }
+ else if(date > 0)
+ {
+ message =
+ WARN_ATTR_DUPLICATE_DATE_ASSERTION_FORMAT.get(
+ value.toString(),date);
+ }
+ else
+ {
+ date = number;
+ index++;
+ }
+ break;
+ case 'M':
+ if(!(index < length-1) || value.byteAt(index+1)!='M')
+ {
+ //the acceptable value is 'MM'.
+ message =
+ WARN_ATTR_MISSING_CHAR_PARTIAL_TIME_ASSERTION_FORMAT.
+ get(value.toString(),
+ (char)value.byteAt(index),'M',index+1);
+ }
+ else if(number == 0)
+ {
+ message =
+ WARN_ATTR_INVALID_MONTH_ASSERTION_FORMAT.
+ get(value.toString(),number);
+ }
+ else if(month > 0)
+ {
+ message =
+ WARN_ATTR_DUPLICATE_MONTH_ASSERTION_FORMAT.get(
+ value.toString(),month);
+ }
+ else
+ {
+ month = number;
+ index++;
+ }
+ break;
+ case 'Y':
+ if(!(index < length-3))
+ {
+ //the acceptable value is 'YYYY".
+ message =
+ WARN_ATTR_MISSING_YEAR_PARTIAL_TIME_ASSERTION_FORMAT.
+ get(value.toString());
+ }
+ else
+ {
+yearLoop: for(int i=index;i<index+3;i++)
+ {
+ if(value.byteAt(i) !='Y')
+ {
+ message =
+ WARN_ATTR_MISSING_CHAR_PARTIAL_TIME_ASSERTION_FORMAT.
+ get(value.toString(),(char)value.byteAt(i),'Y',i);
+ break yearLoop;
+ }
+ }
+ if(message == null)
+ {
+ if(number == 0)
+ {
+ message =
+ WARN_ATTR_INVALID_YEAR_ASSERTION_FORMAT.
+ get(value.toString(),number);
+ }
+ else if(year >0)
+ {
+ message = WARN_ATTR_DUPLICATE_YEAR_ASSERTION_FORMAT.
+ get(value.toString(),year);
+ }
+ else
+ {
+ year = number;
+ index+=3;
+ }
+ }
+ }
+ break;
+ default:
+ message =
+ WARN_ATTR_INVALID_PARTIAL_TIME_ASSERTION_FORMAT.
+ get(value.toString(),(char)value.byteAt(index));
+ }
+ if(message !=null)
+ {
+ logError(message);
+ throw new DirectoryException(
+ ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
+ }
+ else
+ {
+ number = 0;
+ }
+ }
+ }
+
+ //Validate year, month and date in that order.
+ if(year < 0)
+ {
+ //A future date is allowed.
+ Message message =
+ WARN_ATTR_INVALID_YEAR_ASSERTION_FORMAT.
+ get(value.toString(),year);
+ logError(message);
+ throw new DirectoryException(
+ ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
+ }
+
+ switch(month)
+ {
+ case -1:
+ //just allow this.
+ break;
+ case 1:
+ month = Calendar.JANUARY;
+ break;
+ case 2:
+ month = Calendar.FEBRUARY;
+ break;
+ case 3:
+ month = Calendar.MARCH;
+ break;
+ case 4:
+ month = Calendar.APRIL;
+ break;
+ case 5:
+ month = Calendar.MAY;
+ break;
+ case 6:
+ month = Calendar.JUNE;
+ break;
+ case 7:
+ month = Calendar.JULY;
+ break;
+ case 8:
+ month = Calendar.AUGUST;
+ break;
+ case 9:
+ month = Calendar.SEPTEMBER;
+ break;
+ case 10:
+ month = Calendar.OCTOBER;
+ break;
+ case 11:
+ month = Calendar.NOVEMBER;
+ break;
+ case 12:
+ month = Calendar.DECEMBER;
+ break;
+ default:
+ Message message =
+ WARN_ATTR_INVALID_MONTH_ASSERTION_FORMAT.
+ get(value.toString(),month);
+ logError(message);
+ throw new DirectoryException(
+ ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
+ }
+
+ boolean invalidDate = false;
+ switch(date)
+ {
+ case 29:
+ if(month == Calendar.FEBRUARY && year%4 !=0)
+ {
+ invalidDate = true;
+ }
+ break;
+ case 31:
+ if(month != -1 && month != Calendar.JANUARY && month!= Calendar.MARCH
+ && month != Calendar.MAY && month != Calendar.JULY
+ && month != Calendar.AUGUST && month != Calendar.OCTOBER
+ && month != Calendar.DECEMBER)
+ {
+ invalidDate = true;
+ }
+ break;
+ default:
+ if(!(date >=0 && date <=31))
+ {
+ invalidDate = true;
+ }
+ }
+ if(invalidDate)
+ {
+ Message message =
+ WARN_ATTR_INVALID_DATE_ASSERTION_FORMAT.
+ get(value.toString(),date);
+ logError(message);
+ throw new DirectoryException(
+ ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
+ }
+
+ /**
+ * Since we reached here we have a valid assertion value. Construct
+ * a normalized value in the order: DATE MONTH YEAR.
+ */
+ ByteBuffer bb = ByteBuffer.allocate(3*4);
+ bb.putInt(date);
+ bb.putInt(month);
+ bb.putInt(year);
+ return ByteString.wrap(bb.array());
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ConditionResult valuesMatch(ByteSequence attributeValue,
+ ByteSequence assertionValue)
+ {
+ long timeInMS = ((ByteString)attributeValue).toLong();
+ //Build the information from the attribute value.
+ GregorianCalendar cal = new GregorianCalendar(TIME_ZONE_UTC_OBJ);
+ cal.setLenient(false);
+ cal.setTimeInMillis(timeInMS);
+ int date = cal.get(Calendar.DATE);
+ int month = cal.get(Calendar.MONTH);
+ int year = cal.get(Calendar.YEAR);
+
+ //Build the information from the assertion value.
+ ByteBuffer bb = ByteBuffer.wrap(assertionValue.toByteArray());
+ int assertDate = bb.getInt(0);
+ int assertMonth = bb.getInt(4);
+ int assertYear = bb.getInt(8);
+
+ //All the non-zero values should match.
+ if(assertDate !=0 && assertDate != date)
+ {
+ return ConditionResult.FALSE;
+ }
+
+ if(assertMonth !=-1 && assertMonth != month)
+ {
+ return ConditionResult.FALSE;
+ }
+
+ if(assertYear !=0 && assertYear != year)
+ {
+ return ConditionResult.FALSE;
+ }
+
+ return ConditionResult.TRUE;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection<ExtensibleIndexer> getIndexers(IndexConfig config)
+ {
+ if(indexer == null)
+ {
+ indexer = new PartialDateAndTimeExtensibleIndexer(this);
+ }
+ return Collections.singletonList(indexer);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public <T> T createIndexQuery(ByteSequence assertionValue,
+ IndexQueryFactory<T> factory) throws DirectoryException
+ {
+ //Build the information from the assertion value.
+ byte[] arr = normalizeAssertionValue(assertionValue).toByteArray();
+ ByteBuffer bb = ByteBuffer.wrap(arr);
+
+ int assertDate = bb.getInt(0);
+ int assertMonth = bb.getInt(4);
+ int assertYear = bb.getInt(8);
+ List<T> queries = new ArrayList<T>();
+
+ if(assertDate >0)
+ {
+ queries.add(factory.createExactMatchQuery(
+ indexer.getExtensibleIndexID(),
+ ByteString.valueOf(assertDate)));
+ }
+
+ if(assertMonth >=0)
+ {
+ queries.add(factory.createExactMatchQuery(
+ indexer.getExtensibleIndexID(),
+ ByteString.wrap(getMonthKey(assertMonth))));
+ }
+
+ if(assertYear > 0)
+ {
+ queries.add(factory.createExactMatchQuery(
+ indexer.getExtensibleIndexID(),
+ ByteString.valueOf(assertYear)));
+ }
+ return factory.createIntersectionQuery(queries);
+ }
+
+
+
+ /**
+ * Decomposes an attribute value into a set of partial date and time index
+ * keys.
+ *
+ * @param attValue
+ * The normalized attribute value
+ * @param set
+ * A set into which the keys will be inserted.
+ */
+ private void timeKeys(ByteString attributeValue, Set<byte[]> keys)
+ {
+ long timeInMS = 0L;
+ try
+ {
+ timeInMS = decodeGeneralizedTimeValue(attributeValue);
+ }
+ catch(DirectoryException de)
+ {
+ //If the schema check is on this should never reach here. If not then we
+ //would return from here.
+ return;
+ }
+ //Build the information from the attribute value.
+ GregorianCalendar cal = new GregorianCalendar(TIME_ZONE_UTC_OBJ);
+ cal.setTimeInMillis(timeInMS);
+ int date = cal.get(Calendar.DATE);
+ int month = cal.get(Calendar.MONTH);
+ int year = cal.get(Calendar.YEAR);
+
+ //Insert date.
+ if(date > 0)
+ {
+ keys.add(ByteString.valueOf(date).toByteArray());
+ }
+
+ //Insert month.
+ if(month >=0)
+ {
+ keys.add(getMonthKey(month));
+ }
+
+ if(year > 0)
+ {
+ keys.add(ByteString.valueOf(year).toByteArray());
+ }
+ }
+
+
+
+ //Returns a byte array of for the corresponding month.
+ private byte[] getMonthKey(int month)
+ {
+ byte[] key = null;
+ switch(month)
+ {
+ case Calendar.JANUARY:
+ key = JAN;
+ break;
+ case Calendar.FEBRUARY:
+ key = FEB;
+ break;
+ case Calendar.MARCH:
+ key = MAR;
+ break;
+ case Calendar.APRIL:
+ key = APR;
+ break;
+ case Calendar.MAY:
+ key = MAY;
+ break;
+ case Calendar.JUNE:
+ key = JUN;
+ break;
+ case Calendar.JULY:
+ key = JUL;
+ break;
+ case Calendar.AUGUST:
+ key = AUG;
+ break;
+ case Calendar.SEPTEMBER:
+ key = SEP;
+ break;
+ case Calendar.OCTOBER:
+ key = OCT;
+ break;
+ case Calendar.NOVEMBER:
+ key = NOV;
+ break;
+ case Calendar.DECEMBER:
+ key = DEC;
+ break;
+ default:
+ key = new byte[0];
+ }
+ return key;
+ }
+ }
+
+
+
+ /**
+ * Extensible Indexer class for Partial Date and Time Matching rules.
+ */
+ private final class PartialDateAndTimeExtensibleIndexer extends
+ ExtensibleIndexer
+ {
+ // The partial date and Time matching Rule.
+ private final PartialDateAndTimeMatchingRule matchingRule;
+
+
+
+ /**
+ * Creates a new instance of PartialDateAndTimeExtensibleIndexer.
+ *
+ * @param matchingRule
+ * The PartialDateAndTime Rule.
+ */
+ private PartialDateAndTimeExtensibleIndexer(
+ PartialDateAndTimeMatchingRule matchingRule)
+ {
+ this.matchingRule = matchingRule;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void getKeys(AttributeValue value, Set<byte[]> keys)
+ {
+ matchingRule.timeKeys(value.getValue(), keys);
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void getKeys(AttributeValue attValue,
+ Map<byte[], Boolean> modifiedKeys, Boolean insert)
+ {
+ Set<byte[]> keys = new HashSet<byte[]>();
+ getKeys(attValue, keys);
+ for (byte[] key : keys)
+ {
+ Boolean cInsert = modifiedKeys.get(key);
+ if (cInsert == null)
+ {
+ modifiedKeys.put(key, insert);
+ }
+ else if (!cInsert.equals(insert))
+ {
+ modifiedKeys.remove(key);
+ }
+ }
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getPreferredIndexName()
+ {
+ return PARTIAL_DATE_TIME_INDEX_NAME;
+ }
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getExtensibleIndexID()
+ {
+ return EXTENSIBLE_INDEXER_ID_DEFAULT;
+ }
+ }
+}
diff --git a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
index 7230cb6..045ec23 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -2954,6 +2954,14 @@
/**
+ * The extensible indexer identifier string that will be used for default
+ * type.
+ */
+ public static final String EXTENSIBLE_INDEXER_ID_DEFAULT="ext";
+
+
+
+ /**
* The lines that make up the CDDL header. They will not have any prefix, so
* an appropriate prefix may need to be added for some cases (e.g., "# " for
* shell scripts, "rem " for batch files, etc.).
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/TimeBasedMatchingRuleTest.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/TimeBasedMatchingRuleTest.java
new file mode 100644
index 0000000..9d32ae5
--- /dev/null
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/schema/TimeBasedMatchingRuleTest.java
@@ -0,0 +1,508 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
+ * add the following below this CDDL HEADER, with the fields enclosed
+ * by brackets "[]" replaced with your own identifying information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2009 Sun Microsystems, Inc.
+ */
+
+
+package org.opends.server.schema;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import static org.testng.Assert.*;
+
+import java.util.List;
+
+import java.util.TimeZone;
+import org.opends.server.TestCaseUtils;
+import org.opends.server.api.MatchingRule;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.internal.InternalSearchOperation;
+import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.DN;
+import org.opends.server.types.DereferencePolicy;
+import org.opends.server.types.DirectoryException;
+import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchScope;
+import org.opends.server.util.TimeThread;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.opends.server.schema.GeneralizedTimeSyntax.*;
+import static org.opends.server.schema.SchemaConstants.*;
+
+/**
+ * This class tests various time-based matching rules.
+ */
+public final class TimeBasedMatchingRuleTest
+ extends SchemaTestCase
+{
+ //User DNs to be used in tests.
+ private DN user1;
+
+ private DN user2 ;
+
+ private DN user3;
+
+ private DN user4;
+
+ private DN user5;
+
+
+ private final static String TIME_ATTR = "test-time-attribute";
+
+
+ GregorianCalendar cal = null;
+
+ /**
+ * Ensures that the Directory Server is running before executing the
+ * testcases.
+ *
+ * @throws Exception If an unexpected problem occurs.
+ */
+ @BeforeClass()
+ public void startServer()
+ throws Exception
+ {
+ TestCaseUtils.startServer();
+
+ TestCaseUtils.initializeTestBackend(true);
+
+ user1 = DN.decode("cn=user1,dc=example,dc=com");
+ user2 = DN.decode("cn=user2,dc=example,dc=com");
+ user3 = DN.decode("cn=user3,dc=example,dc=com");
+ user4 = DN.decode("cn=user4,dc=example,dc=com");
+ user5 = DN.decode("cn=user5,dc=example,dc=com");
+
+ /**
+ Extend the schema and add an attribute which is baseed on
+ generalizedTimeSyntax. Since all the existing attributes based
+ on that syntax are read-only, let us create a new attribute and
+ add it.*/
+ int resultCode = TestCaseUtils.applyModifications(true,
+ "dn: cn=schema",
+ "changetype: modify",
+ "add: attributeTypes",
+ "attributeTypes: ( test-time-oid NAME 'test-time-attribute' DESC 'Test time attribute' EQUALITY " +
+ "generalizedTimeMatch ORDERING generalizedTimeOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 SINGLE-VALUE )",
+ "-",
+ "add: objectclasses",
+ "objectclasses: ( oc-oid NAME 'testOC' SUP top AUXILIARY MUST test-time-attribute)"
+ );
+ assertTrue(resultCode == 0);
+ //Get the current time.
+ cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ cal.setLenient(false);
+ }
+
+
+
+ /**
+ * Test to search using the less-than relative time matching rule for expired events.
+ */
+ @Test()
+ public void testRTLessThanExpiredEvents() throws Exception
+ {
+ try
+ {
+ populateEntries();
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(
+ conn,
+ InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(),
+ null,
+ ByteString.valueOf("dc=example,dc=com"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ false,
+ LDAPFilter.decode(TIME_ATTR+":"+EXT_OMR_RELATIVE_TIME_LT_OID+":=-60m"), //
+ null, null);
+
+ searchOperation.run();
+ assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+ List<SearchResultEntry> entries = searchOperation.getSearchEntries();
+ assertTrue(dnFoundInEntryList(entries,user1,user2));
+ }
+ finally
+ {
+ TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com");
+ }
+ }
+
+
+
+ /**
+ * Test to search using the less-than relative time matching rule for future events.
+ */
+ @Test()
+ public void testRTLessThanFutureEvents() throws Exception
+ {
+ try
+ {
+ populateEntries();
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(
+ conn,
+ InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(),
+ null,
+ ByteString.valueOf("dc=example,dc=com"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ false,
+ LDAPFilter.decode(TIME_ATTR+":"+EXT_OMR_RELATIVE_TIME_LT_OID+":=1d"),
+ null, null);
+
+ searchOperation.run();
+ assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+ List<SearchResultEntry> entries = searchOperation.getSearchEntries();
+ assertTrue(entries.size() == 4 && dnFoundInEntryList(entries,user1,user2,user3,user5));
+ }
+ finally
+ {
+ TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com");
+ }
+ }
+
+
+
+ /**
+ * Test to search using the greater-than relative time matching rule for expired events.
+ */
+ @Test()
+ public void testRTGreaterThanExpiredEvents() throws Exception
+ {
+ try
+ {
+ populateEntries();
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(
+ conn,
+ InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(),
+ null,
+ ByteString.valueOf("dc=example,dc=com"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ false,
+ LDAPFilter.decode(TIME_ATTR+":"+EXT_OMR_RELATIVE_TIME_GT_OID+":=-1h"),
+ null, null);
+
+ searchOperation.run();
+ assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+ List<SearchResultEntry> entries = searchOperation.getSearchEntries();
+ assertTrue(entries.size()==3 && dnFoundInEntryList(entries,user3,user4,user5));
+ }
+ finally
+ {
+ TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com");
+ }
+ }
+
+
+
+ /**
+ * Test to search using the greater-than relative time matching rule for future events.
+ */
+ @Test()
+ public void testRTGreaterThanFutureEvents() throws Exception
+ {
+ try
+ {
+ populateEntries();
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(
+ conn,
+ InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(),
+ null,
+ ByteString.valueOf("dc=example,dc=com"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ false,
+ LDAPFilter.decode(TIME_ATTR+":"+EXT_OMR_RELATIVE_TIME_GT_OID+":=0s"),
+ null, null);
+
+ searchOperation.run();
+ assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+ List<SearchResultEntry> entries = searchOperation.getSearchEntries();
+ assertTrue(entries.size()==2 && dnFoundInEntryList(entries,user3,user4));
+ }
+ finally
+ {
+ TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com");
+ }
+ }
+
+
+
+ /**
+ * Test to search using the partial date and time matching rule for an assertion value.
+ */
+ @Test()
+ public void testPartialDateNTimeMatchingRule() throws Exception
+ {
+ try
+ {
+ populateEntries();
+ InternalClientConnection conn =
+ InternalClientConnection.getRootConnection();
+ int month = cal.get(Calendar.MONTH)+1; //month starts from 0 in the Calendar.
+ String assertion = cal.get(Calendar.DATE)+"DD"+month+"MM";
+
+ InternalSearchOperation searchOperation =
+ new InternalSearchOperation(
+ conn,
+ InternalClientConnection.nextOperationID(),
+ InternalClientConnection.nextMessageID(),
+ null,
+ ByteString.valueOf("dc=example,dc=com"),
+ SearchScope.WHOLE_SUBTREE,
+ DereferencePolicy.NEVER_DEREF_ALIASES,
+ Integer.MAX_VALUE,
+ Integer.MAX_VALUE,
+ false,
+ LDAPFilter.decode(TIME_ATTR+":"+EXT_PARTIAL_DATE_TIME_OID+":="+assertion),
+ null,null);
+
+ searchOperation.run();
+ assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
+ List<SearchResultEntry> entries = searchOperation.getSearchEntries();
+ assertTrue(entries.size()==3 && dnFoundInEntryList(entries,user1,user3,user5));
+ }
+ finally
+ {
+ TestCaseUtils.clearJEBackend(false, "userRoot", "dc=example,dc=com");
+ }
+ }
+
+
+
+ /**
+ * Tests the assertion syntax of the relative time matching rules.
+ */
+ @Test(dataProvider= "relativeTimeValues")
+ public void testRelativeTimeMatchingRuleAssertionSyntax(String assertion,boolean isValid)
+ {
+ MatchingRule relativeTimeLTRule =
+ DirectoryServer.getOrderingMatchingRule(
+ EXT_OMR_RELATIVE_TIME_LT_ALT_NAME.toLowerCase());
+ boolean exception = false;
+ try
+ {
+ relativeTimeLTRule.normalizeAssertionValue(ByteString.valueOf(assertion));
+ }
+ catch(DirectoryException e)
+ {
+ //invalid values will throw an exception.
+ exception = true;
+ assertTrue(!isValid);
+ }
+ if(!isValid)
+ {
+ //An invalid value can't get away without throwing exception.
+ assertTrue(exception);
+ }
+ }
+
+
+
+ /**
+ * Tests the assertion syntax of the partial date and time matching rules.
+ */
+ @Test(dataProvider= "partialDateTimeValues")
+ public void testPartialDateTimeMatchingRuleAssertionSyntax(String assertion,boolean isValid)
+ {
+ MatchingRule partialDTRule =
+ DirectoryServer.getMatchingRule(EXT_PARTIAL_DATE_TIME_OID);
+ boolean exception = false;
+ try
+ {
+ partialDTRule.normalizeAssertionValue(ByteString.valueOf(assertion));
+ }
+ catch(DirectoryException e)
+ {
+ //invalid values will throw an exception.
+ exception = true;
+ assertTrue(!isValid);
+ }
+ if(!isValid)
+ {
+ assertTrue(exception);
+ }
+ }
+
+
+
+ /**
+ * Generates data for testing relative time matching rule assertion syntax.
+ */
+ @DataProvider(name="relativeTimeValues")
+ private Object[][] createRelativeTimeValues()
+ {
+ return new Object[][] {
+ {"1s",true},
+ {"1s0d",false},
+ {"-1d",true},
+ {"2h",true},
+ {"+2w",true},
+ {"0",true},
+ {"0s",true},
+ {"0d",true},
+ {"xyz",false},
+ {"12w-2d",false},
+ {"1s2s",false},
+ {"1d4s5d",false}
+
+ };
+ }
+
+
+
+ /**
+ * Generates data for testing partial date and time assertion syntax.
+ */
+ @DataProvider(name="partialDateTimeValues")
+ private Object[][] createPartialDateTimeValues()
+ {
+ //Get the date today.
+ int date = cal.get(Calendar.DATE);
+ int month = cal.get(Calendar.MONTH);
+ int year = cal.get(Calendar.YEAR);
+
+ return new Object[][] {
+ {"20MM30DD1978YY",false},
+ {"02MM29DD2009YY",false},
+ {"02MM31DD2010YY",false},
+ {"02MM29DD2008YYYY",true},
+ {"DDYY",false},
+ {"02DD",true},
+ {"12MM",true},
+ {"1978YYYY",true},
+ {"0MM",false},
+ {"20MM03DD10MM",false},
+ {date+"DD",true},
+ {month+"MM",true},
+ {year+"YYYY",true},
+ {month+"MM"+date+"DD",true},
+ {year+"YYYY"+date+"DD",true},
+ {month+"MM"+year+"YYYY"+date+"DD",true}
+ };
+ }
+
+
+
+//validate if the args are found in the entries list.
+ private boolean dnFoundInEntryList( List<SearchResultEntry> entries,DN ... dns)
+ {
+ for(DN dn: dns)
+ {
+ boolean found = false;
+ for(SearchResultEntry entry: entries)
+ {
+ System.out.println("dn from the current entry is " + entry.getDN());
+ if(entry.getDN().equals(dn))
+ {
+ found = true;
+ }
+ }
+ if(!found)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ //Creates the entries.
+ private void populateEntries() throws Exception
+ {
+ //Get the current time from the TimeThread. Using the current time from new
+ // calendar may fail if the time thread using a stale time.
+ long currentTime = TimeThread.getTime();
+
+ TestCaseUtils.clearJEBackend(true, "userRoot", "dc=example,dc=com");
+ TestCaseUtils.addEntries(
+ "dn: cn=user1,dc=example,dc=com",
+ "objectclass: person",
+ "objectclass: testoc",
+ "cn: user1",
+ "sn: user1",
+ "test-time-attribute: "+ format(currentTime-4000*1000), //more than 1 hour old.
+ "",
+ "dn: cn=user2,dc=example,dc=com",
+ "objectclass: person",
+ "objectclass: testoc",
+ "cn: user2",
+ "sn: user2",
+ "test-time-attribute: " + format(currentTime-25*3600*1000), //more than a day old.
+ "",
+ "dn: cn=user3,dc=example,dc=com",
+ "objectclass: person",
+ "objectclass: testoc",
+ "cn: user3",
+ "sn: user3",
+ "test-time-attribute: " + format(currentTime+4000*1000), //more than 1 hour in advance.
+ "",
+ "dn: cn=user4,dc=example,dc=com",
+ "objectclass: person",
+ "objectclass: testoc",
+ "cn: user4",
+ "sn: user4",
+ "test-time-attribute: " + format(currentTime+25*3600*1000), // more than 1 day in advance
+ "",
+ "dn: cn=user5,dc=example,dc=com",
+ "objectclass: person",
+ "objectclass: testoc",
+ "cn: user4",
+ "sn: user4",
+ "test-time-attribute: " + format(currentTime) // now.
+ );
+ }
+}
--
Gitblit v1.10.0