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 2009-2010 Sun Microsystems, Inc. 015 * Portions copyright 2011-2016 ForgeRock AS. 016 */ 017 018package org.forgerock.opendj.io; 019 020import java.io.IOException; 021import java.util.List; 022 023import org.forgerock.i18n.slf4j.LocalizedLogger; 024import org.forgerock.opendj.ldap.Attribute; 025import org.forgerock.opendj.ldap.ByteString; 026import org.forgerock.opendj.ldap.DN; 027import org.forgerock.opendj.ldap.Entry; 028import org.forgerock.opendj.ldap.LinkedAttribute; 029import org.forgerock.opendj.ldap.LinkedHashMapEntry; 030import org.forgerock.opendj.ldap.Modification; 031import org.forgerock.opendj.ldap.controls.Control; 032import org.forgerock.opendj.ldap.requests.AbandonRequest; 033import org.forgerock.opendj.ldap.requests.AddRequest; 034import org.forgerock.opendj.ldap.requests.CompareRequest; 035import org.forgerock.opendj.ldap.requests.DeleteRequest; 036import org.forgerock.opendj.ldap.requests.ExtendedRequest; 037import org.forgerock.opendj.ldap.requests.GenericBindRequest; 038import org.forgerock.opendj.ldap.requests.ModifyDNRequest; 039import org.forgerock.opendj.ldap.requests.ModifyRequest; 040import org.forgerock.opendj.ldap.requests.SearchRequest; 041import org.forgerock.opendj.ldap.requests.UnbindRequest; 042import org.forgerock.opendj.ldap.responses.BindResult; 043import org.forgerock.opendj.ldap.responses.CompareResult; 044import org.forgerock.opendj.ldap.responses.ExtendedResult; 045import org.forgerock.opendj.ldap.responses.IntermediateResponse; 046import org.forgerock.opendj.ldap.responses.Result; 047import org.forgerock.opendj.ldap.responses.SearchResultEntry; 048import org.forgerock.opendj.ldap.responses.SearchResultReference; 049 050/** 051 * Writes LDAP messages to an underlying ASN.1 writer. 052 * <p> 053 * Methods for creating {@link LDAPWriter}s are provided in the {@link LDAP} 054 * class. 055 * 056 * @param <W> 057 * The type of ASN.1 writer used for encoding elements. 058 */ 059public final class LDAPWriter<W extends ASN1Writer> { 060 /** @Checkstyle:ignore AvoidNestedBlocks */ 061 062 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 063 private final W writer; 064 private final int protocolVersion; 065 066 LDAPWriter(final W asn1Writer, final int ldapVersion) { 067 this.writer = asn1Writer; 068 this.protocolVersion = ldapVersion; 069 } 070 071 /** 072 * Returns the protocol version supported by this {@link LDAPWriter}. 073 * 074 * @return The protocol version supported by this {@link LDAPWriter} 075 */ 076 public int getProtocolVersion() { 077 return protocolVersion; 078 } 079 080 /** 081 * Returns the ASN.1 writer to which LDAP messages will be written. 082 * 083 * @return The ASN.1 writer to which LDAP messages will be written. 084 */ 085 public W getASN1Writer() { 086 return writer; 087 } 088 089 /** 090 * Writes the provided abandon request. 091 * 092 * @param messageID 093 * The LDAP message ID. 094 * @param request 095 * The request. 096 * @throws IOException 097 * If an unexpected IO error occurred. 098 */ 099 public void writeAbandonRequest(final int messageID, final AbandonRequest request) 100 throws IOException { 101 logger.trace("ENCODE LDAP ABANDON REQUEST(messageID=%d, request=%s)", messageID, request); 102 writeMessageHeader(messageID); 103 { 104 writer.writeInteger(LDAP.OP_TYPE_ABANDON_REQUEST, request.getRequestID()); 105 } 106 writeMessageFooter(request.getControls()); 107 } 108 109 /** 110 * Writes the provided add request. 111 * 112 * @param messageID 113 * The LDAP message ID. 114 * @param request 115 * The request. 116 * @throws IOException 117 * If an unexpected IO error occurred. 118 */ 119 public void writeAddRequest(final int messageID, final AddRequest request) throws IOException { 120 logger.trace("ENCODE LDAP ADD REQUEST(messageID=%d, request=%s)", messageID, request); 121 writeMessageHeader(messageID); 122 { 123 LDAP.writeEntry(writer, LDAP.OP_TYPE_ADD_REQUEST, adaptEntry(request)); 124 } 125 writeMessageFooter(request.getControls()); 126 } 127 128 private Entry adaptEntry(Entry entry) { 129 if (protocolVersion >= 3) { 130 return entry; 131 } 132 final Entry v2entry = new LinkedHashMapEntry(entry.getName()); 133 for (Attribute attribute : entry.getAllAttributes()) { 134 v2entry.addAttribute( 135 new LinkedAttribute(attribute.getAttributeDescription().withoutAnyOptions(), attribute)); 136 } 137 return v2entry; 138 } 139 140 /** 141 * Writes the provided add result. 142 * 143 * @param messageID 144 * The LDAP message ID. 145 * @param result 146 * The result. 147 * @throws IOException 148 * If an unexpected IO error occurred. 149 */ 150 public void writeAddResult(final int messageID, final Result result) throws IOException { 151 logger.trace("ENCODE LDAP ADD RESULT(messageID=%d, result=%s)", messageID, result); 152 writeMessageHeader(messageID); 153 { 154 writeResultHeader(LDAP.OP_TYPE_ADD_RESPONSE, result); 155 writeResultFooter(writer); 156 } 157 writeMessageFooter(result.getControls()); 158 } 159 160 /** 161 * Writes the provided bind request. 162 * 163 * @param messageID 164 * The LDAP message ID. 165 * @param version 166 * The requested LDAP protocol version. 167 * @param request 168 * The request. 169 * @throws IOException 170 * If an unexpected IO error occurred. 171 */ 172 public void writeBindRequest(final int messageID, final int version, 173 final GenericBindRequest request) throws IOException { 174 logger.trace("ENCODE LDAP BIND REQUEST(messageID=%d, auth=0x%x, request=%s)", 175 messageID, request.getAuthenticationType(), request); 176 writeMessageHeader(messageID); 177 { 178 writer.writeStartSequence(LDAP.OP_TYPE_BIND_REQUEST); 179 { 180 writer.writeInteger(version); 181 writer.writeOctetString(request.getName()); 182 writer.writeOctetString(request.getAuthenticationType(), request 183 .getAuthenticationValue()); 184 } 185 writer.writeEndSequence(); 186 } 187 writeMessageFooter(request.getControls()); 188 } 189 190 /** 191 * Writes the provided bind result. 192 * 193 * @param messageID 194 * The LDAP message ID. 195 * @param result 196 * The result. 197 * @throws IOException 198 * If an unexpected IO error occurred. 199 */ 200 public void writeBindResult(final int messageID, final BindResult result) throws IOException { 201 logger.trace("ENCODE LDAP BIND RESULT(messageID=%d, result=%s)", messageID, result); 202 writeMessageHeader(messageID); 203 { 204 writeResultHeader(LDAP.OP_TYPE_BIND_RESPONSE, result); 205 { 206 final ByteString saslCredentials = result.getServerSASLCredentials(); 207 if (saslCredentials != null && saslCredentials.length() > 0) { 208 writer.writeOctetString(LDAP.TYPE_SERVER_SASL_CREDENTIALS, result 209 .getServerSASLCredentials()); 210 } 211 } 212 writeResultFooter(writer); 213 } 214 writeMessageFooter(result.getControls()); 215 } 216 217 /** 218 * Writes the provided compare request. 219 * 220 * @param messageID 221 * The LDAP message ID. 222 * @param request 223 * The request. 224 * @throws IOException 225 * If an unexpected IO error occurred. 226 */ 227 public void writeCompareRequest(final int messageID, final CompareRequest request) 228 throws IOException { 229 logger.trace("ENCODE LDAP COMPARE REQUEST(messageID=%d, request=%s)", messageID, request); 230 writeMessageHeader(messageID); 231 { 232 writer.writeStartSequence(LDAP.OP_TYPE_COMPARE_REQUEST); 233 { 234 writer.writeOctetString(request.getName().toString()); 235 writer.writeStartSequence(); 236 { 237 writer.writeOctetString(request.getAttributeDescription().toString()); 238 writer.writeOctetString(request.getAssertionValue()); 239 } 240 writer.writeEndSequence(); 241 } 242 writer.writeEndSequence(); 243 } 244 writeMessageFooter(request.getControls()); 245 } 246 247 /** 248 * Writes the provided compare result. 249 * 250 * @param messageID 251 * The LDAP message ID. 252 * @param result 253 * The result. 254 * @throws IOException 255 * If an unexpected IO error occurred. 256 */ 257 public void writeCompareResult(final int messageID, final CompareResult result) 258 throws IOException { 259 logger.trace("ENCODE LDAP COMPARE RESULT(messageID=%d, result=%s)", messageID, result); 260 writeMessageHeader(messageID); 261 { 262 writeResultHeader(LDAP.OP_TYPE_COMPARE_RESPONSE, result); 263 writeResultFooter(writer); 264 } 265 writeMessageFooter(result.getControls()); 266 } 267 268 /** 269 * Writes the provided control. 270 * 271 * @param control 272 * The control. 273 * @throws IOException 274 * If an unexpected IO error occurred. 275 */ 276 public void writeControl(final Control control) throws IOException { 277 writer.writeStartSequence(); 278 { 279 writer.writeOctetString(control.getOID()); 280 if (control.isCritical()) { 281 writer.writeBoolean(control.isCritical()); 282 } 283 if (control.getValue() != null) { 284 writer.writeOctetString(control.getValue()); 285 } 286 } 287 writer.writeEndSequence(); 288 } 289 290 /** 291 * Writes the provided delete request. 292 * 293 * @param messageID 294 * The LDAP message ID. 295 * @param request 296 * The request. 297 * @throws IOException 298 * If an unexpected IO error occurred. 299 */ 300 public void writeDeleteRequest(final int messageID, final DeleteRequest request) 301 throws IOException { 302 logger.trace("ENCODE LDAP DELETE REQUEST(messageID=%d, request=%s)", messageID, request); 303 writeMessageHeader(messageID); 304 { 305 writer.writeOctetString(LDAP.OP_TYPE_DELETE_REQUEST, request.getName().toString()); 306 } 307 writeMessageFooter(request.getControls()); 308 } 309 310 /** 311 * Writes the provided delete result. 312 * 313 * @param messageID 314 * The LDAP message ID. 315 * @param result 316 * The result. 317 * @throws IOException 318 * If an unexpected IO error occurred. 319 */ 320 public void writeDeleteResult(final int messageID, final Result result) throws IOException { 321 logger.trace("ENCODE LDAP DELETE RESULT(messageID=%d, result=%s)", messageID, result); 322 writeMessageHeader(messageID); 323 { 324 writeResultHeader(LDAP.OP_TYPE_DELETE_RESPONSE, result); 325 writeResultFooter(writer); 326 } 327 writeMessageFooter(result.getControls()); 328 } 329 330 /** 331 * Writes the provided extended request. 332 * 333 * @param messageID 334 * The LDAP message ID. 335 * @param request 336 * The request. 337 * @throws IOException 338 * If an unexpected IO error occurred. 339 */ 340 public void writeExtendedRequest(final int messageID, final ExtendedRequest<?> request) 341 throws IOException { 342 logger.trace("ENCODE LDAP EXTENDED REQUEST(messageID=%d, request=%s)", messageID, request); 343 writeMessageHeader(messageID); 344 { 345 writer.writeStartSequence(LDAP.OP_TYPE_EXTENDED_REQUEST); 346 { 347 writer.writeOctetString(LDAP.TYPE_EXTENDED_REQUEST_OID, request.getOID()); 348 final ByteString requestValue = request.getValue(); 349 if (requestValue != null) { 350 writer.writeOctetString(LDAP.TYPE_EXTENDED_REQUEST_VALUE, requestValue); 351 } 352 } 353 writer.writeEndSequence(); 354 } 355 writeMessageFooter(request.getControls()); 356 } 357 358 /** 359 * Writes the provided extended result. 360 * 361 * @param messageID 362 * The LDAP message ID. 363 * @param result 364 * The result. 365 * @throws IOException 366 * If an unexpected IO error occurred. 367 */ 368 public void writeExtendedResult(final int messageID, final ExtendedResult result) 369 throws IOException { 370 logger.trace("ENCODE LDAP EXTENDED RESULT(messageID=%d, result=%s)", messageID, result); 371 writeMessageHeader(messageID); 372 { 373 writeResultHeader(LDAP.OP_TYPE_EXTENDED_RESPONSE, result); 374 { 375 final String responseName = result.getOID(); 376 if (responseName != null) { 377 writer.writeOctetString(LDAP.TYPE_EXTENDED_RESPONSE_OID, responseName); 378 } 379 final ByteString responseValue = result.getValue(); 380 if (responseValue != null) { 381 writer.writeOctetString(LDAP.TYPE_EXTENDED_RESPONSE_VALUE, responseValue); 382 } 383 } 384 writeResultFooter(writer); 385 } 386 writeMessageFooter(result.getControls()); 387 } 388 389 /** 390 * Writes the provided intermediate response. 391 * 392 * @param messageID 393 * The LDAP message ID. 394 * @param response 395 * The response. 396 * @throws IOException 397 * If an unexpected IO error occurred. 398 */ 399 public void writeIntermediateResponse(final int messageID, final IntermediateResponse response) 400 throws IOException { 401 logger.trace("ENCODE LDAP INTERMEDIATE RESPONSE(messageID=%d, response=%s)", messageID, response); 402 writeMessageHeader(messageID); 403 { 404 writer.writeStartSequence(LDAP.OP_TYPE_INTERMEDIATE_RESPONSE); 405 { 406 final String responseName = response.getOID(); 407 if (responseName != null) { 408 writer.writeOctetString(LDAP.TYPE_INTERMEDIATE_RESPONSE_OID, response.getOID()); 409 } 410 final ByteString responseValue = response.getValue(); 411 if (responseValue != null) { 412 writer.writeOctetString(LDAP.TYPE_INTERMEDIATE_RESPONSE_VALUE, response 413 .getValue()); 414 } 415 } 416 writer.writeEndSequence(); 417 } 418 writeMessageFooter(response.getControls()); 419 } 420 421 /** 422 * Writes the provided modify DN request. 423 * 424 * @param messageID 425 * The LDAP message ID. 426 * @param request 427 * The request. 428 * @throws IOException 429 * If an unexpected IO error occurred. 430 */ 431 public void writeModifyDNRequest(final int messageID, final ModifyDNRequest request) 432 throws IOException { 433 logger.trace("ENCODE LDAP MODIFY DN REQUEST(messageID=%d, request=%s)", messageID, request); 434 writeMessageHeader(messageID); 435 { 436 writer.writeStartSequence(LDAP.OP_TYPE_MODIFY_DN_REQUEST); 437 { 438 writer.writeOctetString(request.getName().toString()); 439 writer.writeOctetString(request.getNewRDN().toString()); 440 writer.writeBoolean(request.isDeleteOldRDN()); 441 final DN newSuperior = request.getNewSuperior(); 442 if (newSuperior != null) { 443 writer.writeOctetString(LDAP.TYPE_MODIFY_DN_NEW_SUPERIOR, newSuperior 444 .toString()); 445 } 446 } 447 writer.writeEndSequence(); 448 } 449 writeMessageFooter(request.getControls()); 450 } 451 452 /** 453 * Writes the provided modify DN result. 454 * 455 * @param messageID 456 * The LDAP message ID. 457 * @param result 458 * The result. 459 * @throws IOException 460 * If an unexpected IO error occurred. 461 */ 462 public void writeModifyDNResult(final int messageID, final Result result) throws IOException { 463 logger.trace("ENCODE LDAP MODIFY DN RESULT(messageID=%d, result=%s)", messageID, result); 464 writeMessageHeader(messageID); 465 { 466 writeResultHeader(LDAP.OP_TYPE_MODIFY_DN_RESPONSE, result); 467 writeResultFooter(writer); 468 } 469 writeMessageFooter(result.getControls()); 470 } 471 472 /** 473 * Writes the provided modify request. 474 * 475 * @param messageID 476 * The LDAP message ID. 477 * @param request 478 * The request. 479 * @throws IOException 480 * If an unexpected IO error occurred. 481 */ 482 public void writeModifyRequest(final int messageID, final ModifyRequest request) 483 throws IOException { 484 logger.trace("ENCODE LDAP MODIFY REQUEST(messageID=%d, request=%s)", messageID, request); 485 writeMessageHeader(messageID); 486 { 487 writer.writeStartSequence(LDAP.OP_TYPE_MODIFY_REQUEST); 488 { 489 writer.writeOctetString(request.getName().toString()); 490 writer.writeStartSequence(); 491 { 492 for (final Modification change : request.getModifications()) { 493 writeChange(change); 494 } 495 } 496 writer.writeEndSequence(); 497 } 498 writer.writeEndSequence(); 499 } 500 writeMessageFooter(request.getControls()); 501 } 502 503 /** 504 * Writes the provided extended result. 505 * 506 * @param messageID 507 * The LDAP message ID. 508 * @param result 509 * The result. 510 * @throws IOException 511 * If an unexpected IO error occurred. 512 */ 513 public void writeModifyResult(final int messageID, final Result result) throws IOException { 514 logger.trace("ENCODE LDAP MODIFY RESULT(messageID=%d, result=%s)", messageID, result); 515 writeMessageHeader(messageID); 516 { 517 writeResultHeader(LDAP.OP_TYPE_MODIFY_RESPONSE, result); 518 writeResultFooter(writer); 519 } 520 writeMessageFooter(result.getControls()); 521 } 522 523 /** 524 * Writes the provided search request. 525 * 526 * @param messageID 527 * The LDAP message ID. 528 * @param request 529 * The request. 530 * @throws IOException 531 * If an unexpected IO error occurred. 532 */ 533 public void writeSearchRequest(final int messageID, final SearchRequest request) 534 throws IOException { 535 logger.trace("ENCODE LDAP SEARCH REQUEST(messageID=%d, request=%s)", messageID, request); 536 writeMessageHeader(messageID); 537 { 538 writer.writeStartSequence(LDAP.OP_TYPE_SEARCH_REQUEST); 539 { 540 writer.writeOctetString(request.getName().toString()); 541 writer.writeEnumerated(request.getScope().intValue()); 542 writer.writeEnumerated(request.getDereferenceAliasesPolicy().intValue()); 543 writer.writeInteger(request.getSizeLimit()); 544 writer.writeInteger(request.getTimeLimit()); 545 writer.writeBoolean(request.isTypesOnly()); 546 LDAP.writeFilter(writer, request.getFilter()); 547 writer.writeStartSequence(); 548 { 549 for (final String attribute : request.getAttributes()) { 550 writer.writeOctetString(attribute); 551 } 552 } 553 writer.writeEndSequence(); 554 } 555 writer.writeEndSequence(); 556 } 557 writeMessageFooter(request.getControls()); 558 } 559 560 /** 561 * Writes the provided search result. 562 * 563 * @param messageID 564 * The LDAP message ID. 565 * @param result 566 * The result. 567 * @throws IOException 568 * If an unexpected IO error occurred. 569 */ 570 public void writeSearchResult(final int messageID, final Result result) throws IOException { 571 logger.trace("ENCODE LDAP SEARCH RESULT(messageID=%d, result=%s)", messageID, result); 572 writeMessageHeader(messageID); 573 { 574 writeResultHeader(LDAP.OP_TYPE_SEARCH_RESULT_DONE, result); 575 writeResultFooter(writer); 576 } 577 writeMessageFooter(result.getControls()); 578 } 579 580 /** 581 * Writes the provided search result entry. 582 * 583 * @param messageID 584 * The LDAP message ID. 585 * @param entry 586 * The entry. 587 * @throws IOException 588 * If an unexpected IO error occurred. 589 */ 590 public void writeSearchResultEntry(final int messageID, final SearchResultEntry entry) 591 throws IOException { 592 logger.trace("ENCODE LDAP SEARCH RESULT ENTRY(messageID=%d, entry=%s)", messageID, entry); 593 writeMessageHeader(messageID); 594 { 595 LDAP.writeEntry(writer, LDAP.OP_TYPE_SEARCH_RESULT_ENTRY, adaptEntry(entry)); 596 } 597 writeMessageFooter(entry.getControls()); 598 } 599 600 /** 601 * Writes the provided search result reference. 602 * 603 * @param messageID 604 * The LDAP message ID. 605 * @param reference 606 * The reference. 607 * @throws IOException 608 * If an unexpected IO error occurred. 609 */ 610 public void writeSearchResultReference(final int messageID, 611 final SearchResultReference reference) throws IOException { 612 if (protocolVersion <= 2) { 613 return; 614 } 615 logger.trace("ENCODE LDAP SEARCH RESULT REFERENCE(messageID=%d, reference=%s)", messageID, reference); 616 writeMessageHeader(messageID); 617 { 618 writer.writeStartSequence(LDAP.OP_TYPE_SEARCH_RESULT_REFERENCE); 619 { 620 for (final String url : reference.getURIs()) { 621 writer.writeOctetString(url); 622 } 623 } 624 writer.writeEndSequence(); 625 } 626 writeMessageFooter(reference.getControls()); 627 } 628 629 /** 630 * Writes the provided unbind request. 631 * 632 * @param messageID 633 * The LDAP message ID. 634 * @param request 635 * The request. 636 * @throws IOException 637 * If an unexpected IO error occurred. 638 */ 639 public void writeUnbindRequest(final int messageID, final UnbindRequest request) 640 throws IOException { 641 logger.trace("ENCODE LDAP UNBIND REQUEST(messageID=%d, request=%s)", messageID, request); 642 writeMessageHeader(messageID); 643 { 644 writer.writeNull(LDAP.OP_TYPE_UNBIND_REQUEST); 645 } 646 writeMessageFooter(request.getControls()); 647 } 648 649 /** 650 * Writes a message with the provided id, tag and content bytes. 651 * 652 * @param messageID 653 * The LDAP message ID. 654 * @param messageTag 655 * The LDAP message type. 656 * @param messageBytes 657 * The contents of the LDAP message. 658 * @throws IOException 659 * If an unexpected IO error occurred. 660 */ 661 public void writeUnrecognizedMessage(final int messageID, final byte messageTag, 662 final ByteString messageBytes) throws IOException { 663 logger.trace("ENCODE LDAP UNKNOWN MESSAGE(messageID=%d, messageTag=%x, messageBytes=%s)", 664 messageID, messageTag, messageBytes); 665 writeMessageHeader(messageID); 666 { 667 writer.writeOctetString(messageTag, messageBytes); 668 } 669 writer.writeEndSequence(); 670 } 671 672 private void writeChange(final Modification change) throws IOException { 673 writer.writeStartSequence(); 674 { 675 writer.writeEnumerated(change.getModificationType().intValue()); 676 LDAP.writeAttribute(writer, change.getAttribute()); 677 } 678 writer.writeEndSequence(); 679 } 680 681 private void writeMessageFooter(final List<Control> controls) throws IOException { 682 if (!controls.isEmpty() && protocolVersion >= 3) { 683 writer.writeStartSequence(LDAP.TYPE_CONTROL_SEQUENCE); 684 { 685 for (final Control control : controls) { 686 writeControl(control); 687 } 688 } 689 writer.writeEndSequence(); 690 } 691 writer.writeEndSequence(); 692 } 693 694 private void writeMessageHeader(final int messageID) throws IOException { 695 writer.writeStartSequence(); 696 writer.writeInteger(messageID); 697 } 698 699 private void writeResultFooter(final ASN1Writer writer) throws IOException { 700 writer.writeEndSequence(); 701 } 702 703 private void writeResultHeader(final byte typeTag, final Result rawMessage) throws IOException { 704 writer.writeStartSequence(typeTag); 705 writer.writeEnumerated(rawMessage.getResultCode().intValue()); 706 writer.writeOctetString(rawMessage.getMatchedDN()); 707 writer.writeOctetString(rawMessage.getDiagnosticMessage()); 708 if (protocolVersion >= 3) { 709 final List<String> referralURIs = rawMessage.getReferralURIs(); 710 if (!referralURIs.isEmpty()) { 711 writer.writeStartSequence(LDAP.TYPE_REFERRAL_SEQUENCE); 712 { 713 for (final String s : referralURIs) { 714 writer.writeOctetString(s); 715 } 716 } 717 writer.writeEndSequence(); 718 } 719 } 720 } 721}