Added Paul Harvey's Spring LDAP Object-Directory Mapper (ODM).
This commit is contained in:
173
odm/pom.xml
Normal file
173
odm/pom.xml
Normal file
@@ -0,0 +1,173 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-parent</artifactId>
|
||||
<version>1.3.1.CI-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-odm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring LDAP ODM</name>
|
||||
<description>Object Directory Mapping framework</description>
|
||||
<url>http://springframework.org/ldap</url>
|
||||
<inceptionYear>2009</inceptionYear>
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Paul Harvey</name>
|
||||
<email>paul@pauls-place.me.uk</email>
|
||||
<roles>
|
||||
<role>Developer</role>
|
||||
</roles>
|
||||
<timezone>0</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.springframework.ldap.odm.tools.SchemaToJava</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
<configuration>
|
||||
<forkMode>always</forkMode>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-pmd-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
<configuration>
|
||||
<targetJdk>${java.version}</targetJdk>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
<repositories>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-core</artifactId>
|
||||
<version>${version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-core-tiger</artifactId>
|
||||
<version>${version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-pool</groupId>
|
||||
<artifactId>commons-pool</artifactId>
|
||||
<type>jar</type>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-test</artifactId>
|
||||
<version>${version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.15</version>
|
||||
<type>jar</type>
|
||||
<scope>runtime</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>jmxri</artifactId>
|
||||
<groupId>com.sun.jmx</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jms</artifactId>
|
||||
<groupId>javax.jms</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>jmxtools</artifactId>
|
||||
<groupId>com.sun.jdmk</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jdepend</groupId>
|
||||
<artifactId>jdepend</artifactId>
|
||||
<type>jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<type>jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<type>jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
65
odm/src/main/java/org/springframework/ldap/odm/annotations/Attribute.java
Executable file
65
odm/src/main/java/org/springframework/ldap/odm/annotations/Attribute.java
Executable file
@@ -0,0 +1,65 @@
|
||||
package org.springframework.ldap.odm.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* This annotation describes the mapping of a Java field to an LDAP attribute.
|
||||
* <p>
|
||||
* The containing class must be annotated with {@link Entry}.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
* @see Entry
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Attribute {
|
||||
/**
|
||||
* The Type attribute indicates whether a field is regarded as binary based
|
||||
* or string based by the LDAP JNDI provider.
|
||||
*/
|
||||
enum Type {
|
||||
/**
|
||||
* A string field - returned by the JNDI LDAP provider as a {@link java.lang.String}.
|
||||
*/
|
||||
STRING, /**
|
||||
* A binary field - returned by the JNDI LDAP provider as a <code>byte[]</code>.
|
||||
*/
|
||||
BINARY
|
||||
}
|
||||
|
||||
/**
|
||||
* The LDAP attribute name that this field represents.
|
||||
* <p>
|
||||
* Defaults to "" in which case the Java field name is used as the LDAP attribute name.
|
||||
*
|
||||
* @return The LDAP attribute name.
|
||||
*
|
||||
*/
|
||||
String name() default "";
|
||||
|
||||
/**
|
||||
* Indicates whether this field is returned by the LDAP JNDI provider as a
|
||||
* <code>String</code> (<code>Type.STRING</code>) or as a
|
||||
* <code>byte[]</code> (<code>Type.BINARY</code>).
|
||||
*
|
||||
* @return Either <code>Type.STRING</code> to indicate a string attribute
|
||||
* or <code>Type.BINARY</code> to indicate a binary attribute.
|
||||
*/
|
||||
Type type() default Type.STRING;
|
||||
|
||||
/**
|
||||
* The LDAP syntax of the attribute that this field represents.
|
||||
* <p>
|
||||
* This optional value is typically used to affect the precision of conversion
|
||||
* of values between LDAP and Java,
|
||||
* see {@link org.springframework.ldap.odm.typeconversion.ConverterManager}
|
||||
* and {@link org.springframework.ldap.odm.typeconversion.impl.ConverterManagerImpl}.
|
||||
*
|
||||
* @return The LDAP syntax of this attribute.
|
||||
*/
|
||||
String syntax() default "";
|
||||
}
|
||||
25
odm/src/main/java/org/springframework/ldap/odm/annotations/Entry.java
Executable file
25
odm/src/main/java/org/springframework/ldap/odm/annotations/Entry.java
Executable file
@@ -0,0 +1,25 @@
|
||||
package org.springframework.ldap.odm.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation marks a Java class to be persisted in an LDAP directory.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Entry {
|
||||
/**
|
||||
* A list of LDAP object classes that the annotated Java class represents.
|
||||
* <p>
|
||||
* All fields will be persisted to LDAP unless annotated {@link Transient}.
|
||||
*
|
||||
* @return A list of LDAP classes which the annotated Java class represents.
|
||||
*/
|
||||
String[] objectClasses();
|
||||
}
|
||||
23
odm/src/main/java/org/springframework/ldap/odm/annotations/Id.java
Executable file
23
odm/src/main/java/org/springframework/ldap/odm/annotations/Id.java
Executable file
@@ -0,0 +1,23 @@
|
||||
package org.springframework.ldap.odm.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* This annotation marks a Java field as containing the Distinguished Name of an LDAP Entry.
|
||||
* <p>
|
||||
* The marked field must be of type {@link javax.naming.Name} and must <em>not</em>
|
||||
* be annotated {@link Attribute}.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
* @see Attribute
|
||||
* @see javax.naming.Name
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Id {
|
||||
}
|
||||
19
odm/src/main/java/org/springframework/ldap/odm/annotations/Transient.java
Executable file
19
odm/src/main/java/org/springframework/ldap/odm/annotations/Transient.java
Executable file
@@ -0,0 +1,19 @@
|
||||
package org.springframework.ldap.odm.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* This annotation identifies a field in an {@link Entry} annotated class that
|
||||
* should <em>not</em> be persisted to LDAP.
|
||||
*
|
||||
* @author Paul Harvey <paul@pauls-place.me.uk>
|
||||
*
|
||||
* @see Entry
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Transient {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Provides a set of annotations to describe the mapping of a Java class to an LDAP entry.
|
||||
* <p>
|
||||
* These annotations are for use with {@link org.springframework.ldap.odm.core.OdmManager}.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.annotations;
|
||||
20
odm/src/main/java/org/springframework/ldap/odm/core/OdmException.java
Executable file
20
odm/src/main/java/org/springframework/ldap/odm/core/OdmException.java
Executable file
@@ -0,0 +1,20 @@
|
||||
package org.springframework.ldap.odm.core;
|
||||
|
||||
import org.springframework.ldap.NamingException;
|
||||
|
||||
/**
|
||||
* The root of the Spring LDAP ODM exception hierarchy.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class OdmException extends NamingException {
|
||||
public OdmException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public OdmException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
97
odm/src/main/java/org/springframework/ldap/odm/core/OdmManager.java
Executable file
97
odm/src/main/java/org/springframework/ldap/odm/core/OdmManager.java
Executable file
@@ -0,0 +1,97 @@
|
||||
package org.springframework.ldap.odm.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.Name;
|
||||
import javax.naming.directory.SearchControls;
|
||||
|
||||
/**
|
||||
* The OdmManager interface provides generic CRUD (create/read/update/delete)
|
||||
* and searching operations against an LDAP directory.
|
||||
* <p>
|
||||
* Each managed Java class must be appropriately annotated using
|
||||
* {@link org.springframework.ldap.odm.annotations}.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
* @see org.springframework.ldap.odm.annotations.Entry
|
||||
* @see org.springframework.ldap.odm.annotations.Attribute
|
||||
* @see org.springframework.ldap.odm.annotations.Id
|
||||
* @see org.springframework.ldap.odm.annotations.Transient
|
||||
*/
|
||||
public interface OdmManager {
|
||||
|
||||
|
||||
/**
|
||||
* Read a named entry from the LDAP directory.
|
||||
*
|
||||
* @param <T> The Java type to return
|
||||
* @param clazz The Java type to return
|
||||
* @param dn The distinguished name of the entry to read from the LDAP directory.
|
||||
* @return The entry as read from the directory
|
||||
*
|
||||
* @exception org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
<T> T read(Class<T> clazz, Name dn);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @exception org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @exception org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
void update(Object entry);
|
||||
|
||||
/**
|
||||
* Delete an entry from the LDAP directory.
|
||||
*
|
||||
* @param entry The entry to delete, it must already exist in the directory.
|
||||
*
|
||||
* @exception org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
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
|
||||
* @param base The root of the sub-tree at which to begin the search.
|
||||
* @param searchControls The scope of the search.
|
||||
* @return All entries that are of the type represented by the given
|
||||
* Java class
|
||||
*
|
||||
* @exception org.springframework.ldap.NamingException on error.
|
||||
*/
|
||||
<T> List<T> findAll(Class<T> clazz, Name base, SearchControls searchControls);
|
||||
|
||||
/**
|
||||
* Search for entries in the LDAP directory.
|
||||
* <p>
|
||||
* Only those entries that both match the given search filter and
|
||||
* are represented by the given Java class are returned
|
||||
*
|
||||
* @param <T> The Java type to return
|
||||
* @param clazz The Java type to return
|
||||
* @param base The root of the sub-tree at which to begin the search.
|
||||
* @param filter An LDAP search filter.
|
||||
* @param searchControls The scope of the search.
|
||||
* @return All matching entries.
|
||||
*
|
||||
* @exception org.springframework.ldap.NamingException on error.
|
||||
*
|
||||
* @see <a href="http://java.sun.com/products/jndi/tutorial/basics/directory/filter.html">Sun's JNDI tutorial description of search filters.</a>
|
||||
* @see <a href="http://www.rfc-editor.org/rfc/rfc4515.txt">LDAP: String Representation of Search Filters RFC.</a>
|
||||
*/
|
||||
<T> List<T> search(Class<T> clazz, Name base, String filter, SearchControls searchControls);
|
||||
}
|
||||
226
odm/src/main/java/org/springframework/ldap/odm/core/impl/AttributeMetaData.java
Executable file
226
odm/src/main/java/org/springframework/ldap/odm/core/impl/AttributeMetaData.java
Executable file
@@ -0,0 +1,226 @@
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
import org.springframework.ldap.odm.annotations.Attribute;
|
||||
import org.springframework.ldap.odm.annotations.Id;
|
||||
|
||||
/*
|
||||
* Extract attribute meta-data from the @Attribute annotation, the @Id annotation
|
||||
* and via reflection.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
/* package */ final class AttributeMetaData {
|
||||
private static final CaseIgnoreString OBJECT_CLASS_ATTRIBUTE_CI=new CaseIgnoreString("objectclass");
|
||||
|
||||
// Name of the LDAP attribute from the @Attribute annotation
|
||||
private CaseIgnoreString name;
|
||||
|
||||
// Syntax of the LDAP attribute from the @Attribute annotation
|
||||
private String syntax;
|
||||
|
||||
// Whether this attribute is binary from the @Attribute annotation
|
||||
private boolean isBinary;
|
||||
|
||||
// The Java field corresponding to this meta-data
|
||||
private final Field field;
|
||||
|
||||
// The Java class of the field corresponding to this meta data
|
||||
// This is the actual scalar type meaning that if the field is
|
||||
// List<String> then the valueClass will be String
|
||||
private Class<?> valueClass;
|
||||
|
||||
// Is this field annotated @Id
|
||||
private boolean isId;
|
||||
|
||||
// Is this field multi-valued represented by a List
|
||||
private boolean isList;
|
||||
|
||||
// Is this the objectClass attribute
|
||||
private boolean isObjectClass;
|
||||
|
||||
// Extract information from the @Attribute annotation:
|
||||
// syntax, isBinary, isObjectClass and name.
|
||||
private boolean processAttributeAnnotation(Field field) {
|
||||
// Default to no syntax specified
|
||||
syntax = "";
|
||||
|
||||
// Default to a String based attribute
|
||||
isBinary = false;
|
||||
|
||||
// Default name of attribute to the name of the field
|
||||
name = new CaseIgnoreString(field.getName());
|
||||
|
||||
// We have not yet found the @Attribute annotation
|
||||
boolean foundAnnotation=false;
|
||||
|
||||
// Grab the @Attribute annotation
|
||||
Attribute attribute = field.getAnnotation(Attribute.class);
|
||||
|
||||
// Did we find the annotation?
|
||||
if (attribute != null) {
|
||||
// Pull attribute name, syntax and whether attribute is binary
|
||||
// from the annotation
|
||||
foundAnnotation=true;
|
||||
String localAttributeName = attribute.name();
|
||||
// Would be more efficient to use !isEmpty - but that then makes us Java 6 dependent
|
||||
if (localAttributeName != null && localAttributeName.length()>0) {
|
||||
name = new CaseIgnoreString(localAttributeName);
|
||||
}
|
||||
syntax = attribute.syntax();
|
||||
isBinary = attribute.type() == Attribute.Type.BINARY;
|
||||
}
|
||||
|
||||
isObjectClass=name.equals(OBJECT_CLASS_ATTRIBUTE_CI);
|
||||
|
||||
return foundAnnotation;
|
||||
}
|
||||
|
||||
// Extract reflection information from the field:
|
||||
// valueClass, isList
|
||||
private void determineFieldType(Field field) {
|
||||
// Determine the class of data stored in the field
|
||||
Class<?> fieldType = field.getType();
|
||||
|
||||
// We support only lists for multi-valued attributes, as we must allow duplicate values
|
||||
if (Set.class.isAssignableFrom(fieldType)) {
|
||||
throw new MetaDataException(String.format("Only lists are allowed for multivlaued attributes, errpr in field %1$s in Entry class %2$s",
|
||||
field, field.getDeclaringClass()));
|
||||
}
|
||||
isList = List.class.isAssignableFrom(fieldType);
|
||||
|
||||
valueClass=null;
|
||||
if (!isList) {
|
||||
// It's not a list so assume its single valued - so just take the field type
|
||||
valueClass = fieldType;
|
||||
} else {
|
||||
// It's multi-valued - so we need to look at the signature in
|
||||
// the class file to find the generic type - this is supported for class file
|
||||
// format 49 and greater which corresponds to java 5 and later.
|
||||
ParameterizedType paramType;
|
||||
try {
|
||||
paramType = (ParameterizedType)field.getGenericType();
|
||||
} catch (ClassCastException e) {
|
||||
throw new MetaDataException(String.format("Can't determine destination type for field %1$s in Entry class %2$s",
|
||||
field, field.getDeclaringClass()), e);
|
||||
}
|
||||
Type[] actualParamArguments = paramType.getActualTypeArguments();
|
||||
if (actualParamArguments.length == 1) {
|
||||
if (actualParamArguments[0] instanceof Class) {
|
||||
valueClass = (Class<?>)actualParamArguments[0];
|
||||
} else {
|
||||
if (actualParamArguments[0] instanceof GenericArrayType) {
|
||||
// Deal with arrays
|
||||
Type type=((GenericArrayType)actualParamArguments[0]).getGenericComponentType();
|
||||
if (type instanceof Class) {
|
||||
valueClass=Array.newInstance((Class<?>)type, 0).getClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check we have been able to determine the value class
|
||||
if (valueClass==null) {
|
||||
throw new MetaDataException(String.format("Can't determine destination type for field %1$s in class %2$s",
|
||||
field, field.getDeclaringClass()));
|
||||
}
|
||||
}
|
||||
|
||||
// Extract information from the @Id annotation:
|
||||
// isId
|
||||
private boolean processIdAnnotation(Field field, Class<?> fieldType) {
|
||||
// Are we dealing with the Id field?
|
||||
isId=field.getAnnotation(Id.class)!=null;
|
||||
|
||||
if (isId) {
|
||||
// It must be of type Name or a subclass of that of
|
||||
if (!Name.class.isAssignableFrom(fieldType)) {
|
||||
throw new MetaDataException(
|
||||
String.format("The id field must be of type javax.naming.Name or a subclass that of in Entry class %1$s",
|
||||
field.getDeclaringClass()));
|
||||
}
|
||||
}
|
||||
|
||||
return isId;
|
||||
}
|
||||
|
||||
// Extract meta-data from the given field
|
||||
public AttributeMetaData(Field field) {
|
||||
this.field=field;
|
||||
|
||||
// Reflection data
|
||||
determineFieldType(field);
|
||||
|
||||
// Data from the @Attribute annotation
|
||||
boolean foundAttributeAnnotation=processAttributeAnnotation(field);
|
||||
|
||||
// Data from the @Id annotation
|
||||
boolean foundIdAnnoation=processIdAnnotation(field, valueClass);
|
||||
|
||||
// Check that the field has not been annotated with both @Attribute and with @Id
|
||||
if (foundAttributeAnnotation && foundIdAnnoation) {
|
||||
throw new MetaDataException(
|
||||
String.format("You may not specifiy an %1$s annoation and an %2$s annotation on the same field, error in field %3$s in Entry class %4$s",
|
||||
Id.class, Attribute.class, field.getName(), field.getDeclaringClass()));
|
||||
}
|
||||
|
||||
// If this is the objectclass attribute then it must be of type List<String>
|
||||
if (isObjectClass() && (!isList() || valueClass!=String.class)) {
|
||||
throw new MetaDataException(String.format("The type of the objectclass attribute must be List<String> in classs %1$s",
|
||||
field.getDeclaringClass()));
|
||||
}
|
||||
}
|
||||
|
||||
public String getSyntax() {
|
||||
return syntax;
|
||||
}
|
||||
|
||||
public boolean isBinary() {
|
||||
return isBinary;
|
||||
}
|
||||
|
||||
public Field getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public CaseIgnoreString getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isList() {
|
||||
return isList;
|
||||
}
|
||||
|
||||
public boolean isId() {
|
||||
return isId;
|
||||
}
|
||||
|
||||
public boolean isObjectClass() {
|
||||
return isObjectClass;
|
||||
}
|
||||
|
||||
public Class<?> getValueClass() {
|
||||
return valueClass;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("name=%1$s | field=%2$s | valueClass=%3$s | syntax=%4$s| isBinary=%5$s | isId=%6$s | isList=%7$s | isObjectClass=%8$s",
|
||||
getName(), getField(), getValueClass().getName(), getSyntax(), isBinary(), isId(), isList(), isObjectClass());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
// A case independent String wrapper.
|
||||
/* package */ final class CaseIgnoreString implements Comparable<CaseIgnoreString> {
|
||||
private final String string;
|
||||
private final int hashCode;
|
||||
|
||||
public CaseIgnoreString(String string) {
|
||||
if (string == null)
|
||||
throw new NullPointerException();
|
||||
this.string = string;
|
||||
hashCode = string.toUpperCase().hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof CaseIgnoreString &&
|
||||
((CaseIgnoreString)other).string.equalsIgnoreCase(string);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public int compareTo(CaseIgnoreString other) {
|
||||
CaseIgnoreString cis = (CaseIgnoreString)other;
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(string, cis.string);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
import org.springframework.ldap.odm.core.OdmException;
|
||||
|
||||
/**
|
||||
* Thrown to indicate that an instance is not suitable for persisting in the LDAP directory.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class InvalidEntryException extends OdmException {
|
||||
public InvalidEntryException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidEntryException(String message, Throwable reason) {
|
||||
super(message, reason);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
import org.springframework.ldap.odm.core.OdmException;
|
||||
|
||||
/**
|
||||
* Thrown to indicate an error in the annotated meta-data.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MetaDataException extends OdmException {
|
||||
public MetaDataException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public MetaDataException(String message, Throwable reason) {
|
||||
super(message, reason);
|
||||
}
|
||||
}
|
||||
124
odm/src/main/java/org/springframework/ldap/odm/core/impl/ObjectMetaData.java
Executable file
124
odm/src/main/java/org/springframework/ldap/odm/core/impl/ObjectMetaData.java
Executable file
@@ -0,0 +1,124 @@
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.ldap.odm.annotations.Entry;
|
||||
import org.springframework.ldap.odm.annotations.Id;
|
||||
import org.springframework.ldap.odm.annotations.Transient;
|
||||
|
||||
/*
|
||||
* An internal class to process the meta-data and reflection data for an entry.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
/* package */ final class ObjectMetaData implements Iterable<Field> {
|
||||
private static final Log LOG = LogFactory.getLog(ObjectMetaData.class);
|
||||
|
||||
private AttributeMetaData idAttribute;
|
||||
|
||||
private Map<Field, AttributeMetaData> fieldToAttribute = new HashMap<Field, AttributeMetaData>();
|
||||
|
||||
private Set<CaseIgnoreString> objectClasses = new HashSet<CaseIgnoreString>();
|
||||
|
||||
public Set<CaseIgnoreString> getObjectClasses() {
|
||||
return objectClasses;
|
||||
}
|
||||
|
||||
public AttributeMetaData getIdAttribute() {
|
||||
return idAttribute;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
public Iterator<Field> iterator() {
|
||||
return fieldToAttribute.keySet().iterator();
|
||||
}
|
||||
|
||||
public AttributeMetaData getAttribute(Field field) {
|
||||
return fieldToAttribute.get(field);
|
||||
}
|
||||
|
||||
public ObjectMetaData(Class<?> clazz) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Extracting metadata from %1$s", clazz));
|
||||
}
|
||||
|
||||
// Get object class metadata - the @Entity annotation
|
||||
Entry entity = (Entry)clazz.getAnnotation(Entry.class);
|
||||
if (entity != null) {
|
||||
// Default objectclass name to the class name unless it's specified
|
||||
// in @Entity(name={objectclass1, objectclass2});
|
||||
String[] localObjectClasses = entity.objectClasses();
|
||||
if (localObjectClasses != null && localObjectClasses.length > 0 && localObjectClasses[0].length() > 0) {
|
||||
for (String localObjectClass:localObjectClasses) {
|
||||
objectClasses.add(new CaseIgnoreString(localObjectClass));
|
||||
}
|
||||
} else {
|
||||
objectClasses.add(new CaseIgnoreString(clazz.getSimpleName()));
|
||||
}
|
||||
} else {
|
||||
throw new MetaDataException(String.format("Class %1$s must have a class level %2$s annotation", clazz,
|
||||
Entry.class));
|
||||
}
|
||||
|
||||
// Check the class is final
|
||||
if (!Modifier.isFinal(clazz.getModifiers())) {
|
||||
LOG.warn(String.format("The Entry class %1$s should be declared final", clazz.getSimpleName()));
|
||||
}
|
||||
|
||||
// Get field meta-data - the @Attribute annotation
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
// So we can write to private fields
|
||||
field.setAccessible(true);
|
||||
|
||||
// Skip transient and synthetic fields
|
||||
if (field.getAnnotation(Transient.class) != null || field.isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AttributeMetaData currentAttributeMetaData=new AttributeMetaData(field);
|
||||
if (currentAttributeMetaData.isId()) {
|
||||
if (idAttribute!=null) {
|
||||
// There can be only one id field
|
||||
throw new MetaDataException(
|
||||
String.format("You man have only one field with the %1$s annotation in class %2$s", Id.class, clazz));
|
||||
}
|
||||
idAttribute=currentAttributeMetaData;
|
||||
}
|
||||
fieldToAttribute.put(field, currentAttributeMetaData);
|
||||
}
|
||||
|
||||
if (idAttribute == null) {
|
||||
throw new MetaDataException(
|
||||
String.format("All Entry classes must define a field with the %1$s annotation, error in class %2$s", Id.class,
|
||||
clazz));
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Extracted metadata from %1$s as %2$s", clazz, this));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("objectsClasses=%1$s | idField=%2$s | attributes=%3$s",
|
||||
objectClasses, idAttribute.getName(), fieldToAttribute);
|
||||
}
|
||||
}
|
||||
474
odm/src/main/java/org/springframework/ldap/odm/core/impl/OdmManagerImpl.java
Executable file
474
odm/src/main/java/org/springframework/ldap/odm/core/impl/OdmManagerImpl.java
Executable file
@@ -0,0 +1,474 @@
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
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 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 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.DistinguishedName;
|
||||
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.odm.core.OdmManager;
|
||||
import org.springframework.ldap.odm.typeconversion.ConverterManager;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.springframework.ldap.odm.core.OdmManager} which
|
||||
* uses {@link org.springframework.ldap.odm.typeconversion.ConverterManager} to
|
||||
* convert between Java and LDAP representations of attribute values.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
// 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>();
|
||||
|
||||
public OdmManagerImpl(ConverterManager converterManager,
|
||||
ContextSource contextSource,
|
||||
Set<Class<?>> managedClasses) {
|
||||
|
||||
this.converterManager=converterManager;
|
||||
this.ldapTemplate=new LdapTemplate(contextSource);
|
||||
|
||||
if (managedClasses!=null) {
|
||||
for (Class<?> managedClass: managedClasses) {
|
||||
addManagedClass(managedClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OdmManagerImpl(ConverterManager converterManager,
|
||||
ContextSource contextSource) {
|
||||
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.
|
||||
*
|
||||
* @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()));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @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 has excess object classes", dn));
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Found entry - %s$1", result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @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));
|
||||
}
|
||||
|
||||
DirContextAdapter context = new DirContextAdapter(getId(entry));
|
||||
mapToContext(entry, context);
|
||||
ldapTemplate.rebind(context);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @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));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// Search from the root if we are not told where to search from
|
||||
Name localBase = base;
|
||||
if (base == null || base.size() == 0) {
|
||||
localBase = DistinguishedName.EMPTY_PATH;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.ldap.odm.core.OdmManager#findAll(javax.naming.Name, javax.naming.directory.SearchControls)
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// Object classes are set from the metadata obtained from the @Entity annotation
|
||||
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 (!objectClassesFromJndi.equals(metaData.getObjectClasses())) {
|
||||
// The items found has classes in addition to those searched for - so ditch it
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.odm.typeconversion.ConverterManager;
|
||||
|
||||
/**
|
||||
* A Spring Factory bean which creates {@link OdmManagerImpl} instances.
|
||||
* <p>
|
||||
* Typical configuration would appear as follows:
|
||||
* <pre>
|
||||
* <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>org.myorg.myldapentries.Person</value>
|
||||
* <value>org.myorg.myldapentries.OrganizationalUnit</value>
|
||||
* </set>
|
||||
* </property>
|
||||
* </bean>
|
||||
* </pre>
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public final class OdmManagerImplFactoryBean implements FactoryBean {
|
||||
private ContextSource contextSource=null;
|
||||
private Set<Class<?>> managedClasses=null;
|
||||
private ConverterManager converterManager=null;
|
||||
|
||||
/**
|
||||
* Set the ContextSource to use to interact with the LDAP directory.
|
||||
* @param contextSource The ContextSource to use.
|
||||
*/
|
||||
public void setContextSource(ContextSource contextSource) {
|
||||
this.contextSource=contextSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of {@link org.springframework.ldap.odm.annotations}
|
||||
* annotated classes the OdmManager will process.
|
||||
* @param managedClasses The list of classes to manage.
|
||||
*/
|
||||
public void setManagedClasses(Set<Class<?>> managedClasses) {
|
||||
this.managedClasses=managedClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ConverterManager to use to convert between LDAP
|
||||
* and Java representations of attributes.
|
||||
* @param converterManager The ConverterManager to use.
|
||||
*/
|
||||
public void setConverterManager(ConverterManager converterManager) {
|
||||
this.converterManager=converterManager;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
public Object getObject() throws Exception {
|
||||
if (contextSource==null) {
|
||||
throw new FactoryBeanNotInitializedException("contextSource property has not been set");
|
||||
}
|
||||
if (managedClasses==null) {
|
||||
throw new FactoryBeanNotInitializedException("managedClasses property has not been set");
|
||||
}
|
||||
if (converterManager==null) {
|
||||
throw new FactoryBeanNotInitializedException("converterManager property has not been set");
|
||||
}
|
||||
|
||||
return new OdmManagerImpl(converterManager, contextSource, managedClasses);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
public Class<?> getObjectType() {
|
||||
return OdmManagerImpl.class;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
|
||||
*/
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
|
||||
import org.springframework.ldap.odm.core.OdmException;
|
||||
|
||||
/**
|
||||
* Thrown when an OdmManager method is called with a class
|
||||
* which is not being managed by the OdmManager.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class UnmanagedClassException extends OdmException {
|
||||
public UnmanagedClassException(String message, Throwable reason) {
|
||||
super(message, reason);
|
||||
}
|
||||
|
||||
public UnmanagedClassException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Provides a single public class which implements {@link org.springframework.ldap.odm.core.OdmManager}.
|
||||
* <p>
|
||||
* The OdmManager implementation works in conjunction with {@link org.springframework.ldap.odm.typeconversion} to provide
|
||||
* conversion between the representation of attributes in LDAP and in Java.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
package org.springframework.ldap.odm.core.impl;
|
||||
10
odm/src/main/java/org/springframework/ldap/odm/core/package-info.java
Executable file
10
odm/src/main/java/org/springframework/ldap/odm/core/package-info.java
Executable file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Provides an OdmManager interface for interaction with an LDAP directory.
|
||||
* <p>
|
||||
* Implementations of this interface are intended to be used in conjunction with classes
|
||||
* annotated with {@link org.springframework.ldap.odm.annotations}.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.core;
|
||||
127
odm/src/main/java/org/springframework/ldap/odm/tools/AttributeSchema.java
Executable file
127
odm/src/main/java/org/springframework/ldap/odm/tools/AttributeSchema.java
Executable file
@@ -0,0 +1,127 @@
|
||||
package org.springframework.ldap.odm.tools;
|
||||
|
||||
/**
|
||||
* Simple value class to hold the schema of an attribute.
|
||||
* <p>
|
||||
* It is only public to allow Freemarker access.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public final class AttributeSchema {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final String syntax;
|
||||
|
||||
private final boolean isMultiValued;
|
||||
|
||||
private final boolean isPrimitive;
|
||||
|
||||
private final String scalarType;
|
||||
|
||||
private final boolean isBinary;
|
||||
|
||||
private final boolean isArray;
|
||||
|
||||
public AttributeSchema(final String name, final String syntax, final boolean isMultiValued,
|
||||
final boolean isPrimitive, final boolean isBinary, final boolean isArray, final String scalarType) {
|
||||
this.name = name;
|
||||
this.syntax = syntax;
|
||||
this.isMultiValued = isMultiValued;
|
||||
this.isPrimitive = isPrimitive;
|
||||
this.scalarType = scalarType;
|
||||
this.isBinary = isBinary;
|
||||
this.isArray = isArray;
|
||||
}
|
||||
|
||||
public boolean getIsArray() {
|
||||
return isArray;
|
||||
}
|
||||
|
||||
public boolean getIsBinary() {
|
||||
return isBinary;
|
||||
}
|
||||
|
||||
public boolean getIsPrimitive() {
|
||||
return isPrimitive;
|
||||
}
|
||||
|
||||
public String getScalarType() {
|
||||
return scalarType;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getSyntax() {
|
||||
return syntax;
|
||||
}
|
||||
|
||||
public boolean getIsMultiValued() {
|
||||
return isMultiValued;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return String.format(
|
||||
"{ name=%1$s, syntax=%2$s, isMultiValued=%3$s, isPrimitive=%4$s, isBinary=%5$s, isArray=%6$s, scalarType=%7$s }",
|
||||
name, syntax, isMultiValued, isPrimitive, isBinary, isArray, scalarType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (isArray ? 1231 : 1237);
|
||||
result = prime * result + (isBinary ? 1231 : 1237);
|
||||
result = prime * result + (isMultiValued ? 1231 : 1237);
|
||||
result = prime * result + (isPrimitive ? 1231 : 1237);
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
result = prime * result + ((scalarType == null) ? 0 : scalarType.hashCode());
|
||||
result = prime * result + ((syntax == null) ? 0 : syntax.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
AttributeSchema other = (AttributeSchema) obj;
|
||||
if (isArray != other.isArray)
|
||||
return false;
|
||||
if (isBinary != other.isBinary)
|
||||
return false;
|
||||
if (isMultiValued != other.isMultiValued)
|
||||
return false;
|
||||
if (isPrimitive != other.isPrimitive)
|
||||
return false;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
if (scalarType == null) {
|
||||
if (other.scalarType != null)
|
||||
return false;
|
||||
} else if (!scalarType.equals(other.scalarType))
|
||||
return false;
|
||||
if (syntax == null) {
|
||||
if (other.syntax != null)
|
||||
return false;
|
||||
} else if (!syntax.equals(other.syntax))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
91
odm/src/main/java/org/springframework/ldap/odm/tools/ObjectSchema.java
Executable file
91
odm/src/main/java/org/springframework/ldap/odm/tools/ObjectSchema.java
Executable file
@@ -0,0 +1,91 @@
|
||||
package org.springframework.ldap.odm.tools;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Simple value class to hold the schema of an object class
|
||||
* <p>
|
||||
* It is public only to allow Freemarker access.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public final class ObjectSchema {
|
||||
private final Set<AttributeSchema> must = new HashSet<AttributeSchema>();
|
||||
|
||||
private final Set<AttributeSchema> may = new HashSet<AttributeSchema>();
|
||||
|
||||
private final Set<String> objectClass = new HashSet<String>();
|
||||
|
||||
public void addMust(AttributeSchema must) {
|
||||
this.must.add(must);
|
||||
}
|
||||
|
||||
public void addMay(AttributeSchema may) {
|
||||
this.may.add(may);
|
||||
}
|
||||
|
||||
public void addObjectClass(String objectClass) {
|
||||
this.objectClass.add(objectClass);
|
||||
}
|
||||
|
||||
public Set<AttributeSchema> getMust() {
|
||||
return Collections.unmodifiableSet(must);
|
||||
}
|
||||
|
||||
public Set<AttributeSchema> getMay() {
|
||||
return Collections.unmodifiableSet(may);
|
||||
}
|
||||
|
||||
public Set<String> getObjectClass() {
|
||||
return Collections.unmodifiableSet(objectClass);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("objectClass=%1$s | must=%2$s | may=%3$s", objectClass, must, may);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((may == null) ? 0 : may.hashCode());
|
||||
result = prime * result + ((must == null) ? 0 : must.hashCode());
|
||||
result = prime * result + ((objectClass == null) ? 0 : objectClass.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ObjectSchema other = (ObjectSchema) obj;
|
||||
if (may == null) {
|
||||
if (other.may != null)
|
||||
return false;
|
||||
} else if (!may.equals(other.may))
|
||||
return false;
|
||||
if (must == null) {
|
||||
if (other.must != null)
|
||||
return false;
|
||||
} else if (!must.equals(other.must))
|
||||
return false;
|
||||
if (objectClass == null) {
|
||||
if (other.objectClass != null)
|
||||
return false;
|
||||
} else if (!objectClass.equals(other.objectClass))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
157
odm/src/main/java/org/springframework/ldap/odm/tools/SchemaReader.java
Executable file
157
odm/src/main/java/org/springframework/ldap/odm/tools/SchemaReader.java
Executable file
@@ -0,0 +1,157 @@
|
||||
package org.springframework.ldap.odm.tools;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.DirContext;
|
||||
|
||||
import org.springframework.ldap.odm.tools.SyntaxToJavaClass.ClassInfo;
|
||||
|
||||
// Processes LDAP Schema
|
||||
/* package */ final class SchemaReader {
|
||||
private final DirContext schemaContext;
|
||||
|
||||
private final SyntaxToJavaClass syntaxToJavaClass;
|
||||
|
||||
private final Set<String> binarySet;
|
||||
|
||||
public SchemaReader(DirContext schemaContext, SyntaxToJavaClass syntaxToJavaClass, Set<String> binarySet) {
|
||||
this.schemaContext = schemaContext;
|
||||
this.syntaxToJavaClass = syntaxToJavaClass;
|
||||
this.binarySet = binarySet;
|
||||
}
|
||||
|
||||
// Get the object schema for the given object classes
|
||||
public ObjectSchema getObjectSchema(Set<String> objectClasses)
|
||||
throws NamingException, ClassNotFoundException {
|
||||
|
||||
ObjectSchema result = new ObjectSchema();
|
||||
createObjectClass(objectClasses, schemaContext, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private enum SchemaAttributeType {
|
||||
SUP, MUST, MAY, UNKNOWN
|
||||
}
|
||||
|
||||
private SchemaAttributeType getSchemaAttributeType(String type) {
|
||||
SchemaAttributeType result = SchemaAttributeType.UNKNOWN;
|
||||
|
||||
if (type.equals("SUP")) {
|
||||
result = SchemaAttributeType.SUP;
|
||||
} else {
|
||||
if (type.equals("MUST")) {
|
||||
result = SchemaAttributeType.MUST;
|
||||
} else {
|
||||
if (type.equals("MAY")) {
|
||||
result = SchemaAttributeType.MAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private AttributeSchema createAttributeSchema(String name, DirContext schemaContext)
|
||||
throws NamingException, ClassNotFoundException {
|
||||
|
||||
// Get the schema definition
|
||||
Attributes attributeSchema = schemaContext.getAttributes("AttributeDefinition/" + name);
|
||||
|
||||
// Get the syntax - ditching any trailing length value that { and whatever follows it
|
||||
String syntax = ((String)attributeSchema.get("SYNTAX").get()).split("\\{")[0];
|
||||
|
||||
// Is it binary?
|
||||
boolean isBinary=binarySet.contains(syntax);
|
||||
|
||||
// Use it to look up the required Java class
|
||||
ClassInfo classInfo = syntaxToJavaClass.getClassInfo(syntax);
|
||||
|
||||
// Now we can set the java class
|
||||
String javaClassName = null;
|
||||
boolean isPrimitive = false;
|
||||
boolean isArray = false;
|
||||
|
||||
if (classInfo!=null) {
|
||||
javaClassName=classInfo.getClassName();
|
||||
Class<?> javaClass=Class.forName(classInfo.getFullClassName());
|
||||
javaClassName=javaClass.getSimpleName();
|
||||
isPrimitive=javaClass.isPrimitive();
|
||||
isArray=javaClass.isArray();
|
||||
} else {
|
||||
if (isBinary) {
|
||||
javaClassName="byte[]";
|
||||
isPrimitive=false;
|
||||
isArray=true;
|
||||
} else {
|
||||
javaClassName="String";
|
||||
isPrimitive=false;
|
||||
isArray=false;
|
||||
}
|
||||
}
|
||||
|
||||
return new AttributeSchema(name, syntax,
|
||||
attributeSchema.get("SINGLE-VALUE") == null,
|
||||
isPrimitive, isBinary, isArray, javaClassName);
|
||||
}
|
||||
|
||||
// Recursively extract schema from the directory and process it
|
||||
private void createObjectClass(Set<String> objectClasses, DirContext schemaContext, ObjectSchema schema)
|
||||
throws NamingException, ClassNotFoundException {
|
||||
|
||||
// Super classes
|
||||
Set<String> supList = new HashSet<String>();
|
||||
|
||||
// For each of the given object classes
|
||||
for (String objectClass : objectClasses) {
|
||||
// Add to set of included object classes
|
||||
schema.addObjectClass(objectClass);
|
||||
|
||||
// Grab the LDAP schema of the object class
|
||||
Attributes attributes = schemaContext.getAttributes("ClassDefinition/" + objectClass);
|
||||
NamingEnumeration<? extends Attribute> valuesEnumeration = attributes.getAll();
|
||||
|
||||
// Loop through each of the attributes
|
||||
while (valuesEnumeration.hasMoreElements()) {
|
||||
Attribute currentAttribute = valuesEnumeration.nextElement();
|
||||
|
||||
// Get the attribute name and lower case it (as this is all case indep)
|
||||
String currentId = currentAttribute.getID().toUpperCase();
|
||||
|
||||
// Is this a MUST, MAY or SUP attribute
|
||||
SchemaAttributeType type = getSchemaAttributeType(currentId);
|
||||
|
||||
// Loop through all the values
|
||||
NamingEnumeration<?> currentValues = currentAttribute.getAll();
|
||||
while (currentValues.hasMoreElements()) {
|
||||
String currentValue = (String)currentValues.nextElement();
|
||||
switch (type) {
|
||||
case SUP:
|
||||
// Its a super class
|
||||
String lowerCased=currentValue.toLowerCase();
|
||||
if (!schema.getObjectClass().contains(lowerCased)) {
|
||||
supList.add(lowerCased);
|
||||
}
|
||||
break;
|
||||
case MUST:
|
||||
// Add must attribute
|
||||
schema.addMust(createAttributeSchema(currentValue, schemaContext));
|
||||
break;
|
||||
case MAY:
|
||||
// Add may attribute
|
||||
schema.addMay(createAttributeSchema(currentValue, schemaContext));
|
||||
break;
|
||||
default:
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse for super classes
|
||||
createObjectClass(supList, schemaContext, schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
442
odm/src/main/java/org/springframework/ldap/odm/tools/SchemaToJava.java
Executable file
442
odm/src/main/java/org/springframework/ldap/odm/tools/SchemaToJava.java
Executable file
@@ -0,0 +1,442 @@
|
||||
package org.springframework.ldap.odm.tools;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.cli.PosixParser;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.DefaultObjectWrapper;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
|
||||
/**
|
||||
* This tool creates a Java class representation of a set of LDAP object classes for use
|
||||
* with {@link org.springframework.ldap.odm.core.OdmManager}.
|
||||
* <p>
|
||||
* The schema of a named list of object classes is read from an LDAP directory and used
|
||||
* to generate a representative Java class. The Java class is automatically annotated with
|
||||
* {@link org.springframework.ldap.odm.annotations} for use with
|
||||
* {@link org.springframework.ldap.odm.core.OdmManager}.
|
||||
* <p>
|
||||
* The mapping of LDAP attributes to their Java representations may be configured by supplying the
|
||||
* <code>-s</code> flag or the equivalent <code>--syntaxmap</code> flag whose argument is
|
||||
* the name of a file with the following structure:
|
||||
* <pre>
|
||||
* # List of attribute syntax to java class mappings
|
||||
*
|
||||
* # Syntax Java class
|
||||
* # ------ ----------
|
||||
*
|
||||
* 1.3.6.1.4.1.1466.115.121.1.50, java.lang.Integer
|
||||
* 1.3.6.1.4.1.1466.115.121.1.40, some.other.Class
|
||||
* </pre>
|
||||
* <p>
|
||||
* Syntaxes not included in this map will be represented as {@link java.lang.String} if they are returned as Strings by the
|
||||
* JNDI LDAP provider and will be represented as <code>byte[]</code> if they are returned by the provider as <code>byte[]</code>.
|
||||
* <p>
|
||||
* Command line flags are as follows:
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li><code>-c,--class <class name></code> Name of the Java class to create. Mandatory.</li>
|
||||
* <li><code>-s,--syntaxmap <map file></code> Configuration file of LDAP syntaxes to Java classes mappings. Optional.</li>
|
||||
* <li><code>-h,--help</code> Print this help message then exit.</li>
|
||||
* <li><code>-k,--package <package name></code> Package to create the Java class in. Mandatory.</li>
|
||||
* <li><code>-l,--url <ldap url></code> Ldap url of the directory service to bind to. Defaults to <code>ldap://127.0.0.1:389</code>. Optional.</li>
|
||||
* <li><code>-o,--objectclasses <LDAP object class lists></code> Comma separated list of LDAP object classes. Mandatory.</li>
|
||||
* <li><code>-u,--username <dn></code> DN to bind with. Defaults to "". Optional.</li>
|
||||
* <li><code>-p,--password <password></code> Password to bind with. Defaults to "". Optional.</li>
|
||||
* <li><code>-t,--outputdir <output directory></code> Base output directory, defaults to ".". Optional.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
*/
|
||||
public final class SchemaToJava {
|
||||
private static Log LOG = LogFactory.getLog(SchemaToJava.class);
|
||||
|
||||
// Name of the FreeMarker template used to generate the Java code.
|
||||
private static String TEMPLATE_FILE = "oc-to-java.ftl";
|
||||
|
||||
// Name of file containing the list of attributes syntaxes to
|
||||
// returned as byte[] by the JNDI LDAP provider.
|
||||
private static String BINARY_FILE = "binary-attributes.txt";
|
||||
|
||||
// Class to use a base for loading resources
|
||||
private static final Class<?> loaderClass=SchemaToJava.class;
|
||||
|
||||
// Default LDAP Url to bind with
|
||||
private static final String DEFAULT_URL="ldap://127.0.0.1:389";
|
||||
|
||||
// Command line flags
|
||||
private enum Flag {
|
||||
URL("l", "url"),
|
||||
USERNAME("u", "username"),
|
||||
PASSWORD("p", "password"),
|
||||
OBJECTCLASS("o", "objectclasses"),
|
||||
CLASS("c", "class"),
|
||||
PACKAGE("k", "package"),
|
||||
SYNTAX_MAP("s", "syntaxmap"),
|
||||
OUTPUT_DIR("t", "outputdir"),
|
||||
HELP("h", "help");
|
||||
|
||||
private String shortName;
|
||||
|
||||
private String longName;
|
||||
|
||||
private Flag(String shortName, String longName) {
|
||||
this.shortName = shortName;
|
||||
this.longName = longName;
|
||||
}
|
||||
|
||||
public String getShort() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public String getLong() {
|
||||
return longName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("short=%1$s, long=%2$s", shortName, longName);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Options options = new Options();
|
||||
static {
|
||||
options.addOption(Flag.URL.getShort(), Flag.URL.getLong(), true, "Ldap url (defaults to "+DEFAULT_URL+")");
|
||||
options.addOption(Flag.USERNAME.getShort(), Flag.USERNAME.getLong(), true, "DN to bind with (defaults to \"\"");
|
||||
options.addOption(Flag.PASSWORD.getShort(), Flag.PASSWORD.getLong(), true, "Password to bind with (defaults to \"\"");
|
||||
options.addOption(Flag.OBJECTCLASS.getShort(), Flag.OBJECTCLASS.getLong(), true, "Comma separated list of object classes");
|
||||
options.addOption(Flag.CLASS.getShort(), Flag.CLASS.getLong(), true, "Name of the Java class to create");
|
||||
options.addOption(Flag.PACKAGE.getShort(), Flag.PACKAGE.getLong(), true, "Package to create the Java class in");
|
||||
options.addOption(Flag.SYNTAX_MAP.getShort(), Flag.SYNTAX_MAP.getLong(), true, "Syntax map file (optional)");
|
||||
options.addOption(Flag.OUTPUT_DIR.getShort(), Flag.OUTPUT_DIR.getLong(), true, "Base output directory (defaults to .)");
|
||||
options.addOption(Flag.HELP.getShort(), Flag.HELP.getLong(), false, "Print this help message");
|
||||
}
|
||||
|
||||
// Read list of LDAP syntaxes that are returned as byte[]
|
||||
private static Set<String> readBinarySet(File binarySetFile)
|
||||
throws IOException {
|
||||
|
||||
Set<String> result = new HashSet<String>();
|
||||
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(binarySetFile));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
String trimmed = line.trim();
|
||||
if (trimmed.length() > 0) {
|
||||
if (trimmed.charAt(0) != '#') {
|
||||
String[] parts = trimmed.split("\\s");
|
||||
if (parts.length > 0) {
|
||||
result.add(parts[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read mappings of LDAP syntaxes to Java classes.
|
||||
private static Map<String, String> readSyntaxMap(File syntaxMapFile)
|
||||
throws IOException {
|
||||
|
||||
Map<String, String> result = new HashMap<String, String>();
|
||||
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(syntaxMapFile));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
String trimmed = line.trim();
|
||||
if (trimmed.length() > 0) {
|
||||
if (trimmed.charAt(0) != '#') {
|
||||
String[] parts = trimmed.split(",");
|
||||
if (parts.length != 2) {
|
||||
throw new IOException(String.format("Failed to parse line \"%1$s\"",
|
||||
trimmed));
|
||||
}
|
||||
String partOne = parts[0].trim();
|
||||
String partTwo = parts[1].trim();
|
||||
if (partOne.length() == 0 || partTwo.length() == 0) {
|
||||
throw new IOException(String.format("Failed to parse line \"%1$s\"",
|
||||
trimmed));
|
||||
}
|
||||
result.put(partOne, partTwo);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Bind to the directory, read and process the schema
|
||||
private static ObjectSchema readSchema(String url, String user, String pass,
|
||||
SyntaxToJavaClass syntaxToJavaClass, Set<String> binarySet, Set<String> objectClasses)
|
||||
throws NamingException, ClassNotFoundException {
|
||||
|
||||
// Set up environment
|
||||
Hashtable<String, String> env = new Hashtable<String, String>();
|
||||
env.put(Context.PROVIDER_URL, url);
|
||||
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||
if (user != null) {
|
||||
env.put(Context.SECURITY_PRINCIPAL, user);
|
||||
}
|
||||
if (pass != null) {
|
||||
env.put(Context.SECURITY_CREDENTIALS, pass);
|
||||
}
|
||||
|
||||
DirContext context = new InitialDirContext(env);
|
||||
DirContext schemaContext = context.getSchema("");
|
||||
SchemaReader reader = new SchemaReader(schemaContext, syntaxToJavaClass, binarySet);
|
||||
ObjectSchema schema = reader.getObjectSchema(objectClasses);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Schema - %1$s", schema.toString()));
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
// Create the Java
|
||||
private static void createCode(String packageName,
|
||||
String className, ObjectSchema schema, Set<SyntaxToJavaClass.ClassInfo> imports, File outputFile)
|
||||
throws IOException, TemplateException {
|
||||
|
||||
Configuration freeMarkerConfiguration = new Configuration();
|
||||
|
||||
freeMarkerConfiguration.setClassForTemplateLoading(loaderClass, "");
|
||||
freeMarkerConfiguration.setObjectWrapper(new DefaultObjectWrapper());
|
||||
|
||||
// Build the model for FreeMarker
|
||||
Map<String, Object> model = new HashMap<String, Object>();
|
||||
model.put("package", packageName);
|
||||
model.put("class", className);
|
||||
model.put("schema", schema);
|
||||
model.put("imports", imports);
|
||||
|
||||
// Have FreeMarker process the model with the template
|
||||
Template template = freeMarkerConfiguration.getTemplate(TEMPLATE_FILE);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
Writer out = new OutputStreamWriter(System.out);
|
||||
template.process(model, out);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
LOG.debug(String.format("Writing java to: %1$s", outputFile.getAbsolutePath()));
|
||||
|
||||
FileOutputStream outputStream=new FileOutputStream(outputFile);
|
||||
Writer out = new OutputStreamWriter(outputStream);
|
||||
template.process(model, out);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
// Create the output file for the generated code along with all intervening directories
|
||||
private static File makeOutputFile(String outputDir, String packageName, String className)
|
||||
throws IOException {
|
||||
|
||||
// Convert the package name to a path
|
||||
Pattern pattern=Pattern.compile("\\.");
|
||||
Matcher matcher=pattern.matcher(packageName);
|
||||
String sepToUse=File.separator;
|
||||
if (sepToUse.equals("\\")) {
|
||||
sepToUse="\\\\";
|
||||
}
|
||||
|
||||
// Try to create the necessary directories
|
||||
String directoryPath=outputDir+File.separator+matcher.replaceAll(sepToUse);
|
||||
File directory=new File(directoryPath);
|
||||
File outputFile=new File(directory, className+".java");
|
||||
|
||||
LOG.debug(String.format("Attempting to create output file at %1$s", outputFile.getAbsolutePath()));
|
||||
|
||||
try {
|
||||
directory.mkdirs();
|
||||
outputFile.createNewFile();
|
||||
} catch (SecurityException se) {
|
||||
throw new IOException(String.format("Can't write to output file %1$s", outputFile.getAbsoluteFile()));
|
||||
} catch (IOException ioe) {
|
||||
throw new IOException(String.format("Can't write to output file %1$s", outputFile.getAbsoluteFile()));
|
||||
}
|
||||
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
private static Set<String> parseObjectClassesFlag(String objectClassesFlag) {
|
||||
Set<String> objectClasses = new HashSet<String>();
|
||||
|
||||
for (String objectClassFlag : objectClassesFlag.split(",")) {
|
||||
if (objectClassFlag.length() > 0) {
|
||||
objectClasses.add(objectClassFlag.toLowerCase().trim());
|
||||
}
|
||||
}
|
||||
|
||||
return objectClasses;
|
||||
}
|
||||
|
||||
private static void error(String message) {
|
||||
System.err.println(String.format("%1$s: %2$s", SchemaToJava.class.getSimpleName(), message));
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
public static void main(String[] argv) {
|
||||
CommandLineParser parser = new PosixParser();
|
||||
CommandLine cmd = null;
|
||||
|
||||
// Parse out the command line options
|
||||
try {
|
||||
cmd = parser.parse(options, argv);
|
||||
} catch (ParseException e) {
|
||||
error(e.toString());
|
||||
}
|
||||
|
||||
// If the help flag is specified ignore other flags, print a usage message and exit
|
||||
if (cmd.hasOption(Flag.HELP.getShort())) {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
formatter.printHelp(120, SchemaToJava.class.getSimpleName(), null, options, null, true);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// Class name flag
|
||||
String className = cmd.getOptionValue(Flag.CLASS.getShort());
|
||||
if (className == null) {
|
||||
error("You must specify the name of a Java class to create");
|
||||
}
|
||||
|
||||
// Package name flag
|
||||
String packageName = cmd.getOptionValue(Flag.PACKAGE.getShort());
|
||||
if (packageName == null) {
|
||||
error("You must specifiy a package name");
|
||||
}
|
||||
|
||||
// Output base directory
|
||||
String outputDir = cmd.getOptionValue(Flag.OUTPUT_DIR.getShort(), ".");
|
||||
File outputFile = null;
|
||||
try {
|
||||
outputFile = makeOutputFile(outputDir, packageName, className);
|
||||
} catch (IOException e) {
|
||||
error(e.toString());
|
||||
}
|
||||
|
||||
// Get the flags we need to bind to the directory
|
||||
String url = cmd.getOptionValue(Flag.URL.getShort(), DEFAULT_URL);
|
||||
String user = cmd.getOptionValue(Flag.USERNAME.getShort());
|
||||
String pass = cmd.getOptionValue(Flag.PASSWORD.getShort());
|
||||
|
||||
// Parse out object classes
|
||||
String objectClassesFlag = cmd.getOptionValue(Flag.OBJECTCLASS.getShort());
|
||||
if (objectClassesFlag==null) {
|
||||
error("You must specificy a package name");
|
||||
}
|
||||
Set<String> objectClasses = parseObjectClassesFlag(objectClassesFlag);
|
||||
if (objectClasses.size()==0) {
|
||||
error("You must specificy a package name");
|
||||
}
|
||||
|
||||
// Look for the optional syntax to Java class mapping file
|
||||
String syntaxMapFileName = cmd.getOptionValue(Flag.SYNTAX_MAP.getShort(), null);
|
||||
SyntaxToJavaClass syntaxToJavaClass=new SyntaxToJavaClass(new HashMap<String, String>());
|
||||
if (syntaxMapFileName!=null) {
|
||||
File syntaxMapFile=new File(syntaxMapFileName);
|
||||
if (syntaxMapFile.canRead()) {
|
||||
try {
|
||||
syntaxToJavaClass = new SyntaxToJavaClass(readSyntaxMap(syntaxMapFile));
|
||||
} catch (IOException e) {
|
||||
error(String.format("Error reading syntax map file %1$s - %2$s",
|
||||
syntaxMapFile.getAbsolutePath(), e.toString()));
|
||||
}
|
||||
} else {
|
||||
error(String.format("Cannot read syntax map file %s$1",
|
||||
syntaxMapFile.getAbsolutePath()));
|
||||
}
|
||||
}
|
||||
|
||||
// Read binary mapping file
|
||||
URL binarySetUrl=loaderClass.getResource(BINARY_FILE);
|
||||
if (binarySetUrl==null) {
|
||||
error(String.format("Can't locatate binary mappings file %1$s", BINARY_FILE));
|
||||
}
|
||||
File binarySetFile=new File(binarySetUrl.getFile());
|
||||
if (!binarySetFile.canRead()) {
|
||||
error(String.format("Can't read from binary mappings file %1$s", BINARY_FILE));
|
||||
}
|
||||
Set<String> binarySet = null;
|
||||
try {
|
||||
binarySet = readBinarySet(binarySetFile);
|
||||
} catch (IOException e) {
|
||||
error(String.format("Error reading binary set file %1$s - %2$s", binarySetFile.getAbsolutePath(), e));
|
||||
}
|
||||
|
||||
// Read schema from the directory
|
||||
ObjectSchema schema=null;
|
||||
try {
|
||||
schema=readSchema(url, user, pass, syntaxToJavaClass, binarySet, objectClasses);
|
||||
} catch (NamingException ne) {
|
||||
error(String.format("Error processing schema - %1$s", ne));
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
error(String.format("Error processing schema - %1$s", cnfe));
|
||||
}
|
||||
|
||||
// Work out what imports we need
|
||||
Set<SyntaxToJavaClass.ClassInfo> imports = new HashSet<SyntaxToJavaClass.ClassInfo>();
|
||||
for (AttributeSchema attributeSchema : schema.getMay()) {
|
||||
SyntaxToJavaClass.ClassInfo classInfo = syntaxToJavaClass.getClassInfo(attributeSchema.getSyntax());
|
||||
if (classInfo != null) {
|
||||
String classPackageName = classInfo.getPackageName();
|
||||
if (classPackageName != null && classPackageName.length() > 0) {
|
||||
imports.add(classInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the Java code
|
||||
try {
|
||||
createCode(packageName, className, schema, imports, outputFile);
|
||||
} catch (TemplateException te) {
|
||||
error(String.format("Error generating code - %1$s", te.toString()));
|
||||
} catch (IOException ioe) {
|
||||
error(String.format("Error generatign code - %1$s", ioe.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
233
odm/src/main/java/org/springframework/ldap/odm/tools/SchemaViewer.java
Executable file
233
odm/src/main/java/org/springframework/ldap/odm/tools/SchemaViewer.java
Executable file
@@ -0,0 +1,233 @@
|
||||
package org.springframework.ldap.odm.tools;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import javax.naming.AuthenticationException;
|
||||
import javax.naming.CommunicationException;
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NameClassPair;
|
||||
import javax.naming.NameNotFoundException;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.Attributes;
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.cli.PosixParser;
|
||||
|
||||
/**
|
||||
* A simple utility to list LDAP directory schema.
|
||||
* <p>
|
||||
* <code>SchemaViewer</code> takes the following flags:
|
||||
* <ul>
|
||||
* <li><code>-h,--help<</code> Print this help message</li>
|
||||
* <li><code>-l,--url <arg></code> Ldap url of directory to bind to (defaults to ldap://127.0.0.1:389)</li>
|
||||
* <li><code>-u,--username <arg></code> DN to bind with (defaults to "")</li>
|
||||
* <li><code>-p,--password <arg></code> Password to bind with (defaults to "")</li>
|
||||
* <li><code>-o,--objectclass <arg></code> Object class name or ? for all. Print object class schema</li>
|
||||
* <li><code>-a,--attribute <arg></code> Attribute name or ? for all. Print attribute schema</li>
|
||||
* <li><code>-s,--syntax <arg></code> Syntax or ? for all. Print syntax</li>
|
||||
* </ul>
|
||||
*
|
||||
* Only one of <code>-a</code>, <code>-o</code> and <code>-s</code> should be specified.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*
|
||||
*/
|
||||
public final class SchemaViewer {
|
||||
private static final String DEFAULT_URL="ldap://127.0.0.1:389";
|
||||
|
||||
private enum Flag {
|
||||
URL("l", "url"),
|
||||
USERNAME("u", "username"),
|
||||
PASSWORD("p", "password"),
|
||||
OBJECTCLASS("o", "objectclass"),
|
||||
ATTRIBUTE("a", "attribute"),
|
||||
SYNTAX("s", "syntax"),
|
||||
HELP("h", "help"),
|
||||
ERROR("e", "error");
|
||||
|
||||
private String shortName;
|
||||
|
||||
private String longName;
|
||||
|
||||
private Flag(String shortName, String longName) {
|
||||
this.shortName = shortName;
|
||||
this.longName = longName;
|
||||
}
|
||||
|
||||
public String getShort() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public String getLong() {
|
||||
return longName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("short=%1$s, long=%2$s", shortName, longName);
|
||||
}
|
||||
}
|
||||
|
||||
private enum SchemaContext {
|
||||
OBJECTCLASS("ClassDefinition"), ATTRIBUTE("AttributeDefinition"), SYNTAX("SyntaxDefinition");
|
||||
|
||||
private String value;
|
||||
|
||||
private SchemaContext(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("value=%1$s", value);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Options options = new Options();
|
||||
static {
|
||||
options.addOption(Flag.URL.getShort(), Flag.URL.getLong(), true, "Ldap url (defaults to "+DEFAULT_URL+")");
|
||||
options.addOption(Flag.USERNAME.getShort(), Flag.USERNAME.getLong(), true, "DN to bind with (defaults to \"\")");
|
||||
options.addOption(Flag.PASSWORD.getShort(), Flag.PASSWORD.getLong(), true, "Password to bind with defaults to \"\")");
|
||||
options.addOption(Flag.OBJECTCLASS.getShort(), Flag.OBJECTCLASS.getLong(), true,
|
||||
"Object class name or ? for all. Print object class schema");
|
||||
options.addOption(Flag.ATTRIBUTE.getShort(), Flag.ATTRIBUTE.getLong(), true,
|
||||
"Attribute name or ? for all. Print attribute schema");
|
||||
options.addOption(Flag.SYNTAX.getShort(), Flag.SYNTAX.getLong(), true,
|
||||
"Syntax OID or ? for all. Print attribute syntax");
|
||||
options.addOption(Flag.HELP.getShort(), Flag.HELP.getLong(), false, "Print this help message");
|
||||
options.addOption(Flag.ERROR.getShort(), Flag.ERROR.getLong(), false, "Send output to standard error");
|
||||
}
|
||||
|
||||
private static void printAttrs(Attributes attrs) throws NamingException {
|
||||
NamingEnumeration<? extends Attribute> attrsEnum = attrs.getAll();
|
||||
while (attrsEnum.hasMore()) {
|
||||
Attribute currentAttr = attrsEnum.next();
|
||||
outstream.print(String.format("%1$s:", currentAttr.getID()));
|
||||
NamingEnumeration<?> valuesEnum = currentAttr.getAll();
|
||||
while (valuesEnum.hasMoreElements()) {
|
||||
outstream.print(String.format("%1$s ", valuesEnum.nextElement().toString()));
|
||||
}
|
||||
outstream.println();
|
||||
}
|
||||
}
|
||||
|
||||
private static void printObject(String contextName, String schemaName, DirContext schemaContext)
|
||||
throws NameNotFoundException, NamingException {
|
||||
|
||||
DirContext oContext = (DirContext)schemaContext.lookup(contextName + "/" + schemaName);
|
||||
|
||||
outstream.println("NAME:" + schemaName);
|
||||
printAttrs(oContext.getAttributes(""));
|
||||
}
|
||||
|
||||
private static void printSchema(String contextName, DirContext schemaContext) throws NameNotFoundException,
|
||||
NamingException {
|
||||
|
||||
outstream.println();
|
||||
|
||||
NamingEnumeration<NameClassPair> schemaList = schemaContext.list(contextName);
|
||||
|
||||
while (schemaList.hasMore()) {
|
||||
NameClassPair ncp = schemaList.nextElement();
|
||||
|
||||
printObject(contextName, ncp.getName(), schemaContext);
|
||||
outstream.println();
|
||||
}
|
||||
|
||||
outstream.println();
|
||||
}
|
||||
|
||||
private static void print(String optionValue, String contextName, DirContext schemaContext)
|
||||
throws NameNotFoundException, NamingException {
|
||||
|
||||
if (optionValue.equals(WILDCARD)) {
|
||||
printSchema(contextName, schemaContext);
|
||||
} else {
|
||||
printObject(contextName, optionValue, schemaContext);
|
||||
}
|
||||
}
|
||||
|
||||
private static PrintStream outstream=System.out;
|
||||
private static String WILDCARD = "?";
|
||||
|
||||
public static void main(String[] argv) {
|
||||
CommandLineParser parser = new PosixParser();
|
||||
CommandLine cmd = null;
|
||||
|
||||
try {
|
||||
cmd = parser.parse(options, argv);
|
||||
} catch (ParseException e) {
|
||||
System.out.println(e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (cmd.hasOption(Flag.HELP.getShort())) {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
|
||||
formatter.printHelp(120, SchemaViewer.class.getSimpleName(), null, options, null, true);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
if (cmd.hasOption(Flag.ERROR.getShort())) {
|
||||
outstream=System.err;
|
||||
}
|
||||
|
||||
String url = cmd.getOptionValue(Flag.URL.getShort(), DEFAULT_URL);
|
||||
String user = cmd.getOptionValue(Flag.USERNAME.getShort(), "");
|
||||
String pass = cmd.getOptionValue(Flag.PASSWORD.getShort(), "");
|
||||
|
||||
Hashtable<String, String> env = new Hashtable<String, String>();
|
||||
env.put(Context.PROVIDER_URL, url);
|
||||
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||
if (user != null) {
|
||||
env.put(Context.SECURITY_PRINCIPAL, user);
|
||||
}
|
||||
if (pass != null) {
|
||||
env.put(Context.SECURITY_CREDENTIALS, pass);
|
||||
if (user == null) {
|
||||
System.err.println("You must specify a user if you specify a password");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
DirContext context = new InitialDirContext(env);
|
||||
DirContext schemaContext = context.getSchema("");
|
||||
|
||||
if (cmd.hasOption(Flag.OBJECTCLASS.getShort())) {
|
||||
print(cmd.getOptionValue(Flag.OBJECTCLASS.getShort()), SchemaContext.OBJECTCLASS.getValue(),
|
||||
schemaContext);
|
||||
}
|
||||
|
||||
if (cmd.hasOption(Flag.ATTRIBUTE.getShort())) {
|
||||
print(cmd.getOptionValue(Flag.ATTRIBUTE.getShort()), SchemaContext.ATTRIBUTE.getValue(), schemaContext);
|
||||
}
|
||||
|
||||
if (cmd.hasOption(Flag.SYNTAX.getShort())) {
|
||||
print(cmd.getOptionValue(Flag.SYNTAX.getShort()), SchemaContext.SYNTAX.getValue(), schemaContext);
|
||||
}
|
||||
|
||||
} catch (AuthenticationException e) {
|
||||
System.err.println(String.format("Failed to bind to ldap server at %1$s", url));
|
||||
} catch (CommunicationException e) {
|
||||
System.err.println(String.format("Failed to contact ldap server at %1$s", url));
|
||||
} catch (NameNotFoundException e) {
|
||||
System.err.println(String.format("Can't find object %1$s", e.getMessage()));
|
||||
} catch (NamingException e) {
|
||||
System.err.println(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
63
odm/src/main/java/org/springframework/ldap/odm/tools/SyntaxToJavaClass.java
Executable file
63
odm/src/main/java/org/springframework/ldap/odm/tools/SyntaxToJavaClass.java
Executable file
@@ -0,0 +1,63 @@
|
||||
package org.springframework.ldap.odm.tools;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* A map from an LDAP syntax to the Java class used to represent it.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
/* package */ final class SyntaxToJavaClass {
|
||||
public final static class ClassInfo {
|
||||
private final String className;
|
||||
|
||||
private final String packageName;
|
||||
|
||||
private ClassInfo(String className, String packageName) {
|
||||
this.className = className;
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public String getFullClassName() {
|
||||
StringBuilder result=new StringBuilder();
|
||||
if (packageName!=null) {
|
||||
result.append(packageName).append(".").append(className);
|
||||
} else {
|
||||
result.append(className);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, ClassInfo> mapSyntaxToClassInfo = new HashMap<String, ClassInfo>();
|
||||
|
||||
public SyntaxToJavaClass(Map<String, String> mapSyntaxToClass) {
|
||||
for (Entry<String, String> syntaxAndClass : mapSyntaxToClass.entrySet()) {
|
||||
String fullClassName = syntaxAndClass.getValue().trim();
|
||||
String packageName = null;
|
||||
String className = null;
|
||||
int lastDotIndex = fullClassName.lastIndexOf('.');
|
||||
if (lastDotIndex != -1) {
|
||||
className = fullClassName.substring(lastDotIndex + 1);
|
||||
packageName = fullClassName.substring(0, lastDotIndex);
|
||||
} else {
|
||||
className = fullClassName;
|
||||
}
|
||||
mapSyntaxToClassInfo.put(syntaxAndClass.getKey(), new ClassInfo(className, packageName));
|
||||
}
|
||||
}
|
||||
|
||||
public ClassInfo getClassInfo(String syntax) {
|
||||
return mapSyntaxToClassInfo.get(syntax);
|
||||
}
|
||||
}
|
||||
8
odm/src/main/java/org/springframework/ldap/odm/tools/package-info.java
Executable file
8
odm/src/main/java/org/springframework/ldap/odm/tools/package-info.java
Executable file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Provides a tool to create a Java class representation of a set of LDAP object classes
|
||||
* and a simple tool to view LDAP schema.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.tools;
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.springframework.ldap.odm.typeconversion;
|
||||
|
||||
import org.springframework.ldap.NamingException;
|
||||
|
||||
/**
|
||||
* Thrown by the conversion framework to indicate an error condition - typically a failed type conversion.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public final class ConverterException extends NamingException {
|
||||
public ConverterException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ConverterException(final String message, final Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.ldap.odm.typeconversion;
|
||||
|
||||
/**
|
||||
* A simple interface to be implemented to provide type conversion functionality.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public interface ConverterManager {
|
||||
/**
|
||||
* Determine whether this converter manager is able to carry out a specified conversion.
|
||||
*
|
||||
* @param fromClass Convert from the <code>fromClass</code>.
|
||||
* @param syntax Using the LDAP syntax (may be null).
|
||||
* @param toClass To the <code>toClass</code>.
|
||||
* @return <code>True</code> if the conversion is supported, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean canConvert(Class<?> fromClass, String syntax, Class<?> toClass);
|
||||
|
||||
/**
|
||||
* Convert a given source object with an optional LDAP syntax to an instance of a given class.
|
||||
*
|
||||
* @param <T> The class to convert to.
|
||||
* @param source The object to convert.
|
||||
* @param syntax The LDAP syntax to use (may be null).
|
||||
* @param toClass The class to convert to.
|
||||
* @return The converted object.
|
||||
*
|
||||
* @throws ConverterException If the conversion can not be successfully completed.
|
||||
*/
|
||||
<T> T convert(Object source, String syntax, Class<T> toClass);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.springframework.ldap.odm.typeconversion.impl;
|
||||
|
||||
/**
|
||||
* Interface specifying the conversion between two classes
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public interface Converter {
|
||||
/**
|
||||
* Attempt to convert a given object to a named class.
|
||||
*
|
||||
* @param <T> The class to convert to.
|
||||
* @param source The object to convert.
|
||||
* @param toClass The class to convert to.
|
||||
* @return The converted class or null if the conversion was not possible.
|
||||
* @throws Exception Any exception may be throw by a Converter on error.
|
||||
*/
|
||||
<T> T convert(Object source, Class<T> toClass) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package org.springframework.ldap.odm.typeconversion.impl;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
|
||||
|
||||
/**
|
||||
* A utility class to allow {@link ConverterManagerImpl} instances to be easily configured via <code>spring.xml</code>
|
||||
* <p>
|
||||
* The following shows a typical simple example which creates two {@link Converter} instances:
|
||||
* <ul>
|
||||
* <li><code>fromStringConverter</code></li>
|
||||
* <li><code>toStringConverter</code></li>
|
||||
* </ul>
|
||||
* Configured in an {@link ConverterManagerImpl} to:
|
||||
* <ul>
|
||||
* <li>Use <code>fromStringConverter</code> to convert from <code>String</code> to <code>Byte, Short,
|
||||
* Integer, Long, Float, Double, Boolean</code> </li>
|
||||
* <li>Use <code>toStringConverter</code> to convert from <code>Byte, Short,
|
||||
* Integer, Long, Float, Double, Boolean</code> to <code>String</code></li>
|
||||
* </ul>
|
||||
* <pre>
|
||||
* <bean id="converterManager" class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean">
|
||||
* <property name="converterConfig">
|
||||
* <set>
|
||||
* <bean class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean$ConverterConfig">
|
||||
* <property name="fromClasses">
|
||||
* <set>
|
||||
* <value>java.lang.String</value>
|
||||
* </set>
|
||||
* </property>
|
||||
* <property name="toClasses">
|
||||
* <set>
|
||||
* <value>java.lang.Byte</value>
|
||||
* <value>java.lang.Short</value>
|
||||
* <value>java.lang.Integer</value>
|
||||
* <value>java.lang.Long</value>
|
||||
* <value>java.lang.Float</value>
|
||||
* <value>java.lang.Double</value>
|
||||
* <value>java.lang.Boolean</value>
|
||||
* </set>
|
||||
* </property>
|
||||
* <property name="converter" ref="fromStringConverter" />
|
||||
* </bean>
|
||||
* <bean class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean$ConverterConfig">
|
||||
* <property name="fromClasses">
|
||||
* <set>
|
||||
* <value>java.lang.Byte</value>
|
||||
* <value>java.lang.Short</value>
|
||||
* <value>java.lang.Integer</value>
|
||||
* <value>java.lang.Long</value>
|
||||
* <value>java.lang.Float</value>
|
||||
* <value>java.lang.Double</value>
|
||||
* <value>java.lang.Boolean</value>
|
||||
* </set>
|
||||
* </property>
|
||||
* <property name="toClasses">
|
||||
* <set>
|
||||
* <value>java.lang.String</value>
|
||||
* </set>
|
||||
* </property>
|
||||
* <property name="converter" ref="toStringConverter" />
|
||||
* </bean>
|
||||
* </set>
|
||||
* </property>
|
||||
* </bean>
|
||||
* </pre>
|
||||
* {@link ConverterConfig} has a second constructor which takes an additional parameter to allow
|
||||
* an LDAP syntax to be defined.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public final class ConverterManagerFactoryBean implements FactoryBean {
|
||||
private static Log LOG = LogFactory.getLog(ConverterManagerFactoryBean.class);
|
||||
|
||||
/**
|
||||
* Configuration information for a single Converter instance.
|
||||
*/
|
||||
public final static class ConverterConfig {
|
||||
// The set of classes the Converter will convert from.
|
||||
private Set<Class<?>> fromClasses = new HashSet<Class<?>>();
|
||||
|
||||
// The (optional) LDAP syntax.
|
||||
private String syntax=null;
|
||||
|
||||
// The set of classes the Converter will convert to.
|
||||
private Set<Class<?>> toClasses = new HashSet<Class<?>>();
|
||||
|
||||
// The Converter to use.
|
||||
private Converter converter=null;
|
||||
|
||||
public ConverterConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fromClasses Comma separated list of classes the {@link Converter} should can convert from.
|
||||
*/
|
||||
public void setFromClasses(Set<Class<?>> fromClasses) {
|
||||
this.fromClasses=fromClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param toClasses Comma separated list of classes the {@link Converter} can convert to.
|
||||
*/
|
||||
public void setToClasses(Set<Class<?>> toClasses) {
|
||||
this.toClasses=toClasses;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param syntax An LDAP syntax supported by the {@link Converter}.
|
||||
*/
|
||||
public void setSyntax(String syntax) {
|
||||
this.syntax=syntax;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param converter The {@link Converter} to use.
|
||||
*/
|
||||
public void setConverter(Converter converter) {
|
||||
this.converter=converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("fromClasses=%1$s, syntax=%2$s, toClasses=%3$s, converter=%4$s",
|
||||
fromClasses, syntax, toClasses, converter);
|
||||
}
|
||||
}
|
||||
|
||||
private Set<ConverterConfig> converterConfigList=null;
|
||||
|
||||
|
||||
/**
|
||||
* @param converterConfigList
|
||||
*/
|
||||
public void setConverterConfig(Set<ConverterConfig> converterConfigList) {
|
||||
this.converterConfigList=converterConfigList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ConverterManagerImpl populating it with Converter instances from the converterConfigList property.
|
||||
*
|
||||
* @return The newly created {@link org.springframework.ldap.odm.typeconversion.ConverterManager}.
|
||||
* @throws ClassNotFoundException Thrown if any of the classes to be converted to or from cannot be found.
|
||||
*
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
public Object getObject() throws Exception {
|
||||
if (converterConfigList==null) {
|
||||
throw new FactoryBeanNotInitializedException("converterConfigList has not been set");
|
||||
}
|
||||
|
||||
ConverterManagerImpl result = new ConverterManagerImpl();
|
||||
for (ConverterConfig converterConfig : converterConfigList) {
|
||||
if (converterConfig.fromClasses==null ||
|
||||
converterConfig.toClasses==null ||
|
||||
converterConfig.converter==null) {
|
||||
|
||||
throw new FactoryBeanNotInitializedException(
|
||||
String.format("All of fromClasses, toClasses and converter must be specified in bean %1$s",
|
||||
converterConfig.toString()));
|
||||
}
|
||||
for (Class<?> fromClass : converterConfig.fromClasses) {
|
||||
for (Class<?> toClass : converterConfig.toClasses) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Adding converter from %1$s to %2$s", fromClass, toClass));
|
||||
}
|
||||
result.addConverter(fromClass, converterConfig.syntax, toClass, converterConfig.converter);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
|
||||
*/
|
||||
public Class<?> getObjectType() {
|
||||
return ConverterManagerImpl.class;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
|
||||
*/
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* An implementation of {@link org.springframework.ldap.odm.typeconversion.ConverterManager}.
|
||||
* <p>
|
||||
* The algorithm used is to:
|
||||
* <ol>
|
||||
* <li>Try to find and use a {@link Converter} registered for the
|
||||
* <code>fromClass</code>, <code>syntax</code> and <code>toClass</code> and use it.</li>
|
||||
* <li>If this fails, then if the <code>toClass isAssignableFrom</code>
|
||||
* the <code>fromClass</code> then just assign it.</li>
|
||||
* <li>If this fails try to find and use a {@link Converter} registered for the <code>fromClass</code> and
|
||||
* the <code>toClass</code> ignoring the <code>syntax</code>.</li>
|
||||
* <li>If this fails then throw a {@link org.springframework.ldap.odm.typeconversion.ConverterException}.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public final class ConverterManagerImpl implements ConverterManager {
|
||||
/**
|
||||
* Separator used to form keys into the converters Map.
|
||||
*/
|
||||
private static final String KEY_SEP = ":";
|
||||
|
||||
/**
|
||||
* Map of keys created via makeConverterKey to Converter instances.
|
||||
*/
|
||||
private final Map<String, Converter> converters = new HashMap<String, Converter>();
|
||||
|
||||
/**
|
||||
* Make a key into the converters map - the keys is formed from the <code>fromClass</code>, syntax and <code>toClass</code>
|
||||
*
|
||||
* @param fromClass The class to convert from.
|
||||
* @param syntax The LDAP syntax.
|
||||
* @param toClass The class to convert to.
|
||||
* @return key
|
||||
*/
|
||||
private String makeConverterKey(Class<?> fromClass, String syntax, Class<?> toClass) {
|
||||
StringBuilder key = new StringBuilder();
|
||||
if (syntax==null) {
|
||||
syntax="";
|
||||
}
|
||||
key.append(fromClass.getName()).append(KEY_SEP).append(syntax).append(KEY_SEP).append(toClass.getName());
|
||||
return key.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty ConverterManagerImpl
|
||||
*/
|
||||
public ConverterManagerImpl() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to help in the process of dealing with primitive types by mapping them to
|
||||
* their equivalent boxed class.
|
||||
*/
|
||||
private static Map<Class<?>, Class<?>> primitiveTypeMap = new HashMap<Class<?>, Class<?>>();
|
||||
static {
|
||||
primitiveTypeMap.put(Byte.TYPE, Byte.class);
|
||||
primitiveTypeMap.put(Short.TYPE, Short.class);
|
||||
primitiveTypeMap.put(Integer.TYPE, Integer.class);
|
||||
primitiveTypeMap.put(Long.TYPE, Long.class);
|
||||
primitiveTypeMap.put(Float.TYPE, Float.class);
|
||||
primitiveTypeMap.put(Double.TYPE, Double.class);
|
||||
primitiveTypeMap.put(Boolean.TYPE, Boolean.class);
|
||||
primitiveTypeMap.put(Character.TYPE, Character.class);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.ldap.odm.typeconversion.ConverterManager#canConvert(java.lang.Class, java.lang.String, java.lang.Class)
|
||||
*/
|
||||
public boolean canConvert(Class<?> fromClass, String syntax, Class<?> toClass) {
|
||||
Class<?> fixedToClass = toClass;
|
||||
if (toClass.isPrimitive()) {
|
||||
fixedToClass = primitiveTypeMap.get(toClass);
|
||||
}
|
||||
Class<?> fixedFromClass = fromClass;
|
||||
if (fromClass.isPrimitive()) {
|
||||
fixedFromClass = primitiveTypeMap.get(fromClass);
|
||||
}
|
||||
return fixedToClass.isAssignableFrom(fixedFromClass) ||
|
||||
(converters.get(makeConverterKey(fixedFromClass, syntax, fixedToClass)) != null) ||
|
||||
(converters.get(makeConverterKey(fixedFromClass, null, fixedToClass)) != null);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.ldap.odm.typeconversion.ConverterManager#convert(java.lang.Object, java.lang.String, java.lang.Class)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T convert(Object source, String syntax, Class<T> toClass) {
|
||||
Object result = null;
|
||||
|
||||
// What are we converting form
|
||||
Class<?> fromClass = source.getClass();
|
||||
|
||||
// Deal with primitives
|
||||
Class<?> targetClass = toClass;
|
||||
if (toClass.isPrimitive()) {
|
||||
targetClass = primitiveTypeMap.get(toClass);
|
||||
}
|
||||
|
||||
// Try to convert with any syntax we have been given
|
||||
Converter syntaxConverter = converters.get(makeConverterKey(fromClass, syntax, targetClass));
|
||||
if (syntaxConverter != null) {
|
||||
try {
|
||||
result = syntaxConverter.convert(source, targetClass);
|
||||
} catch (Exception e) {
|
||||
// Ignore as we may still be able to convert successfully
|
||||
}
|
||||
}
|
||||
|
||||
// Do we actually need to do any conversion?
|
||||
if (result == null && targetClass.isAssignableFrom(fromClass)) {
|
||||
result = source;
|
||||
}
|
||||
|
||||
// If we were given a syntax and we failed to convert drop back to any mapping
|
||||
// that will work from class -> to class
|
||||
if (result == null && syntax != null) {
|
||||
Converter nullSyntaxConverter = converters.get(makeConverterKey(fromClass, null, targetClass));
|
||||
if (nullSyntaxConverter != null) {
|
||||
try {
|
||||
result = nullSyntaxConverter.convert(source, targetClass);
|
||||
} catch (Exception e) {
|
||||
// Handled at the end of the method
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
throw new ConverterException(String.format(
|
||||
"Cannot convert %1$s of class %2$s via syntax %3$s to class %4$s", source, source.getClass(),
|
||||
syntax, toClass));
|
||||
}
|
||||
|
||||
// We cannot do the safe thing of doing a .cast as we need to rely on auto-unboxing to deal with primitives!
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a {@link Converter} to this <code>ConverterManager</code>.
|
||||
*
|
||||
* @param fromClass The class the <code>Converter</code> should be used to convert from.
|
||||
* @param syntax The LDAP syntax that the <code>Converter</code> should be used for.
|
||||
* @param toClass The class the <code>Converter</code> should be used to convert to.
|
||||
* @param converter The <code>Converter</code> to add.
|
||||
*/
|
||||
public void addConverter(Class<?> fromClass, String syntax, Class<?> toClass, Converter converter) {
|
||||
converters.put(makeConverterKey(fromClass, syntax, toClass), converter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.springframework.ldap.odm.typeconversion.impl.converters;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import org.springframework.ldap.odm.typeconversion.impl.Converter;
|
||||
|
||||
/**
|
||||
* A Converter from a {@link java.lang.String} to any class which has a single argument
|
||||
* public constructor taking a {@link java.lang.String}.
|
||||
* <p>
|
||||
* This should only be used as a fall-back converter, as a last attempt.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public final class FromStringConverter implements Converter {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.ldap.odm.typeconversion.impl.Converter#convert(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
public <T> T convert(Object source, Class<T> toClass) throws Exception {
|
||||
Constructor<T> constructor = toClass.getConstructor(java.lang.String.class);
|
||||
return constructor.newInstance(source);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.springframework.ldap.odm.typeconversion.impl.converters;
|
||||
|
||||
import org.springframework.ldap.odm.typeconversion.impl.Converter;
|
||||
|
||||
|
||||
/**
|
||||
* A Converter from any class to a {@link java.lang.String} via the <code>toString</code> method.
|
||||
* <p>
|
||||
* This should only be used as a fall-back converter, as a last attempt.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public final class ToStringConverter implements Converter {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.ldap.odm.typeconversion.impl.Converter#convert(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
public <T> T convert(Object source, Class<T> toClass) {
|
||||
return toClass.cast(source.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Provides some basic implementations of the {@link org.springframework.ldap.odm.typeconversion.impl.Converter} interface.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.typeconversion.impl.converters;
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Provides an implementation of the {@link org.springframework.ldap.odm.typeconversion.ConverterManager} interface.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.typeconversion.impl;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Provides an interface to be implemented to create a type conversion framework.
|
||||
* <p>
|
||||
* This is used to convert between the LDAP and Java representations of attributes.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.typeconversion;
|
||||
|
||||
19
odm/src/main/resources/log4j.properties
Executable file
19
odm/src/main/resources/log4j.properties
Executable file
@@ -0,0 +1,19 @@
|
||||
log4j.rootLogger=error, stdout
|
||||
log4j.logger.org.springframework.ldap.odm=error
|
||||
log4j.logger.org.springframework=error
|
||||
|
||||
# Messages to the console
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
|
||||
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
|
||||
|
||||
# Message to a log file
|
||||
log4j.appender.log=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.log.File=jndi.log
|
||||
|
||||
log4j.appender.log.MaxFileSize=100KB
|
||||
log4j.appender.log.MaxBackupIndex=1
|
||||
|
||||
log4j.appender.log.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.log.layout.ConversionPattern=%p %t %c - %m%n
|
||||
@@ -0,0 +1,16 @@
|
||||
# List of syntaxes that will be returned by the JNDI provider as byte[], taken from
|
||||
# http://java.sun.com/products/jndi/tutorial/ldap/misc/attrs.html
|
||||
# See all http://www.ietf.org/rfc/rfc2256.txt for attribute names with ;binary suffix
|
||||
|
||||
# Syntax Used by Attributes
|
||||
# ------ ------------------
|
||||
|
||||
1.3.6.1.4.1.1466.115.121.1.23 photo, personalSignature
|
||||
1.3.6.1.4.1.1466.115.121.1.4 audio
|
||||
1.3.6.1.4.1.1466.115.121.1.28 jpegPhoto
|
||||
1.3.6.1.4.1.1466.115.121.1.40 javaSerializedData
|
||||
1.3.6.1.4.1.1466.115.121.1.40 userPassword
|
||||
1.3.6.1.4.1.1466.115.121.1.8 userCertificate, cACertificate
|
||||
1.3.6.1.4.1.1466.115.121.1.9 authorityRevocationList, certificateRevocationList
|
||||
1.3.6.1.4.1.1466.115.121.1.10 crossCertificatePair
|
||||
1.3.6.1.4.1.1466.115.121.1.6 x500UniqueIdentifier
|
||||
163
odm/src/main/resources/org/springframework/ldap/odm/tools/oc-to-java.ftl
Executable file
163
odm/src/main/resources/org/springframework/ldap/odm/tools/oc-to-java.ftl
Executable file
@@ -0,0 +1,163 @@
|
||||
<#ftl strip_whitespace="true">
|
||||
|
||||
<#macro commaSeparatedList strings>
|
||||
<#local first=true/>
|
||||
<#list strings as listValue>
|
||||
<#if !first>
|
||||
, <#t/>
|
||||
<#else>
|
||||
<#local first=false/>
|
||||
</#if>
|
||||
"${listValue}"<#t/>
|
||||
</#list>
|
||||
</#macro>
|
||||
|
||||
<#macro doAttribute attributes>
|
||||
<#list attributes as attribute>
|
||||
<#local binary=attribute.isBinary?string(", type=Type.BINARY", "")>
|
||||
|
||||
<#lt/> @Attribute(name="${attribute.name}", syntax="${attribute.syntax}"${binary})
|
||||
<#if attribute.isMultiValued>
|
||||
<#lt/> private List<${attribute.scalarType}> ${attribute.name}=new ArrayList<${attribute.scalarType}>();
|
||||
<#else>
|
||||
<#lt/> private ${attribute.scalarType} ${attribute.name};
|
||||
</#if>
|
||||
</#list>
|
||||
</#macro>
|
||||
|
||||
<#macro getSet attributes>
|
||||
<#list attributes as attribute>
|
||||
<#if attribute.name!="objectClass">
|
||||
<#if attribute.isMultiValued>
|
||||
<#lt/> public void add${attribute.name?cap_first}(${attribute.scalarType} ${attribute.name}) {
|
||||
<#lt/> this.${attribute.name}.add(${attribute.name});
|
||||
<#lt/> }
|
||||
|
||||
<#lt/> public void remove${attribute.name?cap_first}(${attribute.scalarType} ${attribute.name}) {
|
||||
<#lt/> this.${attribute.name}.remove(${attribute.name});
|
||||
<#lt/> }
|
||||
|
||||
<#lt/> public Iterator<${attribute.scalarType}> get${attribute.name?cap_first}Iterator() {
|
||||
<#lt/> return ${attribute.name}.iterator();
|
||||
<#lt/> }
|
||||
|
||||
<#else>
|
||||
<#lt/> public ${attribute.scalarType} get${attribute.name?cap_first}() {
|
||||
<#lt/> return ${attribute.name};
|
||||
<#lt/> }
|
||||
|
||||
<#lt/> public void set${attribute.name?cap_first}(${attribute.scalarType} ${attribute.name}) {
|
||||
<#lt/> this.${attribute.name}=${attribute.name};
|
||||
<#lt/> }
|
||||
</#if>
|
||||
<#else>
|
||||
<#lt/> public Iterator<String> get${attribute.name?cap_first}Iterator() {
|
||||
<#lt/> return Collections.unmodifiableList(${attribute.name}).iterator();
|
||||
<#lt/> }
|
||||
|
||||
</#if>
|
||||
</#list>
|
||||
</#macro>
|
||||
|
||||
<#macro equalsCode attributes>
|
||||
<#list attributes as attribute>
|
||||
<#lt/> append(${attribute.name}, other.${attribute.name}).
|
||||
</#list>
|
||||
</#macro>
|
||||
|
||||
<#macro hashCode attributes>
|
||||
<#list attributes as attribute>
|
||||
<#lt/> append(${attribute.name}).
|
||||
</#list>
|
||||
</#macro>
|
||||
|
||||
package ${package};
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||
|
||||
import javax.naming.Name;
|
||||
<#list imports as import>
|
||||
import ${import.packageName}.${import.className};
|
||||
</#list>
|
||||
|
||||
import static org.springframework.ldap.odm.annotations.Attribute.*;
|
||||
import org.springframework.ldap.odm.annotations.Attribute;
|
||||
import org.springframework.ldap.odm.annotations.Entry;
|
||||
import org.springframework.ldap.odm.annotations.Id;
|
||||
|
||||
/**
|
||||
* Generated by Spring LDAP ODM to represent the LDAP object classes:
|
||||
* <ul>
|
||||
<#list schema.objectClass as objectClass>
|
||||
* <li>${objectClass}</li>
|
||||
</#list>
|
||||
* </ul>
|
||||
*/
|
||||
@Entry(objectClasses={<@commaSeparatedList schema.objectClass/>})
|
||||
public final class ${class} {
|
||||
|
||||
@Id
|
||||
private Name dn;
|
||||
<@doAttribute schema.must/>
|
||||
<@doAttribute schema.may/>
|
||||
public Name getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
public void setDn(Name dn) {
|
||||
this.dn=dn;
|
||||
}
|
||||
|
||||
<@getSet schema.must/>
|
||||
<@getSet schema.may/>
|
||||
<#lt/> @Override
|
||||
<#lt/> public String toString() {
|
||||
<#lt/>
|
||||
<#lt/> return new ToStringBuilder(this).
|
||||
<#lt/> append("dn", dn).
|
||||
<#list schema.must as attribute>
|
||||
<#lt/> append("${attribute.name}", ${attribute.name}).
|
||||
</#list>
|
||||
<#list schema.may as attribute>
|
||||
<#lt/> append("${attribute.name}", ${attribute.name}).
|
||||
</#list>
|
||||
<#lt/> toString();
|
||||
<#lt/> }
|
||||
|
||||
<#lt/> @Override
|
||||
<#lt/> public int hashCode() {
|
||||
<#lt> return new HashCodeBuilder().
|
||||
<@hashCode schema.must/>
|
||||
<@hashCode schema.may/>
|
||||
<#lt/> toHashCode();
|
||||
<#lt/> }
|
||||
|
||||
<#lt/> @Override
|
||||
<#lt/> public boolean equals(Object obj) {
|
||||
<#lt/> if (this == obj)
|
||||
<#lt/> return true;
|
||||
<#lt/> if (obj == null)
|
||||
<#lt/> return false;
|
||||
<#lt/> if (getClass() != obj.getClass())
|
||||
<#lt/> return false;
|
||||
<#lt/>
|
||||
<#lt/> ${class} other = (${class}) obj;
|
||||
<#lt/>
|
||||
<#lt/> return new EqualsBuilder().
|
||||
<#lt/> append(dn, other.dn).
|
||||
<@equalsCode schema.must/>
|
||||
<@equalsCode schema.may/>
|
||||
<#lt/> isEquals();
|
||||
<#lt/> }
|
||||
}
|
||||
|
||||
|
||||
137
odm/src/test/java/org/springframework/ldap/odm/test/OrganizationalUnit.java
Executable file
137
odm/src/test/java/org/springframework/ldap/odm/test/OrganizationalUnit.java
Executable file
@@ -0,0 +1,137 @@
|
||||
package org.springframework.ldap.odm.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
import org.springframework.ldap.odm.annotations.Attribute;
|
||||
import org.springframework.ldap.odm.annotations.Entry;
|
||||
import org.springframework.ldap.odm.annotations.Id;
|
||||
|
||||
/**
|
||||
* Automatically generated to represent the LDAP object classes
|
||||
* "organizationalunit", "top".
|
||||
*/
|
||||
@Entry(objectClasses = { "organizationalunit", "top" })
|
||||
public final class OrganizationalUnit {
|
||||
|
||||
@Id
|
||||
private Name dn;
|
||||
|
||||
@Attribute(name = "objectClass", syntax = "1.3.6.1.4.1.1466.115.121.1.38")
|
||||
private List<String> objectClass = new ArrayList<String>();
|
||||
|
||||
@Attribute(name = "ou", syntax = "1.3.6.1.4.1.1466.115.121.1.15")
|
||||
private String ou;
|
||||
|
||||
@Attribute(name = "street", syntax = "1.3.6.1.4.1.1466.115.121.1.15")
|
||||
private String street;
|
||||
|
||||
@Attribute(name = "description", syntax = "1.3.6.1.4.1.1466.115.121.1.15")
|
||||
private String description;
|
||||
|
||||
public OrganizationalUnit() {
|
||||
}
|
||||
|
||||
public OrganizationalUnit(Name dn, String street, String description) {
|
||||
this.dn = dn;
|
||||
this.street = street;
|
||||
this.description = description;
|
||||
|
||||
objectClass.add("top");
|
||||
objectClass.add("organizationalUnit");
|
||||
|
||||
|
||||
int size = dn.size();
|
||||
if (size > 1) {
|
||||
ou = dn.get(size - 1).split("=")[1];
|
||||
} else {
|
||||
ou = "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Name getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
public void setDn(Name dn) {
|
||||
this.dn = dn;
|
||||
}
|
||||
|
||||
public List<String> getObjectClasses() {
|
||||
return Collections.unmodifiableList(objectClass);
|
||||
}
|
||||
|
||||
public String getOu() {
|
||||
return ou;
|
||||
}
|
||||
|
||||
public String getStreet() {
|
||||
return street;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("objectClasses=%1$s | dn=%2$s | ou=%3$s | street=%4$s | description=%5$s", objectClass,
|
||||
dn, ou, street, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((description == null) ? 0 : description.hashCode());
|
||||
result = prime * result + ((dn == null) ? 0 : dn.hashCode());
|
||||
result = prime * result + ((objectClass == null) ? 0 : new HashSet<String>(objectClass).hashCode());
|
||||
result = prime * result + ((ou == null) ? 0 : ou.hashCode());
|
||||
result = prime * result + ((street == null) ? 0 : street.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
OrganizationalUnit other = (OrganizationalUnit) obj;
|
||||
if (description == null) {
|
||||
if (other.description != null)
|
||||
return false;
|
||||
} else if (!description.equals(other.description))
|
||||
return false;
|
||||
if (dn == null) {
|
||||
if (other.dn != null)
|
||||
return false;
|
||||
} else if (!dn.equals(other.dn))
|
||||
return false;
|
||||
if (objectClass == null) {
|
||||
if (other.objectClass != null)
|
||||
return false;
|
||||
} else
|
||||
if (objectClass.size()!=other.objectClass.size() ||
|
||||
!(new HashSet<String>(objectClass)).equals(new HashSet<String>(other.objectClass)))
|
||||
return false;
|
||||
if (ou == null) {
|
||||
if (other.ou != null)
|
||||
return false;
|
||||
} else if (!ou.equals(other.ou))
|
||||
return false;
|
||||
if (street == null) {
|
||||
if (other.street != null)
|
||||
return false;
|
||||
} else if (!street.equals(other.street))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
201
odm/src/test/java/org/springframework/ldap/odm/test/Person.java
Executable file
201
odm/src/test/java/org/springframework/ldap/odm/test/Person.java
Executable file
@@ -0,0 +1,201 @@
|
||||
package org.springframework.ldap.odm.test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
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 org.springframework.ldap.odm.annotations.Attribute.Type;
|
||||
|
||||
// Simple LDAP entry for testing
|
||||
@Entry(objectClasses = { "inetorgperson", "organizationalperson", "person", "top" })
|
||||
public final class Person {
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(Name dn, String surname, List<String> desc, int telephoneNumber, byte[] jpegPhoto) {
|
||||
this.dn = dn;
|
||||
this.surname = surname;
|
||||
this.desc = desc;
|
||||
this.telephoneNumber = telephoneNumber;
|
||||
this.jpegPhoto = jpegPhoto;
|
||||
objectClasses = new ArrayList<String>();
|
||||
objectClasses.add("top");
|
||||
objectClasses.add("person");
|
||||
objectClasses.add("organizationalPerson");
|
||||
objectClasses.add("inetOrgPerson");
|
||||
int size = dn.size();
|
||||
if (size > 1) {
|
||||
cn = dn.get(size - 1).split("=")[1];
|
||||
} else {
|
||||
cn = "";
|
||||
}
|
||||
}
|
||||
|
||||
@Transient
|
||||
private String someRandomField = null;
|
||||
|
||||
@Transient
|
||||
private List<String> someRandomList = new ArrayList<String>();
|
||||
|
||||
@Attribute(name = "objectClass")
|
||||
private List<String> objectClasses;
|
||||
|
||||
@Id
|
||||
private Name dn;
|
||||
|
||||
// No annotation on purpose!
|
||||
private String cn;
|
||||
|
||||
@Attribute(name = "sn")
|
||||
private String surname;
|
||||
|
||||
// Everything should be sets and in search operations also as results can be in any order
|
||||
@Attribute(name = "description")
|
||||
private List<String> desc;
|
||||
|
||||
@Attribute
|
||||
private int telephoneNumber;
|
||||
|
||||
@Attribute(type = Type.BINARY)
|
||||
byte[] jpegPhoto;
|
||||
|
||||
public Name getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
public void setDn(Name dn) {
|
||||
this.dn = dn;
|
||||
}
|
||||
|
||||
public String getCn() {
|
||||
return cn;
|
||||
}
|
||||
|
||||
public void setCn(String cn) {
|
||||
this.cn = cn;
|
||||
}
|
||||
|
||||
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 int getTelephoneNumber() {
|
||||
return telephoneNumber;
|
||||
}
|
||||
|
||||
public void setTelephoneNumber(int telephoneNumber) {
|
||||
this.telephoneNumber = telephoneNumber;
|
||||
}
|
||||
|
||||
public byte[] getJpegPhoto() {
|
||||
return jpegPhoto;
|
||||
}
|
||||
|
||||
public void setJpegPhoto(byte[] jpegPhoto) {
|
||||
this.jpegPhoto = jpegPhoto;
|
||||
}
|
||||
|
||||
public List<String> getObjectClasses() {
|
||||
return objectClasses;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder jpegString=new StringBuilder();
|
||||
if (jpegPhoto!=null) {
|
||||
for (byte b:jpegPhoto) {
|
||||
jpegString.append(Byte.toString(b));
|
||||
}
|
||||
}
|
||||
|
||||
return String.format(
|
||||
"objectClasses=%1$s | dn=%2$s | cn=%3$s | sn=%4$s | desc=%5$s | telephoneNumber=%6$s | jpegPhoto=%7$s",
|
||||
objectClasses, dn, cn, surname, desc, telephoneNumber, jpegString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((cn == null) ? 0 : cn.hashCode());
|
||||
result = prime * result + ((desc == null) ? 0 : new HashSet<String>(desc).hashCode());
|
||||
result = prime * result + ((dn == null) ? 0 : dn.hashCode());
|
||||
result = prime * result + Arrays.hashCode(jpegPhoto);
|
||||
result = prime * result + ((objectClasses == null) ? 0 : new HashSet<String>(objectClasses).hashCode());
|
||||
result = prime * result + ((someRandomField == null) ? 0 : someRandomField.hashCode());
|
||||
result = prime * result + ((someRandomList == null) ? 0 : someRandomList.hashCode());
|
||||
result = prime * result + ((surname == null) ? 0 : surname.hashCode());
|
||||
result = prime * result + telephoneNumber;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
Person other = (Person) obj;
|
||||
if (cn == null) {
|
||||
if (other.cn != null)
|
||||
return false;
|
||||
} else if (!cn.equals(other.cn))
|
||||
return false;
|
||||
if (desc == null) {
|
||||
if (other.desc != null)
|
||||
return false;
|
||||
} else if (desc.size()!=other.desc.size() || !(new HashSet<String>(desc)).equals(new HashSet<String>(other.desc)))
|
||||
return false;
|
||||
if (dn == null) {
|
||||
if (other.dn != null)
|
||||
return false;
|
||||
} else if (!dn.equals(other.dn))
|
||||
return false;
|
||||
if (!Arrays.equals(jpegPhoto, other.jpegPhoto))
|
||||
return false;
|
||||
if (objectClasses == null) {
|
||||
if (other.objectClasses != null)
|
||||
return false;
|
||||
} else if (desc.size()!=other.desc.size() || !(new HashSet<String>(objectClasses)).equals(new HashSet<String>(other.objectClasses)))
|
||||
return false;
|
||||
if (someRandomField == null) {
|
||||
if (other.someRandomField != null)
|
||||
return false;
|
||||
} else if (!someRandomField.equals(other.someRandomField))
|
||||
return false;
|
||||
if (someRandomList == null) {
|
||||
if (other.someRandomList != null)
|
||||
return false;
|
||||
} else if (!someRandomList.equals(other.someRandomList))
|
||||
return false;
|
||||
if (surname == null) {
|
||||
if (other.surname != null)
|
||||
return false;
|
||||
} else if (!surname.equals(other.surname))
|
||||
return false;
|
||||
if (telephoneNumber != other.telephoneNumber)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
207
odm/src/test/java/org/springframework/ldap/odm/test/TestConverterManager.java
Executable file
207
odm/src/test/java/org/springframework/ldap/odm/test/TestConverterManager.java
Executable file
@@ -0,0 +1,207 @@
|
||||
package org.springframework.ldap.odm.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.BitSet;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ldap.odm.test.utils.ExecuteRunnable;
|
||||
import org.springframework.ldap.odm.test.utils.RunnableTest;
|
||||
import org.springframework.ldap.odm.typeconversion.ConverterException;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.Converter;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.ConverterManagerImpl;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.converters.FromStringConverter;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.converters.ToStringConverter;
|
||||
|
||||
public final class TestConverterManager {
|
||||
private ConverterManagerImpl converterManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
converterManager = new ConverterManagerImpl();
|
||||
|
||||
Converter ptc = new FromStringConverter();
|
||||
converterManager.addConverter(String.class, "", Byte.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Short.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Integer.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Long.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Double.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Float.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Boolean.class, ptc);
|
||||
|
||||
Converter tsc = new ToStringConverter();
|
||||
converterManager.addConverter(Byte.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Short.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Integer.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Long.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Double.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Float.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Boolean.class, "", String.class, tsc);
|
||||
|
||||
Converter uric = new UriConverter();
|
||||
converterManager.addConverter(URI.class, "", String.class, uric);
|
||||
converterManager.addConverter(String.class, "", URI.class, uric);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
converterManager = null;
|
||||
}
|
||||
|
||||
private static class ConverterTestData<T> {
|
||||
public final Class<T> destClass;
|
||||
|
||||
public final Object sourceData;
|
||||
|
||||
public final T expectedValue;
|
||||
|
||||
public final String syntax;
|
||||
|
||||
public ConverterTestData(Object sourceData, Class<T> destClass, T expectedValue) {
|
||||
this(sourceData, "", destClass, expectedValue);
|
||||
}
|
||||
|
||||
public ConverterTestData(Object sourceData, String syntax, Class<T> destClass, T expectedValue) {
|
||||
this.destClass = destClass;
|
||||
this.sourceData = sourceData;
|
||||
this.expectedValue = expectedValue;
|
||||
this.syntax = syntax;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("sourceData=%1$s | syntax=%2$s | destClass=%3$s | expectedValue=%4$s", sourceData,
|
||||
syntax, destClass, expectedValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Class to Class conversion without any syntaxes
|
||||
@Test
|
||||
public void basicTypeConverion() throws Exception {
|
||||
final ConverterTestData<?>[] primitiveTypeTests = new ConverterTestData<?>[] {
|
||||
new ConverterTestData<Byte>("33", Byte.class, Byte.valueOf((byte)33)),
|
||||
new ConverterTestData<Byte>("-88", Byte.class, Byte.valueOf((byte)-88)),
|
||||
new ConverterTestData<Short>("666", Short.class, Short.valueOf((short)666)),
|
||||
new ConverterTestData<Short>("-123", Short.class, Short.valueOf((short)-123)),
|
||||
new ConverterTestData<Integer>("123", Integer.class, Integer.valueOf(123)),
|
||||
new ConverterTestData<Integer>("-500", Integer.class, Integer.valueOf(-500)),
|
||||
new ConverterTestData<Long>("123456", Long.class, Long.valueOf(123456)),
|
||||
new ConverterTestData<Long>("-654321", Long.class, Long.valueOf(-654321)),
|
||||
new ConverterTestData<Double>("2", Double.class, Double.valueOf(2)),
|
||||
new ConverterTestData<Double>("-0.4", Double.class, Double.valueOf(-0.4)),
|
||||
new ConverterTestData<Float>("666", Float.class, Float.valueOf(666)),
|
||||
new ConverterTestData<Float>("-0.75", Float.class, Float.valueOf(-0.75F)),
|
||||
new ConverterTestData<Boolean>("false", Boolean.class, Boolean.FALSE),
|
||||
new ConverterTestData<Boolean>("TRUE", Boolean.class, Boolean.TRUE),
|
||||
new ConverterTestData<String>("This is a string", String.class, "This is a string"),
|
||||
new ConverterTestData<String>("This is another String", String.class, "This is another String"),
|
||||
new ConverterTestData<String>((byte)66, String.class, "66"),
|
||||
new ConverterTestData<String>((int)1234, String.class, "1234"),
|
||||
new ConverterTestData<String>((int)-9876, String.class, "-9876"),
|
||||
new ConverterTestData<URI>("http://google.com/", URI.class, new URI("http://google.com/")),
|
||||
new ConverterTestData<URI>("http://apache.org/index.html", URI.class, new URI(
|
||||
"http://apache.org/index.html")),
|
||||
new ConverterTestData<String>(new URI("http://google.com/"), String.class, "http://google.com/"),
|
||||
new ConverterTestData<String>(new URI("http://apache.org/index.html"), String.class,
|
||||
"http://apache.org/index.html") };
|
||||
|
||||
new ExecuteRunnable<ConverterTestData<?>>().runTests(new RunnableTest<ConverterTestData<?>>() {
|
||||
public void runTest(ConverterTestData<?> testData) {
|
||||
assertEquals(testData.expectedValue, converterManager.convert(testData.sourceData, "",
|
||||
testData.destClass));
|
||||
}
|
||||
}, primitiveTypeTests);
|
||||
}
|
||||
|
||||
private static class SquaredConverter implements Converter {
|
||||
public <T> T convert(Object source, Class<T> toClass) throws Exception {
|
||||
Integer intSource = null;
|
||||
|
||||
if (source.getClass() == String.class) {
|
||||
intSource = new Integer((String)source);
|
||||
}
|
||||
else {
|
||||
if (source.getClass() == Integer.class) {
|
||||
intSource = (Integer)source;
|
||||
}
|
||||
}
|
||||
|
||||
Integer result = null;
|
||||
if (intSource != null) {
|
||||
result = intSource * intSource;
|
||||
}
|
||||
|
||||
return toClass.cast(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CubedConverter implements Converter {
|
||||
public <T> T convert(Object source, Class<T> toClass) throws Exception {
|
||||
Integer intSource = null;
|
||||
|
||||
if (source.getClass() == String.class) {
|
||||
intSource = new Integer((String)source);
|
||||
}
|
||||
else {
|
||||
if (source.getClass() == Integer.class) {
|
||||
intSource = (Integer)source;
|
||||
}
|
||||
}
|
||||
|
||||
Integer result = null;
|
||||
if (intSource != null) {
|
||||
result = intSource * intSource * intSource;
|
||||
}
|
||||
|
||||
return toClass.cast(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests using syntaxes for "finer grained" mapping
|
||||
@Test
|
||||
public void syntaxBasedConversion() throws Exception {
|
||||
Converter squaredConverter = new SquaredConverter();
|
||||
converterManager.addConverter(String.class, "1", Integer.class, squaredConverter);
|
||||
converterManager.addConverter(Integer.class, "1", Integer.class, squaredConverter);
|
||||
Converter cubedConverter = new CubedConverter();
|
||||
converterManager.addConverter(String.class, "2", Integer.class, cubedConverter);
|
||||
converterManager.addConverter(Integer.class, "3", Integer.class, cubedConverter);
|
||||
|
||||
final ConverterTestData<?>[] syntaxTests = new ConverterTestData<?>[] {
|
||||
new ConverterTestData<Integer>("3", "", Integer.class, Integer.valueOf(3)),
|
||||
new ConverterTestData<Integer>("4", "", Integer.class, Integer.valueOf(4)),
|
||||
new ConverterTestData<Integer>(5, "", Integer.class, Integer.valueOf(5)),
|
||||
new ConverterTestData<Integer>(6, "", Integer.class, Integer.valueOf(6)),
|
||||
new ConverterTestData<Integer>("3", "1", Integer.class, Integer.valueOf(9)),
|
||||
new ConverterTestData<Integer>("4", "1", Integer.class, Integer.valueOf(16)),
|
||||
new ConverterTestData<Integer>(5, "1", Integer.class, Integer.valueOf(25)),
|
||||
new ConverterTestData<Integer>(6, "1", Integer.class, Integer.valueOf(36)),
|
||||
new ConverterTestData<Integer>("3", "2", Integer.class, Integer.valueOf(27)),
|
||||
new ConverterTestData<Integer>("4", "2", Integer.class, Integer.valueOf(64)),
|
||||
new ConverterTestData<Integer>(5, "3", Integer.class, Integer.valueOf(125)),
|
||||
new ConverterTestData<Integer>(6, "3", Integer.class, Integer.valueOf(216)), };
|
||||
|
||||
new ExecuteRunnable<ConverterTestData<?>>().runTests(new RunnableTest<ConverterTestData<?>>() {
|
||||
public void runTest(ConverterTestData<?> testData) {
|
||||
assertEquals(testData.expectedValue, converterManager.convert(testData.sourceData, testData.syntax,
|
||||
testData.destClass));
|
||||
}
|
||||
}, syntaxTests);
|
||||
|
||||
}
|
||||
|
||||
// No converter for classes
|
||||
@Test(expected = ConverterException.class)
|
||||
public void noClassConverter() throws Exception {
|
||||
converterManager.convert(BitSet.class, "", Integer.class);
|
||||
}
|
||||
|
||||
// Invalid syntax so converter fails
|
||||
@Test(expected = ConverterException.class)
|
||||
public void invalidSyntax() throws Exception {
|
||||
converterManager.convert(String.class, "not a uri", URI.class);
|
||||
}
|
||||
}
|
||||
622
odm/src/test/java/org/springframework/ldap/odm/test/TestLdap.java
Executable file
622
odm/src/test/java/org/springframework/ldap/odm/test/TestLdap.java
Executable file
@@ -0,0 +1,622 @@
|
||||
package org.springframework.ldap.odm.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.naming.Name;
|
||||
import javax.naming.directory.SearchControls;
|
||||
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.cli.PosixParser;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.ldap.NameNotFoundException;
|
||||
import org.springframework.ldap.core.ContextSource;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
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.core.OdmException;
|
||||
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;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.Converter;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.ConverterManagerImpl;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.converters.FromStringConverter;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.converters.ToStringConverter;
|
||||
import org.springframework.ldap.test.LdapTestUtils;
|
||||
|
||||
// Tests all OdmManager functions
|
||||
public final class TestLdap {
|
||||
private static final Log LOG = LogFactory.getLog(TestLdap.class);
|
||||
|
||||
// Base DN for test data
|
||||
private static final DistinguishedName baseName = new DistinguishedName("o=Whoniverse");
|
||||
|
||||
// This port MUST be free on local host for these unit tests to function.
|
||||
private static int port;
|
||||
|
||||
// Maximum number of objects to return in testing
|
||||
private static final long COUNT_LIMIT=20;
|
||||
|
||||
// Maximum time to wait for results in testing (ms)
|
||||
private static final int TIME_LIMIT=60000;
|
||||
|
||||
private static SearchControls searchControls=
|
||||
new SearchControls(SearchControls.SUBTREE_SCOPE,
|
||||
COUNT_LIMIT,
|
||||
TIME_LIMIT,
|
||||
null,
|
||||
true,
|
||||
false);
|
||||
|
||||
private ConverterManagerImpl converterManager;
|
||||
|
||||
private ContextSource contextSource;
|
||||
|
||||
private OdmManager odmManager;
|
||||
|
||||
// Base 64 encoded jpeg photo used to test binary reading and writing
|
||||
private static byte[] photo;
|
||||
static {
|
||||
try {
|
||||
String photoString="/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkS"+
|
||||
"Ew8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRg"+
|
||||
"yIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wA"+
|
||||
"ARCAAnABoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAA"+
|
||||
"gEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcY"+
|
||||
"GRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipK"+
|
||||
"TlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8v"+
|
||||
"P09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFB"+
|
||||
"AQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygp"+
|
||||
"KjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJm"+
|
||||
"aoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9"+
|
||||
"oADAMBAAIRAxEAPwDx2z0mK6gV/tSo2SCpUHGD9Qa2vDvgGfX7+eI3ot4EOEmEefMOOcZPQdOvW"+
|
||||
"s3VLGzsWiihUvM0ayONxATPIHXk4wfxFdd4L1aw0rw3eS3V3GHWRswkgugwCpVSckEk9OhB9azu"+
|
||||
"2ro6lCKnyyZwmvaHcaBrV1plxKkkluw+dRkMCAQfbgjiqi27sob1GfuVc1q9n1XVL3UdqxJI/wD"+
|
||||
"q/MXIXGAMd+B2HWoUZ1jUbZeABwKqzMJWT0Ld3cfaJ5ZFYtnCg+oUBf6V2XhvwVpcvhxfFXibVF"+
|
||||
"tNK3lEgjJ3yMGK7SRzkkHhRnHORXJQ6exVXB2r1K9Cfx7V0UWpeD7S3WJ9N1TUPLYssV7cBYkZg"+
|
||||
"AxAU4zwOcZ4HoKtppWQ4tSk5SNPx14o8M3/AITXT/D2kRRxB1jE4RIyuOc7ME84PzcHr2PPmX2y"+
|
||||
"YcZX8q37fQxPpqzsH8xvuqAeeuO/0H3e9Zn2dxwbK5JHXCH/AOJqNOhdSM1ZzVjRNwRbjGRxxzn"+
|
||||
"FdRouijV9Pi1C6toEMqn7MsK7SqBiCxOSSSVI5J4x6miiliW1FMzpbszreUw3H2VcuscpGT3UE9"+
|
||||
"Rn0HStE+IrZSQbEuRxvU4De4yc0UVFKCnuerms2vZryP/Z";
|
||||
|
||||
byte[] photoBytes=photoString.getBytes("US-ASCII");
|
||||
photo=Base64.decodeBase64(photoBytes);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Problem decoding photo", e);
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
// Added because the close down of Apache DS on Linux does
|
||||
// not seem to free up its port.
|
||||
port=GetFreePort.getFreePort();
|
||||
|
||||
// Start an LDAP server and import test data
|
||||
LdapTestUtils.startApacheDirectoryServer(port, baseName.toString(), "odm-test", "", "", null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
LdapTestUtils.destroyApacheDirectoryServer("", "");
|
||||
}
|
||||
|
||||
private static ContextSource getContextSource(String url, String username, String password) throws Exception {
|
||||
LdapContextSource contextSource = new LdapContextSource();
|
||||
contextSource.setUrl(url);
|
||||
contextSource.setUserDn(username);
|
||||
contextSource.setPassword(password);
|
||||
contextSource.setPooled(false);
|
||||
contextSource.afterPropertiesSet();
|
||||
|
||||
return contextSource;
|
||||
}
|
||||
|
||||
// Pulled out of setup method to allow it to be called with parameters from main as
|
||||
// an integration test
|
||||
public void setUp(String url, String username, String password) throws Exception {
|
||||
// Create some basic converters and a converter manager
|
||||
converterManager = new ConverterManagerImpl();
|
||||
|
||||
Converter ptc = new FromStringConverter();
|
||||
converterManager.addConverter(String.class, "", Byte.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Short.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Integer.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Long.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Double.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Float.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Boolean.class, ptc);
|
||||
|
||||
Converter tsc = new ToStringConverter();
|
||||
converterManager.addConverter(Byte.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Short.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Integer.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Long.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Double.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Float.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Boolean.class, "", String.class, tsc);
|
||||
|
||||
// Bind to the directory
|
||||
contextSource = getContextSource(url, username, password);
|
||||
|
||||
// Clear out any old data - and load the test data
|
||||
LdapTestUtils.cleanAndSetup(contextSource, baseName, new ClassPathResource("testdata.ldif"));
|
||||
|
||||
// Create our OdmManager
|
||||
Set<Class<?>> managedClasses=new HashSet<Class<?>>();
|
||||
managedClasses.add(Person.class);
|
||||
managedClasses.add(OrganizationalUnit.class);
|
||||
odmManager = new OdmManagerImpl(converterManager, contextSource, managedClasses);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
setUp("ldap://127.0.0.1:" + port, "", "");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
LdapTestUtils.clearSubContexts(contextSource, baseName);
|
||||
|
||||
odmManager=null;
|
||||
contextSource=null;
|
||||
converterManager=null;
|
||||
}
|
||||
|
||||
private enum PersonName {
|
||||
WILLIAM(0), PATRICK(1), JON(2), TOM(3), PETER(4), DAVROS(5), DALEKS(6), MASTER(7);
|
||||
|
||||
private int index;
|
||||
|
||||
private PersonName(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
private Person[] personTestData=new Person[] {
|
||||
new Person(new DistinguishedName("cn=William Hartnell,ou=Doctors,o=Whoniverse"), "Hartnell", Arrays
|
||||
.asList(new String[] { "First Doctor", "Grumpy" }), 1, null),
|
||||
new Person(new DistinguishedName("cn=Patrick Troughton,ou=Doctors,o=Whoniverse"), "Troughton", Arrays
|
||||
.asList(new String[] { "Second Doctor", "Clown" }), 2, null),
|
||||
new Person(new DistinguishedName("cn=Jon Pertwee,ou=Doctors,o=Whoniverse"), "Pertwee", Arrays
|
||||
.asList(new String[] { "Third Doctor", "Dandy" }), 3, null),
|
||||
new Person(new DistinguishedName("cn=Tom Baker,ou=Doctors,o=Whoniverse"), "Baker", Arrays
|
||||
.asList(new String[] { "Fourth Doctor", "The one and only!" }), 4, null),
|
||||
new Person(new DistinguishedName("cn=Peter Davison,ou=Doctors,o=Whoniverse"), "Davison", Arrays
|
||||
.asList(new String[] { "Fifth Doctor" }), 5, null),
|
||||
new Person(new DistinguishedName("cn=Davros,ou=Enemies,o=Whoniverse"), "Unknown", Arrays
|
||||
.asList(new String[] { "Creator of the Daleks", "Kaled head scientist" }), 0, null),
|
||||
new Person(new DistinguishedName("cn=Daleks,ou=Enemies,o=Whoniverse"), "NA", Arrays
|
||||
.asList(new String[] { "The Doctor's greatest foe" }), 0, null),
|
||||
new Person(new DistinguishedName("cn=Master,ou=Enemies,o=Whoniverse"), "Unknown", Arrays
|
||||
.asList(new String[] { "An evil Time Lord" }), 0, photo), };
|
||||
|
||||
|
||||
// Read various entries from the sample data set and check they are what we'd expect.
|
||||
@Test
|
||||
public void read() throws Exception {
|
||||
new ExecuteRunnable<Person>().runTests(new RunnableTest<Person>() {
|
||||
public void runTest(Person testData) {
|
||||
Name dn = testData.getDn();
|
||||
LOG.debug(String.format("reading - %1$s", dn));
|
||||
Person personEntry = odmManager.read(Person.class, dn);
|
||||
LOG.debug(String.format("read - %1$s", personEntry));
|
||||
assertEquals(testData, personEntry);
|
||||
}
|
||||
}, personTestData);
|
||||
}
|
||||
|
||||
private static class SearchTestData {
|
||||
private String search;
|
||||
|
||||
private SearchControls searchScope;
|
||||
|
||||
private Person[] people;
|
||||
|
||||
public SearchTestData(String search, SearchControls searchScope, Person[] people) {
|
||||
this.search = search;
|
||||
this.searchScope = searchScope;
|
||||
this.people = people;
|
||||
}
|
||||
}
|
||||
|
||||
private SearchTestData[] searchTestData = {
|
||||
new SearchTestData("(sn=Unknown)",
|
||||
searchControls,
|
||||
new Person[] {
|
||||
personTestData[PersonName.DAVROS.getIndex()],
|
||||
personTestData[PersonName.MASTER.getIndex()] }),
|
||||
new SearchTestData("(description=*Doctor)",
|
||||
searchControls,
|
||||
new Person[] {
|
||||
personTestData[PersonName.WILLIAM.getIndex()],
|
||||
personTestData[PersonName.PATRICK.getIndex()],
|
||||
personTestData[PersonName.JON.getIndex()],
|
||||
personTestData[PersonName.TOM.getIndex()],
|
||||
personTestData[PersonName.PETER.getIndex()] }),
|
||||
};
|
||||
|
||||
// Carry out various searches against the test data set and check the results are what we'd expect.
|
||||
@Test
|
||||
public void search() throws Exception {
|
||||
new ExecuteRunnable<SearchTestData>().runTests(new RunnableTest<SearchTestData>() {
|
||||
public void runTest(SearchTestData testData) {
|
||||
String search = testData.search;
|
||||
LOG.debug(String.format("searching - %1$s", search));
|
||||
List<Person> results = odmManager.search(Person.class, baseName, testData.search, testData.searchScope);
|
||||
LOG.debug(String.format("found - %1$s", results));
|
||||
assertEquals(new HashSet<Person>(Arrays.asList(testData.people)), new HashSet<Person>(results));
|
||||
}
|
||||
}, searchTestData);
|
||||
}
|
||||
|
||||
private enum OrganizationalName {
|
||||
ENEMIES(0), ASSISTANTS(1), DOCTORS(2);
|
||||
|
||||
private int index;
|
||||
|
||||
private OrganizationalName(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
private static OrganizationalUnit ouTestData[]=new OrganizationalUnit[] {
|
||||
new OrganizationalUnit(new DistinguishedName("ou=Enemies,o=Whoniverse"), "Acacia Avenue", "The bad guys"),
|
||||
new OrganizationalUnit(new DistinguishedName("ou=Assistants,o=Whoniverse"), "Somewhere in space", "The plucky helpers"),
|
||||
new OrganizationalUnit(new DistinguishedName("ou=Doctors,o=Whoniverse"), "Somewhere in time", "Our hero"),
|
||||
};
|
||||
|
||||
// Check everything works OK with a second managed class
|
||||
@Test
|
||||
public void testSecondOc() {
|
||||
LOG.debug("Reading all organizatinalUnits");
|
||||
List<OrganizationalUnit> allOus=odmManager.findAll(OrganizationalUnit.class, baseName, searchControls);
|
||||
LOG.debug(String.format("Found - %1$s", allOus));
|
||||
assertEquals(new HashSet<OrganizationalUnit>(Arrays.asList(ouTestData)), new HashSet<OrganizationalUnit>(allOus));
|
||||
|
||||
OrganizationalUnit testOu=ouTestData[OrganizationalName.ASSISTANTS.getIndex()];
|
||||
LOG.debug(String.format("Reading - %1$s", testOu.getDn()));
|
||||
OrganizationalUnit ou=odmManager.read(OrganizationalUnit.class, testOu.getDn());
|
||||
LOG.debug(String.format("Found - %1$s", ou));
|
||||
assertEquals(testOu, ou);
|
||||
}
|
||||
|
||||
// Find all entries managed by the OdmManager in the test data set and check they are what we expect.
|
||||
@Test
|
||||
public void findAll() throws Exception {
|
||||
LOG.debug("finding all people");
|
||||
List<Person> allPeople = odmManager.findAll(Person.class, baseName, searchControls);
|
||||
LOG.debug(String.format("found %1$s", allPeople));
|
||||
assertEquals(new HashSet<Person>(Arrays.asList(personTestData)), new HashSet<Person>(allPeople));
|
||||
}
|
||||
|
||||
private Person[] createTestData = {
|
||||
new Person(new DistinguishedName("cn=Colin Baker,ou=Doctors,o=Whoniverse"), "Baker", Arrays
|
||||
.asList(new String[] { "Sixth Doctor" }), 6, null),
|
||||
new Person(new DistinguishedName("cn=Sylvester McCoy,ou=Doctors,o=Whoniverse"), "McCoy", Arrays
|
||||
.asList(new String[] { "Seventh Doctor" }), 7, null),
|
||||
new Person(new DistinguishedName("cn=Paul McGann,ou=Doctors,o=Whoniverse"), "McGann", Arrays
|
||||
.asList(new String[] { "Eigth Doctor" }), 8, photo), };
|
||||
|
||||
// Create some entries, read them back and check they are what we'd expect.
|
||||
@Test
|
||||
public void create() throws Exception {
|
||||
for (Person person : createTestData) {
|
||||
LOG.debug(String.format("creating - %1$s", person));
|
||||
odmManager.create(person);
|
||||
}
|
||||
LOG.debug("Created all, reading back");
|
||||
new ExecuteRunnable<Person>().runTests(new RunnableTest<Person>() {
|
||||
public void runTest(Person testData) {
|
||||
Name dn = testData.getDn();
|
||||
LOG.debug(String.format("reading - %1$s", dn));
|
||||
Person personEntry = odmManager.read(Person.class, dn);
|
||||
LOG.debug(String.format("read - %1$s", personEntry));
|
||||
assertEquals(testData, personEntry);
|
||||
}
|
||||
}, createTestData);
|
||||
}
|
||||
|
||||
// Update an entry from the test data set, read it back and check it is what we'd expect.
|
||||
@Test
|
||||
public void update() throws Exception {
|
||||
Person william = personTestData[PersonName.WILLIAM.getIndex()];
|
||||
william.setTelephoneNumber(666);
|
||||
william.setSurname("Harvey");
|
||||
odmManager.update(william);
|
||||
Person readWilliam = odmManager.read(Person.class, william.getDn());
|
||||
assertEquals(william, readWilliam);
|
||||
}
|
||||
|
||||
private Person[] deleteData = {
|
||||
personTestData[PersonName.JON.getIndex()],
|
||||
personTestData[PersonName.TOM.getIndex()], personTestData[PersonName.DAVROS.getIndex()], };
|
||||
|
||||
private Person[] whatsLeft = {
|
||||
personTestData[PersonName.WILLIAM.getIndex()],
|
||||
personTestData[PersonName.PATRICK.getIndex()], personTestData[PersonName.PETER.getIndex()],
|
||||
personTestData[PersonName.DALEKS.getIndex()], personTestData[PersonName.MASTER.getIndex()], };
|
||||
|
||||
// Delete a some entries from the the test data set and check what's left is what we'd expect
|
||||
@Test
|
||||
public void delete() throws Exception {
|
||||
for (Person toDelete : deleteData) {
|
||||
LOG.debug(String.format("deleting - %1$s", toDelete.getDn()));
|
||||
odmManager.delete(toDelete);
|
||||
}
|
||||
|
||||
List<Person> allPeople = odmManager.findAll(Person.class, baseName, searchControls);
|
||||
assertEquals(new HashSet<Person>(Arrays.asList(whatsLeft)), new HashSet<Person>(allPeople));
|
||||
}
|
||||
|
||||
// Trying to read a non-existant entry should be flagged as an error
|
||||
@Test(expected = NameNotFoundException.class)
|
||||
public void readNonExistant() throws Exception {
|
||||
odmManager.read(Person.class, new DistinguishedName("cn=Hili Harvey,ou=Doctors,o=Whoniverse"));
|
||||
}
|
||||
|
||||
// Read an entry with classes in addition to those supported by the Entry
|
||||
@Test(expected = OdmException.class)
|
||||
public void readAdditionalObjectClasses() throws Exception {
|
||||
odmManager.read(Person.class, new DistinguishedName("cn=Paul Harvey,ou=Doctors,o=Whoniverse"));
|
||||
}
|
||||
|
||||
private final static class NoEntry {
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
Name id;
|
||||
}
|
||||
|
||||
// Every class to be managed must be annotated @Entry
|
||||
@Test(expected = MetaDataException.class)
|
||||
public void noEntryAnnotation() {
|
||||
((OdmManagerImpl)odmManager).addManagedClass(NoEntry.class);
|
||||
}
|
||||
|
||||
@Entry(objectClasses="test")
|
||||
private final static class NoId {
|
||||
}
|
||||
|
||||
// There must be a field with the @Id annotation
|
||||
@Test(expected = MetaDataException.class)
|
||||
public void noId() {
|
||||
((OdmManagerImpl)odmManager).addManagedClass(NoId.class);
|
||||
}
|
||||
|
||||
@Entry(objectClasses="test")
|
||||
private final static class TwoIds {
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
private Name firstId;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
private Name secondId;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public TwoIds() {
|
||||
}
|
||||
}
|
||||
|
||||
// Only one field may be annotated @Id
|
||||
@Test(expected = MetaDataException.class)
|
||||
public void twoIds() {
|
||||
((OdmManagerImpl)odmManager).addManagedClass(TwoIds.class);
|
||||
}
|
||||
|
||||
@Entry(objectClasses="test")
|
||||
public final static class NoConstructor {
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
private Name id;
|
||||
|
||||
public NoConstructor(String aValue) {
|
||||
}
|
||||
}
|
||||
|
||||
// All Entry annotated classes must have a zero argument public constructor
|
||||
@Test(expected = InvalidEntryException.class)
|
||||
public void noConstructor() {
|
||||
((OdmManagerImpl)odmManager).addManagedClass(NoConstructor.class);
|
||||
}
|
||||
|
||||
@Entry(objectClasses="test")
|
||||
public final static class AttributeOnId {
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
@Attribute
|
||||
private Name id;
|
||||
}
|
||||
|
||||
// It is illegal put put both the Id and the Attribute annotation on the same field
|
||||
@Test(expected = MetaDataException.class)
|
||||
public void attributeOnId() {
|
||||
((OdmManagerImpl)odmManager).addManagedClass(AttributeOnId.class);
|
||||
}
|
||||
|
||||
@Entry(objectClasses="test")
|
||||
public final static class IdIsNotAName {
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
private String id;
|
||||
}
|
||||
|
||||
// The field annotation with @Id must be of type javax.naming.Name
|
||||
@Test(expected = MetaDataException.class)
|
||||
public void idIsNotAName() {
|
||||
((OdmManagerImpl)odmManager).addManagedClass(IdIsNotAName.class);
|
||||
}
|
||||
|
||||
@Entry(objectClasses="test")
|
||||
public final static class MissingConverter {
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
private Name id;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private BufferedImage image;
|
||||
}
|
||||
|
||||
// The OdmManager should flag any missing converters when it is instantiated
|
||||
@Test(expected = InvalidEntryException.class)
|
||||
public void missingConverter() {
|
||||
((OdmManagerImpl)odmManager).addManagedClass(MissingConverter.class);
|
||||
}
|
||||
|
||||
@Entry(objectClasses="test")
|
||||
public final static class WrongClassForOc {
|
||||
@SuppressWarnings("unused")
|
||||
@Id
|
||||
private Name id;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Attribute(name="objectClass")
|
||||
private int ocs;
|
||||
}
|
||||
|
||||
// The OdmManager should flag if the objectClass attribute is not of the appropriate type
|
||||
@Test(expected = MetaDataException.class)
|
||||
public void wrongClassForOc() {
|
||||
((OdmManagerImpl)odmManager).addManagedClass(WrongClassForOc.class);
|
||||
}
|
||||
|
||||
// The OdmManager should flag any attempt to use a "unmanaged" class
|
||||
@Test(expected = UnmanagedClassException.class)
|
||||
public void unManagedClass() {
|
||||
((OdmManagerImpl)odmManager).read(Integer.class, baseName);
|
||||
}
|
||||
|
||||
private enum Flag {
|
||||
URL("l", "url"),
|
||||
USERNAME("u", "username"),
|
||||
PASSWORD("p", "password"),
|
||||
HELP("h", "help");
|
||||
|
||||
private String shortName;
|
||||
private String longName;
|
||||
|
||||
private Flag(String shortName, String longName) {
|
||||
this.shortName = shortName;
|
||||
this.longName = longName;
|
||||
}
|
||||
|
||||
public String getShort() {
|
||||
return shortName;
|
||||
}
|
||||
|
||||
public String getLong() {
|
||||
return longName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("short=%1$s, long=%2$s", shortName, longName);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String DEFAULT_LDAP_URL="ldap://localhost:389";
|
||||
private static final String DEFAULT_USERNAME="";
|
||||
private static final String DEFAULT_PASSWORD="";
|
||||
|
||||
private static final Options options = new Options();
|
||||
static {
|
||||
options.addOption(Flag.URL.getShort(), Flag.URL.getLong(), true, "Ldap url to bind to, defaults to "+DEFAULT_LDAP_URL);
|
||||
options.addOption(Flag.USERNAME.getShort(), Flag.USERNAME.getLong(), true, "DN to bind with, defaults to "+DEFAULT_USERNAME);
|
||||
options.addOption(Flag.PASSWORD.getShort(), Flag.PASSWORD.getLong(), true, "Password to bind with defaults to "+DEFAULT_PASSWORD);
|
||||
options.addOption(Flag.HELP.getShort(), Flag.HELP.getLong(), false, "Print this help message");
|
||||
}
|
||||
|
||||
|
||||
private static void runLdapTestCases(String url, String username, String password, String[] testCases) throws Exception {
|
||||
|
||||
for (String testCase:testCases) {
|
||||
LOG.debug(String.format("Starting ldap test case %1$s", testCase));
|
||||
|
||||
// Set up
|
||||
TestLdap testLdap=new TestLdap();
|
||||
testLdap.setUp(url, username, password);
|
||||
|
||||
// Run the test
|
||||
Method testMethod=testLdap.getClass().getMethod(testCase);
|
||||
testMethod.invoke(testLdap);
|
||||
|
||||
// Tear down
|
||||
testLdap.tearDown();
|
||||
|
||||
LOG.debug(String.format("Test case %1$s completed", testCase));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Run unit tests as an integration test against an external LDAP server.
|
||||
*
|
||||
* Three flags are required:
|
||||
*
|
||||
* -l ldap url of target server
|
||||
* -u dn to bind with
|
||||
* -p password to bind with
|
||||
*
|
||||
* The organisation o=Whoniverse must already exists and the bound user must have
|
||||
* write permission.
|
||||
*
|
||||
*/
|
||||
public static void main(String[] argv) throws Exception {
|
||||
CommandLineParser parser = new PosixParser();
|
||||
CommandLine cmd = null;
|
||||
|
||||
try {
|
||||
cmd = parser.parse(options, argv);
|
||||
} catch (ParseException e) {
|
||||
System.out.println(e.getMessage());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (cmd.hasOption(Flag.HELP.getShort())) {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
|
||||
formatter.printHelp(120, TestLdap.class.getSimpleName(), null, options, null, true);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
String url=cmd.getOptionValue(Flag.URL.getShort(), DEFAULT_LDAP_URL);
|
||||
String username=cmd.getOptionValue(Flag.USERNAME.getShort(), DEFAULT_USERNAME);
|
||||
String password=cmd.getOptionValue(Flag.PASSWORD.getShort(), DEFAULT_PASSWORD);
|
||||
|
||||
// Run all the tests
|
||||
runLdapTestCases(url, username, password, new String[] { "create", "delete", "findAll", "read", "search", "update", "testSecondOc" } );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.springframework.ldap.odm.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.ldap.odm.test.utils.ExecuteRunnable;
|
||||
import org.springframework.ldap.odm.test.utils.RunnableTest;
|
||||
import org.springframework.ldap.odm.typeconversion.ConverterManager;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.Converter;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean;
|
||||
|
||||
public class TestManagerConverterFactory {
|
||||
private static class NullConverter implements Converter {
|
||||
public <T> T convert(Object source, Class<T> toClass) throws Exception {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private static final Converter nullConverter=new NullConverter();
|
||||
|
||||
private static class ConverterConfigTestData {
|
||||
private Class<?>[] fromClasses;
|
||||
private String syntax;
|
||||
private Class<?>[] toClasses;
|
||||
|
||||
private ConverterConfigTestData(Class<?>[] fromClasses, String syntax, Class<?>[] toClasses) {
|
||||
this.fromClasses=fromClasses;
|
||||
this.syntax=syntax;
|
||||
this.toClasses=toClasses;
|
||||
}
|
||||
}
|
||||
|
||||
private static ConverterConfigTestData[] converterConfigTestData=new ConverterConfigTestData[] {
|
||||
new ConverterConfigTestData(new Class<?>[] { String.class }, "", new Class<?>[] { Integer.class }),
|
||||
new ConverterConfigTestData(new Class<?>[] { Byte.class, java.lang.Integer.class }, "", new Class<?>[] { String.class, Long.class }),
|
||||
new ConverterConfigTestData(new Class<?>[] { String.class }, "123", new Class<?>[] { java.net.URI.class }),
|
||||
};
|
||||
|
||||
private static class ConverterTestData {
|
||||
private final Class<?> fromClass;
|
||||
private final String syntax;
|
||||
private final Class<?> toClass;
|
||||
private final boolean canConvert;
|
||||
|
||||
private ConverterTestData(Class<?> fromClass, String syntax, Class<?> toClass, boolean canConvert) {
|
||||
this.fromClass=fromClass;
|
||||
this.syntax=syntax;
|
||||
this.toClass=toClass;
|
||||
this.canConvert=canConvert;
|
||||
}
|
||||
}
|
||||
|
||||
private ConverterTestData[] converterTestData=new ConverterTestData[] {
|
||||
new ConverterTestData(java.lang.String.class, "", java.lang.Integer.class, true),
|
||||
new ConverterTestData(java.lang.Byte.class, "", java.lang.Long.class, true),
|
||||
new ConverterTestData(java.lang.Integer.class, "444", java.lang.String.class, true),
|
||||
new ConverterTestData(java.lang.String.class, "123", java.net.URI.class, true),
|
||||
new ConverterTestData(java.lang.String.class, "123", java.lang.Byte.class, false),
|
||||
new ConverterTestData(java.lang.Byte.class, "", java.lang.Integer.class, false)
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testConverterFactory() throws Exception {
|
||||
ConverterManagerFactoryBean converterManagerFactory=new ConverterManagerFactoryBean();
|
||||
Set<ConverterManagerFactoryBean.ConverterConfig> configList=new HashSet<ConverterManagerFactoryBean.ConverterConfig>();
|
||||
for (ConverterConfigTestData config:converterConfigTestData) {
|
||||
ConverterManagerFactoryBean.ConverterConfig converterConfig=new ConverterManagerFactoryBean.ConverterConfig();
|
||||
converterConfig.setFromClasses(new HashSet<Class<?>>(Arrays.asList(config.fromClasses)));
|
||||
converterConfig.setSyntax(config.syntax);
|
||||
converterConfig.setToClasses(new HashSet<Class<?>>(Arrays.asList(config.toClasses)));
|
||||
converterConfig.setConverter(nullConverter);
|
||||
configList.add(converterConfig);
|
||||
}
|
||||
converterManagerFactory.setConverterConfig(configList);
|
||||
final ConverterManager converterManager=(ConverterManager)converterManagerFactory.getObject();
|
||||
|
||||
new ExecuteRunnable<ConverterTestData>().runTests(new RunnableTest<ConverterTestData>() {
|
||||
public void runTest(ConverterTestData testData) {
|
||||
assertEquals(testData.canConvert,
|
||||
converterManager.canConvert(testData.fromClass, testData.syntax, testData.toClass));
|
||||
}
|
||||
}, converterTestData);
|
||||
}
|
||||
}
|
||||
204
odm/src/test/java/org/springframework/ldap/odm/test/TestSchemaToJava.java
Executable file
204
odm/src/test/java/org/springframework/ldap/odm/test/TestSchemaToJava.java
Executable file
@@ -0,0 +1,204 @@
|
||||
package org.springframework.ldap.odm.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Iterator;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.ldap.odm.core.impl.OdmManagerImpl;
|
||||
import org.springframework.ldap.odm.test.utils.CompilerInterface;
|
||||
import org.springframework.ldap.odm.test.utils.GetFreePort;
|
||||
import org.springframework.ldap.odm.tools.SchemaToJava;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.Converter;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.ConverterManagerImpl;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.converters.FromStringConverter;
|
||||
import org.springframework.ldap.odm.typeconversion.impl.converters.ToStringConverter;
|
||||
import org.springframework.ldap.test.LdapTestUtils;
|
||||
|
||||
// Tests the generation of entry Java classes from LDAP schema
|
||||
public final class TestSchemaToJava {
|
||||
private static final Log LOG = LogFactory.getLog(TestLdap.class);
|
||||
|
||||
private static final DistinguishedName baseName = new DistinguishedName("o=Whoniverse");
|
||||
|
||||
private static final String tempDir=System.getProperty("java.io.tmpdir");
|
||||
|
||||
// These unit tests require this port to free on localhost
|
||||
private static int port;
|
||||
|
||||
private ConverterManagerImpl converterManager;
|
||||
|
||||
private LdapContextSource contextSource;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
// Added because the close down of Apache DS on Linux does
|
||||
// not seem to free up its port.
|
||||
port=GetFreePort.getFreePort();
|
||||
|
||||
// Start an in process LDAP server
|
||||
LdapTestUtils.startApacheDirectoryServer(port, baseName.toString(), "odm-test", "", "", null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
// Stop the in process LDAP server
|
||||
LdapTestUtils.destroyApacheDirectoryServer("", "");
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// Create some basic converters and a converter manager
|
||||
converterManager = new ConverterManagerImpl();
|
||||
|
||||
Converter ptc = new FromStringConverter();
|
||||
converterManager.addConverter(String.class, "", Byte.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Short.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Integer.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Long.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Double.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Float.class, ptc);
|
||||
converterManager.addConverter(String.class, "", Boolean.class, ptc);
|
||||
|
||||
Converter tsc = new ToStringConverter();
|
||||
converterManager.addConverter(Byte.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Short.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Integer.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Long.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Double.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Float.class, "", String.class, tsc);
|
||||
converterManager.addConverter(Boolean.class, "", String.class, tsc);
|
||||
|
||||
// Bind to the directory
|
||||
contextSource = new LdapContextSource();
|
||||
contextSource.setUrl("ldap://127.0.0.1:" + port);
|
||||
contextSource.setUserDn("");
|
||||
contextSource.setPassword("");
|
||||
contextSource.setPooled(false);
|
||||
contextSource.afterPropertiesSet();
|
||||
|
||||
// Clear out any old data - and load the test data
|
||||
LdapTestUtils.cleanAndSetup(contextSource, baseName, new ClassPathResource("testdata.ldif"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
LdapTestUtils.destroyApacheDirectoryServer("", "");
|
||||
|
||||
contextSource=null;
|
||||
converterManager=null;
|
||||
}
|
||||
|
||||
// Figure out the path of the created Java file
|
||||
private static String calculateOutputDirectory(String outputDir, String packageName) {
|
||||
// Convert the package name to a path
|
||||
Pattern pattern=Pattern.compile("\\.");
|
||||
Matcher matcher=pattern.matcher(packageName);
|
||||
String sepToUse=File.separator;
|
||||
if (sepToUse.equals("\\")) {
|
||||
sepToUse="\\\\";
|
||||
}
|
||||
|
||||
return outputDir+File.separator+matcher.replaceAll(sepToUse);
|
||||
}
|
||||
|
||||
// Due of the nature of the code under test this unit test is a little unusual:
|
||||
//
|
||||
// 1) Generate an entry class corresponding to objects classes
|
||||
// "inetorgperson, organizationalperson, person, top"
|
||||
// using the SchemaToJavaTool
|
||||
// 2) Compile the generated code
|
||||
// 3) Create an OdmManager to managing the newly created
|
||||
// entry class.
|
||||
// 4) Use this OdmManager to read an entry from LDAP and check the results.
|
||||
//
|
||||
@Test
|
||||
@Ignore
|
||||
public void generate() throws Exception {
|
||||
final String className="Person";
|
||||
final String packageName="org.springframework.ldap.odm.testclasses";
|
||||
|
||||
// Add classes dir to class path - needed for compilation
|
||||
System.setProperty("java.class.path",
|
||||
System.getProperty("java.class.path")+File.pathSeparator+"target/classes");
|
||||
|
||||
String[] flags=new String[] {
|
||||
"--url", "ldap://127.0.0.1:"+port,
|
||||
"--objectclasses", "inetorgperson",
|
||||
"--syntaxmap", "target/test-classes/syntax-to-class-map.txt",
|
||||
"--class", className,
|
||||
"--package", packageName,
|
||||
"--outputdir", tempDir };
|
||||
|
||||
// Generate the code using SchemaToJava
|
||||
SchemaToJava.main(flags);
|
||||
|
||||
// Java 5 - we'll use the Java 6 Compiler API once we can drop support for Java 5.
|
||||
String javaDir = calculateOutputDirectory(tempDir, packageName);
|
||||
|
||||
CompilerInterface.compile(javaDir, className+".java");
|
||||
// Java 5
|
||||
|
||||
// OK it compiles so lets load our new class
|
||||
URL[] urls = new URL[] { new File(tempDir).toURI().toURL() };
|
||||
URLClassLoader ucl = new URLClassLoader(urls, getClass().getClassLoader());
|
||||
Class<?> clazz = ucl.loadClass(packageName+"."+className);
|
||||
|
||||
// Create our OdmManager using our new class
|
||||
OdmManagerImpl odmManager = new OdmManagerImpl(converterManager, contextSource);
|
||||
odmManager.addManagedClass(clazz);
|
||||
|
||||
// And try reading from the directory using it
|
||||
DistinguishedName testDn=new DistinguishedName(baseName);
|
||||
testDn.addAll(new DistinguishedName("cn=William Hartnell,ou=Doctors"));
|
||||
Object fromDirectory=odmManager.read(clazz, testDn);
|
||||
|
||||
LOG.debug(String.format("Read - %1$s", fromDirectory));
|
||||
|
||||
// Check some returned values
|
||||
Method getDnMethod=clazz.getMethod("getDn");
|
||||
Object dn=getDnMethod.invoke(fromDirectory);
|
||||
assertEquals(testDn, dn);
|
||||
|
||||
Method getCnIteratorMethod=clazz.getMethod("getCnIterator");
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<String> cnIterator=(Iterator<String>)getCnIteratorMethod.invoke(fromDirectory);
|
||||
int cnCount=0;
|
||||
while (cnIterator.hasNext()) {
|
||||
cnCount++;
|
||||
assertEquals("William Hartnell", cnIterator.next());
|
||||
}
|
||||
assertEquals(1, cnCount);
|
||||
|
||||
Method telephoneNumberIteratorMethod=clazz.getMethod("getTelephoneNumberIterator");
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<Integer> telephoneNumberIterator=(Iterator<Integer>)telephoneNumberIteratorMethod.invoke(fromDirectory);
|
||||
int telephoneNumberCount=0;
|
||||
while (telephoneNumberIterator.hasNext()) {
|
||||
telephoneNumberCount++;
|
||||
assertEquals(Integer.valueOf(1), telephoneNumberIterator.next());
|
||||
}
|
||||
assertEquals(1, telephoneNumberCount);
|
||||
|
||||
// Reread and check whether equals and hashCode are at least sane
|
||||
Object fromDirectory2=odmManager.read(clazz, testDn);
|
||||
assertEquals(fromDirectory, fromDirectory2);
|
||||
assertEquals(fromDirectory.hashCode(), fromDirectory2.hashCode());
|
||||
}
|
||||
}
|
||||
125
odm/src/test/java/org/springframework/ldap/odm/test/TestSchemaViewer.java
Executable file
125
odm/src/test/java/org/springframework/ldap/odm/test/TestSchemaViewer.java
Executable file
@@ -0,0 +1,125 @@
|
||||
package org.springframework.ldap.odm.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.odm.test.utils.ExecuteRunnable;
|
||||
import org.springframework.ldap.odm.test.utils.GetFreePort;
|
||||
import org.springframework.ldap.odm.test.utils.RunnableTest;
|
||||
import org.springframework.ldap.odm.tools.SchemaViewer;
|
||||
import org.springframework.ldap.test.LdapTestUtils;
|
||||
|
||||
public final class TestSchemaViewer {
|
||||
// Base DN for test data
|
||||
private static final DistinguishedName baseName = new DistinguishedName("o=Whoniverse");
|
||||
|
||||
private static final String PRINCIPAL="";
|
||||
private static final String CREDENTIALS="";
|
||||
private static final String lineSeparator = System.getProperty ("line.separator");
|
||||
|
||||
private static int port;
|
||||
|
||||
private static String[] commonFlags;
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
// Added because the close down of Apache DS on Linux does
|
||||
// not seem to free up its port.
|
||||
port=GetFreePort.getFreePort();
|
||||
|
||||
commonFlags=new String[] {
|
||||
"--url", "ldap://127.0.0.1:"+port,
|
||||
"--username", "",
|
||||
"--password", "",
|
||||
"--error"};
|
||||
|
||||
// Start an in process LDAP server
|
||||
LdapTestUtils.startApacheDirectoryServer(port, baseName.toString(), "odm-test", "", "", null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
LdapTestUtils.destroyApacheDirectoryServer(PRINCIPAL, CREDENTIALS);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
private static String runSchemaViewer(String... flags) {
|
||||
String result=null;
|
||||
PrintStream originalOut=System.out;
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
try {
|
||||
System.setErr(new PrintStream(output));
|
||||
|
||||
List<String> commandFlags=
|
||||
new ArrayList<String>(Arrays.asList(commonFlags));
|
||||
commandFlags.addAll(Arrays.asList(flags));
|
||||
|
||||
SchemaViewer.main(commandFlags.toArray(new String[0]));
|
||||
|
||||
// Turn end of lines into | for portability
|
||||
result=output.toString().trim().replace(lineSeparator, "|");
|
||||
|
||||
} finally {
|
||||
System.setErr(originalOut);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class TestData {
|
||||
private final String flag;
|
||||
private final String value;
|
||||
private final String result;
|
||||
|
||||
public TestData(String flag, String value, String result) {
|
||||
this.flag=flag;
|
||||
this.value=value;
|
||||
this.result=result;
|
||||
}
|
||||
}
|
||||
|
||||
// This makes the test dependent on the order in which the data is returned - it is invalid to assume that this will not change
|
||||
private static TestData[] viewerTestData=new TestData[] {
|
||||
new TestData("-o", "top",
|
||||
"NAME:top|MUST:objectClass |NAME:top |NUMERICOID:2.5.6.0 |DESC:top of the superclass chain |ABSTRACT:true"),
|
||||
new TestData("-o", "country",
|
||||
"NAME:country|MUST:c |SUP:top |NAME:country |STRUCTURAL:true |NUMERICOID:2.5.6.2 |DESC:RFC2256: a country |MAY:searchGuide description"),
|
||||
new TestData("-a", "sn",
|
||||
"NAME:sn|SUP:name |SYNTAX:1.3.6.1.4.1.1466.115.121.1.15 |NAME:sn surname |EQUALITY:caseIgnoreMatch |SUBSTR:caseIgnoreSubstringsMatch |USAGE:userApplications |NUMERICOID:2.5.4.4 |DESC:RFC2256: last (family) name(s) for which the entity is known by"),
|
||||
new TestData("-a", "jpegPhoto",
|
||||
"NAME:jpegPhoto|SYNTAX:1.3.6.1.4.1.1466.115.121.1.28 |NAME:jpegPhoto |USAGE:userApplications |NUMERICOID:0.9.2342.19200300.100.1.60 |DESC:RFC2798: a JPEG image"),
|
||||
new TestData("-s", "jpeg",
|
||||
"NAME:jpeg|NAME:JPEG |NUMERICOID:1.3.6.1.4.1.1466.115.121.1.28"),
|
||||
new TestData("-s", "OID",
|
||||
"NAME:OID|NAME:OID |NUMERICOID:1.3.6.1.4.1.1466.115.121.1.38"),
|
||||
};
|
||||
|
||||
// Very simple test - mainly just to exercise the code and to
|
||||
// ensure we get representative test coverage
|
||||
@Test
|
||||
public void testSchemaViewer() throws Exception {
|
||||
new ExecuteRunnable<TestData>().runTests(new RunnableTest<TestData>() {
|
||||
public void runTest(TestData testData) {
|
||||
String result=runSchemaViewer(testData.flag, testData.value);
|
||||
assertEquals(testData.result, result);
|
||||
}
|
||||
}, viewerTestData);
|
||||
}
|
||||
}
|
||||
27
odm/src/test/java/org/springframework/ldap/odm/test/TestsWithJdepend.java
Executable file
27
odm/src/test/java/org/springframework/ldap/odm/test/TestsWithJdepend.java
Executable file
@@ -0,0 +1,27 @@
|
||||
package org.springframework.ldap.odm.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jdepend.framework.JDepend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestsWithJdepend {
|
||||
private JDepend jdepend;
|
||||
|
||||
@Before
|
||||
public void setUp() throws IOException {
|
||||
jdepend = new JDepend();
|
||||
jdepend.addDirectory("target/classes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllPackages() {
|
||||
jdepend.analyze();
|
||||
assertEquals(false, jdepend.containsCycles());
|
||||
}
|
||||
|
||||
}
|
||||
29
odm/src/test/java/org/springframework/ldap/odm/test/UriConverter.java
Executable file
29
odm/src/test/java/org/springframework/ldap/odm/test/UriConverter.java
Executable file
@@ -0,0 +1,29 @@
|
||||
package org.springframework.ldap.odm.test;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import org.springframework.ldap.odm.typeconversion.impl.Converter;
|
||||
|
||||
/**
|
||||
* A bi-directional converter between {@link java.net.URI} and {@link java.lang.String}.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
public class UriConverter implements Converter {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.ldap.odm.typeconversion.impl.Converter#convert(java.lang.Object, java.lang.Class)
|
||||
*/
|
||||
public <T> T convert(Object source, Class<T> toClass) throws Exception {
|
||||
T result = null;
|
||||
if (String.class.isAssignableFrom(source.getClass()) && toClass == URI.class) {
|
||||
result = toClass.cast(new URI((String)source));
|
||||
} else {
|
||||
if (URI.class.isAssignableFrom(source.getClass()) && toClass == String.class) {
|
||||
result = toClass.cast(source.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.springframework.ldap.odm.test.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class CompilerInterface {
|
||||
// Compile the given file - when we can drop Java 5 we'll use the Java 6 compiler API
|
||||
public static void compile(String directory, String file) throws Exception {
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
new String[] { "javac",
|
||||
"-cp", "."+File.pathSeparatorChar+"target"+File.separatorChar+"classes"+
|
||||
File.pathSeparatorChar+System.getProperty("java.class.path"),
|
||||
directory+File.separatorChar+file });
|
||||
|
||||
pb.redirectErrorStream(true);
|
||||
Process proc = pb.start();
|
||||
InputStream is = proc.getInputStream();
|
||||
InputStreamReader isr = new InputStreamReader(is);
|
||||
|
||||
char[] buf = new char[1024];
|
||||
int count;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while ((count = isr.read(buf)) > 0) {
|
||||
builder.append(buf, 0, count);
|
||||
}
|
||||
|
||||
boolean ok = proc.waitFor() == 0;
|
||||
|
||||
if (!ok) {
|
||||
throw new RuntimeException(builder.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.springframework.ldap.odm.test.utils;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
// Simple utility class to run a given test over a set of test data
|
||||
public final class ExecuteRunnable<U> {
|
||||
|
||||
public void runTests(RunnableTest<U> runnableTest, U[] testData) throws Exception {
|
||||
StackTraceElement ste = Thread.currentThread().getStackTrace()[2];
|
||||
Log LOG = LogFactory.getLog(ste.getClassName());
|
||||
for (U testDatum : testData) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Running test with data %1$s", testDatum));
|
||||
}
|
||||
runnableTest.runTest(testDatum);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
odm/src/test/java/org/springframework/ldap/odm/test/utils/GetFreePort.java
Executable file
24
odm/src/test/java/org/springframework/ldap/odm/test/utils/GetFreePort.java
Executable file
@@ -0,0 +1,24 @@
|
||||
package org.springframework.ldap.odm.test.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
// Added because the close down of the embedded Apache DS used
|
||||
// for unit testing does not seem to free up its port.
|
||||
public class GetFreePort {
|
||||
private static Log LOG=LogFactory.getLog(GetFreePort.class);
|
||||
|
||||
public static int getFreePort()
|
||||
throws IOException {
|
||||
ServerSocket server = new ServerSocket(0);
|
||||
int port = server.getLocalPort();
|
||||
server.close();
|
||||
|
||||
LOG.debug(String.format("Port number: %1$s", port));
|
||||
|
||||
return port;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package org.springframework.ldap.odm.test.utils;
|
||||
|
||||
// Interface to implement for tests to be run by ExecuteRunnable
|
||||
public interface RunnableTest<T> {
|
||||
void runTest(T testData) throws Exception;
|
||||
}
|
||||
7
odm/src/test/resources/syntax-to-class-map.txt
Executable file
7
odm/src/test/resources/syntax-to-class-map.txt
Executable file
@@ -0,0 +1,7 @@
|
||||
# List of attribute syntax to java class mappings
|
||||
|
||||
# Syntax Java class
|
||||
# ------ ----------
|
||||
|
||||
1.3.6.1.4.1.1466.115.121.1.50, java.lang.Integer
|
||||
1.3.6.1.4.1.1466.115.121.1.40, [B
|
||||
142
odm/src/test/resources/testdata.ldif
Executable file
142
odm/src/test/resources/testdata.ldif
Executable file
@@ -0,0 +1,142 @@
|
||||
dn: ou=Enemies,o=Whoniverse
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
ou: Enemies
|
||||
street: Acacia Avenue
|
||||
description: The bad guys
|
||||
|
||||
dn: ou=Assistants,o=Whoniverse
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
ou: Assistants
|
||||
street: Somewhere in space
|
||||
description: The plucky helpers
|
||||
|
||||
dn: ou=Doctors,o=Whoniverse
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
ou: Doctors
|
||||
street: Somewhere in time
|
||||
description: Our hero
|
||||
|
||||
dn: cn=William Hartnell,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: William Hartnell
|
||||
description: First Doctor
|
||||
description: Grumpy
|
||||
sn: Hartnell
|
||||
telephonenumber: 1
|
||||
|
||||
dn: cn=Patrick Troughton,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Patrick Troughton
|
||||
description: Second Doctor
|
||||
description:Clown
|
||||
sn: Troughton
|
||||
telephonenumber: 2
|
||||
|
||||
dn: cn=Jon Pertwee,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Jon Pertwee
|
||||
description: Third Doctor
|
||||
description: Dandy
|
||||
sn: Pertwee
|
||||
telephonenumber: 3
|
||||
|
||||
dn: cn=Tom Baker,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Tom Baker
|
||||
description: Fourth Doctor
|
||||
description: The one and only!
|
||||
sn: Baker
|
||||
telephonenumber: 4
|
||||
|
||||
dn: cn=Peter Davison,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Peter Davison
|
||||
description: Fifth Doctor
|
||||
sn: Davison
|
||||
telephonenumber: 5
|
||||
|
||||
dn: cn=Paul Harvey,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
objectClass: userSecurityInformation
|
||||
cn: Paul Harvey
|
||||
description: Not a Doctor
|
||||
sn: Harvey
|
||||
telephonenumber: 11
|
||||
|
||||
dn: cn=Bramble Harvey,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
cn: Bramble
|
||||
description: Really not a Doctor
|
||||
sn: Harvey
|
||||
telephonenumber: 22
|
||||
|
||||
dn: cn=Davros,ou=Enemies,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Davros
|
||||
description: Creator of the Daleks
|
||||
description: Kaled head scientist
|
||||
sn: Unknown
|
||||
|
||||
dn: cn=Daleks,ou=Enemies,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Daleks
|
||||
description: The Doctor's greatest foe
|
||||
sn: NA
|
||||
|
||||
dn: cn=Master,ou=Enemies,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Master
|
||||
description: An evil Time Lord
|
||||
sn: Unknown
|
||||
jpegphoto:: /9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkS
|
||||
Ew8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRg
|
||||
yIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wA
|
||||
ARCAAnABoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAA
|
||||
gEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcY
|
||||
GRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipK
|
||||
TlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8v
|
||||
P09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFB
|
||||
AQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygp
|
||||
KjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJm
|
||||
aoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9
|
||||
oADAMBAAIRAxEAPwDx2z0mK6gV/tSo2SCpUHGD9Qa2vDvgGfX7+eI3ot4EOEmEefMOOcZPQdOvW
|
||||
s3VLGzsWiihUvM0ayONxATPIHXk4wfxFdd4L1aw0rw3eS3V3GHWRswkgugwCpVSckEk9OhB9azu
|
||||
2ro6lCKnyyZwmvaHcaBrV1plxKkkluw+dRkMCAQfbgjiqi27sob1GfuVc1q9n1XVL3UdqxJI/wD
|
||||
q/MXIXGAMd+B2HWoUZ1jUbZeABwKqzMJWT0Ld3cfaJ5ZFYtnCg+oUBf6V2XhvwVpcvhxfFXibVF
|
||||
tNK3lEgjJ3yMGK7SRzkkHhRnHORXJQ6exVXB2r1K9Cfx7V0UWpeD7S3WJ9N1TUPLYssV7cBYkZg
|
||||
AxAU4zwOcZ4HoKtppWQ4tSk5SNPx14o8M3/AITXT/D2kRRxB1jE4RIyuOc7ME84PzcHr2PPmX2y
|
||||
YcZX8q37fQxPpqzsH8xvuqAeeuO/0H3e9Zn2dxwbK5JHXCH/AOJqNOhdSM1ZzVjRNwRbjGRxxzn
|
||||
FdRouijV9Pi1C6toEMqn7MsK7SqBiCxOSSSVI5J4x6miiliW1FMzpbszreUw3H2VcuscpGT3UE9
|
||||
Rn0HStE+IrZSQbEuRxvU4De4yc0UVFKCnuerms2vZryP/Z
|
||||
|
||||
@@ -395,13 +395,28 @@
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<version>2.1</version>
|
||||
<version>2.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-pool</groupId>
|
||||
<artifactId>commons-pool</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-cli</groupId>
|
||||
<artifactId>commons-cli</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.freemarker</groupId>
|
||||
<artifactId>freemarker</artifactId>
|
||||
<version>2.3.9</version>
|
||||
</dependency>
|
||||
<!-- Logging dependencies -->
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
@@ -433,6 +448,13 @@
|
||||
<version>2.0.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jdepend</groupId>
|
||||
<artifactId>jdepend</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<type>jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
7
pom.xml
7
pom.xml
@@ -64,6 +64,12 @@
|
||||
<contributor>
|
||||
<name>Eric Dalquist</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Keith Barlow</name>
|
||||
</contributor>
|
||||
<contributor>
|
||||
<name>Paul Harvey</name>
|
||||
</contributor>
|
||||
</contributors>
|
||||
<organization>
|
||||
<name>The Spring LDAP Framework</name>
|
||||
@@ -109,6 +115,7 @@
|
||||
<module>parent</module>
|
||||
<module>core</module>
|
||||
<module>core-tiger</module>
|
||||
<module>odm</module>
|
||||
<module>test-support</module>
|
||||
<module>test</module>
|
||||
</modules>
|
||||
|
||||
104
samples/simple-odm/pom.xml
Normal file
104
samples/simple-odm/pom.xml
Normal file
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-parent</artifactId>
|
||||
<version>1.3.1.CI-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-odm-sample</artifactId>
|
||||
<name>Simple Spring LDAP ODM Sample</name>
|
||||
<description>A very simple example of using the the Spring LDAP ODM</description>
|
||||
<developers>
|
||||
<developer>
|
||||
<name>Paul Harvey</name>
|
||||
<email>paul@pauls-place.me.uk</email>
|
||||
<roles>
|
||||
<role>Developer</role>
|
||||
</roles>
|
||||
<timezone>0</timezone>
|
||||
</developer>
|
||||
</developers>
|
||||
<repositories>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.springframework.ldap.odm.sample.SearchForPeople</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
<configuration>
|
||||
<forkMode>always</forkMode>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-odm</artifactId>
|
||||
<version>${version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<type>jar</type>
|
||||
<version>4.4</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-test</artifactId>
|
||||
<version>${version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ldap</groupId>
|
||||
<artifactId>spring-ldap-core</artifactId>
|
||||
<version>${version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-lang</groupId>
|
||||
<artifactId>commons-lang</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.springframework.ldap.odm.sample;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.naming.directory.SearchControls;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.odm.core.OdmManager;
|
||||
|
||||
// A very simple example - just showing how little code you actually need to write
|
||||
// when using Spring LDAP ODM
|
||||
public class SearchForPeople {
|
||||
private static final SearchControls searchControls =
|
||||
new SearchControls(SearchControls.SUBTREE_SCOPE, 100, 10000,
|
||||
null, true, false);
|
||||
|
||||
private static final DistinguishedName baseDn = new DistinguishedName("o=Whoniverse");
|
||||
|
||||
private static void print(List<SimplePerson> personList) {
|
||||
for (SimplePerson person : personList) {
|
||||
System.out.println(person);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] argv) {
|
||||
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "spring.xml" });
|
||||
|
||||
// Grab the OdmManager wired by Spring
|
||||
OdmManager odmManager = (OdmManager)context.getBean("odmManager");
|
||||
|
||||
// Find people with a surname of Harvey
|
||||
List<SimplePerson> searchResults = odmManager.search(SimplePerson.class, baseDn, "sn=Harvey", searchControls);
|
||||
|
||||
// Print the results
|
||||
print(searchResults);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package org.springframework.ldap.odm.sample;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.naming.Name;
|
||||
|
||||
import static org.springframework.ldap.odm.annotations.Attribute.*;
|
||||
|
||||
import org.springframework.ldap.odm.annotations.Attribute;
|
||||
import org.springframework.ldap.odm.annotations.Entry;
|
||||
import org.springframework.ldap.odm.annotations.Id;
|
||||
|
||||
/**
|
||||
* Automatically generated to represent the LDAP object classes
|
||||
* "person", "top".
|
||||
*/
|
||||
@Entry(objectClasses={"person", "top"})
|
||||
public final class SimplePerson {
|
||||
|
||||
@Id
|
||||
private Name dn;
|
||||
|
||||
@Attribute(name="objectClass", syntax="1.3.6.1.4.1.1466.115.121.1.38")
|
||||
private List<String> objectClass=new ArrayList<String>();
|
||||
|
||||
|
||||
@Attribute(name="cn", syntax="1.3.6.1.4.1.1466.115.121.1.15")
|
||||
private List<String> cn=new ArrayList<String>();
|
||||
|
||||
|
||||
@Attribute(name="sn", syntax="1.3.6.1.4.1.1466.115.121.1.15")
|
||||
private List<String> sn=new ArrayList<String>();
|
||||
|
||||
|
||||
@Attribute(name="description", syntax="1.3.6.1.4.1.1466.115.121.1.15")
|
||||
private List<String> description=new ArrayList<String>();
|
||||
|
||||
|
||||
@Attribute(name="userPassword", syntax="1.3.6.1.4.1.1466.115.121.1.40", type=Type.BINARY)
|
||||
private List<byte[]> userPassword=new ArrayList<byte[]>();
|
||||
|
||||
|
||||
@Attribute(name="telephoneNumber", syntax="1.3.6.1.4.1.1466.115.121.1.50")
|
||||
private List<String> telephoneNumber=new ArrayList<String>();
|
||||
|
||||
|
||||
@Attribute(name="seeAlso", syntax="1.3.6.1.4.1.1466.115.121.1.12")
|
||||
private List<String> seeAlso=new ArrayList<String>();
|
||||
|
||||
public Name getDn() {
|
||||
return dn;
|
||||
}
|
||||
|
||||
public void setDn(Name dn) {
|
||||
this.dn=dn;
|
||||
}
|
||||
|
||||
public Iterator<String> getObjectClassIterator() {
|
||||
return Collections.unmodifiableList(objectClass).iterator();
|
||||
}
|
||||
|
||||
public void addCn(String cn) {
|
||||
this.cn.add(cn);
|
||||
}
|
||||
|
||||
public void removeCn(String cn) {
|
||||
this.cn.remove(cn);
|
||||
}
|
||||
|
||||
public Iterator<String> getCnIterator() {
|
||||
return cn.iterator();
|
||||
}
|
||||
|
||||
public void addSn(String sn) {
|
||||
this.sn.add(sn);
|
||||
}
|
||||
|
||||
public void removeSn(String sn) {
|
||||
this.sn.remove(sn);
|
||||
}
|
||||
|
||||
public Iterator<String> getSnIterator() {
|
||||
return sn.iterator();
|
||||
}
|
||||
|
||||
public void addDescription(String description) {
|
||||
this.description.add(description);
|
||||
}
|
||||
|
||||
public void removeDescription(String description) {
|
||||
this.description.remove(description);
|
||||
}
|
||||
|
||||
public Iterator<String> getDescriptionIterator() {
|
||||
return description.iterator();
|
||||
}
|
||||
|
||||
public void addUserPassword(byte[] userPassword) {
|
||||
this.userPassword.add(userPassword);
|
||||
}
|
||||
|
||||
public void removeUserPassword(byte[] userPassword) {
|
||||
this.userPassword.remove(userPassword);
|
||||
}
|
||||
|
||||
public Iterator<byte[]> getUserPasswordIterator() {
|
||||
return userPassword.iterator();
|
||||
}
|
||||
|
||||
public void addTelephoneNumber(String telephoneNumber) {
|
||||
this.telephoneNumber.add(telephoneNumber);
|
||||
}
|
||||
|
||||
public void removeTelephoneNumber(String telephoneNumber) {
|
||||
this.telephoneNumber.remove(telephoneNumber);
|
||||
}
|
||||
|
||||
public Iterator<String> getTelephoneNumberIterator() {
|
||||
return telephoneNumber.iterator();
|
||||
}
|
||||
|
||||
public void addSeeAlso(String seeAlso) {
|
||||
this.seeAlso.add(seeAlso);
|
||||
}
|
||||
|
||||
public void removeSeeAlso(String seeAlso) {
|
||||
this.seeAlso.remove(seeAlso);
|
||||
}
|
||||
|
||||
public Iterator<String> getSeeAlsoIterator() {
|
||||
return seeAlso.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result=new StringBuilder();
|
||||
result.append(String.format("dn=%1$s", dn));
|
||||
result.append(String.format(" | objectClass=%1$s", objectClass));
|
||||
result.append(String.format(" | cn=%1$s", cn));
|
||||
result.append(String.format(" | sn=%1$s", sn));
|
||||
result.append(String.format(" | description=%1$s", description));
|
||||
result.append(String.format(" | userPassword=%1$s", userPassword));
|
||||
result.append(String.format(" | telephoneNumber=%1$s", telephoneNumber));
|
||||
result.append(String.format(" | seeAlso=%1$s", seeAlso));
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((dn == null) ? 0 : dn.hashCode());
|
||||
result = prime * result + ((objectClass == null) ? 0 : (new HashSet<String>(objectClass)).hashCode());
|
||||
result = prime * result + ((cn == null) ? 0 : (new HashSet<String>(cn)).hashCode());
|
||||
result = prime * result + ((sn == null) ? 0 : (new HashSet<String>(sn)).hashCode());
|
||||
result = prime * result + ((description == null) ? 0 : (new HashSet<String>(description)).hashCode());
|
||||
result = prime * result + ((userPassword == null) ? 0 : (new HashSet<byte[]>(userPassword)).hashCode());
|
||||
result = prime * result + ((telephoneNumber == null) ? 0 : (new HashSet<String>(telephoneNumber)).hashCode());
|
||||
result = prime * result + ((seeAlso == null) ? 0 : (new HashSet<String>(seeAlso)).hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
SimplePerson other = (SimplePerson) obj;
|
||||
if (dn == null) {
|
||||
if (other.dn != null)
|
||||
return false;
|
||||
} else if (!dn.equals(other.dn))
|
||||
return false;
|
||||
if (objectClass == null) {
|
||||
if (other.objectClass != null)
|
||||
return false;
|
||||
} else if (!(new HashSet<String>(objectClass)).equals(new HashSet<String>(other.objectClass)))
|
||||
return false;
|
||||
if (cn == null) {
|
||||
if (other.cn != null)
|
||||
return false;
|
||||
} else if (!(new HashSet<String>(cn)).equals(new HashSet<String>(other.cn)))
|
||||
return false;
|
||||
if (sn == null) {
|
||||
if (other.sn != null)
|
||||
return false;
|
||||
} else if (!(new HashSet<String>(sn)).equals(new HashSet<String>(other.sn)))
|
||||
return false;
|
||||
if (description == null) {
|
||||
if (other.description != null)
|
||||
return false;
|
||||
} else if (!(new HashSet<String>(description)).equals(new HashSet<String>(other.description)))
|
||||
return false;
|
||||
if (userPassword == null) {
|
||||
if (other.userPassword != null)
|
||||
return false;
|
||||
} else if (!(new HashSet<byte[]>(userPassword)).equals(new HashSet<byte[]>(other.userPassword)))
|
||||
return false;
|
||||
if (telephoneNumber == null) {
|
||||
if (other.telephoneNumber != null)
|
||||
return false;
|
||||
} else if (!(new HashSet<String>(telephoneNumber)).equals(new HashSet<String>(other.telephoneNumber)))
|
||||
return false;
|
||||
if (seeAlso == null) {
|
||||
if (other.seeAlso != null)
|
||||
return false;
|
||||
} else if (!(new HashSet<String>(seeAlso)).equals(new HashSet<String>(other.seeAlso)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* A very simple example of the use of Spring ODM.
|
||||
*
|
||||
* @author Paul Harvey <paul.at.pauls-place.me.uk>
|
||||
*/
|
||||
|
||||
package org.springframework.ldap.odm.sample;
|
||||
19
samples/simple-odm/src/main/resources/log4j.properties
Executable file
19
samples/simple-odm/src/main/resources/log4j.properties
Executable file
@@ -0,0 +1,19 @@
|
||||
log4j.rootLogger=error, stdout
|
||||
log4j.logger.org.springframework.ldap.odm=error
|
||||
log4j.logger.org.springframework=error
|
||||
|
||||
# Messages to the console
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
|
||||
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
|
||||
|
||||
# Message to a log file
|
||||
log4j.appender.log=org.apache.log4j.RollingFileAppender
|
||||
log4j.appender.log.File=jndi.log
|
||||
|
||||
log4j.appender.log.MaxFileSize=100KB
|
||||
log4j.appender.log.MaxBackupIndex=1
|
||||
|
||||
log4j.appender.log.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.log.layout.ConversionPattern=%p %t %c - %m%n
|
||||
96
samples/simple-odm/src/main/resources/spring.xml
Executable file
96
samples/simple-odm/src/main/resources/spring.xml
Executable file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- An example Spring configuration for Spring LDAP ODM -->
|
||||
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
|
||||
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
|
||||
|
||||
<bean id="fromStringConverter" class="org.springframework.ldap.odm.typeconversion.impl.converters.FromStringConverter" />
|
||||
<bean id="toStringConverter" class="org.springframework.ldap.odm.typeconversion.impl.converters.ToStringConverter" />
|
||||
|
||||
<bean id="converterManager" class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean">
|
||||
<property name="converterConfig">
|
||||
<set>
|
||||
<bean class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean$ConverterConfig">
|
||||
<property name="fromClasses">
|
||||
<set>
|
||||
<value>java.lang.String</value>
|
||||
</set>
|
||||
</property>
|
||||
<property name="toClasses">
|
||||
<set>
|
||||
<value>java.lang.Byte</value>
|
||||
<value>java.lang.Short</value>
|
||||
<value>java.lang.Integer</value>
|
||||
<value>java.lang.Long</value>
|
||||
<value>java.lang.Float</value>
|
||||
<value>java.lang.Double</value>
|
||||
<value>java.lang.Boolean</value>
|
||||
</set>
|
||||
</property>
|
||||
<property name="converter" ref="fromStringConverter" />
|
||||
</bean>
|
||||
<bean class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean$ConverterConfig">
|
||||
<property name="fromClasses">
|
||||
<set>
|
||||
<value>java.lang.Byte</value>
|
||||
<value>java.lang.Short</value>
|
||||
<value>java.lang.Integer</value>
|
||||
<value>java.lang.Long</value>
|
||||
<value>java.lang.Float</value>
|
||||
<value>java.lang.Double</value>
|
||||
<value>java.lang.Boolean</value>
|
||||
</set>
|
||||
</property>
|
||||
<property name="toClasses">
|
||||
<set>
|
||||
<value>java.lang.String</value>
|
||||
</set>
|
||||
</property>
|
||||
<property name="converter" ref="toStringConverter" />
|
||||
</bean>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://127.0.0.1:10389" />
|
||||
<property name="userDn" value="uid=admin,ou=system" />
|
||||
<property name="password" value="secret" />
|
||||
<property name="pooled" value="false" />
|
||||
</bean>
|
||||
|
||||
<bean id="dirContextValidator" class="org.springframework.ldap.pool.validation.DefaultDirContextValidator">
|
||||
<property name="base" value="" />
|
||||
<property name="filter" value="objectclass=*" />
|
||||
<property name="searchControls.searchScope">
|
||||
<util:constant static-field="javax.naming.directory.SearchControls.OBJECT_SCOPE" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="contextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
|
||||
<property name="contextSource" ref="contextSourceTarget" />
|
||||
<property name="dirContextValidator" ref="dirContextValidator" />
|
||||
<property name="maxActive" value="10" />
|
||||
<property name="maxTotal" value="10" />
|
||||
<property name="maxIdle" value="5" />
|
||||
<property name="minIdle" value="1" />
|
||||
<property name="timeBetweenEvictionRunsMillis" value="60000" />
|
||||
<property name="testOnBorrow" value="true" />
|
||||
<property name="testWhileIdle" value="true" />
|
||||
</bean>
|
||||
|
||||
<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>org.springframework.ldap.odm.sample.SimplePerson</value>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.springframework.ldap.odm.sample.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.ldap.core.DistinguishedName;
|
||||
import org.springframework.ldap.core.LdapTemplate;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.ldap.odm.sample.SearchForPeople;
|
||||
import org.springframework.ldap.test.LdapTestUtils;
|
||||
|
||||
public class TestSearchForPeople {
|
||||
// Base DN for test data
|
||||
private static final DistinguishedName baseName = new DistinguishedName("o=Whoniverse");
|
||||
|
||||
private static final String PRINCIPAL="uid=admin,ou=system";
|
||||
private static final String CREDENTIALS="secret";
|
||||
|
||||
// This port MUST be free on local host for these unit tests to function.
|
||||
private static int PORT=10389;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpClass() throws Exception {
|
||||
// Start an LDAP server and import test data
|
||||
LdapTestUtils.startApacheDirectoryServer(PORT, baseName.toString(), "odm-test", PRINCIPAL, CREDENTIALS, null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownClass() throws Exception {
|
||||
LdapTestUtils.destroyApacheDirectoryServer(PRINCIPAL, CREDENTIALS);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// Bind to the directory
|
||||
LdapContextSource contextSource = new LdapContextSource();
|
||||
contextSource.setUrl("ldap://127.0.0.1:" + PORT);
|
||||
contextSource.setUserDn("");
|
||||
contextSource.setPassword("");
|
||||
contextSource.setPooled(false);
|
||||
contextSource.afterPropertiesSet();
|
||||
|
||||
// Create the Sprint LDAP template
|
||||
LdapTemplate template = new LdapTemplate(contextSource);
|
||||
|
||||
// Clear out any old data - and load the test data
|
||||
LdapTestUtils.cleanAndSetup(template.getContextSource(), baseName, new ClassPathResource("testdata.ldif"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
// Very simple test - mainly just to exercise the code and to
|
||||
// ensure we get representative test coverage
|
||||
@Test
|
||||
public void runSample() throws Exception {
|
||||
PrintStream originalOut=System.out;
|
||||
try {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(output));
|
||||
|
||||
SearchForPeople.main(null);
|
||||
|
||||
assertEquals("dn=cn=Bramble Harvey,ou=Doctors,o=Whoniverse | objectClass=[person, top] | cn=[Bramble Harvey] | sn=[Harvey] | description=[Really not a Doctor] | userPassword=[] | telephoneNumber=[22] | seeAlso=[]",
|
||||
output.toString().trim());
|
||||
} finally {
|
||||
System.setOut(originalOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
116
samples/simple-odm/src/test/resources/testdata.ldif
Executable file
116
samples/simple-odm/src/test/resources/testdata.ldif
Executable file
@@ -0,0 +1,116 @@
|
||||
dn: ou=Enemies,o=Whoniverse
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
ou: Blizzard
|
||||
|
||||
dn: ou=Assistants,o=Whoniverse
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
ou: Blizzard
|
||||
|
||||
dn: ou=Doctors,o=Whoniverse
|
||||
objectClass: organizationalUnit
|
||||
objectClass: top
|
||||
ou: NCSoft
|
||||
|
||||
dn: cn=William Hartnell,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: William Hartnell
|
||||
description: First Doctor
|
||||
description: Grumpy
|
||||
sn: Hartnell
|
||||
telephonenumber: 1
|
||||
|
||||
dn: cn=Patrick Troughton,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Patrick Troughton
|
||||
description: Second Doctor
|
||||
description:Clown
|
||||
sn: Troughton
|
||||
telephonenumber: 2
|
||||
|
||||
dn: cn=Jon Pertwee,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Jon Pertwee
|
||||
description: Third Doctor
|
||||
description: Dandy
|
||||
sn: Pertwee
|
||||
telephonenumber: 3
|
||||
|
||||
dn: cn=Tom Baker,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Tom Baker
|
||||
description: Fourth Doctor
|
||||
description: The one and only!
|
||||
sn: Baker
|
||||
telephonenumber: 4
|
||||
|
||||
dn: cn=Peter Davison,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Peter Davison
|
||||
description: Fifth Doctor
|
||||
sn: Davison
|
||||
telephonenumber: 5
|
||||
|
||||
dn: cn=Paul Harvey,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
objectClass: userSecurityInformation
|
||||
cn: Paul Harvey
|
||||
description: Not a Doctor
|
||||
sn: Harvey
|
||||
telephonenumber: 11
|
||||
|
||||
dn: cn=Bramble Harvey,ou=Doctors,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: top
|
||||
cn: Bramble
|
||||
description: Really not a Doctor
|
||||
sn: Harvey
|
||||
telephonenumber: 22
|
||||
|
||||
dn: cn=Davros,ou=Enemies,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Davros
|
||||
description: Creator of the Daleks
|
||||
description: Kaled head scientist
|
||||
sn: Unknown
|
||||
|
||||
dn: cn=Daleks,ou=Enemies,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Daleks
|
||||
description: The Doctor's greatest foe
|
||||
sn: NA
|
||||
|
||||
dn: cn=Master,ou=Enemies,o=Whoniverse
|
||||
objectClass: person
|
||||
objectClass: inetorgperson
|
||||
objectclass: organizationalperson
|
||||
objectClass: top
|
||||
cn: Master
|
||||
description: An evil Time Lord
|
||||
sn: Unknown
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<includes>
|
||||
<include>org.springframework.ldap:spring-ldap-core</include>
|
||||
<include>org.springframework.ldap:spring-ldap-core-tiger</include>
|
||||
<include>org.springframework.ldap:spring-ldap-odm</include>
|
||||
<include>org.springframework.ldap:spring-ldap-test</include>
|
||||
</includes>
|
||||
<binaries>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<includes>
|
||||
<include>org.springframework.ldap:spring-ldap-core</include>
|
||||
<include>org.springframework.ldap:spring-ldap-core-tiger</include>
|
||||
<include>org.springframework.ldap:spring-ldap-odm</include>
|
||||
<include>org.springframework.ldap:spring-ldap-test</include>
|
||||
</includes>
|
||||
<binaries>
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
<directory>core-tiger/src/main/java</directory>
|
||||
<outputDirectory/>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>odm/src/main/java</directory>
|
||||
<outputDirectory/>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>test-support/src/main/java</directory>
|
||||
<outputDirectory/>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<include>org.springframework.ldap:spring-ldap-parent</include>
|
||||
<include>org.springframework.ldap:spring-ldap-core</include>
|
||||
<include>org.springframework.ldap:spring-ldap-core-tiger</include>
|
||||
<include>org.springframework.ldap:spring-ldap-odm</include>
|
||||
<include>org.springframework.ldap:spring-ldap-test</include>
|
||||
</includes>
|
||||
<binaries>
|
||||
|
||||
Reference in New Issue
Block a user