LDAP-270: Convert Spring LDAP doc to asciidoc

This commit is contained in:
Rob Winch
2013-10-23 10:54:48 -05:00
parent 27593ac973
commit fc8a31e4f0
32 changed files with 2278 additions and 4570 deletions

View File

@@ -1,14 +1,15 @@
buildscript {
repositories {
maven { url "http://repo.springsource.org/plugins-release" }
maven { url "http://dl.bintray.com/content/aalmiray/asciidoctor" }
}
dependencies {
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.3")
classpath("org.springframework.build.gradle:docbook-reference-plugin:0.2.6")
classpath('org.asciidoctor:asciidoctor-gradle-plugin:0.7.0')
}
}
apply plugin: "docbook-reference"
apply plugin: "asciidoctor"
apply plugin: "sonar-runner"
ext.GRADLE_SCRIPT_DIR = "${rootProject.projectDir}/gradle"
@@ -22,6 +23,7 @@ configure(allprojects) {
apply plugin: 'propdeps'
apply plugin: 'propdeps-idea'
apply plugin: 'propdeps-eclipse'
apply plugin: 'groovy'
group = "org.springframework.ldap"
@@ -65,9 +67,24 @@ sonarRunner {
}
}
reference {
sourceDir = file("src/docbkx")
pdfFilename = "spring-ldap-reference.pdf"
asciidoctor {
outputDir = new File("$buildDir/docs")
options = [
eruby: 'erubis',
attributes: [
copycss : '',
icons : 'font',
'source-highlighter': 'prettify',
sectanchors : '',
toc2: '',
idprefix: '',
idseparator: '-',
doctype: 'book',
numbered: '',
'spring-ldap-version' : project.version,
revnumber : project.version
]
]
}
task api(type: Javadoc) {
@@ -88,7 +105,7 @@ task api(type: Javadoc) {
classpath = files(coreModules*.javadoc*.classpath)
}
task docsZip(type: Zip) {
task docsZip(type: Zip, dependsOn: asciidoctor) {
group = "Distribution"
baseName = "spring-ldap"
classifier = "docs"
@@ -103,7 +120,8 @@ task docsZip(type: Zip) {
into "apidocs"
}
from (reference) {
from (asciidoctor.outputDir) {
include "*.html"
into "reference"
}
}

12
src/asciidoc/Guardfile Normal file
View File

@@ -0,0 +1,12 @@
require 'asciidoctor'
require 'erb'
guard 'shell' do
watch(/^.*\.adoc$/) {|m|
Asciidoctor.render_file(m[0], :to_dir => "build/", :safe => Asciidoctor::SafeMode::UNSAFE, :attributes=> {'idprefix' => '', 'numbered'=>'', 'idseparator' => '-', 'copycss' => '', 'icons' => 'font', 'source-highlighter' => 'prettify', 'sectanchors' => '', 'doctype' => 'book','toc2' => '', 'spring-ldap-version' => '2.0.0.CI-SNAPSHOT', 'revnumber' => '2.0.0.CI-SNAPSHOT' })
}
end
guard 'livereload' do
watch(%r{build/.+\.(css|js|html)$})
end

13
src/asciidoc/faq.adoc Normal file
View File

@@ -0,0 +1,13 @@
= Spring LDAP FAQ
== Operational Attributes
=== How do I remove an operational attribute using context.removeAttributeValue()?
The `DirContextAdapter` will only read the visible attributes per default. This is because the operational attributes will only be returned by the server if explicitly asked for, and there is no way for Spring LDAP to know what attributes to ask for. This means that the `DirContextAdapter` will not be populated with the operational attributes, and hence the `removeAttributeValue` will not have any effect (since from the `DirContextAdapter`'s point of view, it wasn't there in the first place).
There are basically two ways to do this:
* Use a search or lookup method that takes the attribute names as argument, like `LdapTemplate#lookup(Name, String[], ContextMapper)`. Use a `ContextMapper` implementation that just returns the supplied `DirContextAdapter` in `mapFromContext()`.
* Use `LdapTemplate#modifyAttributes(Name, ModificationItem[])` directly, manually building the `ModificationItem` array.

2228
src/asciidoc/index.adoc Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,135 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="query-builder-advanced">
<title>Advanced LDAP Queries</title>
<sect1>
<title>LDAP Query Builder Parameters</title>
<para>The <literal>LdapQueryBuilder</literal> and its associated classes is intended to support all parameters
that can be supplied to an LDAP search. The following parameters are supported:
<itemizedlist>
<listitem><literal>base</literal> - specifies the root DN in the LDAP tree where the search should start.</listitem>
<listitem><literal>searchScope</literal> - specifies how deep into the LDAP tree the search should traverse.</listitem>
<listitem><literal>attributes</literal> - specifies the attributes to return from the search. Default is all.</listitem>
<listitem><literal>countLimit</literal> - specifies the maximum number of entries to return from the search.</listitem>
<listitem><literal>timeLimit</literal> - specifies the maximum time that the search may take.</listitem>
<listitem>Search filter - the conditions that the entries we are looking for must meet.</listitem>
</itemizedlist>
</para>
<para>
An <literal>LdapQueryBuilder</literal> is created with a call to the <literal>query</literal> method of
<literal>LdapQueryBuilder</literal>. It's intended as a fluent builder API, where the base parameters are defined first,
followed by the filter specification calls. Once filter conditions have been started to be defined with a call to the
<literal>where</literal> method of <literal>LdapQueryBuilder</literal>, later attempts to call e.g. <literal>base</literal>
will be rejected. The base search parameters are optional, but at least one filter specification call is required.
</para>
<example>
<title>Search for all entries with objectclass person</title>
<programlisting>import static org.springframework.ldap.query.LdapQueryBuilder.query;
...
List&lt;Person&gt; persons = ldapTemplate.search(
query().where("objectclass").is("person"),
new PersonAttributesMapper());
</programlisting>
</example>
<example>
<title>Search for all entries with objectclass person and cn=John Doe</title>
<programlisting>import static org.springframework.ldap.query.LdapQueryBuilder.query;
...
List&lt;Person&gt; persons = ldapTemplate.search(
query().where("objectclass").is("person")
.and("cn").is("John Doe"),
new PersonAttributesMapper());
</programlisting>
</example>
<example>
<title>Search for all entries with objectclass person starting at <literal>dc=261consulting,dc=com</literal></title>
<programlisting>import static org.springframework.ldap.query.LdapQueryBuilder.query;
...
List&lt;Person&gt; persons = ldapTemplate.search(
query().base("dc=261consulting,dc=com")
.where("objectclass").is("person"),
new PersonAttributesMapper());
</programlisting>
</example>
<example>
<title>Search for all entries with objectclass person starting at <literal>dc=261consulting,dc=com</literal>,
only returning the cn attribute</title>
<programlisting>import static org.springframework.ldap.query.LdapQueryBuilder.query;
...
List&lt;Person&gt; persons = ldapTemplate.search(
query().base("dc=261consulting,dc=com")
.attributes("cn")
.where("objectclass").is("person"),
new PersonAttributesMapper());
</programlisting>
</example>
<example>
<title>Search for all entries with objectclass person where sn=Doe or Doo (nested query)</title>
<programlisting>import static org.springframework.ldap.query.LdapQueryBuilder.query;
...
List&lt;Person&gt; persons = ldapTemplate.search(
query().where("objectclass").is("person"),
.and(query().where("cn").is("Doe").or("cn").is("Doo));
new PersonAttributesMapper());
</programlisting>
</example>
</sect1>
<sect1>
<title>Filter Criteria</title>
<para>
The examples above demonstrates simple equals conditions in LDAP filters. The LDAP query builder has support
for the following criteria types:
<itemizedlist>
<listitem><literal>is</literal> - specifies an equals condition (=).</listitem>
<listitem><literal>gte</literal> - specifies a greater than or equals condition (&gt;=).</listitem>
<listitem><literal>lte</literal> - specifies a less than or equals condition (&lt;=).</listitem>
<listitem>
<literal>like</literal> - specifies a &quot;like&quot; condition where wildcards can be included in the query,
e.g. <literal>where("cn").like("J*hn Doe")</literal> will result int the filter <literal>(cn=J*hn Doe)</literal>.
</listitem>
<listitem>
<literal>whitespaceWildcardsLike</literal> - specifies a condition where all whitespace is replaced with wildcards,
e.g. <literal>where("cn").whitespaceWildcardsLike("John Doe")</literal> will result in the filter
<literal>(cn=*John*Doe*)</literal>.
</listitem>
<listitem>
<literal>isPresent</literal> - specifies condition that checks for the presence of an attribute,
e.g. <literal>where("cn").isPresent()</literal> will result in the filter <literal>(cn=*)</literal>.
</listitem>
<listitem>
<literal>not</literal> - specifies that the current condition should be negated, e.g.
<literal>where("sn").not().is("Doe)</literal> will result in the filter <literal>(!(sn=Doe))</literal>
</listitem>
</itemizedlist>
</para>
</sect1>
<sect1>
<title>Hardcoded Filters</title>
<para>
There are occasions when you will want to specify a hardcoded filter as input to an <literal>LdapQuery</literal>.
<literal>LdapQueryBuilder</literal> has two methods for this purpose:
<itemizedlist>
<listitem>
<literal>filter(String hardcodedFilter)</literal> - uses the specified string as filter.
Note that the specified input string will not be touched in any way, meaning that this method is not
particularly well suited if you are building filters from user input.
</listitem>
<listitem>
<literal>filter(String filterFormat, String... params)</literal> - uses the specified string as input
to <literal>MessageFormat</literal>, properly encoding the parameters and inserting them at the
specified places in the filter string.
</listitem>
</itemizedlist>
</para>
<para>
You cannot mix the hardcoded filter methods with the <literal>where</literal> approach described above; it's
either one or the other. What this means is that if you specified a filter using <literal>filter()</literal>
you will get an exception if you try to call <literal>where</literal> afterwards.
</para>
</sect1>
</chapter>

View File

@@ -1,407 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="basic">
<title>Basic Operations</title>
<sect1 id="basic-searches">
<title>Search and Lookup Using AttributesMapper</title>
<para>In this example we will use an <literal>AttributesMapper</literal>
to easily build a List of all common names of all person objects.</para>
<example>
<title>AttributesMapper that returns a single attribute</title>
<programlisting>package com.example.dao;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
public void setLdapTemplate(LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
public List&lt;String&gt; getAllPersonNames() {
return ldapTemplate.search(query()
.where("objectclass").is("person"),
<emphasis role="bold"> new AttributesMapper&lt;String&gt;() {
public String mapFromAttributes(Attributes attrs)
throws NamingException {
return (String) attrs.get("cn").get();
}
}</emphasis>);
}
}</programlisting>
</example>
<para>The inline implementation of <literal>AttributesMapper</literal>
just gets the desired attribute value from the
<literal>Attributes</literal> and returns it. Internally,
<literal>LdapTemplate</literal> iterates over all entries found, calling
the given <literal>AttributesMapper</literal> for each entry, and collects
the results in a list. The list is then returned by the
<literal>search</literal> method.</para>
<para>Note that the <literal>AttributesMapper</literal> implementation
could easily be modified to return a full <literal>Person</literal>
object:</para>
<example>
<title>AttributesMapper that returns a Person object</title>
<programlisting>package com.example.dao;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
...
<emphasis role="bold"> private class PersonAttributesMapper implements AttributesMapper&lt;Person&gt; {
public Person mapFromAttributes(Attributes attrs) throws NamingException {
Person person = new Person();
person.setFullName((String)attrs.get("cn").get());
person.setLastName((String)attrs.get("sn").get());
person.setDescription((String)attrs.get("description").get());
return person;
}
}
</emphasis>
public List&lt;Person&gt; getAllPersons() {
return ldapTemplate.search(query()
.where("objectclass").is("person"), <emphasis
role="bold">new PersonAttributesMapper()</emphasis>);
}
}</programlisting>
</example>
<para>If you have the distinguished name (<literal>dn</literal>) that
identifies an entry, you can retrieve the entry directly, without
searching for it. This is called a <emphasis>lookup</emphasis> in Java
LDAP. The following example shows how a lookup results in a Person
object:</para>
<example>
<title>A lookup resulting in a Person object</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
...
public Person findPerson(String dn) {
return ldapTemplate.lookup(dn, new PersonAttributesMapper());
}
}</programlisting>
</example>
<para>This will look up the specified <literal>dn</literal> and pass the
found attributes to the supplied <literal>AttributesMapper</literal>, in
this case resulting in a <literal>Person</literal> object.</para>
</sect1>
<sect1 id="basic-queries">
<title>Building LDAP Queries</title>
<para>LDAP searches involve a number of parameters, e.g. Base LDAP path,
search scope, attributes to return, and search filters.</para>
<para>Spring LDAP provides an <literal>LdapQueryBuilder</literal> with a fluent
API for building LDAP Queries.</para>
<para>Let's say that we want to perform a search starting at the
base DN <literal>dc=261consulting,dc=com</literal>, limiting the returned attributes to &quot;cn&quot;
and &quot;sn&quot;, with the following filter:
<literal>(&amp;(objectclass=person)(sn=?))</literal>, where we want the
<literal>?</literal> to be replaced with the value of the parameter
<literal>lastName</literal>. This is how we do it using the LdapQueryBuilder:</para>
<example>
<title>Building a search filter dynamically</title>
<programlisting>package com.example.dao;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
...
public List getPersonNamesByLastName(String lastName) {
<emphasis role="bold">
LdapQuery query = query()
.base("dc=261consulting,dc=com")
.attributes("cn", "sn")
.where("objectclass").is("person")
.and("sn").is(lastName);
</emphasis>
return ldapTemplate.search(query,
new AttributesMapper() {
public Object mapFromAttributes(Attributes attrs)
throws NamingException {
return attrs.get("cn").get();
}
});
}
}</programlisting>
</example>
<para>
<note>
In addition to simplifying building of complex search parameters,
the <literal>LdapQueryBuilder</literal> and its associated classes
also provide proper escaping of any unsafe characters in search filters.
This prevents &quot;ldap injection&quot;, where a user might use such
characters to inject unwanted operations into your LDAP operations.
</note>
<note>
There are many overloaded methods in <literal>LdapTemplate</literal> for
performing LDAP searches. This is in order to accommodate for as many different use cases
and programming style preferences as possible. For the vast majority of use cases the ones that
take an <literal>LdapQuery</literal> as input will be the recommended methods to use.
</note>
<note>
The <literal>AttributesMapper</literal> is just one of the available callback interfaces to use
when handling search and lookup data. See <xref linkend="dirobjectfactory" /> for alternatives.
</note>
</para>
<para>
For more information on the <literal>LdapQueryBuilder</literal> see <xref linkend="query-builder-advanced" />.
</para>
</sect1>
<sect1 id="ldap-names">
<title>Dynamically Building Distinguished Names</title>
<para>
The standard Java implementation of Distinguished Name, <ulink
url="http://docs.oracle.com/javase/6/docs/api/javax/naming/ldap/LdapName.html">LdapName</ulink>,
performs very well when it comes to parsing of Distinguished Names. However, in practical use
this implementation has a number of shortcomings:
<itemizedlist>
<listitem>
<para>
The <literal>LdapName</literal> implementation is mutable, which is badly suited for an object
representing identity.
</para>
</listitem>
<listitem>
<para>
Despite its mutable nature, the API for dynamically building or modifying Distinguished Names using
<literal>LdapName</literal> is cumbersome. Extracting values of indexed or (particularly)
named components is also a little bit awkward.
</para>
</listitem>
<listitem>
<para>
Many of the operations on <literal>LdapName</literal> throw checked Exceptions, requiring unnecessary
try-catch statements for situations where the error is typically fatal and cannot be repaired in
a meaningful manner.
</para>
</listitem>
</itemizedlist>
To simplify working with Distinguished Names, Spring LDAP provides an <literal>LdapNameBuilder</literal>, as
well as a number of utility methods in <literal>LdapUtils</literal> that helps working with
<literal>LdapName</literal>.
</para>
<para>
Below are a couple of examples of how these utilities can simplify handling of distinguished names.
<example>
<title>Dynamically building an LdapName using LdapNameBuilder</title>
<programlisting>package com.example.dao;
import org.springframework.ldap.support.LdapNameBuilder;
import javax.naming.Name;
public class PersonDaoImpl implements PersonDao {
public static final String BASE_DN = "dc=example,dc=com";
...
protected Name buildDn(Person p) {
<emphasis role="bold"> return LdapNameBuilder.newLdapName(BASE_DN)
.add("c", p.getCountry())
.add("ou", p.getCompany())
.add("cn", p.getFullname())
.build();
</emphasis>
}
</programlisting>
</example>
Assuming that a Person has the following attributes:
</para>
<informaltable>
<tgroup cols="2">
<tbody>
<row>
<entry><literal>country</literal></entry>
<entry>Sweden</entry>
</row>
<row>
<entry><literal>company</literal></entry>
<entry>Some Company</entry>
</row>
<row>
<entry><literal>fullname</literal></entry>
<entry>Some Person</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>The code above would then result in the following distinguished
name:</para>
<para><programlisting>cn=Some Person, ou=Some Company, c=Sweden, dc=example, dc=com</programlisting></para>
<para>
<example>
<title>Extracting values from a distinguished name using LdapUtils</title>
<programlisting>package com.example.dao;
import org.springframework.ldap.support.LdapNameBuilder;
import javax.naming.Name;
public class PersonDaoImpl implements PersonDao {
...
protected Person buildPerson(Name dn, Attributes attrs) {
Person person = new Person();
person.setCountry(<emphasis role="bold">LdapUtils.getStringValue(dn, "c")</emphasis>);
person.setCompany(<emphasis role="bold">LdapUtils.getStringValue(dn, "ou")</emphasis>);
person.setFullname(<emphasis role="bold">LdapUtils.getStringValue(dn, "cn")</emphasis>);
// Populate rest of person object using attributes.
return person;
}
</programlisting>
</example>
Since Java version &lt;=1.4 didn't provide any public Distinguished Name implementation at all, Spring LDAP
1.3.2 and lower provided its own implementation, <literal>DistinguishedName</literal>. This implementation
suffered from a couple of shortcomings of its own, and has been deprecated in version 2.0.
Users are now recommended to use <literal>LdapName</literal> along with the utilities described above instead.
</para>
</sect1>
<sect1 id="basic-binding-unbinding">
<title>Binding and Unbinding</title>
<sect2 id="basic-binding-data">
<title>Binding Data</title>
<para>Inserting data in Java LDAP is called binding. In order to do
that, a distinguished name that uniquely identifies the new entry is
required. The following example shows how data is bound using
LdapTemplate:</para>
<example>
<title>Binding data using Attributes</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
...
public void create(Person p) {
Name dn = buildDn(p);
<emphasis role="bold"> ldapTemplate.bind(dn, null, buildAttributes(p));
</emphasis> }
private Attributes buildAttributes(Person p) {
Attributes attrs = new BasicAttributes();
BasicAttribute ocattr = new BasicAttribute("objectclass");
ocattr.add("top");
ocattr.add("person");
attrs.put(ocattr);
attrs.put("cn", "Some Person");
attrs.put("sn", "Person");
return attrs;
}
}</programlisting>
</example>
<para>The Attributes building is--while dull and verbose--sufficient for
many purposes. It is, however, possible to simplify the binding
operation further, which will be described in <xref
linkend="dirobjectfactory" />.</para>
</sect2>
<sect2 id="basic-unbinding-data">
<title>Unbinding Data</title>
<para>Removing data in Java LDAP is called unbinding. A distinguished
name (dn) is required to identify the entry, just as in the binding
operation. The following example shows how data is unbound using
LdapTemplate:</para>
<example>
<title>Unbinding data</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
...
public void delete(Person p) {
Name dn = buildDn(p);
<emphasis role="bold"> ldapTemplate.unbind(dn);
</emphasis> }
}</programlisting>
</example>
</sect2>
</sect1>
<sect1 id="basic-modifying">
<title>Modifying</title>
<para>In Java LDAP, data can be modified in two ways: either using
<emphasis>rebind</emphasis> or
<emphasis>modifyAttributes</emphasis>.</para>
<sect2>
<title>Modifying using <literal>rebind</literal></title>
<para>A <literal>rebind</literal> is a very crude way to modify data.
It's basically an <literal>unbind</literal> followed by a
<literal>bind</literal>. It looks like this:</para>
<example>
<title>Modifying using rebind</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
...
public void update(Person p) {
Name dn = buildDn(p);
<emphasis role="bold"> ldapTemplate.rebind(dn, null, buildAttributes(p));
</emphasis> }
}</programlisting>
</example>
</sect2>
<sect2 id="modify-modifyAttributes">
<title>Modifying using <literal>modifyAttributes</literal></title>
<para>If only the modified attributes should be replaced, there is a
method called <literal>modifyAttributes</literal> that takes an array of
modifications:</para>
<example>
<title>Modifying using modifyAttributes</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
...
public void updateDescription(Person p) {
Name dn = buildDn(p);
Attribute attr = new BasicAttribute("description", p.getDescription())
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
<emphasis role="bold"> ldapTemplate.modifyAttributes(dn, new ModificationItem[] {item});
</emphasis> }
}</programlisting>
</example>
<para>Building <literal>Attributes</literal> and
<literal>ModificationItem</literal> arrays is a lot of work, but as you
will see in <xref linkend="dirobjectfactory" />, the update operations
can be simplified.</para>
</sect2>
</sect1>
<sect1 id="samples">
<title>Sample applications</title>
<para>It is recommended that you review the Spring LDAP sample
applications included in the release distribution for best-practice
illustrations of the features of this library.</para>
</sect1>
</chapter>

View File

@@ -1,587 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="configuration">
<title>Configuration</title>
<sect1 id="configuration-intro">
<title>Introduction</title>
The recommended way of configuring Spring LDAP is using the custom XML configuration namespace.
In order to make this available you need to include the Spring LDAP namespace declaration in your
bean file, e.g.:
<informalexample>
<programlisting>
&lt;beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<emphasis role="bold">xmlns:ldap="http://www.springframework.org/schema/ldap"</emphasis>
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
<emphasis role="bold">http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd"</emphasis>&gt;
</programlisting>
</informalexample>
</sect1>
<sect1 id="context-source-configuration">
<title>ContextSource Configuration</title>
<para>
The <literal>ContextSource</literal> is defined using a <literal>&lt;ldap:context-source&gt;</literal>
tag. The simplest possible <literal>context-source</literal> declaration requires you to specify a
server url, a username, and a password:
<example>
<title>Simplest possible context-source declaration</title>
<programlisting><![CDATA[
<ldap:context-source username="cn=Administrator" password="secret" url="ldap://localhost:389" />]]></programlisting>
</example>
This will create an <literal>LdapContextSource</literal> with default values (see below),
and the url and authentication information as specified.
</para>
<para>
The configurable attributes on context-source are as follows (required attributes marked with *):
</para>
<table frame="all">
<title>ContextSource Configuration Attributes</title>
<tgroup align="left" cols="3" colsep="1" rowsep="1">
<colspec colname="c1" />
<colspec colname="c2" />
<colspec colname="c3" />
<thead>
<row>
<entry>Attribute</entry>
<entry>Default</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<literal>id</literal>
</entry>
<entry>
<literal>contextSource</literal>
</entry>
<entry>
The id of the created bean.
</entry>
</row>
<row>
<entry>
<literal>username</literal>
</entry>
<entry>
</entry>
<entry>
The username (principal) to use when authenticating with the LDAP server.
This will usually be the distinguished name of an admin user (e.g.
<literal>cn=Administrator</literal>, but may differ depending on server
and authentication method.
Required if <literal>authentication-source-ref</literal> is not explicitly configured.
</entry>
</row>
<row>
<entry>
<literal>password</literal>
</entry>
<entry>
</entry>
<entry>
The password (credentials) to use when authenticating with the LDAP server.
Required if <literal>authentication-source-ref</literal> is not explicitly configured.
</entry>
</row>
<row>
<entry>
<literal>url</literal> *
</entry>
<entry>
</entry>
<entry>
The URL of the LDAP server to use. The URL should be in the format
<literal>ldap://myserver.example.com:389</literal>.
For SSL access, use the <literal>ldaps</literal> protocol and the appropriate port, e.g.
<literal>ldaps://myserver.example.com:636</literal>. If fail-over functionality is desired,
more than one URL can be specified, separated using comma (,).
</entry>
</row>
<row>
<entry>
<literal>base</literal>
</entry>
<entry>
<literal>LdapUtils.emptyLdapName()</literal>
</entry>
<entry>
The base DN. When this attribute has been configured, all Distinguished Names supplied to
and received from LDAP operations will be relative to the sepecified LDAP path.
This can significantly simplify working against the LDAP tree; however there are several
occasions when you will need to have access to the base path.
For more information on this, please refer to <xref linkend="base-context-configuration" />
</entry>
</row>
<row>
<entry>
<literal>anonymous-read-only</literal>
</entry>
<entry>
<literal>false</literal>
</entry>
<entry>
Defines whether read-only operations will be performed using an anonymous (unauthenticated) context.
<emphasis role="bold">Note</emphasis> that setting this parameter to <literal>true</literal>
together with the compensating transaction support is not supported and will be rejected.
</entry>
</row>
<row>
<entry>
<literal>referral</literal>
</entry>
<entry>
<literal>null</literal>
</entry>
<entry>
Defines the strategy to handle referrals, as described
<ulink url="http://docs.oracle.com/javase/jndi/tutorial/ldap/referral/jndi.html">here</ulink>.
Valid values are:
<itemizedlist>
<listitem><literal>ignore</literal></listitem>
<listitem><literal>follow</literal></listitem>
<listitem><literal>throw</literal></listitem>
</itemizedlist>
</entry>
</row>
<row>
<entry>
<literal>native-pooling</literal>
</entry>
<entry>
<literal>false</literal>
</entry>
<entry>
Specify whether native Java LDAP connection pooling should be used. Consider using
Spring LDAP connection pooling instead. See <xref linkend="pooling" /> for more information.
</entry>
</row>
<row>
<entry>
<literal>authentication-source-ref</literal>
</entry>
<entry>
A <literal>SimpleAuthenticationSource</literal> instance.
</entry>
<entry>
Id of the AuthenticationSource instance to use (see below).
</entry>
</row>
<row>
<entry>
<literal>authentication-strategy-ref</literal>
</entry>
<entry>
A <literal>SimpleDirContextAuthenticationStrategy</literal> instance.
</entry>
<entry>
Id of the DirContextAuthenticationStrategy instance to use (see below).
</entry>
</row>
<row>
<entry>
<literal>base-env-props-ref</literal>
</entry>
<entry>
A <literal>SimpleDirContextAuthenticationStrategy</literal> instance.
</entry>
<entry>
Reference to a Map of custom environment properties that should supplied with the environment
sent to the <literal>DirContext</literal> on construction.
</entry>
</row>
</tbody>
</tgroup>
</table>
<sect2 id="dir-context-authentication">
<title>DirContext Authentication</title>
<para>
When <literal>DirContext</literal> instances are created to be used for performing
operations on an LDAP server these contexts often need to be authenticated. There are
different options for configuring this using Spring LDAP, described in this chapter.
<note>
<para>
This section refers to authenticating contexts in the core functionality
of the <literal>ContextSource</literal> - to construct <literal>DirContext</literal> instances
for use by <literal>LdapTemplate</literal>. LDAP is commonly used for the sole purpose
of user authentication, and the <literal>ContextSource</literal> may be used for that as
well. This process is discussed in <xref linkend="user-authentication" />.
</para>
</note>
</para>
<para>
Authenticated contexts are created for both read-only and
read-write operations by default. You specify
<literal>username</literal> and <literal>password</literal> of the LDAP
user to be used for authentication on the
<literal>context-source</literal> element.
<note>
<para>
If <literal>username</literal> is the dn of an LDAP user, it needs to be the full
Distinguished Name (DN) of the user from the root of the LDAP tree,
regardless of whether a <literal>base</literal> LDAP path has been specified on
the <literal>context-source</literal> element.
</para>
</note>
</para>
<para>
Some LDAP server setups allow anonymous read-only access. If you
want to use anonymous Contexts for read-only operations, set the
<literal>anonymous-read-only</literal> attribute to
<literal>true</literal>.
</para>
<sect3 id="custom-authentication-processing">
<title>Custom DirContext Authentication Processing</title>
<para>
The default authentication mechanism used in Spring LDAP is <literal>SIMPLE</literal> authentication.
This means that the principal (as specified to the <literal>username</literal> attribute) and
the credentials (as specified to the <literal>password</literal>) are set in
the Hashtable sent to the <literal>DirContext</literal> implementation constructor.
</para>
<para>
There are many occasions when this processing is not sufficient. For instance,
LDAP Servers are commonly set up to only accept communication on a secure TLS channel;
there might be a need to use the particular LDAP Proxy Auth mechanism, etc.
</para>
<para>
It is possible to specify an alternative authentication mechanism by supplying a
<literal>DirContextAuthenticationStrategy</literal> implementation reference
to the <literal>context-source</literal> element using the <literal>authentication-strategy-ref</literal>
attribute.
</para>
<sect4 id="authentication-tls">
<title>TLS</title>
<para>
Spring LDAP provides two different configuration options for LDAP servers requiring TLS secure
channel communication: <literal>DefaultTlsDirContextAuthenticationStrategy</literal> and
<literal>ExternalTlsDirContextAuthenticationStrategy</literal>. Both these
implementations will negotiate a TLS channel on the target connection, but they differ in the actual authentication mechanism.
Whereas the <literal>DefaultTlsDirContextAuthenticationStrategy</literal> will apply SIMPLE authentication
on the secure channel (using the specified <literal>userDn</literal> and <literal>password</literal>),
the <literal>ExternalDirContextAuthenticationStrategy</literal> will use EXTERNAL SASL authentication,
applying a client certificate configured using system properties for authentication.
</para>
<para>
Since different LDAP server implementations respond differently to explicit shutdown of the
TLS channel (some servers require the connection be shutdown gracefully; others do not support it),
the TLS <literal>DirContextAuthenticationStrategy</literal> implementations support specifying
the shutdown behavior using the <literal>shutdownTlsGracefully</literal> parameter. If this
property is set to <literal>false</literal> (the default), no explicit TLS shutdown will happen;
if it is <literal>true</literal>, Spring LDAP will try to shutdown the TLS channel gracefully
before closing the target context.
</para>
<note>
<para>
When working with TLS connections you need to make sure that the native LDAP
Pooling functionality (as specified using the <literal>native-pooling</literal> attribute
is turned off. This is particularly important if <literal>shutdownTlsGracefully</literal>
is set to <literal>false</literal>. However, since the TLS channel negotiation process is
quite expensive, great performance benefits will be gained by using the Spring LDAP
Pooling Support, described in <xref linkend="pooling" />.
</para>
</note>
</sect4>
</sect3>
<sect3>
<title>Custom Principal and Credentials Management</title>
<para>
While the user name (i.e. user DN) and password used for
creating an authenticated <literal>Context</literal> are statically defined by
default - the ones defined in the <literal>context-source</literal> element
configuration will be used throughout the lifetime of the
<literal>ContextSource</literal> - there are several cases where this is not the desired behaviour.
A common scenario is that the principal and credentials of the current user should be used when
executing LDAP operations for that user. The default behaviour can be
modified by supplying a reference to an <literal>AuthenticationSource</literal>
implementation to the <literal>context-source</literal> element using the
<literal>authentication-source-ref</literal> element,
instead of explicitly specifying the <literal>username</literal> and
<literal>password</literal>. The
<literal>AuthenticationSource</literal> will be queried by the
<literal>ContextSource</literal> for principal and credentials each
time an authenticated <literal>Context</literal> is to be
created.
</para>
<para>
If you are using <ulink url="http://springsecurity.org">Spring Security</ulink>
you can make sure the principal and credentials of the currently logged in user
is used at all times by configuring your <literal>ContextSource</literal>
with an instance of the <literal>SpringSecurityAuthenticationSource</literal>
shipped with Spring Security.
</para>
<example>
<title>Using the <literal>SpringSecurityAuthenticationSource</literal></title>
<programlisting><![CDATA[
<beans>
...
<ldap:context-source
url="ldap://localhost:389"
authentication-source-ref="springSecurityAuthenticationSource/>
<bean id="springSecurityAuthenticationSource"
class="org.springframework.security.ldap.SpringSecurityAuthenticationSource" />
...
</beans>]]></programlisting>
</example>
<note>
<para>
We don't specify any <literal>username</literal> or
<literal>password</literal> to our <literal>context-source</literal>
when using an <literal>AuthenticationSource</literal> - these
properties are needed only when the default behaviour is
used.
</para>
</note>
<note>
<para>
When using the <literal>SpringSecurityAuthenticationSource</literal>
you need to use Spring Security's <literal>LdapAuthenticationProvider</literal> to authenticate the
users against LDAP.
</para>
</note>
</sect3>
</sect2>
<sect2 id="context-source-pooling">
<title>Native Java LDAP Pooling</title>
<para>The internal Java LDAP provider provides some very basic pooling capabilities.
This LDAP connection pooling can be turned on/off using the
<literal>pooled</literal> flag on <literal>AbstractContextSource</literal>.
The default value is <literal>false</literal> (since release 1.3), i.e. the native
Java LDAP pooling will be turned off. The configuration of LDAP connection pooling is managed using
<literal>System</literal> properties, so this needs to be handled
manually, outside of the Spring Context configuration. Details of the native pooling configuration
can be found <ulink url="http://java.sun.com/products/jndi/tutorial/ldap/connect/config.html">here</ulink>.
</para>
<para><note>
There are several serious deficiencies in the built-in LDAP connection pooling,
which is why Spring LDAP provides a more sophisticated approach to LDAP connection pooling,
described in <xref linkend="pooling" />. If pooling functionality is required, this is the
recommended approach.</note></para>
<para><note>
Regardless of the pooling configuration, the <literal>ContextSource#getContext(String principal, String credentials)</literal>
method will always explicitly <emphasis>not</emphasis> use native Java LDAP Pooling, in order for
reset passwords to take effect as soon as possible.
</note></para>
</sect2>
<sect2 id="context-source-advanced">
<title>Advanced ContextSource Configuration</title>
<sect3 id="context-source-custom-env-properties">
<title>Custom DirContext Environment Properties</title>
<para>
In some cases the user might want to specify additional environment setup properties
in addition to the ones directly configurable on <literal>context-source</literal>.
Such properties should be set in a <literal>Map</literal> and referenced in
the <literal>base-env-props-ref</literal> attribute.</para>
</sect3>
</sect2>
</sect1>
<sect1 id="ldap-template-configuration">
<title>LdapTemplate Configuration</title>
<para>
The <literal>LdapTemplate</literal> is defined using a <literal>&lt;ldap:ldap-template&gt;</literal>
tag. The simplest possible <literal>ldap-template</literal> declaration is the simple tag:
<example>
<title>Simplest possible ldap-template declaration</title>
<programlisting><![CDATA[
<ldap:ldap-template />]]></programlisting>
</example>
This will create an <literal>LdapTemplate</literal> instance with the default id, referencing the
default <literal>ContextSource</literal>, which is expected to have the id <literal>contextSource</literal>
(the default for the <literal>context-source</literal> element).
</para>
<para>
The configurable attributes on <literal>ldap-template</literal> are as follows:
</para>
<table frame="all">
<title>LdapTemplate Configuration Attributes</title>
<tgroup align="left" cols="3" colsep="1" rowsep="1">
<colspec colname="c1" />
<colspec colname="c2" />
<colspec colname="c3" />
<thead>
<row>
<entry>Attribute</entry>
<entry>Default</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<literal>id</literal>
</entry>
<entry>
<literal>ldapTemplate</literal>
</entry>
<entry>
The id of the created bean.
</entry>
</row>
<row>
<entry>
<literal>context-source-ref</literal>
</entry>
<entry>
<literal>contextSource</literal>
</entry>
<entry>
Id of the ContextSource instance to use.
</entry>
</row>
<row>
<entry>
<literal>count-limit</literal>
</entry>
<entry>
<literal>0</literal>
</entry>
<entry>
The default count limit for searches. 0 means no limit.
</entry>
</row>
<row>
<entry>
<literal>time-limit</literal>
</entry>
<entry>
<literal>0</literal>
</entry>
<entry>
The default time limit for searches in milliseconds. 0 means no limit.
</entry>
</row>
<row>
<entry>
<literal>search-scope</literal>
</entry>
<entry>
<literal>SUBTREE</literal>
</entry>
<entry>
The default search scope for searches.
Valid values are:
<itemizedlist>
<listitem><literal>OBJECT</literal></listitem>
<listitem><literal>ONELEVEL</literal></listitem>
<listitem><literal>SUBTREE</literal></listitem>
</itemizedlist>
</entry>
</row>
<row>
<entry>
<literal>ignore-name-not-found</literal>
</entry>
<entry>
<literal>false</literal>
</entry>
<entry>
Specifies whether NameNotFoundException should be ignored in searches. Setting this
attribute to true will cause errors caused by invalid search base to be silently swallowed.
</entry>
</row>
<row>
<entry>
<literal>ignore-partial-result</literal>
</entry>
<entry>
<literal>false</literal>
</entry>
<entry>
Specifies whether PartialResultException should be ignored in searches. Some LDAP servers
have problems with referrals; these should normally be followed automatically, but if this
doesn't work it will manifest itself with a PartialResultException. Setting this attribute
to true presents a work-around to this problem.
</entry>
</row>
<row>
<entry>
<literal>odm-ref</literal>
</entry>
<entry>
</entry>
<entry>
Id of the ObjectDirectoryMapper instance to use. Default is a default-configured DefaultObjectDirectoryMapper.
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="base-context-configuration">
<title>Obtaining a reference to the base LDAP path</title>
<para>
As described above, a base LDAP path may be supplied to the <literal>ContextSource</literal>,
specifying the root in the LDAP tree to which all operations will be relative. This means that
you will only be working with relative distinguished names throughout your system, which is
typically rather handy. There are however some cases in which you will need to have access
to the base path in order to be able to construct full DNs, relative to the actual root of the LDAP tree.
One example would be when working with LDAP groups (e.g. <literal>groupOfNames</literal> objectclass),
in which case each group member attribute value will need to be the full DN of the referenced member.</para>
<para>For that reason, Spring LDAP has a mechanism by which any Spring controlled bean may be supplied
the base path on startup. For beans to be notified of the base path, two things need to be in place:
First of all, the bean that wants the base path reference needs to implement the
<literal>BaseLdapNameAware</literal> interface. Secondly, a <literal>BaseLdapPathBeanPostProcessor</literal>
needs to be defined in the application context
</para>
<example>
<title>Implementing <literal>BaseLdapNameAware</literal></title>
<programlisting>package com.example.service;
public class PersonService implements PersonService, <emphasis role="bold">BaseLdapNameAware</emphasis> {
...
<emphasis role="bold">private LdapName basePath;
public void setBaseLdapPath(LdapName basePath) {
this.basePath = basePath;
}</emphasis>
...
private LdapName getFullPersonDn(Person person) {
return LdapNameBuilder.newLdapName(<emphasis role="bold">basePath</emphasis>)
.append(person.getDn())
.build();
}
...
}</programlisting>
</example>
<example>
<title>Specifying a <literal>BaseLdapPathBeanPostProcessor</literal> in your <literal>ApplicationContext</literal></title>
<programlisting>&lt;beans&gt;
...
&lt;ldap:context-source
username="cn=Administrator"
password="secret"
url="ldap://localhost:389"
base="dc=261consulting,dc=com" /&gt;
...
<emphasis role="bold">&lt;bean class="org.springframework.ldap.core.support.BaseLdapPathBeanPostProcessor" /&gt;</emphasis>
&lt;/beans&gt;
</programlisting>
</example>
<para>
The default behaviour of the <literal>BaseLdapPathBeanPostProcessor</literal> is to use the base path of the single
defined <literal>BaseLdapPathSource</literal> (<literal>AbstractContextSource</literal>)in the <literal>ApplicationContext</literal>.
If more than one <literal>BaseLdapPathSource</literal> is defined, you will need to specify which one to use with the
<literal>baseLdapPathSourceName</literal> property.
</para>
</sect1>
</chapter>

View File

@@ -1,206 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="processor">
<title>Processing the DirContext</title>
<sect1 id="processor-overview">
<title>Custom DirContext Pre/Postprocessing</title>
<para>In some situations, one would like to perform operations on the
<literal>DirContext</literal> before and after the search operation. The
interface that is used for this is called
<literal>DirContextProcessor</literal>:</para>
<informalexample>
<programlisting>public interface DirContextProcessor {
public void preProcess(DirContext ctx) throws NamingException;
public void postProcess(DirContext ctx) throws NamingException;
}</programlisting>
</informalexample>
<para>The <literal>LdapTemplate</literal> class has a search method that
takes a <literal>DirContextProcessor</literal>:</para>
<informalexample>
<programlisting>public void search(SearchExecutor se, NameClassPairCallbackHandler handler,
DirContextProcessor processor) throws DataAccessException;</programlisting>
</informalexample>
<para>Before the search operation, the <literal>preProcess</literal>
method is called on the given <literal>DirContextProcessor</literal>
instance. After the search has been executed and the resulting
<literal>NamingEnumeration</literal> has been processed, the
<literal>postProcess</literal> method is called. This enables a user to
perform operations on the <literal>DirContext</literal> to be used in the
search, and to check the <literal>DirContext</literal> when the search has
been performed. This can be very useful for example when handling request
and response controls.</para>
<para>There are also a few convenience methods for those that don't need a
custom <literal>SearchExecutor</literal>:</para>
<informalexample>
<programlisting>public void search(Name base, String filter,
SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor)
public void search(String base, String filter,
SearchControls controls, NameClassPairCallbackHandler handler, DirContextProcessor processor)
public void search(Name base, String filter,
SearchControls controls, AttributesMapper mapper, DirContextProcessor processor)
public void search(String base, String filter,
SearchControls controls, AttributesMapper mapper, DirContextProcessor processor)
public void search(Name base, String filter,
SearchControls controls, ContextMapper mapper, DirContextProcessor processor)
public void search(String base, String filter,
SearchControls controls, ContextMapper mapper, DirContextProcessor processor)</programlisting>
</informalexample>
</sect1>
<sect1 id="processor-others">
<title>Implementing a Request Control DirContextProcessor</title>
<para>The LDAPv3 protocol uses Controls to send and receive additional
data to affect the behavior of predefined operations. In order to simplify
the implementation of a request control
<literal>DirContextProcessor</literal>, Spring LDAP provides the base
class <literal>AbstractRequestControlDirContextProcessor</literal>. This
class handles the retrieval of the current request controls from the
<literal>LdapContext</literal>, calls a template method for creating a
request control, and adds it to the <literal>LdapContext</literal>. All
you have to do in the subclass is to implement the template method
<literal>createRequestControl</literal>, and of course the
<literal>postProcess</literal> method for performing whatever you need to
do after the search.</para>
<informalexample>
<programlisting>public abstract class AbstractRequestControlDirContextProcessor implements
DirContextProcessor {
public void preProcess(DirContext ctx) throws NamingException {
...
}
public abstract Control createRequestControl();
}</programlisting>
<para>A typical <literal>DirContextProcessor</literal> will be similar to the following:</para>
</informalexample>
<example>
<title>A request control DirContextProcessor implementation</title>
<programlisting>package com.example.control;
public class MyCoolRequestControl extends AbstractRequestControlDirContextProcessor {
private static final boolean CRITICAL_CONTROL = true;
private MyCoolCookie cookie;
...
public MyCoolCookie getCookie() {
return cookie;
}
public Control createRequestControl() {
return new SomeCoolControl(cookie.getCookie(), CRITICAL_CONTROL);
}
public void postProcess(DirContext ctx) throws NamingException {
LdapContext ldapContext = (LdapContext) ctx;
Control[] responseControls = ldapContext.getResponseControls();
for (int i = 0; i &lt; responseControls.length; i++) {
if (responseControls[i] instanceof SomeCoolResponseControl) {
SomeCoolResponseControl control = (SomeCoolResponseControl) responseControls[i];
this.cookie = new MyCoolCookie(control.getCookie());
}
}
}
}</programlisting>
</example>
<note>
<para>Make sure you use <literal>LdapContextSource</literal> when you
use Controls. The <literal><ulink
url="http://download.oracle.com/javase/1.5.0/docs/api/javax/naming/ldap/Control.html">Control</ulink></literal>
interface is specific for LDAPv3 and requires that
<literal>LdapContext</literal> is used instead of
<literal>DirContext</literal>. If an
<literal>AbstractRequestControlDirContextProcessor</literal> subclass is
called with an argument that is not an <literal>LdapContext</literal>,
it will throw an <literal>IllegalArgumentException</literal>.</para>
</note>
</sect1>
<sect1>
<title>Paged Search Results</title>
<para>Some searches may return large numbers of results. When there is no
easy way to filter out a smaller amount, it would be convenient to have
the server return only a certain number of results each time it is called.
This is known as <emphasis>paged search results</emphasis>. Each "page" of
the result could then be displayed at the time, with links to the next and
previous page. Without this functionality, the client must either manually
limit the search result into pages, or retrieve the whole result and then
chop it into pages of suitable size. The former would be rather
complicated, and the latter would be consuming unnecessary amounts of
memory.</para>
<para>Some LDAP servers have support for the
<literal>PagedResultsControl</literal>, which requests that the results of
a search operation are returned by the LDAP server in pages of a specified
size. The user controls the rate at which the pages are returned, simply
by the rate at which the searches are called. However, the user must keep
track of a <emphasis>cookie</emphasis> between the calls. The server uses
this cookie to keep track of where it left off the previous time it was
called with a paged results request.</para>
<para>Spring LDAP provides support for paged results by leveraging the
concept for pre- and postprocessing of an <literal>LdapContext</literal> that was discussed
in the previous sections. It does so using the class
<literal>PagedResultsDirContextProcessor</literal>. The
<literal>PagedResultsDirContextProcessor</literal> class creates a
<literal>PagedResultsControl</literal> with the requested page size and
adds it to the <literal>LdapContext</literal>. After the search, it gets
the <literal>PagedResultsResponseControl</literal> and retrieves the paged results
cookie, which is needed to keep the context between consecutive paged results requests.</para>
<para>Below is an example of how the paged search results functionality may
be used:</para>
<example>
<title>Paged results using <literal>PagedResultsDirContextProcessor</literal></title>
<programlisting>
public List&lt;String&gt; getAllPersonNames() {
final SearchControls searchControls = new SearchControls();
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
final PagedResultsDirContextProcessor processor = new PagedResultsDirContextProcessor(PAGE_SIZE);
return SingleContextSource.doWithSingleContext(contextSource, new LdapOperationsCallback&lt;List&lt;String&gt;&gt;() {
@Override
public List&lt;String&gt; doWithLdapOperations(LdapOperations operations) {
List&lt;String&gt; result = new LinkedList&lt;String&gt;();
do {
List&lt;String&gt; oneResult = operations.search(
"ou=People",
"(&amp;(objectclass=person))",
searchControls,
CN_ATTRIBUTES_MAPPER,
processor);
result.addAll(oneResult);
} while(processor.hasMore());
return result;
}
});
}
</programlisting>
</example>
<note>In order for a paged results cookie to continue being valid, it is imperative that the same underlying
connection is used for each paged results call. This can be accomplished using the <literal>SingleContextSource</literal>,
as demonstrated in the example.</note>
</sect1>
</chapter>

View File

@@ -1,348 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="dirobjectfactory">
<title>Simpler Attribute Access and Manipulation with DirContextAdapter</title>
<sect1 id="dirobjectfactory-intro">
<title>Introduction</title>
<para>A little-known--and probably underestimated--feature of the Java
LDAP API is the ability to register a <literal>DirObjectFactory</literal>
to automatically create objects from found contexts. One of the reasons
why it is seldom used is that you will need an implementation of
<literal>DirObjectFactory</literal> that creates instances of a meaningful
implementation of <literal>DirContext</literal>. The Spring LDAP library
provides the missing pieces: a default implementation of
<literal>DirContext</literal> called <literal>DirContextAdapter</literal>,
and a corresponding implementation of <literal>DirObjectFactory</literal>
called <literal>DefaultDirObjectFactory</literal>. Used together with
<literal>DefaultDirObjectFactory</literal>, the
<literal>DirContextAdapter</literal> can be a very powerful tool.</para>
</sect1>
<sect1>
<title>Search and Lookup Using ContextMapper</title>
<para>The <literal>DefaultDirObjectFactory</literal> is registered with
the <literal>ContextSource</literal> by default, which means that whenever
a context is found in the LDAP tree, its <literal>Attributes</literal> and
Distinguished Name (DN) will be used to construct a
<literal>DirContextAdapter</literal>. This enables us to use a
<literal>ContextMapper</literal> instead of an
<literal>AttributesMapper</literal> to transform found values:</para>
<example>
<title>Searching using a ContextMapper</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
...
<emphasis role="bold">private static class PersonContextMapper implements ContextMapper {
public Object mapFromContext(Object ctx) {
DirContextAdapter context = (DirContextAdapter)ctx;
Person p = new Person();
p.setFullName(context.getStringAttribute("cn"));
p.setLastName(context.getStringAttribute("sn"));
p.setDescription(context.getStringAttribute("description"));
return p;
}
}</emphasis>
public Person findByPrimaryKey(
String name, String company, String country) {
Name dn = buildDn(name, company, country);
return ldapTemplate.lookup(dn, <emphasis role="bold">new PersonContextMapper()</emphasis>);
}
}</programlisting>
</example>
<para>The above code shows that it is possible to retrieve the attributes
directly by name, without having to go through the
<literal>Attributes</literal> and <literal>BasicAttribute</literal>
classes. This is particularly useful when working with multi-value attributes. Extracting values from
multi-value attributes normally requires looping through a <literal>NamingEnumeration</literal> of
attribute values returned from the <literal>Attributes</literal> implementation. The
<literal>DirContextAdapter</literal> can do this for you, using the <literal>getStringAttributes()</literal>
or <literal>getObjectAttributes()</literal> methods:</para>
<example>
<title>Getting multi-value attribute values using <literal>getStringAttributes()</literal></title>
<programlisting>private static class PersonContextMapper implements ContextMapper {
public Object mapFromContext(Object ctx) {
DirContextAdapter context = (DirContextAdapter)ctx;
Person p = new Person();
p.setFullName(context.getStringAttribute("cn"));
p.setLastName(context.getStringAttribute("sn"));
p.setDescription(context.getStringAttribute("description"));
// The roleNames property of Person is an String array
<emphasis role="bold">p.setRoleNames(context.getStringAttributes("roleNames"));</emphasis>
return p;
}
}
</programlisting>
</example>
<sect2>
<title>The AbstractContextMapper</title>
<para>Spring LDAP provides an abstract base implementation of <literal>ContextMapper</literal>,
<literal>AbstractContextMapper</literal>. This automatically takes care of the casting of the supplied
<literal>Object</literal> parameter to <literal>DirContexOperations</literal>.
The <literal>PersonContextMapper</literal> above can thus be re-written as follows:
</para>
<example>
<title>Using an AbstractContextMapper</title>
<programlisting>
private static class PersonContextMapper <emphasis role="bold">extends AbstractContextMapper</emphasis> {
public Object <emphasis role="bold">doMapFromContext</emphasis>(DirContextOperations ctx) {
Person p = new Person();
p.setFullName(context.getStringAttribute("cn"));
p.setLastName(context.getStringAttribute("sn"));
p.setDescription(context.getStringAttribute("description"));
return p;
}
}
</programlisting>
</example>
</sect2>
</sect1>
<sect1>
<title>Binding and Modifying Using DirContextAdapter</title>
<para>While very useful when extracting attribute values, <literal>DirContextAdapter</literal> is even more
powerful for hiding attribute details when binding and modifying data.</para>
<sect2>
<title>Binding</title>
<para>This is an example of an improved implementation of the create DAO
method. Compare it with the previous implementation in <xref
linkend="basic-binding-data" />.</para>
<example id="example-binding-contextmapper">
<title>Binding using <literal>DirContextAdapter</literal></title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
...
public void create(Person p) {
Name dn = buildDn(p);
DirContextAdapter context = new DirContextAdapter(dn);
<emphasis role="bold">context.setAttributeValues("objectclass", new String[] {"top", "person"});
context.setAttributeValue("cn", p.getFullname());
context.setAttributeValue("sn", p.getLastname());
context.setAttributeValue("description", p.getDescription());</emphasis>
ldapTemplate.bind(context);
}
}</programlisting>
</example>
<para>Note that we use the <literal>DirContextAdapter</literal> instance
as the second parameter to bind, which should be a <literal>Context</literal>.
The third parameter is <literal>null</literal>, since we're not using any
<literal>Attributes</literal>.</para>
<para>Also note the use of the <literal>setAttributeValues()</literal> method when setting the
<literal>objectclass</literal> attribute values. The <literal>objectclass</literal> attribute is
multi-value, and similar to the troubles of extracting muti-value attribute data, building multi-value
attributes is tedious and verbose work. Using the <literal>setAttributeValues()</literal> mehtod you can
have <literal>DirContextAdapter</literal> handle that work for you.</para>
</sect2>
<sect2>
<title>Modifying</title>
<para>The code for a <literal>rebind</literal> would be pretty much
identical to <xref linkend="example-binding-contextmapper" />, except
that the method called would be <literal>rebind</literal>. As we saw in
<xref linkend="modify-modifyAttributes"/> a more correct approach would be to
build a <literal>ModificationItem</literal> array containing the actual
modifications you want to do. This would require you to determine the actual
modifications compared to the data present in the LDAP tree. Again, this
is something that <literal>DirContextAdapter</literal> can help you with; the
<literal>DirContextAdapter</literal> has the ability to keep track of
its modified attributes. The following example takes advantage of this
feature:</para>
<example>
<title>Modifying using <literal>DirContextAdapter</literal></title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
...
public void update(Person p) {
Name dn = buildDn(p);
<emphasis role="bold">DirContextOperations context = ldapTemplate.lookupContext(dn);</emphasis>
context.setAttributeValues("objectclass", new String[] {"top", "person"});
context.setAttributeValue("cn", p.getFullname());
context.setAttributeValue("sn", p.getLastname());
context.setAttributeValue("description", p.getDescription());
<emphasis role="bold">ldapTemplate.modifyAttributes(context);</emphasis>
}
}</programlisting>
</example>
<para>When no mapper is passed to a <literal>ldapTemplate.lookup()</literal> operation,
the result will be a <literal>DirContextAdapter</literal> instance.
While the <literal>lookup</literal> method returns an <literal>Object</literal>, the convenience
method <literal>lookupContext</literal> method automatically casts the return value to
a <literal>DirContextOperations</literal> (the interface that <literal>DirContextAdapter</literal> implements.</para>
<para>The observant reader will see that we have duplicated code in the
<literal>create</literal> and <literal>update</literal> methods. This
code maps from a domain object to a context. It can be extracted to a
separate method:</para>
<example>
<title>Binding and modifying using DirContextAdapter</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
...
public void create(Person p) {
Name dn = buildDn(p);
DirContextAdapter context = new DirContextAdapter(dn);
mapToContext(p, context);
ldapTemplate.bind(context);
}
public void update(Person p) {
Name dn = buildDn(p);
DirContextOperations context = ldapTemplate.lookupContext(dn);
mapToContext(person, context);
ldapTemplate.modifyAttributes(context);
}
protected void mapToContext (Person p, DirContextOperations context) {
context.setAttributeValues("objectclass", new String[] {"top", "person"});
context.setAttributeValue("cn", p.getFullName());
context.setAttributeValue("sn", p.getLastName());
context.setAttributeValue("description", p.getDescription());
}
}</programlisting>
</example>
</sect2>
</sect1>
<sect1>
<title>A Complete PersonDao Class</title>
<para>To illustrate the power of Spring LDAP, here is a complete Person
DAO implementation for LDAP in just 68 lines:</para>
<example>
<title>A complete PersonDao class</title>
<programlisting>package com.example.dao;
import java.util.List;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.ldap.LdapName;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.filter.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.WhitespaceWildcardsFilter;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
public void setLdapTemplate(LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
public void create(Person person) {
DirContextAdapter context = new DirContextAdapter(buildDn(person));
mapToContext(person, context);
ldapTemplate.bind(context);
}
public void update(Person person) {
Name dn = buildDn(person);
DirContextOperations context = ldapTemplate.lookupContext(dn);
mapToContext(person, context);
ldapTemplate.modifyAttributes(context);
}
public void delete(Person person) {
ldapTemplate.unbind(buildDn(person));
}
public Person findByPrimaryKey(String name, String company, String country) {
Name dn = buildDn(name, company, country);
return (Person) ldapTemplate.lookup(dn, getContextMapper());
}
public List findByName(String name) {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person")).and(new WhitespaceWildcardsFilter("cn",name));
return ldapTemplate.search(LdapUtils.emptyPath(), filter.encode(), getContextMapper());
}
public List findAll() {
EqualsFilter filter = new EqualsFilter("objectclass", "person");
return ldapTemplate.search(LdapUtils.emptyPath(), filter.encode(), getContextMapper());
}
protected ContextMapper getContextMapper() {
return new PersonContextMapper();
}
protected Name buildDn(Person person) {
return buildDn(person.getFullname(), person.getCompany(), person.getCountry());
}
protected Name buildDn(String fullname, String company, String country) {
return LdapNameBuilder.newLdapName()
.add("c", country)
.add("ou", company)
.add("cn", fullname)
.build();
}
protected void mapToContext(Person person, DirContextOperations context) {
context.setAttributeValues("objectclass", new String[] {"top", "person"});
context.setAttributeValue("cn", person.getFullName());
context.setAttributeValue("sn", person.getLastName());
context.setAttributeValue("description", person.getDescription());
}
private static class PersonContextMapper extends AbstractContextMapper {
public Object doMapFromContext(DirContextOperations context) {
Person person = new Person();
person.setFullName(context.getStringAttribute("cn"));
person.setLastName(context.getStringAttribute("sn"));
person.setDescription(context.getStringAttribute("description"));
return person;
}
}
}</programlisting>
</example>
<note>
<para>In several cases the Distinguished Name (DN) of an object is
constructed using properties of the object. E.g. in the above example,
the country, company and full name of the <literal>Person</literal> are
used in the DN, which means that updating any of these properties will
actually require moving the entry in the LDAP tree using the
<literal>rename()</literal> operation in addition to updating the
<literal>Attribute</literal> values. Since this is highly implementation
specific this is something you'll need to keep track of yourself -
either by disallowing the user to change these properties or performing
the <literal>rename()</literal> operation in your
<literal>update()</literal> method if needed.</para>
</note>
</sect1>
</chapter>

View File

@@ -1,148 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="executors">
<title>Adding Missing Overloaded API Methods</title>
<sect1 id="executors-search">
<title>Implementing Custom Search Methods</title>
<para>While <literal>LdapTemplate</literal> contains several overloaded
versions of the most common operations in <literal>DirContext</literal>,
we have not provided an alternative for each and every method signature,
mostly because there are so many of them. We have, however, provided a
means to call whichever <literal>DirContext</literal> method you want
and still get the benefits that LdapTemplate provides.</para>
<para>Let's say that you want to call the following <literal>DirContext</literal>
method:</para>
<programlisting>NamingEnumeration search(Name name, String filterExpr, Object[] filterArgs, SearchControls ctls)</programlisting>
<para>There is no corresponding overloaded method in LdapTemplate. The way to solve
this is to use a custom <literal>SearchExecutor</literal> implementation:</para>
<informalexample>
<programlisting>public interface SearchExecutor {
public NamingEnumeration executeSearch(DirContext ctx) throws NamingException;
}</programlisting>
</informalexample>
<para>In your custom executor, you have access to a <literal>DirContext</literal>
object, which you use to call the method you want. You then provide a handler
that is responsible for mapping attributes and collecting the results. You can
for example use one of the available implementations of
<literal>CollectingNameClassPairCallbackHandler</literal>, which will collect
the mapped results in an internal list. In order to
actually execute the search, you call the <literal>search</literal>
method in LdapTemplate that takes an executor and a handler as arguments. Finally,
you return whatever your handler has collected.</para>
<example>
<title>A custom search method using SearchExecutor and
AttributesMapper</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
...
public List search(final Name base, final String filter, final String[] params,
final SearchControls ctls) {
<emphasis role="bold">SearchExecutor executor = new SearchExecutor() {
public NamingEnumeration executeSearch(DirContext ctx) {
return ctx.search(base, filter, params, ctls);
}
}</emphasis>;
CollectingNameClassPairCallbackHandler handler =
new AttributesMapperCallbackHandler(new PersonAttributesMapper());
ldapTemplate.search(<emphasis role="bold">executor</emphasis>, handler);
return handler.getList();
}
}</programlisting>
</example>
<para>If you prefer the <literal>ContextMapper</literal> to the
<literal>AttributesMapper</literal>, this is what it would look
like:</para>
<example>
<title>A custom search method using SearchExecutor and
ContextMapper</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
...
public List search(final Name base, final String filter, final String[] params,
final SearchControls ctls) {
SearchExecutor executor = new SearchExecutor() {
public NamingEnumeration executeSearch(DirContext ctx) {
return ctx.search(base, filter, params, ctls);
}
};
CollectingNameClassPairCallbackHandler handler =
<emphasis role="bold">new ContextMapperCallbackHandler(new PersonContextMapper())</emphasis>;
ldapTemplate.search(executor, handler);
return handler.getList();
}
}</programlisting>
</example>
<note>
<para>When using the
<literal>ContextMapperCallbackHandler</literal> you must
make sure that you have called
<literal>setReturningObjFlag(true)</literal> on your
<literal>SearchControls</literal> instance.</para>
</note>
</sect1>
<sect1 id="executors-others">
<title>Implementing Other Custom Context Methods</title>
<para>In the same manner as for custom <literal>search</literal> methods,
you can actually execute any method in <literal>DirContext</literal> by
using a <literal>ContextExecutor</literal>.</para>
<informalexample>
<programlisting>public interface ContextExecutor {
public Object executeWithContext(DirContext ctx) throws NamingException;
}</programlisting>
<para>When implementing a custom <literal>ContextExecutor</literal>, you
can choose between using the <literal>executeReadOnly()</literal> or the
<literal>executeReadWrite()</literal> method. Let's say that we want to
call this method:</para>
</informalexample>
<programlisting>Object lookupLink(Name name)</programlisting>
<para>It's available in <literal>DirContext</literal>, but there is no
matching method in <literal>LdapTemplate</literal>. It's a lookup method,
so it should be read-only. We can implement it like this:</para>
<example>
<title>A custom DirContext method using ContextExecutor</title>
<programlisting>package com.example.dao;
public class PersonDaoImpl implements PersonDao {
...
public Object lookupLink(final Name name) {
ContextExecutor executor = new ContextExecutor() {
public Object executeWithContext(DirContext ctx) {
return ctx.lookupLink(name);
}
};
return ldapTemplate.executeReadOnly(executor);
}
}</programlisting>
<para>In the same manner you can execute a read-write operation using
the <literal>executeReadWrite()</literal> method.</para>
</example>
</sect1>
</chapter>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<bookinfo>
<title>Spring LDAP - Reference Documentation</title>
<releaseinfo>&version;</releaseinfo>
<authorgroup>
<author>
<firstname>Mattias</firstname>
<surname>Hellborg Arthursson</surname>
</author>
<author>
<firstname>Ulrik</firstname>
<surname>Sandberg</surname>
</author>
<author>
<firstname>Eric</firstname>
<surname>Dalquist</surname>
</author>
<author>
<firstname>Keith</firstname>
<surname>Barlow</surname>
</author>
</authorgroup>
<legalnotice>
<para>
Copies of this document may be made for your own use and
for distribution to others, provided that you do not
charge any fee for such copies and further provided that
each copy contains this Copyright Notice, whether
distributed in print or electronically.
</para>
</legalnotice>
</bookinfo>
<toc />
<xi:include href="preface.xml" />
<xi:include href="overview.xml" />
<xi:include href="basic.xml" />
<xi:include href="dirobjectfactory.xml" />
<xi:include href="odm.xml" />
<xi:include href="advancedqueries.xml" />
<xi:include href="configuration.xml" />
<xi:include href="repositories.xml" />
<xi:include href="pooling.xml" />
<xi:include href="executors.xml" />
<xi:include href="contextprocessor.xml" />
<xi:include href="transactions.xml" />
<xi:include href="user-authentication.xml" />
<xi:include href="ldif-parsing.xml" />
<xi:include href="utilities.xml" />
<xi:include href="simple.xml" />
</book>

