001/* 002 * Copyright 2009-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2014 UnboundID Corp. 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.persist; 022 023 024 025import java.io.ByteArrayInputStream; 026import java.io.ByteArrayOutputStream; 027import java.io.ObjectInputStream; 028import java.io.ObjectOutputStream; 029import java.io.Serializable; 030import java.lang.reflect.Array; 031import java.lang.reflect.Field; 032import java.lang.reflect.InvocationTargetException; 033import java.lang.reflect.Method; 034import java.lang.reflect.Type; 035import java.math.BigDecimal; 036import java.math.BigInteger; 037import java.net.URI; 038import java.net.URL; 039import java.util.ArrayList; 040import java.util.Collection; 041import java.util.Date; 042import java.util.HashSet; 043import java.util.LinkedHashSet; 044import java.util.LinkedList; 045import java.util.List; 046import java.util.Set; 047import java.util.TreeSet; 048import java.util.UUID; 049import java.util.concurrent.CopyOnWriteArrayList; 050import java.util.concurrent.CopyOnWriteArraySet; 051import java.util.concurrent.atomic.AtomicInteger; 052import java.util.concurrent.atomic.AtomicLong; 053 054import com.unboundid.asn1.ASN1OctetString; 055import com.unboundid.ldap.matchingrules.CaseIgnoreStringMatchingRule; 056import com.unboundid.ldap.matchingrules.MatchingRule; 057import com.unboundid.ldap.sdk.Attribute; 058import com.unboundid.ldap.sdk.DN; 059import com.unboundid.ldap.sdk.Filter; 060import com.unboundid.ldap.sdk.LDAPURL; 061import com.unboundid.ldap.sdk.RDN; 062import com.unboundid.ldap.sdk.LDAPException; 063import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 064import com.unboundid.ldap.sdk.schema.AttributeUsage; 065import com.unboundid.util.NotMutable; 066import com.unboundid.util.ThreadSafety; 067import com.unboundid.util.ThreadSafetyLevel; 068 069import static com.unboundid.ldap.sdk.persist.PersistMessages.*; 070import static com.unboundid.util.Debug.*; 071import static com.unboundid.util.StaticUtils.*; 072 073 074 075/** 076 * This class provides the default implementation of an {@link ObjectEncoder} 077 * object that will be used when encoding and decoding fields to be written to 078 * or read from an LDAP directory server. 079 * <BR><BR> 080 * The following basic types will be supported, with the following encodings: 081 * <UL> 082 * <LI>Any kind of enumeration -- Encoded using the name of the enum 083 * value</LI> 084 * <LI>{@code java.util.concurrent.atomic.AtomicInteger} -- Encoded using the 085 * string representation of the value</LI> 086 * <LI>{@code java.util.concurrent.atomic.AtomicLong} -- Encoded using the 087 * string representation of the value</LI> 088 * <LI>{@code java.math.BigDecimal} -- Encoded using the string representation 089 * of the value</LI> 090 * <LI>{@code java.math.BigInteger} -- Encoded using the string representation 091 * of the value</LI> 092 * <LI>{@code boolean} -- Encoded as either "TRUE" or "FALSE"</LI> 093 * <LI>{@code java.lang.Boolean} -- Encoded as either "TRUE" or "FALSE"</LI> 094 * <LI>{@code byte[]} -- Encoded as the raw bytes contained in the array</LI> 095 * <LI>{@code char[]} -- Encoded as a string containing the characters in the 096 * array</LI> 097 * <LI>{@code java.util.Date} -- Encoded using the generalized time 098 * syntax</LI> 099 * <LI>{@code com.unboundid.ldap.sdk.DN} -- Encoded using the string 100 * representation of the value</LI> 101 * <LI>{@code double} -- Encoded using the string representation of the 102 * value</LI> 103 * <LI>{@code java.lang.Double} -- Encoded using the string representation of 104 * the value</LI> 105 * <LI>{@code com.unboundid.ldap.sdk.Filter} -- Encoded using the string 106 * representation of the value</LI> 107 * <LI>{@code float} -- Encoded using the string representation of the 108 * value</LI> 109 * <LI>{@code java.lang.Float} -- Encoded using the string representation of 110 * the value</LI> 111 * <LI>{@code int} -- Encoded using the string representation of the 112 * value</LI> 113 * <LI>{@code java.lang.Integer} -- Encoded using the string representation of 114 * the value</LI> 115 * <LI>{@code com.unboundid.ldap.sdk.LDAPURL} -- Encoded using the string 116 * representation of the value</LI> 117 * <LI>{@code long} -- Encoded using the string representation of the 118 * value</LI> 119 * <LI>{@code java.lang.Long} -- Encoded using the string representation of 120 * the value</LI> 121 * <LI>{@code com.unboundid.ldap.sdk.RDN} -- Encoded using the string 122 * representation of the value</LI> 123 * <LI>{@code short} -- Encoded using the string representation of the 124 * value</LI> 125 * <LI>{@code java.lang.Short} -- Encoded using the string representation of 126 * the value</LI> 127 * <LI>{@code java.lang.String} -- Encoded using the value</LI> 128 * <LI>{@code java.lang.StringBuffer} -- Encoded using the string 129 * representation of the value</LI> 130 * <LI>{@code java.lang.StringBuilder} -- Encoded using the string 131 * representation of the value</LI> 132 * <LI>{@code java.net.URI} -- Encoded using the string representation of the 133 * value.</LI> 134 * <LI>{@code java.net.URL} -- Encoded using the string representation of the 135 * value.</LI> 136 * <LI>{@code java.util.UUID} -- Encoded using the string representation of 137 * the value</LI> 138 * </UL> 139 * Serializable objects are also supported, in which case the raw bytes that 140 * comprise the serialized representation will be used. This may be 141 * undesirable, because the value may only be interpretable by Java-based 142 * clients. If you wish to better control the encoding for serialized objects, 143 * have them implement custom {@code writeObject}, {@code readObject}, and 144 * {@code readObjectNoData} methods that use the desired encoding. Alternately, 145 * you may create a custom {@link ObjectEncoder} implementation for that object 146 * type, or use getter/setter methods that convert between string/byte[] 147 * representations and the desired object types. 148 * <BR><BR> 149 * In addition, arrays of all of the above types are also supported, in which 150 * case each element of the array will be a separate value in the corresponding 151 * LDAP attribute. Lists (including {@code ArrayList}, {@code LinkedList}, and 152 * {@code CopyOnWriteArrayList}) and sets (including {@code HashSet}, 153 * {@code LinkedHashSet}, {@code TreeSet}, and {@code CopyOnWriteArraySet}) of 154 * the above types are also supported. 155 * <BR><BR> 156 * Note that you should be careful when using primitive types, since they cannot 157 * be unassigned and therefore will always have a value. When using an LDAP 158 * entry to initialize an object any fields with primitive types which are 159 * associated with LDAP attributes not present in the entry will have the 160 * default value assigned to them in the zero-argument constructor, or will have 161 * the JVM-supplied default value if no value was assigned to it in the 162 * constructor. If the associated object is converted back to an LDAP entry, 163 * then those fields will be included in the entry that is generated, even if 164 * they were not present in the original entry. To avoid this problem, you can 165 * use the object types rather than the primitive types (e.g., 166 * {@code java.lang.Boolean} instead of the {@code boolean} primitive), in which 167 * case any fields associated with attributes that are not present in the entry 168 * being de-serialized will be explicitly set to {@code null}. 169 */ 170@NotMutable() 171@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 172public final class DefaultObjectEncoder 173 extends ObjectEncoder 174{ 175 /** 176 * The serial version UID for this serializable class. 177 */ 178 private static final long serialVersionUID = -4566874784628920022L; 179 180 181 182 /** 183 * Creates a new instance of this encoder. 184 */ 185 public DefaultObjectEncoder() 186 { 187 super(); 188 } 189 190 191 192 /** 193 * {@inheritDoc} 194 */ 195 @Override() 196 public boolean supportsType(final Type t) 197 { 198 final TypeInfo typeInfo = new TypeInfo(t); 199 if (! typeInfo.isSupported()) 200 { 201 return false; 202 } 203 204 final Class<?> baseClass = typeInfo.getBaseClass(); 205 206 if (supportsTypeInternal(baseClass)) 207 { 208 return true; 209 } 210 211 final Class<?> componentType = typeInfo.getComponentType(); 212 if (componentType == null) 213 { 214 return false; 215 } 216 217 if (typeInfo.isArray()) 218 { 219 return supportsTypeInternal(componentType); 220 } 221 222 if (typeInfo.isList()) 223 { 224 return (isSupportedListType(baseClass) && 225 supportsTypeInternal(componentType)); 226 } 227 228 if (typeInfo.isSet()) 229 { 230 return (isSupportedSetType(baseClass) && 231 supportsTypeInternal(componentType)); 232 } 233 234 return false; 235 } 236 237 238 239 /** 240 * Indicates whether this object encoder supports objects of the specified 241 * type. 242 * 243 * @param c The object type class for which to make the determination. 244 * 245 * @return {@code true} if this object supports objects of the specified 246 * type, or {@code false} if not. 247 */ 248 private static boolean supportsTypeInternal(final Class<?> c) 249 { 250 if (c.equals(AtomicInteger.class) || 251 c.equals(AtomicLong.class) || 252 c.equals(BigDecimal.class) || 253 c.equals(BigInteger.class) || 254 c.equals(Boolean.class) || 255 c.equals(Boolean.TYPE) || 256 c.equals(Date.class) || 257 c.equals(DN.class) || 258 c.equals(Double.class) || 259 c.equals(Double.TYPE) || 260 c.equals(Filter.class) || 261 c.equals(Float.class) || 262 c.equals(Float.TYPE) || 263 c.equals(Integer.class) || 264 c.equals(Integer.TYPE) || 265 c.equals(LDAPURL.class) || 266 c.equals(Long.class) || 267 c.equals(Long.TYPE) || 268 c.equals(RDN.class) || 269 c.equals(Short.class) || 270 c.equals(Short.TYPE) || 271 c.equals(String.class) || 272 c.equals(StringBuffer.class) || 273 c.equals(StringBuilder.class) || 274 c.equals(URI.class) || 275 c.equals(URL.class) || 276 c.equals(UUID.class)) 277 { 278 return true; 279 } 280 281 if (c.isArray()) 282 { 283 final Class<?> t = c.getComponentType(); 284 if (t.equals(Byte.TYPE) || 285 t.equals(Character.TYPE)) 286 { 287 return true; 288 } 289 } 290 291 if (c.isEnum()) 292 { 293 return true; 294 } 295 296 if (Serializable.class.isAssignableFrom(c)) 297 { 298 return (! (c.isArray() || Collection.class.isAssignableFrom(c))); 299 } 300 301 return false; 302 } 303 304 305 306 /** 307 * Indicates whether the provided type is a supported list type. 308 * 309 * @param t The type for which to make the determination. 310 * 311 * @return {@code true} if the provided type is a supported list type, or 312 * or {@code false}. 313 */ 314 private static boolean isSupportedListType(final Class<?> t) 315 { 316 return (t.equals(List.class) || 317 t.equals(ArrayList.class) || 318 t.equals(LinkedList.class) || 319 t.equals(CopyOnWriteArrayList.class)); 320 } 321 322 323 324 /** 325 * Creates a new list of the specified type. 326 * 327 * @param t The type of list to create. 328 * @param size The number of values that will be included in the list. 329 * 330 * @return The created list, or {@code null} if it is not a supported list 331 * type. 332 */ 333 @SuppressWarnings("rawtypes") 334 private static List<?> createList(final Class<?> t, final int size) 335 { 336 if (t.equals(List.class) || t.equals(ArrayList.class)) 337 { 338 return new ArrayList(size); 339 } 340 else if (t.equals(LinkedList.class)) 341 { 342 return new LinkedList(); 343 } 344 else if (t.equals(CopyOnWriteArrayList.class)) 345 { 346 return new CopyOnWriteArrayList(); 347 } 348 349 return null; 350 } 351 352 353 354 /** 355 * Indicates whether the provided type is a supported set type. 356 * 357 * @param t The type for which to make the determination. 358 * 359 * @return {@code true} if the provided type is a supported set type, or 360 * or {@code false}. 361 */ 362 private static boolean isSupportedSetType(final Class<?> t) 363 { 364 return (t.equals(Set.class) || 365 t.equals(HashSet.class) || 366 t.equals(LinkedHashSet.class) || 367 t.equals(TreeSet.class) || 368 t.equals(CopyOnWriteArraySet.class)); 369 } 370 371 372 373 /** 374 * Creates a new set of the specified type. 375 * 376 * @param t The type of set to create. 377 * @param size The number of values that will be included in the set. 378 * 379 * @return The created list, or {@code null} if it is not a supported set 380 * type. 381 */ 382 @SuppressWarnings("rawtypes") 383 private static Set<?> createSet(final Class<?> t, final int size) 384 { 385 if (t.equals(Set.class) || t.equals(LinkedHashSet.class)) 386 { 387 return new LinkedHashSet(size); 388 } 389 else if (t.equals(HashSet.class)) 390 { 391 return new HashSet(size); 392 } 393 else if (t.equals(TreeSet.class)) 394 { 395 return new TreeSet(); 396 } 397 else if (t.equals(CopyOnWriteArraySet.class)) 398 { 399 return new CopyOnWriteArraySet(); 400 } 401 402 return null; 403 } 404 405 406 407 /** 408 * {@inheritDoc} 409 */ 410 @Override() 411 public AttributeTypeDefinition constructAttributeType(final Field f, 412 final OIDAllocator a) 413 throws LDAPPersistException 414 { 415 final LDAPField at = f.getAnnotation(LDAPField.class); 416 417 final String attrName; 418 if (at.attribute().length() == 0) 419 { 420 attrName = f.getName(); 421 } 422 else 423 { 424 attrName = at.attribute(); 425 } 426 427 final String oid = a.allocateAttributeTypeOID(attrName); 428 429 final TypeInfo typeInfo = new TypeInfo(f.getGenericType()); 430 if (! typeInfo.isSupported()) 431 { 432 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 433 String.valueOf(typeInfo.getType()))); 434 } 435 436 final boolean isSingleValued = (! supportsMultipleValues(typeInfo)); 437 438 final String syntaxOID; 439 if (isSingleValued) 440 { 441 syntaxOID = getSyntaxOID(typeInfo.getBaseClass()); 442 } 443 else 444 { 445 syntaxOID = getSyntaxOID(typeInfo.getComponentType()); 446 } 447 448 final MatchingRule mr = MatchingRule.selectMatchingRuleForSyntax(syntaxOID); 449 return new AttributeTypeDefinition(oid, new String[] { attrName }, null, 450 false, null, mr.getEqualityMatchingRuleNameOrOID(), 451 mr.getOrderingMatchingRuleNameOrOID(), 452 mr.getSubstringMatchingRuleNameOrOID(), syntaxOID, isSingleValued, 453 false, false, AttributeUsage.USER_APPLICATIONS, null); 454 } 455 456 457 458 /** 459 * {@inheritDoc} 460 */ 461 @Override() 462 public AttributeTypeDefinition constructAttributeType(final Method m, 463 final OIDAllocator a) 464 throws LDAPPersistException 465 { 466 final LDAPGetter at = m.getAnnotation(LDAPGetter.class); 467 468 final String attrName; 469 if (at.attribute().length() == 0) 470 { 471 attrName = toInitialLowerCase(m.getName().substring(3)); 472 } 473 else 474 { 475 attrName = at.attribute(); 476 } 477 478 final String oid = a.allocateAttributeTypeOID(attrName); 479 480 final TypeInfo typeInfo = new TypeInfo(m.getGenericReturnType()); 481 if (! typeInfo.isSupported()) 482 { 483 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 484 String.valueOf(typeInfo.getType()))); 485 } 486 487 final boolean isSingleValued = (! supportsMultipleValues(typeInfo)); 488 489 final String syntaxOID; 490 if (isSingleValued) 491 { 492 syntaxOID = getSyntaxOID(typeInfo.getBaseClass()); 493 } 494 else 495 { 496 syntaxOID = getSyntaxOID(typeInfo.getComponentType()); 497 } 498 499 return new AttributeTypeDefinition(oid, new String[] { attrName }, null, 500 false, null, null, null, null, syntaxOID, isSingleValued, false, false, 501 AttributeUsage.USER_APPLICATIONS, null); 502 } 503 504 505 506 /** 507 * Retrieves the syntax that should be used for the specified object type. 508 * 509 * @param t The type for which to make the determination. 510 * 511 * @return The syntax that should be used for the specified object type, or 512 * {@code null} if it cannot be determined. 513 */ 514 private static String getSyntaxOID(final Class<?> t) 515 { 516 if (t.equals(BigDecimal.class) || 517 t.equals(Double.class) || 518 t.equals(Double.TYPE) || 519 t.equals(Float.class) || 520 t.equals(Float.TYPE) || 521 t.equals(String.class) || 522 t.equals(StringBuffer.class) || 523 t.equals(StringBuilder.class) || 524 t.equals(URI.class) || 525 t.equals(URL.class) || 526 t.equals(Filter.class) || 527 t.equals(LDAPURL.class)) 528 { 529 return "1.3.6.1.4.1.1466.115.121.1.15"; 530 } 531 else if (t.equals(AtomicInteger.class) || 532 t.equals(AtomicLong.class) || 533 t.equals(BigInteger.class) || 534 t.equals(Integer.class) || 535 t.equals(Integer.TYPE) || 536 t.equals(Long.class) || 537 t.equals(Long.TYPE) || 538 t.equals(Short.class) || 539 t.equals(Short.TYPE)) 540 { 541 return "1.3.6.1.4.1.1466.115.121.1.27"; 542 } 543 else if (t.equals(UUID.class)) 544 { 545 // Although "1.3.6.1.1.16.1" (which is the UUID syntax as defined in RFC 546 // 4530) might be more correct, some servers may not support this syntax 547 // since it is relatively new, so we'll fall back on the more 548 // widely-supported directory string syntax. 549 return "1.3.6.1.4.1.1466.115.121.1.15"; 550 } 551 else if (t.equals(DN.class) || 552 t.equals(RDN.class)) 553 { 554 return "1.3.6.1.4.1.1466.115.121.1.12"; 555 } 556 else if (t.equals(Boolean.class) || 557 t.equals(Boolean.TYPE)) 558 { 559 return "1.3.6.1.4.1.1466.115.121.1.7"; 560 } 561 else if (t.equals(Date.class)) 562 { 563 return "1.3.6.1.4.1.1466.115.121.1.24"; 564 } 565 else if (t.isArray()) 566 { 567 final Class<?> ct = t.getComponentType(); 568 if (ct.equals(Byte.TYPE)) 569 { 570 return "1.3.6.1.4.1.1466.115.121.1.40"; 571 } 572 else if (ct.equals(Character.TYPE)) 573 { 574 return "1.3.6.1.4.1.1466.115.121.1.15"; 575 } 576 } 577 else if (t.isEnum()) 578 { 579 return "1.3.6.1.4.1.1466.115.121.1.15"; 580 } 581 else if (Serializable.class.isAssignableFrom(t)) 582 { 583 return "1.3.6.1.4.1.1466.115.121.1.40"; 584 } 585 586 return null; 587 } 588 589 590 591 /** 592 * {@inheritDoc} 593 */ 594 @Override() 595 public boolean supportsMultipleValues(final Field field) 596 { 597 return supportsMultipleValues(new TypeInfo(field.getGenericType())); 598 } 599 600 601 602 /** 603 * {@inheritDoc} 604 */ 605 @Override() 606 public boolean supportsMultipleValues(final Method method) 607 { 608 final Type[] paramTypes = method.getGenericParameterTypes(); 609 if (paramTypes.length != 1) 610 { 611 return false; 612 } 613 614 return supportsMultipleValues(new TypeInfo(paramTypes[0])); 615 } 616 617 618 619 /** 620 * Indicates whether the provided object type supports multiple values. 621 * 622 * @param t The type for which to make the determination. 623 * 624 * @return {@code true} if the provided object type supports multiple values, 625 * or {@code false} if not. 626 */ 627 private static boolean supportsMultipleValues(final TypeInfo t) 628 { 629 if (t.isArray()) 630 { 631 final Class<?> componentType = t.getComponentType(); 632 return (! (componentType.equals(Byte.TYPE) || 633 componentType.equals(Character.TYPE))); 634 } 635 else 636 { 637 return t.isMultiValued(); 638 } 639 } 640 641 642 643 /** 644 * {@inheritDoc} 645 */ 646 @Override() 647 public Attribute encodeFieldValue(final Field field, final Object value, 648 final String name) 649 throws LDAPPersistException 650 { 651 return encodeValue(field.getGenericType(), value, name); 652 } 653 654 655 656 /** 657 * {@inheritDoc} 658 */ 659 @Override() 660 public Attribute encodeMethodValue(final Method method, final Object value, 661 final String name) 662 throws LDAPPersistException 663 { 664 return encodeValue(method.getGenericReturnType(), value, name); 665 } 666 667 668 669 /** 670 * Encodes the provided value to an LDAP attribute. 671 * 672 * @param type The type for the provided value. 673 * @param value The value for the field in the object to be encoded. 674 * @param name The name to use for the constructed attribute. 675 * 676 * @return The attribute containing the encoded representation of the 677 * provided field. 678 * 679 * @throws LDAPPersistException If a problem occurs while attempting to 680 * construct an attribute for the field. 681 */ 682 private static Attribute encodeValue(final Type type, final Object value, 683 final String name) 684 throws LDAPPersistException 685 { 686 final TypeInfo typeInfo = new TypeInfo(type); 687 688 final Class<?> c = typeInfo.getBaseClass(); 689 if (c.equals(AtomicInteger.class) || 690 c.equals(AtomicLong.class) || 691 c.equals(BigDecimal.class) || 692 c.equals(BigInteger.class) || 693 c.equals(Double.class) || 694 c.equals(Double.TYPE) || 695 c.equals(Float.class) || 696 c.equals(Float.TYPE) || 697 c.equals(Integer.class) || 698 c.equals(Integer.TYPE) || 699 c.equals(Long.class) || 700 c.equals(Long.TYPE) || 701 c.equals(Short.class) || 702 c.equals(Short.TYPE) || 703 c.equals(String.class) || 704 c.equals(StringBuffer.class) || 705 c.equals(StringBuilder.class) || 706 c.equals(UUID.class) || 707 c.equals(DN.class) || 708 c.equals(Filter.class) || 709 c.equals(LDAPURL.class) || 710 c.equals(RDN.class)) 711 { 712 return new Attribute(name, String.valueOf(value)); 713 } 714 else if (value instanceof URI) 715 { 716 final URI uri = (URI) value; 717 return new Attribute(name, uri.toASCIIString()); 718 } 719 else if (value instanceof URL) 720 { 721 final URL url = (URL) value; 722 return new Attribute(name, url.toExternalForm()); 723 } 724 else if (value instanceof byte[]) 725 { 726 return new Attribute(name, (byte[]) value); 727 } 728 else if (value instanceof char[]) 729 { 730 return new Attribute(name, new String((char[]) value)); 731 } 732 else if (c.equals(Boolean.class) || c.equals(Boolean.TYPE)) 733 { 734 final Boolean b = (Boolean) value; 735 if (b) 736 { 737 return new Attribute(name, "TRUE"); 738 } 739 else 740 { 741 return new Attribute(name, "FALSE"); 742 } 743 } 744 else if (c.equals(Date.class)) 745 { 746 final Date d = (Date) value; 747 return new Attribute(name, encodeGeneralizedTime(d)); 748 } 749 else if (typeInfo.isArray()) 750 { 751 return encodeArray(typeInfo.getComponentType(), value, name); 752 } 753 else if (typeInfo.isEnum()) 754 { 755 final Enum<?> e = (Enum<?>) value; 756 return new Attribute(name, e.name()); 757 } 758 else if (Collection.class.isAssignableFrom(c)) 759 { 760 return encodeCollection(typeInfo.getComponentType(), 761 (Collection<?>) value, name); 762 } 763 else if (Serializable.class.isAssignableFrom(c)) 764 { 765 try 766 { 767 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 768 final ObjectOutputStream oos = new ObjectOutputStream(baos); 769 oos.writeObject(value); 770 oos.close(); 771 return new Attribute(name, baos.toByteArray()); 772 } 773 catch (final Exception e) 774 { 775 debugException(e); 776 throw new LDAPPersistException( 777 ERR_DEFAULT_ENCODER_CANNOT_SERIALIZE.get(name, 778 getExceptionMessage(e)), 779 e); 780 } 781 } 782 783 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 784 String.valueOf(type))); 785 } 786 787 788 789 /** 790 * Encodes the contents of the provided array object. 791 * 792 * @param arrayType The component type of the array. 793 * @param arrayObject The array object to process. 794 * @param attributeName The name to use for the attribute to create. 795 * 796 * @return The attribute containing the encoded array contents. 797 * 798 * @throws LDAPPersistException If a problem occurs while trying to create 799 * the attribute. 800 */ 801 private static Attribute encodeArray(final Class<?> arrayType, 802 final Object arrayObject, 803 final String attributeName) 804 throws LDAPPersistException 805 { 806 final ASN1OctetString[] values = 807 new ASN1OctetString[Array.getLength(arrayObject)]; 808 for (int i=0; i < values.length; i++) 809 { 810 final Object o = Array.get(arrayObject, i); 811 if (arrayType.equals(AtomicInteger.class) || 812 arrayType.equals(AtomicLong.class) || 813 arrayType.equals(BigDecimal.class) || 814 arrayType.equals(BigInteger.class) || 815 arrayType.equals(Double.class) || 816 arrayType.equals(Double.TYPE) || 817 arrayType.equals(Float.class) || 818 arrayType.equals(Float.TYPE) || 819 arrayType.equals(Integer.class) || 820 arrayType.equals(Integer.TYPE) || 821 arrayType.equals(Long.class) || 822 arrayType.equals(Long.TYPE) || 823 arrayType.equals(Short.class) || 824 arrayType.equals(Short.TYPE) || 825 arrayType.equals(String.class) || 826 arrayType.equals(StringBuffer.class) || 827 arrayType.equals(StringBuilder.class) || 828 arrayType.equals(UUID.class) || 829 arrayType.equals(DN.class) || 830 arrayType.equals(Filter.class) || 831 arrayType.equals(LDAPURL.class) || 832 arrayType.equals(RDN.class)) 833 { 834 values[i] = new ASN1OctetString(String.valueOf(o)); 835 } 836 else if (arrayType.equals(URI.class)) 837 { 838 final URI uri = (URI) o; 839 values[i] = new ASN1OctetString(uri.toASCIIString()); 840 } 841 else if (arrayType.equals(URL.class)) 842 { 843 final URL url = (URL) o; 844 values[i] = new ASN1OctetString(url.toExternalForm()); 845 } 846 else if (o instanceof byte[]) 847 { 848 values[i] = new ASN1OctetString((byte[]) o); 849 } 850 else if (o instanceof char[]) 851 { 852 values[i] = new ASN1OctetString(new String((char[]) o)); 853 } 854 else if (arrayType.equals(Boolean.class) || 855 arrayType.equals(Boolean.TYPE)) 856 { 857 final Boolean b = (Boolean) o; 858 if (b) 859 { 860 values[i] = new ASN1OctetString("TRUE"); 861 } 862 else 863 { 864 values[i] = new ASN1OctetString("FALSE"); 865 } 866 } 867 else if (arrayType.equals(Date.class)) 868 { 869 final Date d = (Date) o; 870 values[i] = new ASN1OctetString(encodeGeneralizedTime(d)); 871 } 872 else if (arrayType.isEnum()) 873 { 874 final Enum<?> e = (Enum<?>) o; 875 values[i] = new ASN1OctetString(e.name()); 876 } 877 else if (Serializable.class.isAssignableFrom(arrayType)) 878 { 879 try 880 { 881 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 882 final ObjectOutputStream oos = new ObjectOutputStream(baos); 883 oos.writeObject(o); 884 oos.close(); 885 values[i] = new ASN1OctetString(baos.toByteArray()); 886 } 887 catch (final Exception e) 888 { 889 debugException(e); 890 throw new LDAPPersistException( 891 ERR_DEFAULT_ENCODER_CANNOT_SERIALIZE.get(attributeName, 892 getExceptionMessage(e)), 893 e); 894 } 895 } 896 else 897 { 898 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 899 arrayType.getName())); 900 } 901 } 902 903 return new Attribute(attributeName, 904 CaseIgnoreStringMatchingRule.getInstance(), values); 905 } 906 907 908 909 /** 910 * Encodes the contents of the provided collection. 911 * 912 * @param genericType The generic type of the collection. 913 * @param collection The collection to process. 914 * @param attributeName The name to use for the attribute to create. 915 * 916 * @return The attribute containing the encoded collection contents. 917 * 918 * @throws LDAPPersistException If a problem occurs while trying to create 919 * the attribute. 920 */ 921 private static Attribute encodeCollection(final Class<?> genericType, 922 final Collection<?> collection, 923 final String attributeName) 924 throws LDAPPersistException 925 { 926 final ASN1OctetString[] values = new ASN1OctetString[collection.size()]; 927 928 int i=0; 929 for (final Object o : collection) 930 { 931 if (genericType.equals(AtomicInteger.class) || 932 genericType.equals(AtomicLong.class) || 933 genericType.equals(BigDecimal.class) || 934 genericType.equals(BigInteger.class) || 935 genericType.equals(Double.class) || 936 genericType.equals(Double.TYPE) || 937 genericType.equals(Float.class) || 938 genericType.equals(Float.TYPE) || 939 genericType.equals(Integer.class) || 940 genericType.equals(Integer.TYPE) || 941 genericType.equals(Long.class) || 942 genericType.equals(Long.TYPE) || 943 genericType.equals(Short.class) || 944 genericType.equals(Short.TYPE) || 945 genericType.equals(String.class) || 946 genericType.equals(StringBuffer.class) || 947 genericType.equals(StringBuilder.class) || 948 genericType.equals(UUID.class) || 949 genericType.equals(DN.class) || 950 genericType.equals(Filter.class) || 951 genericType.equals(LDAPURL.class) || 952 genericType.equals(RDN.class)) 953 { 954 values[i] = new ASN1OctetString(String.valueOf(o)); 955 } 956 else if (genericType.equals(URI.class)) 957 { 958 final URI uri = (URI) o; 959 values[i] = new ASN1OctetString(uri.toASCIIString()); 960 } 961 else if (genericType.equals(URL.class)) 962 { 963 final URL url = (URL) o; 964 values[i] = new ASN1OctetString(url.toExternalForm()); 965 } 966 else if (o instanceof byte[]) 967 { 968 values[i] = new ASN1OctetString((byte[]) o); 969 } 970 else if (o instanceof char[]) 971 { 972 values[i] = new ASN1OctetString(new String((char[]) o)); 973 } 974 else if (genericType.equals(Boolean.class) || 975 genericType.equals(Boolean.TYPE)) 976 { 977 final Boolean b = (Boolean) o; 978 if (b) 979 { 980 values[i] = new ASN1OctetString("TRUE"); 981 } 982 else 983 { 984 values[i] = new ASN1OctetString("FALSE"); 985 } 986 } 987 else if (genericType.equals(Date.class)) 988 { 989 final Date d = (Date) o; 990 values[i] = new ASN1OctetString(encodeGeneralizedTime(d)); 991 } 992 else if (genericType.isEnum()) 993 { 994 final Enum<?> e = (Enum<?>) o; 995 values[i] = new ASN1OctetString(e.name()); 996 } 997 else if (Serializable.class.isAssignableFrom(genericType)) 998 { 999 try 1000 { 1001 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1002 final ObjectOutputStream oos = new ObjectOutputStream(baos); 1003 oos.writeObject(o); 1004 oos.close(); 1005 values[i] = new ASN1OctetString(baos.toByteArray()); 1006 } 1007 catch (final Exception e) 1008 { 1009 debugException(e); 1010 throw new LDAPPersistException( 1011 ERR_DEFAULT_ENCODER_CANNOT_SERIALIZE.get(attributeName, 1012 getExceptionMessage(e)), 1013 e); 1014 } 1015 } 1016 else 1017 { 1018 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 1019 genericType.getName())); 1020 } 1021 1022 i++; 1023 } 1024 1025 return new Attribute(attributeName, 1026 CaseIgnoreStringMatchingRule.getInstance(), values); 1027 } 1028 1029 1030 1031 /** 1032 * {@inheritDoc} 1033 */ 1034 @Override() 1035 public void decodeField(final Field field, final Object object, 1036 final Attribute attribute) 1037 throws LDAPPersistException 1038 { 1039 field.setAccessible(true); 1040 final TypeInfo typeInfo = new TypeInfo(field.getGenericType()); 1041 1042 try 1043 { 1044 final Class<?> baseClass = typeInfo.getBaseClass(); 1045 final Object newValue = getValue(baseClass, attribute, 0); 1046 if (newValue != null) 1047 { 1048 field.set(object, newValue); 1049 return; 1050 } 1051 1052 if (typeInfo.isArray()) 1053 { 1054 final Class<?> componentType = typeInfo.getComponentType(); 1055 final ASN1OctetString[] values = attribute.getRawValues(); 1056 final Object arrayObject = 1057 Array.newInstance(componentType, values.length); 1058 for (int i=0; i < values.length; i++) 1059 { 1060 final Object o = getValue(componentType, attribute, i); 1061 if (o == null) 1062 { 1063 throw new LDAPPersistException( 1064 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 1065 componentType.getName())); 1066 } 1067 Array.set(arrayObject, i, o); 1068 } 1069 1070 field.set(object, arrayObject); 1071 return; 1072 } 1073 else if (typeInfo.isList() && isSupportedListType(baseClass)) 1074 { 1075 final Class<?> componentType = typeInfo.getComponentType(); 1076 if (componentType == null) 1077 { 1078 throw new LDAPPersistException( 1079 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName())); 1080 } 1081 1082 final ASN1OctetString[] values = attribute.getRawValues(); 1083 final List<?> l = createList(baseClass, values.length); 1084 for (int i=0; i < values.length; i++) 1085 { 1086 final Object o = getValue(componentType, attribute, i); 1087 if (o == null) 1088 { 1089 throw new LDAPPersistException( 1090 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 1091 componentType.getName())); 1092 } 1093 1094 invokeAdd(l, o); 1095 } 1096 1097 field.set(object, l); 1098 return; 1099 } 1100 else if (typeInfo.isSet() && isSupportedSetType(baseClass)) 1101 { 1102 final Class<?> componentType = typeInfo.getComponentType(); 1103 if (componentType == null) 1104 { 1105 throw new LDAPPersistException( 1106 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName())); 1107 } 1108 1109 final ASN1OctetString[] values = attribute.getRawValues(); 1110 final Set<?> l = createSet(baseClass, values.length); 1111 for (int i=0; i < values.length; i++) 1112 { 1113 final Object o = getValue(componentType, attribute, i); 1114 if (o == null) 1115 { 1116 throw new LDAPPersistException( 1117 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 1118 componentType.getName())); 1119 } 1120 1121 invokeAdd(l, o); 1122 } 1123 1124 field.set(object, l); 1125 return; 1126 } 1127 1128 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 1129 baseClass.getName())); 1130 } 1131 catch (LDAPPersistException lpe) 1132 { 1133 debugException(lpe); 1134 throw lpe; 1135 } 1136 catch (Exception e) 1137 { 1138 debugException(e); 1139 throw new LDAPPersistException(getExceptionMessage(e), e); 1140 } 1141 } 1142 1143 1144 1145 /** 1146 * {@inheritDoc} 1147 */ 1148 @Override() 1149 public void invokeSetter(final Method method, final Object object, 1150 final Attribute attribute) 1151 throws LDAPPersistException 1152 { 1153 final TypeInfo typeInfo = 1154 new TypeInfo(method.getGenericParameterTypes()[0]); 1155 final Class<?> baseClass = typeInfo.getBaseClass(); 1156 method.setAccessible(true); 1157 1158 try 1159 { 1160 final Object newValue = getValue(baseClass, attribute, 0); 1161 if (newValue != null) 1162 { 1163 method.invoke(object, newValue); 1164 return; 1165 } 1166 1167 if (typeInfo.isArray()) 1168 { 1169 final Class<?> componentType = typeInfo.getComponentType(); 1170 final ASN1OctetString[] values = attribute.getRawValues(); 1171 final Object arrayObject = 1172 Array.newInstance(componentType, values.length); 1173 for (int i=0; i < values.length; i++) 1174 { 1175 final Object o = getValue(componentType, attribute, i); 1176 if (o == null) 1177 { 1178 throw new LDAPPersistException( 1179 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 1180 componentType.getName())); 1181 } 1182 Array.set(arrayObject, i, o); 1183 } 1184 1185 method.invoke(object, arrayObject); 1186 return; 1187 } 1188 else if (typeInfo.isList() && isSupportedListType(baseClass)) 1189 { 1190 final Class<?> componentType = typeInfo.getComponentType(); 1191 if (componentType == null) 1192 { 1193 throw new LDAPPersistException( 1194 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName())); 1195 } 1196 1197 final ASN1OctetString[] values = attribute.getRawValues(); 1198 final List<?> l = createList(baseClass, values.length); 1199 for (int i=0; i < values.length; i++) 1200 { 1201 final Object o = getValue(componentType, attribute, i); 1202 if (o == null) 1203 { 1204 throw new LDAPPersistException( 1205 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 1206 componentType.getName())); 1207 } 1208 1209 invokeAdd(l, o); 1210 } 1211 1212 method.invoke(object, l); 1213 return; 1214 } 1215 else if (typeInfo.isSet() && isSupportedSetType(baseClass)) 1216 { 1217 final Class<?> componentType = typeInfo.getComponentType(); 1218 if (componentType == null) 1219 { 1220 throw new LDAPPersistException( 1221 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName())); 1222 } 1223 1224 final ASN1OctetString[] values = attribute.getRawValues(); 1225 final Set<?> s = createSet(baseClass, values.length); 1226 for (int i=0; i < values.length; i++) 1227 { 1228 final Object o = getValue(componentType, attribute, i); 1229 if (o == null) 1230 { 1231 throw new LDAPPersistException( 1232 ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 1233 componentType.getName())); 1234 } 1235 1236 invokeAdd(s, o); 1237 } 1238 1239 method.invoke(object, s); 1240 return; 1241 } 1242 1243 throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( 1244 baseClass.getName())); 1245 } 1246 catch (LDAPPersistException lpe) 1247 { 1248 debugException(lpe); 1249 throw lpe; 1250 } 1251 catch (Throwable t) 1252 { 1253 debugException(t); 1254 1255 if (t instanceof InvocationTargetException) 1256 { 1257 t = ((InvocationTargetException) t).getTargetException(); 1258 } 1259 1260 throw new LDAPPersistException(getExceptionMessage(t), t); 1261 } 1262 } 1263 1264 1265 1266 /** 1267 * Creates an object of the specified type from the given attribute value. 1268 * 1269 * @param t The type of object to create. 1270 * @param a The attribute to use to create the object. 1271 * @param p The position in the set of values for the object to create. 1272 * 1273 * @return The created object, or {@code null} if the provided type is not 1274 * supported. 1275 * 1276 * @throws LDAPPersistException If a problem occurs while creating the 1277 * object. 1278 */ 1279 @SuppressWarnings("unchecked") 1280 private static Object getValue(final Class<?> t, final Attribute a, 1281 final int p) 1282 throws LDAPPersistException 1283 { 1284 final ASN1OctetString v = a.getRawValues()[p]; 1285 1286 if (t.equals(AtomicInteger.class)) 1287 { 1288 return new AtomicInteger(Integer.valueOf(v.stringValue())); 1289 } 1290 else if (t.equals(AtomicLong.class)) 1291 { 1292 return new AtomicLong(Long.valueOf(v.stringValue())); 1293 } 1294 else if (t.equals(BigDecimal.class)) 1295 { 1296 return new BigDecimal(v.stringValue()); 1297 } 1298 else if (t.equals(BigInteger.class)) 1299 { 1300 return new BigInteger(v.stringValue()); 1301 } 1302 else if (t.equals(Double.class) || t.equals(Double.TYPE)) 1303 { 1304 return Double.valueOf(v.stringValue()); 1305 } 1306 else if (t.equals(Float.class) || t.equals(Float.TYPE)) 1307 { 1308 return Float.valueOf(v.stringValue()); 1309 } 1310 else if (t.equals(Integer.class) || t.equals(Integer.TYPE)) 1311 { 1312 return Integer.valueOf(v.stringValue()); 1313 } 1314 else if (t.equals(Long.class) || t.equals(Long.TYPE)) 1315 { 1316 return Long.valueOf(v.stringValue()); 1317 } 1318 else if (t.equals(Short.class) || t.equals(Short.TYPE)) 1319 { 1320 return Short.valueOf(v.stringValue()); 1321 } 1322 else if (t.equals(String.class)) 1323 { 1324 return String.valueOf(v.stringValue()); 1325 } 1326 else if (t.equals(StringBuffer.class)) 1327 { 1328 return new StringBuffer(v.stringValue()); 1329 } 1330 else if (t.equals(StringBuilder.class)) 1331 { 1332 return new StringBuilder(v.stringValue()); 1333 } 1334 else if (t.equals(URI.class)) 1335 { 1336 try 1337 { 1338 return new URI(v.stringValue()); 1339 } 1340 catch (final Exception e) 1341 { 1342 debugException(e); 1343 throw new LDAPPersistException( 1344 ERR_DEFAULT_ENCODER_VALUE_INVALID_URI.get(v.stringValue(), 1345 getExceptionMessage(e)), e); 1346 } 1347 } 1348 else if (t.equals(URL.class)) 1349 { 1350 try 1351 { 1352 return new URL(v.stringValue()); 1353 } 1354 catch (final Exception e) 1355 { 1356 debugException(e); 1357 throw new LDAPPersistException( 1358 ERR_DEFAULT_ENCODER_VALUE_INVALID_URL.get(v.stringValue(), 1359 getExceptionMessage(e)), e); 1360 } 1361 } 1362 else if (t.equals(UUID.class)) 1363 { 1364 try 1365 { 1366 return UUID.fromString(v.stringValue()); 1367 } 1368 catch (Exception e) 1369 { 1370 debugException(e); 1371 throw new LDAPPersistException( 1372 ERR_DEFAULT_ENCODER_VALUE_INVALID_UUID.get(v.stringValue(), 1373 getExceptionMessage(e)), e); 1374 } 1375 } 1376 else if (t.equals(DN.class)) 1377 { 1378 try 1379 { 1380 return new DN(v.stringValue()); 1381 } 1382 catch (LDAPException le) 1383 { 1384 debugException(le); 1385 throw new LDAPPersistException(le.getMessage(), le); 1386 } 1387 } 1388 else if (t.equals(Filter.class)) 1389 { 1390 try 1391 { 1392 return Filter.create(v.stringValue()); 1393 } 1394 catch (LDAPException le) 1395 { 1396 debugException(le); 1397 throw new LDAPPersistException(le.getMessage(), le); 1398 } 1399 } 1400 else if (t.equals(LDAPURL.class)) 1401 { 1402 try 1403 { 1404 return new LDAPURL(v.stringValue()); 1405 } 1406 catch (LDAPException le) 1407 { 1408 debugException(le); 1409 throw new LDAPPersistException(le.getMessage(), le); 1410 } 1411 } 1412 else if (t.equals(RDN.class)) 1413 { 1414 try 1415 { 1416 return new RDN(v.stringValue()); 1417 } 1418 catch (LDAPException le) 1419 { 1420 debugException(le); 1421 throw new LDAPPersistException(le.getMessage(), le); 1422 } 1423 } 1424 else if (t.equals(Boolean.class) || t.equals(Boolean.TYPE)) 1425 { 1426 final String s = v.stringValue(); 1427 if (s.equalsIgnoreCase("TRUE")) 1428 { 1429 return Boolean.TRUE; 1430 } 1431 else if (s.equalsIgnoreCase("FALSE")) 1432 { 1433 return Boolean.FALSE; 1434 } 1435 else 1436 { 1437 throw new LDAPPersistException( 1438 ERR_DEFAULT_ENCODER_VALUE_INVALID_BOOLEAN.get(s)); 1439 } 1440 } 1441 else if (t.equals(Date.class)) 1442 { 1443 try 1444 { 1445 return decodeGeneralizedTime(v.stringValue()); 1446 } 1447 catch (Exception e) 1448 { 1449 debugException(e); 1450 throw new LDAPPersistException( 1451 ERR_DEFAULT_ENCODER_VALUE_INVALID_DATE.get(v.stringValue(), 1452 e.getMessage()), e); 1453 } 1454 } 1455 else if (t.isArray()) 1456 { 1457 final Class<?> componentType = t.getComponentType(); 1458 if (componentType.equals(Byte.TYPE)) 1459 { 1460 return v.getValue(); 1461 } 1462 else if (componentType.equals(Character.TYPE)) 1463 { 1464 return v.stringValue().toCharArray(); 1465 } 1466 } 1467 else if (t.isEnum()) 1468 { 1469 try 1470 { 1471 final Class<? extends Enum> enumClass = (Class<? extends Enum>) t; 1472 return Enum.valueOf(enumClass, v.stringValue()); 1473 } 1474 catch (final Exception e) 1475 { 1476 debugException(e); 1477 throw new LDAPPersistException( 1478 ERR_DEFAULT_ENCODER_VALUE_INVALID_ENUM.get(v.stringValue(), 1479 getExceptionMessage(e)), e); 1480 } 1481 } 1482 else if (Serializable.class.isAssignableFrom(t)) 1483 { 1484 // We shouldn't attempt to work on arrays/collections themselves. Return 1485 // null and then we'll work on each element. 1486 if (t.isArray() || Collection.class.isAssignableFrom(t)) 1487 { 1488 return null; 1489 } 1490 1491 try 1492 { 1493 final ByteArrayInputStream bais = 1494 new ByteArrayInputStream(v.getValue()); 1495 final ObjectInputStream ois = new ObjectInputStream(bais); 1496 final Object o = ois.readObject(); 1497 ois.close(); 1498 return o; 1499 } 1500 catch (final Exception e) 1501 { 1502 debugException(e); 1503 throw new LDAPPersistException( 1504 ERR_DEFAULT_ENCODER_CANNOT_DESERIALIZE.get(a.getName(), 1505 getExceptionMessage(e)), 1506 e); 1507 } 1508 } 1509 1510 return null; 1511 } 1512 1513 1514 1515 /** 1516 * Invokes the {@code add} method on the provided {@code List} or {@code Set} 1517 * object. 1518 * 1519 * @param l The list or set on which to invoke the {@code add} method. 1520 * @param o The object to add to the {@code List} or {@code Set} object. 1521 * 1522 * @throws LDAPPersistException If a problem occurs while attempting to 1523 * invoke the {@code add} method. 1524 */ 1525 private static void invokeAdd(final Object l, final Object o) 1526 throws LDAPPersistException 1527 { 1528 final Class<?> c = l.getClass(); 1529 1530 for (final Method m : c.getMethods()) 1531 { 1532 if (m.getName().equals("add") && 1533 (m.getGenericParameterTypes().length == 1)) 1534 { 1535 try 1536 { 1537 m.invoke(l, o); 1538 return; 1539 } 1540 catch (final Exception e) 1541 { 1542 debugException(e); 1543 throw new LDAPPersistException( 1544 ERR_DEFAULT_ENCODER_CANNOT_ADD.get(getExceptionMessage(e)), e); 1545 } 1546 } 1547 } 1548 1549 throw new LDAPPersistException( 1550 ERR_DEFAULT_ENCODER_CANNOT_FIND_ADD_METHOD.get()); 1551 } 1552}