Moved everything from mvn-build to trunk root.
This commit is contained in:
34
src/assembly/all.xml
Normal file
34
src/assembly/all.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly>
|
||||
<id>all</id>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<moduleSets>
|
||||
<moduleSet>
|
||||
<includes>
|
||||
<include>org.springframework.ldap:spring-ldap-core</include>
|
||||
<include>org.springframework.ldap:spring-ldap-core-tiger</include>
|
||||
<include>org.springframework.ldap:spring-ldap-acegi-support</include>
|
||||
</includes>
|
||||
<binaries>
|
||||
<outputDirectory/>
|
||||
<includeDependencies>false</includeDependencies>
|
||||
<unpack>true</unpack>
|
||||
<unpackOptions>
|
||||
<excludes>
|
||||
<exclude>**/spring.handlers</exclude>
|
||||
<exclude>**/spring.schemas</exclude>
|
||||
</excludes>
|
||||
</unpackOptions>
|
||||
</binaries>
|
||||
</moduleSet>
|
||||
</moduleSets>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src/main/resources/META-INF</directory>
|
||||
<outputDirectory>META-INF</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
15
src/assembly/docs.xml
Normal file
15
src/assembly/docs.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>.</directory>
|
||||
<includes>
|
||||
<include>readme.txt</include>
|
||||
<include>license.txt</include>
|
||||
<include>notice.txt</include>
|
||||
</includes>
|
||||
<outputDirectory/>
|
||||
<lineEnding>dos</lineEnding>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</component>
|
||||
32
src/assembly/minimal.xml
Normal file
32
src/assembly/minimal.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly>
|
||||
<id>minimal</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<componentDescriptors>
|
||||
<componentDescriptor>src/assembly/docs.xml</componentDescriptor>
|
||||
</componentDescriptors>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<moduleSets>
|
||||
<moduleSet>
|
||||
<includes>
|
||||
<include>org.springframework.ldap:spring-ldap-core</include>
|
||||
<include>org.springframework.ldap:spring-ldap-core-tiger</include>
|
||||
<include>org.springframework.ldap:spring-ldap-test</include>
|
||||
<include>org.springframework.ldap:spring-ldap-acegi-support</include>
|
||||
</includes>
|
||||
<binaries>
|
||||
<includeDependencies>false</includeDependencies>
|
||||
<unpack>false</unpack>
|
||||
<outputFileNameMapping>dist/modules/${module.artifactId}-${module.version}.${module.extension}</outputFileNameMapping>
|
||||
</binaries>
|
||||
</moduleSet>
|
||||
</moduleSets>
|
||||
<files>
|
||||
<file>
|
||||
<source>target/${artifactId}-${version}.jar</source>
|
||||
<outputDirectory>dist</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
||||
26
src/assembly/sources.xml
Normal file
26
src/assembly/sources.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly>
|
||||
<id>sources</id>
|
||||
<formats>
|
||||
<format>jar</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>core/src/main/java</directory>
|
||||
<outputDirectory/>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>core-tiger/src/main/java</directory>
|
||||
<outputDirectory/>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>test-support/src/main/java</directory>
|
||||
<outputDirectory/>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>acegi-support/src/main/java</directory>
|
||||
<outputDirectory/>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
88
src/assembly/with-dependencies.xml
Normal file
88
src/assembly/with-dependencies.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<assembly>
|
||||
<id>with-dependencies</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<componentDescriptors>
|
||||
<componentDescriptor>src/assembly/docs.xml</componentDescriptor>
|
||||
</componentDescriptors>
|
||||
<includeBaseDirectory>true</includeBaseDirectory>
|
||||
<moduleSets>
|
||||
<moduleSet>
|
||||
<includes>
|
||||
<include>org.springframework.ldap:spring-ldap-parent</include>
|
||||
<include>org.springframework.ldap:spring-ldap-core</include>
|
||||
<include>org.springframework.ldap:spring-ldap-core-tiger</include>
|
||||
<include>org.springframework.ldap:spring-ldap-test</include>
|
||||
<include>org.springframework.ldap:spring-ldap-acegi-support</include>
|
||||
</includes>
|
||||
<binaries>
|
||||
<includeDependencies>false</includeDependencies>
|
||||
<unpack>false</unpack>
|
||||
<outputFileNameMapping>dist/modules/${module.artifactId}-${module.version}.${module.extension}</outputFileNameMapping>
|
||||
</binaries>
|
||||
<sources>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>src/main/java</directory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
<outputDirectoryMapping>dist/module-sources/${module.artifactId}</outputDirectoryMapping>
|
||||
</sources>
|
||||
</moduleSet>
|
||||
</moduleSets>
|
||||
<files>
|
||||
<file>
|
||||
<source>target/${artifactId}-${version}.jar</source>
|
||||
<outputDirectory>dist</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>target/${artifactId}-${version}-sources.jar</source>
|
||||
<outputDirectory>dist</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
<fileSets>
|
||||
<!-- Documentation -->
|
||||
<fileSet>
|
||||
<directory>target/site/apidocs</directory>
|
||||
<outputDirectory>docs/api</outputDirectory>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target/site/reference</directory>
|
||||
<outputDirectory>docs/reference</outputDirectory>
|
||||
</fileSet>
|
||||
<!-- Dependencies -->
|
||||
<fileSet>
|
||||
<directory>target/dependency</directory>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
<includes>
|
||||
<include>**/*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<!-- Samples -->
|
||||
<fileSet>
|
||||
<directory>samples</directory>
|
||||
<outputDirectory>samples</outputDirectory>
|
||||
<includes>
|
||||
<include>**/readme.txt</include>
|
||||
</includes>
|
||||
<lineEnding>dos</lineEnding>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>samples</directory>
|
||||
<outputDirectory>samples</outputDirectory>
|
||||
<includes>
|
||||
<include>**/src/**</include>
|
||||
<include>**/build*.xml</include>
|
||||
<include>**/*.bat</include>
|
||||
<include>**/pom.xml</include>
|
||||
</includes>
|
||||
<excludes>
|
||||
<exclude>**/target</exclude>
|
||||
<exclude>**/target/**</exclude>
|
||||
<exclude>**/bin</exclude>
|
||||
<exclude>**/bin/**</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
385
src/docbkx/basic.xml
Normal file
385
src/docbkx/basic.xml
Normal file
@@ -0,0 +1,385 @@
|
||||
<?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;
|
||||
|
||||
public class PersonDaoImpl implements PersonDao {
|
||||
private LdapTemplate ldapTemplate;
|
||||
|
||||
public void setLdapTemplate(LdapTemplate ldapTemplate) {
|
||||
this.ldapTemplate = ldapTemplate;
|
||||
}
|
||||
|
||||
public List getAllPersonNames() {
|
||||
return ldapTemplate.search(
|
||||
"", "(objectclass=person)",
|
||||
<emphasis role="bold"> new AttributesMapper() {
|
||||
public Object mapFromAttributes(Attributes attrs)
|
||||
throws NamingException {
|
||||
return 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;
|
||||
|
||||
public class PersonDaoImpl implements PersonDao {
|
||||
private LdapTemplate ldapTemplate;
|
||||
...
|
||||
<emphasis role="bold"> private class PersonAttributesMapper implements AttributesMapper {
|
||||
public Object 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 getAllPersons() {
|
||||
return ldapTemplate.search("", "(objectclass=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 (Person) 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-filters">
|
||||
<title>Building Dynamic Filters</title>
|
||||
|
||||
<para>We can build dynamic filters to use in searches, using the classes
|
||||
from the <literal>org.springframework.ldap.filter</literal>
|
||||
package. Let's say that we want the following filter:
|
||||
<literal>(&(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 filter support
|
||||
classes:</para>
|
||||
|
||||
<example>
|
||||
<title>Building a search filter dynamically</title>
|
||||
|
||||
<programlisting>package com.example.dao;
|
||||
|
||||
public class PersonDaoImpl implements PersonDao {
|
||||
private LdapTemplate ldapTemplate;
|
||||
...
|
||||
public List getPersonNamesByLastName(String lastName) {
|
||||
<emphasis role="bold"> AndFilter filter = new AndFilter();
|
||||
filter.and(new EqualsFilter("objectclass", "person"));
|
||||
filter.and(new EqualsFilter("sn", lastName));
|
||||
</emphasis> return ldapTemplate.search(
|
||||
"", <emphasis role="bold">filter.encode()</emphasis>,
|
||||
new AttributesMapper() {
|
||||
public Object mapFromAttributes(Attributes attrs)
|
||||
throws NamingException {
|
||||
return attrs.get("cn").get();
|
||||
}
|
||||
});
|
||||
}
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>To perform a wildcard search, it's possible to use the
|
||||
<literal>WhitespaceWildcardsFilter</literal>:</para>
|
||||
|
||||
<example>
|
||||
<title>Building a wildcard search filter</title>
|
||||
|
||||
<programlisting>AndFilter filter = new AndFilter();
|
||||
filter.and(new EqualsFilter("objectclass", "person"));
|
||||
filter.and(new WhitespaceWildcardsFilter("cn", cn));</programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
<note>
|
||||
In addition to simplifying building of complex search filters,
|
||||
the <literal>Filter</literal> classes also provide proper escaping
|
||||
of any unsafe characters. This prevents "ldap injection",
|
||||
where a user might use such characters to inject unwanted operations
|
||||
into your LDAP operations.
|
||||
</note>
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Building Dynamic Distinguished Names</title>
|
||||
|
||||
<para>The standard <ulink
|
||||
url="http://java.sun.com/j2se/1.5.0/docs/api/javax/naming/Name.html">Name</ulink>
|
||||
interface represents a generic name, which is basically an ordered
|
||||
sequence of components. The <literal>Name</literal> interface also
|
||||
provides operations on that sequence; e.g., <literal>add</literal> or
|
||||
<literal>remove</literal>. LdapTemplate provides an implementation of the
|
||||
<literal>Name</literal> interface: <literal>DistinguishedName</literal>.
|
||||
Using this class will greatly simplify building distinguished names,
|
||||
especially considering the sometimes complex rules regarding escapings and
|
||||
encodings. As with the <literal>Filter</literal> classes this helps preventing
|
||||
potentially malicious data being injected into your LDAP operations.
|
||||
</para>
|
||||
<para>
|
||||
The following example illustrates how
|
||||
<literal>DistinguishedName</literal> can be used to dynamically construct
|
||||
a distinguished name:</para>
|
||||
|
||||
<example>
|
||||
<title>Building a distinguished name dynamically</title>
|
||||
|
||||
<programlisting>package com.example.dao;
|
||||
|
||||
import org.springframework.ldap.core.support.DistinguishedName;
|
||||
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"> DistinguishedName dn = new DistinguishedName(BASE_DN);
|
||||
dn.add("c", p.getCountry());
|
||||
dn.add("ou", p.getCompany());
|
||||
dn.add("cn", p.getFullname());
|
||||
</emphasis> return dn;
|
||||
}
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>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>In Java 5, there is an implementation of the Name interface: <ulink
|
||||
url="http://java.sun.com/j2se/1.5.0/docs/api/javax/naming/ldap/LdapName.html">LdapName</ulink>.
|
||||
If you are in the Java 5 world, you might as well use
|
||||
<literal>LdapName</literal>. However, you may still use
|
||||
<literal>DistinguishedName</literal> if you so wish.</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. A description of each
|
||||
sample is provided below:</para>
|
||||
|
||||
<para><orderedlist>
|
||||
<listitem>
|
||||
<para>spring-ldap-person - the sample demonstrating most
|
||||
features.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>spring-ldap-article - the sample application that was written
|
||||
to accompany a <ulink
|
||||
url="http://today.java.net/pub/a/today/2006/04/18/ldaptemplate-java-ldap-made-simple.html">java.net
|
||||
article</ulink> about Spring LDAP.</para>
|
||||
</listitem>
|
||||
</orderedlist></para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
320
src/docbkx/configuration.xml
Normal file
320
src/docbkx/configuration.xml
Normal file
@@ -0,0 +1,320 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<chapter id="configuration">
|
||||
<title>Configuration</title>
|
||||
|
||||
<sect1 id="context-source-configuration">
|
||||
<title>ContextSource Configuration</title>
|
||||
|
||||
<para>There are several properties in <literal>AbstractContextSource</literal>
|
||||
(superclass of <literal>DirContextSource</literal> and <literal>LdapContextSource</literal>)
|
||||
that can be used to modify its behaviour.</para>
|
||||
|
||||
<sect2 id="dir-context-url">
|
||||
<title>LDAP Server URLs</title>
|
||||
|
||||
<para>The URL of the LDAP server is specified using the <literal>url</literal> property.
|
||||
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></para>
|
||||
<para>It is possible to configure multiple alternate LDAP servers using the
|
||||
<literal>urls</literal> property. In this case, supply all server urls in a String
|
||||
array to the <literal>urls</literal> property.</para>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="dir-context-base">
|
||||
<title>Base LDAP path</title>
|
||||
|
||||
<para>It is possible to specify the root context for all LDAP operations using the
|
||||
<literal>base</literal> property of <literal>AbstractContextSource</literal>.
|
||||
When a value has been specified to this property, all Distinguished Names supplied to and received from LDAP operations
|
||||
will be relative to the LDAP path supplied. This can significantly simplify working against the LDAP
|
||||
tree; however there are several occations when you will need to have access to the base path.
|
||||
For more information on this, please refer to <xref linkend="base-context-configuration" /></para>
|
||||
</sect2>
|
||||
|
||||
<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.</para>
|
||||
|
||||
<para><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>userDn</literal> and <literal>password</literal> of the LDAP
|
||||
user to be used for authentication on the
|
||||
<literal>ContextSource</literal>.</para>
|
||||
|
||||
<para><note>
|
||||
<para>The <literal>userDn</literal> 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 supplied to
|
||||
the <literal>ContextSource</literal>.</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>anonymousReadOnly</literal> property to
|
||||
<literal>true</literal>.<literal></literal></para>
|
||||
|
||||
<sect3 id="custom-authentication-processing">
|
||||
<title>Custom DirContext Authentication Processing</title>
|
||||
|
||||
<para>The default authentication mechanism used in Spring LDAP is SIMPLE authentication.
|
||||
This means that in the user DN (as specified to the <literal>userDn</literal> property) 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 to the <literal>ContextSource</literal>
|
||||
in the configuration.</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>
|
||||
|
||||
<para><note><para>When working with TLS connections you need to explicitly turn off
|
||||
the native LDAP Pooling functionality by setting the <literal>pooled</literal> property to <literal>false</literal>,
|
||||
particularly 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></para>
|
||||
</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 static by
|
||||
default - the ones set on the <literal>ContextSource</literal> on
|
||||
startup will be used throughout the lifetime of the
|
||||
<literal>ContextSource</literal> - there are however several cases in
|
||||
which 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 custom <literal>AuthenticationSource</literal>
|
||||
implementation to the <literal>ContextSource</literal> on startup,
|
||||
instead of explicitly specifying the <literal>userDn</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>The Spring bean definition for a
|
||||
SpringSecurityAuthenticationSource</title>
|
||||
|
||||
<programlisting><beans>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="authenticationSource" ref="springSecurityAuthenticationSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="springSecurityAuthenticationSource"
|
||||
class="org.springframework.security.ldap.SpringSecurityAuthenticationSource" />
|
||||
...
|
||||
</beans></programlisting>
|
||||
</example>
|
||||
|
||||
<note>
|
||||
<para>We don't specify any <literal>userDn</literal> or
|
||||
<literal>password</literal> to our <literal>ContextSource</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>
|
||||
|
||||
<sect3>
|
||||
<title>Default Authentication</title>
|
||||
|
||||
<para>When using <literal>SpringSecurityAuthenticationSource</literal>,
|
||||
authenticated contexts will only be possible to create once the user
|
||||
is logged in using Acegi. To use default authentication information
|
||||
when no user is logged in, use the
|
||||
<literal>DefaultValuesAuthenticationSourceDecorator</literal>:</para>
|
||||
|
||||
<example>
|
||||
<title>Configuring a
|
||||
DefaultValuesAuthenticationSourceDecorator</title>
|
||||
|
||||
<programlisting><beans>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="authenticationSource" ref="authenticationSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="authenticationSource"
|
||||
class="org.springframework.ldap.authentication.DefaultValuesAuthenticationSourceDecorator">
|
||||
<property name="target" ref="springSecurityAuthenticationSource" />
|
||||
<property name="defaultUser" value="cn=myDefaultUser" />
|
||||
<property name="defaultPassword" value="pass" />
|
||||
</bean>
|
||||
|
||||
<bean id="springSecurityAuthenticationSource"
|
||||
class="org.springframework.security.ldap.SpringSecurityAuthenticationSource" />
|
||||
...
|
||||
</beans></programlisting>
|
||||
</example>
|
||||
</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>true</literal>, i.e. the native Java LDAP pooling will be turned on.
|
||||
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>
|
||||
</sect2>
|
||||
<sect2 id="context-source-advanced">
|
||||
<title>Advanced ContextSource Configuration</title>
|
||||
<sect3 id="context-source-context-factory">
|
||||
<title>Alternate ContextFactory</title>
|
||||
|
||||
<para>It is possible to configure the <literal>ContextFactory</literal> that the
|
||||
<literal>ContextSource</literal> is to use when creating Contexts using the
|
||||
<literal>contextFactory</literal> property. The default value is
|
||||
<literal>com.sun.jndi.ldap.LdapCtxFactory</literal>.</para>
|
||||
</sect3>
|
||||
<sect3 id="context-source-object-factory">
|
||||
<title>Custom DirObjectFactory</title>
|
||||
|
||||
<para>As described in <xref linkend="dirobjectfactory" />, a <literal>DirObjectFactory</literal>
|
||||
can be used to translate the <literal>Attributes</literal> of found Contexts
|
||||
to a more useful <literal>DirContext</literal> implementation. This can be
|
||||
configured using the <literal>dirObjectFactory</literal> property. You can use
|
||||
this property if you have your own, custom <literal>DirObjectFactory</literal> implementation.</para>
|
||||
<para>The default value is <literal>DefaultDirObjectFactory</literal>.</para>
|
||||
</sect3>
|
||||
<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 from <literal>AbstractContextSource</literal>.
|
||||
Such properties should be set in a <literal>Map</literal> and supplied to
|
||||
the <literal>baseEnvironmentProperties</literal> property.</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="ldap-template-configuration">
|
||||
<title>LdapTemplate Configuration</title>
|
||||
|
||||
<sect2 id="ldap-template-ignore-partial-result">
|
||||
<title>Ignoring PartialResultExceptions</title>
|
||||
|
||||
<para>Some Active Directory (AD) servers are unable to automatically following
|
||||
referrals, which often leads to a <literal>PartialResultException</literal> being
|
||||
thrown in searches. You can specify that <literal>PartialResultException</literal>
|
||||
is to be ignored by setting the <literal>ignorePartialResultException</literal>
|
||||
property to <literal>true</literal>.
|
||||
<note>This causes all referrals to be ignored, and no notice will be given that
|
||||
a <literal>PartialResultException</literal> has been encountered.
|
||||
There is currently no way of manually following referrals using LdapTemplate.</note></para>
|
||||
</sect2>
|
||||
</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>BaseLdapPathAware</literal> interface. Secondly, a <literal>BaseLdapPathBeanPostProcessor</literal>
|
||||
needs to be defined in the application context</para>
|
||||
<example>
|
||||
<title>Implementing <literal>BaseLdapPathAware</literal></title>
|
||||
|
||||
<programlisting>package com.example.service;
|
||||
|
||||
public class PersonService implements PersonService, <emphasis role="bold">BaseLdapPathAware</emphasis> {
|
||||
...
|
||||
<emphasis role="bold">private DistinguishedName basePath;
|
||||
|
||||
public void setBaseLdapPath(DistinguishedName basePath) {
|
||||
this.basePath = basePath;
|
||||
}</emphasis>
|
||||
...
|
||||
private DistinguishedName getFullPersonDn(Person person) {
|
||||
return new DistinguishedName(<emphasis role="bold">basePath</emphasis>).append(person.getDn());
|
||||
}
|
||||
...
|
||||
}</programlisting>
|
||||
</example>
|
||||
<example>
|
||||
<title>Specifying a <literal>BaseLdapPathBeanPostProcessor</literal> in your <literal>ApplicationContext</literal></title>
|
||||
|
||||
<programlisting><beans>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="acegiAuthenticationSource" ref="authenticationSource" />
|
||||
</bean>
|
||||
...
|
||||
<emphasis role="bold"><bean class="org.springframework.ldap.core.support.BaseLdapPathBeanPostProcessor" /></emphasis>
|
||||
</beans>
|
||||
</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>
|
||||
200
src/docbkx/contextprocessor.xml
Normal file
200
src/docbkx/contextprocessor.xml
Normal file
@@ -0,0 +1,200 @@
|
||||
<?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 < 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://java.sun.com/j2se/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 by providing two classes:
|
||||
<literal>PagedResultsRequestControl</literal> and
|
||||
<literal>PagedResultsCookie</literal>. The
|
||||
<literal>PagedResultsRequestControl</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 two
|
||||
pieces of information from it: the estimated total result size and a
|
||||
cookie. This cookie is a byte array containing information that the server
|
||||
needs the next time it is called with a
|
||||
<literal>PagedResultsControl</literal>. In order to make it easy to store
|
||||
this cookie between searches, Spring LDAP provides the wrapper class
|
||||
<literal>PagedResultsCookie</literal>.</para>
|
||||
|
||||
<para>Below is an example of how the paged search results functionality may
|
||||
be used:</para>
|
||||
|
||||
<example>
|
||||
<title>Paged results using <literal>PagedResultsRequestControl</literal></title>
|
||||
|
||||
<programlisting>public PagedResult getAllPersons(PagedResultsCookie cookie) {
|
||||
PagedResultsRequestControl control = new PagedResultsRequestControl(PAGE_SIZE, cookie);
|
||||
SearchControls searchControls = new SearchControls();
|
||||
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
|
||||
List persons = ldapTemplate.search("", "objectclass=person", searchControls, control);
|
||||
|
||||
return new PagedResult(persons, control.getCookie());
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para>In the first call to this method, <literal>null</literal> will be supplied as
|
||||
the cookie parameter. On subsequent calls the client will need to supply the cookie from
|
||||
the last search (returned wrapped in the <literal>PagedResult</literal>) each time the
|
||||
method is called. When the actual cookie is <literal>null</literal> (i.e.
|
||||
<literal>pagedResult.getCookie().getCookie()</literal> returns <literal>null</literal>),
|
||||
the last batch has been returned from the search.</para>
|
||||
|
||||
</sect1>
|
||||
</chapter>
|
||||
341
src/docbkx/dirobjectfactory.xml
Normal file
341
src/docbkx/dirobjectfactory.xml
Normal file
@@ -0,0 +1,341 @@
|
||||
<?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(dn, context, null);
|
||||
}
|
||||
}</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(dn, context, null);
|
||||
}
|
||||
|
||||
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 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.core.support.DistinguishedName;
|
||||
import org.springframework.ldap.filter.EqualsFilter;
|
||||
|
||||
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();
|
||||
mapToContext(person, context);
|
||||
ldapTemplate.bind(buildDn(person), context, null);
|
||||
}
|
||||
|
||||
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 findAll() {
|
||||
EqualsFilter filter = new EqualsFilter("objectclass", "person");
|
||||
return ldapTemplate.search(DistinguishedName.EMPTY_PATH, 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) {
|
||||
DistinguishedName dn = new DistinguishedName();
|
||||
dn.add("c", country);
|
||||
dn.add("ou", company);
|
||||
dn.add("cn", fullname);
|
||||
return dn;
|
||||
}
|
||||
|
||||
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>
|
||||
148
src/docbkx/executors.xml
Normal file
148
src/docbkx/executors.xml
Normal file
@@ -0,0 +1,148 @@
|
||||
<?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>
|
||||
BIN
src/docbkx/images/NamingException.png
Normal file
BIN
src/docbkx/images/NamingException.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
BIN
src/docbkx/images/banner4.jpg
Normal file
BIN
src/docbkx/images/banner4.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
BIN
src/docbkx/images/bannerR.gif
Normal file
BIN
src/docbkx/images/bannerR.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.8 KiB |
BIN
src/docbkx/images/logo.png
Normal file
BIN
src/docbkx/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
src/docbkx/images/package-dependencies.png
Normal file
BIN
src/docbkx/images/package-dependencies.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
src/docbkx/images/package-structure.png
Normal file
BIN
src/docbkx/images/package-structure.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
417
src/docbkx/overview.xml
Normal file
417
src/docbkx/overview.xml
Normal file
@@ -0,0 +1,417 @@
|
||||
<?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 filters 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;
|
||||
|
||||
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(
|
||||
"", "(objectclass=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><beans>
|
||||
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="userDn" value="cn=Manager" />
|
||||
<property name="password" value="secret" />
|
||||
</bean>
|
||||
|
||||
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
|
||||
<constructor-arg ref="contextSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="personDao" class="com.example.dao.PersonDaoImpl">
|
||||
<property name="ldapTemplate" ref="ldapTemplate" />
|
||||
</bean>
|
||||
</beans></programlisting>
|
||||
</informalexample>
|
||||
</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</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>commons-logging</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>acegi-security</emphasis> (For Acegi security integration using <literal>AcegiAuthenticationSource</literal>)</para>
|
||||
</listitem>
|
||||
<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-dao</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>ldapbp</emphasis> (Sun LDAP Booster Pack - if you will use the LDAP v3 Server controls integration)</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para><emphasis>commons-pool</emphasis> (If you are planning to use the pooling functionality)</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</sect1>
|
||||
|
||||
<sect1 id="introduction-package-structure">
|
||||
<title>Package structure</title>
|
||||
|
||||
<para>This section provides an overview of the logical package structure
|
||||
of the Spring LDAP codebase. The dependencies for each package are clearly
|
||||
noted. A package dependency noted as <emphasis>(optional)</emphasis> means
|
||||
that the dependency is needed to compile the package but is optionally
|
||||
needed at runtime (depending on your use of the package). For example, use
|
||||
of Spring LDAP together with Acegi Security entails use of the
|
||||
<literal>org.acegisecurity</literal> package.</para>
|
||||
|
||||
<figure>
|
||||
<title>Spring LDAP package structure</title>
|
||||
|
||||
<mediaobject>
|
||||
<imageobject>
|
||||
<imagedata fileref="images/package-dependencies.png" format="png" />
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</figure>
|
||||
|
||||
<sect2 id="transaction.compensating">
|
||||
<title>org.springframework.transaction.compensating</title>
|
||||
|
||||
<para>The <emphasis>transaction.compensating</emphasis> package contains
|
||||
the generic compensating transaction support. This is not LDAP-specific
|
||||
or JNDI-specific in any way.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: commons-logging</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap">
|
||||
<title>org.springframework.ldap</title>
|
||||
|
||||
<para>The <emphasis>ldap</emphasis> package contains the exceptions of
|
||||
the library. These exceptions form an unchecked hierarchy that mirrors
|
||||
the NamingException hierarchy.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: spring-core</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap.core">
|
||||
<title>org.springframework.ldap.core</title>
|
||||
|
||||
<para>The <emphasis>ldap.core</emphasis> package contains the central
|
||||
abstractions of the library. These abstractions include
|
||||
AuthenticationSource, ContextSource, DirContextProcessor, and
|
||||
NameClassPairCallbackHandler. This package also contains the central
|
||||
class LdapTemplate, plus various mappers and executors.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: ldap, ldap.support, spring-beans,
|
||||
commons-lang, commons-logging</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap.core.support">
|
||||
<title>org.springframework.ldap.core.support</title>
|
||||
|
||||
<para>The <emphasis>ldap.core.support</emphasis> package contains
|
||||
supporting implementations of some of the core interfaces.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: ldap.core, ldap.support, spring-core,
|
||||
spring-beans, spring-context (optional), commons-lang, commons-logging</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap.support">
|
||||
<title>org.springframework.ldap.support</title>
|
||||
|
||||
<para>The <emphasis>ldap.support</emphasis> package contains supporting
|
||||
utilities, like the exception translation mechanism.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: ldap, spring-core, commons-logging</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap.authentication">
|
||||
<title>org.springframework.ldap.authentication</title>
|
||||
|
||||
<para>The <emphasis>ldap.authentication</emphasis> package contains an
|
||||
implementation of the AuthenticationSource interface that can be used
|
||||
with <ulink url="http://www.acegisecurity.org/">Acegi Security</ulink>,
|
||||
as well as related helper classes.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: ldap.core, acegi-security (optional),
|
||||
spring-beans, commons-lang, commons-logging</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap.control">
|
||||
<title>org.springframework.ldap.control</title>
|
||||
|
||||
<para>The <emphasis>ldap.control</emphasis> package contains an abstract
|
||||
implementation of the DirContextProcessor interface that can be used as
|
||||
a basis for processing RequestControls and ResponseControls. There is
|
||||
also a concrete implementation that handles paged search results. The
|
||||
<ulink url="http://java.sun.com/products/jndi/">LDAP Booster
|
||||
Pack</ulink> is used to get support for controls.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: ldap, ldap.core, LDAP booster pack (optional), spring-core,
|
||||
commons-lang, commons-logging</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap.filter">
|
||||
<title>org.springframework.ldap.filter</title>
|
||||
|
||||
<para>The <emphasis>ldap.filter</emphasis> package contains the Filter
|
||||
abstraction and several implementations of it.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: ldap.core, commons-lang</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap.transaction.compensating">
|
||||
<title>org.springframework.ldap.transaction.compensating</title>
|
||||
|
||||
<para>The <emphasis>ldap.transaction.compensating</emphasis> package contains the
|
||||
core LDAP-specific implementation of compensating transactions.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: ldap, ldap.core, transaction.compensating,
|
||||
spring-core, commons-lang, commons-logging</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap.transaction.compensating.manager">
|
||||
<title>org.springframework.ldap.transaction.compensating.manager</title>
|
||||
|
||||
<para>The <emphasis>ldap.transaction.compensating.manager</emphasis> package contains the
|
||||
core implementation classes for client-side compensating transactions.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: ldap, ldap.core, ldap.transaction.compensating,
|
||||
ldap.transaction.compensating.support, transaction.compensating,
|
||||
spring-dao (optional), spring-jdbc (optional), commons-logging</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<sect2 id="ldap.transaction.compensating.support">
|
||||
<title>org.springframework.ldap.transaction.compensating.support</title>
|
||||
|
||||
<para>The <emphasis>ldap.transaction.compensating.support</emphasis> package contains
|
||||
useful helper classes for client-side compensating transactions.</para>
|
||||
|
||||
<itemizedlist spacing="compact">
|
||||
<listitem>
|
||||
<para>Dependencies: ldap.core, ldap.transaction.compensating</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</sect2>
|
||||
|
||||
<para>For the exact list of jar dependencies, see the Spring LDAP <ulink
|
||||
url="http://jayasoft.org/ivy">Ivy</ulink> dependency manager descriptor
|
||||
located within the Spring LDAP distribution at
|
||||
<literal>spring-ldap/ivy.xml</literal></para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="introduction-support">
|
||||
<title>Support</title>
|
||||
|
||||
<para>Spring LDAP 1.2.1 is supported on Spring 1.2.8 or later, including
|
||||
2.0.x.</para>
|
||||
|
||||
<para>The community support forum is located at <ulink
|
||||
url="http://forum.springframework.org">http://forum.springframework.org</ulink>,
|
||||
and the project web page is <ulink
|
||||
url="http://www.springframework.org/ldap">http://www.springframework.org/ldap</ulink>.</para>
|
||||
</sect1>
|
||||
</chapter>
|
||||
550
src/docbkx/pooling.xml
Normal file
550
src/docbkx/pooling.xml
Normal file
@@ -0,0 +1,550 @@
|
||||
<?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
|
||||
<literal>PoolingContextSource</literal>
|
||||
which can wrap any
|
||||
<literal>ContextSource</literal>
|
||||
and pool both read-only and read-write
|
||||
<literal>DirContext</literal>
|
||||
objects.
|
||||
<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>
|
||||
The
|
||||
<literal>DirContextValidator</literal>
|
||||
interface is used by the
|
||||
<literal>PoolingContextSource</literal>
|
||||
for validation and
|
||||
<literal>DefaultDirContextValidator</literal>
|
||||
is provided as the default validation implementation.
|
||||
<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
|
||||
<literal>DefaultDirContextValidator</literal>
|
||||
should work with no configuration changes on most LDAP
|
||||
servers and provide the fastest way to validate the
|
||||
<literal>DirContext</literal>
|
||||
.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="pooling-properties">
|
||||
<title>Pool Properties</title>
|
||||
|
||||
<para>
|
||||
The following properties are available on the
|
||||
<literal>PoolingContextSource</literal>
|
||||
for configuration of the DirContext pool. The
|
||||
<literal>contextSource</literal>
|
||||
property must be set and the
|
||||
<literal>dirContextValidator</literal>
|
||||
property must be set if validation is enabled, all other
|
||||
properties are optional.
|
||||
</para>
|
||||
|
||||
<table frame="all">
|
||||
<title>Pooling Configuration Properties</title>
|
||||
<tgroup align="left" cols="3" colsep="1" rowsep="1">
|
||||
<colspec colname="c1" />
|
||||
|
||||
<colspec colname="c2" />
|
||||
|
||||
<colspec colname="c3" />
|
||||
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Parameter</entry>
|
||||
|
||||
<entry>Default</entry>
|
||||
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<literal>contextSource</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<literal>null</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
The
|
||||
<literal>ContextSource</literal>
|
||||
implementation to get
|
||||
<literal>DirContext</literal>
|
||||
s from to populate the pool.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>dirContextValidator</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<literal>null</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
The
|
||||
<literal>DirContextValidator</literal>
|
||||
implementation to use when validating
|
||||
connections. This is required if
|
||||
<literal>testOnBorrow</literal>
|
||||
,
|
||||
<literal>testOnReturn</literal>
|
||||
, or
|
||||
<literal>testWhileIdle</literal>
|
||||
options are set to
|
||||
<literal>true</literal>
|
||||
.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>maxActive</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>maxTotal</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>maxIdle</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>minIdle</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>maxWait</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>whenExhaustedAction</literal>
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
<literal>1</literal> (BLOCK)
|
||||
</entry>
|
||||
|
||||
<entry>
|
||||
Specifies the behaviour when the pool is
|
||||
exhausted.
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
The
|
||||
FAIL (<literal>0</literal>)
|
||||
option will throw a
|
||||
<literal>
|
||||
NoSuchElementException
|
||||
</literal>
|
||||
when the pool is exhausted.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The
|
||||
BLOCK (<literal>1</literal>)
|
||||
option will wait until a new
|
||||
object is available. If
|
||||
<literal>maxWait</literal>
|
||||
is positive a
|
||||
<literal>
|
||||
NoSuchElementException
|
||||
</literal>
|
||||
is thrown if no new object is
|
||||
available after the
|
||||
<literal>maxWait</literal>
|
||||
time expires.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The
|
||||
GROW (<literal>2</literal>)
|
||||
option will create and return a
|
||||
new object (essentially making
|
||||
<literal>maxActive</literal>
|
||||
meaningless).
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry>
|
||||
<literal>testOnBorrow</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>testOnReturn</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>testWhileIdle</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>
|
||||
timeBetweenEvictionRunsMillis
|
||||
</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>numTestsPerEvictionRun</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>
|
||||
minEvictableIdleTimeMillis
|
||||
</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>
|
||||
</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>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
|
||||
<property name="contextSource" ref="contextSourceTarget" />
|
||||
</bean>
|
||||
|
||||
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="userDn" value="cn=Manager" />
|
||||
<property name="password" value="secret" />
|
||||
<property name="pooled" value="false"/>
|
||||
</bean>
|
||||
...
|
||||
</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.
|
||||
<note>
|
||||
<para>
|
||||
Ensure that the
|
||||
<literal>pooled</literal>
|
||||
property is set to
|
||||
<literal>false</literal>
|
||||
on any
|
||||
<literal>ContextSource</literal>
|
||||
that will be wrapped in a
|
||||
<literal>PoolingContextSource</literal>
|
||||
. The
|
||||
<literal>PoolingContextSource</literal>
|
||||
must be able to create new connections when needed
|
||||
and if
|
||||
<literal>pooled</literal>
|
||||
is set to
|
||||
<literal>true</literal>
|
||||
that may not be possible.
|
||||
</para>
|
||||
</note>
|
||||
<note>
|
||||
<para>
|
||||
You'll notice that the actual
|
||||
<literal>ContextSource</literal>
|
||||
gets an id with a "Target" suffix. The bean you will
|
||||
actually refer to is the
|
||||
<literal>PoolingContextSource</literal>
|
||||
that wraps the target
|
||||
<literal>contextSource</literal>
|
||||
</para>
|
||||
</note>
|
||||
</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>
|
||||
...
|
||||
<bean id="contextSource" class="org.springframework.ldap.pool.factory.PoolingContextSource">
|
||||
<property name="contextSource" ref="contextSourceTarget" />
|
||||
<property name="dirContextValidator" ref="dirContextValidator" />
|
||||
<property name="testOnBorrow" value="true" />
|
||||
<property name="testWhileIdle" value="true" />
|
||||
</bean>
|
||||
|
||||
<bean id="dirContextValidator"
|
||||
class="org.springframework.ldap.pool.validation.DefaultDirContextValidator" />
|
||||
|
||||
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="userDn" value="cn=Manager" />
|
||||
<property name="password" value="secret" />
|
||||
<property name="pooled" value="false"/>
|
||||
</bean>
|
||||
...
|
||||
</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>
|
||||
32
src/docbkx/preface.xml
Normal file
32
src/docbkx/preface.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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>
|
||||
303
src/docbkx/resources/css/html.css
Normal file
303
src/docbkx/resources/css/html.css
Normal file
@@ -0,0 +1,303 @@
|
||||
body {
|
||||
text-align: justify;
|
||||
margin-right: 2em;
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
a,
|
||||
a[accesskey^="h"],
|
||||
a[accesskey^="n"],
|
||||
a[accesskey^="u"],
|
||||
a[accesskey^="p"] {
|
||||
font-family: Verdana, Arial, helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
color: #003399;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #003399;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: Verdana, Arial, sans-serif;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-family: Verdana, Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
p, dl, dt, dd, blockquote {
|
||||
color: #000000;
|
||||
margin-bottom: 3px;
|
||||
margin-top: 3px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
ol, ul, p {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
p, blockquote {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
p.releaseinfo {
|
||||
font-size: 100%;
|
||||
font-weight: bold;
|
||||
font-family: Verdana, Arial, helvetica, sans-serif;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
p.pubdate {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
font-family: Verdana, Arial, helvetica, sans-serif;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
td, th, span {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
td[width^="40%"] {
|
||||
font-family: Verdana, Arial, helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
color: #003399;
|
||||
}
|
||||
|
||||
table[summary^="Navigation header"] tbody tr th[colspan^="3"] {
|
||||
font-family: Verdana, Arial, helvetica, sans-serif;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h6 {
|
||||
color: #000000;
|
||||
font-weight: 500;
|
||||
margin-top: 0;
|
||||
padding-top: 14px;
|
||||
font-family: Verdana, Arial, helvetica, sans-serif;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h2.title {
|
||||
font-weight: 800;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
h2.subtitle {
|
||||
font-weight: 800;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.firstname, .surname {
|
||||
font-size: 12px;
|
||||
font-family: Verdana, Arial, helvetica, sans-serif;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: 1px black;
|
||||
empty-cells: hide;
|
||||
margin: 10px 0 30px 50px;
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
div.table {
|
||||
margin: 30px 0 10px 0;
|
||||
border: 1px dashed gray;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
div .table-contents table {
|
||||
border: 1px solid black;
|
||||
}
|
||||
|
||||
div.table > p.title {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
table[summary^="Navigation footer"] {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: 1px black;
|
||||
empty-cells: hide;
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table[summary^="Note"],
|
||||
table[summary^="Warning"],
|
||||
table[summary^="Tip"] {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: 1px black;
|
||||
empty-cells: hide;
|
||||
margin: 10px 0px 10px -20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 4pt;
|
||||
font-family: Verdana, Arial, helvetica, sans-serif;
|
||||
}
|
||||
|
||||
div.warning TD {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 150%;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 100%; font-weight: bold;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 90%; font-weight: bold;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 90%; font-style: italic;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 100%; font-style: italic;
|
||||
}
|
||||
|
||||
tt {
|
||||
font-size: 110%;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.navheader, .navfooter {
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.navfooter table {
|
||||
border-style: dashed;
|
||||
border-color: gray;
|
||||
border-width: 1px 1px 1px 1px;
|
||||
background-color: #cde48d;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 110%;
|
||||
padding: 5px;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: #CCCCCC;
|
||||
background-color: #f3f5e9;
|
||||
}
|
||||
|
||||
ul, ol, li {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: #CCCCCC;
|
||||
border-width: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.variablelist {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.term {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.mediaobject {
|
||||
padding-top: 30px;
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.legalnotice {
|
||||
font-family: Verdana, Arial, helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
float: right;
|
||||
margin: 10px 0 10px 30px;
|
||||
padding: 10px 20px 20px 20px;
|
||||
width: 33%;
|
||||
border: 1px solid black;
|
||||
background-color: #F4F4F4;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.property {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
}
|
||||
|
||||
a code {
|
||||
font-family: Verdana, Arial, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
td code {
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
div.note * td,
|
||||
div.tip * td,
|
||||
div.warning * td,
|
||||
div.calloutlist * td {
|
||||
text-align: justify;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.programlisting {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.programlisting .interfacename,
|
||||
.programlisting .literal,
|
||||
.programlisting .classname {
|
||||
font-size: 95%;
|
||||
}
|
||||
|
||||
.title .interfacename,
|
||||
.title .literal,
|
||||
.title .classname {
|
||||
font-size: 130%;
|
||||
}
|
||||
|
||||
/* everything in a <lineannotation/> is displayed in a coloured, comment-like font */
|
||||
.programlisting * .lineannotation,
|
||||
.programlisting * .lineannotation * {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.question * p {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.answer * p {
|
||||
font-size: 100%;
|
||||
}
|
||||
418
src/docbkx/resources/xsl/fopdf.xsl
Normal file
418
src/docbkx/resources/xsl/fopdf.xsl
Normal file
@@ -0,0 +1,418 @@
|
||||
<?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 © 2005-2007 </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 Security (</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>
|
||||
|
||||
<!--###################################################
|
||||
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>
|
||||
91
src/docbkx/resources/xsl/html.xsl
Normal file
91
src/docbkx/resources/xsl/html.xsl
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This is the XSL HTML configuration file for the Spring
|
||||
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>
|
||||
208
src/docbkx/resources/xsl/html_chunk.xsl
Normal file
208
src/docbkx/resources/xsl/html_chunk.xsl
Normal file
@@ -0,0 +1,208 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This is the XSL HTML configuration file for the Spring 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-ws/site/"
|
||||
title="The Spring Framework - Spring Web Services">
|
||||
<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-banner-rhs.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 SpringSource' 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) > 0
|
||||
or count($up) > 0
|
||||
or count($next) > 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> </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> | </xsl:text>
|
||||
</xsl:if>
|
||||
</xsl:when>
|
||||
<xsl:otherwise> </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> </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> </xsl:text>
|
||||
</td>
|
||||
<td width="20%" align="center">
|
||||
<span style="color:white;font-size:90%;">
|
||||
<a href="http://www.springsource.com/"
|
||||
title="SpringSource">Sponsored by SpringSource
|
||||
</a>
|
||||
</span>
|
||||
</td>
|
||||
<td width="40%" align="right" valign="top">
|
||||
<xsl:text> </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>
|
||||
35
src/docbkx/simple.xml
Normal file
35
src/docbkx/simple.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<chapter id="simple">
|
||||
<title>Java 5 Support</title>
|
||||
|
||||
<sect1 id="simple-ldap-template">
|
||||
<title>SimpleLdapTemplate</title>
|
||||
|
||||
<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<Person> getAllPersons(){
|
||||
return simpleLdapTemplate.search("", "(objectclass=person)",
|
||||
new <emphasis role="bold">ParameterizedContextMapper<Person></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>
|
||||
50
src/docbkx/spring-ldap-reference.xml
Normal file
50
src/docbkx/spring-ldap-reference.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version='1.0' encoding="iso-8859-1"?>
|
||||
<book version="5.0" xml:id="spring-security-reference-guide" xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<info>
|
||||
<title>Spring LDAP</title>
|
||||
<subtitle>Reference Documentation</subtitle>
|
||||
<releaseinfo>Version 1.3-RC1</releaseinfo>
|
||||
<pubdate>2008</pubdate>
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Mattias</firstname>
|
||||
<surname>Arthursson</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Ulrik</firstname>
|
||||
<surname>Sandberg</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Eric</firstname>
|
||||
<surname>Dalquist</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>
|
||||
</info>
|
||||
|
||||
<toc />
|
||||
|
||||
<xi:include href="preface.xml" />
|
||||
<xi:include href="overview.xml" />
|
||||
<xi:include href="basic.xml" />
|
||||
<xi:include href="dirobjectfactory.xml" />
|
||||
<xi:include href="executors.xml" />
|
||||
<xi:include href="contextprocessor.xml" />
|
||||
<xi:include href="transactions.xml" />
|
||||
<xi:include href="simple.xml" />
|
||||
<xi:include href="configuration.xml" />
|
||||
<xi:include href="pooling.xml" />
|
||||
<xi:include href="user-authentication.xml" />
|
||||
</book>
|
||||
216
src/docbkx/transactions.xml
Normal file
216
src/docbkx/transactions.xml
Normal file
@@ -0,0 +1,216 @@
|
||||
<?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 "real" 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 create a <literal>TransactionManager</literal> instance and wrap your target object using a
|
||||
<literal>TransactionProxyFactoryBean</literal>. In addition to this, you will also need to wrap your
|
||||
<literal>ContextSource</literal> in a <literal>TransactionAwareContextSourceProxy</literal>.
|
||||
<informalexample>
|
||||
<programlisting><beans>
|
||||
...
|
||||
<bean id="contextSourceTarget" class="org.springframework.ldap.core.support.LdapContextSource">
|
||||
<property name="url" value="ldap://localhost:389" />
|
||||
<property name="base" value="dc=example,dc=com" />
|
||||
<property name="userDn" value="cn=Manager" />
|
||||
<property name="password" value="secret" />
|
||||
</bean>
|
||||
|
||||
<bean id="contextSource"
|
||||
class="org.springframework.ldap.transaction.compensating.manager.TransactionAwareContextSourceProxy">
|
||||
<constructor-arg ref="contextSourceTarget" />
|
||||
</bean>
|
||||
|
||||
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
|
||||
<constructor-arg ref="contextSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="transactionManager"
|
||||
class="org.springframework.ldap.transaction.compensating.manager.ContextSourceTransactionManager">
|
||||
<constructor-arg ref="contextSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="myDataAccessObjectTarget" class="com.example.MyDataAccessObject">
|
||||
<property name="ldapTemplate" ref="ldapTemplate" />
|
||||
</bean>
|
||||
|
||||
<bean id="myDataAccessObject"
|
||||
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
|
||||
<property name="transactionManager" ref="transactionManager" />
|
||||
<property name="target" ref="myDataAccessObjectTarget" />
|
||||
<property name="transactionAttributes">
|
||||
<props>
|
||||
<prop key="*">PROPAGATION_REQUIRES_NEW</prop>
|
||||
</props>
|
||||
</property>
|
||||
</bean>
|
||||
...</programlisting>
|
||||
</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.
|
||||
<note>You'll notice that the actual <literal>ContextSource</literal> and DAO instances get ids with a
|
||||
"Target" suffix. The beans you will actually refer to are the Proxies that are created
|
||||
around the targets; <literal>contextSource</literal> and <literal>myDataAccessObject</literal></note>
|
||||
</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 using the <literal>ContextSourceAndDataSourceTransactionManager</literal>.
|
||||
A <literal>DataSource</literal> and a <literal>ContextSource</literal> is supplied to the
|
||||
<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.
|
||||
<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>
|
||||
</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>
|
||||
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>
|
||||
supplied to the <literal>ContextSourceTransactionManager</literal>. Two implementations are supplied with Spring LDAP,
|
||||
but if specific behaviour is required a custom implementation can easily be implemented by the user. The
|
||||
provided <literal>TempEntryRenamingStrategy</literal> implementations are:
|
||||
<itemizedlist>
|
||||
<listitem><para><literal>DefaultTempEntryRenamingStrategy</literal> (the default). 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>tempSuffix</literal>
|
||||
property</para></listitem>
|
||||
<listitem><para><literal>DifferentSubtreeTempEntryRenamingStrategy</literal>. 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>subtreeNode</literal> property. E.g., if
|
||||
<literal>subtreeNode</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>
|
||||
101
src/docbkx/user-authentication.xml
Normal file
101
src/docbkx/user-authentication.xml
Normal file
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<chapter id="user-authentication">
|
||||
<title>User Authentication using Spring LDAP</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.
|
||||
</para>
|
||||
<example>
|
||||
<title>Using <literal>ContextSource</literal> for user authentication</title>
|
||||
|
||||
<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>
|
||||
</example>
|
||||
|
||||
<para><note><para>
|
||||
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></note></para>
|
||||
|
||||
<example>
|
||||
<title>Finding a user based on uid attribute.</title>
|
||||
<programlisting>private String getDnForUser(String uid) {
|
||||
Filter f = new EqualsFilter("uid", uid);
|
||||
List result = ldapTemplate.search(DistinguishedName.EMPTY_PATH, f.toString(), 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>
|
||||
</example>
|
||||
|
||||
<para><note><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.
|
||||
</para></note></para>
|
||||
|
||||
<example>
|
||||
<title>Performing LDAP operation on returned <literal>DirContext</literal> objects.</title>
|
||||
|
||||
<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.
|
||||
ctx.lookup(userDn);
|
||||
|
||||
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>
|
||||
</example>
|
||||
|
||||
<para><note><para>
|
||||
While the approach above may be sufficient for very simple authentication scenarios, requirements in this
|
||||
area commonly expand rapidly. There is a multitude of aspects that apply to this area, including
|
||||
authentication, authorization, web integration, user context management, etc. If you
|
||||
suspect that the requirements might expand you should definitely consider using
|
||||
<ulink url="http://springsecurity.org">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></note></para>
|
||||
</chapter>
|
||||
48
src/main/javadoc/javadoc.css
Normal file
48
src/main/javadoc/javadoc.css
Normal file
@@ -0,0 +1,48 @@
|
||||
/* Spring-specific Javadoc style sheet rules */
|
||||
|
||||
#overviewBody {
|
||||
|
||||
}
|
||||
|
||||
.code {
|
||||
border: 1px solid black;
|
||||
background-color: #F4F4F4;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
/* Vanilla Javadoc style sheet rules */
|
||||
|
||||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
background-color: white;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
td { font-size: 10pt; font-family: Helvetica, Arial, sans-serif }/* Javadoc style sheet */
|
||||
|
||||
/* Define colors, fonts and other style attributes here to override the defaults */
|
||||
|
||||
/* Page background color */
|
||||
body { background-color: #FFFFFF }
|
||||
|
||||
/* Headings */
|
||||
h1 { font-size: 145% }
|
||||
|
||||
/* Table colors */
|
||||
.TableHeadingColor { background: #CCCCFF } /* Dark mauve */
|
||||
.TableSubHeadingColor { background: #EEEEFF } /* Light mauve */
|
||||
.TableRowColor { background: #FFFFFF } /* White */
|
||||
|
||||
/* Font used in left-hand frame lists */
|
||||
.FrameTitleFont { font-size: 100%; font-family: Helvetica, Arial, sans-serif }
|
||||
.FrameHeadingFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif }
|
||||
.FrameItemFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif }
|
||||
|
||||
/* Navigation bar fonts and colors */
|
||||
.NavBarCell1 { background-color:#EEEEFF;} /* Light mauve */
|
||||
.NavBarCell1Rev { background-color:#00008B;} /* Dark Blue */
|
||||
.NavBarFont1 { font-family: Arial, Helvetica, sans-serif; color:#000000;}
|
||||
.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
|
||||
|
||||
.NavBarCell2 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
|
||||
.NavBarCell3 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
|
||||
16
src/main/javadoc/overview.html
Normal file
16
src/main/javadoc/overview.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<html>
|
||||
<body>
|
||||
This document is the API specification for the Spring LDAP Framework
|
||||
<hr/>
|
||||
<div id="overviewBody">
|
||||
<p>
|
||||
For further API reference and developer documentation, see the
|
||||
<a href="http://static.springframework.org/spring-ldap/site/reference.html" target="_top">Spring
|
||||
LDAP reference documentation</a>.
|
||||
That documentation contains more detailed, developer-targeted
|
||||
descriptions, with conceptual overviews, definitions of terms,
|
||||
workarounds, and working code examples.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user