View File

@@ -1,168 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="ldif-parsing">
<title>LDIF Parsing</title>
<section id="ldif-parsing-intro">
<title>Introduction</title>
<para>LDAP Directory Interchange Format (LDIF) files are the standard
medium for describing directory data in a flat file format. The most common
uses of this format include information transfer and archival. However,
the standard also defines a way to describe modifications to stored data
in a flat file format. LDIFs of this later type are typically referred to
as <emphasis>changetype</emphasis> or <emphasis>modify</emphasis> LDIFs.
</para>
<para>The <token>org.springframework.ldap.ldif</token> package provides
classes needed to parse LDIF files and deserialize them into tangible
objects. The <token>LdifParser</token> is the main class of the
<token>org.springframework.ldap.ldif</token> package and is capable of
parsing files that are RFC 2849 compliant. This class reads lines from a
resource and assembles them into an <token>LdapAttributes</token> object.
The <token>LdifParser</token> currently ignores
<emphasis>changetype</emphasis> LDIF entries as their usefulness in the
context of an application has yet to be determined.</para>
</section>
<section id="ldif-parsing-obj-repr">
<title>Object Representation</title>
<para>Two classes in the <token>org.springframework.ldap.core</token>
package provide the means to represent an LDIF in code:</para>
<itemizedlist>
<listitem>
<para><token>LdapAttribute</token> - Extends
<token>javax.naming.directory.BasicAttribute</token> adding support
for LDIF options as defined in RFC2849.</para>
</listitem>
<listitem>
<para><token>LdapAttributes</token> - Extends
<token>javax.naming.directory.BasicAttributes</token> adding
specialized support for DNs.</para>
</listitem>
</itemizedlist>
<para><token>LdapAttribute</token> objects represent options as a
<token>Set&lt;String&gt;</token>. The DN support added to the
<token>LdapAttributes</token> object employs the
<token>javax.naming.ldap.LdapName</token> class. </para>
</section>
<section id="ldif-parsing-parser">
<title>The Parser</title>
<para>The <token>Parser</token> interface provides the foundation for
operation and employs three supporting policy definitions:</para>
<itemizedlist>
<listitem>
<para><token>SeparatorPolicy</token> - establishes the mechanism by
which lines are assembled into attributes. </para>
</listitem>
<listitem>
<para><token>AttributeValidationPolicy</token> - ensures that
attributes are correctly structured prior to parsing.</para>
</listitem>
<listitem>
<para><token>Specification</token> - provides a mechanism by which
object structure can be validated after assembly. </para>
</listitem>
</itemizedlist>
<simpara>The default implementations of these interfaces are the
<token>org.springframework.ldap.ldif.parser.LdifParser</token>, the
<token>org.springframework.ldap.ldif.support.SeparatorPolicy</token>, and
the
<token>org.springframework.ldap.ldif.support.DefaultAttributeValidationPolicy</token>,
and the
<token>org.springframework.ldap.schema.DefaultSchemaSpecification</token>
respectively. Together, these 4 classes parse a resource line by line and
translate the data into <token>LdapAttributes</token> objects. </simpara>
<simpara>The <token>SeparatorPolicy</token> determines how individual
lines read from the source file should be interpreted as the LDIF
specification allows attributes to span multiple lines. The default policy
assess lines in the context of the order in which they were read to
determine the nature of the line in consideration.
<emphasis>control</emphasis> attributes and
<emphasis>changetype</emphasis> records are ignored.</simpara>
<simpara>The <token>DefaultAttributeValidationPolicy</token> uses REGEX
expressions to ensure each attribute conforms to a valid attribute format
according to RFC 2849 once parsed. If an attribute fails validation, an
<token>InvalidAttributeFormatException</token> is logged and the record is
skipped (the parser returns null).</simpara>
</section>
<section id="ldif-parsing-schema">
<title>Schema Validation</title>
<para>A mechanism for validating parsed objects against a schema and is
available via the <token>Specification</token> interface in the
<token>org.springframework.ldap.schema</token> package. The
<token>DefaultSchemaSpecification</token> does not do any validation and
is available for instances where records are known to be valid and not
required to be checked. This option saves the performance penalty that
validation imposes. The <token>BasicSchemaSpecification</token> applies
basic checks such as ensuring DN and object class declarations have been
provided. Currently, validation against an actual schema requires
implementation of the <token>Specification</token> interface. </para>
</section>
<section id="ldif-parsing-batch">
<title>Spring Batch Integration</title>
<para>While the <token>LdifParser</token> can be employed by any
application that requires parsing of LDIF files, Spring offers a batch
processing framework that offers many file processing utilities for
parsing delimited files such as CSV. The
<token>org.springframework.ldap.ldif.batch</token> package offers the
classes necessary for using the <token>LdifParser</token> as a valid
configuration option in the Spring Batch framework.</para>
<para>There are 5 classes in this package which offer three basic use
cases:</para>
<itemizedlist>
<listitem>
<para>Use Case 1: Read LDIF records from a file and return an
<token>LdapAttributes</token> object.</para>
</listitem>
<listitem>
<para>Use Case 2: Read LDIF records from a file and map records to
Java objects (POJOs).</para>
</listitem>
<listitem>
<para>Use Case 3: Write LDIF records to a file.</para>
</listitem>
</itemizedlist>
<para>The first use case is accomplished with the LdifReader. This class
extends Spring Batch's
<token>AbstractItemCountingItemSteamItemReader</token> and implements its
<token>ResourceAwareItemReaderItemStream</token>. It fits naturally into
the framework and can be used to read <token>LdapAttributes</token>
objects from a file.</para>
<para>The <token>MappingLdifReader</token> can be used to map LDIF objects
directly to any POJO. This class requires an implementation of the
<token>RecordMapper</token> interface be provided. This implementation
should implement the logic for mapping objects to POJOs.</para>
<para>The <token>RecordCallbackHandler</token> can be implemented and
provided to either reader. This handler can be used to operate on skipped
records. Consult the Spring Batch documentation for more
information.</para>
<para>The last member of this package, the <token>LdifAggregator</token>,
can be used to write LDIF records to a file. This class simply invokes the
<token>toString()</token> method of the <token>LdapAttributes</token>
object.</para>
</section>
</chapter>

