File: dirobjectfactory.xml

package info (click to toggle)
libspring-ldap-java 1.3.1.RELEASE-5
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 2,872 kB
  • sloc: java: 12,509; xml: 4,106; jsp: 36; makefile: 33; sh: 13
file content (349 lines) | stat: -rw-r--r-- 14,945 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="dirobjectfactory">
  <title>Simpler Attribute Access and Manipulation with DirContextAdapter</title>

  <sect1 id="dirobjectfactory-intro">
    <title>Introduction</title>

    <para>A little-known--and probably underestimated--feature of the Java
    LDAP API is the ability to register a <literal>DirObjectFactory</literal>
    to automatically create objects from found contexts. One of the reasons
    why it is seldom used is that you will need an implementation of
    <literal>DirObjectFactory</literal> that creates instances of a meaningful
    implementation of <literal>DirContext</literal>. The Spring LDAP library
    provides the missing pieces: a default implementation of
    <literal>DirContext</literal> called <literal>DirContextAdapter</literal>,
    and a corresponding implementation of <literal>DirObjectFactory</literal>
    called <literal>DefaultDirObjectFactory</literal>. Used together with
    <literal>DefaultDirObjectFactory</literal>, the
    <literal>DirContextAdapter</literal> can be a very powerful tool.</para>
  </sect1>

  <sect1>
    <title>Search and Lookup Using ContextMapper</title>

    <para>The <literal>DefaultDirObjectFactory</literal> is registered with
    the <literal>ContextSource</literal> by default, which means that whenever
    a context is found in the LDAP tree, its <literal>Attributes</literal> and
    Distinguished Name (DN) will be used to construct a
    <literal>DirContextAdapter</literal>. This enables us to use a
    <literal>ContextMapper</literal> instead of an
    <literal>AttributesMapper</literal> to transform found values:</para>

    <example>
      <title>Searching using a ContextMapper</title>

      <programlisting>package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   ...
   <emphasis role="bold">private static class PersonContextMapper implements ContextMapper {
      public Object mapFromContext(Object ctx) {
         DirContextAdapter context = (DirContextAdapter)ctx;
         Person p = new Person();
         p.setFullName(context.getStringAttribute("cn"));
         p.setLastName(context.getStringAttribute("sn"));
         p.setDescription(context.getStringAttribute("description"));
         return p;
      }
   }</emphasis>

   public Person findByPrimaryKey(
      String name, String company, String country) {
      Name dn = buildDn(name, company, country);
      return ldapTemplate.lookup(dn, <emphasis role="bold">new PersonContextMapper()</emphasis>);
   }
}</programlisting>
    </example>

    <para>The above code shows that it is possible to retrieve the attributes
    directly by name, without having to go through the
    <literal>Attributes</literal> and <literal>BasicAttribute</literal>
    classes. This is particularly useful when working with multi-value attributes. Extracting values from
    multi-value attributes normally requires looping through a <literal>NamingEnumeration</literal> of
    attribute values returned from the <literal>Attributes</literal> implementation. The
    <literal>DirContextAdapter</literal> can do this for you, using the <literal>getStringAttributes()</literal>
    or <literal>getObjectAttributes()</literal> methods:</para>
    <example>
      <title>Getting multi-value attribute values using <literal>getStringAttributes()</literal></title>

      <programlisting>private static class PersonContextMapper implements ContextMapper {
   public Object mapFromContext(Object ctx) {
      DirContextAdapter context = (DirContextAdapter)ctx;
      Person p = new Person();
      p.setFullName(context.getStringAttribute("cn"));
      p.setLastName(context.getStringAttribute("sn"));
      p.setDescription(context.getStringAttribute("description"));
      // The roleNames property of Person is an String array
      <emphasis role="bold">p.setRoleNames(context.getStringAttributes("roleNames"));</emphasis>
      return p;
   }
}
</programlisting>
    </example>

	<sect2>
	<title>The AbstractContextMapper</title>
    <para>Spring LDAP provides an abstract base implementation of <literal>ContextMapper</literal>, 
    <literal>AbstractContextMapper</literal>. This automatically takes care of the casting of the supplied
    <literal>Object</literal> parameter to <literal>DirContexOperations</literal>. 
    The <literal>PersonContextMapper</literal> above can thus be re-written as follows:
    </para>
        <example>
      <title>Using an AbstractContextMapper</title>

      <programlisting>
  private static class PersonContextMapper <emphasis role="bold">extends AbstractContextMapper</emphasis> {
      public Object <emphasis role="bold">doMapFromContext</emphasis>(DirContextOperations ctx) {
         Person p = new Person();
         p.setFullName(context.getStringAttribute("cn"));
         p.setLastName(context.getStringAttribute("sn"));
         p.setDescription(context.getStringAttribute("description"));
         return p;
      }
  }
