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.extras.controls.syncrepl_impl; 021 022 023import java.nio.ByteBuffer; 024import java.util.ArrayList; 025import java.util.List; 026 027import org.apache.directory.api.asn1.Asn1Object; 028import org.apache.directory.api.asn1.DecoderException; 029import org.apache.directory.api.asn1.EncoderException; 030import org.apache.directory.api.asn1.ber.Asn1Decoder; 031import org.apache.directory.api.asn1.ber.tlv.BerValue; 032import org.apache.directory.api.asn1.ber.tlv.TLV; 033import org.apache.directory.api.asn1.ber.tlv.UniversalTag; 034import org.apache.directory.api.i18n.I18n; 035import org.apache.directory.api.ldap.codec.api.ControlDecorator; 036import org.apache.directory.api.ldap.codec.api.LdapApiService; 037import org.apache.directory.api.ldap.extras.controls.syncrepl.syncInfoValue.SyncInfoValue; 038import org.apache.directory.api.ldap.extras.controls.syncrepl.syncInfoValue.SyncInfoValueImpl; 039import org.apache.directory.api.ldap.extras.controls.syncrepl.syncInfoValue.SynchronizationInfoEnum; 040import org.apache.directory.api.util.Strings; 041 042 043/** 044 * A syncInfoValue object, as defined in RFC 4533 045 * 046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 047 */ 048public class SyncInfoValueDecorator extends ControlDecorator<SyncInfoValue> implements SyncInfoValue 049{ 050 /** The syncUUIDs cumulative length */ 051 private int syncUUIDsLength; 052 053 /** An instance of this decoder */ 054 private static final Asn1Decoder decoder = new Asn1Decoder(); 055 056 057 /** 058 * The constructor for this codec. Dont't forget to set the type. 059 */ 060 public SyncInfoValueDecorator( LdapApiService codec ) 061 { 062 super( codec, new SyncInfoValueImpl() ); 063 } 064 065 066 /** 067 * The constructor for this codec. Dont't forget to set the type. 068 */ 069 public SyncInfoValueDecorator( LdapApiService codec, SyncInfoValue control ) 070 { 071 super( codec, control ); 072 } 073 074 075 /** 076 * The constructor for this codec. 077 * @param type The kind of syncInfo we will store. Can be newCookie, 078 * refreshPresent, refreshDelete or syncIdSet 079 */ 080 public SyncInfoValueDecorator( LdapApiService codec, SynchronizationInfoEnum type ) 081 { 082 this( codec ); 083 084 setType( type ); 085 } 086 087 /** The global length for this control */ 088 private int syncInfoValueLength; 089 090 091 /** 092 * {@inheritDoc} 093 */ 094 public SynchronizationInfoEnum getType() 095 { 096 return getDecorated().getType(); 097 } 098 099 100 /** 101 * {@inheritDoc} 102 */ 103 public void setType( SynchronizationInfoEnum type ) 104 { 105 this.getDecorated().setType( type ); 106 107 // Initialize the arrayList if needed 108 if ( ( type == SynchronizationInfoEnum.SYNC_ID_SET ) && ( getDecorated().getSyncUUIDs() == null ) ) 109 { 110 getDecorated().setSyncUUIDs( new ArrayList<byte[]>() ); 111 } 112 } 113 114 115 /** 116 * {@inheritDoc} 117 */ 118 public byte[] getCookie() 119 { 120 return getDecorated().getCookie(); 121 } 122 123 124 /** 125 * {@inheritDoc} 126 */ 127 public void setCookie( byte[] cookie ) 128 { 129 // Copy the bytes 130 if ( !Strings.isEmpty( cookie ) ) 131 { 132 byte[] copy = new byte[cookie.length]; 133 System.arraycopy( cookie, 0, copy, 0, cookie.length ); 134 getDecorated().setCookie( copy ); 135 } 136 else 137 { 138 getDecorated().setCookie( null ); 139 } 140 } 141 142 143 /** 144 * {@inheritDoc} 145 */ 146 public boolean isRefreshDone() 147 { 148 return getDecorated().isRefreshDone(); 149 } 150 151 152 /** 153 * {@inheritDoc} 154 */ 155 public void setRefreshDone( boolean refreshDone ) 156 { 157 getDecorated().setRefreshDone( refreshDone ); 158 } 159 160 161 /** 162 * {@inheritDoc} 163 */ 164 public boolean isRefreshDeletes() 165 { 166 return getDecorated().isRefreshDeletes(); 167 } 168 169 170 /** 171 * {@inheritDoc} 172 */ 173 public void setRefreshDeletes( boolean refreshDeletes ) 174 { 175 getDecorated().setRefreshDeletes( refreshDeletes ); 176 } 177 178 179 /** 180 * {@inheritDoc} 181 */ 182 public List<byte[]> getSyncUUIDs() 183 { 184 return getDecorated().getSyncUUIDs(); 185 } 186 187 188 /** 189 * {@inheritDoc} 190 */ 191 public void setSyncUUIDs( List<byte[]> syncUUIDs ) 192 { 193 getDecorated().setSyncUUIDs( syncUUIDs ); 194 } 195 196 197 /** 198 * {@inheritDoc} 199 */ 200 public void addSyncUUID( byte[] syncUUID ) 201 { 202 getDecorated().addSyncUUID( syncUUID ); 203 } 204 205 206 /** 207 * Compute the SyncInfoValue length. 208 * 209 * SyncInfoValue : 210 * 211 * 0xA0 L1 abcd // newCookie 212 * 0xA1 L2 // refreshDelete 213 * | 214 * [+--> 0x04 L3 abcd] // cookie 215 * [+--> 0x01 0x01 (0x00|0xFF) // refreshDone 216 * 0xA2 L4 // refreshPresent 217 * | 218 * [+--> 0x04 L5 abcd] // cookie 219 * [+--> 0x01 0x01 (0x00|0xFF) // refreshDone 220 * 0xA3 L6 // syncIdSet 221 * | 222 * [+--> 0x04 L7 abcd] // cookie 223 * [+--> 0x01 0x01 (0x00|0xFF) // refreshDeletes 224 * +--> 0x31 L8 // SET OF syncUUIDs 225 * | 226 * [+--> 0x04 L9 abcd] // syncUUID public static final int AND_FILTER_TAG = 0xA0; 227 228 public static final int OR_FILTER_TAG = 0xA1; 229 230 public static final int NOT_FILTER_TAG = 0xA2; 231 232 public static final int BIND_REQUEST_SASL_TAG = 0xA3; 233 234 */ 235 @Override 236 public int computeLength() 237 { 238 // The mode length 239 syncInfoValueLength = 0; 240 241 switch ( getType() ) 242 { 243 case NEW_COOKIE: 244 if ( getCookie() != null ) 245 { 246 syncInfoValueLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length; 247 } 248 else 249 { 250 syncInfoValueLength = 1 + 1; 251 } 252 253 valueLength = syncInfoValueLength; 254 255 // Call the super class to compute the global control length 256 return valueLength; 257 258 case REFRESH_DELETE: 259 case REFRESH_PRESENT: 260 if ( getCookie() != null ) 261 { 262 syncInfoValueLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length; 263 } 264 265 // The refreshDone flag, only if not true, as it default to true 266 if ( !isRefreshDone() ) 267 { 268 syncInfoValueLength += 1 + 1 + 1; 269 } 270 271 valueLength = 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength; 272 273 // Call the super class to compute the global control length 274 return valueLength; 275 276 case SYNC_ID_SET: 277 if ( getCookie() != null ) 278 { 279 syncInfoValueLength = 1 + TLV.getNbBytes( getCookie().length ) + getCookie().length; 280 } 281 282 // The refreshDeletes flag, default to false 283 if ( isRefreshDeletes() ) 284 { 285 syncInfoValueLength += 1 + 1 + 1; 286 } 287 288 // The syncUUIDs if any 289 syncUUIDsLength = 0; 290 291 if ( getSyncUUIDs().size() != 0 ) 292 { 293 for ( byte[] syncUUID : getSyncUUIDs() ) 294 { 295 int uuidLength = 1 + TLV.getNbBytes( syncUUID.length ) + syncUUID.length; 296 297 syncUUIDsLength += uuidLength; 298 } 299 } 300 301 syncInfoValueLength += 1 + TLV.getNbBytes( syncUUIDsLength ) + syncUUIDsLength; 302 valueLength = 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength; 303 304 // Call the super class to compute the global control length 305 return valueLength; 306 307 default: 308 309 } 310 311 return 1 + TLV.getNbBytes( syncInfoValueLength ) + syncInfoValueLength; 312 } 313 314 315 /** 316 * Encode the SyncInfoValue control 317 * 318 * @param buffer The encoded sink 319 * @return A ByteBuffer that contains the encoded PDU 320 * @throws EncoderException If anything goes wrong. 321 */ 322 @Override 323 public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException 324 { 325 if ( buffer == null ) 326 { 327 throw new EncoderException( I18n.err( I18n.ERR_04023 ) ); 328 } 329 330 switch ( getType() ) 331 { 332 case NEW_COOKIE: 333 // The first case : newCookie 334 buffer.put( ( byte ) SyncInfoValueTags.NEW_COOKIE_TAG.getValue() ); 335 336 // As the OCTET_STRING is absorbed by the Application tag, 337 // we have to store the L and V separately 338 if ( ( getCookie() == null ) || ( getCookie().length == 0 ) ) 339 { 340 buffer.put( ( byte ) 0 ); 341 } 342 else 343 { 344 buffer.put( TLV.getBytes( getCookie().length ) ); 345 buffer.put( getCookie() ); 346 } 347 348 break; 349 350 case REFRESH_DELETE: 351 // The second case : refreshDelete 352 buffer.put( ( byte ) SyncInfoValueTags.REFRESH_DELETE_TAG.getValue() ); 353 buffer.put( TLV.getBytes( syncInfoValueLength ) ); 354 355 // The cookie, if any 356 if ( getCookie() != null ) 357 { 358 BerValue.encode( buffer, getCookie() ); 359 } 360 361 // The refreshDone flag 362 if ( !isRefreshDone() ) 363 { 364 BerValue.encode( buffer, isRefreshDone() ); 365 } 366 367 break; 368 369 case REFRESH_PRESENT: 370 // The third case : refreshPresent 371 buffer.put( ( byte ) SyncInfoValueTags.REFRESH_PRESENT_TAG.getValue() ); 372 buffer.put( TLV.getBytes( syncInfoValueLength ) ); 373 374 // The cookie, if any 375 if ( getCookie() != null ) 376 { 377 BerValue.encode( buffer, getCookie() ); 378 } 379 380 // The refreshDone flag 381 if ( !isRefreshDone() ) 382 { 383 BerValue.encode( buffer, isRefreshDone() ); 384 } 385 386 break; 387 388 case SYNC_ID_SET: 389 // The last case : syncIdSet 390 buffer.put( ( byte ) SyncInfoValueTags.SYNC_ID_SET_TAG.getValue() ); 391 buffer.put( TLV.getBytes( syncInfoValueLength ) ); 392 393 // The cookie, if any 394 if ( getCookie() != null ) 395 { 396 BerValue.encode( buffer, getCookie() ); 397 } 398 399 // The refreshDeletes flag if not false 400 if ( isRefreshDeletes() ) 401 { 402 BerValue.encode( buffer, isRefreshDeletes() ); 403 } 404 405 // The syncUUIDs 406 buffer.put( UniversalTag.SET.getValue() ); 407 buffer.put( TLV.getBytes( syncUUIDsLength ) ); 408 409 // Loop on the UUIDs if any 410 if ( getSyncUUIDs().size() != 0 ) 411 { 412 for ( byte[] syncUUID : getSyncUUIDs() ) 413 { 414 BerValue.encode( buffer, syncUUID ); 415 } 416 } 417 } 418 419 return buffer; 420 } 421 422 423 /** 424 * {@inheritDoc} 425 */ 426 @Override 427 public byte[] getValue() 428 { 429 if ( value == null ) 430 { 431 try 432 { 433 computeLength(); 434 ByteBuffer buffer = ByteBuffer.allocate( valueLength ); 435 436 switch ( getType() ) 437 { 438 case NEW_COOKIE: 439 // The first case : newCookie 440 buffer.put( ( byte ) SyncInfoValueTags.NEW_COOKIE_TAG.getValue() ); 441 442 // As the OCTET_STRING is absorbed by the Application tag, 443 // we have to store the L and V separately 444 if ( ( getCookie() == null ) || ( getCookie().length == 0 ) ) 445 { 446 buffer.put( ( byte ) 0 ); 447 } 448 else 449 { 450 buffer.put( TLV.getBytes( getCookie().length ) ); 451 buffer.put( getCookie() ); 452 } 453 454 break; 455 456 case REFRESH_DELETE: 457 // The second case : refreshDelete 458 buffer.put( ( byte ) SyncInfoValueTags.REFRESH_DELETE_TAG.getValue() ); 459 buffer.put( TLV.getBytes( syncInfoValueLength ) ); 460 461 // The cookie, if any 462 if ( getCookie() != null ) 463 { 464 BerValue.encode( buffer, getCookie() ); 465 } 466 467 // The refreshDone flag 468 if ( !isRefreshDone() ) 469 { 470 BerValue.encode( buffer, isRefreshDone() ); 471 } 472 473 break; 474 475 case REFRESH_PRESENT: 476 // The third case : refreshPresent 477 buffer.put( ( byte ) SyncInfoValueTags.REFRESH_PRESENT_TAG.getValue() ); 478 buffer.put( TLV.getBytes( syncInfoValueLength ) ); 479 480 // The cookie, if any 481 if ( getCookie() != null ) 482 { 483 BerValue.encode( buffer, getCookie() ); 484 } 485 486 // The refreshDone flag 487 if ( !isRefreshDone() ) 488 { 489 BerValue.encode( buffer, isRefreshDone() ); 490 } 491 492 break; 493 494 case SYNC_ID_SET: 495 // The last case : syncIdSet 496 buffer.put( ( byte ) SyncInfoValueTags.SYNC_ID_SET_TAG.getValue() ); 497 buffer.put( TLV.getBytes( syncInfoValueLength ) ); 498 499 // The cookie, if any 500 if ( getCookie() != null ) 501 { 502 BerValue.encode( buffer, getCookie() ); 503 } 504 505 // The refreshDeletes flag if not false 506 if ( isRefreshDeletes() ) 507 { 508 BerValue.encode( buffer, isRefreshDeletes() ); 509 } 510 511 // The syncUUIDs 512 buffer.put( UniversalTag.SET.getValue() ); 513 buffer.put( TLV.getBytes( syncUUIDsLength ) ); 514 515 // Loop on the UUIDs if any 516 if ( getSyncUUIDs().size() != 0 ) 517 { 518 for ( byte[] syncUUID : getSyncUUIDs() ) 519 { 520 BerValue.encode( buffer, syncUUID ); 521 } 522 } 523 } 524 525 value = buffer.array(); 526 } 527 catch ( EncoderException e ) 528 { 529 return null; 530 } 531 } 532 533 return value; 534 } 535 536 537 /** 538 * {@inheritDoc} 539 */ 540 public Asn1Object decode( byte[] controlBytes ) throws DecoderException 541 { 542 ByteBuffer bb = ByteBuffer.wrap( controlBytes ); 543 SyncInfoValueContainer container = new SyncInfoValueContainer( getCodecService(), this ); 544 decoder.decode( bb, container ); 545 return this; 546 } 547 548 549 /** 550 * @see Object#toString() 551 */ 552 public String toString() 553 { 554 return getDecorated().toString(); 555 } 556}