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