View File

@@ -1,300 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="odm">
<title>Object-Directory Mapping (ODM)</title>
<sect1 id="odm-intro">
<title>Introduction</title>
<para>
Relational mapping frameworks like Hibernate and JPA have offered
developers the ability to use annotations to map database tables to Java
objects for some time. Spring LDAP project offers a similar
ability with respect to directories through the use of a number of methods:
in <literal>LdapOperations</literal>
<itemizedlist>
<listitem><literal>&lt;T&gt; T findByDn(Name dn, Class&lt;T&gt; clazz)</literal></listitem>
<listitem><literal>&lt;T&gt; T findOne(LdapQuery query, Class&lt;T&gt; clazz)</literal></listitem>
<listitem><literal>&lt;T&gt; List&lt;T&gt; find(LdapQuery query, Class&lt;T&gt; clazz)</literal></listitem>
<listitem><literal>&lt;T&gt; List&lt;T&gt; findAll(Class&lt;T&gt; clazz)</literal></listitem>
<listitem><literal>&lt;T&gt; List&lt;T&gt; findAll(Name base, SearchControls searchControls,
Class&lt;T&gt; clazz)</literal></listitem>
<listitem><literal>&lt;T&gt; List&lt;T&gt; findAll(Name base, Filter filter, SearchControls searchControls,
Class&lt;T&gt; clazz)</literal></listitem>
<listitem><literal>void create(Object entry)</literal></listitem>
<listitem><literal>void update(Object entry)</literal></listitem>
<listitem><literal>void delete(Object entry)</literal></listitem>
</itemizedlist>
</para>
</sect1>
<sect1 id="odm-annotations">
<title>Annotations</title>
<para>Entity classes managed used with the object mapping methods are required
to be annotated with annotations from the
<literal>org.springframework.ldap.odm.annotations</literal> package. The
available annotations are:</para>
<itemizedlist>
<listitem>
<para><literal>@Entry</literal> - Class level annotation indicating the
<literal>objectClass</literal> definitions to which the entity
maps.<emphasis> (required)</emphasis></para>
</listitem>
<listitem>
<para><literal>@Id</literal> - Indicates the entity DN; the field declaring
this attribute must be a derivative of the
<literal>javax.naming.Name</literal> class.
<emphasis>(required)</emphasis></para>
</listitem>
<listitem>
<para><literal>@Attribute</literal> - Indicates the mapping of a directory
attribute to the object class field.</para>
</listitem>
<listitem>
<para><literal>@DnAttribute</literal> - Indicates the mapping of a dn
attribute to the object class field.</para>
</listitem>
<listitem>
<para><literal>@Transient</literal> - Indicates the field is not persistent
and should be ignored by the <literal>OdmManager</literal>.</para>
</listitem>
</itemizedlist>
<simpara>
The <literal>@Entry</literal> and <literal>@Id</literal> attributes are
required to be declared on managed classes.
<literal>@Entry</literal> is used to specify which object classes the entity maps to.
All object classes for which fields are mapped are required to be declared. Also, in order for a
directory entry to be considered a match to the managed entity, all object
classes declared by the directory entry must match be declared by in the
<literal>@Entry</literal> annotation. For example: let's assume that you have entries in
your LDAP tree that have the objectclasses<literal>inetOrgPerson,organizationalPerson,person,top</literal>.
If you are only interested in changing the attributes defined in the <literal>person</literal>
objectclass, your <literal>@Entry</literal> annotation can be
<literal>@Entry(objectClasses = { "person", "top" })</literal>. However, if you want to manage
attributes defined in the <literal>inetOrgPerson</literal> objectclass you'll need to use the full
monty: <literal>@Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" })</literal>.
</simpara>
<simpara>The <literal>@Id</literal> annotation is used to map the distinguished
name of the entry to a field. The field must be an instance of
<literal>javax.naming.Name</literal>.</simpara>
<simpara>The <literal>@Attribute</literal> annotation is used to map object
class fields to entity fields. <literal>@Attribute</literal> is required to
declare the name of the object class property to which the field maps and
may optionally declare the syntax OID of the LDAP attribute, to guarantee
exact matching. <literal>@Attribute</literal> also provides the type declaration
which allows you to indicate whether the attribute is regarded as binary
based or string based by the LDAP JNDI provider.</simpara>
<simpara>
The <literal>@DnAttribute</literal> annotation is used to map object class fields
to and from components in the distinguished name of an entry. Fields annotated with
<literal>@DnAttribute</literal>
will automatically be populated with the appropriate value from the distinguished name
when an entry is read from the directory tree. If the <literal>index</literal> attribute
of all <literal>@DnAttribute</literal> annotations in a class is specified, the DN
will also be calculated when creating and updating entries. For update scenarios,
this will also automatically take care of moving entries in the tree if attributes
that are part of the distinguished name have changed.
</simpara>
<simpara>The <literal>@Transient</literal> annotation is used to indicate the
field should be ignored by the object directory mapping and not mapped to
an underlying LDAP property. Note that if a <literal>@DnAttribute</literal> is not to be bound
to an Attribute, i.e. it is only part of the Distinguished Name and not represented by an object attibute,
it must also be annotated with <literal>@Transient</literal>.</simpara>
</sect1>
<sect1 id="odm-typeconversion">
<title>Type Conversion</title>
<para>The object directory mapping relies on the
<literal>org.springframework.ldap.odm.typeconversion</literal> package to
convert LDAP attributes to Java fields. For simple setups, no particular configuraion
is needed for this purpose. However, more complex mapping scenarios require the
<literal>ObjectDirectoryMapper</literal> and its associated <literal>ConverterManager</literal>
to be explicitly configured on the <literal>LdapTemplate</literal> instance.
The default <literal>ConverterManager</literal> implementation uses the
following algorithm when parsing objects to convert fields:<orderedlist>
<listitem>
<para>Try to find and use a <literal>Converter</literal> registered for
the <literal>fromClass</literal>, <literal>syntax</literal> and
<literal>toClass</literal> and use it.</para>
</listitem>
<listitem>
<para>If this fails, then if the <literal>toClass</literal>
<literal>isAssignableFrom</literal> the
<literal>fromClass</literal> then just assign it.</para>
</listitem>
<listitem>
<para>If this fails try to find and use a
<literal>Converter</literal> registered for the
<literal>fromClass</literal> and the <literal>toClass</literal> ignoring the
syntax.</para>
</listitem>
<listitem>
<para>If this fails then throw a
<exceptionname>ConverterException</exceptionname>.</para>
</listitem>
</orderedlist></para>
<para>Implementations of the <code>ConverterManager</code> interface can
be obtained from the
<code>o.s.l.odm.typeconversion.impl.ConvertManagerFactoryBean</code>.
The factory bean requires converter configurations to be declared in the
bean configuration.</para>
<para>The converterConfig property accepts a set of
<code>ConverterConfig</code> classes, each one defining some conversion
logic. A converter config is an instance of
<code>o.s.l.odm.typeconversion.impl.ConverterManagerFactoryBean.ConverterConfig</code>.
The config defines a set of source classes, the set of target classes, and
an implementation of the
<code>org.springframework.ldap.odm.typeconversion.impl.Converter</code>
interface which provides the logic to convert from the
<code>fromClass</code> to the <code>toClass</code>. A sample configuration
is provided in the following example:</para>
<example>
<title>Configuring the Converter Manager Factory</title>
<programlisting>
&lt;bean id="fromStringConverter"
class="org.springframework.ldap.odm.typeconversion.impl.converters.FromStringConverter" /&gt;
&lt;bean id="toStringConverter"
class="org.springframework.ldap.odm.typeconversion.impl.converters.ToStringConverter" /&gt;
&lt;bean id="converterManager"
class="org.springframework.ldap.odm.typeconversion.impl.ConverterManagerFactoryBean"&gt;
&lt;property name="converterConfig"&gt;
&lt;set&gt;
&lt;bean class="org.springframework.ldap.odm.\
typeconversion.impl.ConverterManagerFactoryBean$ConverterConfig"&gt;
&lt;property name="fromClasses"&gt;
&lt;set&gt;
&lt;value&gt;java.lang.String&lt;/value&gt;
&lt;/set&gt;
&lt;/property&gt;
&lt;property name="toClasses"&gt;
&lt;set&gt;
&lt;value&gt;java.lang.Byte&lt;/value&gt;
&lt;value&gt;java.lang.Short&lt;/value&gt;
&lt;value&gt;java.lang.Integer&lt;/value&gt;
&lt;value&gt;java.lang.Long&lt;/value&gt;
&lt;value&gt;java.lang.Float&lt;/value&gt;
&lt;value&gt;java.lang.Double&lt;/value&gt;
&lt;value&gt;java.lang.Boolean&lt;/value&gt;
&lt;/set&gt;
&lt;/property&gt;
&lt;property name="converter" ref="fromStringConverter" /&gt;
&lt;/bean&gt;
&lt;bean class="org.springframework.ldap.odm.\
typeconversion.impl.ConverterManagerFactoryBean$ConverterConfig"&gt;
&lt;property name="fromClasses"&gt;
&lt;set&gt;
&lt;value&gt;java.lang.Byte&lt;/value&gt;
&lt;value&gt;java.lang.Short&lt;/value&gt;
&lt;value&gt;java.lang.Integer&lt;/value&gt;
&lt;value&gt;java.lang.Long&lt;/value&gt;
&lt;value&gt;java.lang.Float&lt;/value&gt;
&lt;value&gt;java.lang.Double&lt;/value&gt;
&lt;value&gt;java.lang.Boolean&lt;/value&gt;
&lt;/set&gt;
&lt;/property&gt;
&lt;property name="toClasses"&gt;
&lt;set&gt;
&lt;value&gt;java.lang.String&lt;/value&gt;
&lt;/set&gt;
&lt;/property&gt;
&lt;property name="converter" ref="toStringConverter" /&gt;
&lt;/bean&gt;
&lt;/set&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;ldap:ldap-template id="ldapTemplate" odm-ref="odm" /&gt;
&lt;bean id="odm" class="org.springframework.ldap.odm.impl.DefaultObjectDirectoryMapper"&gt;
&lt;property name="converterManager" ref="converterManager" /&gt;
&lt;/bean&gt;
</programlisting>
</example>
</sect1>
<sect1 id="odm-execution">
<title>Execution</title>
<para>
When all components have been properly configured and annotated, the object mapping
methods of <literal>LdapTemplate</literal> can be used as follows:</para>
<example>
<title>Execution</title>
<programlisting>
@Entry(objectClasses = { "person", "top" }, base="ou=someOu")
public class Person {
@Id
private Name dn;
@Attribute(name="cn")
@DnAttribute(value="cn", index=1)
private String fullName;
// No @Attribute annotation means this will be bound to the LDAP attribute
// with the same value
private String description;
@DnAttribute(value="ou", index=0)
@Transient
private String company;
@Transient
private String someUnmappedField;
// ...more attributes below
}
public class OdmPersonDao {
@Autowired
private LdapTemplate ldapTemplate;
public Person create(Person person) {
ldapTemplate.create(person);
return person;
}
public Person findByUid(String uid) {
return ldapTemplate.findOne(query().where("uid").is(uid), Person.class);
}
public void update(Person person) {
ldapTemplate.update(person);
}
public void delete(Person person) {
ldapTemplate.delete(person);
}
public List&gt;Person&lt; findAll() {
return ldapTemplate.findAll(Person.class);
}
public List&gt;Person&lt; findByLastName(String lastName) {
return ldapTemplate.find(query().where("sn").is(lastName), Person.class);
}
}
</programlisting>
</example>
</sect1>
</chapter>

