001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.codec.controls.search.entryChange; 021 022 023import java.nio.ByteBuffer; 024 025import org.apache.directory.api.asn1.Asn1Object; 026import org.apache.directory.api.asn1.DecoderException; 027import org.apache.directory.api.asn1.EncoderException; 028import org.apache.directory.api.asn1.ber.Asn1Decoder; 029import org.apache.directory.api.asn1.ber.tlv.BerValue; 030import org.apache.directory.api.asn1.ber.tlv.TLV; 031import org.apache.directory.api.asn1.ber.tlv.UniversalTag; 032import org.apache.directory.api.i18n.I18n; 033import org.apache.directory.api.ldap.codec.api.ControlDecorator; 034import org.apache.directory.api.ldap.codec.api.LdapApiService; 035import org.apache.directory.api.ldap.model.message.controls.ChangeType; 036import org.apache.directory.api.ldap.model.message.controls.EntryChange; 037import org.apache.directory.api.ldap.model.message.controls.EntryChangeImpl; 038import org.apache.directory.api.ldap.model.name.Dn; 039import org.apache.directory.api.util.Strings; 040 041 042/** 043 * An EntryChange implementation, that wraps and decorates the Control with codec 044 * specific functionality. 045 * 046 * 047 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 048 */ 049public class EntryChangeDecorator extends ControlDecorator<EntryChange> implements EntryChange 050{ 051 /** Default value when no change number is provided */ 052 public static final int UNDEFINED_CHANGE_NUMBER = -1; 053 054 /** A temporary storage for the previous Dn */ 055 private byte[] previousDnBytes = null; 056 057 /** The entry change global length */ 058 private int eccSeqLength; 059 060 /** An instance of this decoder */ 061 private static final Asn1Decoder DECODER = new Asn1Decoder(); 062 063 064 /** 065 * Creates a new instance of EntryChangeDecoder wrapping a newly created 066 * EntryChange Control object. 067 * 068 * @param codec The LDAP service instance 069 */ 070 public EntryChangeDecorator( LdapApiService codec ) 071 { 072 super( codec, new EntryChangeImpl() ); 073 } 074 075 076 /** 077 * Creates a new instance of EntryChangeDecorator wrapping the supplied 078 * EntryChange Control. 079 * 080 * @param codec The LDAP service instance 081 * @param control The EntryChange Control to be decorated. 082 */ 083 public EntryChangeDecorator( LdapApiService codec, EntryChange control ) 084 { 085 super( codec, control ); 086 } 087 088 089 /** 090 * Internally used to not have to cast the decorated Control. 091 * 092 * @return the decorated Control. 093 */ 094 private EntryChange getEntryChange() 095 { 096 return getDecorated(); 097 } 098 099 100 /** 101 * Compute the EntryChangeControl length 102 * 103 * <pre> 104 * 0x30 L1 105 * | 106 * +--> 0x0A 0x0(1-4) [1|2|4|8] (changeType) 107 * [+--> 0x04 L2 previousDN] 108 * [+--> 0x02 0x0(1-4) [0..2^63-1] (changeNumber)] 109 * </pre> 110 * 111 * @return the control length. 112 */ 113 @Override 114 public int computeLength() 115 { 116 int changeTypesLength = 1 + 1 + 1; 117 118 int previousDnLength = 0; 119 int changeNumberLength = 0; 120 121 if ( getPreviousDn() != null ) 122 { 123 previousDnBytes = Strings.getBytesUtf8( getPreviousDn().getName() ); 124 previousDnLength = 1 + TLV.getNbBytes( previousDnBytes.length ) + previousDnBytes.length; 125 } 126 127 if ( getChangeNumber() != UNDEFINED_CHANGE_NUMBER ) 128 { 129 changeNumberLength = 1 + 1 + BerValue.getNbBytes( getChangeNumber() ); 130 } 131 132 eccSeqLength = changeTypesLength + previousDnLength + changeNumberLength; 133 valueLength = 1 + TLV.getNbBytes( eccSeqLength ) + eccSeqLength; 134 135 return valueLength; 136 } 137 138 139 /** 140 * Encodes the entry change control. 141 * 142 * @param buffer The encoded sink 143 * @return A ByteBuffer that contains the encoded PDU 144 * @throws EncoderException If anything goes wrong. 145 */ 146 @Override 147 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 148 { 149 if ( buffer == null ) 150 { 151 throw new EncoderException( I18n.err( I18n.ERR_04023 ) ); 152 } 153 154 buffer.put( UniversalTag.SEQUENCE.getValue() ); 155 buffer.put( TLV.getBytes( eccSeqLength ) ); 156 157 buffer.put( UniversalTag.ENUMERATED.getValue() ); 158 buffer.put( ( byte ) 1 ); 159 buffer.put( BerValue.getBytes( getChangeType().getValue() ) ); 160 161 if ( getPreviousDn() != null ) 162 { 163 BerValue.encode( buffer, previousDnBytes ); 164 } 165 166 if ( getChangeNumber() != UNDEFINED_CHANGE_NUMBER ) 167 { 168 BerValue.encode( buffer, getChangeNumber() ); 169 } 170 171 return buffer; 172 } 173 174 175 /** 176 * {@inheritDoc} 177 */ 178 @Override 179 public byte[] getValue() 180 { 181 if ( value == null ) 182 { 183 try 184 { 185 computeLength(); 186 ByteBuffer buffer = ByteBuffer.allocate( valueLength ); 187 188 buffer.put( UniversalTag.SEQUENCE.getValue() ); 189 buffer.put( TLV.getBytes( eccSeqLength ) ); 190 191 buffer.put( UniversalTag.ENUMERATED.getValue() ); 192 buffer.put( ( byte ) 1 ); 193 buffer.put( BerValue.getBytes( getChangeType().getValue() ) ); 194 195 if ( getPreviousDn() != null ) 196 { 197 BerValue.encode( buffer, previousDnBytes ); 198 } 199 200 if ( getChangeNumber() != UNDEFINED_CHANGE_NUMBER ) 201 { 202 BerValue.encode( buffer, getChangeNumber() ); 203 } 204 205 value = buffer.array(); 206 } 207 catch ( Exception e ) 208 { 209 return null; 210 } 211 } 212 213 return value; 214 } 215 216 217 /** 218 * {@inheritDoc} 219 */ 220 @Override 221 public ChangeType getChangeType() 222 { 223 return getEntryChange().getChangeType(); 224 } 225 226 227 /** 228 * {@inheritDoc} 229 */ 230 @Override 231 public void setChangeType( ChangeType changeType ) 232 { 233 getEntryChange().setChangeType( changeType ); 234 } 235 236 237 /** 238 * {@inheritDoc} 239 */ 240 @Override 241 public Dn getPreviousDn() 242 { 243 return getEntryChange().getPreviousDn(); 244 } 245 246 247 /** 248 * {@inheritDoc} 249 */ 250 @Override 251 public void setPreviousDn( Dn previousDn ) 252 { 253 getEntryChange().setPreviousDn( previousDn ); 254 } 255 256 257 /** 258 * {@inheritDoc} 259 */ 260 @Override 261 public long getChangeNumber() 262 { 263 return getEntryChange().getChangeNumber(); 264 } 265 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override 271 public void setChangeNumber( long changeNumber ) 272 { 273 getEntryChange().setChangeNumber( changeNumber ); 274 } 275 276 277 /** 278 * {@inheritDoc} 279 */ 280 @Override 281 public Asn1Object decode( byte[] controlBytes ) throws DecoderException 282 { 283 ByteBuffer bb = ByteBuffer.wrap( controlBytes ); 284 EntryChangeContainer container = new EntryChangeContainer( getCodecService(), this ); 285 DECODER.decode( bb, container ); 286 return this; 287 } 288}