LDAP-265: Moved ODM functionality to core and added methods in LdapTemplate.
This commit is contained in:
178
core/src/main/java/org/springframework/LdapDataEntry.java
Normal file
178
core/src/main/java/org/springframework/LdapDataEntry.java
Normal file
@@ -0,0 +1,178 @@
|
||||
package org.springframework;
|
||||
|
||||
import javax.naming.Name;
|
||||
import javax.naming.directory.Attributes;
|
||||
import java.util.SortedSet;
|
||||
|
||||
/**
|
||||
* Common data access methods for entries in an LDAP tree.
|
||||
*
|
||||
* @author Mattias Hellborg Arthursson
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface LdapDataEntry {
|
||||
/**
|
||||
* Get the value of a String attribute. If more than one attribute value
|
||||
* exists for the specified attribute, only the first one will be returned.
|
||||
* If an attribute has no value, <code>null</code> will be returned.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return the value of the attribute if it exists, or <code>null</code> if
|
||||
* the attribute doesn't exist or if it exists but with no value.
|
||||
* @throws ClassCastException if the value of the entry is not a String.
|
||||
*/
|
||||
String getStringAttribute(String name);
|
||||
|
||||
/**
|
||||
* Get the value of an Object attribute. If more than one attribute value
|
||||
* exists for the specified attribute, only the first one will be returned.
|
||||
* If an attribute has no value, <code>null</code> will be returned.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return the attribute value as an object if it exists, or
|
||||
* <code>null</code> if the attribute doesn't exist or if it exists but with
|
||||
* no value.
|
||||
*/
|
||||
Object getObjectAttribute(String name);
|
||||
|
||||
/**
|
||||
* Check if an Object attribute exists, regardless of whether it has a value
|
||||
* or not.
|
||||
*
|
||||
* @param name name of the attribute
|
||||
* @return <code>true</code> if the attribute exists, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
boolean attributeExists(String name);
|
||||
|
||||
/**
|
||||
* Set the with the name <code>name</code> to the <code>value</code>.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @param value value to set the attribute to.
|
||||
*/
|
||||
public void setAttributeValue(String name, Object value);
|
||||
|
||||
/**
|
||||
* Sets a multivalue attribute, disregarding the order of the values.
|
||||
*
|
||||
* If value is null or value.length == 0 then the attribute will be removed.
|
||||
*
|
||||
* If update mode, changes will be made only if the array has more or less
|
||||
* objects or if one or more object has changed. Reordering the objects will
|
||||
* not cause an update.
|
||||
*
|
||||
* @param name The id of the attribute.
|
||||
* @param values Attribute values.
|
||||
*/
|
||||
void setAttributeValues(String name, Object[] values);
|
||||
|
||||
/**
|
||||
* Sets a multivalue attribute.
|
||||
*
|
||||
* If value is null or value.length == 0 then the attribute will be removed.
|
||||
*
|
||||
* If update mode, changes will be made if the array has more or less
|
||||
* objects or if one or more string has changed.
|
||||
*
|
||||
* Reordering the objects will only cause an update if orderMatters is set
|
||||
* to true.
|
||||
*
|
||||
* @param name The id of the attribute.
|
||||
* @param values Attribute values.
|
||||
* @param orderMatters If <code>true</code>, it will be changed even if data
|
||||
* was just reordered.
|
||||
*/
|
||||
void setAttributeValues(String name, Object[] values, boolean orderMatters);
|
||||
|
||||
/**
|
||||
* Add a value to the Attribute with the specified name. If the Attribute
|
||||
* doesn't exist it will be created. This method makes sure that the there
|
||||
* will be no duplicates of an added value - it the value exists it will not
|
||||
* be added again.
|
||||
*
|
||||
* @param name the name of the Attribute to which the specified value should
|
||||
* be added.
|
||||
* @param value the Attribute value to add.
|
||||
*/
|
||||
void addAttributeValue(String name, Object value);
|
||||
|
||||
/**
|
||||
* Add a value to the Attribute with the specified name. If the Attribute
|
||||
* doesn't exist it will be created. The <code>addIfDuplicateExists</code>
|
||||
* parameter controls the handling of duplicates. It <code>false</code>,
|
||||
* this method makes sure that the there will be no duplicates of an added
|
||||
* value - it the value exists it will not be added again.
|
||||
*
|
||||
* @param name the name of the Attribute to which the specified value should
|
||||
* be added.
|
||||
* @param value the Attribute value to add.
|
||||
* @param addIfDuplicateExists <code>true</code> will add the value
|
||||
* regardless of whether there is an identical value already, allowing for
|
||||
* duplicate attribute values; <code>false</code> will not add the value if
|
||||
* it already exists.
|
||||
*/
|
||||
void addAttributeValue(String name, Object value,
|
||||
boolean addIfDuplicateExists);
|
||||
|
||||
/**
|
||||
* Remove a value from the Attribute with the specified name. If the
|
||||
* Attribute doesn't exist, do nothing.
|
||||
*
|
||||
* @param name the name of the Attribute from which the specified value
|
||||
* should be removed.
|
||||
* @param value the value to remove.
|
||||
*/
|
||||
void removeAttributeValue(String name, Object value);
|
||||
|
||||
/**
|
||||
* Get all values of a String attribute.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return a (possibly empty) array containing all registered values of the
|
||||
* attribute as Strings if the attribute is defined or <code>null</code>
|
||||
* otherwise.
|
||||
* @throws IllegalArgumentException if any of the attribute values is not a
|
||||
* String.
|
||||
*/
|
||||
String[] getStringAttributes(String name);
|
||||
|
||||
/**
|
||||
* Get all values of an Object attribute.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return a (possibly empty) array containing all registered values of the
|
||||
* attribute if the attribute is defined or <code>null</code> otherwise.
|
||||
* @since 1.3
|
||||
*/
|
||||
Object[] getObjectAttributes(String name);
|
||||
|
||||
/**
|
||||
* Get all String values of the attribute as a <code>SortedSet</code>.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return a <code>SortedSet</code> containing all values of the attribute,
|
||||
* or <code>null</code> if the attribute does not exist.
|
||||
* @throws IllegalArgumentException if one of the found attribute values cannot be cast to a String.
|
||||
*/
|
||||
SortedSet<String> getAttributeSortedStringSet(String name);
|
||||
|
||||
/**
|
||||
* Returns the DN relative to the base path.
|
||||
* <b>NB</b>: as of version 2.0 the returned name will be an LdapName instance.
|
||||
*
|
||||
* @return The distinguished name of the current context.
|
||||
*
|
||||
* @see org.springframework.ldap.core.DirContextAdapter#getNameInNamespace()
|
||||
*/
|
||||
Name getDn();
|
||||
|
||||
|
||||
/**
|
||||
* Get all the Attributes.
|
||||
*
|
||||
* @return all the Attributes.
|
||||
* @since 1.3
|
||||
*/
|
||||
Attributes getAttributes();
|
||||
}
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
package org.springframework.ldap.core;
|
||||
|
||||
import org.springframework.LdapDataEntry;
|
||||
|
||||
import javax.naming.Name;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.DirContext;
|
||||
import java.util.SortedSet;
|
||||
|
||||
/**
|
||||
* Interface for DirContextAdapter.
|
||||
@@ -27,7 +27,7 @@ import java.util.SortedSet;
|
||||
* @author Mattias Hellborg Arthursson
|
||||
* @see DirContextAdapter
|
||||
*/
|
||||
public interface DirContextOperations extends DirContext,
|
||||
public interface DirContextOperations extends DirContext, LdapDataEntry,
|
||||
AttributeModificationsAware {
|
||||
|
||||
/**
|
||||
@@ -52,120 +52,6 @@ public interface DirContextOperations extends DirContext,
|
||||
*/
|
||||
String[] getNamesOfModifiedAttributes();
|
||||
|
||||
/**
|
||||
* Get the value of a String attribute. If more than one attribute value
|
||||
* exists for the specified attribute, only the first one will be returned.
|
||||
* If an attribute has no value, <code>null</code> will be returned.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return the value of the attribute if it exists, or <code>null</code> if
|
||||
* the attribute doesn't exist or if it exists but with no value.
|
||||
* @throws ClassCastException if the value of the entry is not a String.
|
||||
*/
|
||||
String getStringAttribute(String name);
|
||||
|
||||
/**
|
||||
* Get the value of an Object attribute. If more than one attribute value
|
||||
* exists for the specified attribute, only the first one will be returned.
|
||||
* If an attribute has no value, <code>null</code> will be returned.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return the attribute value as an object if it exists, or
|
||||
* <code>null</code> if the attribute doesn't exist or if it exists but with
|
||||
* no value.
|
||||
*/
|
||||
Object getObjectAttribute(String name);
|
||||
|
||||
/**
|
||||
* Check if an Object attribute exists, regardless of whether it has a value
|
||||
* or not.
|
||||
*
|
||||
* @param name name of the attribute
|
||||
* @return <code>true</code> if the attribute exists, <code>false</code>
|
||||
* otherwise
|
||||
*/
|
||||
boolean attributeExists(String name);
|
||||
|
||||
/**
|
||||
* Set the with the name <code>name</code> to the <code>value</code>.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @param value value to set the attribute to.
|
||||
*/
|
||||
public void setAttributeValue(String name, Object value);
|
||||
|
||||
/**
|
||||
* Sets a multivalue attribute, disregarding the order of the values.
|
||||
*
|
||||
* If value is null or value.length == 0 then the attribute will be removed.
|
||||
*
|
||||
* If update mode, changes will be made only if the array has more or less
|
||||
* objects or if one or more object has changed. Reordering the objects will
|
||||
* not cause an update.
|
||||
*
|
||||
* @param name The id of the attribute.
|
||||
* @param values Attribute values.
|
||||
*/
|
||||
void setAttributeValues(String name, Object[] values);
|
||||
|
||||
/**
|
||||
* Sets a multivalue attribute.
|
||||
*
|
||||
* If value is null or value.length == 0 then the attribute will be removed.
|
||||
*
|
||||
* If update mode, changes will be made if the array has more or less
|
||||
* objects or if one or more string has changed.
|
||||
*
|
||||
* Reordering the objects will only cause an update if orderMatters is set
|
||||
* to true.
|
||||
*
|
||||
* @param name The id of the attribute.
|
||||
* @param values Attribute values.
|
||||
* @param orderMatters If <code>true</code>, it will be changed even if data
|
||||
* was just reordered.
|
||||
*/
|
||||
void setAttributeValues(String name, Object[] values, boolean orderMatters);
|
||||
|
||||
/**
|
||||
* Add a value to the Attribute with the specified name. If the Attribute
|
||||
* doesn't exist it will be created. This method makes sure that the there
|
||||
* will be no duplicates of an added value - it the value exists it will not
|
||||
* be added again.
|
||||
*
|
||||
* @param name the name of the Attribute to which the specified value should
|
||||
* be added.
|
||||
* @param value the Attribute value to add.
|
||||
*/
|
||||
void addAttributeValue(String name, Object value);
|
||||
|
||||
/**
|
||||
* Add a value to the Attribute with the specified name. If the Attribute
|
||||
* doesn't exist it will be created. The <code>addIfDuplicateExists</code>
|
||||
* parameter controls the handling of duplicates. It <code>false</code>,
|
||||
* this method makes sure that the there will be no duplicates of an added
|
||||
* value - it the value exists it will not be added again.
|
||||
*
|
||||
* @param name the name of the Attribute to which the specified value should
|
||||
* be added.
|
||||
* @param value the Attribute value to add.
|
||||
* @param addIfDuplicateExists <code>true</code> will add the value
|
||||
* regardless of whether there is an identical value already, allowing for
|
||||
* duplicate attribute values; <code>false</code> will not add the value if
|
||||
* it already exists.
|
||||
*/
|
||||
void addAttributeValue(String name, Object value,
|
||||
boolean addIfDuplicateExists);
|
||||
|
||||
/**
|
||||
* Remove a value from the Attribute with the specified name. If the
|
||||
* Attribute doesn't exist, do nothing.
|
||||
*
|
||||
* @param name the name of the Attribute from which the specified value
|
||||
* should be removed.
|
||||
* @param value the value to remove.
|
||||
*/
|
||||
void removeAttributeValue(String name, Object value);
|
||||
|
||||
/**
|
||||
* Update the attributes.This will mean that the getters (
|
||||
* <code>getStringAttribute</code> methods) will return the updated values,
|
||||
@@ -175,48 +61,6 @@ public interface DirContextOperations extends DirContext,
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Get all values of a String attribute.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return a (possibly empty) array containing all registered values of the
|
||||
* attribute as Strings if the attribute is defined or <code>null</code>
|
||||
* otherwise.
|
||||
* @throws IllegalArgumentException if any of the attribute values is not a
|
||||
* String.
|
||||
*/
|
||||
String[] getStringAttributes(String name);
|
||||
|
||||
/**
|
||||
* Get all values of an Object attribute.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return a (possibly empty) array containing all registered values of the
|
||||
* attribute if the attribute is defined or <code>null</code> otherwise.
|
||||
* @since 1.3
|
||||
*/
|
||||
Object[] getObjectAttributes(String name);
|
||||
|
||||
/**
|
||||
* Get all String values of the attribute as a <code>SortedSet</code>.
|
||||
*
|
||||
* @param name name of the attribute.
|
||||
* @return a <code>SortedSet</code> containing all values of the attribute,
|
||||
* or <code>null</code> if the attribute does not exist.
|
||||
* @throws IllegalArgumentException if one of the found attribute values cannot be cast to a String.
|
||||
*/
|
||||
SortedSet<String> getAttributeSortedStringSet(String name);
|
||||
|
||||
/**
|
||||
* Returns the DN relative to the base path.
|
||||
* <b>NB</b>: as of version 2.0 the returned name will be an LdapName instance.
|
||||
*
|
||||
* @return The distinguished name of the current context.
|
||||
*
|
||||
* @see DirContextAdapter#getNameInNamespace()
|
||||
*/
|
||||
Name getDn();
|
||||
|
||||
/**
|
||||
* Set the dn of this entry.
|
||||
*
|
||||
@@ -250,12 +94,4 @@ public interface DirContextOperations extends DirContext,
|
||||
* @since 1.3
|
||||
*/
|
||||
boolean isReferral();
|
||||
|
||||
/**
|
||||
* Get all the Attributes.
|
||||
*
|
||||
* @return all the Attributes.
|
||||
* @since 1.3
|
||||
*/
|
||||
Attributes getAttributes();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.ldap.ContextNotEmptyException;
|
||||
import org.springframework.ldap.NamingException;
|
||||
import org.springframework.ldap.core.support.AbstractContextSource;
|
||||
import org.springframework.ldap.filter.Filter;
|
||||
import org.springframework.ldap.query.LdapQuery;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
|
||||
@@ -1676,4 +1677,120 @@ public interface LdapOperations {
|
||||
* @see org.springframework.ldap.query.LdapQueryBuilder
|
||||
*/
|
||||
<T> T searchForObject(LdapQuery query, ContextMapper<T> mapper);
|
||||
|
||||
/**
|
||||
* Read a named entry from the LDAP directory.
|
||||
*
|
||||
* @param <T> The Java type to return
|
||||
* @param dn The distinguished name of the entry to read from the LDAP directory.
|
||||
* @param clazz The Java type to return
|
||||
* @return The entry as read from the directory
|
||||
*
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
* @since 2.0
|
||||
*/
|
||||
<T> T findByDn(Name dn, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Create the given entry in the LDAP directory.
|
||||
*
|
||||
* @param entry The entry to be create, it must <em>not</em> already exist in the directory.
|
||||
*
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
* @since 2.0
|
||||
*/
|
||||
void create(Object entry);
|
||||
|
||||
/**
|
||||
* Update the given entry in the LDAP directory.
|
||||
*
|
||||
* @param entry The entry to update, it must already exist in the directory.
|
||||
*
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
* @since 2.0
|
||||
*/
|
||||
void update(Object entry);
|
||||
|
||||
/**
|
||||
* Delete an entry from the LDAP directory.
|
||||
*
|
||||
* @param entry The entry to delete, it must already exist in the directory.
|
||||
*
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
* @since 2.0
|
||||
*/
|
||||
void delete(Object entry);
|
||||
|
||||
/**
|
||||
* Find all entries in the LDAP directory of a given type.
|
||||
*
|
||||
* @param <T> The Java type to return
|
||||
* @param clazz The Java type to return
|
||||
* @return All entries that are of the type represented by the given
|
||||
* Java class
|
||||
*
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
* @since 2.0
|
||||
*/
|
||||
<T> List<T> findAll(Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Find all entries in the LDAP directory of a given type.
|
||||
*
|
||||
* @param <T> The Java type to return
|
||||
* @param base The root of the sub-tree at which to begin the search.
|
||||
* @param searchControls The search controls of the search. Note that the 'returned attributes' parameter should
|
||||
* typically not be tampered with, since that may affect the attributes populated in returned entries.
|
||||
* @param clazz The Java type to return
|
||||
* @return All entries that are of the type represented by the given
|
||||
* Java class
|
||||
*
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
* @since 2.0
|
||||
*/
|
||||
<T> List<T> findAll(Name base, SearchControls searchControls, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Find all entries in the LDAP directory of a given type that matches the specified filter.
|
||||
*
|
||||
* @param <T> The Java type to return
|
||||
* @param base The root of the sub-tree at which to begin the search.
|
||||
* @param filter The search filter.
|
||||
* @param searchControls The search controls of the search. Note that the 'returned attributes' parameter should
|
||||
* typically not be tampered with, since that may affect the attributes populated in returned entries.
|
||||
* @param clazz The Java type to return
|
||||
* @return All entries that are of the type represented by the given
|
||||
* Java class
|
||||
*
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
* @since 2.0
|
||||
*/
|
||||
public <T> List<T> find(Name base, Filter filter, SearchControls searchControls, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Search for entries in the LDAP directory.
|
||||
* <p>
|
||||
* Only those entries that both match the query search filter and
|
||||
* are represented by the given Java class are returned.
|
||||
*
|
||||
* @param <T> The Java type to return
|
||||
* @param query the LDAP query specification
|
||||
* @param clazz The Java type to return
|
||||
* @return All matching entries.
|
||||
*
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
* @see org.springframework.ldap.query.LdapQueryBuilder
|
||||
* @since 2.0
|
||||
*/
|
||||
<T> List<T> find(LdapQuery query, Class<T> clazz);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
* @since 2.0
|
||||
*/
|
||||
<T> T findOne(LdapQuery query, Class<T> clazz);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.ldap.AuthenticationException;
|
||||
import org.springframework.ldap.NamingException;
|
||||
import org.springframework.ldap.UncategorizedLdapException;
|
||||
import org.springframework.ldap.filter.Filter;
|
||||
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
|
||||
import org.springframework.ldap.odm.core.OdmException;
|
||||
import org.springframework.ldap.odm.core.impl.DefaultObjectDirectoryMapper;
|
||||
import org.springframework.ldap.query.LdapQuery;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -83,6 +87,8 @@ public class LdapTemplate implements LdapOperations, InitializingBean {
|
||||
|
||||
private int defaultCountLimit = 0;
|
||||
|
||||
private ObjectDirectoryMapper odm = new DefaultObjectDirectoryMapper();
|
||||
|
||||
/**
|
||||
* Constructor for bean usage.
|
||||
*/
|
||||
@@ -108,7 +114,17 @@ public class LdapTemplate implements LdapOperations, InitializingBean {
|
||||
this.contextSource = contextSource;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Set the ObjectDirectoryMapper instance to use.
|
||||
*
|
||||
* @param odm the ObejctDirectoryMapper to use.
|
||||
* @since 2.0
|
||||
*/
|
||||
public void setObjectDirectoryMapper(ObjectDirectoryMapper odm) {
|
||||
this.odm = odm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ContextSource.
|
||||
*
|
||||
* @return the ContextSource.
|
||||
@@ -1713,4 +1729,124 @@ public class LdapTemplate implements LdapOperations, InitializingBean {
|
||||
searchControls,
|
||||
mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findByDn(Name dn, final Class<T> clazz) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Reading Entry at - %s$1", dn));
|
||||
}
|
||||
|
||||
// TODO: validate class before lookup
|
||||
// getEntityData(clazz);
|
||||
|
||||
T result = lookup(dn, new ContextMapper<T>() {
|
||||
@Override
|
||||
public T mapFromContext(Object ctx) throws javax.naming.NamingException {
|
||||
return odm.mapFromLdapDataEntry((DirContextOperations) ctx, clazz);
|
||||
}
|
||||
});
|
||||
|
||||
if (result == null) {
|
||||
throw new OdmException(String.format("Entry %1$s does not have the required objectclasses ", dn));
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Found entry - %s$1", result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(Object entry) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Creating entry - %s$1", entry));
|
||||
}
|
||||
|
||||
DirContextAdapter context = new DirContextAdapter(odm.getId(entry));
|
||||
odm.mapToLdapDataEntry(entry, context);
|
||||
|
||||
bind(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Object entry) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Updating entry - %s$1", entry));
|
||||
}
|
||||
|
||||
DirContextOperations context = lookupContext(odm.getId(entry));
|
||||
odm.mapToLdapDataEntry(entry, context);
|
||||
modifyAttributes(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Object entry) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Deleting %s$1", entry));
|
||||
}
|
||||
|
||||
// Just to check that this is a managed class
|
||||
unbind(odm.getId(entry));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findAll(Name base, SearchControls searchControls, final Class<T> clazz) {
|
||||
return find(base, null, searchControls, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> findAll(Class<T> clazz) {
|
||||
return findAll(LdapUtils.emptyLdapName(),
|
||||
getDefaultSearchControls(defaultSearchScope, RETURN_OBJ_FLAG, ALL_ATTRIBUTES),
|
||||
clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> find(Name base, Filter filter, SearchControls searchControls, final Class<T> clazz) {
|
||||
Filter finalFilter = odm.filterFor(clazz, filter);
|
||||
|
||||
// Search from the root if we are not told where to search from
|
||||
Name localBase = base;
|
||||
if (base == null || base.size() == 0) {
|
||||
localBase = LdapUtils.emptyLdapName();
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Searching - base=%1$s, finalFilter=%2$s, scope=%3$s", base, finalFilter, searchControls));
|
||||
}
|
||||
|
||||
List<T> result = search(localBase, finalFilter.encode(), searchControls, new ContextMapper<T>() {
|
||||
@Override
|
||||
public T mapFromContext(Object ctx) throws javax.naming.NamingException {
|
||||
return odm.mapFromLdapDataEntry((DirContextOperations) ctx, clazz);
|
||||
}
|
||||
});
|
||||
result.remove(null);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(String.format("Found %1$s Entries - %2$s", result.size(), result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> find(LdapQuery query, Class<T> clazz) {
|
||||
SearchControls searchControls = searchControlsForQuery(query, RETURN_OBJ_FLAG);
|
||||
return find(query.base(), query.filter(), searchControls, clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T findOne(LdapQuery query, Class<T> clazz) {
|
||||
List<T> result = find(query, clazz);
|
||||
|
||||
if (result.size() == 0) {
|
||||
throw new EmptyResultDataAccessException(1);
|
||||
}
|
||||
else if (result.size() != 1) {
|
||||
throw new IncorrectResultSizeDataAccessException(1, result.size());
|
||||
}
|
||||
|
||||
return result.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.core;
|
||||
|
||||
import org.springframework.LdapDataEntry;
|
||||
import org.springframework.ldap.filter.Filter;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
/**
|
||||
* The ObjectDirectoryMapper keeps track of managed class metadata and is used by {@link org.springframework.ldap.core.LdapTemplate}
|
||||
* to map to/from entity objects annotated with the annotations specified in the {@link org.springframework.ldap.odm.annotations}
|
||||
* package. Instances of this class are typically intended for internal use only.
|
||||
*
|
||||
* @author Mattias Hellborg Arthursson
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface ObjectDirectoryMapper {
|
||||
|
||||
/**
|
||||
* Used to convert from Java representation of an Ldap Entry when writing to
|
||||
* the Ldap directory
|
||||
*
|
||||
* @param entry - The entry to convert.
|
||||
* @param context - The LDAP context to store the converted entry
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
void mapToLdapDataEntry(Object entry, LdapDataEntry context);
|
||||
|
||||
/**
|
||||
* Used to convert from the JNDI LDAP representation of an Entry to the Java representation when reading from LDAP.
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
<T> T mapFromLdapDataEntry(LdapDataEntry ctx, Class<T> clazz);
|
||||
|
||||
/**
|
||||
* Get the distinguished name for the specified object.
|
||||
*
|
||||
* @param entry the entry to get distinguished name for.
|
||||
* @return the distinguished name of the entry.
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
Name getId(Object entry);
|
||||
|
||||
/**
|
||||
* Use the specified search filter and return a new one that only applies to entries of the specified class.
|
||||
* In effect this means padding the original filter with an objectclass condition.
|
||||
*
|
||||
* @param clazz the class.
|
||||
* @param baseFilter the filter we want to use.
|
||||
* @return the original filter, modified so that it only applies to entries of the specified class.
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
Filter filterFor(Class<?> clazz, Filter baseFilter);
|
||||
|
||||
/**
|
||||
* Check if the specified class is already managed by this instance; if not, check the metadata and add the class to the
|
||||
* managed classes.
|
||||
*
|
||||
* @param clazz the class to manage.
|
||||
* @throws org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
void manageClass(Class<?> clazz);
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.LdapDataEntry;
|
||||
import org.springframework.ldap.filter.AndFilter;
|
||||
import org.springframework.ldap.filter.EqualsFilter;
|
||||
import org.springframework.ldap.filter.Filter;
|
||||
import org.springframework.ldap.odm.core.ObjectDirectoryMapper;
|
||||
import org.springframework.ldap.odm.typeconversion.ConverterManager;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.ConverterManagerImpl;
|
||||
|
||||
import javax.naming.Name;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.Attributes;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link ObjectDirectoryMapper}. Unless you need to explicitly configure
|
||||
* converters there is typically no reason to explicitly consider yourself with this class.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
* @author Mattias Hellborg Arthursson
|
||||
* @since 2.0
|
||||
*/
|
||||
public class DefaultObjectDirectoryMapper implements ObjectDirectoryMapper {
|
||||
private static final Log LOG = LogFactory.getLog(DefaultObjectDirectoryMapper.class);
|
||||
|
||||
// The converter manager to use to translate values between LDAP and Java
|
||||
private ConverterManager converterManager;
|
||||
|
||||
private static String OBJECT_CLASS_ATTRIBUTE="objectclass";
|
||||
private static CaseIgnoreString OBJECT_CLASS_ATTRIBUTE_CI=new CaseIgnoreString(OBJECT_CLASS_ATTRIBUTE);
|
||||
|
||||
|
||||
public DefaultObjectDirectoryMapper() {
|
||||
this.converterManager = new ConverterManagerImpl();
|
||||
}
|
||||
|
||||
public void setConverterManager(ConverterManager converterManager) {
|
||||
this.converterManager = converterManager;
|
||||
}
|
||||
|
||||
private static final class EntityData {
|
||||
private final ObjectMetaData metaData;
|
||||
private final Filter ocFilter;
|
||||
|
||||
private EntityData(ObjectMetaData metaData, Filter ocFilter) {
|
||||
this.metaData=metaData;
|
||||
this.ocFilter=ocFilter;
|
||||
}
|
||||
}
|
||||
|
||||
// A map of managed classes to to meta data about those classes
|
||||
private final ConcurrentMap<Class<?>, EntityData> metaDataMap=new ConcurrentHashMap<Class<?>, EntityData>();
|
||||
|
||||
private EntityData getEntityData(Class<?> managedClass) {
|
||||
EntityData result = metaDataMap.get(managedClass);
|
||||
if (result == null) {
|
||||
return addManagedClass(managedClass);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void manageClass(Class<?> clazz) {
|
||||
// This throws exception if data is invalid
|
||||
getEntityData(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link org.springframework.ldap.odm.annotations} annotated class to the set
|
||||
* managed by this OdmManager.
|
||||
*
|
||||
* @param managedClass The class to add to the managed set.
|
||||
*/
|
||||
private EntityData addManagedClass(Class<?> managedClass) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Adding class %1$s to managed set", managedClass));
|
||||
}
|
||||
|
||||
// Extract the meta-data from the class
|
||||
ObjectMetaData metaData=new ObjectMetaData(managedClass);
|
||||
|
||||
// Check we can construct the target type - it must have a zero argument public constructor
|
||||
try {
|
||||
managedClass.getConstructor();
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new InvalidEntryException(String.format(
|
||||
"The class %1$s must have a zero argument constructor to be an Entry", managedClass));
|
||||
}
|
||||
|
||||
// Check we have all of the necessary converters for the class
|
||||
for (Field field : metaData) {
|
||||
AttributeMetaData attributeInfo = metaData.getAttribute(field);
|
||||
if (!attributeInfo.isId() && !(attributeInfo.isObjectClass())) {
|
||||
Class<?> jndiClass = (attributeInfo.isBinary()) ? byte[].class : String.class;
|
||||
Class<?> javaClass = attributeInfo.getValueClass();
|
||||
if (!converterManager.canConvert(jndiClass, attributeInfo.getSyntax(), javaClass)) {
|
||||
throw new InvalidEntryException(String.format(
|
||||
"Missing converter from %1$s to %2$s, this is needed for field %3$s on Entry %4$s",
|
||||
jndiClass, javaClass, field.getName(), managedClass));
|
||||
}
|
||||
if (!converterManager.canConvert(javaClass, attributeInfo.getSyntax(), jndiClass)) {
|
||||
throw new InvalidEntryException(String.format(
|
||||
"Missing converter from %1$s to %2$s, this is needed for field %3$s on Entry %4$s",
|
||||
javaClass, jndiClass, field.getName(), managedClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter so we only read the object classes supported by the managedClass
|
||||
AndFilter ocFilter = new AndFilter();
|
||||
for (CaseIgnoreString oc : metaData.getObjectClasses()) {
|
||||
ocFilter.and(new EqualsFilter(OBJECT_CLASS_ATTRIBUTE, oc.toString()));
|
||||
}
|
||||
|
||||
EntityData newValue = new EntityData(metaData, ocFilter);
|
||||
EntityData previousValue = metaDataMap.putIfAbsent(managedClass, newValue);
|
||||
// Just in case someone beat us to it
|
||||
if(previousValue != null) {
|
||||
return previousValue;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapToLdapDataEntry(Object entry, LdapDataEntry context) {
|
||||
ObjectMetaData metaData=getEntityData(entry.getClass()).metaData;
|
||||
|
||||
Attribute objectclassAttribute = context.getAttributes().get(OBJECT_CLASS_ATTRIBUTE);
|
||||
if(objectclassAttribute == null || objectclassAttribute.size() == 0) {
|
||||
// Object classes are set from the metadata obtained from the @Entity annotation,
|
||||
// but only if this is a new entry.
|
||||
int numOcs=metaData.getObjectClasses().size();
|
||||
CaseIgnoreString[] metaDataObjectClasses=metaData.getObjectClasses().toArray(new CaseIgnoreString[numOcs]);
|
||||
|
||||
String[] stringOcs=new String[numOcs];
|
||||
for (int ocIndex=0; ocIndex<numOcs; ocIndex++) {
|
||||
stringOcs[ocIndex]=metaDataObjectClasses[ocIndex].toString();
|
||||
}
|
||||
|
||||
context.setAttributeValues(OBJECT_CLASS_ATTRIBUTE, stringOcs);
|
||||
}
|
||||
|
||||
// Loop through each of the fields in the object to write to LDAP
|
||||
for (Field field : metaData) {
|
||||
// Grab the meta data for the current field
|
||||
AttributeMetaData attributeInfo = metaData.getAttribute(field);
|
||||
// We dealt with the object class field about, and the DN is set by the call to write the object to LDAP
|
||||
if (!attributeInfo.isId() && !(attributeInfo.isObjectClass())) {
|
||||
try {
|
||||
// If this is a "binary" object the JNDI expects a byte[] otherwise a String
|
||||
Class<?> targetClass = (attributeInfo.isBinary()) ? byte[].class : String.class;
|
||||
// Multi valued?
|
||||
if (!attributeInfo.isList()) {
|
||||
// Single valued - get the value of the field
|
||||
Object fieldValue = field.get(entry);
|
||||
// Ignore null field values
|
||||
if (fieldValue != null) {
|
||||
// Convert the field value to the required type and write it into the JNDI context
|
||||
context.setAttributeValue(attributeInfo.getName().toString(), converterManager.convert(fieldValue,
|
||||
attributeInfo.getSyntax(), targetClass));
|
||||
}
|
||||
} else { // Multi-valued
|
||||
// We need to build up a list of of the values
|
||||
List<String> attributeValues = new ArrayList<String>();
|
||||
// Get the list of values
|
||||
Collection<?> fieldValues = (Collection<?>)field.get(entry);
|
||||
// Ignore null lists
|
||||
if (fieldValues != null) {
|
||||
for (final Object o : fieldValues) {
|
||||
// Ignore null values
|
||||
if (o != null) {
|
||||
attributeValues.add((String)converterManager.convert(o, attributeInfo.getSyntax(),
|
||||
targetClass));
|
||||
}
|
||||
}
|
||||
context.setAttributeValues(attributeInfo.getName().toString(), attributeValues.toArray());
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InvalidEntryException(String.format("Can't set attribute %1$s", attributeInfo.getName()),
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T mapFromLdapDataEntry(LdapDataEntry context, Class<T> clazz) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Converting to Java Entry class %1$s from %2$s", clazz, context));
|
||||
}
|
||||
|
||||
// The Java representation of the LDAP entry
|
||||
T result;
|
||||
|
||||
ObjectMetaData metaData=getEntityData(clazz).metaData;
|
||||
|
||||
try {
|
||||
// The result class must have a zero argument constructor
|
||||
result = clazz.newInstance();
|
||||
|
||||
// Build a map of JNDI attribute names to values
|
||||
Map<CaseIgnoreString, Attribute> attributeValueMap = new HashMap<CaseIgnoreString, Attribute>();
|
||||
// Get a NamingEnumeration to loop through the JNDI attributes in the entry
|
||||
Attributes attributes = context.getAttributes();
|
||||
NamingEnumeration<? extends Attribute> attributesEnumeration = attributes.getAll();
|
||||
// Loop through all of the JNDI attributes
|
||||
while (attributesEnumeration.hasMoreElements()) {
|
||||
Attribute currentAttribute = (Attribute)attributesEnumeration.nextElement();
|
||||
// Add the current attribute to the map keyed on the lowercased (case indep) id of the attribute
|
||||
attributeValueMap.put(new CaseIgnoreString(currentAttribute.getID()), currentAttribute);
|
||||
}
|
||||
|
||||
// Now loop through all the fields in the Java representation populating it with values from the
|
||||
// attributeValueMap
|
||||
for (Field field : metaData) {
|
||||
// Get the current field
|
||||
AttributeMetaData attributeInfo = metaData.getAttribute(field);
|
||||
// We deal with the Id field specially
|
||||
if (!attributeInfo.isId()) {
|
||||
// Not the ID - but is is multi valued?
|
||||
if (!attributeInfo.isList()) {
|
||||
// No - its single valued, grab the JNDI attribute that corresponds to the metadata on the
|
||||
// current field
|
||||
Attribute attribute = attributeValueMap.get(attributeInfo.getName());
|
||||
// There is no guarantee that this attribute is present in the directory - so ignore nulls
|
||||
if (attribute != null) {
|
||||
// Grab the JNDI value
|
||||
Object value = attribute.get();
|
||||
// Check the value is not null
|
||||
if (value != null) {
|
||||
// Convert the JNDI value to its Java representation - this will throw if the
|
||||
// conversion fails
|
||||
Object convertedValue = converterManager.convert(value, attributeInfo.getSyntax(),
|
||||
attributeInfo.getValueClass());
|
||||
// Set it in the Java version
|
||||
field.set(result, convertedValue);
|
||||
}
|
||||
}
|
||||
} else { // We are dealing with a multi valued attribute
|
||||
// We need to build up a list of values
|
||||
List<Object> fieldValues = new ArrayList<Object>();
|
||||
// Grab the attribute from the JNDI representation
|
||||
Attribute currentAttribute = attributeValueMap.get(attributeInfo.getName());
|
||||
// There is no guarantee that this attribute is present in the directory - so ignore nulls
|
||||
if (currentAttribute != null) {
|
||||
// Loop through the values of the JNDI attribute
|
||||
NamingEnumeration<?> valuesEmumeration = currentAttribute.getAll();
|
||||
while (valuesEmumeration.hasMore()) {
|
||||
// Get the current value
|
||||
Object value = valuesEmumeration.nextElement();
|
||||
// Check the value is not null
|
||||
if (value != null) {
|
||||
// Convert the value to its Java representation and add it to our working list
|
||||
fieldValues.add(converterManager.convert(value, attributeInfo.getSyntax(),
|
||||
attributeInfo.getValueClass()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now we need to set the List in to a Java object
|
||||
field.set(result, fieldValues);
|
||||
}
|
||||
} else { // The id field
|
||||
field.set(result, converterManager.convert(context.getDn(), attributeInfo.getSyntax(),
|
||||
attributeInfo.getValueClass()));
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the objectclass attribute then check that values correspond to the metadata we have
|
||||
// for the Java representation
|
||||
Attribute ocAttribute = attributeValueMap.get(OBJECT_CLASS_ATTRIBUTE_CI);
|
||||
if (ocAttribute != null) {
|
||||
// Get all object class values from the JNDI attribute
|
||||
Set<CaseIgnoreString> objectClassesFromJndi = new HashSet<CaseIgnoreString>();
|
||||
NamingEnumeration<?> objectClassesFromJndiEnum = ocAttribute.getAll();
|
||||
while (objectClassesFromJndiEnum.hasMoreElements()) {
|
||||
objectClassesFromJndi.add(new CaseIgnoreString((String)objectClassesFromJndiEnum.nextElement()));
|
||||
}
|
||||
// OK - checks its the same as the meta-data we have
|
||||
if(!collectionContainsAll(objectClassesFromJndi, metaData.getObjectClasses())) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
throw new InvalidEntryException(String.format("No object classes were returned for class %1$s",
|
||||
clazz.getName()));
|
||||
}
|
||||
|
||||
} catch (NamingException ne) {
|
||||
throw new InvalidEntryException(String.format("Problem creating %1$s from LDAP Entry %2$s",
|
||||
clazz, context), ne);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new InvalidEntryException(String.format(
|
||||
"Could not create an instance of %1$s could not access field", clazz.getName()), iae);
|
||||
} catch (InstantiationException ie) {
|
||||
throw new InvalidEntryException(String.format("Could not instantiate %1$s", clazz), ie);
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Converted object - %1$s", result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Name getId(Object entry) {
|
||||
try {
|
||||
return (Name)getEntityData(entry.getClass()).metaData.getIdAttribute().getField().get(entry);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidEntryException(String.format("Can't get Id field from Entry %1$s", entry),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter filterFor(Class<?> clazz, Filter baseFilter) {
|
||||
Filter ocFilter = getEntityData(clazz).ocFilter;
|
||||
|
||||
if(baseFilter == null) {
|
||||
return ocFilter;
|
||||
}
|
||||
|
||||
AndFilter andFilter = new AndFilter();
|
||||
return andFilter.append(ocFilter).append(baseFilter);
|
||||
}
|
||||
|
||||
static boolean collectionContainsAll(Collection<?> collection, Set<?> shouldBePresent) {
|
||||
for (Object o : shouldBePresent) {
|
||||
if(!collection.contains(o)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.springframework.ldap.odm.typeconversion.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ldap.odm.typeconversion.ConverterException;
|
||||
import org.springframework.ldap.odm.typeconversion.ConverterManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.springframework.ldap.odm.typeconversion.ConverterManager}.
|
||||
* <p>
|
||||
@@ -52,7 +52,7 @@ public class LdapNameBuilder {
|
||||
*
|
||||
* @return a new instance.
|
||||
*/
|
||||
public static LdapNameBuilder newInstance(Name name) {
|
||||
public static LdapNameBuilder newLdapName(Name name) {
|
||||
return new LdapNameBuilder(LdapUtils.newLdapName(name));
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class LdapNameBuilder {
|
||||
*
|
||||
* @return a new instance.
|
||||
*/
|
||||
public static LdapNameBuilder newInstance(String name) {
|
||||
public static LdapNameBuilder newLdapName(String name) {
|
||||
return new LdapNameBuilder(LdapUtils.newLdapName(name));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import java.util.List;
|
||||
* @see org.springframework.ldap.odm.annotations.Attribute
|
||||
* @see org.springframework.ldap.odm.annotations.Id
|
||||
* @see org.springframework.ldap.odm.annotations.Transient
|
||||
* @deprecated This functionality is automatically available in LdapTemplate as of version 2.0
|
||||
*/
|
||||
public interface OdmManager {
|
||||
|
||||
|
||||
@@ -16,36 +16,19 @@
|
||||
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.LdapOperations;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.simple.ParameterizedContextMapper;
|
||||
import org.springframework.ldap.filter.AndFilter;
|
||||
import org.springframework.ldap.filter.EqualsFilter;
|
||||
import org.springframework.ldap.odm.core.OdmException;
|
||||
import org.springframework.ldap.filter.Filter;
|
||||
import org.springframework.ldap.filter.HardcodedFilter;
|
||||
import org.springframework.ldap.odm.core.OdmManager;
|
||||
import org.springframework.ldap.odm.typeconversion.ConverterManager;
|
||||
import org.springframework.ldap.query.LdapQuery;
|
||||
import org.springframework.ldap.query.SearchScope;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.naming.Name;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.SearchControls;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -55,43 +38,31 @@ import java.util.Set;
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*
|
||||
* @deprecated This functionality is automatically available in LdapTemplate as of version 2.0
|
||||
*/
|
||||
public final class OdmManagerImpl implements OdmManager {
|
||||
private static final Log LOG = LogFactory.getLog(OdmManagerImpl.class);
|
||||
|
||||
// The link to the LDAP directory
|
||||
private final LdapOperations ldapTemplate;
|
||||
private final LdapTemplate ldapTemplate;
|
||||
|
||||
// The converter manager to use to translate values between LDAP and Java
|
||||
private final ConverterManager converterManager;
|
||||
|
||||
private static String OBJECT_CLASS_ATTRIBUTE="objectclass";
|
||||
private static CaseIgnoreString OBJECT_CLASS_ATTRIBUTE_CI=new CaseIgnoreString(OBJECT_CLASS_ATTRIBUTE);
|
||||
|
||||
private static final class EntityData {
|
||||
private final ObjectMetaData metaData;
|
||||
private final String ocFilter;
|
||||
|
||||
private EntityData(ObjectMetaData metaData, String ocFilter) {
|
||||
this.metaData=metaData;
|
||||
this.ocFilter=ocFilter;
|
||||
}
|
||||
}
|
||||
|
||||
// A map of managed classes to to meta data about those classes
|
||||
private final Map<Class<?>, EntityData> metaDataMap=new HashMap<Class<?>, EntityData>();
|
||||
private DefaultObjectDirectoryMapper objectDirectoryMapper;
|
||||
|
||||
public OdmManagerImpl(ConverterManager converterManager,
|
||||
LdapOperations ldapOperations,
|
||||
Set<Class<?>> managedClasses) {
|
||||
this.converterManager=converterManager;
|
||||
this.ldapTemplate = ldapOperations;
|
||||
this.ldapTemplate = (LdapTemplate)ldapOperations;
|
||||
objectDirectoryMapper = new DefaultObjectDirectoryMapper();
|
||||
|
||||
if(converterManager != null) {
|
||||
objectDirectoryMapper.setConverterManager(converterManager);
|
||||
}
|
||||
|
||||
if (managedClasses!=null) {
|
||||
for (Class<?> managedClass: managedClasses) {
|
||||
addManagedClass(managedClass);
|
||||
}
|
||||
}
|
||||
|
||||
this.ldapTemplate.setObjectDirectoryMapper(objectDirectoryMapper);
|
||||
}
|
||||
|
||||
public OdmManagerImpl(ConverterManager converterManager,
|
||||
@@ -105,14 +76,6 @@ public final class OdmManagerImpl implements OdmManager {
|
||||
this(converterManager, contextSource, null);
|
||||
}
|
||||
|
||||
private EntityData getEntityData(Class<?> managedClass) {
|
||||
EntityData result=metaDataMap.get(managedClass);
|
||||
if (result==null) {
|
||||
throw new UnmanagedClassException(String.format("The %1$s class is not managed by this OdmManager", managedClass));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link org.springframework.ldap.odm.annotations} annotated class to the set
|
||||
* managed by this OdmManager.
|
||||
@@ -120,47 +83,7 @@ public final class OdmManagerImpl implements OdmManager {
|
||||
* @param managedClass The class to add to the managed set.
|
||||
*/
|
||||
public void addManagedClass(Class<?> managedClass) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Adding class %1$s to managed set", managedClass));
|
||||
}
|
||||
|
||||
// Extract the meta-data from the class
|
||||
ObjectMetaData metaData=new ObjectMetaData(managedClass);
|
||||
|
||||
// Check we can construct the target type - it must have a zero argument public constructor
|
||||
try {
|
||||
managedClass.getConstructor();
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new InvalidEntryException(String.format(
|
||||
"The class %1$s must have a zero argument constructor to be an Entry", managedClass));
|
||||
}
|
||||
|
||||
// Check we have all of the necessary converters for the class
|
||||
for (Field field : metaData) {
|
||||
AttributeMetaData attributeInfo = metaData.getAttribute(field);
|
||||
if (!attributeInfo.isId() && !(attributeInfo.isObjectClass())) {
|
||||
Class<?> jndiClass = (attributeInfo.isBinary()) ? byte[].class : String.class;
|
||||
Class<?> javaClass = attributeInfo.getValueClass();
|
||||
if (!converterManager.canConvert(jndiClass, attributeInfo.getSyntax(), javaClass)) {
|
||||
throw new InvalidEntryException(String.format(
|
||||
"Missing converter from %1$s to %2$s, this is needed for field %3$s on Entry %4$s",
|
||||
jndiClass, javaClass, field.getName(), managedClass));
|
||||
}
|
||||
if (!converterManager.canConvert(javaClass, attributeInfo.getSyntax(), jndiClass)) {
|
||||
throw new InvalidEntryException(String.format(
|
||||
"Missing converter from %1$s to %2$s, this is needed for field %3$s on Entry %4$s",
|
||||
javaClass, jndiClass, field.getName(), managedClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter so we only read the object classes supported by the managedClass
|
||||
AndFilter ocFilter = new AndFilter();
|
||||
for (CaseIgnoreString oc : metaData.getObjectClasses()) {
|
||||
ocFilter.and(new EqualsFilter(OBJECT_CLASS_ATTRIBUTE, oc.toString()));
|
||||
}
|
||||
|
||||
metaDataMap.put(managedClass, new EntityData(metaData, ocFilter.encode()));
|
||||
objectDirectoryMapper.manageClass(managedClass);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -169,21 +92,7 @@ public final class OdmManagerImpl implements OdmManager {
|
||||
* @see org.springframework.ldap.odm.core.OdmManager#create(java.lang.Object)
|
||||
*/
|
||||
public <T> T read(Class<T> clazz, Name dn) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Reading Entry at - %s$1", dn));
|
||||
}
|
||||
|
||||
getEntityData(clazz);
|
||||
|
||||
T result = clazz.cast(ldapTemplate.lookup(dn, new GenericContextMapper<T>(clazz)));
|
||||
if (result==null) {
|
||||
throw new OdmException(String.format("Entry %1$s does not have the required objectclasses ", dn));
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Found entry - %s$1", result));
|
||||
}
|
||||
|
||||
return result;
|
||||
return ldapTemplate.findByDn(dn, clazz);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -192,13 +101,7 @@ public final class OdmManagerImpl implements OdmManager {
|
||||
* @see org.springframework.ldap.odm.core.OdmManager#create(java.lang.Object)
|
||||
*/
|
||||
public void create(Object entry) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Creating entry - %s$1", entry));
|
||||
}
|
||||
|
||||
DirContextAdapter context = new DirContextAdapter(getId(entry));
|
||||
mapToContext(entry, context);
|
||||
ldapTemplate.bind(context);
|
||||
ldapTemplate.create(entry);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -207,13 +110,7 @@ public final class OdmManagerImpl implements OdmManager {
|
||||
* @see org.springframework.ldap.odm.core.OdmManager#update(java.lang.Object, boolean)
|
||||
*/
|
||||
public void update(Object entry) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Updating entry - %s$1", entry));
|
||||
}
|
||||
|
||||
DirContextOperations context = ldapTemplate.lookupContext(getId(entry));
|
||||
mapToContext(entry, context);
|
||||
ldapTemplate.modifyAttributes(context);
|
||||
ldapTemplate.update(entry);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -222,309 +119,27 @@ public final class OdmManagerImpl implements OdmManager {
|
||||
* @see org.springframework.ldap.odm.core.OdmManager#delete(javax.naming.Name)
|
||||
*/
|
||||
public void delete(Object entry) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Deleting %s$1", entry));
|
||||
}
|
||||
|
||||
// Just to check that this is a managed class
|
||||
getEntityData(entry.getClass());
|
||||
ldapTemplate.unbind(getId(entry));
|
||||
ldapTemplate.delete(entry);
|
||||
}
|
||||
|
||||
private Name getId(Object entry) {
|
||||
try {
|
||||
return (Name)getEntityData(entry.getClass()).metaData.getIdAttribute().getField().get(entry);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidEntryException(String.format("Can't get Id field from Entry %1$s", entry),
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.ldap.odm.core.OdmManager#search(java.lang.Class, javax.naming.Name, java.lang.String, javax.naming.directory.SearchControls)
|
||||
*/
|
||||
public <T> List<T> search(Class<T> managedClass, Name base, String filter, SearchControls scope) {
|
||||
EntityData entityData=getEntityData(managedClass);
|
||||
|
||||
// Add a filter so we only read the object class we can deal with
|
||||
String finalFilter = entityData.ocFilter;
|
||||
if (filter != null && filter.length() != 0) {
|
||||
StringBuilder fixedFilter = new StringBuilder();
|
||||
fixedFilter.append("(&(").append(filter).append(")").append(entityData.ocFilter).append(")");
|
||||
finalFilter = fixedFilter.toString();
|
||||
Filter searchFilter = null;
|
||||
if(StringUtils.hasText(filter)) {
|
||||
searchFilter = new HardcodedFilter(filter);
|
||||
}
|
||||
|
||||
// Search from the root if we are not told where to search from
|
||||
Name localBase = base;
|
||||
if (base == null || base.size() == 0) {
|
||||
localBase = LdapUtils.emptyLdapName();
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Searching - base=%1$s, finalFilter=%2$s, scope=%3$s", base, finalFilter, scope));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<T> result = ldapTemplate.search(localBase, finalFilter, scope, new GenericContextMapper<T>(managedClass));
|
||||
result.remove(null);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Found %1$s Entries - %2$s", result.size(), result));
|
||||
}
|
||||
|
||||
return result;
|
||||
return ldapTemplate.find(base, searchFilter, scope, managedClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> List<T> search(Class<T> clazz, LdapQuery query) {
|
||||
SearchControls searchControls = new SearchControls();
|
||||
SearchScope searchScope = query.searchScope();
|
||||
if(searchScope == null) {
|
||||
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
} else {
|
||||
searchControls.setSearchScope(searchScope.getId());
|
||||
}
|
||||
|
||||
Integer countLimit = query.countLimit();
|
||||
if(countLimit != null) {
|
||||
searchControls.setCountLimit(countLimit);
|
||||
}
|
||||
|
||||
Integer timeLimit = query.timeLimit();
|
||||
if(timeLimit != null) {
|
||||
searchControls.setCountLimit(timeLimit);
|
||||
}
|
||||
|
||||
// Defaults to null which means 'all', so if it's not set we're still good.
|
||||
searchControls.setReturningAttributes(query.attributes());
|
||||
|
||||
return search(clazz, query.base(), query.filter().encode(), searchControls);
|
||||
return ldapTemplate.find(query, clazz);
|
||||
}
|
||||
|
||||
public <T> List<T> findAll(Class<T> managedClass, Name base, SearchControls scope) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Searching for all Entries with objectClass=%1$s, with base=%2$s, scope=%3$s",
|
||||
getEntityData(managedClass).metaData.getObjectClasses(), base, scope));
|
||||
}
|
||||
|
||||
return search(managedClass, base, null, scope);
|
||||
return ldapTemplate.findAll(base, scope, managedClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to convert from Java representation of an Ldap Entry when writing to
|
||||
* the Ldap directory
|
||||
*
|
||||
* @param entry - The entry to convert.
|
||||
* @param context - The LDAP context to store the converted entry
|
||||
* @throws javax.naming.NamingEnumeration on error.
|
||||
*/
|
||||
private void mapToContext(Object entry, DirContextOperations context) {
|
||||
ObjectMetaData metaData=getEntityData(entry.getClass()).metaData;
|
||||
|
||||
Attribute objectclassAttribute = context.getAttributes().get(OBJECT_CLASS_ATTRIBUTE);
|
||||
if(objectclassAttribute == null || objectclassAttribute.size() == 0) {
|
||||
// Object classes are set from the metadata obtained from the @Entity annotation,
|
||||
// but only if this is a new entry.
|
||||
int numOcs=metaData.getObjectClasses().size();
|
||||
CaseIgnoreString[] metaDataObjectClasses=metaData.getObjectClasses().toArray(new CaseIgnoreString[numOcs]);
|
||||
|
||||
String[] stringOcs=new String[numOcs];
|
||||
for (int ocIndex=0; ocIndex<numOcs; ocIndex++) {
|
||||
stringOcs[ocIndex]=metaDataObjectClasses[ocIndex].toString();
|
||||
}
|
||||
|
||||
context.setAttributeValues(OBJECT_CLASS_ATTRIBUTE, stringOcs);
|
||||
}
|
||||
|
||||
// Loop through each of the fields in the object to write to LDAP
|
||||
for (Field field : metaData) {
|
||||
// Grab the meta data for the current field
|
||||
AttributeMetaData attributeInfo = metaData.getAttribute(field);
|
||||
// We dealt with the object class field about, and the DN is set by the call to write the object to LDAP
|
||||
if (!attributeInfo.isId() && !(attributeInfo.isObjectClass())) {
|
||||
try {
|
||||
// If this is a "binary" object the JNDI expects a byte[] otherwise a String
|
||||
Class<?> targetClass = (attributeInfo.isBinary()) ? byte[].class : String.class;
|
||||
// Multi valued?
|
||||
if (!attributeInfo.isList()) {
|
||||
// Single valued - get the value of the field
|
||||
Object fieldValue = field.get(entry);
|
||||
// Ignore null field values
|
||||
if (fieldValue != null) {
|
||||
// Convert the field value to the required type and write it into the JNDI context
|
||||
context.setAttributeValue(attributeInfo.getName().toString(), converterManager.convert(fieldValue,
|
||||
attributeInfo.getSyntax(), targetClass));
|
||||
}
|
||||
} else { // Multi-valued
|
||||
// We need to build up a list of of the values
|
||||
List<String> attributeValues = new ArrayList<String>();
|
||||
// Get the list of values
|
||||
Collection<?> fieldValues = (Collection<?>)field.get(entry);
|
||||
// Ignore null lists
|
||||
if (fieldValues != null) {
|
||||
for (final Object o : fieldValues) {
|
||||
// Ignore null values
|
||||
if (o != null) {
|
||||
attributeValues.add((String)converterManager.convert(o, attributeInfo.getSyntax(),
|
||||
targetClass));
|
||||
}
|
||||
}
|
||||
context.setAttributeValues(attributeInfo.getName().toString(), attributeValues.toArray());
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InvalidEntryException(String.format("Can't set attribute %1$s", attributeInfo.getName()),
|
||||
e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to convert from the JNDI LDAP representation of an Entry to the Java representation when reading from LDAP
|
||||
*/
|
||||
private class GenericContextMapper<T> implements ParameterizedContextMapper<T> {
|
||||
private final Class<T> managedClass;
|
||||
|
||||
private GenericContextMapper(Class<T> managedClass) {
|
||||
this.managedClass=managedClass;
|
||||
}
|
||||
|
||||
// Called by Spring LDAP to do the conversion
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.ldap.core.simple.ParameterizedContextMapper#mapFromContext(java.lang.Object)
|
||||
*/
|
||||
public T mapFromContext(Object object) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Converting to Java Entry class %1$s from %2$s", managedClass, object));
|
||||
}
|
||||
|
||||
// The Java representation of the LDAP entry
|
||||
T result = null;
|
||||
|
||||
// This is guaranteed by Spring LDAP to be a DirContextOperations
|
||||
DirContextOperations context = (DirContextOperations)object;
|
||||
|
||||
ObjectMetaData metaData=getEntityData(managedClass).metaData;
|
||||
|
||||
try {
|
||||
// The result class must have a zero argument constructor
|
||||
result = managedClass.newInstance();
|
||||
|
||||
// Build a map of JNDI attribute names to values
|
||||
Map<CaseIgnoreString, Attribute> attributeValueMap = new HashMap<CaseIgnoreString, Attribute>();
|
||||
// Get a NamingEnumeration to loop through the JNDI attributes in the entry
|
||||
Attributes attributes = context.getAttributes();
|
||||
NamingEnumeration<? extends Attribute> attributesEnumeration = attributes.getAll();
|
||||
// Loop through all of the JNDI attributes
|
||||
while (attributesEnumeration.hasMoreElements()) {
|
||||
Attribute currentAttribute = (Attribute)attributesEnumeration.nextElement();
|
||||
// Add the current attribute to the map keyed on the lowercased (case indep) id of the attribute
|
||||
attributeValueMap.put(new CaseIgnoreString(currentAttribute.getID()), currentAttribute);
|
||||
}
|
||||
|
||||
// Now loop through all the fields in the Java representation populating it with values from the
|
||||
// attributeValueMap
|
||||
for (Field field : metaData) {
|
||||
// Get the current field
|
||||
AttributeMetaData attributeInfo = metaData.getAttribute(field);
|
||||
// We deal with the Id field specially
|
||||
if (!attributeInfo.isId()) {
|
||||
// Not the ID - but is is multi valued?
|
||||
if (!attributeInfo.isList()) {
|
||||
// No - its single valued, grab the JNDI attribute that corresponds to the metadata on the
|
||||
// current field
|
||||
Attribute attribute = attributeValueMap.get(attributeInfo.getName());
|
||||
// There is no guarantee that this attribute is present in the directory - so ignore nulls
|
||||
if (attribute != null) {
|
||||
// Grab the JNDI value
|
||||
Object value = attribute.get();
|
||||
// Check the value is not null
|
||||
if (value != null) {
|
||||
// Convert the JNDI value to its Java representation - this will throw if the
|
||||
// conversion fails
|
||||
Object convertedValue = converterManager.convert(value, attributeInfo.getSyntax(),
|
||||
attributeInfo.getValueClass());
|
||||
// Set it in the Java version
|
||||
field.set(result, convertedValue);
|
||||
}
|
||||
}
|
||||
} else { // We are dealing with a multi valued attribute
|
||||
// We need to build up a list of values
|
||||
List<Object> fieldValues = new ArrayList<Object>();
|
||||
// Grab the attribute from the JNDI representation
|
||||
Attribute currentAttribute = attributeValueMap.get(attributeInfo.getName());
|
||||
// There is no guarantee that this attribute is present in the directory - so ignore nulls
|
||||
if (currentAttribute != null) {
|
||||
// Loop through the values of the JNDI attribute
|
||||
NamingEnumeration<?> valuesEmumeration = currentAttribute.getAll();
|
||||
while (valuesEmumeration.hasMore()) {
|
||||
// Get the current value
|
||||
Object value = valuesEmumeration.nextElement();
|
||||
// Check the value is not null
|
||||
if (value != null) {
|
||||
// Convert the value to its Java representation and add it to our working list
|
||||
fieldValues.add(converterManager.convert(value, attributeInfo.getSyntax(),
|
||||
attributeInfo.getValueClass()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now we need to set the List in to a Java object
|
||||
field.set(result, fieldValues);
|
||||
}
|
||||
} else { // The id field
|
||||
field.set(result, converterManager.convert(context.getDn(), attributeInfo.getSyntax(),
|
||||
attributeInfo.getValueClass()));
|
||||
}
|
||||
}
|
||||
|
||||
// If this is the objectclass attribute then check that values correspond to the metadata we have
|
||||
// for the Java representation
|
||||
Attribute ocAttribute = attributeValueMap.get(OBJECT_CLASS_ATTRIBUTE_CI);
|
||||
if (ocAttribute != null) {
|
||||
// Get all object class values from the JNDI attribute
|
||||
Set<CaseIgnoreString> objectClassesFromJndi = new HashSet<CaseIgnoreString>();
|
||||
NamingEnumeration<?> objectClassesFromJndiEnum = ocAttribute.getAll();
|
||||
while (objectClassesFromJndiEnum.hasMoreElements()) {
|
||||
objectClassesFromJndi.add(new CaseIgnoreString((String)objectClassesFromJndiEnum.nextElement()));
|
||||
}
|
||||
// OK - checks its the same as the meta-data we have
|
||||
if(!collectionContainsAll(objectClassesFromJndi, metaData.getObjectClasses())) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
throw new InvalidEntryException(String.format("No object classes were returned for class %1$s",
|
||||
managedClass.getName()));
|
||||
}
|
||||
|
||||
} catch (NamingException ne) {
|
||||
throw new InvalidEntryException(String.format("Problem creating %1$s from LDAP Entry %2$s",
|
||||
managedClass, object), ne);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new InvalidEntryException(String.format(
|
||||
"Could not create an instance of %1$s could not access field", managedClass.getName()), iae);
|
||||
} catch (InstantiationException ie) {
|
||||
throw new InvalidEntryException(String.format("Could not instantiate %1$s", managedClass), ie);
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Converted object - %1$s", result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean collectionContainsAll(Collection<?> collection, Set<?> shouldBePresent) {
|
||||
for (Object o : shouldBePresent) {
|
||||
if(!collection.contains(o)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import java.util.Set;
|
||||
* </pre>
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
* @deprecated This functionality is automatically available in LdapTemplate as of version 2.0
|
||||
*/
|
||||
public final class OdmManagerImplFactoryBean implements FactoryBean {
|
||||
private LdapOperations ldapOperations = null;
|
||||
|
||||
@@ -42,7 +42,6 @@ import org.springframework.ldap.odm.core.OdmManager;
|
||||
import org.springframework.ldap.odm.core.impl.InvalidEntryException;
|
||||
import org.springframework.ldap.odm.core.impl.MetaDataException;
|
||||
import org.springframework.ldap.odm.core.impl.OdmManagerImpl;
|
||||
import org.springframework.ldap.odm.core.impl.UnmanagedClassException;
|
||||
import org.springframework.ldap.odm.test.utils.ExecuteRunnable;
|
||||
import org.springframework.ldap.odm.test.utils.GetFreePort;
|
||||
import org.springframework.ldap.odm.test.utils.RunnableTest;
|
||||
@@ -577,7 +576,7 @@ public final class TestLdap {
|
||||
}
|
||||
|
||||
// The OdmManager should flag any attempt to use a "unmanaged" class
|
||||
@Test(expected = UnmanagedClassException.class)
|
||||
@Test(expected = MetaDataException.class)
|
||||
public void unManagedClass() {
|
||||
odmManager.read(Integer.class, baseName);
|
||||
}
|
||||
|
||||
16
samples/odm/build.gradle
Normal file
16
samples/odm/build.gradle
Normal file
@@ -0,0 +1,16 @@
|
||||
apply from: JAVA_SCRIPT
|
||||
apply plugin: 'war'
|
||||
apply plugin: 'jetty'
|
||||
|
||||
dependencies {
|
||||
compile project(':spring-ldap-test'),
|
||||
'log4j:log4j:1.2.9',
|
||||
'javax.servlet:jstl:1.2',
|
||||
"org.springframework:spring-context:$springVersion",
|
||||
"org.springframework:spring-webmvc:$springVersion"
|
||||
|
||||
provided "javax.servlet:servlet-api:2.5"
|
||||
|
||||
testCompile "org.springframework:spring-test:$springVersion",
|
||||
"junit:junit:$junitVersion"
|
||||
}
|
||||
9
samples/odm/readme.txt
Normal file
9
samples/odm/readme.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
Sample application demonstrating how to do the most basic stuff in Spring LDAP using the Object-Directory Mapping facilities.
|
||||
A very simple dao implementation is provided in org.springframework.ldap.samples.plain.dao.PersonDaoImpl
|
||||
It demonstrates some basic operations using Spring LDAP Object-Directory Mapping.
|
||||
|
||||
The core Spring application context of the sample is defined in resources/applicationContext.xml.
|
||||
This ApplicationContext will start an in-process Apache Directory Server instance, automatically populated
|
||||
with some test data. The data will be reset every time the application is restarted.
|
||||
|
||||
To run the example, do 'gradle jettyRun', and then navigate to http://localhost:8080/spring-ldap-plain-sample
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2005-2010 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.springframework.ldap.samples.plain.dao;
|
||||
|
||||
import org.springframework.ldap.samples.plain.domain.Person;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Data Access Object interface for the Person entity.
|
||||
*
|
||||
* @author Mattias Hellborg Arthursson
|
||||
* @author Ulrik Sandberg
|
||||
*/
|
||||
public interface PersonDao {
|
||||
void create(Person person);
|
||||
|
||||
void update(Person person);
|
||||
|
||||
void delete(Person person);
|
||||
|
||||
List<String> getAllPersonNames();
|
||||
|
||||
List<Person> findAll();
|
||||
|
||||
Person findByPrimaryKey(String country, String company, String fullname);
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2005-2010 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.springframework.ldap.samples.plain.dao;
|
||||
|
||||
import org.springframework.ldap.core.AttributesMapper;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.samples.plain.domain.Person;
|
||||
import org.springframework.ldap.support.LdapNameBuilder;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.ldap.LdapName;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springframework.ldap.query.LdapQueryBuilder.query;
|
||||
|
||||
/**
|
||||
* Default implementation of PersonDao. This implementation uses
|
||||
* DirContextAdapter for managing attribute values. We use a ContextMapper
|
||||
* to map from the found contexts to our domain objects. This is especially useful
|
||||
* since we in this case have properties in our domain objects that depend on parts of the DN.
|
||||
*
|
||||
* We could have worked with Attributes and an AttributesMapper implementation
|
||||
* instead, but working with Attributes is a bore and also, working with
|
||||
* AttributesMapper objects (or, indeed Attributes) does not give us access to
|
||||
* the distinguished name. However, we do use it in one method that only needs a
|
||||
* single attribute: {@link #getAllPersonNames()}.
|
||||
*
|
||||
* @author Mattias Hellborg Arthursson
|
||||
* @author Ulrik Sandberg
|
||||
*/
|
||||
public class PersonDaoImpl implements PersonDao {
|
||||
|
||||
private LdapTemplate ldapTemplate;
|
||||
|
||||
@Override
|
||||
public void create(Person person) {
|
||||
person.setDn(buildDn(person));
|
||||
ldapTemplate.create(person);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Person person) {
|
||||
person.setDn(buildDn(person));
|
||||
ldapTemplate.update(person);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Person person) {
|
||||
ldapTemplate.delete(ldapTemplate.findByDn(buildDn(person), Person.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllPersonNames() {
|
||||
return ldapTemplate.search(query()
|
||||
.attributes("cn")
|
||||
.where("objectclass").is("person"),
|
||||
new AttributesMapper<String>() {
|
||||
public String mapFromAttributes(Attributes attrs) throws NamingException {
|
||||
return attrs.get("cn").get().toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Person> findAll() {
|
||||
return ldapTemplate.findAll(Person.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Person findByPrimaryKey(String country, String company, String fullname) {
|
||||
LdapName dn = buildDn(country, company, fullname);
|
||||
Person person = ldapTemplate.findByDn(dn, Person.class);
|
||||
|
||||
// TODO: This needs to happen automatically
|
||||
person.setCountry(country);
|
||||
person.setCompany(company);
|
||||
person.setFullName(fullname);
|
||||
|
||||
return person;
|
||||
}
|
||||
|
||||
private LdapName buildDn(Person person) {
|
||||
return buildDn(person.getCountry(), person.getCompany(), person.getFullName());
|
||||
}
|
||||
|
||||
private LdapName buildDn(String country, String company, String fullname) {
|
||||
return LdapNameBuilder.newInstance()
|
||||
.add("c", country)
|
||||
.add("ou", company)
|
||||
.add("cn", fullname)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setLdapTemplate(LdapTemplate ldapTemplate) {
|
||||
this.ldapTemplate = ldapTemplate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2005-2010 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.springframework.ldap.samples.plain.domain;
|
||||
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringStyle;
|
||||
import org.springframework.ldap.odm.annotations.Attribute;
|
||||
import org.springframework.ldap.odm.annotations.Entry;
|
||||
import org.springframework.ldap.odm.annotations.Id;
|
||||
import org.springframework.ldap.odm.annotations.Transient;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
/**
|
||||
* Simple class representing a single person.
|
||||
*
|
||||
* @author Mattias Hellborg Arthursson
|
||||
* @author Ulrik Sandberg
|
||||
*/
|
||||
@Entry(objectClasses = {"inetOrgPerson", "organizationalPerson", "person", "top"})
|
||||
public class Person {
|
||||
@Id
|
||||
private Name dn;
|
||||
|
||||
@Attribute(name = "cn")
|
||||
private String fullName;
|
||||
|
||||
@Attribute(name = "sn")
|
||||
private String lastName;
|
||||
|
||||
@Attribute(name = "description")
|
||||
private String description;
|
||||
|
||||
@Transient
|
||||
private String country;
|
||||
|
||||
@Transient
|
||||
private String company;
|
||||
|
||||
@Attribute(name = "telephoneNumber")
|
||||
private String phone;
|
||||
|
||||
public Name getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
public void setDn(Name dn) {
|
||||
this.dn = dn;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
public void setFullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getCompany() {
|
||||
return company;
|
||||
}
|
||||
|
||||
public void setCompany(String company) {
|
||||
this.company = company;
|
||||
}
|
||||
|
||||
public String getCountry() {
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(String country) {
|
||||
this.country = country;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return EqualsBuilder.reflectionEquals(
|
||||
this, obj);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return HashCodeBuilder
|
||||
.reflectionHashCode(this);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(
|
||||
this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.springframework.ldap.samples.plain.web;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.samples.plain.dao.PersonDao;
|
||||
import org.springframework.ldap.samples.plain.domain.Person;
|
||||
import org.springframework.ldap.samples.utils.HtmlRowLdapTreeVisitor;
|
||||
import org.springframework.ldap.samples.utils.LdapTree;
|
||||
import org.springframework.ldap.samples.utils.LdapTreeBuilder;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.ModelMap;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.naming.Name;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
/**
|
||||
* Default controller.
|
||||
*
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
@Controller
|
||||
public class DefaultController {
|
||||
|
||||
@Autowired
|
||||
private LdapTreeBuilder ldapTreeBuilder;
|
||||
|
||||
@Autowired
|
||||
private PersonDao personDao;
|
||||
|
||||
@RequestMapping("/welcome.do")
|
||||
public void welcomeHandler() {
|
||||
}
|
||||
|
||||
@RequestMapping("/showTree.do")
|
||||
public ModelAndView showTree() {
|
||||
LdapTree ldapTree = ldapTreeBuilder.getLdapTree(LdapUtils.emptyLdapName());
|
||||
HtmlRowLdapTreeVisitor visitor = new PersonLinkHtmlRowLdapTreeVisitor();
|
||||
ldapTree.traverse(visitor);
|
||||
return new ModelAndView("showTree", "rows", visitor.getRows());
|
||||
}
|
||||
|
||||
@RequestMapping("/addPerson.do")
|
||||
public String addPerson() {
|
||||
Person person = getPerson();
|
||||
|
||||
personDao.create(person);
|
||||
return "redirect:/showTree.do";
|
||||
}
|
||||
|
||||
@RequestMapping("/updatePhoneNumber.do")
|
||||
public String updatePhoneNumber() {
|
||||
Person person = personDao.findByPrimaryKey("Sweden", "company1", "John Doe");
|
||||
person.setPhone(StringUtils.join(new String[] { person.getPhone(), "0" }));
|
||||
|
||||
personDao.update(person);
|
||||
return "redirect:/showTree.do";
|
||||
}
|
||||
|
||||
@RequestMapping("/removePerson.do")
|
||||
public String removePerson() {
|
||||
Person person = getPerson();
|
||||
|
||||
personDao.delete(person);
|
||||
return "redirect:/showTree.do";
|
||||
}
|
||||
|
||||
@RequestMapping("/showPerson.do")
|
||||
public ModelMap showPerson(String country, String company, String fullName) {
|
||||
Person person = personDao.findByPrimaryKey(country, company, fullName);
|
||||
return new ModelMap("person", person);
|
||||
}
|
||||
|
||||
private Person getPerson() {
|
||||
Person person = new Person();
|
||||
person.setFullName("John Doe");
|
||||
person.setLastName("Doe");
|
||||
person.setCompany("company1");
|
||||
person.setCountry("Sweden");
|
||||
person.setDescription("Test user");
|
||||
return person;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates appropriate links for person leaves in the tree.
|
||||
*
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
private static final class PersonLinkHtmlRowLdapTreeVisitor extends HtmlRowLdapTreeVisitor {
|
||||
@Override
|
||||
protected String getLinkForNode(DirContextOperations node) {
|
||||
String[] objectClassValues = node.getStringAttributes("objectClass");
|
||||
if (containsValue(objectClassValues, "person")) {
|
||||
Name dn = node.getDn();
|
||||
String country = encodeValue(LdapUtils.getStringValue(dn, "c"));
|
||||
String company = encodeValue(LdapUtils.getStringValue(dn, "ou"));
|
||||
String fullName = encodeValue(LdapUtils.getStringValue(dn, "cn"));
|
||||
|
||||
return "showPerson.do?country=" + country + "&company=" + company + "&fullName=" + fullName;
|
||||
}
|
||||
else {
|
||||
return super.getLinkForNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
private String encodeValue(String value) {
|
||||
try {
|
||||
return URLEncoder.encode(value, "UTF8");
|
||||
}
|
||||
catch (UnsupportedEncodingException e) {
|
||||
// Not supposed to happen
|
||||
throw new RuntimeException("Unexpected encoding exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean containsValue(String[] values, String value) {
|
||||
for (String oneValue : values) {
|
||||
if (StringUtils.equals(oneValue, value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.samples.utils;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class HtmlRowLdapTreeVisitor implements LdapTreeVisitor {
|
||||
|
||||
private List<String> rows = new LinkedList<String>();
|
||||
|
||||
public void visit(DirContextOperations node, int currentDepth) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < currentDepth; i++) {
|
||||
sb.append(" ");
|
||||
}
|
||||
|
||||
sb.append("<a href='").append(getLinkForNode(node)).append("'>").append(node.getDn()).append("</a>")
|
||||
.append("<br>\n");
|
||||
|
||||
rows.add(sb.toString());
|
||||
}
|
||||
|
||||
protected String getLinkForNode(DirContextOperations node) {
|
||||
return "#";
|
||||
}
|
||||
|
||||
public List<String> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.samples.utils;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class LdapTree {
|
||||
private final DirContextOperations node;
|
||||
|
||||
private List<LdapTree> subContexts = new LinkedList<LdapTree>();
|
||||
|
||||
public LdapTree(DirContextOperations node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public DirContextOperations getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public List<LdapTree> getSubContexts() {
|
||||
return subContexts;
|
||||
}
|
||||
|
||||
public void setSubContexts(List<LdapTree> subContexts) {
|
||||
this.subContexts = subContexts;
|
||||
}
|
||||
|
||||
public void addSubTree(LdapTree ldapTree) {
|
||||
subContexts.add(ldapTree);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void traverse(LdapTreeVisitor visitor) {
|
||||
traverse(visitor, 0);
|
||||
}
|
||||
|
||||
private void traverse(LdapTreeVisitor visitor, int currentDepth) {
|
||||
visitor.visit(node, currentDepth);
|
||||
for (LdapTree subContext : subContexts) {
|
||||
subContext.traverse(visitor, currentDepth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.samples.utils;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.support.AbstractContextMapper;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
public class LdapTreeBuilder {
|
||||
|
||||
private LdapTemplate ldapTemplate;
|
||||
|
||||
public LdapTreeBuilder(LdapTemplate ldapTemplate) {
|
||||
this.ldapTemplate = ldapTemplate;
|
||||
}
|
||||
|
||||
public LdapTree getLdapTree(Name root) {
|
||||
DirContextOperations context = ldapTemplate.lookupContext(root);
|
||||
return getLdapTree(context);
|
||||
}
|
||||
|
||||
private LdapTree getLdapTree(final DirContextOperations rootContext) {
|
||||
final LdapTree ldapTree = new LdapTree(rootContext);
|
||||
ldapTemplate.listBindings(rootContext.getDn(),
|
||||
new AbstractContextMapper<Object>() {
|
||||
@Override
|
||||
protected Object doMapFromContext(DirContextOperations ctx) {
|
||||
Name dn = ctx.getDn();
|
||||
dn = LdapUtils.prepend(dn, rootContext.getDn());
|
||||
ldapTree.addSubTree(getLdapTree(ldapTemplate
|
||||
.lookupContext(dn)));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
return ldapTree;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.samples.utils;
|
||||
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
|
||||
public interface LdapTreeVisitor {
|
||||
|
||||
public void visit(DirContextOperations node, int currentDepth);
|
||||
}
|
||||
3
samples/odm/src/main/java/overview.html
Normal file
3
samples/odm/src/main/java/overview.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<body>
|
||||
Plain example of Spring LDAP usage.
|
||||
</body>
|
||||
52
samples/odm/src/main/resources/applicationContext.xml
Normal file
52
samples/odm/src/main/resources/applicationContext.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:property-placeholder location="classpath:/ldap.properties" />
|
||||
|
||||
<!--
|
||||
This is for test and demo purposes only - the TestContextSourceFactoryBean starts an in-process
|
||||
Apache Directory Server instance and populates it with data from the specified LDIF file.
|
||||
|
||||
A real-world application would use a DirContextSource instead.
|
||||
-->
|
||||
<bean id="contextSource"
|
||||
class="org.springframework.ldap.test.TestContextSourceFactoryBean">
|
||||
<property name="defaultPartitionSuffix" value="dc=jayway,dc=se" />
|
||||
<property name="defaultPartitionName" value="jayway" />
|
||||
<property name="principal" value="${userDn}" />
|
||||
<property name="password" value="${password}" />
|
||||
<property name="ldifFile" value="classpath:/setup_data.ldif" />
|
||||
<property name="port" value="18880" />
|
||||
</bean>
|
||||
|
||||
<!--
|
||||
Below is an example of a ContextSource definition as it would look in a real application, connecting
|
||||
against an external LDAP server.
|
||||
|
||||
<bean class="org.springframework.ldap.core.support.LdapContextSource" id="contextSource">
|
||||
<property name="url" value="ldap://ldap.example.com" />
|
||||
<property name="userDn" value="cn=admin,dc=261consulting,dc=com"/>
|
||||
<property name="password" value="secret"/>
|
||||
<property name="base" value="dc=261consulting,dc=com" />
|
||||
</bean>
|
||||
-->
|
||||
|
||||
<bean id="ldapTemplate"
|
||||
class="org.springframework.ldap.core.LdapTemplate">
|
||||
<constructor-arg ref="contextSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="ldapTreeBuilder"
|
||||
class="org.springframework.ldap.samples.utils.LdapTreeBuilder">
|
||||
<constructor-arg ref="ldapTemplate" />
|
||||
</bean>
|
||||
|
||||
<bean id="personDao"
|
||||
class="org.springframework.ldap.samples.plain.dao.PersonDaoImpl">
|
||||
<property name="ldapTemplate" ref="ldapTemplate" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
4
samples/odm/src/main/resources/ldap.properties
Normal file
4
samples/odm/src/main/resources/ldap.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
urls=ldap://127.0.0.1:18880
|
||||
userDn=uid=admin,ou=system
|
||||
password=secret
|
||||
base=dc=jayway,dc=se
|
||||
7
samples/odm/src/main/resources/log4j.properties
Normal file
7
samples/odm/src/main/resources/log4j.properties
Normal file
@@ -0,0 +1,7 @@
|
||||
log4j.rootCategory=INFO, stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n
|
||||
|
||||
log4j.logger.org.apache.directory=ERROR
|
||||
35
samples/odm/src/main/resources/setup_data.ldif
Normal file
35
samples/odm/src/main/resources/setup_data.ldif
Normal file
@@ -0,0 +1,35 @@
|
||||
dn: c=Sweden,dc=jayway,dc=se
|
||||
objectclass: top
|
||||
objectclass: country
|
||||
c: Sweden
|
||||
description: The country of Sweden
|
||||
|
||||
dn: ou=company1,c=Sweden,dc=jayway,dc=se
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: company1
|
||||
description: First company in Sweden
|
||||
|
||||
dn: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
uid: some.person
|
||||
userPassword: password
|
||||
cn: Some Person
|
||||
sn: Person
|
||||
description: Sweden, Company1, Some Person
|
||||
telephoneNumber: +46 555-123456
|
||||
|
||||
dn: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
uid: some.person2
|
||||
userPassword: password
|
||||
cn: Some Person2
|
||||
sn: Person2
|
||||
description: Sweden, Company1, Some Person2
|
||||
telephoneNumber: +46 555-654321
|
||||
17
samples/odm/src/main/webapp/WEB-INF/basic-servlet.xml
Normal file
17
samples/odm/src/main/webapp/WEB-INF/basic-servlet.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:component-scan
|
||||
base-package="org.springframework.ldap.samples.plain.web" />
|
||||
|
||||
<bean
|
||||
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
|
||||
|
||||
<property name="prefix" value="/WEB-INF/jsp/" />
|
||||
<property name="suffix" value=".jsp" />
|
||||
</bean>
|
||||
</beans>
|
||||
20
samples/odm/src/main/webapp/WEB-INF/jsp/showPerson.jsp
Executable file
20
samples/odm/src/main/webapp/WEB-INF/jsp/showPerson.jsp
Executable file
@@ -0,0 +1,20 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<html>
|
||||
<body>
|
||||
<a href="showTree.do">Back</a>
|
||||
<p>
|
||||
|
||||
Full name: ${person.fullName}
|
||||
<br>
|
||||
LastName: ${person.lastName}
|
||||
<br>
|
||||
Description: ${person.description}
|
||||
<br>
|
||||
Country: ${person.country}
|
||||
<br>
|
||||
Company: ${person.company}
|
||||
<br>
|
||||
Phone: ${person.phone}
|
||||
<br>
|
||||
</p>
|
||||
</html>
|
||||
17
samples/odm/src/main/webapp/WEB-INF/jsp/showTree.jsp
Normal file
17
samples/odm/src/main/webapp/WEB-INF/jsp/showTree.jsp
Normal file
@@ -0,0 +1,17 @@
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<html>
|
||||
<body>
|
||||
<h2>Operations</h2>
|
||||
<h3>Clicking a link below performs the described operation which will be reflected in the LDAP tree below</h3>
|
||||
<a href="addPerson.do">Add new test person 'John Doe'</a> (only works once)<br>
|
||||
<a href="updatePhoneNumber.do">Add a '0' to the phone number of test person</a> (only works if the person has been created)<br>
|
||||
<a href="removePerson.do">Remove test person</a><br>
|
||||
<p>
|
||||
<h2>Tree contents</h2>
|
||||
<h3>Click a person row to see the attribute values (country and company rows do not have additional info)</h3>
|
||||
<c:forEach var="row" items="${rows}">
|
||||
${row}
|
||||
</c:forEach>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
39
samples/odm/src/main/webapp/WEB-INF/web.xml
Normal file
39
samples/odm/src/main/webapp/WEB-INF/web.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app id="Tiink-preview" xmlns="http://java.sun.com/xml/ns/j2ee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
|
||||
version="2.4">
|
||||
|
||||
<display-name>Spring LDAP Basic Example</display-name>
|
||||
<listener>
|
||||
<listener-class>
|
||||
org.springframework.web.context.ContextLoaderListener
|
||||
</listener-class>
|
||||
</listener>
|
||||
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>classpath:/applicationContext.xml</param-value>
|
||||
</context-param>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>basic</servlet-name>
|
||||
<servlet-class>
|
||||
org.springframework.web.servlet.DispatcherServlet
|
||||
</servlet-class>
|
||||
<init-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>/WEB-INF/basic-servlet.xml</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>basic</servlet-name>
|
||||
<url-pattern>*.do</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.htm</welcome-file>
|
||||
</welcome-file-list>
|
||||
</web-app>
|
||||
5
samples/odm/src/main/webapp/index.htm
Normal file
5
samples/odm/src/main/webapp/index.htm
Normal file
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta HTTP-EQUIV="REFRESH" content="0; url=showTree.do">
|
||||
</head>
|
||||
</html>
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.springframework.ldap.samples.plain.dao;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ldap.NameNotFoundException;
|
||||
import org.springframework.ldap.samples.plain.domain.Person;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Abstract base class for PersonDao integration tests.
|
||||
*
|
||||
* @author Mattias Hellborg Arthursson
|
||||
* @author Ulrik Sandberg
|
||||
*/
|
||||
@ContextConfiguration("/config/testContext.xml")
|
||||
public class PersonDaoSampleIntegrationTest extends
|
||||
AbstractJUnit4SpringContextTests {
|
||||
|
||||
protected Person person;
|
||||
|
||||
@Autowired
|
||||
private PersonDao personDao;
|
||||
|
||||
@Before
|
||||
public void preparePerson() throws Exception {
|
||||
person = new Person();
|
||||
person.setCountry("Sweden");
|
||||
person.setCompany("company1");
|
||||
person.setFullName("Some Person");
|
||||
person.setLastName("Person");
|
||||
person
|
||||
.setDescription("Sweden, Company1, Some Person");
|
||||
person.setPhone("+46 555-123456");
|
||||
}
|
||||
|
||||
/**
|
||||
* Having a single test method test create, update and delete is not exactly
|
||||
* the ideal way of testing, since they depend on each other. A better way
|
||||
* would be to separate the tests and load a test fixture before each
|
||||
* operation, in order to guarantee the expected state every time. See the
|
||||
* ldaptemplate-person sample for the correct way to do this.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateUpdateDelete() {
|
||||
try {
|
||||
person.setFullName("Another Person");
|
||||
personDao.create(person);
|
||||
personDao.findByPrimaryKey(
|
||||
"Sweden", "company1",
|
||||
"Another Person");
|
||||
// if we got here, create succeeded
|
||||
|
||||
person.setDescription("Another description");
|
||||
personDao.update(person);
|
||||
Person result = personDao
|
||||
.findByPrimaryKey(
|
||||
"Sweden", "company1",
|
||||
"Another Person");
|
||||
assertEquals(
|
||||
"Another description", result
|
||||
.getDescription());
|
||||
} finally {
|
||||
personDao.delete(person);
|
||||
try {
|
||||
personDao.findByPrimaryKey(
|
||||
"Sweden", "company1",
|
||||
"Another Person");
|
||||
fail("NameNotFoundException (when using Spring LDAP) or RuntimeException (when using traditional) expected");
|
||||
} catch (NameNotFoundException expected) {
|
||||
// expected
|
||||
} catch (RuntimeException expected) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllPersonNames() {
|
||||
List<String> result = personDao.getAllPersonNames();
|
||||
assertEquals(2, result.size());
|
||||
String first = result.get(0);
|
||||
assertEquals("Some Person", first);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAll() {
|
||||
List<Person> result = personDao.findAll();
|
||||
assertEquals(2, result.size());
|
||||
Person first = result.get(0);
|
||||
assertEquals("Some Person", first
|
||||
.getFullName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindByPrimaryKey() {
|
||||
Person result = personDao.findByPrimaryKey(
|
||||
"Sweden", "company1", "Some Person");
|
||||
|
||||
assertEquals("Sweden", result.getCountry());
|
||||
assertEquals("company1", result.getCompany());
|
||||
assertEquals("Sweden, Company1, Some Person", result.getDescription());
|
||||
assertEquals("+46 555-123456", result.getPhone());
|
||||
assertEquals("Some Person", result.getFullName());
|
||||
assertEquals("Person", result.getLastName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.samples.utils;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
|
||||
import javax.naming.ldap.LdapName;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
@ContextConfiguration(locations = { "/config/testContext.xml" })
|
||||
public class LdapTreeBuilderIntegrationTest extends AbstractJUnit4SpringContextTests {
|
||||
|
||||
@Autowired
|
||||
private LdapTreeBuilder tested;
|
||||
|
||||
@Test
|
||||
public void testGetLdapTree() {
|
||||
LdapTree ldapTree = tested.getLdapTree(LdapUtils.newLdapName("c=Sweden"));
|
||||
ldapTree.traverse(new TestVisitor());
|
||||
}
|
||||
|
||||
private static final class TestVisitor implements LdapTreeVisitor {
|
||||
private static final LdapName DN_1 = LdapUtils.newLdapName("c=Sweden");
|
||||
private static final LdapName DN_2 = LdapUtils.newLdapName("ou=company1,c=Sweden");
|
||||
private static final LdapName DN_3 = LdapUtils.newLdapName("cn=Some Person,ou=company1,c=Sweden");
|
||||
private static final LdapName DN_4 = LdapUtils.newLdapName("cn=Some Person2,ou=company1,c=Sweden");
|
||||
|
||||
private Map<LdapName, Integer> names = new LinkedHashMap<LdapName, Integer>();
|
||||
|
||||
private Iterator<LdapName> keyIterator;
|
||||
|
||||
public TestVisitor() {
|
||||
names.put(DN_1, 0);
|
||||
names.put(DN_2, 1);
|
||||
names.put(DN_3, 2);
|
||||
names.put(DN_4, 2);
|
||||
|
||||
keyIterator = names.keySet().iterator();
|
||||
}
|
||||
|
||||
public void visit(DirContextOperations node, int currentDepth) {
|
||||
LdapName next = keyIterator.next();
|
||||
assertEquals(next, node.getDn());
|
||||
assertEquals(names.get(next).intValue(), currentDepth);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
4
samples/odm/src/test/resources/config/ldap.properties
Normal file
4
samples/odm/src/test/resources/config/ldap.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
urls=ldap://127.0.0.1:18880
|
||||
userDn=uid=admin,ou=system
|
||||
password=secret
|
||||
base=dc=jayway,dc=se
|
||||
33
samples/odm/src/test/resources/config/testContext.xml
Normal file
33
samples/odm/src/test/resources/config/testContext.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
|
||||
<bean id="placeholderConfig"
|
||||
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
|
||||
<property name="location" value="classpath:/config/ldap.properties" />
|
||||
</bean>
|
||||
|
||||
<bean id="contextSource"
|
||||
class="org.springframework.ldap.test.TestContextSourceFactoryBean">
|
||||
<property name="defaultPartitionSuffix" value="dc=jayway,dc=se" />
|
||||
<property name="defaultPartitionName" value="jayway" />
|
||||
<property name="principal" value="${userDn}" />
|
||||
<property name="password" value="${password}" />
|
||||
<property name="ldifFile" value="/setup_data.ldif" />
|
||||
<property name="port" value="18880" />
|
||||
</bean>
|
||||
|
||||
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
|
||||
<constructor-arg ref="contextSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="personDao"
|
||||
class="org.springframework.ldap.samples.plain.dao.PersonDaoImpl">
|
||||
<property name="ldapTemplate" ref="ldapTemplate" />
|
||||
</bean>
|
||||
|
||||
<bean class="org.springframework.ldap.samples.utils.LdapTreeBuilder">
|
||||
<constructor-arg ref="ldapTemplate" />
|
||||
</bean>
|
||||
</beans>
|
||||
35
samples/odm/src/test/resources/setup_data.ldif
Normal file
35
samples/odm/src/test/resources/setup_data.ldif
Normal file
@@ -0,0 +1,35 @@
|
||||
dn: c=Sweden,dc=jayway,dc=se
|
||||
objectclass: top
|
||||
objectclass: country
|
||||
c: Sweden
|
||||
description: The country of Sweden
|
||||
|
||||
dn: ou=company1,c=Sweden,dc=jayway,dc=se
|
||||
objectclass: top
|
||||
objectclass: organizationalUnit
|
||||
ou: company1
|
||||
description: First company in Sweden
|
||||
|
||||
dn: cn=Some Person,ou=company1,c=Sweden,dc=jayway,dc=se
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
uid: some.person
|
||||
userPassword: password
|
||||
cn: Some Person
|
||||
sn: Person
|
||||
description: Sweden, Company1, Some Person
|
||||
telephoneNumber: +46 555-123456
|
||||
|
||||
dn: cn=Some Person2,ou=company1,c=Sweden,dc=jayway,dc=se
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
uid: some.person2
|
||||
userPassword: password
|
||||
cn: Some Person2
|
||||
sn: Person2
|
||||
description: Sweden, Company1, Some Person2
|
||||
telephoneNumber: +46 555-654321
|
||||
@@ -15,6 +15,7 @@ include 'test/integration-tests-openldap'
|
||||
include 'test/integration-tests-sunone'
|
||||
include 'test/integration-tests-ad'
|
||||
include 'samples/plain'
|
||||
include 'samples/odm'
|
||||
include 'samples/simple-odm'
|
||||
|
||||
rootProject.children.each { p->
|
||||
|
||||
@@ -208,7 +208,7 @@ public class PersonDaoImpl implements PersonDao {
|
||||
public static final String BASE_DN = "dc=example,dc=com";
|
||||
...
|
||||
protected Name buildDn(Person p) {
|
||||
<emphasis role="bold"> return LdapNameBuilder.newInstance(BASE_DN)
|
||||
<emphasis role="bold"> return LdapNameBuilder.newLdapName(BASE_DN)
|
||||
.add("c", p.getCountry())
|
||||
.add("ou", p.getCompany())
|
||||
.add("cn", p.getFullname())
|
||||
|
||||
@@ -296,7 +296,7 @@ public class PersonService implements PersonService, <emphasis role="bold">BaseL
|
||||
}</emphasis>
|
||||
...
|
||||
private LdapName getFullPersonDn(Person person) {
|
||||
return LdapNameBuilder.newInstance(<emphasis role="bold">basePath</emphasis>)
|
||||
return LdapNameBuilder.newLdapName(<emphasis role="bold">basePath</emphasis>)
|
||||
.append(person.getDn())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ public class PersonDaoImpl implements PersonDao {
|
||||
}
|
||||
|
||||
protected Name buildDn(String fullname, String company, String country) {
|
||||
return LdapNameBuilder.newInstance()
|
||||
return LdapNameBuilder.newLdapName()
|
||||
.add("c", country)
|
||||
.add("ou", company)
|
||||
.add("cn", fullname)
|
||||
|
||||
@@ -5,172 +5,122 @@
|
||||
<sect1 id="odm-intro">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>Relational mapping frameworks like Hibernate and JPA have offered
|
||||
developers the ability to use annotations to map database tables to Java
|
||||
objects for some time. The Spring Framework LDAP project now offers the
|
||||
same ability with respect to directories through the use of the
|
||||
<code>org.springframework.ldap.odm</code> package (sometimes abbreviated
|
||||
as <code>o.s.l.odm</code>).</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="odm-odmmanager">
|
||||
<title>OdmManager</title>
|
||||
|
||||
<para>The <code>org.springframework.ldap.odm.OdmManager</code> interface,
|
||||
and its implementation, is the central class in the ODM package. The
|
||||
<code>OdmManager</code> orchestrates the process of reading objects from
|
||||
the directory and mapping the data to annotated Java object classes. This
|
||||
interface provides access to the underlying directory instance through the
|
||||
following methods:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><code><T> T read(Class<T> clazz, Name
|
||||
dn)</code></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>void create(Object entry)</code></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>void update(Object entry)</code></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>void delete(Object entry)</code></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code><T> List<T> findAll(Class<T> clazz, Name
|
||||
base, SearchControls searchControls)</code></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code><T> List<T> search(Class<T> clazz, Name
|
||||
base, String filter, SearchControls searchControls)</code></para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><code><T> List<T> search(Class<T> clazz, LdapQuery query)</code></para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>A reference to an implementation of this interface can be obtained
|
||||
through the
|
||||
<code>org.springframework.ldap.odm.core.impl.OdmManagerImplFactoryBean</code>.
|
||||
A basic configuration of this factory would be as follows:</para>
|
||||
|
||||
<example>
|
||||
<title>Configuring the OdmManager Factory</title>
|
||||
<programlisting>
|
||||
<beans>
|
||||
...
|
||||
<bean id="odmManager"
|
||||
class="org.springframework.ldap.odm.core.impl.OdmManagerImplFactoryBean">
|
||||
<property name="converterManager" ref="converterManager" />
|
||||
<property name="contextSource" ref="contextSource" />
|
||||
<property name="managedClasses">
|
||||
<set>
|
||||
<value>com.example.dao.SimplePerson</value>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
...
|
||||
</beans>
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<para>The factory requires the list of entity classes to be managed by the
|
||||
<code>OdmManager</code> to be explicitly declared. These classes should be
|
||||
properly annotated as defined in the next section. The
|
||||
<code>converterManager</code> referenced in the above definition is
|
||||
described in <xref linkend="odm-typeconversion" />.</para>
|
||||
<para>
|
||||
Relational mapping frameworks like Hibernate and JPA have offered
|
||||
developers the ability to use annotations to map database tables to Java
|
||||
objects for some time. Spring LDAP project offers a similar
|
||||
ability with respect to directories through the use of a number of methods:
|
||||
in <literal>LdapOperations</literal>
|
||||
<itemizedlist>
|
||||
<listitem><literal><T> T findByDn(Name dn, Class<T> clazz)</literal></listitem>
|
||||
<listitem><literal><T> T findOne(LdapQuery query, Class<T> clazz)</literal></listitem>
|
||||
<listitem><literal><T> List<T> find(LdapQuery query, Class<T> clazz)</literal></listitem>
|
||||
<listitem><literal><T> List<T> findAll(Class<T> clazz)</literal></listitem>
|
||||
<listitem><literal><T> List<T> findAll(Name base, SearchControls searchControls,
|
||||
Class<T> clazz)</literal></listitem>
|
||||
<listitem><literal><T> List<T> findAll(Name base, Filter filter, SearchControls searchControls,
|
||||
Class<T> clazz)</literal></listitem>
|
||||
<listitem><literal>void create(Object entry)</literal></listitem>
|
||||
<listitem><literal>void update(Object entry)</literal></listitem>
|
||||
<listitem><literal>void delete(Object entry)</literal></listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="odm-annotations">
|
||||
<title>Annotations</title>
|
||||
|
||||
<para>Entity classes managed by the <code>OdmManager</code> are required
|
||||
to be annotated with the annotations in the
|
||||
<code>org.springframework.ldap.odm.annotations</code> package. The
|
||||
<para>Entity classes managed used with the object mapping methods are required
|
||||
to be annotated with annotations from the
|
||||
<literal>org.springframework.ldap.odm.annotations</literal> package. The
|
||||
available annotations are:</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para><code>@Entry</code> - Class level annotation indicating the
|
||||
<code>objectClass</code> definitions to which the entity
|
||||
<para><literal>@Entry</literal> - Class level annotation indicating the
|
||||
<literal>objectClass</literal> definitions to which the entity
|
||||
maps.<emphasis> (required)</emphasis></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>@Id</code> - Indicates the entity DN; the field declaring
|
||||
<para><literal>@Id</literal> - Indicates the entity DN; the field declaring
|
||||
this attribute must be a derivative of the
|
||||
<code>javax.naming.Name</code> class.
|
||||
<literal>javax.naming.Name</literal> class.
|
||||
<emphasis>(required)</emphasis></para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>@Attribute</code> - Indicates the mapping of a directory
|
||||
<para><literal>@Attribute</literal> - Indicates the mapping of a directory
|
||||
attribute to the object class field.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para><code>@Transient</code> - Indicates the field is not persistent
|
||||
and should be ignored by the <code>OdmManager</code>.</para>
|
||||
<para><literal>@Transient</literal> - Indicates the field is not persistent
|
||||
and should be ignored by the <literal>OdmManager</literal>.</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<simpara>The <code>@Entry</code> and <code>@Id</code> attributes are
|
||||
required to be declared on managed classes. <code>@Entry</code> is used to
|
||||
specify which object classes the entity maps too. All object classes for
|
||||
which fields are mapped are required to be declared. Also, in order for a
|
||||
directory entry to be considered a match to the managed entity, all object
|
||||
classes declared by the directory entry must match be declared by in the
|
||||
<code>@Entry</code> annotation.</simpara>
|
||||
<simpara>
|
||||
The <literal>@Entry</literal> and <literal>@Id</literal> attributes are
|
||||
required to be declared on managed classes.
|
||||
<literal>@Entry</literal> is used to specify which object classes the entity maps to.
|
||||
All object classes for which fields are mapped are required to be declared. Also, in order for a
|
||||
directory entry to be considered a match to the managed entity, all object
|
||||
classes declared by the directory entry must match be declared by in the
|
||||
<literal>@Entry</literal> annotation. For example: let's assume that you have entries in
|
||||
your LDAP tree that have the objectclasses<literal>inetOrgPerson,organizationalPerson,person,top</literal>.
|
||||
If you are only interested in changing the attributes defined in the <literal>person</literal>
|
||||
objectclass, your <literal>@Entry</literal> annotation can be
|
||||
<literal>@Entry(objectClasses = { "person", "top" })</literal>. However, if you want to manage
|
||||
attributes defined in the <literal>inetOrgPerson</literal> objectclass you'll need to use the full
|
||||
monty: <literal>@Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" })</literal>.
|
||||
</simpara>
|
||||
|
||||
<simpara>The <code>@Id</code> annotation is used to map the distinguished
|
||||
<simpara>The <literal>@Id</literal> annotation is used to map the distinguished
|
||||
name of the entry to a field. The field must be an instance of
|
||||
<code>javax.naming.Name</code> or a subclass of it.</simpara>
|
||||
<literal>javax.naming.Name</literal>.</simpara>
|
||||
|
||||
<simpara>The <code>@Attribute</code> annotation is used to map object
|
||||
class fields to entity fields. <code>@Attribute</code> is required to
|
||||
<simpara>The <literal>@Attribute</literal> annotation is used to map object
|
||||
class fields to entity fields. <literal>@Attribute</literal> is required to
|
||||
declare the name of the object class property to which the field maps and
|
||||
may optionally declare the syntax OID of the LDAP attribute, to guarantee
|
||||
exact matching. <code>@Attribute</code> also provides the type declaration
|
||||
exact matching. <literal>@Attribute</literal> also provides the type declaration
|
||||
which allows you to indicate whether the attribute is regarded as binary
|
||||
based or string based by the LDAP JNDI provider.</simpara>
|
||||
|
||||
<simpara>The <code>@Transient</code> annotation is used to indicate the
|
||||
field should be ignored by the <code>OdmManager</code> and not mapped to
|
||||
<simpara>The <literal>@Transient</literal> annotation is used to indicate the
|
||||
field should be ignored by the object directory mapping and not mapped to
|
||||
an underlying LDAP property.</simpara>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="odm-typeconversion">
|
||||
<title>Type Conversion</title>
|
||||
|
||||
<para>The <code>OdmManager</code> relies on the
|
||||
<code>org.springframework.ldap.odm.typeconversion</code> package to
|
||||
convert LDAP attributes to Java fields. The main interface in this class
|
||||
is the
|
||||
<code>org.springframework.ldap.odm.typeconversion.ConverterManager</code>.
|
||||
The default <code>ConverterManager</code> implementation uses the
|
||||
<para>The object directory mapping relies on the
|
||||
<literal>org.springframework.ldap.odm.typeconversion</literal> package to
|
||||
convert LDAP attributes to Java fields. For simple setups, no particular configuraion
|
||||
is needed for this purpose. However, more complex mapping scenarios require the
|
||||
<literal>ObjectDirectoryMapper</literal> and its associated <literal>ConverterManager</literal>
|
||||
to be explicitly configured on the <literal>LdapTemplate</literal> instance.
|
||||
|
||||
The default <literal>ConverterManager</literal> implementation uses the
|
||||
following algorithm when parsing objects to convert fields:<orderedlist>
|
||||
<listitem>
|
||||
<para>Try to find and use a <code>Converter</code> registered for
|
||||
the <code>fromClass</code>, <code>syntax</code> and
|
||||
<code>toClass</code> and use it.</para>
|
||||
<para>Try to find and use a <literal>Converter</literal> registered for
|
||||
the <literal>fromClass</literal>, <literal>syntax</literal> and
|
||||
<literal>toClass</literal> and use it.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>If this fails, then if the <code>toClass</code>
|
||||
<code>isAssignableFrom</code> the
|
||||
<code>fromClass</code> then just assign it.</para>
|
||||
<para>If this fails, then if the <literal>toClass</literal>
|
||||
<literal>isAssignableFrom</literal> the
|
||||
<literal>fromClass</literal> then just assign it.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>If this fails try to find and use a
|
||||
<code>Converter</code> registered for the
|
||||
<code>fromClass</code> and the <code>toClass</code> ignoring the
|
||||
<literal>Converter</literal> registered for the
|
||||
<literal>fromClass</literal> and the <literal>toClass</literal> ignoring the
|
||||
syntax.</para>
|
||||
</listitem>
|
||||
|
||||
@@ -252,6 +202,17 @@
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="ldapTemplate"
|
||||
class="org.springframework.ldap.core.LdapTemplate">
|
||||
<property name="objectDirectoryMapper">
|
||||
<bean class="org.springframework.ldap.odm.impl.DefaultObjectDirectoryMapper">
|
||||
<property name="converterManager" ref="converterManager" />
|
||||
</bean>
|
||||
</property>
|
||||
<!-- More configuration of LdapTemplate here -->
|
||||
</bean>
|
||||
|
||||
</programlisting>
|
||||
</example>
|
||||
</sect1>
|
||||
@@ -259,31 +220,47 @@
|
||||
<sect1 id="odm-execution">
|
||||
<title>Execution</title>
|
||||
|
||||
<para>After all components are configured, directory interaction can be
|
||||
achieved through a reference to the <code>OdmManager</code>, as shown in
|
||||
this example:</para>
|
||||
|
||||
<para>
|
||||
When all components have been properly configured and annotated, the object mapping
|
||||
methods of <literal>LdapTemplate</literal> can be used as follows:</para>
|
||||
<example>
|
||||
<title>Execution</title>
|
||||
|
||||
<programlisting>
|
||||
public class App {
|
||||
private static Log log = LogFactory.getLog(App.class);
|
||||
private static final SearchControls searchControls =
|
||||
new SearchControls(SearchControls.SUBTREE_SCOPE, 100, 10000, null, true, false);
|
||||
public static void main( String[] args ) {
|
||||
try {
|
||||
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
|
||||
OdmManager manager = (OdmManager) context.getBean("odmManager");
|
||||
List<SimplePerson> people = manager.search(SimplePerson.class,
|
||||
LdapUtils.newLdapName("dc=example,dc=com"), "uid=*", searchControls);
|
||||
log.info("People found: " + people.size());
|
||||
for (SimplePerson person : people) {
|
||||
log.info( person );
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
public class OdmPersonDao {
|
||||
@Autowired
|
||||
private LdapTemplate ldapTemplate;
|
||||
|
||||
public Person create(Person person) {
|
||||
person.setDn(buildDn(person));
|
||||
ldapTemplate.create(person);
|
||||
return person;
|
||||
}
|
||||
|
||||
private Name buildDn(Person person) {
|
||||
// build a distinguished name based on a person.
|
||||
}
|
||||
|
||||
public Person findByUid(String uid) {
|
||||
return ldapTemplate.findOne(query().where("uid").is(uid), Person.class);
|
||||
}
|
||||
|
||||
public void update(Person person) {
|
||||
// Requires that the Person was originally retrieved from this Dao, i.e. that the Dn is populated.
|
||||
ldapTemplate.update(person);
|
||||
}
|
||||
|
||||
public void delete(Person person) {
|
||||
// Requires that the Person was originally retrieved from this Dao, i.e. that the Dn is populated.
|
||||
ldapTemplate.delete(person);
|
||||
}
|
||||
|
||||
public List>Person< findAll() {
|
||||
return ldapTemplate.findAll(Person.class);
|
||||
}
|
||||
|
||||
public List>Person< findByLastName(String lastName) {
|
||||
return ldapTemplate.find(query().where("sn").is(lastName), Person.class);
|
||||
}
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package org.springframework.ldap.itest.odm;
|
||||
|
||||
import org.springframework.ldap.odm.annotations.Attribute;
|
||||
import org.springframework.ldap.odm.annotations.Entry;
|
||||
import org.springframework.ldap.odm.annotations.Id;
|
||||
|
||||
import javax.naming.Name;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
@Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" })
|
||||
public class Person {
|
||||
@Id
|
||||
private Name dn;
|
||||
|
||||
@Attribute(name = "cn")
|
||||
private String commonName;
|
||||
|
||||
@Attribute(name = "sn")
|
||||
private String surname;
|
||||
|
||||
@Attribute(name = "description")
|
||||
private List<String> desc;
|
||||
|
||||
@Attribute(name = "uid")
|
||||
private List<String> userId;
|
||||
|
||||
@Attribute(name = "telephoneNumber")
|
||||
private String telephoneNumber;
|
||||
|
||||
public Name getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
public void setDn(Name dn) {
|
||||
this.dn = dn;
|
||||
}
|
||||
|
||||
public String getCommonName() {
|
||||
return commonName;
|
||||
}
|
||||
|
||||
public void setCommonName(String commonName) {
|
||||
this.commonName = commonName;
|
||||
}
|
||||
|
||||
public String getSurname() {
|
||||
return surname;
|
||||
}
|
||||
|
||||
public void setSurname(String surname) {
|
||||
this.surname = surname;
|
||||
}
|
||||
|
||||
public List<String> getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(List<String> desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public List<String> getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(List<String> userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getTelephoneNumber() {
|
||||
return telephoneNumber;
|
||||
}
|
||||
|
||||
public void setTelephoneNumber(String telephoneNumber) {
|
||||
this.telephoneNumber = telephoneNumber;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2005-2013 the original author or authors.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.itest.odm;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.itest.AbstractLdapTemplateIntegrationTest;
|
||||
import org.springframework.ldap.odm.core.OdmException;
|
||||
import org.springframework.ldap.support.LdapNameBuilder;
|
||||
import org.springframework.ldap.support.LdapUtils;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.ldap.query.LdapQueryBuilder.query;
|
||||
|
||||
/**
|
||||
* @author Mattias Hellborg Arthursson
|
||||
*/
|
||||
@ContextConfiguration(locations = {"/conf/ldapTemplateTestContext.xml"})
|
||||
public class LdapTemplateOdmITest extends AbstractLdapTemplateIntegrationTest {
|
||||
@Autowired
|
||||
private LdapTemplate tested;
|
||||
|
||||
@Test
|
||||
public void testFindOne() {
|
||||
Person person = tested.findOne(query()
|
||||
.where("cn").is("Some Person3"), Person.class);
|
||||
|
||||
assertNotNull(person);
|
||||
assertEquals("Some Person3", person.getCommonName());
|
||||
assertEquals("Person3", person.getSurname());
|
||||
assertEquals("Sweden, Company1, Some Person3", person.getDesc().get(0));
|
||||
assertEquals("+46 555-123654", person.getTelephoneNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindByDn() {
|
||||
Person person = tested.findByDn(LdapUtils.newLdapName("cn=Some Person3,ou=company1,c=Sweden"), Person.class);
|
||||
|
||||
assertNotNull(person);
|
||||
assertEquals("Some Person3", person.getCommonName());
|
||||
assertEquals("Person3", person.getSurname());
|
||||
assertEquals("Sweden, Company1, Some Person3", person.getDesc().get(0));
|
||||
assertEquals("+46 555-123654", person.getTelephoneNumber());
|
||||
}
|
||||
|
||||
@Test(expected = OdmException.class)
|
||||
public void testFindByDnThrowsExceptionOnInvalidEntry() {
|
||||
tested.findByDn(LdapUtils.newLdapName("ou=company1,c=Sweden"), Person.class);
|
||||
}
|
||||
|
||||
@Test(expected = EmptyResultDataAccessException.class)
|
||||
public void testFindOneThrowsEmptyResultIfNotFound() {
|
||||
tested.findOne(query()
|
||||
.where("cn").is("This cn does not exist"), Person.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFind() {
|
||||
List<Person> persons = tested.find(query()
|
||||
.where("cn").is("Some Person3"), Person.class);
|
||||
|
||||
assertEquals(1, persons.size());
|
||||
Person person = persons.get(0);
|
||||
|
||||
assertNotNull(person);
|
||||
assertEquals("Some Person3", person.getCommonName());
|
||||
assertEquals("Person3", person.getSurname());
|
||||
assertEquals("Sweden, Company1, Some Person3", person.getDesc().get(0));
|
||||
assertEquals("+46 555-123654", person.getTelephoneNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindInCountry() {
|
||||
List<Person> persons = tested.find(query()
|
||||
.base("c=Sweden")
|
||||
.where("cn").isPresent(), Person.class);
|
||||
|
||||
assertEquals(4, persons.size());
|
||||
Person person = persons.get(0);
|
||||
|
||||
assertNotNull(person);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAll() {
|
||||
List<Person> result = tested.findAll(Person.class);
|
||||
assertEquals(5, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() {
|
||||
Person person = new Person();
|
||||
person.setDn(LdapNameBuilder.newLdapName("ou=company1,c=Sweden")
|
||||
.add("cn", "New Person").build());
|
||||
person.setCommonName("New Person");
|
||||
person.setSurname("Person");
|
||||
person.setDesc(Arrays.asList("This is the description"));
|
||||
person.setTelephoneNumber("0123456");
|
||||
|
||||
tested.create(person);
|
||||
|
||||
assertEquals(6, tested.findAll(Person.class).size());
|
||||
|
||||
person = tested.findOne(query()
|
||||
.where("cn").is("New Person"), Person.class);
|
||||
|
||||
assertEquals("New Person", person.getCommonName());
|
||||
assertEquals("Person", person.getSurname());
|
||||
assertEquals("This is the description", person.getDesc().get(0));
|
||||
assertEquals("0123456", person.getTelephoneNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
Person person = tested.findOne(query()
|
||||
.where("cn").is("Some Person3"), Person.class);
|
||||
|
||||
person.setDesc(Arrays.asList("New Description"));
|
||||
tested.update(person);
|
||||
|
||||
person = tested.findOne(query()
|
||||
.where("cn").is("Some Person3"), Person.class);
|
||||
|
||||
assertEquals("Some Person3", person.getCommonName());
|
||||
assertEquals("Person3", person.getSurname());
|
||||
assertEquals("New Description", person.getDesc().get(0));
|
||||
assertEquals("+46 555-123654", person.getTelephoneNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete() {
|
||||
Person person = tested.findOne(query()
|
||||
.where("cn").is("Some Person3"), Person.class);
|
||||
|
||||
tested.delete(person);
|
||||
|
||||
try {
|
||||
tested.findOne(query().where("cn").is("Some Person3"), Person.class);
|
||||
fail("EmptyResultDataAccessException e");
|
||||
} catch (EmptyResultDataAccessException e) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user