View File

@@ -1,299 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="introduction">
<title>Introduction</title>
<sect1 id="introduction-overview">
<title>Overview</title>
<para>Spring LDAP (<ulink
url="http://www.springframework.org/ldap">http://www.springframework.org/ldap</ulink>)
is a library for simpler LDAP programming in Java, built on the same
principles as the <ulink
url="http://static.springframework.org/spring/docs/current/api/org/springframework/jdbc/core/JdbcTemplate.html">JdbcTemplate</ulink>
in Spring JDBC. It completely eliminates the need to worry about creating
and closing <literal>LdapContext</literal> and looping through
<literal>NamingEnumeration</literal>. It also provides a more
comprehensive unchecked Exception hierarchy, built on Spring's
<literal>DataAccessException</literal>. As a bonus, it also contains
classes for dynamically building LDAP queries and DNs (Distinguished
Names), LDAP attribute management, and client-side LDAP transaction management.</para>
<para>Consider, for example, a method that should search some storage for
all persons and return their names in a list. Using JDBC, we would create
a <emphasis>connection</emphasis> and execute a <emphasis>query</emphasis>
using a <emphasis>statement</emphasis>. We would then loop over the
<emphasis>result set</emphasis> and retrieve the
<emphasis>column</emphasis> we want, adding it to a list. In contrast,
using Java LDAP, we would create a <emphasis>context</emphasis> and
perform a <emphasis>search</emphasis> using a <emphasis>search
filter</emphasis>. We would then loop over the resulting <emphasis>naming
enumeration</emphasis> and retrieve the <emphasis>attribute</emphasis> we
want, adding it to a list.</para>
<para>The traditional way of implementing this person name search method
in Java LDAP looks like this, where the code marked as bold actually
performs tasks related to the business purpose of the method:</para>
<informalexample>
<programlisting>package com.example.dao;
public class TraditionalPersonDaoImpl implements PersonDao {
public List getAllPersonNames() {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/dc=example,dc=com");
DirContext ctx;
try {
ctx = new InitialDirContext(env);
} catch (NamingException e) {
throw new RuntimeException(e);
}
LinkedList list = new LinkedList();
NamingEnumeration results = null;
try {
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
results = ctx.<emphasis role="bold">search("", "(objectclass=person)"</emphasis>, controls);
while (results.hasMore()) {
SearchResult searchResult = (SearchResult) results.next();
Attributes attributes = searchResult.getAttributes();
<emphasis role="bold">Attribute attr = attributes.get("cn");
String cn = (String) attr.get();
list.add(cn);</emphasis>
}
} catch (NameNotFoundException e) {
// The base context was not found.
// Just clean up and exit.
} catch (NamingException e) {
throw new RuntimeException(e);
} finally {
if (results != null) {
try {
results.close();
} catch (Exception e) {
// Never mind this.
}
}
if (ctx != null) {
try {
ctx.close();
} catch (Exception e) {
// Never mind this.
}
}
}
<emphasis role="bold">return list;</emphasis>
}
}</programlisting>
</informalexample>
<para>By using the Spring LDAP classes <literal>AttributesMapper</literal>
and <literal>LdapTemplate</literal>, we get the exact same functionality
with the following code:</para>
<informalexample>
<programlisting>package com.example.dao;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
public class PersonDaoImpl implements PersonDao {
private LdapTemplate ldapTemplate;
public void setLdapTemplate(LdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
}
public List getAllPersonNames() {
return ldapTemplate.<emphasis role="bold">search(
query().where("objectclass").is("person")</emphasis>,
new AttributesMapper() {
public Object mapFromAttributes(Attributes attrs)
throws NamingException {
<emphasis role="bold">return attrs.get("cn").get();</emphasis>
}
});
}
}</programlisting>
</informalexample>
<para>The amount of boiler-plate code is significantly less than in the
traditional example. The <literal>LdapTemplate</literal> version of the
search method performs the search, maps the attributes to a string using
the given <literal>AttributesMapper</literal>, collects the strings in an
internal list, and finally returns the list.</para>
<para>Note that the <literal>PersonDaoImpl</literal> code simply assumes
that it has an <literal>LdapTemplate</literal> instance, rather than
looking one up somewhere. It provides a set method for this purpose. There
is nothing Spring-specific about this "Inversion of Control". Anyone that
can create an instance of <literal>PersonDaoImpl</literal> can also set
the <literal>LdapTemplate</literal> on it. However, Spring provides a very
flexible and easy way of <ulink
url="http://static.springframework.org/spring/docs/current/reference/beans.html">achieving
this</ulink>. The Spring container can be told to wire up an instance of
<literal>LdapTemplate</literal> with its required dependencies and inject
it into the <literal>PersonDao</literal> instance. This wiring can be
defined in various ways, but the most common is through XML:</para>
<informalexample>
<programlisting><![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ldap="http://www.springframework.org/schema/ldap"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/ldap http://www.springframework.org/schema/ldap/spring-ldap.xsd">
<ldap:context-source
url="ldap://localhost:389"
base="dc=example,dc=com"
username="cn=Manager"
password="secret" />
<ldap:ldap-template id="ldapTemplate" />
<bean id="personDao" class="com.example.dao.PersonDaoImpl">
<property name="ldapTemplate" ref="ldapTemplate" />
</bean>
</beans>
]]></programlisting>
</informalexample>
<note>
In order to use the custom XML namespace for configuring the Spring LDAP components
you need to include references to this namespace in your XML declaration as in the example above.
</note>
</sect1>
<sect1 id="introduction-packaging">
<title>Packaging overview</title>
<para>At a minimum, to use Spring LDAP you need:</para>
<itemizedlist spacing="compact">
<listitem>
<para><emphasis>spring-ldap-core</emphasis> (the Spring LDAP library)</para>
</listitem>
<listitem>
<para><emphasis>spring-core</emphasis> (miscellaneous utility classes used internally by
the framework)</para>
</listitem>
<listitem>
<para><emphasis>spring-beans</emphasis> (contains interfaces and classes for manipulating
Java beans)</para>
</listitem>
<listitem>
<para><emphasis>slf4j</emphasis> (a simple logging facade, used
internally)</para>
</listitem>
<listitem>
<para><emphasis>commons-lang</emphasis> (misc utilities, used internally)</para>
</listitem>
</itemizedlist>
<para>In addition to the required dependencies the following optional dependencies
are required for certain functionality:</para>
<itemizedlist>
<listitem>
<para><emphasis>spring-context</emphasis> (If your application is wired up using the Spring Application Context -
adds the ability for application objects to obtain resources using a consistent API. Definitely needed if you are
planning on using the BaseLdapPathBeanPostProcessor.)</para>
</listitem>
<listitem>
<para><emphasis>spring-tx</emphasis> (If you are planning to use the client side compensating transaction support)</para>
</listitem>
<listitem>
<para><emphasis>spring-jdbc</emphasis> (If you are planning to use the client side compensating transaction support)</para>
</listitem>
<listitem>
<para><emphasis>commons-pool</emphasis> (If you are planning to use the pooling functionality)</para>
</listitem>
<listitem>
<para><emphasis>spring-batch</emphasis> (If you are planning to use the LDIF parsing functionality together with Spring Batch)</para>
</listitem>
</itemizedlist>
</sect1>
<sect1 id="new-in-20">
<title>What's new in Spring LDAP 2.0?</title>
<para>
While quite significant modernizations have been made to the Spring LDAP APi in version 2.0, great care has been
taken to ensure backward compatibility as far as possible.
Code that works with Spring LDAP 1.3.x should with very few exceptions still compile and run using the 2.0 libraries
without any modifications whatsoever.
</para>
<para>
The exception is a small number of classes that have been moved to new packages in order to make
a couple of important refactorings possible. The moved classes are usually not part of the intended
public API, and the migration procedure should be very smooth - wherever a Spring LDAP class cannot be found
after upgrade, just organize the imports in your IDE.
</para>
<para>
You will probably encounter some deprecation warnings though, and there are also a lot of other API improvements.
The recommendation for getting as much as possible out of the 2.0 version is to move away from the deprecated
classes and methods and migrate to the new, improved API utilities.
</para>
<para>
Below is a list of the most important changes in Spring LDAP 2.0.
</para>
<itemizedlist>
<listitem>
Java 1.6 is now required when using Spring LDAP. Spring versions starting at 2.0 and up are still supported.
</listitem>
<listitem>
The central API has been updated with Java 5 features such as generics and varargs. As a consequence,
the entire <literal>spring-ldap-tiger</literal> module has been deprecated and users are encouraged to migrate
to use the core Spring LDAP classes. The parameterization of the core interfaces will most likely cause
lots of compilation warnings, and you are obviously encouraged to take appropriate action to get rid
of these warning.
</listitem>
<listitem>
The ODM (Object-Directory Mapping) functionality has been moved to core and there are new methods
in <literal>LdapOperations</literal>/<literal>LdapTemplate</literal> that uses this automatic
translation to/from ODM-annotated classes. See <xref linkend="odm" /> for more information.
</listitem>
<listitem>
A custom XML namespace is now provided to simplify configuration of Spring LDAP.
See <xref linkend="configuration" /> for more information.
</listitem>
<listitem>
Spring Data Repository and QueryDSL support is now included in Spring LDAP.
See <xref linkend="repositories" /> for more information.
</listitem>
<listitem>
<literal>DistinguishedName</literal> and associated classes have been deprecated in favor of standard
Java <literal>LdapName</literal>. See <xref linkend="ldap-names" /> for information on how the library
helps working with <literal>LdapNames</literal>.
</listitem>
<listitem>
Fluent LDAP query support has been added. This makes for a more pleasant programming experience when
working with LDAP searches in Spring LDAP. See <xref linkend="basic-queries" /> and
<xref linkend="query-builder-advanced" /> for more information about the LDAP query builder support.
</listitem>
<listitem>
The old <literal>authenticate</literal> methods in <literal>LdapTemplate</literal> have been deprecated
in favor of a couple of new <literal>authenticate</literal> methods that work with
<literal>LdapQuery</literal> objects and <emphasis>throw exceptions</emphasis> on authentication failure,
making it easier for the user to find out what caused an authentication attempt to fail.
</listitem>
</itemizedlist>
</sect1>
<sect1 id="introduction-support">
<title>Support</title>
<para>Spring LDAP 2.0 is supported on Spring 2.0 and later.</para>
<para>The community support forum is located at <ulink
url="http://forum.spring.io/forum/spring-projects/data/ldap">http://forum.spring.io/forum/spring-projects/data/ldap</ulink>,
and the project web page is <ulink
url="http://projects.spring.io/spring-ldap/">http://projects.spring.io/spring-ldap/</ulink>.</para>
</sect1>
</chapter>

