1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.prefs;
18
19
20 import java.util.ArrayList;
21 import java.util.Dictionary;
22 import java.util.HashMap;
23 import java.util.Hashtable;
24 import java.util.List;
25 import java.util.prefs.AbstractPreferences;
26 import java.util.prefs.BackingStoreException;
27 import java.util.prefs.Preferences;
28
29 import javax.naming.Context;
30 import javax.naming.NameClassPair;
31 import javax.naming.NamingEnumeration;
32 import javax.naming.NamingException;
33 import javax.naming.directory.Attribute;
34 import javax.naming.directory.Attributes;
35 import javax.naming.directory.BasicAttribute;
36 import javax.naming.directory.DirContext;
37 import javax.naming.directory.ModificationItem;
38 import javax.naming.ldap.InitialLdapContext;
39 import javax.naming.ldap.LdapContext;
40
41 import org.apache.ldap.common.Lockable;
42 import org.apache.ldap.common.message.LockableAttributeImpl;
43 import org.apache.ldap.common.message.LockableAttributesImpl;
44 import org.apache.ldap.common.util.PreferencesDictionary;
45 import org.apache.ldap.server.configuration.MutableStartupConfiguration;
46 import org.apache.ldap.server.configuration.ShutdownConfiguration;
47 import org.apache.ldap.server.jndi.CoreContextFactory;
48
49
50 /***
51 * A server side system {@link Preferences} implementation. This implementation
52 * presumes the creation of a root system preferences node in advance. This
53 * should be included with the system.ldif that is packaged with the server.
54 *
55 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
56 * @version $Rev: 264732 $
57 */
58 public class ServerSystemPreferences extends AbstractPreferences
59 {
60 /*** an empty array of ModificationItems used to get array from list */
61 private static final ModificationItem[] EMPTY_MODS = new ModificationItem[0];
62
63 /*** an empty array of Strings used to get array from list */
64 private static final String[] EMPTY_STRINGS = new String[0];
65
66 /*** the LDAP context representing this preferences object */
67 private LdapContext ctx;
68
69 /*** the changes (ModificationItems) representing cached alterations to preferences */
70 private ArrayList changes = new ArrayList(3);
71
72 /*** maps changes based on key: key->list of mods (on same key) */
73 private HashMap keyToChange = new HashMap(3);
74
75
76 /***
77 * Creates a preferences object for the system preferences root.
78 */
79 public ServerSystemPreferences()
80 {
81 super( null, "" );
82
83 super.newNode = false;
84
85 MutableStartupConfiguration cfg = new MutableStartupConfiguration();
86 cfg.setAllowAnonymousAccess( true );
87
88 Hashtable env = new Hashtable( cfg.toJndiEnvironment() );
89 env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
90 env.put( Context.PROVIDER_URL, PreferencesUtils.SYSPREF_BASE );
91
92 try
93 {
94 ctx = new InitialLdapContext( env, null );
95 }
96 catch ( Exception e )
97 {
98 throw new ServerSystemPreferenceException( "Failed to open.", e );
99 }
100 }
101
102 public synchronized void close()
103 {
104 if( this.parent() != null )
105 {
106 throw new ServerSystemPreferenceException( "Cannot close child preferences." );
107 }
108
109 Hashtable env = new Hashtable( new ShutdownConfiguration().toJndiEnvironment() );
110 env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
111 env.put( Context.PROVIDER_URL, PreferencesUtils.SYSPREF_BASE );
112
113 try
114 {
115 ctx = new InitialLdapContext( env, null );
116 }
117 catch ( Exception e )
118 {
119 throw new ServerSystemPreferenceException( "Failed to close.", e );
120 }
121 }
122
123
124 /***
125 * Creates a preferences object using a relative name.
126 */
127 public ServerSystemPreferences( ServerSystemPreferences parent, String name )
128 {
129 super( parent, name );
130
131 LdapContext parentCtx = parent.getLdapContext();
132
133 try
134 {
135 ctx = ( LdapContext ) parentCtx.lookup( "prefNodeName=" + name );
136
137 super.newNode = false;
138 }
139 catch ( NamingException e )
140 {
141 super.newNode = true;
142 }
143
144 if ( super.newNode )
145 {
146 try
147 {
148 setUpNode( name );
149 }
150 catch ( Exception e )
151 {
152 throw new ServerSystemPreferenceException( "Failed to set up node.", e );
153 }
154 }
155 }
156
157
158
159
160
161
162
163 /***
164 * Wrapps this ServerPreferences object as a Dictionary.
165 *
166 * @return a Dictionary that uses this ServerPreferences object as the underlying backing store
167 */
168 public Dictionary wrapAsDictionary()
169 {
170 return new PreferencesDictionary( this );
171 }
172
173
174 /***
175 * Gets access to the LDAP context associated with this ServerPreferences node.
176 *
177 * @return the LDAP context associate with this ServerPreferences node
178 */
179 LdapContext getLdapContext()
180 {
181 return ctx;
182 }
183
184
185 /***
186 * Sets up a new ServerPreferences node by injecting the required information
187 * such as the node name attribute and the objectClass attribute.
188 *
189 * @param name the name of the new ServerPreferences node.
190 */
191 private void setUpNode( String name ) throws NamingException
192 {
193 Attributes attrs = new LockableAttributesImpl();
194
195 Attribute attr = new LockableAttributeImpl( ( Lockable ) attrs, "objectClass" );
196
197 attr.add( "top" );
198
199 attr.add( "prefNode" );
200
201 attr.add( "extensibleObject" );
202
203 attrs.put( attr );
204
205 attr = new LockableAttributeImpl( ( Lockable ) attrs, "prefNodeName" );
206
207 attr.add( name );
208
209 attrs.put( attr );
210
211 LdapContext parent = ( ( ServerSystemPreferences ) parent() ).getLdapContext();
212
213 parent.bind( "prefNodeName=" + name, null, attrs );
214
215 ctx = ( LdapContext ) parent.lookup( "prefNodeName=" + name );
216
217 super.newNode = false;
218 }
219
220
221
222
223
224
225
226 protected void flushSpi() throws BackingStoreException
227 {
228 if ( ctx == null )
229 {
230 throw new BackingStoreException( "Ldap context not available for " + super.absolutePath() );
231 }
232
233
234 if ( changes.isEmpty() )
235 {
236 return;
237 }
238
239 try
240 {
241 ctx.modifyAttributes( "", ( ModificationItem[] ) changes.toArray( EMPTY_MODS ) );
242 }
243 catch ( NamingException e )
244 {
245 throw new BackingStoreException( e );
246 }
247
248 changes.clear();
249
250 keyToChange.clear();
251 }
252
253
254 protected void removeNodeSpi() throws BackingStoreException
255 {
256 try
257 {
258 ctx.destroySubcontext( "" );
259 }
260 catch ( NamingException e )
261 {
262 throw new BackingStoreException( e );
263 }
264
265 ctx = null;
266
267 changes.clear();
268
269 keyToChange.clear();
270 }
271
272
273 protected void syncSpi() throws BackingStoreException
274 {
275 if ( ctx == null )
276 {
277 throw new BackingStoreException( "Ldap context not available for " + super.absolutePath() );
278 }
279
280
281 if ( changes.isEmpty() )
282 {
283 return;
284 }
285
286 try
287 {
288 ctx.modifyAttributes( "", ( ModificationItem[] ) changes.toArray( EMPTY_MODS ) );
289 }
290 catch ( NamingException e )
291 {
292 throw new BackingStoreException( e );
293 }
294
295 changes.clear();
296
297 keyToChange.clear();
298 }
299
300
301 protected String[] childrenNamesSpi() throws BackingStoreException
302 {
303 ArrayList children = new ArrayList();
304
305 NamingEnumeration list = null;
306
307 try
308 {
309 list = ctx.list( "" );
310
311 while ( list.hasMore() )
312 {
313 NameClassPair ncp = ( NameClassPair ) list.next();
314
315 children.add( ncp.getName() );
316 }
317 }
318 catch ( NamingException e )
319 {
320 throw new BackingStoreException( e );
321 }
322
323 return ( String[] ) children.toArray( EMPTY_STRINGS );
324 }
325
326
327 protected String[] keysSpi() throws BackingStoreException
328 {
329 Attributes attrs = null;
330
331 ArrayList keys = new ArrayList();
332
333 try
334 {
335 attrs = ctx.getAttributes( "" );
336
337 NamingEnumeration ids = attrs.getIDs();
338
339 while ( ids.hasMore() )
340 {
341 String id = ( String ) ids.next();
342
343 if ( id.equals( "objectClass" ) || id.equals( "prefNodeName" ) )
344 {
345 continue;
346 }
347
348 keys.add( id );
349 }
350 }
351 catch ( NamingException e )
352 {
353 throw new BackingStoreException( e );
354 }
355
356 return ( String[] ) keys.toArray( EMPTY_STRINGS );
357 }
358
359
360 protected void removeSpi( String key )
361 {
362 Attribute attr = new BasicAttribute( key );
363
364 ModificationItem mi = new ModificationItem( DirContext.REMOVE_ATTRIBUTE, attr );
365
366 addDelta( mi );
367 }
368
369
370 private void addDelta( ModificationItem mi )
371 {
372 String key = mi.getAttribute().getID();
373
374 List deltas = null;
375
376 changes.add( mi );
377
378 if ( keyToChange.containsKey( key ) )
379 {
380 deltas = ( List ) keyToChange.get( key );
381 }
382 else
383 {
384 deltas = new ArrayList();
385 }
386
387 deltas.add( mi );
388
389 keyToChange.put( key, deltas );
390 }
391
392
393 protected String getSpi( String key )
394 {
395 String value = null;
396
397 try
398 {
399 Attribute attr = ctx.getAttributes( "" ).get( key );
400
401 if ( keyToChange.containsKey( key ) )
402 {
403 List mods = ( List ) keyToChange.get( key );
404
405 for ( int ii = 0; ii < mods.size(); ii++ )
406 {
407 ModificationItem mi = ( ModificationItem ) mods.get( ii );
408
409 if ( mi.getModificationOp() == DirContext.REMOVE_ATTRIBUTE )
410 {
411 attr = null;
412 }
413 else
414 {
415 attr = mi.getAttribute();
416 }
417 }
418 }
419
420 if ( attr == null )
421 {
422 return null;
423 }
424
425 value = ( String ) attr.get();
426 }
427 catch ( Exception e )
428 {
429 throw new ServerSystemPreferenceException( "Failed to get SPI.", e );
430 }
431
432 return value;
433 }
434
435
436 protected void putSpi( String key, String value )
437 {
438 Attribute attr = new BasicAttribute( key );
439
440 attr.add( value );
441
442 ModificationItem mi = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr );
443
444 addDelta( mi );
445 }
446
447
448 protected AbstractPreferences childSpi( String name )
449 {
450 return new ServerSystemPreferences( this, name );
451 }
452 }