package com.hwlcn.ldap.ldap.sdk.persist;
import com.hwlcn.core.annotation.NotMutable;
import com.hwlcn.core.annotation.ThreadSafety;
import com.hwlcn.ldap.asn1.ASN1OctetString;
import com.hwlcn.ldap.ldap.sdk.*;
import com.hwlcn.ldap.ldap.sdk.schema.ObjectClassDefinition;
import com.hwlcn.ldap.ldap.sdk.schema.ObjectClassType;
import com.hwlcn.ldap.util.ThreadSafetyLevel;
import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.hwlcn.ldap.ldap.sdk.persist.PersistMessages.*;
import static com.hwlcn.ldap.util.Debug.debugException;
import static com.hwlcn.ldap.util.StaticUtils.*;
@NotMutable()
@ThreadSafety(level = ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class LDAPObjectHandler<T>
implements Serializable {
private static final long serialVersionUID = -1480360011153517161L;
private final Attribute objectClassAttribute;
private final Class<T> type;
private final Constructor<T> constructor;
private final DN defaultParentDN;
private final Field dnField;
private final Field entryField;
private final LDAPObject ldapObject;
private final LDAPObjectHandler<? super T> superclassHandler;
private final List<FieldInfo> alwaysAllowedFilterFields;
private final List<FieldInfo> conditionallyAllowedFilterFields;
private final List<FieldInfo> requiredFilterFields;
private final List<FieldInfo> rdnFields;
private final List<GetterInfo> alwaysAllowedFilterGetters;
private final List<GetterInfo> conditionallyAllowedFilterGetters;
private final List<GetterInfo> requiredFilterGetters;
private final List<GetterInfo> rdnGetters;
private final Map<String, FieldInfo> fieldMap;
private final Map<String, GetterInfo> getterMap;
private final Map<String, SetterInfo> setterMap;
private final Method postDecodeMethod;
private final Method postEncodeMethod;
private final String structuralClass;
private final String[] attributesToRequest;
private final String[] auxiliaryClasses;
private final String[] lazilyLoadedAttributes;
private final String[] superiorClasses;
@SuppressWarnings("unchecked")
LDAPObjectHandler(final Class<T> type)
throws LDAPPersistException {
this.type = type;
final Class<? super T> superclassType = type.getSuperclass();
if (superclassType == null) {
superclassHandler = null;
} else {
final LDAPObject superclassAnnotation =
superclassType.getAnnotation(LDAPObject.class);
if (superclassAnnotation == null) {
superclassHandler = null;
} else {
superclassHandler = new LDAPObjectHandler(superclassType);
}
}
final TreeMap<String, FieldInfo> fields = new TreeMap<String, FieldInfo>();
final TreeMap<String, GetterInfo> getters = new TreeMap<String, GetterInfo>();
final TreeMap<String, SetterInfo> setters = new TreeMap<String, SetterInfo>();
ldapObject = type.getAnnotation(LDAPObject.class);
if (ldapObject == null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_OBJECT_NOT_ANNOTATED.get(type.getName()));
}
final LinkedHashMap<String, String> objectClasses =
new LinkedHashMap<String, String>(10);
final String oc = ldapObject.structuralClass();
if (oc.length() == 0) {
structuralClass = getUnqualifiedClassName(type);
} else {
structuralClass = oc;
}
final StringBuilder invalidReason = new StringBuilder();
if (PersistUtils.isValidLDAPName(structuralClass, invalidReason)) {
objectClasses.put(toLowerCase(structuralClass), structuralClass);
} else {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_INVALID_STRUCTURAL_CLASS.get(type.getName(),
structuralClass, invalidReason.toString()));
}
auxiliaryClasses = ldapObject.auxiliaryClass();
for (final String auxiliaryClass : auxiliaryClasses) {
if (PersistUtils.isValidLDAPName(auxiliaryClass, invalidReason)) {
objectClasses.put(toLowerCase(auxiliaryClass), auxiliaryClass);
} else {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_INVALID_AUXILIARY_CLASS.get(type.getName(),
auxiliaryClass, invalidReason.toString()));
}
}
superiorClasses = ldapObject.superiorClass();
for (final String superiorClass : superiorClasses) {
if (PersistUtils.isValidLDAPName(superiorClass, invalidReason)) {
objectClasses.put(toLowerCase(superiorClass), superiorClass);
} else {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_INVALID_SUPERIOR_CLASS.get(type.getName(),
superiorClass, invalidReason.toString()));
}
}
if (superclassHandler != null) {
for (final String s : superclassHandler.objectClassAttribute.getValues()) {
objectClasses.put(toLowerCase(s), s);
}
}
objectClassAttribute = new Attribute("objectClass", objectClasses.values());
final String parentDNStr = ldapObject.defaultParentDN();
try {
defaultParentDN = new DN(parentDNStr);
} catch (LDAPException le) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_INVALID_DEFAULT_PARENT.get(type.getName(),
parentDNStr, le.getMessage()), le);
}
final String postDecodeMethodName = ldapObject.postDecodeMethod();
if (postDecodeMethodName.length() > 0) {
try {
postDecodeMethod = type.getDeclaredMethod(postDecodeMethodName);
postDecodeMethod.setAccessible(true);
} catch (Exception e) {
debugException(e);
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_INVALID_POST_DECODE_METHOD.get(type.getName(),
postDecodeMethodName, getExceptionMessage(e)), e);
}
} else {
postDecodeMethod = null;
}
final String postEncodeMethodName = ldapObject.postEncodeMethod();
if (postEncodeMethodName.length() > 0) {
try {
postEncodeMethod = type.getDeclaredMethod(postEncodeMethodName,
Entry.class);
postEncodeMethod.setAccessible(true);
} catch (Exception e) {
debugException(e);
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_INVALID_POST_ENCODE_METHOD.get(type.getName(),
postEncodeMethodName, getExceptionMessage(e)), e);
}
} else {
postEncodeMethod = null;
}
try {
constructor = type.getDeclaredConstructor();
constructor.setAccessible(true);
} catch (Exception e) {
debugException(e);
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_NO_DEFAULT_CONSTRUCTOR.get(type.getName()), e);
}
Field tmpDNField = null;
Field tmpEntryField = null;
final LinkedList<FieldInfo> tmpRFilterFields = new LinkedList<FieldInfo>();
final LinkedList<FieldInfo> tmpAAFilterFields = new LinkedList<FieldInfo>();
final LinkedList<FieldInfo> tmpCAFilterFields = new LinkedList<FieldInfo>();
final LinkedList<FieldInfo> tmpRDNFields = new LinkedList<FieldInfo>();
for (final Field f : type.getDeclaredFields()) {
final LDAPField fieldAnnotation = f.getAnnotation(LDAPField.class);
final LDAPDNField dnFieldAnnotation = f.getAnnotation(LDAPDNField.class);
final LDAPEntryField entryFieldAnnotation =
f.getAnnotation(LDAPEntryField.class);
if (fieldAnnotation != null) {
f.setAccessible(true);
final FieldInfo fieldInfo = new FieldInfo(f, type);
final String attrName = toLowerCase(fieldInfo.getAttributeName());
if (fields.containsKey(attrName)) {
throw new LDAPPersistException(ERR_OBJECT_HANDLER_ATTR_CONFLICT.get(
type.getName(), fieldInfo.getAttributeName()));
} else {
fields.put(attrName, fieldInfo);
}
switch (fieldInfo.getFilterUsage()) {
case REQUIRED:
tmpRFilterFields.add(fieldInfo);
break;
case ALWAYS_ALLOWED:
tmpAAFilterFields.add(fieldInfo);
break;
case CONDITIONALLY_ALLOWED:
tmpCAFilterFields.add(fieldInfo);
break;
case EXCLUDED:
default:
break;
}
if (fieldInfo.includeInRDN()) {
tmpRDNFields.add(fieldInfo);
}
}
if (dnFieldAnnotation != null) {
f.setAccessible(true);
if (fieldAnnotation != null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_CONFLICTING_FIELD_ANNOTATIONS.get(
type.getName(), "LDAPField", "LDAPDNField", f.getName()));
}
if (tmpDNField != null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_MULTIPLE_DN_FIELDS.get(type.getName()));
}
final int modifiers = f.getModifiers();
if (Modifier.isFinal(modifiers)) {
throw new LDAPPersistException(ERR_OBJECT_HANDLER_DN_FIELD_FINAL.get(
f.getName(), type.getName()));
} else if (Modifier.isStatic(modifiers)) {
throw new LDAPPersistException(ERR_OBJECT_HANDLER_DN_FIELD_STATIC.get(
f.getName(), type.getName()));
}
final Class<?> fieldType = f.getType();
if (fieldType.equals(String.class)) {
tmpDNField = f;
} else {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_INVALID_DN_FIELD_TYPE.get(type.getName(),
f.getName(), fieldType.getName()));
}
}
if (entryFieldAnnotation != null) {
f.setAccessible(true);
if (fieldAnnotation != null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_CONFLICTING_FIELD_ANNOTATIONS.get(
type.getName(), "LDAPField", "LDAPEntryField",
f.getName()));
}
if (tmpEntryField != null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_MULTIPLE_ENTRY_FIELDS.get(type.getName()));
}
final int modifiers = f.getModifiers();
if (Modifier.isFinal(modifiers)) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_ENTRY_FIELD_FINAL.get(f.getName(),
type.getName()));
} else if (Modifier.isStatic(modifiers)) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_ENTRY_FIELD_STATIC.get(f.getName(),
type.getName()));
}
final Class<?> fieldType = f.getType();
if (fieldType.equals(ReadOnlyEntry.class)) {
tmpEntryField = f;
} else {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_INVALID_ENTRY_FIELD_TYPE.get(type.getName(),
f.getName(), fieldType.getName()));
}
}
}
dnField = tmpDNField;
entryField = tmpEntryField;
requiredFilterFields = Collections.unmodifiableList(tmpRFilterFields);
alwaysAllowedFilterFields = Collections.unmodifiableList(tmpAAFilterFields);
conditionallyAllowedFilterFields =
Collections.unmodifiableList(tmpCAFilterFields);
rdnFields = Collections.unmodifiableList(tmpRDNFields);
final LinkedList<GetterInfo> tmpRFilterGetters =
new LinkedList<GetterInfo>();
final LinkedList<GetterInfo> tmpAAFilterGetters =
new LinkedList<GetterInfo>();
final LinkedList<GetterInfo> tmpCAFilterGetters =
new LinkedList<GetterInfo>();
final LinkedList<GetterInfo> tmpRDNGetters = new LinkedList<GetterInfo>();
for (final Method m : type.getDeclaredMethods()) {
final LDAPGetter getter = m.getAnnotation(LDAPGetter.class);
final LDAPSetter setter = m.getAnnotation(LDAPSetter.class);
if (getter != null) {
m.setAccessible(true);
if (setter != null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_CONFLICTING_METHOD_ANNOTATIONS.get(
type.getName(), "LDAPGetter", "LDAPSetter",
m.getName()));
}
final GetterInfo methodInfo = new GetterInfo(m, type);
final String attrName = toLowerCase(methodInfo.getAttributeName());
if (fields.containsKey(attrName) || getters.containsKey(attrName)) {
throw new LDAPPersistException(ERR_OBJECT_HANDLER_ATTR_CONFLICT.get(
type.getName(), methodInfo.getAttributeName()));
} else {
getters.put(attrName, methodInfo);
}
switch (methodInfo.getFilterUsage()) {
case REQUIRED:
tmpRFilterGetters.add(methodInfo);
break;
case ALWAYS_ALLOWED:
tmpAAFilterGetters.add(methodInfo);
break;
case CONDITIONALLY_ALLOWED:
tmpCAFilterGetters.add(methodInfo);
break;
case EXCLUDED:
default:
// No action required.
break;
}
if (methodInfo.includeInRDN()) {
tmpRDNGetters.add(methodInfo);
}
}
if (setter != null) {
m.setAccessible(true);
final SetterInfo methodInfo = new SetterInfo(m, type);
final String attrName = toLowerCase(methodInfo.getAttributeName());
if (fields.containsKey(attrName) || setters.containsKey(attrName)) {
throw new LDAPPersistException(ERR_OBJECT_HANDLER_ATTR_CONFLICT.get(
type.getName(), methodInfo.getAttributeName()));
} else {
setters.put(attrName, methodInfo);
}
}
}
requiredFilterGetters = Collections.unmodifiableList(tmpRFilterGetters);
alwaysAllowedFilterGetters =
Collections.unmodifiableList(tmpAAFilterGetters);
conditionallyAllowedFilterGetters =
Collections.unmodifiableList(tmpCAFilterGetters);
rdnGetters = Collections.unmodifiableList(tmpRDNGetters);
if (rdnFields.isEmpty() && rdnGetters.isEmpty()) {
throw new LDAPPersistException(ERR_OBJECT_HANDLER_NO_RDN_DEFINED.get(
type.getName()));
}
fieldMap = Collections.unmodifiableMap(fields);
getterMap = Collections.unmodifiableMap(getters);
setterMap = Collections.unmodifiableMap(setters);
final TreeSet<String> attrSet = new TreeSet<String>();
final TreeSet<String> lazySet = new TreeSet<String>();
if (ldapObject.requestAllAttributes()) {
attrSet.add("*");
attrSet.add("+");
} else {
for (final FieldInfo i : fields.values()) {
if (i.lazilyLoad()) {
lazySet.add(i.getAttributeName());
} else {
attrSet.add(i.getAttributeName());
}
}
for (final SetterInfo i : setters.values()) {
attrSet.add(i.getAttributeName());
}
}
attributesToRequest = new String[attrSet.size()];
attrSet.toArray(attributesToRequest);
lazilyLoadedAttributes = new String[lazySet.size()];
lazySet.toArray(lazilyLoadedAttributes);
}
private static <T> LDAPObjectHandler<T> getHandler(final Class<T> type)
throws LDAPPersistException {
return new LDAPObjectHandler<T>(type);
}
public Class<T> getType() {
return type;
}
public LDAPObjectHandler<?> getSuperclassHandler() {
return superclassHandler;
}
public LDAPObject getLDAPObjectAnnotation() {
return ldapObject;
}
public Constructor<T> getConstructor() {
return constructor;
}
public Field getDNField() {
return dnField;
}
public Field getEntryField() {
return entryField;
}
public DN getDefaultParentDN() {
return defaultParentDN;
}
public String getStructuralClass() {
return structuralClass;
}
public String[] getAuxiliaryClasses() {
return auxiliaryClasses;
}
public String[] getSuperiorClasses() {
return superiorClasses;
}
public String[] getAttributesToRequest() {
return attributesToRequest;
}
public String[] getLazilyLoadedAttributes() {
return lazilyLoadedAttributes;
}
public String getEntryDN(final T o)
throws LDAPPersistException {
if (dnField != null) {
try {
final Object dnObject = dnField.get(o);
if (dnObject != null) {
return String.valueOf(dnObject);
}
} catch (Exception e) {
debugException(e);
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_ERROR_ACCESSING_DN_FIELD.get(dnField.getName(),
type.getName(), getExceptionMessage(e)), e);
}
}
final ReadOnlyEntry entry = getEntry(o);
if (entry != null) {
return entry.getDN();
}
return null;
}
public ReadOnlyEntry getEntry(final T o)
throws LDAPPersistException {
if (entryField != null) {
try {
final Object entryObject = entryField.get(o);
if (entryObject != null) {
return (ReadOnlyEntry) entryObject;
}
} catch (Exception e) {
debugException(e);
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_ERROR_ACCESSING_ENTRY_FIELD.get(
entryField.getName(), type.getName(), getExceptionMessage(e)),
e);
}
}
return null;
}
public Map<String, FieldInfo> getFields() {
return fieldMap;
}
public Map<String, GetterInfo> getGetters() {
return getterMap;
}
public Map<String, SetterInfo> getSetters() {
return setterMap;
}
List<ObjectClassDefinition> constructObjectClasses(final OIDAllocator a)
throws LDAPPersistException {
final LinkedHashMap<String, ObjectClassDefinition> ocMap =
new LinkedHashMap<String, ObjectClassDefinition>(
1 + auxiliaryClasses.length);
if (superclassHandler != null) {
for (final ObjectClassDefinition d :
superclassHandler.constructObjectClasses(a)) {
ocMap.put(toLowerCase(d.getNameOrOID()), d);
}
}
final String lowerStructuralClass = toLowerCase(structuralClass);
if (!ocMap.containsKey(lowerStructuralClass)) {
if (superclassHandler == null) {
ocMap.put(lowerStructuralClass, constructObjectClass(structuralClass,
"top", ObjectClassType.STRUCTURAL, a));
} else {
ocMap.put(lowerStructuralClass, constructObjectClass(structuralClass,
superclassHandler.getStructuralClass(), ObjectClassType.STRUCTURAL,
a));
}
}
for (final String s : auxiliaryClasses) {
final String lowerName = toLowerCase(s);
if (!ocMap.containsKey(lowerName)) {
ocMap.put(lowerName,
constructObjectClass(s, "top", ObjectClassType.AUXILIARY, a));
}
}
return Collections.unmodifiableList(new ArrayList<ObjectClassDefinition>(
ocMap.values()));
}
ObjectClassDefinition constructObjectClass(final String name,
final String sup,
final ObjectClassType type,
final OIDAllocator a) {
final TreeMap<String, String> requiredAttrs = new TreeMap<String, String>();
final TreeMap<String, String> optionalAttrs = new TreeMap<String, String>();
for (final FieldInfo i : fieldMap.values()) {
boolean found = false;
for (final String s : i.getObjectClasses()) {
if (name.equalsIgnoreCase(s)) {
found = true;
break;
}
}
if (!found) {
continue;
}
final String attrName = i.getAttributeName();
final String lowerName = toLowerCase(attrName);
if (i.includeInRDN() ||
(i.isRequiredForDecode() && i.isRequiredForEncode())) {
requiredAttrs.put(lowerName, attrName);
} else {
optionalAttrs.put(lowerName, attrName);
}
}
for (final GetterInfo i : getterMap.values()) {
boolean found = false;
for (final String s : i.getObjectClasses()) {
if (name.equalsIgnoreCase(s)) {
found = true;
break;
}
}
if (!found) {
continue;
}
final String attrName = i.getAttributeName();
final String lowerName = toLowerCase(attrName);
if (i.includeInRDN()) {
requiredAttrs.put(lowerName, attrName);
} else {
optionalAttrs.put(lowerName, attrName);
}
}
if (name.equalsIgnoreCase(structuralClass)) {
for (final SetterInfo i : setterMap.values()) {
final String attrName = i.getAttributeName();
final String lowerName = toLowerCase(attrName);
if (requiredAttrs.containsKey(lowerName) ||
optionalAttrs.containsKey(lowerName)) {
continue;
}
optionalAttrs.put(lowerName, attrName);
}
}
final String[] reqArray = new String[requiredAttrs.size()];
requiredAttrs.values().toArray(reqArray);
final String[] optArray = new String[optionalAttrs.size()];
optionalAttrs.values().toArray(optArray);
return new ObjectClassDefinition(a.allocateObjectClassOID(name),
new String[]{name}, null, false, new String[]{sup}, type,
reqArray, optArray, null);
}
T decode(final Entry e)
throws LDAPPersistException {
final T o;
try {
o = constructor.newInstance();
} catch (Throwable t) {
debugException(t);
if (t instanceof InvocationTargetException) {
t = ((InvocationTargetException) t).getTargetException();
}
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_ERROR_INVOKING_CONSTRUCTOR.get(type.getName(),
getExceptionMessage(t)), t);
}
decode(o, e);
return o;
}
void decode(final T o, final Entry e)
throws LDAPPersistException {
if (superclassHandler != null) {
superclassHandler.decode(o, e);
}
setDNAndEntryFields(o, e);
final ArrayList<String> failureReasons = new ArrayList<String>(5);
boolean successful = true;
for (final FieldInfo i : fieldMap.values()) {
successful &= i.decode(o, e, failureReasons);
}
for (final SetterInfo i : setterMap.values()) {
successful &= i.invokeSetter(o, e, failureReasons);
}
Throwable cause = null;
if (postDecodeMethod != null) {
try {
postDecodeMethod.invoke(o);
} catch (final Throwable t) {
debugException(t);
if (t instanceof InvocationTargetException) {
cause = ((InvocationTargetException) t).getTargetException();
} else {
cause = t;
}
successful = false;
failureReasons.add(
ERR_OBJECT_HANDLER_ERROR_INVOKING_POST_DECODE_METHOD.get(
postDecodeMethod.getName(), type.getName(),
getExceptionMessage(t)));
}
}
if (!successful) {
throw new LDAPPersistException(concatenateStrings(failureReasons), o,
cause);
}
}
Entry encode(final T o, final String parentDN)
throws LDAPPersistException {
// Get the attributes that should be included in the entry.
final LinkedHashMap<String, Attribute> attrMap =
new LinkedHashMap<String, Attribute>();
attrMap.put("objectClass", objectClassAttribute);
for (final Map.Entry<String, FieldInfo> e : fieldMap.entrySet()) {
final FieldInfo i = e.getValue();
if (!i.includeInAdd()) {
continue;
}
final Attribute a = i.encode(o, false);
if (a != null) {
attrMap.put(e.getKey(), a);
}
}
for (final Map.Entry<String, GetterInfo> e : getterMap.entrySet()) {
final GetterInfo i = e.getValue();
if (!i.includeInAdd()) {
continue;
}
final Attribute a = i.encode(o);
if (a != null) {
attrMap.put(e.getKey(), a);
}
}
final String dn = constructDN(o, parentDN, attrMap);
final Entry entry = new Entry(dn, attrMap.values());
if (postEncodeMethod != null) {
try {
postEncodeMethod.invoke(o, entry);
} catch (Throwable t) {
debugException(t);
if (t instanceof InvocationTargetException) {
t = ((InvocationTargetException) t).getTargetException();
}
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_ERROR_INVOKING_POST_ENCODE_METHOD.get(
postEncodeMethod.getName(), type.getName(),
getExceptionMessage(t)), t);
}
}
setDNAndEntryFields(o, entry);
if (superclassHandler != null) {
final Entry e = superclassHandler.encode(o, parentDN);
for (final Attribute a : e.getAttributes()) {
entry.addAttribute(a);
}
}
return entry;
}
private void setDNAndEntryFields(final T o, final Entry e)
throws LDAPPersistException {
if (dnField != null) {
try {
dnField.set(o, e.getDN());
} catch (Exception ex) {
debugException(ex);
throw new LDAPPersistException(ERR_OBJECT_HANDLER_ERROR_SETTING_DN.get(
type.getName(), e.getDN(), dnField.getName(),
getExceptionMessage(ex)), ex);
}
}
if (entryField != null) {
try {
entryField.set(o, new ReadOnlyEntry(e));
} catch (Exception ex) {
debugException(ex);
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_ERROR_SETTING_ENTRY.get(type.getName(),
entryField.getName(), getExceptionMessage(ex)), ex);
}
}
}
public String constructDN(final T o, final String parentDN)
throws LDAPPersistException {
final String existingDN = getEntryDN(o);
if (existingDN != null) {
return existingDN;
}
final LinkedHashMap<String, Attribute> attrMap =
new LinkedHashMap<String, Attribute>(1);
for (final FieldInfo i : rdnFields) {
final Attribute a = i.encode(o, true);
if (a == null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_RDN_FIELD_MISSING_VALUE.get(type.getName(),
i.getField().getName()));
}
attrMap.put(toLowerCase(i.getAttributeName()), a);
}
for (final GetterInfo i : rdnGetters) {
final Attribute a = i.encode(o);
if (a == null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_RDN_GETTER_MISSING_VALUE.get(type.getName(),
i.getMethod().getName()));
}
attrMap.put(toLowerCase(i.getAttributeName()), a);
}
return constructDN(o, parentDN, attrMap);
}
String constructDN(final T o, final String parentDN,
final Map<String, Attribute> attrMap)
throws LDAPPersistException {
final String existingDN = getEntryDN(o);
if (existingDN != null) {
return existingDN;
}
final ArrayList<String> rdnNameList = new ArrayList<String>(1);
final ArrayList<byte[]> rdnValueList = new ArrayList<byte[]>(1);
for (final FieldInfo i : rdnFields) {
final Attribute a = attrMap.get(toLowerCase(i.getAttributeName()));
if (a == null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_RDN_FIELD_MISSING_VALUE.get(type.getName(),
i.getField().getName()));
}
rdnNameList.add(a.getName());
rdnValueList.add(a.getValueByteArray());
}
for (final GetterInfo i : rdnGetters) {
final Attribute a = attrMap.get(toLowerCase(i.getAttributeName()));
if (a == null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_RDN_GETTER_MISSING_VALUE.get(type.getName(),
i.getMethod().getName()));
}
rdnNameList.add(a.getName());
rdnValueList.add(a.getValueByteArray());
}
final String[] rdnNames = new String[rdnNameList.size()];
rdnNameList.toArray(rdnNames);
final byte[][] rdnValues = new byte[rdnNames.length][];
rdnValueList.toArray(rdnValues);
final RDN rdn = new RDN(rdnNames, rdnValues);
if (parentDN == null) {
return new DN(rdn, defaultParentDN).toString();
} else {
try {
final DN parsedParentDN = new DN(parentDN);
return new DN(rdn, parsedParentDN).toString();
} catch (LDAPException le) {
debugException(le);
throw new LDAPPersistException(ERR_OBJECT_HANDLER_INVALID_PARENT_DN.get(
type.getName(), parentDN, le.getMessage()), le);
}
}
}
List<Modification> getModifications(final T o, final boolean deleteNullValues,
final String... attributes)
throws LDAPPersistException {
final ReadOnlyEntry originalEntry;
if (entryField != null) {
originalEntry = getEntry(o);
} else {
originalEntry = null;
}
if (originalEntry != null) {
try {
final T decodedOrig = decode(originalEntry);
final Entry reEncodedOriginal =
encode(decodedOrig, originalEntry.getParentDNString());
final Entry newEntry = encode(o, originalEntry.getParentDNString());
final List<Modification> mods = Entry.diff(reEncodedOriginal, newEntry,
true, false, attributes);
if (!deleteNullValues) {
final Iterator<Modification> iterator = mods.iterator();
while (iterator.hasNext()) {
final Modification m = iterator.next();
if (m.getRawValues().length == 0) {
iterator.remove();
}
}
}
HashSet<String> stripAttrs = null;
for (final FieldInfo i : fieldMap.values()) {
if (!i.includeInModify()) {
if (stripAttrs == null) {
stripAttrs = new HashSet<String>(10);
}
stripAttrs.add(toLowerCase(i.getAttributeName()));
}
}
for (final GetterInfo i : getterMap.values()) {
if (!i.includeInModify()) {
if (stripAttrs == null) {
stripAttrs = new HashSet<String>(10);
}
stripAttrs.add(toLowerCase(i.getAttributeName()));
}
}
if (stripAttrs != null) {
final Iterator<Modification> iterator = mods.iterator();
while (iterator.hasNext()) {
final Modification m = iterator.next();
if (stripAttrs.contains(toLowerCase(m.getAttributeName()))) {
iterator.remove();
}
}
}
return mods;
} catch (final Exception e) {
debugException(e);
} finally {
setDNAndEntryFields(o, originalEntry);
}
}
final HashSet<String> attrSet;
if ((attributes == null) || (attributes.length == 0)) {
attrSet = null;
} else {
attrSet = new HashSet<String>(attributes.length);
for (final String s : attributes) {
attrSet.add(toLowerCase(s));
}
}
final ArrayList<Modification> mods = new ArrayList<Modification>(5);
for (final Map.Entry<String, FieldInfo> e : fieldMap.entrySet()) {
final String attrName = toLowerCase(e.getKey());
if ((attrSet != null) && (!attrSet.contains(attrName))) {
continue;
}
final FieldInfo i = e.getValue();
if (!i.includeInModify()) {
continue;
}
final Attribute a = i.encode(o, false);
if (a == null) {
if (!deleteNullValues) {
continue;
}
if ((originalEntry != null) && (!originalEntry.hasAttribute(attrName))) {
continue;
}
mods.add(new Modification(ModificationType.REPLACE,
i.getAttributeName()));
continue;
}
if (originalEntry != null) {
final Attribute originalAttr = originalEntry.getAttribute(attrName);
if ((originalAttr != null) && originalAttr.equals(a)) {
continue;
}
}
mods.add(new Modification(ModificationType.REPLACE, i.getAttributeName(),
a.getRawValues()));
}
for (final Map.Entry<String, GetterInfo> e : getterMap.entrySet()) {
final String attrName = toLowerCase(e.getKey());
if ((attrSet != null) && (!attrSet.contains(attrName))) {
continue;
}
final GetterInfo i = e.getValue();
if (!i.includeInModify()) {
continue;
}
final Attribute a = i.encode(o);
if (a == null) {
if (!deleteNullValues) {
continue;
}
if ((originalEntry != null) && (!originalEntry.hasAttribute(attrName))) {
continue;
}
mods.add(new Modification(ModificationType.REPLACE,
i.getAttributeName()));
continue;
}
if (originalEntry != null) {
final Attribute originalAttr = originalEntry.getAttribute(attrName);
if ((originalAttr != null) && originalAttr.equals(a)) {
continue;
}
}
mods.add(new Modification(ModificationType.REPLACE, i.getAttributeName(),
a.getRawValues()));
}
if (superclassHandler != null) {
final List<Modification> superMods =
superclassHandler.getModifications(o, deleteNullValues, attributes);
final ArrayList<Modification> modsToAdd =
new ArrayList<Modification>(superMods.size());
for (final Modification sm : superMods) {
boolean add = true;
for (final Modification m : mods) {
if (m.getAttributeName().equalsIgnoreCase(sm.getAttributeName())) {
add = false;
break;
}
}
if (add) {
modsToAdd.add(sm);
}
}
mods.addAll(modsToAdd);
}
return Collections.unmodifiableList(mods);
}
public Filter createBaseFilter() {
if (auxiliaryClasses.length == 0) {
return Filter.createEqualityFilter("objectClass", structuralClass);
} else {
final ArrayList<Filter> comps =
new ArrayList<Filter>(1 + auxiliaryClasses.length);
comps.add(Filter.createEqualityFilter("objectClass", structuralClass));
for (final String s : auxiliaryClasses) {
comps.add(Filter.createEqualityFilter("objectClass", s));
}
return Filter.createANDFilter(comps);
}
}
public Filter createFilter(final T o)
throws LDAPPersistException {
final AtomicBoolean addedRequiredOrAllowed = new AtomicBoolean(false);
final Filter f = createFilter(o, addedRequiredOrAllowed);
if (!addedRequiredOrAllowed.get()) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_FILTER_MISSING_REQUIRED_OR_ALLOWED.get());
}
return f;
}
private Filter createFilter(final T o,
final AtomicBoolean addedRequiredOrAllowed)
throws LDAPPersistException {
final ArrayList<Attribute> attrs = new ArrayList<Attribute>(5);
attrs.add(objectClassAttribute);
for (final FieldInfo i : requiredFilterFields) {
final Attribute a = i.encode(o, true);
if (a == null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_FILTER_MISSING_REQUIRED_FIELD.get(
i.getField().getName()));
} else {
attrs.add(a);
addedRequiredOrAllowed.set(true);
}
}
for (final GetterInfo i : requiredFilterGetters) {
final Attribute a = i.encode(o);
if (a == null) {
throw new LDAPPersistException(
ERR_OBJECT_HANDLER_FILTER_MISSING_REQUIRED_GETTER.get(
i.getMethod().getName()));
} else {
attrs.add(a);
addedRequiredOrAllowed.set(true);
}
}
for (final FieldInfo i : alwaysAllowedFilterFields) {
final Attribute a = i.encode(o, true);
if (a != null) {
attrs.add(a);
addedRequiredOrAllowed.set(true);
}
}
for (final GetterInfo i : alwaysAllowedFilterGetters) {
final Attribute a = i.encode(o);
if (a != null) {
attrs.add(a);
addedRequiredOrAllowed.set(true);
}
}
for (final FieldInfo i : conditionallyAllowedFilterFields) {
final Attribute a = i.encode(o, true);
if (a != null) {
attrs.add(a);
}
}
for (final GetterInfo i : conditionallyAllowedFilterGetters) {
final Attribute a = i.encode(o);
if (a != null) {
attrs.add(a);
}
}
final ArrayList<Filter> comps = new ArrayList<Filter>(attrs.size());
for (final Attribute a : attrs) {
for (final ASN1OctetString v : a.getRawValues()) {
comps.add(Filter.createEqualityFilter(a.getName(), v.getValue()));
}
}
if (superclassHandler != null) {
final Filter f =
superclassHandler.createFilter(o, addedRequiredOrAllowed);
if (f.getFilterType() == Filter.FILTER_TYPE_AND) {
comps.addAll(Arrays.asList(f.getComponents()));
} else {
comps.add(f);
}
}
return Filter.createANDFilter(comps);
}
}