View File

@@ -1,518 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="pooling">
<title>Pooling Support</title>
<sect1 id="pooling-intro">
<title>Introduction</title>
<para>
Pooling LDAP connections helps mitigate the overhead of
creating a new LDAP connection for each LDAP interaction.
While
<ulink
url="http://java.sun.com/products/jndi/tutorial/ldap/connect/pool.html">
Java LDAP pooling support
</ulink>
exists it is limited in its configuration options and
features, such as connection validation and pool
maintenance. Spring LDAP provides support for detailed pool
configuration on a per-
<literal>ContextSource</literal>
basis.
</para>
<para>
Pooling support is provided by supplying a <literal>&lt;ldap:pooling /&gt;</literal> sub-element
to the <literal>&lt;ldap:context-source /&gt;</literal> element in the application context configuration.
Read-only and read-write <literal>DirContext</literal> objects are pooled separately
(if <literal>anonymous-read-only</literal> is specified.
<ulink url="http://commons.apache.org/pool/index.html">
Jakarta Commons-Pool
</ulink>
is used to provide the underlying pool implementation.
</para>
</sect1>
<sect1 id="pooling-validation">
<title>DirContext Validation</title>
<para>
Validation of pooled connections is the primary motivation
for using a custom pooling library versus the JDK provided
LDAP pooling functionality. Validation allows pooled
<literal>DirContext</literal>
connections to be checked to ensure they are still properly
connected and configured when checking them out of the pool,
in to the pool or while idle in the pool.
</para>
<para>
If connection validation is configured, pooled connections are validated using
<literal>DefaultDirContextValidator</literal>.
<literal>DefaultDirContextValidator</literal>
does a
<literal>
DirContext.search(String, String, SearchControls)
</literal>
, with an empty name, a filter of
<literal>"objectclass=*"</literal>
and
<literal>SearchControls</literal>
set to limit a single result with the only the objectclass
attribute and a 500ms timeout. If the returned
<literal>NamingEnumeration</literal>
has results the
<literal>DirContext</literal>
passes validation, if no results are returned or an
exception is thrown the
<literal>DirContext</literal>
fails validation. The default settings
should work with no configuration changes on most LDAP
servers and provide the fastest way to validate the
<literal>DirContext</literal>. If customization required this can be done using the validation
configuration attributes, described below
</para>
<note>
Connections will be automatically invalidated if they throw an exception that is considered
non-transient. E.g. if a <literal>DirContext</literal> instance throws a
<literal>javax.naming.CommunicationException</literal>, this will be interpreted
as a non-transient error and the instance will be automatically invalidated, without the overhead
of an additional testOnReturn operation. The exceptions that are interpreted as non-transient are
configured using the <literal>nonTransientExceptions</literal> property of the
<literal>PoolingContextSource</literal>.
</note>
</sect1>
<sect1 id="pooling-properties">
<title>Pool Configuration</title>
<para>
The following attributes are available on the
<literal>&lt;ldap:pooling /&gt;</literal> element
for configuration of the DirContext pool:
</para>
<table frame="all">
<title>Pooling Configuration Attributes</title>
<tgroup align="left" cols="3" colsep="1" rowsep="1">
<colspec colname="c1" />
<colspec colname="c2" />
<colspec colname="c3" />
<thead>
<row>
<entry>Attribute</entry>
<entry>Default</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<literal>max-active</literal>
</entry>
<entry>
<literal>8</literal>
</entry>
<entry>
The maximum number of active connections of
each type (read-only|read-write) that can be
allocated from this pool at the same time,
or non-positive for no limit.
</entry>
</row>
<row>
<entry>
<literal>max-total</literal>
</entry>
<entry>
<literal>-1</literal>
</entry>
<entry>
The overall maximum number of active
connections (for all types) that can be
allocated from this pool at the same time,
or non-positive for no limit.
</entry>
</row>
<row>
<entry>
<literal>max-idle</literal>
</entry>
<entry>
<literal>8</literal>
</entry>
<entry>
The maximum number of active connections of
each type (read-only|read-write) that can
remain idle in the pool, without extra ones
being released, or non-positive for no
limit.
</entry>
</row>
<row>
<entry>
<literal>min-idle</literal>
</entry>
<entry>
<literal>0</literal>
</entry>
<entry>
The minimum number of active connections of
each type (read-only|read-write) that can
remain idle in the pool, without extra ones
being created, or zero to create none.
</entry>
</row>
<row>
<entry>
<literal>max-wait</literal>
</entry>
<entry>
<literal>-1</literal>
</entry>
<entry>
The maximum number of milliseconds that the
pool will wait (when there are no available
connections) for a connection to be returned
before throwing an exception, or
non-positive to wait indefinitely.
</entry>
</row>
<row>
<entry>
<literal>when-exhausted</literal>
</entry>
<entry>
<literal>BLOCK</literal>
</entry>
<entry>
Specifies the behaviour when the pool is
exhausted.
<itemizedlist>
<listitem>
<para>
The <literal>FAIL</literal> option will throw a
<literal>
NoSuchElementException
</literal>
when the pool is exhausted.
</para>
</listitem>
<listitem>
<para>
The <literal>BLOCK</literal>
option will wait until a new
object is available. If
<literal>max-wait</literal>
is positive a
<literal>
NoSuchElementException
</literal>
is thrown if no new object is
available after the
<literal>max-wait</literal>
time expires.
</para>
</listitem>
<listitem>
<para>
The <literal>GROW</literal>
option will create and return a
new object (essentially making
<literal>max-active</literal>
meaningless).
</para>
</listitem>
</itemizedlist>
</entry>
</row>
<row>
<entry>
<literal>test-on-borrow</literal>
</entry>
<entry>
<literal>false</literal>
</entry>
<entry>
The indication of whether objects will be
validated before being borrowed from the
pool. If the object fails to validate, it
will be dropped from the pool, and an
attempt to borrow another will be made.
</entry>
</row>
<row>
<entry>
<literal>test-on-return</literal>
</entry>
<entry>
<literal>false</literal>
</entry>
<entry>
The indication of whether objects will be
validated before being returned to the pool.
</entry>
</row>
<row>
<entry>
<literal>test-while-idle</literal>
</entry>
<entry>
<literal>false</literal>
</entry>
<entry>
The indication of whether objects will be
validated by the idle object evictor (if
any). If an object fails to validate, it
will be dropped from the pool.
</entry>
</row>
<row>
<entry>
<literal>
eviction-run-interval-millis
</literal>
</entry>
<entry>
<literal>-1</literal>
</entry>
<entry>
The number of milliseconds to sleep between
runs of the idle object evictor thread. When
non-positive, no idle object evictor thread
will be run.
</entry>
</row>
<row>
<entry>
<literal>tests-per-eviction-run</literal>
</entry>
<entry>
<literal>3</literal>
</entry>
<entry>
The number of objects to examine during each
run of the idle object evictor thread (if
any).
</entry>
</row>
<row>
<entry>
<literal>
min-evictable-time-millis
</literal>
</entry>
<entry>
<literal>1000 * 60 * 30</literal>
</entry>
<entry>
The minimum amount of time an object may sit
idle in the pool before it is eligible for
eviction by the idle object evictor (if
any).
</entry>
</row>
<row>
<entry>
<literal>
validation-query-base
</literal>
</entry>
<entry>
<literal>LdapUtils.emptyName()</literal>
</entry>
<entry>
The search base to be used when validating connections. Only used if
<literal>test-on-borrow</literal>, <literal>test-on-return</literal>,
or <literal>test-while-idle</literal> is specified
</entry>
</row>
<row>
<entry>
<literal>
validation-query-filter
</literal>
</entry>
<entry>
<literal>objectclass=*</literal>
</entry>
<entry>
The search filter to be used when validating connections. Only used if
<literal>test-on-borrow</literal>, <literal>test-on-return</literal>,
or <literal>test-while-idle</literal> is specified
</entry>
</row>
<row>
<entry>
<literal>
validation-query-search-controls-ref
</literal>
</entry>
<entry>
<literal>null</literal>; default search control settings are described above.
</entry>
<entry>
Id of a SearchControls instance to be used when validating connections. Only used if
<literal>test-on-borrow</literal>, <literal>test-on-return</literal>,
or <literal>test-while-idle</literal> is specified
</entry>
</row>
<row>
<entry>
<literal>
non-transient-exceptions
</literal>
</entry>
<entry>
<literal>javax.naming.CommunicationException</literal>
</entry>
<entry>
Comma-separated list of Exception classes. The listed exceptions will be considered
non-transient with regards to eager invalidation. Should any of the listed exceptions
(or subclasses of them) be thrown by a call to a
pooled <literal>DirContext</literal> instance, that object will be
automatically invalidated without any additional testOnReturn operation.
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="pooling-configuration">
<title>Configuration</title>
<para>
Configuring pooling should look very familiar if you're used
to Jakarta Commons-Pool or Commons-DBCP. You will first
create a normal
<literal>ContextSource</literal>
then wrap it in a
<literal>PoolingContextSource</literal>
.
<informalexample>
<programlisting><![CDATA[
<beans>
...
<ldap:context-source
password="secret" url="ldap://localhost:389" username="cn=Manager">
<ldap:pooling />
</ldap:context-source>
...
</beans>
]]></programlisting>
</informalexample>
In a real world example you would probably configure the
pool options and enable connection validation; the above
serves as an example to demonstrate the general idea.
</para>
<sect2 id="pooling-advanced-configuration">
<title>Validation Configuration</title>
<para>
Adding validation and a few pool configuration tweaks to
the above example is straight forward. Inject a
<literal>DirContextValidator</literal>
and set when validation should occur and the pool is
ready to go.
<informalexample>
<programlisting><![CDATA[
<beans>
...
<ldap:context-source
username="cn=Manager" password="secret" url="ldap://localhost:389" >
<ldap:pooling
test-on-borrow="true"
test-while-idle="true" />
</ldap:context-source>
...
</beans>
]]></programlisting>
</informalexample>
The above example will test each
<literal>DirContext</literal>
before it is passed to the client application and test
<literal>DirContext</literal>s that have been sitting idle in the pool.
</para>
</sect2>
</sect1>
<sect1 id="pooling-issues">
<title>Known Issues</title>
<sect2 id="pooling-custom-auth-issue">
<title>Custom Authentication</title>
<para>
The <literal>PoolingContextSource</literal> assumes that all
<literal>DirContext</literal> objects retrieved from
<literal>ContextSource.getReadOnlyContext()</literal> will have
the same environment and likewise that all
<literal>DirContext</literal> objects retrieved from
<literal>ContextSource.getReadWriteContext()</literal> will
have the same environment. This means that wrapping a
<literal>LdapContextSource</literal> configured with an
<literal>AuthenticationSource</literal> in a
<literal>PoolingContextSource</literal> will not function
as expected. The pool would be populated using the credentials
of the first user and unless new connections were needed
subsequent context requests would not be filled for the user
specified by the <literal>AuthenticationSource</literal> for
the requesting thread.
</para>
</sect2>
</sect1>
</chapter>

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<preface id="preface">
<title>Preface</title>
<para>
The Java Naming and Directory Interface (JNDI) is for LDAP programming
what Java Database Connectivity (JDBC) is for SQL programming. There are
several similarities between JDBC and JNDI/LDAP (Java LDAP). Despite
being two completely different APIs with different pros and cons, they
share a number of less flattering characteristics:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>They require extensive plumbing code, even to perform the simplest of tasks.</para>
</listitem>
<listitem>
<para>All resources need to be correctly closed, no matter what happens.</para>
</listitem>
<listitem>
<para>Exception handling is difficult.</para>
</listitem>
</itemizedlist>
<para>
The above points often lead to massive code duplication in common usages
of the APIs. As we all know, code duplication is one of the worst code smells.
All in all, it boils down to this: JDBC and LDAP programming in Java are both
incredibly dull and repetitive.
</para>
<para>
Spring JDBC, a part of the Spring framework, provides excellent utilities for
simplifying SQL programming. We need a similar framework for Java LDAP programming.
</para>
</preface>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="repositories">
<title>Spring LDAP Repositories</title>
<sect1 id="repositories-overview">
<title>Overview</title>
<para>
Spring LDAP has built-in support for Spring Data repositories. The basic functionality and configuration is described <ulink
url="http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html">here</ulink>.
When working with Spring LDAP repositories, please note the following:
<itemizedlist>
<listitem>
Spring LDAP repositories can be enabled using an <literal>&lt;ldap:repositories&gt;</literal> tag in
your XML configuration or using an <literal>@EnableLdapRepositories</literal> annotation on a
configuration class.
</listitem>
<listitem>
To include support for <literal>LdapQuery</literal> parameters in automatically generated repositories,
have your interface extend <literal>LdapRepository</literal> rather than <literal>CrudRepository</literal>.
</listitem>
<listitem>
All Spring LDAP repositories must work with entities annotated with the ODM annotations, as described
in <xref linkend="odm" />.
</listitem>
<listitem>
Since all ODM managed classes must have a Distinguished Name as ID, all Spring LDAP repositories must
have the ID type parameter set to <literal>javax.naming.Name</literal>. Indeed, the built-in
<literal>SpringLdapRepository</literal> only takes one type parameter; the managed entity class, defaulting
ID to <literal>javax.naming.Name</literal>.
</listitem>
<listitem>
Due to specifics of the LDAP protocol, paging and sorting is not supported for Spring LDAP repositories.
</listitem>
</itemizedlist>
</para>
</sect1>
<sect1 id="querydsl-repositories">
<title>QueryDSL support</title>
<para>
Basic QueryDSL support is included in Spring LDAP. This support includes the following:
<itemizedlist>
<listitem>
An Annotation Processor, <literal>LdapAnnotationProcessor</literal>, for generating QueryDSL classes
based on Spring LDAP ODM annotations. See <xref linkend="odm" /> for more information on the ODM annotations.
</listitem>
<listitem>
A Query implementation, <literal>QueryDslLdapQuery</literal>, for building and executing QueryDSL
queries in code.
</listitem>
<listitem>
Spring Data repository support for QueryDSL predicates. <literal>QueryDslPredicateExecutor</literal>
includes a number of additional methods with appropriate parameters; extend this interface along with
<literal>LdapRepository</literal> to include this support in your repository.
</listitem>
</itemizedlist>
</para>
</sect1>
</chapter>

View File

@@ -1,426 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This is the XSL FO (PDF) stylesheet for the Spring reference
documentation.
Thanks are due to Christian Bauer of the Hibernate project
team for writing the original stylesheet upon which this one
is based.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="1.0">
<xsl:import href="urn:docbkx:stylesheet"/>
<!--###################################################
Custom Title Page
################################################### -->
<xsl:template name="book.titlepage.recto">
<fo:block>
<fo:table table-layout="fixed" width="175mm">
<fo:table-column column-width="175mm"/>
<fo:table-body>
<fo:table-row>
<fo:table-cell text-align="center">
<fo:block>
<fo:block font-family="Helvetica" font-size="24pt" padding-before="10mm">
<xsl:value-of select="bookinfo/title"/>
</fo:block>
</fo:block>
<fo:block font-family="Helvetica" font-size="22pt" padding-before="10mm">
<xsl:value-of select="bookinfo/subtitle"/>
</fo:block>
<fo:block font-family="Helvetica" font-size="12pt" padding="10mm">
<xsl:value-of select="bookinfo/releaseinfo"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell text-align="center">
<fo:block font-family="Helvetica" font-size="14pt" padding="10mm">
<xsl:value-of select="bookinfo/pubdate"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell text-align="center">
<fo:block font-family="Helvetica" font-size="12pt" padding="10mm">
<xsl:text>Copyright &#xA9; 2005-2010 </xsl:text>
<xsl:for-each select="bookinfo/authorgroup/author">
<xsl:if test="position() > 1">
<xsl:text>, </xsl:text>
</xsl:if>
<xsl:value-of select="firstname"/>
<xsl:text> </xsl:text>
<xsl:value-of select="surname"/>
</xsl:for-each>
</fo:block>
<fo:block font-family="Helvetica" font-size="10pt" padding="1mm">
<xsl:value-of select="bookinfo/legalnotice"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
</xsl:template>
<!-- Prevent blank pages in output -->
<xsl:template name="book.titlepage.before.verso">
</xsl:template>
<xsl:template name="book.titlepage.verso">
</xsl:template>
<xsl:template name="book.titlepage.separator">
</xsl:template>
<!--###################################################
Header
################################################### -->
<!-- More space in the center header for long text -->
<xsl:attribute-set name="header.content.properties">
<xsl:attribute name="font-family">
<xsl:value-of select="$body.font.family"/>
</xsl:attribute>
<xsl:attribute name="margin-left">-5em</xsl:attribute>
<xsl:attribute name="margin-right">-5em</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Custom Footer
################################################### -->
<xsl:template name="footer.content">
<xsl:param name="pageclass" select="''"/>
<xsl:param name="sequence" select="''"/>
<xsl:param name="position" select="''"/>
<xsl:param name="gentext-key" select="''"/>
<xsl:variable name="Version">
<xsl:if test="//releaseinfo">
<xsl:text>Spring LDAP (</xsl:text>
<xsl:value-of select="//releaseinfo"/>
<xsl:text>)</xsl:text>
</xsl:if>
</xsl:variable>
<xsl:choose>
<xsl:when test="$sequence='blank'">
<xsl:if test="$position = 'center'">
<xsl:value-of select="$Version"/>
</xsl:if>
</xsl:when>
<!-- for double sided printing, print page numbers on alternating sides (of the page) -->
<xsl:when test="$double.sided != 0">
<xsl:choose>
<xsl:when test="$sequence = 'even' and $position='left'">
<fo:page-number/>
</xsl:when>
<xsl:when test="$sequence = 'odd' and $position='right'">
<fo:page-number/>
</xsl:when>
<xsl:when test="$position='center'">
<xsl:value-of select="$Version"/>
</xsl:when>
</xsl:choose>
</xsl:when>
<!-- for single sided printing, print all page numbers on the right (of the page) -->
<xsl:when test="$double.sided = 0">
<xsl:choose>
<xsl:when test="$position='center'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:when test="$position='right'">
<fo:page-number/>
</xsl:when>
</xsl:choose>
</xsl:when>
</xsl:choose>
</xsl:template>
<!--###################################################
Extensions
################################################### -->
<!-- These extensions are required for table printing and other stuff -->
<xsl:param name="use.extensions">1</xsl:param>
<xsl:param name="tablecolumns.extension">0</xsl:param>
<xsl:param name="callout.extensions">1</xsl:param>
<!-- FOP provide only PDF Bookmarks at the moment -->
<xsl:param name="fop.extensions">1</xsl:param>
<!--
Ulrik Sandberg, 2010-11-28
fop 0.95 (docbkx-maven-plugin 2.0.9+) requires fop1.extensions.
fop 0.25 (docbkx-maven-plugin up to 2.0.7) requires fop.extensions.
fop 0.94 (docbkx-maven-plugin 2.0.8) requires ?.
<xsl:param name="fop1.extensions">1</xsl:param>
-->
<!--###################################################
Table Of Contents
################################################### -->
<!-- Generate the TOCs for named components only -->
<xsl:param name="generate.toc">
book toc
</xsl:param>
<!-- Show only Sections up to level 3 in the TOCs -->
<xsl:param name="toc.section.depth">2</xsl:param>
<!-- Dot and Whitespace as separator in TOC between Label and Title-->
<xsl:param name="autotoc.label.separator" select="'. '"/>
<!--###################################################
Paper & Page Size
################################################### -->
<!-- Paper type, no headers on blank pages, no double sided printing -->
<xsl:param name="paper.type" select="'A4'"/>
<xsl:param name="double.sided">0</xsl:param>
<xsl:param name="headers.on.blank.pages">0</xsl:param>
<xsl:param name="footers.on.blank.pages">0</xsl:param>
<!-- Space between paper border and content (chaotic stuff, don't touch) -->
<xsl:param name="page.margin.top">5mm</xsl:param>
<xsl:param name="region.before.extent">10mm</xsl:param>
<xsl:param name="body.margin.top">10mm</xsl:param>
<xsl:param name="body.margin.bottom">15mm</xsl:param>
<xsl:param name="region.after.extent">10mm</xsl:param>
<xsl:param name="page.margin.bottom">0mm</xsl:param>
<xsl:param name="page.margin.outer">18mm</xsl:param>
<xsl:param name="page.margin.inner">18mm</xsl:param>
<!-- No intendation of Titles -->
<xsl:param name="title.margin.left">0pc</xsl:param>
<!--###################################################
Fonts & Styles
################################################### -->
<!-- Left aligned text and no hyphenation -->
<xsl:param name="alignment">justify</xsl:param>
<xsl:param name="hyphenate">false</xsl:param>
<!-- Default Font size -->
<xsl:param name="body.font.master">11</xsl:param>
<xsl:param name="body.font.small">8</xsl:param>
<!-- Line height in body text -->
<xsl:param name="line-height">1.4</xsl:param>
<!-- Monospaced fonts are smaller than regular text -->
<xsl:attribute-set name="monospace.properties">
<xsl:attribute name="font-family">
<xsl:value-of select="$monospace.font.family"/>
</xsl:attribute>
<xsl:attribute name="font-size">0.8em</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Tables
################################################### -->
<!-- The table width should be adapted to the paper size -->
<xsl:param name="default.table.width">17.4cm</xsl:param>
<!-- Some padding inside tables -->
<xsl:attribute-set name="table.cell.padding">
<xsl:attribute name="padding-left">4pt</xsl:attribute>
<xsl:attribute name="padding-right">4pt</xsl:attribute>
<xsl:attribute name="padding-top">4pt</xsl:attribute>
<xsl:attribute name="padding-bottom">4pt</xsl:attribute>
</xsl:attribute-set>
<!-- Only hairlines as frame and cell borders in tables -->
<xsl:param name="table.frame.border.thickness">0.1pt</xsl:param>
<xsl:param name="table.cell.border.thickness">0.1pt</xsl:param>
<!--###################################################
Labels
################################################### -->
<!-- Label Chapters and Sections (numbering) -->
<xsl:param name="chapter.autolabel">1</xsl:param>
<xsl:param name="section.autolabel" select="1"/>
<xsl:param name="section.label.includes.component.label" select="1"/>
<!--###################################################
Titles
################################################### -->
<!-- Chapter title size -->
<xsl:attribute-set name="chapter.titlepage.recto.style">
<xsl:attribute name="text-align">left</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.8"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
</xsl:attribute-set>
<!-- Why is the font-size for chapters hardcoded in the XSL FO templates?
Let's remove it, so this sucker can use our attribute-set only... -->
<xsl:template match="title" mode="chapter.titlepage.recto.auto.mode">
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format"
xsl:use-attribute-sets="chapter.titlepage.recto.style">
<xsl:call-template name="component.title">
<xsl:with-param name="node" select="ancestor-or-self::chapter[1]"/>
</xsl:call-template>
</fo:block>
</xsl:template>
<!-- Sections 1, 2 and 3 titles have a small bump factor and padding -->
<xsl:attribute-set name="section.title.level1.properties">
<xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.5"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level2.properties">
<xsl:attribute name="space-before.optimum">0.6em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.6em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.6em</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.25"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level3.properties">
<xsl:attribute name="space-before.optimum">0.4em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.4em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.4em</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.0"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<!-- Titles of formal objects (tables, examples, ...) -->
<xsl:attribute-set name="formal.title.properties" use-attribute-sets="normal.para.spacing">
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="hyphenate">false</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.4em</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.6em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.8em</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Programlistings
################################################### -->
<!-- Verbatim text formatting (programlistings) -->
<xsl:attribute-set name="monospace.verbatim.properties">
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.small * 1.0"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="verbatim.properties">
<xsl:attribute name="space-before.minimum">1em</xsl:attribute>
<xsl:attribute name="space-before.optimum">1em</xsl:attribute>
<xsl:attribute name="space-before.maximum">1em</xsl:attribute>
<xsl:attribute name="border-color">#444444</xsl:attribute>
<xsl:attribute name="border-style">solid</xsl:attribute>
<xsl:attribute name="border-width">0.1pt</xsl:attribute>
<xsl:attribute name="padding-top">0.5em</xsl:attribute>
<xsl:attribute name="padding-left">0.5em</xsl:attribute>
<xsl:attribute name="padding-right">0.5em</xsl:attribute>
<xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
<xsl:attribute name="margin-left">0.5em</xsl:attribute>
<xsl:attribute name="margin-right">0.5em</xsl:attribute>
</xsl:attribute-set>
<!-- Shade (background) programlistings -->
<xsl:param name="shade.verbatim">1</xsl:param>
<xsl:attribute-set name="shade.verbatim.style">
<xsl:attribute name="background-color">#F0F0F0</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Callouts
################################################### -->
<!-- Use images for callouts instead of (1) (2) (3) -->
<xsl:param name="callout.graphics">0</xsl:param>
<xsl:param name="callout.unicode">1</xsl:param>
<!-- Place callout marks at this column in annotated areas -->
<xsl:param name="callout.defaultcolumn">90</xsl:param>
<!--###################################################
Admonitions
################################################### -->
<!-- Use nice graphics for admonitions -->
<xsl:param name="admon.graphics">'1'</xsl:param>
<!-- <xsl:param name="admon.graphics.path">&admon_gfx_path;</xsl:param> -->
<!--###################################################
Misc
################################################### -->
<!-- Placement of titles -->
<xsl:param name="formal.title.placement">
figure after
example before
equation before
table before
procedure before
</xsl:param>
<!-- Format Variable Lists as Blocks (prevents horizontal overflow) -->
<xsl:param name="variablelist.as.blocks">1</xsl:param>
<!-- The horrible list spacing problems -->
<xsl:attribute-set name="list.block.spacing">
<xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
colored and hyphenated links
################################################### -->
<xsl:template match="ulink">
<fo:basic-link external-destination="{@url}"
xsl:use-attribute-sets="xref.properties"
text-decoration="underline"
color="blue">
<xsl:choose>
<xsl:when test="count(child::node())=0">
<xsl:value-of select="@url"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</fo:basic-link>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,91 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This is the XSL HTML configuration file for the Spring
LDAP Reference Documentation.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="1.0">
<xsl:import href="urn:docbkx:stylesheet"/>
<!--###################################################
HTML Settings
################################################### -->
<xsl:param name="html.stylesheet">html.css</xsl:param>
<!-- These extensions are required for table printing and other stuff -->
<xsl:param name="use.extensions">1</xsl:param>
<xsl:param name="tablecolumns.extension">0</xsl:param>
<xsl:param name="callout.extensions">1</xsl:param>
<xsl:param name="graphicsize.extension">0</xsl:param>
<!--###################################################
Table Of Contents
################################################### -->
<!-- Generate the TOCs for named components only -->
<xsl:param name="generate.toc">
book toc
</xsl:param>
<!-- Show only Sections up to level 3 in the TOCs -->
<xsl:param name="toc.section.depth">3</xsl:param>
<!--###################################################
Labels
################################################### -->
<!-- Label Chapters and Sections (numbering) -->
<xsl:param name="chapter.autolabel">1</xsl:param>
<xsl:param name="section.autolabel" select="1"/>
<xsl:param name="section.label.includes.component.label" select="1"/>
<!--###################################################
Callouts
################################################### -->
<!-- Use images for callouts instead of (1) (2) (3) -->
<xsl:param name="callout.graphics">0</xsl:param>
<!-- Place callout marks at this column in annotated areas -->
<xsl:param name="callout.defaultcolumn">90</xsl:param>
<!--###################################################
Admonitions
################################################### -->
<!-- Use nice graphics for admonitions -->
<xsl:param name="admon.graphics">0</xsl:param>
<!--###################################################
Misc
################################################### -->
<!-- Placement of titles -->
<xsl:param name="formal.title.placement">
figure after
example before
equation before
table before
procedure before
</xsl:param>
<xsl:template match="author" mode="titlepage.mode">
<xsl:if test="name(preceding-sibling::*[1]) = 'author'">
<xsl:text>, </xsl:text>
</xsl:if>
<span class="{name(.)}">
<xsl:call-template name="person.name"/>
<xsl:apply-templates mode="titlepage.mode" select="./contrib"/>
<xsl:apply-templates mode="titlepage.mode" select="./affiliation"/>
</span>
</xsl:template>
<xsl:template match="authorgroup" mode="titlepage.mode">
<div class="{name(.)}">
<h2>Authors</h2>
<p/>
<xsl:apply-templates mode="titlepage.mode"/>
</div>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,208 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This is the XSL HTML configuration file for the Spring LDAP Reference Documentation.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
version="1.0">
<xsl:import href="urn:docbkx:stylesheet"/>
<!--###################################################
HTML Settings
################################################### -->
<xsl:param name="chunk.section.depth">'5'</xsl:param>
<xsl:param name="use.id.as.filename">'1'</xsl:param>
<!-- These extensions are required for table printing and other stuff -->
<xsl:param name="use.extensions">1</xsl:param>
<xsl:param name="tablecolumns.extension">0</xsl:param>
<xsl:param name="callout.extensions">1</xsl:param>
<xsl:param name="graphicsize.extension">0</xsl:param>
<!--###################################################
Table Of Contents
################################################### -->
<!-- Generate the TOCs for named components only -->
<xsl:param name="generate.toc">
book toc
</xsl:param>
<!-- Show only Sections up to level 3 in the TOCs -->
<xsl:param name="toc.section.depth">3</xsl:param>
<!--###################################################
Labels
################################################### -->
<!-- Label Chapters and Sections (numbering) -->
<xsl:param name="chapter.autolabel">1</xsl:param>
<xsl:param name="section.autolabel" select="1"/>
<xsl:param name="section.label.includes.component.label" select="1"/>
<!--###################################################
Callouts
################################################### -->
<!-- Place callout marks at this column in annotated areas -->
<xsl:param name="callout.graphics">1</xsl:param>
<xsl:param name="callout.defaultcolumn">90</xsl:param>
<!--###################################################
Misc
################################################### -->
<!-- Placement of titles -->
<xsl:param name="formal.title.placement">
figure after
example before
equation before
table before
procedure before
</xsl:param>
<xsl:template match="author" mode="titlepage.mode">
<xsl:if test="name(preceding-sibling::*[1]) = 'author'">
<xsl:text>, </xsl:text>
</xsl:if>
<span class="{name(.)}">
<xsl:call-template name="person.name"/>
<xsl:apply-templates mode="titlepage.mode" select="./contrib"/>
<xsl:apply-templates mode="titlepage.mode" select="./affiliation"/>
</span>
</xsl:template>
<xsl:template match="authorgroup" mode="titlepage.mode">
<div class="{name(.)}">
<h2>Authors</h2>
<p/>
<xsl:apply-templates mode="titlepage.mode"/>
</div>
</xsl:template>
<!--###################################################
Headers and Footers
################################################### -->
<!-- let's have a Spring and SpringSource banner across the top of each page -->
<xsl:template name="user.header.navigation">
<div style="background-color:white;border:none;height:73px;border:1px solid black;">
<a style="border:none;" href="http://static.springframework.org/spring-ldap/site/"
title="The Spring Framework - Spring LDAP">
<img style="border:none;" src="images/xdev-spring_logo.jpg"/>
</a>
<a style="border:none;" href="http://www.springsource.com/" title="SpringSource">
<img style="border:none;position:absolute;padding-top:5px;right:42px;" src="images/s2_box_logo.png"/>
</a>
</div>
</xsl:template>
<!-- no other header navigation (prev, next, etc.) -->
<xsl:template name="header.navigation"/>
<xsl:param name="navig.showtitles">1</xsl:param>
<!-- let's have a 'Sponsored by Jayway' strapline (or somesuch) across the bottom of each page -->
<xsl:template name="footer.navigation">
<xsl:param name="prev" select="/foo"/>
<xsl:param name="next" select="/foo"/>
<xsl:param name="nav.context"/>
<xsl:variable name="home" select="/*[1]"/>
<xsl:variable name="up" select="parent::*"/>
<xsl:variable name="row1" select="count($prev) &gt; 0
or count($up) &gt; 0
or count($next) &gt; 0"/>
<xsl:variable name="row2" select="($prev and $navig.showtitles != 0)
or (generate-id($home) != generate-id(.)
or $nav.context = 'toc')
or ($chunk.tocs.and.lots != 0
and $nav.context != 'toc')
or ($next and $navig.showtitles != 0)"/>
<xsl:if test="$suppress.navigation = '0' and $suppress.footer.navigation = '0'">
<div class="navfooter">
<xsl:if test="$footer.rule != 0">
<hr/>
</xsl:if>
<xsl:if test="$row1 or $row2">
<table width="100%" summary="Navigation footer">
<xsl:if test="$row1">
<tr>
<td width="40%" align="left">
<xsl:if test="count($prev)>0">
<a accesskey="p">
<xsl:attribute name="href">
<xsl:call-template name="href.target">
<xsl:with-param name="object" select="$prev"/>
</xsl:call-template>
</xsl:attribute>
<xsl:call-template name="navig.content">
<xsl:with-param name="direction" select="'prev'"/>
</xsl:call-template>
</a>
</xsl:if>
<xsl:text>&#160;</xsl:text>
</td>
<td width="20%" align="center">
<xsl:choose>
<xsl:when test="$home != . or $nav.context = 'toc'">
<a accesskey="h">
<xsl:attribute name="href">
<xsl:call-template name="href.target">
<xsl:with-param name="object" select="$home"/>
</xsl:call-template>
</xsl:attribute>
<xsl:call-template name="navig.content">
<xsl:with-param name="direction" select="'home'"/>
</xsl:call-template>
</a>
<xsl:if test="$chunk.tocs.and.lots != 0 and $nav.context != 'toc'">
<xsl:text>&#160;|&#160;</xsl:text>
</xsl:if>
</xsl:when>
<xsl:otherwise>&#160;</xsl:otherwise>
</xsl:choose>
<xsl:if test="$chunk.tocs.and.lots != 0 and $nav.context != 'toc'">
<a accesskey="t">
<xsl:attribute name="href">
<xsl:apply-templates select="/*[1]" mode="recursive-chunk-filename">
<xsl:with-param name="recursive" select="true()"/>
</xsl:apply-templates>
<xsl:text>-toc</xsl:text>
<xsl:value-of select="$html.ext"/>
</xsl:attribute>
<xsl:call-template name="gentext">
<xsl:with-param name="key" select="'nav-toc'"/>
</xsl:call-template>
</a>
</xsl:if>
</td>
<td width="40%" align="right">
<xsl:text>&#160;</xsl:text>
<xsl:if test="count($next)>0">
<a accesskey="n">
<xsl:attribute name="href">
<xsl:call-template name="href.target">
<xsl:with-param name="object" select="$next"/>
</xsl:call-template>
</xsl:attribute>
<xsl:call-template name="navig.content">
<xsl:with-param name="direction" select="'next'"/>
</xsl:call-template>
</a>
</xsl:if>
</td>
</tr>
</xsl:if>
<xsl:if test="$row2">
<tr>
<td width="40%" align="left" valign="top">
<xsl:if test="$navig.showtitles != 0">
<xsl:apply-templates select="$prev" mode="object.title.markup"/>
</xsl:if>
<xsl:text>&#160;</xsl:text>
</td>
<td width="20%" align="center">
<span style="color:white;font-size:90%;">
<a href="http://www.jayway.com/"
title="Jayway">Sponsored by Jayway
</a>
</span>
</td>
<td width="40%" align="right" valign="top">
<xsl:text>&#160;</xsl:text>
<xsl:if test="$navig.showtitles != 0">
<xsl:apply-templates select="$next" mode="object.title.markup"/>
</xsl:if>
</td>
</tr>
</xsl:if>
</table>
</xsl:if>
</div>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="simple">
<title>Java 5 Support</title>
<sect1 id="simple-ldap-template">
<title>SimpleLdapTemplate</title>
<note>
As of Spring LDAP 2.0 the core API has full Java 5 support, and <literal>SimpleLdapTemplate</literal>
and associated classes are all deprecated.
</note>
<para>As of version 1.3 Spring LDAP includes the spring-ldap-core-tiger.jar distributable, which adds
a thin layer of Java 5 functionality on top of Spring LDAP.</para>
<para>The <literal>SimpleLdapTemplate</literal> class adds search and lookup methods that take a
<literal>ParameterizedContextMapper</literal>, adding generics support to these methods.</para>
<para><literal>ParametrizedContextMapper</literal> is a typed version of <literal>ContextMapper</literal>,
which simplifies working with searches and lookups:
<example>
<title>Using <literal>ParameterizedContextMapper</literal></title>
<programlisting>public List&lt;Person&gt; getAllPersons(){
return simpleLdapTemplate.search("", "(objectclass=person)",
new <emphasis role="bold">ParameterizedContextMapper&lt;Person&gt;</emphasis>() {
public <emphasis role="bold">Person</emphasis> mapFromContext(Object ctx) {
DirContextAdapter adapter = (DirContextAdapter) ctx;
Person person = new Person();
// Fill the domain object with data from the DirContextAdapter
return person;
}
};
}
</programlisting>
</example>
</para>
</sect1>
</chapter>

View File

@@ -1,241 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="transactions">
<title>Transaction Support</title>
<sect1 id="transactions-intro">
<title>Introduction</title>
<para>Programmers used to working with relational databases coming to the LDAP
world often express surprise to the fact that there is no notion of transactions.
It is not specified in the protocol, and thus no servers support it.
Recognizing that this may be a major problem, Spring LDAP provides support for client-side,
compensating transactions on LDAP resources.</para>
<para>LDAP transaction support is provided by <literal>ContextSourceTransactionManager</literal>, a
<literal>PlatformTransactionManager</literal> implementation that manages Spring transaction
support for LDAP operations. Along with its collaborators it keeps track of the LDAP operations
performed in a transaction, making record of the state before each operation and taking steps to
restore the initial state should the transaction need to be rolled back.</para>
<para>In addition to the actual transaction management, Spring LDAP transaction support also
makes sure that the same <literal>DirContext</literal> instance will be used throughout the same transaction,
i.e. the <literal>DirContext</literal> will not actually be closed until the transaction is finished,
allowing for more efficient resources usage.</para>
<para>
<note>It is important to note that while the approach used by Spring LDAP to provide transaction support
is sufficient for many cases it is by no means &quot;real&quot; transactions in the traditional sense.
The server is completely unaware of the transactions, so e.g. if the connection is broken there will
be no hope to rollback the transaction. While this should be carefully considered it should also be noted
that the alternative will be to operate without any transaction support whatsoever; this is pretty much
as good as it gets.</note>
<note>The client side transaction support will add some overhead in addition to the work required
by the original operations. While this overhead should not be something to worry about in most cases,
if your application will not perform several LDAP operations within the same
transaction (e.g. a <literal>modifyAttributes</literal> followed by a <literal>rebind</literal>), or
if transaction synchronization with a JDBC data source is not required (see below) there will be nothing to gain
by using the LDAP transaction support.</note>
</para>
</sect1>
<sect1 id="transactions-configuration">
<title>Configuration</title>
<para>
Configuring Spring LDAP transactions should look very familiar if you're used to configuring Spring transactions.
You will annotate your transacted classes with <literal>@Transactional</literal>, create a
<literal>TransactionManager</literal> instance and include a <literal>&lt;tx:annotation-driven&gt;</literal>
tag in your bean configuraion.
<informalexample>
<programlisting>
&lt;beans&gt;
...
&lt;ldap:context-source
url="ldap://localhost:389"
base="dc=example,dc=com"
username="cn=Manager"
password="secret" /&gt;
&lt;ldap:ldap-template id="ldapTemplate" /&gt;
&lt;ldap:transaction-manager&gt;
&lt;!--
Note this default configuration will not work for more complex scenarios, see below for more information on RenamingStrategies.
--&gt;
&lt;ldap:default-renaming-strategy /&gt;
&lt;/ldap:transaction-manager&gt;
&lt;!--
The MyDataAccessObject class is annotated with <literal>@Transactional</literal>.
--&gt;
&lt;bean id="myDataAccessObject" class="com.example.MyDataAccessObject"&gt;
&lt;property name="ldapTemplate" ref="ldapTemplate" /&gt;
&lt;/bean&gt;
&lt;tx:annotation-driven /&gt;
...</programlisting>
<note>While this setup will work fine for most simple use cases, some more complex scenarios will
require additional configuration; more specifically if you will be creating or deleting subtrees within
transactions, you will need to use an alternative <literal>TempEntryRenamingStrategy</literal>, as described
in <xref linkend="renaming-strategies"/> below</note>
</informalexample>
In a real world example you would probably apply the transactions on the service object level
rather than the DAO level; the above serves as an example to demonstrate the general idea.
</para>
</sect1>
<sect1 id="jdbc-transaction-integration">
<title>JDBC Transaction Integration</title>
<para>
A common use case when working against LDAP is that some of the data is stored in the LDAP tree, but
other data is stored in a relational database. In this case, transaction support becomes even more important,
since the update of the different resources should be synchronized.
</para>
<para>
While actual XA transactions is not supported, support is provided to conceptually wrap JDBC and LDAP
access within the same transaction by supplying a <literal>data-source-ref</literal> attribute to the
<literal>&lt;ldap:transaction-manager&gt;</literal> tag.
This will create a <literal>ContextSourceAndDataSourceTransactionManager</literal>,
which will then manage the two transactions, virtually as if they were one. When performing a commit,
the LDAP part of the operation will always be performed first, allowing both transactions to be rolled
back should the LDAP commit fail. The JDBC part of the transaction is managed exactly as in
<literal>DataSourceTransactionManager</literal>, except that nested transactions is not supported:
<informalexample>
<programlisting>
&lt;ldap:transaction-manager data-source-ref="dataSource" &gt;
&lt;ldap:default-renaming-strategy /&gt;
&lt;ldap:transaction-manager /&gt;
</programlisting>
</informalexample>
<note>
Once again it should be noted that the provided support is all client side. The wrapped transaction is not
an XA transaction. No two-phase as such commit is performed, as the LDAP server will be unable to vote on its outcome.
Once again, however, for the majority of cases the supplied support will be sufficient.
</note>
</para>
<para>
The same thing can be accomplished for Hibernate integration by supplying a <literal>session-factory-ref</literal>
attribute to the <literal>&lt;ldap:transaction-manager&gt;</literal> tag.
<informalexample>
<programlisting>
&lt;ldap:transaction-manager session-factory-ref="dataSource" &gt;
&lt;ldap:default-renaming-strategy /&gt;
&lt;ldap:transaction-manager /&gt;
</programlisting>
</informalexample>
</para>
</sect1>
<sect1 id="compensating-transactions-explained">
<title>LDAP Compensating Transactions Explained</title>
<para>Spring LDAP manages compensating transactions by making record of the state in the LDAP tree
before each modifying operation (<literal>bind</literal>, <literal>unbind</literal>, <literal>rebind</literal>,
<literal>modifyAttributes</literal>, and <literal>rename</literal>).</para>
<para>This enables the system
to perform compensating operations should the transaction need to be rolled back. In many cases the
compensating operation is pretty straightforward. E.g. the compensating rollback operation for a
<literal>bind</literal> operation will quite obviously be to unbind the entry. Other operations however require
a different, more complicated approach because of some particular characteristics of LDAP databases. Specifically,
it is not always possible to get the values of all <literal>Attributes</literal> of an entry, making the above
strategy insufficient for e.g. an <literal>unbind</literal> operation.</para>
<para>This is why each modifying operation performed within a Spring LDAP managed transaction is internally
split up in four distinct operations - a recording operation, a preparation operation, a commit operation,
and a rollback operation. The specifics for each LDAP operation is described in the table below:</para>
<table frame="all">
<tgroup cols='5' align='left' colsep='1' rowsep='1'>
<colspec colname="c1" />
<colspec colname="c2" />
<colspec colname="c3" />
<colspec colname="c4" />
<colspec colname="c5" />
<thead>
<row>
<entry>LDAP Operation</entry>
<entry>Recording</entry>
<entry>Preparation</entry>
<entry>Commit</entry>
<entry>Rollback</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>bind</literal></entry>
<entry>Make record of the DN of the entry to bind.</entry>
<entry>Bind the entry.</entry>
<entry>No operation.</entry>
<entry>Unbind the entry using the recorded DN.</entry>
</row>
<row>
<entry><literal>rename</literal></entry>
<entry>Make record of the original and target DN.</entry>
<entry>Rename the entry.</entry>
<entry>No operation.</entry>
<entry>Rename the entry back to its original DN.</entry>
</row>
<row>
<entry><literal>unbind</literal></entry>
<entry>Make record of the original DN and calculate a temporary DN.</entry>
<entry>Rename the entry to the temporary location.</entry>
<entry>Unbind the temporary entry.</entry>
<entry>Rename the entry from the temporary location back to its original DN.</entry>
</row>
<row>
<entry><literal>rebind</literal></entry>
<entry>Make record of the original DN and the new <literal>Attributes</literal>, and calculate a temporary DN.</entry>
<entry>Rename the entry to a temporary location.</entry>
<entry>Bind the new <literal>Attributes</literal> at the original DN, and unbind the original entry
from its temporary location.</entry>
<entry>Rename the entry from the temporary location back to its original DN.</entry>
</row>
<row>
<entry><literal>modifyAttributes</literal></entry>
<entry>Make record of the DN of the entry to modify and calculate compensating <literal>ModificationItem</literal>s
for the modifications to be done.</entry>
<entry>Perform the <literal>modifyAttributes</literal> operation.</entry>
<entry>No operation.</entry>
<entry>Perform a <literal>modifyAttributes</literal> operation using the calculated compensating
<literal>ModificationItem</literal>s.</entry>
</row>
</tbody>
</tgroup>
</table>
<para>A more detailed description of the internal workings of the Spring LDAP transaction support is available in the
javadocs.</para>
<sect2 id="renaming-strategies">
<title>Renaming Strategies</title>
<para>
As described in the table above, the transaction management of some operations require the original entry affected
by the operation to be temporarily renamed before the actual modification can be made in the commit.
The manner in which the temporary DN of the entry is calculated is managed by a <literal>TempEntryRenamingStrategy</literal>
specified in a sub-element to the <literal>&lt;ldap:transaction-manager &gt;</literal> declaration
in the configuration. Two implementations are supplied with Spring LDAP:
</para>
<itemizedlist>
<listitem>
<para>
<literal>DefaultTempEntryRenamingStrategy</literal> (the default). Specified using a
<literal>&lt;ldap:default-renaming-strategy /&gt;</literal> element. Adds a suffix to the least significant
part of the entry DN. E.g. for the DN <literal>cn=john doe, ou=users</literal>, this strategy would return the
temporary DN <literal>cn=john doe_temp, ou=users</literal>.
The suffix is configurable using the <literal>temp-suffix</literal> attribute.
</para>
</listitem>
<listitem>
<para>
<literal>DifferentSubtreeTempEntryRenamingStrategy</literal>. Specified using a
<literal>&lt;ldap:different-subtree-renaming-strategy /&gt;</literal> element.
Takes the least significant part of the DN and appends a subtree DN to this.
This makes all temporary entries be placed at a specific location in the LDAP tree.
The temporary subtree DN is configured using the <literal>subtree-node</literal> attribute. E.g., if
<literal>subtree-node</literal> is <literal>ou=tempEntries</literal> and the original DN of the entry is
<literal>cn=john doe, ou=users</literal>, the temporary DN will be <literal>cn=john doe, ou=tempEntries</literal>.
Note that the configured subtree node needs to be present in the LDAP tree.
</para>
</listitem>
</itemizedlist>
<note>
There are some situations where the <literal>DefaultTempEntryRenamingStrategy</literal> will not work. E.g. if your are planning
to do recursive deletes you'll need to use <literal>DifferentSubtreeTempEntryRenamingStrategy</literal>. This is because
the recursive delete operation actually consists of a depth-first delete of each node in the sub tree individually.
Since it is not allowed to rename an entry that has any children, and <literal>DefaultTempEntryRenamingStrategy</literal> would
leave each node in the same subtree (with a different name) in stead of actually removing it, this operation would fail.
When in doubt, use <literal>DifferentSubtreeTempEntryRenamingStrategy</literal>.
</note>
</sect2>
</sect1>
</chapter>

View File

@@ -1,165 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="user-authentication">
<title>User Authentication using Spring LDAP</title>
<sect1>
<title>Basic Authentication</title>
<para>While the core functionality of the <literal>ContextSource</literal>
is to provide <literal>DirContext</literal> instances for use by
<literal>LdapTemplate</literal>, it may also be used for authenticating
users against an LDAP server. The <literal>getContext(principal,
credentials)</literal> method of <literal>ContextSource</literal> will do
exactly that; construct a <literal>DirContext</literal> instance according
to the <literal>ContextSource</literal> configuration, authenticating the
context using the supplied principal and credentials. A custom
authenticate method could look like this:</para>
<para><programlisting>public boolean authenticate(String userDn, String credentials) {
DirContext ctx = null;
try {
ctx = contextSource.getContext(userDn, credentials);
return true;
} catch (Exception e) {
// Context creation failed - authentication did not succeed
logger.error("Login failed", e);
return false;
} finally {
// It is imperative that the created DirContext instance is always closed
LdapUtils.closeContext(ctx);
}
}</programlisting>The userDn supplied to the <literal>authenticate</literal>
method needs to be the full DN of the user to authenticate (regardless of
the <literal>base</literal> setting on the
<literal>ContextSource</literal>). You will typically need to perform an
LDAP search based on e.g. the user name to get this DN:</para>
<para><programlisting>private String getDnForUser(String uid) {
List result = ldapTemplate.search(query().where("uid").is(uid),
new AbstractContextMapper() {
protected Object doMapFromContext(DirContextOperations ctx) {
return ctx.getNameInNamespace();
}
});
if(result.size() != 1) {
throw new RuntimeException("User not found or not unique");
}
return (String)result.get(0);
}</programlisting>There are some drawbacks to this approach. The user is
forced to concern herself with the DN of the user, she can only search for
the user's uid, and the search always starts at the root of the tree (the
empty path). A more flexible method would let the user specify the search
base, the search filter, and the credentials. Spring LDAP includes an authenticate
method in LdapTemplate that provide this functionality: <literal>boolean authenticate(LdapQuery query, String password);</literal>
</para>
<para>Using this method authentication becomes as simple as this:</para>
<para><example>
<title>Authenticating a user using Spring LDAP.</title>
<programlisting>ldapTemplate.authenticate(query().where("uid").is("john.doe"), "secret");</programlisting>
</example>
<note>
As described in below, some setups may require additional operations to be performed
in order for actual authentication to occur. See <xref linkend="operationsOnAuthenticatedContext"/>
for details.
</note>
<tip>
Don't write your own custom authenticate methods. Use the ones
provided in Spring LDAP 1.3.x.
</tip>
</para>
</sect1>
<sect1 id="operationsOnAuthenticatedContext">
<title>Performing Operations on the Authenticated Context</title>
<para>Some authentication schemes and LDAP servers require some operation
to be performed on the created <literal>DirContext</literal> instance for
the actual authentication to occur. You should test and make sure how your
server setup and authentication schemes behave; failure to do so might
result in that users will be admitted into your system regardless of the
DN/credentials supplied. This is a naïve implementation of an authenticate
method where a hard-coded <literal>lookup</literal> operation is performed
on the authenticated context:</para>
<para><programlisting>public boolean authenticate(String userDn, String credentials) {
DirContext ctx = null;
try {
ctx = contextSource.getContext(userDn, credentials);
// Take care here - if a base was specified on the ContextSource
// that needs to be removed from the user DN for the lookup to succeed.
<emphasis role="bold"> ctx.lookup(userDn);</emphasis>
return true;
} catch (Exception e) {
// Context creation failed - authentication did not succeed
logger.error("Login failed", e);
return false;
} finally {
// It is imperative that the created DirContext instance is always closed
LdapUtils.closeContext(ctx);
}
}</programlisting>
It would be better if the operation could be provided as an
implementation of a callback interface, thus not limiting the operation to
always be a <literal>lookup</literal>. Spring LDAP includes the callback interface
<literal>AuthenticatedLdapEntryContextMapper</literal> and a
corresponding <literal>authenticate</literal> method:
<literal>&lt;T&gt; T authenticate(LdapQuery query, String password, AuthenticatedLdapEntryContextMapper&lt;T&gt; mapper);</literal></para>
<itemizedlist>
<listitem>
</listitem>
</itemizedlist>
<para>This opens up for any operation to be performed on the authenticated
context:</para>
<example>
<title>Performing an LDAP operation on the authenticated context using
Spring LDAP.</title>
<programlisting>AuthenticatedLdapEntryContextMapper&lt;DirContextOperations&gt; mapper = new AuthenticatedLdapEntryContextMapper&lt;DirContextOperations&gt;() {
public DirContextOperations mapWithContext(DirContext ctx, LdapEntryIdentification ldapEntryIdentification) {
try {
return (DirContextOperations) ctx.lookup(ldapEntryIdentification.getRelativeName());
}
catch (NamingException e) {
throw new RuntimeException("Failed to lookup " + ldapEntryIdentification.getRelativeName(), e);
}
}
};
ldapTemplate.authenticate(query().where("uid").is("john.doe"), "secret", mapper);</programlisting>
</example>
</sect1>
<sect1>
<title>Obsolete authentication methods</title>
<para>
In addition to the <literal>authenticate</literal> methods described above
there are a number of deprecated methods that can be used for authentication.
While these will work fine, the recommendation is to use the
<literal>LdapQuery</literal> methods instead.
</para>
</sect1>
<sect1>
<title>Use Spring Security</title>
<para>While the approach above may be sufficient for simple authentication
scenarios, requirements in this area commonly expand rapidly. There is a
multitude of aspects that apply, including authentication, authorization,
web integration, user context management, etc. If you suspect that the
requirements might expand beyond just simple authentication, you should
definitely consider using <ulink type=""
url="http://static.springsource.org/spring-security/site/">Spring
Security</ulink> for your security purposes instead. It is a full-blown,
mature security framework addressing the above aspects as well as several
others.</para>
</sect1>
</chapter>

View File

@@ -1,24 +0,0 @@
<chapter id="utilities">
<title>Utilities</title>
<sect1 id="incremental-attributes">
<title>Incremental Retrieval of Multi-Valued Attributes</title>
<para>When there are a very large number of attribute values (>1500) for a specific attribute,
Active Directory will typically refuse to return all these values at once. Instead
the attribute values will be returned according to the
<ulink url="http://www.watersprings.org/pub/id/draft-kashi-incremental-00.txt">Incremental Retrieval of Multi-valued Properties</ulink>
method. This requires the calling part to inspect the returned attribute for specific markers and,
if necessary, make additional lookup requests until all values are found.
</para>
<para>
Spring LDAP's <code>org.springframework.ldap.core.support.DefaultIncrementalAttributesMapper</code>
helps working with this kind of attributes, as follows:
<programlisting>
Attributes attrs = DefaultIncrementalAttributeMapper.lookupAttributes(ldapTemplate, theDn, new Object[]{"oneAttribute", "anotherAttribute"});
</programlisting>
This will parse any returned attribute range markers and make repeated requests as necessary until all values
for all requested attributes have been retrieved.
</para>
</sect1>
</chapter>

View File

@@ -1,108 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<faqs title="Frequently Asked Questions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://maven.apache.org/maven-1.x/plugins/faq/faq.xsd">
<part id="operational">
<title>Operational Attributes</title>
<faq id="remove-oper-attr">
<question>How do I remove an operational attribute using <tt>context.removeAttributeValue()</tt>?</question>
<answer>
<p>
The DirContextAdapter will only read the visible attributes per default. This is because the
operational attributes will only be returned by the server if explicitly asked for, and there
is no way for Spring LDAP to know what attributes to ask for. This means that the DirContextAdapter
will not be populated with the operational attributes, and hence the <tt>removeAttributeValue</tt>
will not have any effect (since from the DirContextAdapter's point of view, it wasn't there in the
first place).
</p>
<p>
There are basically two ways to do this:
</p>
<ol>
<li>Use a search or lookup method that takes the attribute names as argument, like
<tt>LdapTemplate#lookup(Name, String[], ContextMapper)</tt>. Use a ContextMapper
implementation that just returns the supplied DirContextAdapter in <tt>mapFromContext()</tt>.
</li>
<li>Use <tt>LdapTemplate#modifyAttributes(Name, ModificationItem[])</tt> directly, manually
building the ModificationItem array.
</li>
</ol>
</answer>
</faq>
</part>
<!--
<part id="general">
<title>General</title>
<faq id="requirements">
<question>Do I need any other SOAP framework to run Spring Web Services?</question>
<answer>
You don't need any other SOAP framework to use Spring Web services, though it can use some of the
features of Axis 1 and 2.
</answer>
</faq>
<faq id="namespace_err">
<question>I get <tt>NAMESPACE_ERR</tt> exceptions when using Spring-WS. What can I do about it?</question>
<answer>
<p>
If you get the following Exception:
</p>
<pre>
NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
</pre>
<p>
Most often, this exception is related to an older version of Xalan being used. Make sure to upgrade
to 2.7.0.
</p>
</answer>
</faq>
</part>
<part id="java">
<title>Java</title>
<faq id="java-1.4">
<question>Does Spring-WS work under Java 1.4?</question>
<answer>
<p>
Spring Web Services works under Java 1.4, but it requires some effort to make it work. Java 1.4 is
bundled with the older XML parser Crimson, which does not handle namespaces correctly. Additionally,
it is bundled with an older version of Xalan, which also has problems.
Unfortunately, placing newer versions of these on the class path does not override them.
See <a href="http://xml.apache.org/xalan-j/faq.html#faq-N100D6">this FAQ</a> entry on the Xalan
site, and also <a href="http://xerces.apache.org/xerces2-j/faq-general.html#faq-4">this entry</a>
on the Xerces site.
</p>
<p>
The only solution that works is to add newer versions of Xerces and Xalan in the lib/endorsed
directory of your JDK, as explained in those FAQs (i.e.<tt>$JAVA_HOME/lib/endorsed</tt>).
The following libraries are known to work with Java 1.4.2:
</p>
<table class="bodyTable">
<tbody>
<tr><th>Library</th><th>Version</th></tr>
<tr><td><a href="http://xerces.apache.org/xerces2-j/">Xerces</a></td><td>2.8.1</td></tr>
<tr><td><a href="http://xml.apache.org/xalan-j/">Xalan</a></td><td>2.7.0</td></tr>
<tr><td><a href="http://xerces.apache.org/xerces2-j/">XML-APIs</a></td><td>1.3.04</td></tr>
<tr><td><a href="http://java.sun.com/webservices/downloads/1.3/index.html">SAAJ</a></td><td>1.2</td></tr>
</tbody>
</table>
<p>
If you want to use WS-Security, note that the <code>XwsSecurityInterceptor</code> requires Java 5,
because an underlying library (XWSS) requires it. Instead, you can use the
<code>Wss4jSecurityInterceptor</code>.
</p>
</answer>
</faq>
<faq id="java-1.6">
<question>Does Spring-WS work under Java 1.6?</question>
<answer>
<p>
Java 1.6 ships with SAAJ 1.3, JAXB 2.0, and JAXP 1.4 (a custom version of Xerces and Xalan).
Overriding these libraries by putting different version on the classpath will result in various
classloading issues, or exceptions in <tt>org.apache.xml.serializer.ToXMLSAXHandler</tt>.
The only option for using more recent versions is to put the newer version in the
<code>endorsed</code> directory (see above).
</p>
</answer>
</faq>
</part>
-->
</faqs>