View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
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     // Utility Methods
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     // Protected SPI Methods
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 }