package com.hwlcn.ldap.ldap.sdk.persist;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.hwlcn.ldap.ldap.sdk.AddRequest;
import com.hwlcn.ldap.ldap.sdk.Attribute;
import com.hwlcn.ldap.ldap.sdk.BindResult;
import com.hwlcn.ldap.ldap.sdk.Control;
import com.hwlcn.ldap.ldap.sdk.DeleteRequest;
import com.hwlcn.ldap.ldap.sdk.DereferencePolicy;
import com.hwlcn.ldap.ldap.sdk.Entry;
import com.hwlcn.ldap.ldap.sdk.Filter;
import com.hwlcn.ldap.ldap.sdk.LDAPConnection;
import com.hwlcn.ldap.ldap.sdk.LDAPEntrySource;
import com.hwlcn.ldap.ldap.sdk.LDAPException;
import com.hwlcn.ldap.ldap.sdk.LDAPInterface;
import com.hwlcn.ldap.ldap.sdk.LDAPResult;
import com.hwlcn.ldap.ldap.sdk.Modification;
import com.hwlcn.ldap.ldap.sdk.ModificationType;
import com.hwlcn.ldap.ldap.sdk.ModifyRequest;
import com.hwlcn.ldap.ldap.sdk.ResultCode;
import com.hwlcn.ldap.ldap.sdk.SearchRequest;
import com.hwlcn.ldap.ldap.sdk.SearchResult;
import com.hwlcn.ldap.ldap.sdk.SearchScope;
import com.hwlcn.ldap.ldap.sdk.SimpleBindRequest;
import com.hwlcn.ldap.ldap.sdk.schema.AttributeTypeDefinition;
import com.hwlcn.ldap.ldap.sdk.schema.ObjectClassDefinition;
import com.hwlcn.ldap.ldap.sdk.schema.Schema;
import com.hwlcn.core.annotation.NotMutable;
import com.hwlcn.core.annotation.ThreadSafety;
import com.hwlcn.ldap.util.ThreadSafetyLevel;
import static com.hwlcn.ldap.ldap.sdk.persist.PersistMessages.*;
import static com.hwlcn.ldap.util.Debug.*;
import static com.hwlcn.ldap.util.StaticUtils.*;
import static com.hwlcn.ldap.util.Validator.*;
@NotMutable()
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class LDAPPersister<T>
implements Serializable
{
private static final long serialVersionUID = -4001743482496453961L;
private static final Control[] NO_CONTROLS = new Control[0];
private static final ConcurrentHashMap<Class<?>,LDAPPersister<?>> INSTANCES =
new ConcurrentHashMap<Class<?>,LDAPPersister<?>>();
private final LDAPObjectHandler<T> handler;
private LDAPPersister(final Class<T> type)
throws LDAPPersistException
{
handler = new LDAPObjectHandler<T>(type);
}
@SuppressWarnings("unchecked")
public static <T> LDAPPersister<T> getInstance(final Class<T> type)
throws LDAPPersistException
{
ensureNotNull(type);
LDAPPersister<T> p = (LDAPPersister<T>) INSTANCES.get(type);
if (p == null)
{
p = new LDAPPersister<T>(type);
INSTANCES.put(type, p);
}
return p;
}
public LDAPObject getLDAPObjectAnnotation()
{
return handler.getLDAPObjectAnnotation();
}
public LDAPObjectHandler<T> getObjectHandler()
{
return handler;
}
public List<AttributeTypeDefinition> constructAttributeTypes()
throws LDAPPersistException
{
return constructAttributeTypes(DefaultOIDAllocator.getInstance());
}
public List<AttributeTypeDefinition> constructAttributeTypes(
final OIDAllocator a)
throws LDAPPersistException
{
final LinkedList<AttributeTypeDefinition> attrList =
new LinkedList<AttributeTypeDefinition>();
for (final FieldInfo i : handler.getFields().values())
{
attrList.add(i.constructAttributeType(a));
}
for (final GetterInfo i : handler.getGetters().values())
{
attrList.add(i.constructAttributeType(a));
}
return Collections.unmodifiableList(attrList);
}
public List<ObjectClassDefinition> constructObjectClasses()
throws LDAPPersistException
{
return constructObjectClasses(DefaultOIDAllocator.getInstance());
}
public List<ObjectClassDefinition> constructObjectClasses(
final OIDAllocator a)
throws LDAPPersistException
{
return handler.constructObjectClasses(a);
}
public boolean updateSchema(final LDAPInterface i)
throws LDAPException
{
return updateSchema(i, DefaultOIDAllocator.getInstance());
}
public boolean updateSchema(final LDAPInterface i, final OIDAllocator a)
throws LDAPException
{
final Schema s = i.getSchema();
final List<AttributeTypeDefinition> generatedTypes =
constructAttributeTypes(a);
final List<ObjectClassDefinition> generatedClasses =
constructObjectClasses(a);
final LinkedList<String> newAttrList = new LinkedList<String>();
for (final AttributeTypeDefinition d : generatedTypes)
{
if (s.getAttributeType(d.getNameOrOID()) == null)
{
newAttrList.add(d.toString());
}
}
final LinkedList<String> newOCList = new LinkedList<String>();
for (final ObjectClassDefinition d : generatedClasses)
{
final ObjectClassDefinition existing = s.getObjectClass(d.getNameOrOID());
if (existing == null)
{
newOCList.add(d.toString());
}
else
{
final Set<AttributeTypeDefinition> existingRequired =
existing.getRequiredAttributes(s, true);
final Set<AttributeTypeDefinition> existingOptional =
existing.getOptionalAttributes(s, true);
final LinkedHashSet<String> newOptionalNames =
new LinkedHashSet<String>(0);
addMissingAttrs(d.getRequiredAttributes(), existingRequired,
existingOptional, newOptionalNames);
addMissingAttrs(d.getOptionalAttributes(), existingRequired,
existingOptional, newOptionalNames);
if (! newOptionalNames.isEmpty())
{
final LinkedHashSet<String> newOptionalSet =
new LinkedHashSet<String>();
newOptionalSet.addAll(
Arrays.asList(existing.getOptionalAttributes()));
newOptionalSet.addAll(newOptionalNames);
final String[] newOptional = new String[newOptionalSet.size()];
newOptionalSet.toArray(newOptional);
final ObjectClassDefinition newOC = new ObjectClassDefinition(
existing.getOID(), existing.getNames(),
existing.getDescription(), existing.isObsolete(),
existing.getSuperiorClasses(), existing.getObjectClassType(),
existing.getRequiredAttributes(), newOptional,
existing.getExtensions());
newOCList.add(newOC.toString());
}
}
}
final LinkedList<Modification> mods = new LinkedList<Modification>();
if (! newAttrList.isEmpty())
{
final String[] newAttrValues = new String[newAttrList.size()];
mods.add(new Modification(ModificationType.ADD,
Schema.ATTR_ATTRIBUTE_TYPE, newAttrList.toArray(newAttrValues)));
}
if (! newOCList.isEmpty())
{
final String[] newOCValues = new String[newOCList.size()];
mods.add(new Modification(ModificationType.ADD,
Schema.ATTR_OBJECT_CLASS, newOCList.toArray(newOCValues)));
}
if (mods.isEmpty())
{
return false;
}
else
{
i.modify(s.getSchemaEntry().getDN(), mods);
return true;
}
}
private static void addMissingAttrs(final String[] names,
final Set<AttributeTypeDefinition> required,
final Set<AttributeTypeDefinition> optional,
final Set<String> missing)
{
for (final String name : names)
{
boolean found = false;
for (final AttributeTypeDefinition eA : required)
{
if (eA.hasNameOrOID(name))
{
found = true;
break;
}
}
if (! found)
{
for (final AttributeTypeDefinition eA : optional)
{
if (eA.hasNameOrOID(name))
{
found = true;
break;
}
}
if (! found)
{
missing.add(name);
}
}
}
}
public Entry encode(final T o, final String parentDN)
throws LDAPPersistException
{
ensureNotNull(o);
return handler.encode(o, parentDN);
}
public T decode(final Entry entry)
throws LDAPPersistException
{
ensureNotNull(entry);
return handler.decode(entry);
}
public void decode(final T o, final Entry entry)
throws LDAPPersistException
{
ensureNotNull(o, entry);
handler.decode(o, entry);
}
public LDAPResult add(final T o, final LDAPInterface i, final String parentDN,
final Control... controls)
throws LDAPPersistException
{
ensureNotNull(o, i);
final Entry e = encode(o, parentDN);
try
{
final AddRequest addRequest = new AddRequest(e);
if (controls != null)
{
addRequest.setControls(controls);
}
return i.add(addRequest);
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
}
public LDAPResult delete(final T o, final LDAPInterface i,
final Control... controls)
throws LDAPPersistException
{
ensureNotNull(o, i);
final String dn = handler.getEntryDN(o);
if (dn == null)
{
throw new LDAPPersistException(ERR_PERSISTER_DELETE_NO_DN.get());
}
try
{
final DeleteRequest deleteRequest = new DeleteRequest(dn);
if (controls != null)
{
deleteRequest.setControls(controls);
}
return i.delete(deleteRequest);
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
}
public List<Modification> getModifications(final T o,
final boolean deleteNullValues,
final String... attributes)
throws LDAPPersistException
{
ensureNotNull(o);
return handler.getModifications(o, deleteNullValues, attributes);
}
public LDAPResult modify(final T o, final LDAPInterface i, final String dn,
final boolean deleteNullValues,
final String... attributes)
throws LDAPPersistException
{
return modify(o, i, dn, deleteNullValues, attributes, NO_CONTROLS);
}
public LDAPResult modify(final T o, final LDAPInterface i, final String dn,
final boolean deleteNullValues,
final String[] attributes, final Control... controls)
throws LDAPPersistException
{
ensureNotNull(o, i);
final List<Modification> mods =
handler.getModifications(o, deleteNullValues, attributes);
if (mods.isEmpty())
{
return null;
}
final String targetDN;
if (dn == null)
{
targetDN = handler.getEntryDN(o);
if (targetDN == null)
{
throw new LDAPPersistException(ERR_PERSISTER_MODIFY_NO_DN.get());
}
}
else
{
targetDN = dn;
}
try
{
final ModifyRequest modifyRequest = new ModifyRequest(targetDN, mods);
if (controls != null)
{
modifyRequest.setControls(controls);
}
return i.modify(modifyRequest);
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
}
public BindResult bind(final T o, final String baseDN, final String password,
final LDAPConnection c, final Control... controls)
throws LDAPException
{
ensureNotNull(o, password, c);
String dn = handler.getEntryDN(o);
if (dn == null)
{
String base = baseDN;
if (base == null)
{
base = handler.getDefaultParentDN().toString();
}
final SearchRequest r = new SearchRequest(base, SearchScope.SUB,
handler.createFilter(o), SearchRequest.NO_ATTRIBUTES);
r.setSizeLimit(1);
final Entry e = c.searchForEntry(r);
if (e == null)
{
throw new LDAPException(ResultCode.NO_RESULTS_RETURNED,
ERR_PERSISTER_BIND_NO_ENTRY_FOUND.get());
}
else
{
dn = e.getDN();
}
}
return c.bind(new SimpleBindRequest(dn, password, controls));
}
public T get(final T o, final LDAPInterface i, final String parentDN)
throws LDAPPersistException
{
final String dn = handler.constructDN(o, parentDN);
final Entry entry;
try
{
entry = i.getEntry(dn, handler.getAttributesToRequest());
if (entry == null)
{
return null;
}
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
return decode(entry);
}
public T get(final String dn, final LDAPInterface i)
throws LDAPPersistException
{
final Entry entry;
try
{
entry = i.getEntry(dn, handler.getAttributesToRequest());
if (entry == null)
{
return null;
}
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
return decode(entry);
}
public void lazilyLoad(final T o, final LDAPInterface i,
final FieldInfo... fields)
throws LDAPPersistException
{
ensureNotNull(o, i);
final String[] attrs;
if ((fields == null) || (fields.length == 0))
{
attrs = handler.getLazilyLoadedAttributes();
}
else
{
final ArrayList<String> attrList = new ArrayList<String>(fields.length);
for (final FieldInfo f : fields)
{
if (f.lazilyLoad())
{
attrList.add(f.getAttributeName());
}
}
attrs = new String[attrList.size()];
attrList.toArray(attrs);
}
if (attrs.length == 0)
{
return;
}
final String dn = handler.getEntryDN(o);
if (dn == null)
{
throw new LDAPPersistException(ERR_PERSISTER_LAZILY_LOAD_NO_DN.get());
}
final Entry entry;
try
{
entry = i.getEntry(handler.getEntryDN(o), attrs);
}
catch (final LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
if (entry == null)
{
throw new LDAPPersistException(
ERR_PERSISTER_LAZILY_LOAD_NO_ENTRY.get(dn));
}
boolean successful = true;
final ArrayList<String> failureReasons = new ArrayList<String>(5);
final Map<String,FieldInfo> fieldMap = handler.getFields();
for (final Attribute a : entry.getAttributes())
{
final String lowerName = toLowerCase(a.getName());
final FieldInfo f = fieldMap.get(lowerName);
if (f != null)
{
successful &= f.decode(o, entry, failureReasons);
}
}
if (! successful)
{
throw new LDAPPersistException(concatenateStrings(failureReasons), o,
null);
}
}
public PersistedObjects<T> search(final T o, final LDAPConnection c)
throws LDAPPersistException
{
return search(o, c, null, SearchScope.SUB, DereferencePolicy.NEVER, 0, 0,
null, NO_CONTROLS);
}
public PersistedObjects<T> search(final T o, final LDAPConnection c,
final String baseDN,
final SearchScope scope)
throws LDAPPersistException
{
return search(o, c, baseDN, scope, DereferencePolicy.NEVER, 0, 0, null,
NO_CONTROLS);
}
public PersistedObjects<T> search(final T o, final LDAPConnection c,
final String baseDN,
final SearchScope scope,
final DereferencePolicy derefPolicy,
final int sizeLimit, final int timeLimit,
final Filter extraFilter,
final Control... controls)
throws LDAPPersistException
{
ensureNotNull(o, c, scope, derefPolicy);
final String base;
if (baseDN == null)
{
base = handler.getDefaultParentDN().toString();
}
else
{
base = baseDN;
}
final Filter filter;
if (extraFilter == null)
{
filter = handler.createFilter(o);
}
else
{
filter = Filter.createANDFilter(handler.createFilter(o), extraFilter);
}
final SearchRequest searchRequest = new SearchRequest(base, scope,
derefPolicy, sizeLimit, timeLimit, false, filter,
handler.getAttributesToRequest());
if (controls != null)
{
searchRequest.setControls(controls);
}
final LDAPEntrySource entrySource;
try
{
entrySource = new LDAPEntrySource(c, searchRequest, false);
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
return new PersistedObjects<T>(this, entrySource);
}
public SearchResult search(final T o, final LDAPInterface i,
final ObjectSearchListener<T> l)
throws LDAPPersistException
{
return search(o, i, null, SearchScope.SUB, DereferencePolicy.NEVER, 0, 0,
null, l, NO_CONTROLS);
}
public SearchResult search(final T o, final LDAPInterface i,
final String baseDN, final SearchScope scope,
final ObjectSearchListener<T> l)
throws LDAPPersistException
{
return search(o, i, baseDN, scope, DereferencePolicy.NEVER, 0, 0, null, l,
NO_CONTROLS);
}
public SearchResult search(final T o, final LDAPInterface i,
final String baseDN, final SearchScope scope,
final DereferencePolicy derefPolicy,
final int sizeLimit, final int timeLimit,
final Filter extraFilter,
final ObjectSearchListener<T> l,
final Control... controls)
throws LDAPPersistException
{
ensureNotNull(o, i, scope, derefPolicy, l);
final String base;
if (baseDN == null)
{
base = handler.getDefaultParentDN().toString();
}
else
{
base = baseDN;
}
final Filter filter;
if (extraFilter == null)
{
filter = handler.createFilter(o);
}
else
{
filter = Filter.createANDFilter(handler.createFilter(o), extraFilter);
}
final SearchListenerBridge<T> bridge = new SearchListenerBridge<T>(this, l);
final SearchRequest searchRequest = new SearchRequest(bridge, base, scope,
derefPolicy, sizeLimit, timeLimit, false, filter,
handler.getAttributesToRequest());
if (controls != null)
{
searchRequest.setControls(controls);
}
try
{
return i.search(searchRequest);
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
}
public PersistedObjects<T> search(final LDAPConnection c, final String baseDN,
final SearchScope scope,
final DereferencePolicy derefPolicy,
final int sizeLimit, final int timeLimit,
final Filter filter,
final Control... controls)
throws LDAPPersistException
{
ensureNotNull(c, scope, derefPolicy, filter);
final String base;
if (baseDN == null)
{
base = handler.getDefaultParentDN().toString();
}
else
{
base = baseDN;
}
final Filter f = Filter.createANDFilter(filter, handler.createBaseFilter());
final SearchRequest searchRequest = new SearchRequest(base, scope,
derefPolicy, sizeLimit, timeLimit, false, f,
handler.getAttributesToRequest());
if (controls != null)
{
searchRequest.setControls(controls);
}
final LDAPEntrySource entrySource;
try
{
entrySource = new LDAPEntrySource(c, searchRequest, false);
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
return new PersistedObjects<T>(this, entrySource);
}
public SearchResult search(final LDAPInterface i, final String baseDN,
final SearchScope scope,
final DereferencePolicy derefPolicy,
final int sizeLimit, final int timeLimit,
final Filter filter,
final ObjectSearchListener<T> l,
final Control... controls)
throws LDAPPersistException
{
ensureNotNull(i, scope, derefPolicy, filter, l);
final String base;
if (baseDN == null)
{
base = handler.getDefaultParentDN().toString();
}
else
{
base = baseDN;
}
final Filter f = Filter.createANDFilter(filter, handler.createBaseFilter());
final SearchListenerBridge<T> bridge = new SearchListenerBridge<T>(this, l);
final SearchRequest searchRequest = new SearchRequest(bridge, base, scope,
derefPolicy, sizeLimit, timeLimit, false, f,
handler.getAttributesToRequest());
if (controls != null)
{
searchRequest.setControls(controls);
}
try
{
return i.search(searchRequest);
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
}
public T searchForObject(final T o, final LDAPInterface i)
throws LDAPPersistException
{
return searchForObject(o, i, null, SearchScope.SUB, DereferencePolicy.NEVER,
0, 0, null, NO_CONTROLS);
}
public T searchForObject(final T o, final LDAPInterface i,
final String baseDN, final SearchScope scope)
throws LDAPPersistException
{
return searchForObject(o, i, baseDN, scope, DereferencePolicy.NEVER, 0, 0,
null, NO_CONTROLS);
}
public T searchForObject(final T o, final LDAPInterface i,
final String baseDN, final SearchScope scope,
final DereferencePolicy derefPolicy,
final int sizeLimit, final int timeLimit,
final Filter extraFilter, final Control... controls)
throws LDAPPersistException
{
ensureNotNull(o, i, scope, derefPolicy);
final String base;
if (baseDN == null)
{
base = handler.getDefaultParentDN().toString();
}
else
{
base = baseDN;
}
final Filter filter;
if (extraFilter == null)
{
filter = handler.createFilter(o);
}
else
{
filter = Filter.createANDFilter(handler.createFilter(o), extraFilter);
}
final SearchRequest searchRequest = new SearchRequest(base, scope,
derefPolicy, sizeLimit, timeLimit, false, filter,
handler.getAttributesToRequest());
if (controls != null)
{
searchRequest.setControls(controls);
}
try
{
final Entry e = i.searchForEntry(searchRequest);
if (e == null)
{
return null;
}
else
{
return decode(e);
}
}
catch (LDAPPersistException lpe)
{
debugException(lpe);
throw lpe;
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
}
public SearchResult getAll(final LDAPInterface i, final String baseDN,
final ObjectSearchListener<T> l,
final Control... controls)
throws LDAPPersistException
{
ensureNotNull(i, l);
final String base;
if (baseDN == null)
{
base = handler.getDefaultParentDN().toString();
}
else
{
base = baseDN;
}
final SearchListenerBridge<T> bridge = new SearchListenerBridge<T>(this, l);
final SearchRequest searchRequest = new SearchRequest(bridge, base,
SearchScope.SUB, DereferencePolicy.NEVER, 0, 0, false,
handler.createBaseFilter(), handler.getAttributesToRequest());
if (controls != null)
{
searchRequest.setControls(controls);
}
try
{
return i.search(searchRequest);
}
catch (LDAPException le)
{
debugException(le);
throw new LDAPPersistException(le);
}
}
}