All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.persist.DefaultObjectEncoder Maven / Gradle / Ivy

Go to download

The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use Java API for communicating with LDAP directory servers and performing related tasks like reading and writing LDIF, encoding and decoding data using base64 and ASN.1 BER, and performing secure communication. This package contains the Standard Edition of the LDAP SDK, which is a complete, general-purpose library for communicating with LDAPv3 directory servers.

There is a newer version: 7.0.1
Show newest version
/*
 * Copyright 2009-2023 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2009-2023 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2009-2023 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.persist;



import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import com.unboundid.asn1.ASN1OctetString;
import com.unboundid.ldap.matchingrules.BooleanMatchingRule;
import com.unboundid.ldap.matchingrules.CaseIgnoreStringMatchingRule;
import com.unboundid.ldap.matchingrules.GeneralizedTimeMatchingRule;
import com.unboundid.ldap.matchingrules.MatchingRule;
import com.unboundid.ldap.matchingrules.OctetStringMatchingRule;
import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPURL;
import com.unboundid.ldap.sdk.RDN;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
import com.unboundid.ldap.sdk.schema.AttributeUsage;
import com.unboundid.util.Debug;
import com.unboundid.util.NotMutable;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;

import static com.unboundid.ldap.sdk.persist.PersistMessages.*;



/**
 * This class provides the default implementation of an {@link ObjectEncoder}
 * object that will be used when encoding and decoding fields to be written to
 * or read from an LDAP directory server.
 * 

* The following basic types will be supported, with the following encodings: *
    *
  • Any kind of enumeration -- Encoded using the name of the enum * value
  • *
  • {@code java.util.concurrent.atomic.AtomicInteger} -- Encoded using the * string representation of the value
  • *
  • {@code java.util.concurrent.atomic.AtomicLong} -- Encoded using the * string representation of the value
  • *
  • {@code java.math.BigDecimal} -- Encoded using the string representation * of the value
  • *
  • {@code java.math.BigInteger} -- Encoded using the string representation * of the value
  • *
  • {@code boolean} -- Encoded as either "TRUE" or "FALSE"
  • *
  • {@code java.lang.Boolean} -- Encoded as either "TRUE" or "FALSE"
  • *
  • {@code byte[]} -- Encoded as the raw bytes contained in the array
  • *
  • {@code char[]} -- Encoded as a string containing the characters in the * array
  • *
  • {@code java.util.Date} -- Encoded using the generalized time * syntax
  • *
  • {@code com.unboundid.ldap.sdk.DN} -- Encoded using the string * representation of the value
  • *
  • {@code double} -- Encoded using the string representation of the * value
  • *
  • {@code java.lang.Double} -- Encoded using the string representation of * the value
  • *
  • {@code com.unboundid.ldap.sdk.Filter} -- Encoded using the string * representation of the value
  • *
  • {@code float} -- Encoded using the string representation of the * value
  • *
  • {@code java.lang.Float} -- Encoded using the string representation of * the value
  • *
  • {@code int} -- Encoded using the string representation of the * value
  • *
  • {@code java.lang.Integer} -- Encoded using the string representation of * the value
  • *
  • {@code com.unboundid.ldap.sdk.LDAPURL} -- Encoded using the string * representation of the value
  • *
  • {@code long} -- Encoded using the string representation of the * value
  • *
  • {@code java.lang.Long} -- Encoded using the string representation of * the value
  • *
  • {@code com.unboundid.ldap.sdk.RDN} -- Encoded using the string * representation of the value
  • *
  • {@code short} -- Encoded using the string representation of the * value
  • *
  • {@code java.lang.Short} -- Encoded using the string representation of * the value
  • *
  • {@code java.lang.String} -- Encoded using the value
  • *
  • {@code java.lang.StringBuffer} -- Encoded using the string * representation of the value
  • *
  • {@code java.lang.StringBuilder} -- Encoded using the string * representation of the value
  • *
  • {@code java.net.URI} -- Encoded using the string representation of the * value.
  • *
  • {@code java.net.URL} -- Encoded using the string representation of the * value.
  • *
  • {@code java.util.UUID} -- Encoded using the string representation of * the value
  • *
* Serializable objects are also supported, in which case the raw bytes that * comprise the serialized representation will be used. This may be * undesirable, because the value may only be interpretable by Java-based * clients. If you wish to better control the encoding for serialized objects, * have them implement custom {@code writeObject}, {@code readObject}, and * {@code readObjectNoData} methods that use the desired encoding. Alternately, * you may create a custom {@link ObjectEncoder} implementation for that object * type, or use getter/setter methods that convert between string/byte[] * representations and the desired object types. *

* In addition, arrays of all of the above types are also supported, in which * case each element of the array will be a separate value in the corresponding * LDAP attribute. Lists (including {@code ArrayList}, {@code LinkedList}, and * {@code CopyOnWriteArrayList}) and sets (including {@code HashSet}, * {@code LinkedHashSet}, {@code TreeSet}, and {@code CopyOnWriteArraySet}) of * the above types are also supported. *

* Note that you should be careful when using primitive types, since they cannot * be unassigned and therefore will always have a value. When using an LDAP * entry to initialize an object any fields with primitive types which are * associated with LDAP attributes not present in the entry will have the * default value assigned to them in the zero-argument constructor, or will have * the JVM-supplied default value if no value was assigned to it in the * constructor. If the associated object is converted back to an LDAP entry, * then those fields will be included in the entry that is generated, even if * they were not present in the original entry. To avoid this problem, you can * use the object types rather than the primitive types (e.g., * {@code java.lang.Boolean} instead of the {@code boolean} primitive), in which * case any fields associated with attributes that are not present in the entry * being de-serialized will be explicitly set to {@code null}. */ @NotMutable() @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) public final class DefaultObjectEncoder extends ObjectEncoder { /** * The serial version UID for this serializable class. */ private static final long serialVersionUID = -4566874784628920022L; /** * Creates a new instance of this encoder. */ public DefaultObjectEncoder() { super(); } /** * {@inheritDoc} */ @Override() public boolean supportsType(@NotNull final Type t) { final TypeInfo typeInfo = new TypeInfo(t); if (! typeInfo.isSupported()) { return false; } final Class baseClass = typeInfo.getBaseClass(); if (supportsTypeInternal(baseClass)) { return true; } final Class componentType = typeInfo.getComponentType(); if (componentType == null) { return false; } if (typeInfo.isArray()) { return supportsTypeInternal(componentType); } if (typeInfo.isList()) { return (isSupportedListType(baseClass) && supportsTypeInternal(componentType)); } if (typeInfo.isSet()) { return (isSupportedSetType(baseClass) && supportsTypeInternal(componentType)); } return false; } /** * Indicates whether this object encoder supports objects of the specified * type. * * @param c The object type class for which to make the determination. * * @return {@code true} if this object supports objects of the specified * type, or {@code false} if not. */ private static boolean supportsTypeInternal(@NotNull final Class c) { if (c.equals(AtomicInteger.class) || c.equals(AtomicLong.class) || c.equals(BigDecimal.class) || c.equals(BigInteger.class) || c.equals(Boolean.class) || c.equals(Boolean.TYPE) || c.equals(Date.class) || c.equals(DN.class) || c.equals(Double.class) || c.equals(Double.TYPE) || c.equals(Filter.class) || c.equals(Float.class) || c.equals(Float.TYPE) || c.equals(Integer.class) || c.equals(Integer.TYPE) || c.equals(LDAPURL.class) || c.equals(Long.class) || c.equals(Long.TYPE) || c.equals(RDN.class) || c.equals(Short.class) || c.equals(Short.TYPE) || c.equals(String.class) || c.equals(StringBuffer.class) || c.equals(StringBuilder.class) || c.equals(URI.class) || c.equals(URL.class) || c.equals(UUID.class)) { return true; } if (c.isArray()) { final Class t = c.getComponentType(); if (t.equals(Byte.TYPE) || t.equals(Character.TYPE)) { return true; } } if (c.isEnum()) { return true; } if (Serializable.class.isAssignableFrom(c)) { return (! (c.isArray() || Collection.class.isAssignableFrom(c))); } return false; } /** * Indicates whether the provided type is a supported list type. * * @param t The type for which to make the determination. * * @return {@code true} if the provided type is a supported list type, or * or {@code false}. */ private static boolean isSupportedListType(@NotNull final Class t) { return (t.equals(List.class) || t.equals(ArrayList.class) || t.equals(LinkedList.class) || t.equals(CopyOnWriteArrayList.class)); } /** * Creates a new list of the specified type. * * @param t The type of list to create. * @param size The number of values that will be included in the list. * * @return The created list, or {@code null} if it is not a supported list * type. */ @SuppressWarnings("rawtypes") @Nullable() private static List createList(@NotNull final Class t, final int size) { if (t.equals(List.class) || t.equals(ArrayList.class)) { return new ArrayList(size); } else if (t.equals(LinkedList.class)) { return new LinkedList(); } else if (t.equals(CopyOnWriteArrayList.class)) { return new CopyOnWriteArrayList(); } return null; } /** * Indicates whether the provided type is a supported set type. * * @param t The type for which to make the determination. * * @return {@code true} if the provided type is a supported set type, or * or {@code false}. */ private static boolean isSupportedSetType(@NotNull final Class t) { return (t.equals(Set.class) || t.equals(HashSet.class) || t.equals(LinkedHashSet.class) || t.equals(TreeSet.class) || t.equals(CopyOnWriteArraySet.class)); } /** * Creates a new set of the specified type. * * @param t The type of set to create. * @param size The number of values that will be included in the set. * * @return The created list, or {@code null} if it is not a supported set * type. */ @SuppressWarnings("rawtypes") @Nullable() private static Set createSet(@NotNull final Class t, final int size) { if (t.equals(Set.class) || t.equals(LinkedHashSet.class)) { return new LinkedHashSet(StaticUtils.computeMapCapacity(size)); } else if (t.equals(HashSet.class)) { return new HashSet(StaticUtils.computeMapCapacity(size)); } else if (t.equals(TreeSet.class)) { return new TreeSet(); } else if (t.equals(CopyOnWriteArraySet.class)) { return new CopyOnWriteArraySet(); } return null; } /** * {@inheritDoc} */ @Override() @NotNull() public AttributeTypeDefinition constructAttributeType(@NotNull final Field f, @NotNull final OIDAllocator a) throws LDAPPersistException { final LDAPField at = f.getAnnotation(LDAPField.class); final String attrName; if (at.attribute().isEmpty()) { attrName = f.getName(); } else { attrName = at.attribute(); } final String oid = a.allocateAttributeTypeOID(attrName); final TypeInfo typeInfo = new TypeInfo(f.getGenericType()); if (! typeInfo.isSupported()) { throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( String.valueOf(typeInfo.getType()))); } final boolean isSingleValued = (! supportsMultipleValues(typeInfo)); final String syntaxOID; if (isSingleValued) { syntaxOID = getSyntaxOID(typeInfo.getBaseClass()); } else { syntaxOID = getSyntaxOID(typeInfo.getComponentType()); } final MatchingRule mr = MatchingRule.selectMatchingRuleForSyntax(syntaxOID); return new AttributeTypeDefinition(oid, new String[] { attrName }, null, false, null, mr.getEqualityMatchingRuleNameOrOID(), mr.getOrderingMatchingRuleNameOrOID(), mr.getSubstringMatchingRuleNameOrOID(), syntaxOID, isSingleValued, false, false, AttributeUsage.USER_APPLICATIONS, null); } /** * {@inheritDoc} */ @Override() @NotNull() public AttributeTypeDefinition constructAttributeType(@NotNull final Method m, @NotNull final OIDAllocator a) throws LDAPPersistException { final LDAPGetter at = m.getAnnotation(LDAPGetter.class); final String attrName; if (at.attribute().isEmpty()) { attrName = StaticUtils.toInitialLowerCase(m.getName().substring(3)); } else { attrName = at.attribute(); } final String oid = a.allocateAttributeTypeOID(attrName); final TypeInfo typeInfo = new TypeInfo(m.getGenericReturnType()); if (! typeInfo.isSupported()) { throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( String.valueOf(typeInfo.getType()))); } final boolean isSingleValued = (! supportsMultipleValues(typeInfo)); final String syntaxOID; if (isSingleValued) { syntaxOID = getSyntaxOID(typeInfo.getBaseClass()); } else { syntaxOID = getSyntaxOID(typeInfo.getComponentType()); } return new AttributeTypeDefinition(oid, new String[] { attrName }, null, false, null, null, null, null, syntaxOID, isSingleValued, false, false, AttributeUsage.USER_APPLICATIONS, null); } /** * Retrieves the syntax that should be used for the specified object type. * * @param t The type for which to make the determination. * * @return The syntax that should be used for the specified object type, or * {@code null} if it cannot be determined. */ @Nullable() private static String getSyntaxOID(@NotNull final Class t) { if (t.equals(BigDecimal.class) || t.equals(Double.class) || t.equals(Double.TYPE) || t.equals(Float.class) || t.equals(Float.TYPE) || t.equals(String.class) || t.equals(StringBuffer.class) || t.equals(StringBuilder.class) || t.equals(URI.class) || t.equals(URL.class) || t.equals(Filter.class) || t.equals(LDAPURL.class)) { return "1.3.6.1.4.1.1466.115.121.1.15"; } else if (t.equals(AtomicInteger.class) || t.equals(AtomicLong.class) || t.equals(BigInteger.class) || t.equals(Integer.class) || t.equals(Integer.TYPE) || t.equals(Long.class) || t.equals(Long.TYPE) || t.equals(Short.class) || t.equals(Short.TYPE)) { return "1.3.6.1.4.1.1466.115.121.1.27"; } else if (t.equals(UUID.class)) { // Although "1.3.6.1.1.16.1" (which is the UUID syntax as defined in RFC // 4530) might be more correct, some servers may not support this syntax // since it is relatively new, so we'll fall back on the more // widely-supported directory string syntax. return "1.3.6.1.4.1.1466.115.121.1.15"; } else if (t.equals(DN.class) || t.equals(RDN.class)) { return "1.3.6.1.4.1.1466.115.121.1.12"; } else if (t.equals(Boolean.class) || t.equals(Boolean.TYPE)) { return "1.3.6.1.4.1.1466.115.121.1.7"; } else if (t.equals(Date.class)) { return "1.3.6.1.4.1.1466.115.121.1.24"; } else if (t.isArray()) { final Class ct = t.getComponentType(); if (ct.equals(Byte.TYPE)) { return "1.3.6.1.4.1.1466.115.121.1.40"; } else if (ct.equals(Character.TYPE)) { return "1.3.6.1.4.1.1466.115.121.1.15"; } } else if (t.isEnum()) { return "1.3.6.1.4.1.1466.115.121.1.15"; } else if (Serializable.class.isAssignableFrom(t)) { return "1.3.6.1.4.1.1466.115.121.1.40"; } return null; } /** * {@inheritDoc} */ @Override() public boolean supportsMultipleValues(@NotNull final Field field) { return supportsMultipleValues(new TypeInfo(field.getGenericType())); } /** * {@inheritDoc} */ @Override() public boolean supportsMultipleValues(@NotNull final Method method) { final Type[] paramTypes = method.getGenericParameterTypes(); if (paramTypes.length != 1) { return false; } return supportsMultipleValues(new TypeInfo(paramTypes[0])); } /** * Indicates whether the provided object type supports multiple values. * * @param t The type for which to make the determination. * * @return {@code true} if the provided object type supports multiple values, * or {@code false} if not. */ private static boolean supportsMultipleValues(@NotNull final TypeInfo t) { if (t.isArray()) { final Class componentType = t.getComponentType(); return (! (componentType.equals(Byte.TYPE) || componentType.equals(Character.TYPE))); } else { return t.isMultiValued(); } } /** * {@inheritDoc} */ @Override() @NotNull() public Attribute encodeFieldValue(@NotNull final Field field, @NotNull final Object value, @NotNull final String name) throws LDAPPersistException { return encodeValue(field.getGenericType(), value, name); } /** * {@inheritDoc} */ @Override() @NotNull() public Attribute encodeMethodValue(@NotNull final Method method, @NotNull final Object value, @NotNull final String name) throws LDAPPersistException { return encodeValue(method.getGenericReturnType(), value, name); } /** * Encodes the provided value to an LDAP attribute. * * @param type The type for the provided value. * @param value The value for the field in the object to be encoded. * @param name The name to use for the constructed attribute. * * @return The attribute containing the encoded representation of the * provided field. * * @throws LDAPPersistException If a problem occurs while attempting to * construct an attribute for the field. */ @NotNull() private static Attribute encodeValue(@NotNull final Type type, @NotNull final Object value, @NotNull final String name) throws LDAPPersistException { final TypeInfo typeInfo = new TypeInfo(type); final Class c = typeInfo.getBaseClass(); if (c.equals(AtomicInteger.class) || c.equals(AtomicLong.class) || c.equals(BigDecimal.class) || c.equals(BigInteger.class) || c.equals(Double.class) || c.equals(Double.TYPE) || c.equals(Float.class) || c.equals(Float.TYPE) || c.equals(Integer.class) || c.equals(Integer.TYPE) || c.equals(Long.class) || c.equals(Long.TYPE) || c.equals(Short.class) || c.equals(Short.TYPE) || c.equals(String.class) || c.equals(StringBuffer.class) || c.equals(StringBuilder.class) || c.equals(UUID.class) || c.equals(DN.class) || c.equals(Filter.class) || c.equals(LDAPURL.class) || c.equals(RDN.class)) { final String syntaxOID = getSyntaxOID(c); final MatchingRule matchingRule = MatchingRule.selectMatchingRuleForSyntax(syntaxOID); return new Attribute(name, matchingRule, String.valueOf(value)); } else if (value instanceof URI) { final URI uri = (URI) value; return new Attribute(name, uri.toASCIIString()); } else if (value instanceof URL) { final URL url = (URL) value; return new Attribute(name, url.toExternalForm()); } else if (value instanceof byte[]) { return new Attribute(name, OctetStringMatchingRule.getInstance(), (byte[]) value); } else if (value instanceof char[]) { return new Attribute(name, new String((char[]) value)); } else if (c.equals(Boolean.class) || c.equals(Boolean.TYPE)) { final Boolean b = (Boolean) value; final MatchingRule matchingRule = BooleanMatchingRule.getInstance(); if (b) { return new Attribute(name, matchingRule, "TRUE"); } else { return new Attribute(name, matchingRule, "FALSE"); } } else if (c.equals(Date.class)) { final Date d = (Date) value; return new Attribute(name, GeneralizedTimeMatchingRule.getInstance(), StaticUtils.encodeGeneralizedTime(d)); } else if (typeInfo.isArray()) { return encodeArray(typeInfo.getComponentType(), value, name); } else if (typeInfo.isEnum()) { final Enum e = (Enum) value; return new Attribute(name, e.name()); } else if (Collection.class.isAssignableFrom(c)) { return encodeCollection(typeInfo.getComponentType(), (Collection) value, name); } else if (Serializable.class.isAssignableFrom(c)) { try { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(value); oos.close(); return new Attribute(name, OctetStringMatchingRule.getInstance(), baos.toByteArray()); } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_CANNOT_SERIALIZE.get(name, StaticUtils.getExceptionMessage(e)), e); } } throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( String.valueOf(type))); } /** * Encodes the contents of the provided array object. * * @param arrayType The component type of the array. * @param arrayObject The array object to process. * @param attributeName The name to use for the attribute to create. * * @return The attribute containing the encoded array contents. * * @throws LDAPPersistException If a problem occurs while trying to create * the attribute. */ @NotNull() private static Attribute encodeArray(@NotNull final Class arrayType, @NotNull final Object arrayObject, @NotNull final String attributeName) throws LDAPPersistException { final ASN1OctetString[] values = new ASN1OctetString[Array.getLength(arrayObject)]; final AtomicReference matchingRule = new AtomicReference<>(); for (int i=0; i < values.length; i++) { final Object o = Array.get(arrayObject, i); if (arrayType.equals(AtomicInteger.class) || arrayType.equals(AtomicLong.class) || arrayType.equals(BigDecimal.class) || arrayType.equals(BigInteger.class) || arrayType.equals(Double.class) || arrayType.equals(Double.TYPE) || arrayType.equals(Float.class) || arrayType.equals(Float.TYPE) || arrayType.equals(Integer.class) || arrayType.equals(Integer.TYPE) || arrayType.equals(Long.class) || arrayType.equals(Long.TYPE) || arrayType.equals(Short.class) || arrayType.equals(Short.TYPE) || arrayType.equals(String.class) || arrayType.equals(StringBuffer.class) || arrayType.equals(StringBuilder.class) || arrayType.equals(UUID.class) || arrayType.equals(DN.class) || arrayType.equals(Filter.class) || arrayType.equals(LDAPURL.class) || arrayType.equals(RDN.class)) { if (matchingRule.get() == null) { final String syntaxOID = getSyntaxOID(arrayType); matchingRule.set(MatchingRule.selectMatchingRuleForSyntax(syntaxOID)); } values[i] = new ASN1OctetString(String.valueOf(o)); } else if (arrayType.equals(URI.class)) { final URI uri = (URI) o; values[i] = new ASN1OctetString(uri.toASCIIString()); } else if (arrayType.equals(URL.class)) { final URL url = (URL) o; values[i] = new ASN1OctetString(url.toExternalForm()); } else if (o instanceof byte[]) { matchingRule.compareAndSet(null, OctetStringMatchingRule.getInstance()); values[i] = new ASN1OctetString((byte[]) o); } else if (o instanceof char[]) { values[i] = new ASN1OctetString(new String((char[]) o)); } else if (arrayType.equals(Boolean.class) || arrayType.equals(Boolean.TYPE)) { matchingRule.compareAndSet(null, BooleanMatchingRule.getInstance()); final Boolean b = (Boolean) o; if (b) { values[i] = new ASN1OctetString("TRUE"); } else { values[i] = new ASN1OctetString("FALSE"); } } else if (arrayType.equals(Date.class)) { matchingRule.compareAndSet(null, GeneralizedTimeMatchingRule.getInstance()); final Date d = (Date) o; values[i] = new ASN1OctetString(StaticUtils.encodeGeneralizedTime(d)); } else if (arrayType.isEnum()) { final Enum e = (Enum) o; values[i] = new ASN1OctetString(e.name()); } else if (Serializable.class.isAssignableFrom(arrayType)) { matchingRule.compareAndSet(null, OctetStringMatchingRule.getInstance()); try { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); values[i] = new ASN1OctetString(baos.toByteArray()); } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_CANNOT_SERIALIZE.get(attributeName, StaticUtils.getExceptionMessage(e)), e); } } else { throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( arrayType.getName())); } } matchingRule.compareAndSet(null, CaseIgnoreStringMatchingRule.getInstance()); return new Attribute(attributeName, matchingRule.get(), values); } /** * Encodes the contents of the provided collection. * * @param genericType The generic type of the collection. * @param collection The collection to process. * @param attributeName The name to use for the attribute to create. * * @return The attribute containing the encoded collection contents. * * @throws LDAPPersistException If a problem occurs while trying to create * the attribute. */ @NotNull() private static Attribute encodeCollection(@NotNull final Class genericType, @NotNull final Collection collection, @NotNull final String attributeName) throws LDAPPersistException { final ASN1OctetString[] values = new ASN1OctetString[collection.size()]; final AtomicReference matchingRule = new AtomicReference<>(); int i=0; for (final Object o : collection) { if (genericType.equals(AtomicInteger.class) || genericType.equals(AtomicLong.class) || genericType.equals(BigDecimal.class) || genericType.equals(BigInteger.class) || genericType.equals(Double.class) || genericType.equals(Double.TYPE) || genericType.equals(Float.class) || genericType.equals(Float.TYPE) || genericType.equals(Integer.class) || genericType.equals(Integer.TYPE) || genericType.equals(Long.class) || genericType.equals(Long.TYPE) || genericType.equals(Short.class) || genericType.equals(Short.TYPE) || genericType.equals(String.class) || genericType.equals(StringBuffer.class) || genericType.equals(StringBuilder.class) || genericType.equals(UUID.class) || genericType.equals(DN.class) || genericType.equals(Filter.class) || genericType.equals(LDAPURL.class) || genericType.equals(RDN.class)) { if (matchingRule.get() == null) { final String syntaxOID = getSyntaxOID(genericType); matchingRule.set(MatchingRule.selectMatchingRuleForSyntax(syntaxOID)); } values[i] = new ASN1OctetString(String.valueOf(o)); } else if (genericType.equals(URI.class)) { final URI uri = (URI) o; values[i] = new ASN1OctetString(uri.toASCIIString()); } else if (genericType.equals(URL.class)) { final URL url = (URL) o; values[i] = new ASN1OctetString(url.toExternalForm()); } else if (o instanceof byte[]) { matchingRule.compareAndSet(null, OctetStringMatchingRule.getInstance()); values[i] = new ASN1OctetString((byte[]) o); } else if (o instanceof char[]) { values[i] = new ASN1OctetString(new String((char[]) o)); } else if (genericType.equals(Boolean.class) || genericType.equals(Boolean.TYPE)) { matchingRule.compareAndSet(null, BooleanMatchingRule.getInstance()); final Boolean b = (Boolean) o; if (b) { values[i] = new ASN1OctetString("TRUE"); } else { values[i] = new ASN1OctetString("FALSE"); } } else if (genericType.equals(Date.class)) { matchingRule.compareAndSet(null, GeneralizedTimeMatchingRule.getInstance()); final Date d = (Date) o; values[i] = new ASN1OctetString(StaticUtils.encodeGeneralizedTime(d)); } else if (genericType.isEnum()) { final Enum e = (Enum) o; values[i] = new ASN1OctetString(e.name()); } else if (Serializable.class.isAssignableFrom(genericType)) { matchingRule.compareAndSet(null, OctetStringMatchingRule.getInstance()); try { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); values[i] = new ASN1OctetString(baos.toByteArray()); } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_CANNOT_SERIALIZE.get(attributeName, StaticUtils.getExceptionMessage(e)), e); } } else { throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( genericType.getName())); } i++; } matchingRule.compareAndSet(null, CaseIgnoreStringMatchingRule.getInstance()); return new Attribute(attributeName, matchingRule.get(), values); } /** * {@inheritDoc} */ @Override() public void decodeField(@NotNull final Field field, @NotNull final Object object, @NotNull final Attribute attribute) throws LDAPPersistException { field.setAccessible(true); final TypeInfo typeInfo = new TypeInfo(field.getGenericType()); try { final Class baseClass = typeInfo.getBaseClass(); final Object newValue = getValue(baseClass, attribute, 0); if (newValue != null) { field.set(object, newValue); return; } if (typeInfo.isArray()) { final Class componentType = typeInfo.getComponentType(); final ASN1OctetString[] values = attribute.getRawValues(); final Object arrayObject = Array.newInstance(componentType, values.length); for (int i=0; i < values.length; i++) { final Object o = getValue(componentType, attribute, i); if (o == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( componentType.getName())); } Array.set(arrayObject, i, o); } field.set(object, arrayObject); return; } else if (typeInfo.isList() && isSupportedListType(baseClass)) { final Class componentType = typeInfo.getComponentType(); if (componentType == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName())); } final ASN1OctetString[] values = attribute.getRawValues(); final List l = createList(baseClass, values.length); for (int i=0; i < values.length; i++) { final Object o = getValue(componentType, attribute, i); if (o == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( componentType.getName())); } invokeAdd(l, o); } field.set(object, l); return; } else if (typeInfo.isSet() && isSupportedSetType(baseClass)) { final Class componentType = typeInfo.getComponentType(); if (componentType == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName())); } final ASN1OctetString[] values = attribute.getRawValues(); final Set l = createSet(baseClass, values.length); for (int i=0; i < values.length; i++) { final Object o = getValue(componentType, attribute, i); if (o == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( componentType.getName())); } invokeAdd(l, o); } field.set(object, l); return; } throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( baseClass.getName())); } catch (final LDAPPersistException lpe) { Debug.debugException(lpe); throw lpe; } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException(StaticUtils.getExceptionMessage(e), e); } } /** * {@inheritDoc} */ @Override() public void invokeSetter(@NotNull final Method method, @NotNull final Object object, @NotNull final Attribute attribute) throws LDAPPersistException { final TypeInfo typeInfo = new TypeInfo(method.getGenericParameterTypes()[0]); final Class baseClass = typeInfo.getBaseClass(); method.setAccessible(true); try { final Object newValue = getValue(baseClass, attribute, 0); if (newValue != null) { method.invoke(object, newValue); return; } if (typeInfo.isArray()) { final Class componentType = typeInfo.getComponentType(); final ASN1OctetString[] values = attribute.getRawValues(); final Object arrayObject = Array.newInstance(componentType, values.length); for (int i=0; i < values.length; i++) { final Object o = getValue(componentType, attribute, i); if (o == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( componentType.getName())); } Array.set(arrayObject, i, o); } method.invoke(object, arrayObject); return; } else if (typeInfo.isList() && isSupportedListType(baseClass)) { final Class componentType = typeInfo.getComponentType(); if (componentType == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName())); } final ASN1OctetString[] values = attribute.getRawValues(); final List l = createList(baseClass, values.length); for (int i=0; i < values.length; i++) { final Object o = getValue(componentType, attribute, i); if (o == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( componentType.getName())); } invokeAdd(l, o); } method.invoke(object, l); return; } else if (typeInfo.isSet() && isSupportedSetType(baseClass)) { final Class componentType = typeInfo.getComponentType(); if (componentType == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get(baseClass.getName())); } final ASN1OctetString[] values = attribute.getRawValues(); final Set s = createSet(baseClass, values.length); for (int i=0; i < values.length; i++) { final Object o = getValue(componentType, attribute, i); if (o == null) { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( componentType.getName())); } invokeAdd(s, o); } method.invoke(object, s); return; } throw new LDAPPersistException(ERR_DEFAULT_ENCODER_UNSUPPORTED_TYPE.get( baseClass.getName())); } catch (final LDAPPersistException lpe) { Debug.debugException(lpe); throw lpe; } catch (final Exception e) { Debug.debugException(e); if (e instanceof InvocationTargetException) { final Throwable targetException = ((InvocationTargetException) e).getTargetException(); throw new LDAPPersistException( StaticUtils.getExceptionMessage(targetException), targetException); } else { throw new LDAPPersistException(StaticUtils.getExceptionMessage(e), e); } } } /** * Creates an object of the specified type from the given attribute value. * * @param t The type of object to create. * @param a The attribute to use to create the object. * @param p The position in the set of values for the object to create. * * @return The created object, or {@code null} if the provided type is not * supported. * * @throws LDAPPersistException If a problem occurs while creating the * object. */ @SuppressWarnings("unchecked") @Nullable() private static Object getValue(@NotNull final Class t, @NotNull final Attribute a, final int p) throws LDAPPersistException { final ASN1OctetString v = a.getRawValues()[p]; if (t.equals(AtomicInteger.class)) { return new AtomicInteger(Integer.valueOf(v.stringValue())); } else if (t.equals(AtomicLong.class)) { return new AtomicLong(Long.valueOf(v.stringValue())); } else if (t.equals(BigDecimal.class)) { return new BigDecimal(v.stringValue()); } else if (t.equals(BigInteger.class)) { return new BigInteger(v.stringValue()); } else if (t.equals(Double.class) || t.equals(Double.TYPE)) { return Double.valueOf(v.stringValue()); } else if (t.equals(Float.class) || t.equals(Float.TYPE)) { return Float.valueOf(v.stringValue()); } else if (t.equals(Integer.class) || t.equals(Integer.TYPE)) { return Integer.valueOf(v.stringValue()); } else if (t.equals(Long.class) || t.equals(Long.TYPE)) { return Long.valueOf(v.stringValue()); } else if (t.equals(Short.class) || t.equals(Short.TYPE)) { return Short.valueOf(v.stringValue()); } else if (t.equals(String.class)) { return String.valueOf(v.stringValue()); } else if (t.equals(StringBuffer.class)) { return new StringBuffer(v.stringValue()); } else if (t.equals(StringBuilder.class)) { return new StringBuilder(v.stringValue()); } else if (t.equals(URI.class)) { try { return new URI(v.stringValue()); } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_VALUE_INVALID_URI.get(v.stringValue(), StaticUtils.getExceptionMessage(e)), e); } } else if (t.equals(URL.class)) { try { return new URL(v.stringValue()); } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_VALUE_INVALID_URL.get(v.stringValue(), StaticUtils.getExceptionMessage(e)), e); } } else if (t.equals(UUID.class)) { try { return UUID.fromString(v.stringValue()); } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_VALUE_INVALID_UUID.get(v.stringValue(), StaticUtils.getExceptionMessage(e)), e); } } else if (t.equals(DN.class)) { try { return new DN(v.stringValue()); } catch (final LDAPException le) { Debug.debugException(le); throw new LDAPPersistException(le.getMessage(), le); } } else if (t.equals(Filter.class)) { try { return Filter.create(v.stringValue()); } catch (final LDAPException le) { Debug.debugException(le); throw new LDAPPersistException(le.getMessage(), le); } } else if (t.equals(LDAPURL.class)) { try { return new LDAPURL(v.stringValue()); } catch (final LDAPException le) { Debug.debugException(le); throw new LDAPPersistException(le.getMessage(), le); } } else if (t.equals(RDN.class)) { try { return new RDN(v.stringValue()); } catch (final LDAPException le) { Debug.debugException(le); throw new LDAPPersistException(le.getMessage(), le); } } else if (t.equals(Boolean.class) || t.equals(Boolean.TYPE)) { final String s = v.stringValue(); if (s.equalsIgnoreCase("TRUE")) { return Boolean.TRUE; } else if (s.equalsIgnoreCase("FALSE")) { return Boolean.FALSE; } else { throw new LDAPPersistException( ERR_DEFAULT_ENCODER_VALUE_INVALID_BOOLEAN.get(s)); } } else if (t.equals(Date.class)) { try { return StaticUtils.decodeGeneralizedTime(v.stringValue()); } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_VALUE_INVALID_DATE.get(v.stringValue(), e.getMessage()), e); } } else if (t.isArray()) { final Class componentType = t.getComponentType(); if (componentType.equals(Byte.TYPE)) { return v.getValue(); } else if (componentType.equals(Character.TYPE)) { return v.stringValue().toCharArray(); } } else if (t.isEnum()) { try { @SuppressWarnings("rawtypes") final Class enumClass = (Class) t; return Enum.valueOf(enumClass, v.stringValue()); } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_VALUE_INVALID_ENUM.get(v.stringValue(), StaticUtils.getExceptionMessage(e)), e); } } else if (Serializable.class.isAssignableFrom(t)) { // We shouldn't attempt to work on arrays/collections themselves. Return // null and then we'll work on each element. if (t.isArray() || Collection.class.isAssignableFrom(t)) { return null; } try { final ByteArrayInputStream bais = new ByteArrayInputStream(v.getValue()); final ObjectInputStream ois = new ObjectInputStream(bais); final Object o = ois.readObject(); ois.close(); return o; } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_CANNOT_DESERIALIZE.get(a.getName(), StaticUtils.getExceptionMessage(e)), e); } } return null; } /** * Invokes the {@code add} method on the provided {@code List} or {@code Set} * object. * * @param l The list or set on which to invoke the {@code add} method. * @param o The object to add to the {@code List} or {@code Set} object. * * @throws LDAPPersistException If a problem occurs while attempting to * invoke the {@code add} method. */ private static void invokeAdd(@NotNull final Object l, @NotNull final Object o) throws LDAPPersistException { final Class c = l.getClass(); for (final Method m : c.getMethods()) { if (m.getName().equals("add") && (m.getGenericParameterTypes().length == 1)) { try { m.invoke(l, o); return; } catch (final Exception e) { Debug.debugException(e); throw new LDAPPersistException( ERR_DEFAULT_ENCODER_CANNOT_ADD.get( StaticUtils.getExceptionMessage(e)), e); } } } throw new LDAPPersistException( ERR_DEFAULT_ENCODER_CANNOT_FIND_ADD_METHOD.get()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy