001/* 002 * Copyright 2009-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.migrate.jndi; 022 023 024 025import java.util.Collection; 026import javax.naming.NamingEnumeration; 027import javax.naming.NamingException; 028import javax.naming.directory.Attributes; 029import javax.naming.directory.BasicAttribute; 030import javax.naming.directory.BasicAttributes; 031import javax.naming.directory.DirContext; 032import javax.naming.directory.ModificationItem; 033import javax.naming.directory.SearchResult; 034import javax.naming.ldap.BasicControl; 035import javax.naming.ldap.ExtendedResponse; 036 037import com.unboundid.asn1.ASN1Exception; 038import com.unboundid.asn1.ASN1OctetString; 039import com.unboundid.ldap.sdk.Attribute; 040import com.unboundid.ldap.sdk.Control; 041import com.unboundid.ldap.sdk.DN; 042import com.unboundid.ldap.sdk.Entry; 043import com.unboundid.ldap.sdk.ExtendedRequest; 044import com.unboundid.ldap.sdk.ExtendedResult; 045import com.unboundid.ldap.sdk.Modification; 046import com.unboundid.ldap.sdk.ModificationType; 047import com.unboundid.ldap.sdk.RDN; 048import com.unboundid.util.Debug; 049import com.unboundid.util.NotMutable; 050import com.unboundid.util.StaticUtils; 051import com.unboundid.util.ThreadSafety; 052import com.unboundid.util.ThreadSafetyLevel; 053 054 055 056/** 057 * This utility class provides a set of methods that may be used to convert 058 * between data structures in the Java Naming and Directory Interface (JNDI) 059 * and the corresponding data structures in the UnboundID LDAP SDK for Java. 060 */ 061@NotMutable() 062@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 063public final class JNDIConverter 064{ 065 /** 066 * An empty array of attributes. 067 */ 068 private static final Attribute[] NO_ATTRIBUTES = new Attribute[0]; 069 070 071 072 /** 073 * An empty array of JNDI controls. 074 */ 075 private static final javax.naming.ldap.Control[] NO_JNDI_CONTROLS = 076 new javax.naming.ldap.Control[0]; 077 078 079 080 /** 081 * An empty array of SDK modifications. 082 */ 083 private static final Modification[] NO_MODIFICATIONS = new Modification[0]; 084 085 086 087 /** 088 * An empty array of JNDI modification items. 089 */ 090 private static final ModificationItem[] NO_MODIFICATION_ITEMS = 091 new ModificationItem[0]; 092 093 094 095 /** 096 * An empty array of SDK controls. 097 */ 098 private static final Control[] NO_SDK_CONTROLS = new Control[0]; 099 100 101 102 /** 103 * Prevent this utility class from being instantiated. 104 */ 105 private JNDIConverter() 106 { 107 // No implementation required. 108 } 109 110 111 112 /** 113 * Converts the provided JNDI attribute to an LDAP SDK attribute. 114 * 115 * @param a The attribute to be converted. 116 * 117 * @return The LDAP SDK attribute that corresponds to the provided JNDI 118 * attribute. 119 * 120 * @throws NamingException If a problem is encountered during the conversion 121 * process. 122 */ 123 public static Attribute convertAttribute( 124 final javax.naming.directory.Attribute a) 125 throws NamingException 126 { 127 if (a == null) 128 { 129 return null; 130 } 131 132 final String name = a.getID(); 133 final ASN1OctetString[] values = new ASN1OctetString[a.size()]; 134 135 for (int i=0; i < values.length; i++) 136 { 137 final Object value = a.get(i); 138 if (value instanceof byte[]) 139 { 140 values[i] = new ASN1OctetString((byte[]) value); 141 } 142 else 143 { 144 values[i] = new ASN1OctetString(String.valueOf(value)); 145 } 146 } 147 148 return new Attribute(name, values); 149 } 150 151 152 153 /** 154 * Converts the provided LDAP SDK attribute to a JNDI attribute. 155 * 156 * @param a The attribute to be converted. 157 * 158 * @return The JNDI attribute that corresponds to the provided LDAP SDK 159 * attribute. 160 */ 161 public static javax.naming.directory.Attribute convertAttribute( 162 final Attribute a) 163 { 164 if (a == null) 165 { 166 return null; 167 } 168 169 final BasicAttribute attr = new BasicAttribute(a.getName(), true); 170 for (final String v : a.getValues()) 171 { 172 attr.add(v); 173 } 174 175 return attr; 176 } 177 178 179 180 /** 181 * Converts the provided JNDI attributes to an array of LDAP SDK attributes. 182 * 183 * @param a The attributes to be converted. 184 * 185 * @return The array of LDAP SDK attributes that corresponds to the 186 * provided JNDI attributes. 187 * 188 * @throws NamingException If a problem is encountered during the conversion 189 * process. 190 */ 191 public static Attribute[] convertAttributes(final Attributes a) 192 throws NamingException 193 { 194 if (a == null) 195 { 196 return NO_ATTRIBUTES; 197 } 198 199 int i=0; 200 final Attribute[] attributes = new Attribute[a.size()]; 201 final NamingEnumeration<? extends javax.naming.directory.Attribute> e = 202 a.getAll(); 203 204 try 205 { 206 while (e.hasMoreElements()) 207 { 208 attributes[i++] = convertAttribute(e.next()); 209 } 210 } 211 finally 212 { 213 e.close(); 214 } 215 216 return attributes; 217 } 218 219 220 221 /** 222 * Converts the provided array of LDAP SDK attributes to a set of JNDI 223 * attributes. 224 * 225 * @param a The array of attributes to be converted. 226 * 227 * @return The JNDI attributes that corresponds to the provided LDAP SDK 228 * attributes. 229 */ 230 public static Attributes convertAttributes(final Attribute... a) 231 { 232 final BasicAttributes attrs = new BasicAttributes(true); 233 if (a == null) 234 { 235 return attrs; 236 } 237 238 for (final Attribute attr : a) 239 { 240 attrs.put(convertAttribute(attr)); 241 } 242 243 return attrs; 244 } 245 246 247 248 /** 249 * Converts the provided collection of LDAP SDK attributes to a set of JNDI 250 * attributes. 251 * 252 * @param a The collection of attributes to be converted. 253 * 254 * @return The JNDI attributes that corresponds to the provided LDAP SDK 255 * attributes. 256 */ 257 public static Attributes convertAttributes(final Collection<Attribute> a) 258 { 259 final BasicAttributes attrs = new BasicAttributes(true); 260 if (a == null) 261 { 262 return attrs; 263 } 264 265 for (final Attribute attr : a) 266 { 267 attrs.put(convertAttribute(attr)); 268 } 269 270 return attrs; 271 } 272 273 274 275 /** 276 * Converts the provided JNDI control to an LDAP SDK control. 277 * 278 * @param c The control to be converted. 279 * 280 * @return The LDAP SDK control that corresponds to the provided JNDI 281 * control. 282 * 283 * @throws NamingException If a problem is encountered during the conversion 284 * process. 285 */ 286 public static Control convertControl(final javax.naming.ldap.Control c) 287 throws NamingException 288 { 289 if (c == null) 290 { 291 return null; 292 } 293 294 final ASN1OctetString value; 295 final byte[] valueBytes = c.getEncodedValue(); 296 if ((valueBytes == null) || (valueBytes.length == 0)) 297 { 298 value = null; 299 } 300 else 301 { 302 try 303 { 304 value = ASN1OctetString.decodeAsOctetString(valueBytes); 305 } 306 catch (final ASN1Exception ae) 307 { 308 throw new NamingException(StaticUtils.getExceptionMessage(ae)); 309 } 310 } 311 312 return new Control(c.getID(), c.isCritical(), value); 313 } 314 315 316 317 /** 318 * Converts the provided LDAP SDK control to a JNDI control. 319 * 320 * @param c The control to be converted. 321 * 322 * @return The JNDI control that corresponds to the provided LDAP SDK 323 * control. 324 */ 325 public static javax.naming.ldap.Control convertControl(final Control c) 326 { 327 if (c == null) 328 { 329 return null; 330 } 331 332 final ASN1OctetString value = c.getValue(); 333 if (value == null) 334 { 335 return new BasicControl(c.getOID(), c.isCritical(), null); 336 } 337 else 338 { 339 return new BasicControl(c.getOID(), c.isCritical(), value.encode()); 340 } 341 } 342 343 344 345 /** 346 * Converts the provided array of JNDI controls to an array of LDAP SDK 347 * controls. 348 * 349 * @param c The array of JNDI controls to be converted. 350 * 351 * @return The array of LDAP SDK controls that corresponds to the provided 352 * array of JNDI controls. 353 * 354 * @throws NamingException If a problem is encountered during the conversion 355 * process. 356 */ 357 public static Control[] convertControls(final javax.naming.ldap.Control... c) 358 throws NamingException 359 { 360 if (c == null) 361 { 362 return NO_SDK_CONTROLS; 363 } 364 365 final Control[] controls = new Control[c.length]; 366 for (int i=0; i < controls.length; i++) 367 { 368 controls[i] = convertControl(c[i]); 369 } 370 371 return controls; 372 } 373 374 375 376 /** 377 * Converts the provided array of LDAP SDK controls to an array of JNDI 378 * controls. 379 * 380 * @param c The array of LDAP SDK controls to be converted. 381 * 382 * @return The array of JNDI controls that corresponds to the provided array 383 * of LDAP SDK controls. 384 */ 385 public static javax.naming.ldap.Control[] convertControls(final Control... c) 386 { 387 if (c == null) 388 { 389 return NO_JNDI_CONTROLS; 390 } 391 392 final javax.naming.ldap.Control[] controls = 393 new javax.naming.ldap.Control[c.length]; 394 for (int i=0; i < controls.length; i++) 395 { 396 controls[i] = convertControl(c[i]); 397 } 398 399 return controls; 400 } 401 402 403 404 /** 405 * Converts the provided JNDI extended request to an LDAP SDK extended 406 * request. 407 * 408 * @param r The request to be converted. 409 * 410 * @return The LDAP SDK extended request that corresponds to the provided 411 * JNDI extended request. 412 * 413 * @throws NamingException If a problem is encountered during the conversion 414 * process. 415 */ 416 public static ExtendedRequest convertExtendedRequest( 417 final javax.naming.ldap.ExtendedRequest r) 418 throws NamingException 419 { 420 if (r == null) 421 { 422 return null; 423 } 424 425 return JNDIExtendedRequest.toSDKExtendedRequest(r); 426 } 427 428 429 430 /** 431 * Converts the provided LDAP SDK extended request to a JNDI extended request. 432 * 433 * @param r The request to be converted. 434 * 435 * @return The JNDI extended request that corresponds to the provided LDAP 436 * SDK extended request. 437 */ 438 public static javax.naming.ldap.ExtendedRequest convertExtendedRequest( 439 final ExtendedRequest r) 440 { 441 if (r == null) 442 { 443 return null; 444 } 445 446 return new JNDIExtendedRequest(r); 447 } 448 449 450 451 /** 452 * Converts the provided JNDI extended response to an LDAP SDK extended 453 * result. 454 * 455 * @param r The response to be converted. 456 * 457 * @return The LDAP SDK extended result that corresponds to the provided 458 * JNDI extended response. 459 * 460 * @throws NamingException If a problem is encountered during the conversion 461 * process. 462 */ 463 public static ExtendedResult convertExtendedResponse(final ExtendedResponse r) 464 throws NamingException 465 { 466 if (r == null) 467 { 468 return null; 469 } 470 471 return JNDIExtendedResponse.toSDKExtendedResult(r); 472 } 473 474 475 476 /** 477 * Converts the provided LDAP SDK extended result to a JNDI extended response. 478 * 479 * @param r The result to be converted. 480 * 481 * @return The JNDI extended response that corresponds to the provided LDAP 482 * SDK extended result. 483 */ 484 public static ExtendedResponse convertExtendedResult(final ExtendedResult r) 485 { 486 if (r == null) 487 { 488 return null; 489 } 490 491 return new JNDIExtendedResponse(r); 492 } 493 494 495 496 /** 497 * Converts the provided JNDI modification item to an LDAP SDK modification. 498 * 499 * @param m The JNDI modification item to be converted. 500 * 501 * @return The LDAP SDK modification that corresponds to the provided JNDI 502 * modification item. 503 * 504 * @throws NamingException If a problem is encountered during the conversion 505 * process. 506 */ 507 public static Modification convertModification(final ModificationItem m) 508 throws NamingException 509 { 510 if (m == null) 511 { 512 return null; 513 } 514 515 final ModificationType modType; 516 switch (m.getModificationOp()) 517 { 518 case DirContext.ADD_ATTRIBUTE: 519 modType = ModificationType.ADD; 520 break; 521 case DirContext.REMOVE_ATTRIBUTE: 522 modType = ModificationType.DELETE; 523 break; 524 case DirContext.REPLACE_ATTRIBUTE: 525 modType = ModificationType.REPLACE; 526 break; 527 default: 528 throw new NamingException("Unsupported modification type " + m); 529 } 530 531 final Attribute a = convertAttribute(m.getAttribute()); 532 533 return new Modification(modType, a.getName(), a.getRawValues()); 534 } 535 536 537 538 /** 539 * Converts the provided LDAP SDK modification to a JNDI modification item. 540 * 541 * @param m The LDAP SDK modification to be converted. 542 * 543 * @return The JNDI modification item that corresponds to the provided LDAP 544 * SDK modification. 545 * 546 * @throws NamingException If a problem is encountered during the conversion 547 * process. 548 */ 549 public static ModificationItem convertModification(final Modification m) 550 throws NamingException 551 { 552 if (m == null) 553 { 554 return null; 555 } 556 557 final int modType; 558 switch (m.getModificationType().intValue()) 559 { 560 case ModificationType.ADD_INT_VALUE: 561 modType = DirContext.ADD_ATTRIBUTE; 562 break; 563 case ModificationType.DELETE_INT_VALUE: 564 modType = DirContext.REMOVE_ATTRIBUTE; 565 break; 566 case ModificationType.REPLACE_INT_VALUE: 567 modType = DirContext.REPLACE_ATTRIBUTE; 568 break; 569 default: 570 throw new NamingException("Unsupported modification type " + m); 571 } 572 573 return new ModificationItem(modType, convertAttribute(m.getAttribute())); 574 } 575 576 577 578 /** 579 * Converts the provided array of JNDI modification items to an array of LDAP 580 * SDK modifications. 581 * 582 * @param m The array of JNDI modification items to be converted. 583 * 584 * @return The array of LDAP SDK modifications that corresponds to the 585 * provided array of JNDI modification items. 586 * 587 * @throws NamingException If a problem is encountered during the conversion 588 * process. 589 */ 590 public static Modification[] convertModifications(final ModificationItem... m) 591 throws NamingException 592 { 593 if (m == null) 594 { 595 return NO_MODIFICATIONS; 596 } 597 598 final Modification[] mods = new Modification[m.length]; 599 for (int i=0; i < m.length; i++) 600 { 601 mods[i] = convertModification(m[i]); 602 } 603 604 return mods; 605 } 606 607 608 609 /** 610 * Converts the provided array of LDAP SDK modifications to an array of JNDI 611 * modification items. 612 * 613 * @param m The array of LDAP SDK modifications to be converted. 614 * 615 * @return The array of JNDI modification items that corresponds to the 616 * provided array of LDAP SDK modifications. 617 * 618 * @throws NamingException If a problem is encountered during the conversion 619 * process. 620 */ 621 public static ModificationItem[] convertModifications(final Modification... m) 622 throws NamingException 623 { 624 if (m == null) 625 { 626 return NO_MODIFICATION_ITEMS; 627 } 628 629 final ModificationItem[] mods = new ModificationItem[m.length]; 630 for (int i=0; i < m.length; i++) 631 { 632 mods[i] = convertModification(m[i]); 633 } 634 635 return mods; 636 } 637 638 639 640 /** 641 * Converts the provided JNDI search result object to an LDAP SDK entry. 642 * 643 * @param r The JNDI search result object to be converted. 644 * 645 * @return The LDAP SDK entry that corresponds to the provided JNDI search 646 * result. 647 * 648 * @throws NamingException If a problem is encountered during the conversion 649 * process. 650 */ 651 public static Entry convertSearchEntry(final SearchResult r) 652 throws NamingException 653 { 654 return convertSearchEntry(r, null); 655 } 656 657 658 659 /** 660 * Converts the provided JNDI search result object to an LDAP SDK entry. 661 * 662 * @param r The JNDI search result object to be converted. 663 * @param contextBaseDN The base DN for the JNDI context over which the 664 * search result was retrieved. If it is 665 * non-{@code null} and non-empty, then it will be 666 * appended to the result of the {@code getName} method 667 * to obtain the entry's full DN. 668 * 669 * @return The LDAP SDK entry that corresponds to the provided JNDI search 670 * result. 671 * 672 * @throws NamingException If a problem is encountered during the conversion 673 * process. 674 */ 675 public static Entry convertSearchEntry(final SearchResult r, 676 final String contextBaseDN) 677 throws NamingException 678 { 679 if (r == null) 680 { 681 return null; 682 } 683 684 final String dn; 685 if ((contextBaseDN == null) || contextBaseDN.isEmpty()) 686 { 687 dn = r.getName(); 688 } 689 else 690 { 691 final String name = r.getName(); 692 if ((name == null) || name.isEmpty()) 693 { 694 dn = contextBaseDN; 695 } 696 else 697 { 698 dn = r.getName() + ',' + contextBaseDN; 699 } 700 } 701 702 return new Entry(dn, convertAttributes(r.getAttributes())); 703 } 704 705 706 707 /** 708 * Converts the provided LDAP SDK entry to a JNDI search result. 709 * 710 * @param e The entry to be converted to a JNDI search result. 711 * 712 * @return The JNDI search result that corresponds to the provided LDAP SDK 713 * entry. 714 */ 715 public static SearchResult convertSearchEntry(final Entry e) 716 { 717 return convertSearchEntry(e, null); 718 } 719 720 721 722 /** 723 * Converts the provided LDAP SDK entry to a JNDI search result. 724 * 725 * @param e The entry to be converted to a JNDI search result. 726 * @param contextBaseDN The base DN for the JNDI context over which the 727 * search result was retrieved. If it is 728 * non-{@code null} and non-empty, then it will be 729 * removed from the end of the entry's DN in order to 730 * obtain the name for the {@code SearchResult} that is 731 * returned. 732 * 733 * @return The JNDI search result that corresponds to the provided LDAP SDK 734 * entry. 735 */ 736 public static SearchResult convertSearchEntry(final Entry e, 737 final String contextBaseDN) 738 { 739 if (e == null) 740 { 741 return null; 742 } 743 744 String name = e.getDN(); 745 if ((contextBaseDN != null) && (! contextBaseDN.isEmpty())) 746 { 747 try 748 { 749 final DN parsedEntryDN = e.getParsedDN(); 750 final DN parsedBaseDN = new DN(contextBaseDN); 751 if (parsedEntryDN.equals(parsedBaseDN)) 752 { 753 name = ""; 754 } 755 else if (parsedEntryDN.isDescendantOf(parsedBaseDN, false)) 756 { 757 final RDN[] entryRDNs = parsedEntryDN.getRDNs(); 758 final RDN[] baseRDNs = parsedBaseDN.getRDNs(); 759 final RDN[] remainingRDNs = 760 new RDN[entryRDNs.length - baseRDNs.length]; 761 System.arraycopy(entryRDNs, 0, remainingRDNs, 0, 762 remainingRDNs.length); 763 name = new DN(remainingRDNs).toString(); 764 } 765 } 766 catch (final Exception ex) 767 { 768 Debug.debugException(ex); 769 } 770 } 771 772 final Collection<Attribute> attrs = e.getAttributes(); 773 final Attribute[] attributes = new Attribute[attrs.size()]; 774 attrs.toArray(attributes); 775 776 return new SearchResult(name, null, convertAttributes(attributes)); 777 } 778}