</programlisting>
    </example>
    </sect2>
  </sect1>

  <sect1>
    <title>Binding and Modifying Using DirContextAdapter</title>

    <para>While very useful when extracting attribute values, <literal>DirContextAdapter</literal> is even more
    powerful for hiding attribute details when binding and modifying data.</para>

    <sect2>
      <title>Binding</title>

      <para>This is an example of an improved implementation of the create DAO
      method. Compare it with the previous implementation in <xref
      linkend="basic-binding-data" />.</para>

      <example id="example-binding-contextmapper">
        <title>Binding using <literal>DirContextAdapter</literal></title>

        <programlisting>package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   ...
   public void create(Person p) {
      Name dn = buildDn(p);
      DirContextAdapter context = new DirContextAdapter(dn);

      <emphasis role="bold">context.setAttributeValues("objectclass", new String[] {"top", "person"});
      context.setAttributeValue("cn", p.getFullname());
      context.setAttributeValue("sn", p.getLastname());
      context.setAttributeValue("description", p.getDescription());</emphasis>

      ldapTemplate.bind(context);
   }
}</programlisting>
      </example>

      <para>Note that we use the <literal>DirContextAdapter</literal> instance 
      as the second parameter to bind, which should be a <literal>Context</literal>.
      The third parameter is <literal>null</literal>, since we're not using any
      <literal>Attributes</literal>.</para>
      <para>Also note the use of the <literal>setAttributeValues()</literal> method when setting the
      <literal>objectclass</literal> attribute values. The <literal>objectclass</literal> attribute is
      multi-value, and similar to the troubles of extracting muti-value attribute data, building multi-value
      attributes is tedious and verbose work. Using the <literal>setAttributeValues()</literal> mehtod you can
      have <literal>DirContextAdapter</literal> handle that work for you.</para>    
    </sect2>

    <sect2>
      <title>Modifying</title>

      <para>The code for a <literal>rebind</literal> would be pretty much
      identical to <xref linkend="example-binding-contextmapper" />, except
      that the method called would be <literal>rebind</literal>. As we saw in
      <xref linkend="modify-modifyAttributes"/> a more correct approach would be to
      build a <literal>ModificationItem</literal> array containing the actual
      modifications you want to do. This would require you to determine the actual
      modifications compared to the data present in the LDAP tree. Again, this
      is something that <literal>DirContextAdapter</literal> can help you with; the
      <literal>DirContextAdapter</literal> has the ability to keep track of
      its modified attributes. The following example takes advantage of this
      feature:</para>

      <example>
        <title>Modifying using <literal>DirContextAdapter</literal></title>

        <programlisting>package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   ...
   public void update(Person p) {
      Name dn = buildDn(p);
      <emphasis role="bold">DirContextOperations context = ldapTemplate.lookupContext(dn);</emphasis>

      context.setAttributeValues("objectclass", new String[] {"top", "person"});
      context.setAttributeValue("cn", p.getFullname());
      context.setAttributeValue("sn", p.getLastname());
      context.setAttributeValue("description", p.getDescription());

      <emphasis role="bold">ldapTemplate.modifyAttributes(context);</emphasis>
   }
}</programlisting>
      </example>
      <para>When no mapper is passed to a <literal>ldapTemplate.lookup()</literal> operation,
      the result will be a <literal>DirContextAdapter</literal> instance.
      While the <literal>lookup</literal> method returns an <literal>Object</literal>, the convenience
      method <literal>lookupContext</literal> method automatically casts the return value to
      a <literal>DirContextOperations</literal> (the interface that <literal>DirContextAdapter</literal> implements.</para>
      <para>The observant reader will see that we have duplicated code in the
      <literal>create</literal> and <literal>update</literal> methods. This
      code maps from a domain object to a context. It can be extracted to a
      separate method:</para>

      <example>
        <title>Binding and modifying using DirContextAdapter</title>

        <programlisting>package com.example.dao;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;

   ...
   public void create(Person p) {
      Name dn = buildDn(p);
      DirContextAdapter context = new DirContextAdapter(dn);
      mapToContext(p, context);
      ldapTemplate.bind(context);
   }

   public void update(Person p) {
      Name dn = buildDn(p);
      DirContextOperations context = ldapTemplate.lookupContext(dn);
      mapToContext(person, context);
      ldapTemplate.modifyAttributes(context);
   }

   protected void mapToContext (Person p, DirContextOperations context) {
      context.setAttributeValues("objectclass", new String[] {"top", "person"});
      context.setAttributeValue("cn", p.getFullName());
      context.setAttributeValue("sn", p.getLastName());
      context.setAttributeValue("description", p.getDescription());
   }
}</programlisting>
      </example>
    </sect2>
  </sect1>

  <sect1>
    <title>A Complete PersonDao Class</title>

    <para>To illustrate the power of Spring LDAP, here is a complete Person
    DAO implementation for LDAP in just 68 lines:</para>

    <example>
      <title>A complete PersonDao class</title>

      <programlisting>package com.example.dao;

import java.util.List;

import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;

import 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.AndFilter;
import org.springframework.ldap.filter.EqualsFilter;
import org.springframework.ldap.filter.WhitespaceWildcardsFilter;

public class PersonDaoImpl implements PersonDao {
   private LdapTemplate ldapTemplate;

   public void setLdapTemplate(LdapTemplate ldapTemplate) {
      this.ldapTemplate = ldapTemplate;
   }

   public void create(Person person) {
      DirContextAdapter context = new DirContextAdapter(buildDn(person));
      mapToContext(person, context);
      ldapTemplate.bind(context);
   }

   public void update(Person person) {
      Name dn = buildDn(person);
      DirContextOperations context = ldapTemplate.lookupContext(dn);
      mapToContext(person, context);
      ldapTemplate.modifyAttributes(context);
   }

   public void delete(Person person) {
      ldapTemplate.unbind(buildDn(person));
   }

   public Person findByPrimaryKey(String name, String company, String country) {
      Name dn = buildDn(name, company, country);
      return (Person) ldapTemplate.lookup(dn, getContextMapper());
   }

   public List findByName(String name) {
      AndFilter filter = new AndFilter();
      filter.and(new EqualsFilter("objectclass", "person")).and(new WhitespaceWildcardsFilter("cn",name));
      return ldapTemplate.search(DistinguishedName.EMPTY_PATH, filter.encode(), 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>