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 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 public EntryChangeDecorator( LdapApiService codec ) 069 { 070 super( codec, new EntryChangeImpl() ); 071 } 072 073 074 /** 075 * Creates a new instance of EntryChangeDecorator wrapping the supplied 076 * EntryChange Control. 077 * 078 * @param control The EntryChange Control to be decorated. 079 */ 080 public EntryChangeDecorator( LdapApiService codec, EntryChange control ) 081 { 082 super( codec, control ); 083 } 084 085 086 /** 087 * Internally used to not have to cast the decorated Control. 088 * 089 * @return the decorated Control. 090 */ 091 private EntryChange getEntryChange() 092 { 093 return ( EntryChange ) getDecorated(); 094 } 095 096 097 /** 098 * Compute the EntryChangeControl length 099 * 100 * 0x30 L1 101 * | 102 * +--> 0x0A 0x0(1-4) [1|2|4|8] (changeType) 103 * [+--> 0x04 L2 previousDN] 104 * [+--> 0x02 0x0(1-4) [0..2^63-1] (changeNumber)] 105 */ 106 public int computeLength() 107 { 108 int changeTypesLength = 1 + 1 + 1; 109 110 int previousDnLength = 0; 111 int changeNumberLength = 0; 112 113 if ( getPreviousDn() != null ) 114 { 115 previousDnBytes = Strings.getBytesUtf8( getPreviousDn().getName() ); 116 previousDnLength = 1 + TLV.getNbBytes( previousDnBytes.length ) + previousDnBytes.length; 117 } 118 119 if ( getChangeNumber() != UNDEFINED_CHANGE_NUMBER ) 120 { 121 changeNumberLength = 1 + 1 + BerValue.getNbBytes( getChangeNumber() ); 122 } 123 124 eccSeqLength = changeTypesLength + previousDnLength + changeNumberLength; 125 valueLength = 1 + TLV.getNbBytes( eccSeqLength ) + eccSeqLength; 126 127 return valueLength; 128 } 129 130 131 /** 132 * Encodes the entry change control. 133 * 134 * @param buffer The encoded sink 135 * @return A ByteBuffer that contains the encoded PDU 136 * @throws EncoderException If anything goes wrong. 137 */ 138 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 139 { 140 if ( buffer == null ) 141 { 142 throw new EncoderException( I18n.err( I18n.ERR_04023 ) ); 143 } 144 145 buffer.put( UniversalTag.SEQUENCE.getValue() ); 146 buffer.put( TLV.getBytes( eccSeqLength ) ); 147 148 buffer.put( UniversalTag.ENUMERATED.getValue() ); 149 buffer.put( ( byte ) 1 ); 150 buffer.put( BerValue.getBytes( getChangeType().getValue() ) ); 151 152 if ( getPreviousDn() != null ) 153 { 154 BerValue.encode( buffer, previousDnBytes ); 155 } 156 157 if ( getChangeNumber() != UNDEFINED_CHANGE_NUMBER ) 158 { 159 BerValue.encode( buffer, getChangeNumber() ); 160 } 161 162 return buffer; 163 } 164 165 166 /** 167 * {@inheritDoc} 168 */ 169 public byte[] getValue() 170 { 171 if ( value == null ) 172 { 173 try 174 { 175 computeLength(); 176 ByteBuffer buffer = ByteBuffer.allocate( valueLength ); 177 178 buffer.put( UniversalTag.SEQUENCE.getValue() ); 179 buffer.put( TLV.getBytes( eccSeqLength ) ); 180 181 buffer.put( UniversalTag.ENUMERATED.getValue() ); 182 buffer.put( ( byte ) 1 ); 183 buffer.put( BerValue.getBytes( getChangeType().getValue() ) ); 184 185 if ( getPreviousDn() != null ) 186 { 187 BerValue.encode( buffer, previousDnBytes ); 188 } 189 190 if ( getChangeNumber() != UNDEFINED_CHANGE_NUMBER ) 191 { 192 BerValue.encode( buffer, getChangeNumber() ); 193 } 194 195 value = buffer.array(); 196 } 197 catch ( Exception e ) 198 { 199 return null; 200 } 201 } 202 203 return value; 204 } 205 206 207 /** 208 * {@inheritDoc} 209 */ 210 public ChangeType getChangeType() 211 { 212 return getEntryChange().getChangeType(); 213 } 214 215 216 /** 217 * {@inheritDoc} 218 */ 219 public void setChangeType( ChangeType changeType ) 220 { 221 getEntryChange().setChangeType( changeType ); 222 } 223 224 225 /** 226 * {@inheritDoc} 227 */ 228 public Dn getPreviousDn() 229 { 230 return getEntryChange().getPreviousDn(); 231 } 232 233 234 /** 235 * {@inheritDoc} 236 */ 237 public void setPreviousDn( Dn previousDn ) 238 { 239 getEntryChange().setPreviousDn( previousDn ); 240 } 241 242 243 /** 244 * {@inheritDoc} 245 */ 246 public long getChangeNumber() 247 { 248 return getEntryChange().getChangeNumber(); 249 } 250 251 252 /** 253 * {@inheritDoc} 254 */ 255 public void setChangeNumber( long changeNumber ) 256 { 257 getEntryChange().setChangeNumber( changeNumber ); 258 } 259 260 261 /** 262 * {@inheritDoc} 263 */ 264 public Asn1Object decode( byte[] controlBytes ) throws DecoderException 265 { 266 ByteBuffer bb = ByteBuffer.wrap( controlBytes ); 267 EntryChangeContainer container = new EntryChangeContainer( getCodecService(), this ); 268 decoder.decode( bb, container ); 269 return this; 270 } 271}