001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2012-2016 ForgeRock AS. 016 */ 017package org.opends.server.replication.plugin; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.List; 022 023import org.forgerock.opendj.ldap.Assertion; 024import org.forgerock.opendj.ldap.ByteSequence; 025import org.forgerock.opendj.ldap.ByteString; 026import org.forgerock.opendj.ldap.ByteStringBuilder; 027import org.forgerock.opendj.ldap.ConditionResult; 028import org.forgerock.opendj.ldap.DecodeException; 029import org.forgerock.opendj.ldap.schema.MatchingRuleImpl; 030import org.forgerock.opendj.ldap.schema.Schema; 031import org.forgerock.opendj.ldap.schema.SchemaBuilder; 032import org.forgerock.opendj.ldap.spi.IndexQueryFactory; 033import org.forgerock.opendj.ldap.spi.Indexer; 034import org.forgerock.opendj.ldap.spi.IndexingOptions; 035import org.opends.server.replication.common.CSN; 036 037import static org.forgerock.opendj.ldap.Assertion.*; 038import static org.opends.messages.ReplicationMessages.*; 039import static org.opends.server.util.StaticUtils.*; 040 041/** 042 * Matching rule used to establish an order between historical information and index them. 043 */ 044public final class HistoricalCsnOrderingMatchingRuleImpl implements MatchingRuleImpl 045{ 046 private static final String ORDERING_ID = "changeSequenceNumberOrderingMatch"; 047 048 private final Collection<? extends Indexer> indexers = Collections.singleton(new HistoricalIndexer()); 049 050 /** Indexer for the matching rule. */ 051 private final class HistoricalIndexer implements Indexer 052 { 053 @Override 054 public void createKeys(Schema schema, ByteSequence value, Collection<ByteString> keys) throws DecodeException 055 { 056 keys.add(normalizeAttributeValue(schema, value)); 057 } 058 059 @Override 060 public String getIndexID() 061 { 062 return ORDERING_ID; 063 } 064 065 @Override 066 public String keyToHumanReadableString(ByteSequence key) 067 { 068 ByteStringBuilder bsb = new ByteStringBuilder(); 069 bsb.appendBytes(key.subSequence(2, 10)); 070 bsb.appendBytes(key.subSequence(0, 2)); 071 bsb.appendBytes(key.subSequence(10, 14)); 072 CSN csn = CSN.valueOf(bsb.toByteString()); 073 return csn.toStringUI(); 074 } 075 } 076 077 /** {@inheritDoc} */ 078 @Override 079 public ByteString normalizeAttributeValue(Schema schema, ByteSequence value) throws DecodeException 080 { 081 /* 082 * Change the format of the value to index and start with the serverId. In 083 * that manner, the search response time is optimized for a particular 084 * serverId. The format of the key is now : serverId + timestamp + seqNum 085 */ 086 try 087 { 088 int csnIndex = value.toString().indexOf(':') + 1; 089 String csn = value.subSequence(csnIndex, csnIndex + 28).toString(); 090 ByteStringBuilder builder = new ByteStringBuilder(14); 091 builder.appendBytes(hexStringToByteArray(csn.substring(16, 20))); 092 builder.appendBytes(hexStringToByteArray(csn.substring(0, 16))); 093 builder.appendBytes(hexStringToByteArray(csn.substring(20, 28))); 094 return builder.toByteString(); 095 } 096 catch (Exception e) 097 { 098 // This should never occur in practice since these attributes are managed 099 // internally. 100 throw DecodeException.error(WARN_INVALID_SYNC_HIST_VALUE.get(value), e); 101 } 102 } 103 104 /** {@inheritDoc} */ 105 @Override 106 public Assertion getAssertion(final Schema schema, final ByteSequence value) throws DecodeException 107 { 108 final ByteString normAssertion = normalizeAttributeValue(schema, value); 109 return new Assertion() 110 { 111 @Override 112 public ConditionResult matches(final ByteSequence attributeValue) 113 { 114 return ConditionResult.valueOf(attributeValue.compareTo(normAssertion) < 0); 115 } 116 117 @Override 118 public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException 119 { 120 return factory.createRangeMatchQuery(ORDERING_ID, ByteString.empty(), normAssertion, false, false); 121 } 122 }; 123 } 124 125 /** {@inheritDoc} */ 126 @Override 127 public Assertion getSubstringAssertion(Schema schema, ByteSequence subInitial, 128 List<? extends ByteSequence> subAnyElements, ByteSequence subFinal) throws DecodeException 129 { 130 return UNDEFINED_ASSERTION; 131 } 132 133 /** {@inheritDoc} */ 134 @Override 135 public Assertion getGreaterOrEqualAssertion(Schema schema, ByteSequence value) throws DecodeException 136 { 137 final ByteString normAssertion = normalizeAttributeValue(schema, value); 138 return new Assertion() 139 { 140 @Override 141 public ConditionResult matches(final ByteSequence normalizedAttributeValue) 142 { 143 return ConditionResult.valueOf(normalizedAttributeValue.compareTo(normAssertion) >= 0); 144 } 145 146 @Override 147 public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException 148 { 149 return factory.createRangeMatchQuery(ORDERING_ID, normAssertion, ByteString.empty(), true, false); 150 } 151 }; 152 } 153 154 /** {@inheritDoc} */ 155 @Override 156 public Assertion getLessOrEqualAssertion(Schema schema, ByteSequence value) throws DecodeException 157 { 158 final ByteString normAssertion = normalizeAttributeValue(schema, value); 159 return new Assertion() 160 { 161 @Override 162 public ConditionResult matches(final ByteSequence normalizedAttributeValue) 163 { 164 return ConditionResult.valueOf(normalizedAttributeValue.compareTo(normAssertion) <= 0); 165 } 166 167 @Override 168 public <T> T createIndexQuery(IndexQueryFactory<T> factory) throws DecodeException 169 { 170 return factory.createRangeMatchQuery(ORDERING_ID, ByteString.empty(), normAssertion, false, true); 171 } 172 }; 173 } 174 175 @Override 176 public Collection<? extends Indexer> createIndexers(IndexingOptions options) 177 { 178 return indexers; 179 } 180 181 /** 182 * Adds the historical csn ordering matching rule to the provided schema builder. 183 * 184 * @param builder 185 * where to add the historical csn ordering matching rule 186 * @return the provided builder 187 */ 188 public static SchemaBuilder addHistoricalCsnOrderingMatchingRule(SchemaBuilder builder) 189 { 190 return builder 191 .buildMatchingRule("1.3.6.1.4.1.26027.1.4.4") 192 .names("historicalCsnOrderingMatch") 193 .syntaxOID("1.3.6.1.4.1.1466.115.121.1.40") 194 .implementation(new HistoricalCsnOrderingMatchingRuleImpl()) 195 .addToSchema(); 196 } 197}