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