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