001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.core.prefs;
021    
022    
023    import java.util.ArrayList;
024    import java.util.Dictionary;
025    import java.util.HashMap;
026    import java.util.List;
027    import java.util.prefs.AbstractPreferences;
028    import java.util.prefs.BackingStoreException;
029    import java.util.prefs.Preferences;
030    
031    import org.apache.directory.server.constants.ApacheSchemaConstants;
032    import org.apache.directory.server.core.DirectoryService;
033    import org.apache.directory.server.core.entry.ClonedServerEntry;
034    import org.apache.directory.server.core.filtering.EntryFilteringCursor;
035    import org.apache.directory.server.i18n.I18n;
036    import org.apache.directory.shared.ldap.constants.SchemaConstants;
037    import org.apache.directory.shared.ldap.entry.DefaultServerAttribute;
038    import org.apache.directory.shared.ldap.entry.EntryAttribute;
039    import org.apache.directory.shared.ldap.entry.Modification;
040    import org.apache.directory.shared.ldap.entry.ModificationOperation;
041    import org.apache.directory.shared.ldap.entry.ServerEntry;
042    import org.apache.directory.shared.ldap.entry.ServerModification;
043    import org.apache.directory.shared.ldap.exception.LdapException;
044    import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
045    import org.apache.directory.shared.ldap.message.AliasDerefMode;
046    import org.apache.directory.shared.ldap.name.DN;
047    import org.apache.directory.shared.ldap.schema.AttributeType;
048    import org.apache.directory.shared.ldap.util.PreferencesDictionary;
049    
050    
051    /**
052     * A server side system {@link Preferences} implementation.  This implementation
053     * presumes the creation of a root system preferences node in advance.  This
054     * should be included with the system.ldif that is packaged with the server.
055     *
056     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
057     * @version $Rev: 927839 $
058     */
059    public class ServerSystemPreferences extends AbstractPreferences
060    {
061        /** an empty array of Strings used to get array from list */
062        private static final String[] EMPTY_STRINGS = new String[0];
063    
064        /** the changes representing cached alterations to preferences */
065        private List<Modification> changes = new ArrayList<Modification>( 3 );
066    
067        /** maps changes based on key: key->list of mods (on same key) */
068        private HashMap<String, List<Modification>> keyToChange = new HashMap<String, List<Modification>>( 3 );
069        
070        private DN dn;
071        
072        private DirectoryService directoryService;
073        
074    
075    
076        /**
077         * Creates a preferences object for the system preferences root.
078         * @param directoryService the directory service core
079         */
080        public ServerSystemPreferences( DirectoryService directoryService )
081        {
082            super( null, "" );
083            super.newNode = false;
084            
085            try
086            {
087                dn = new DN( "prefNodeName=sysPrefRoot,ou=system" );
088            }
089            catch ( LdapInvalidDnException e )
090            {
091                // never happens
092            }
093            
094            this.directoryService = directoryService;
095        }
096    
097        
098        public void close() throws LdapException
099        {
100        }
101    
102    
103        /**
104         * Creates a preferences object using a relative name.
105         * 
106         * @param name the name of the preference node to create
107         * @param parent the parent of the preferences node to create
108         */
109        public ServerSystemPreferences( ServerSystemPreferences parent, String name )
110        {
111            super( parent, name );
112    
113            this.directoryService = parent.directoryService;
114            DN parentDn = ( ( ServerSystemPreferences ) parent() ).dn;
115            try
116            {
117                dn = new DN( "prefNodeName=" + name + "," + parentDn.getName() );
118                dn.normalize( directoryService.getSchemaManager().getNormalizerMapping() );
119                
120                if ( ! directoryService.getAdminSession().exists( dn ) )
121                {
122                    ServerEntry entry = directoryService.newEntry( dn );
123                    entry.add( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, 
124                        ApacheSchemaConstants.PREF_NODE_OC, SchemaConstants.EXTENSIBLE_OBJECT_OC );
125                    entry.add( "prefNodeName", name );
126        
127                    directoryService.getAdminSession().add( entry );
128                    
129                    super.newNode = false;
130                }
131            }
132            catch ( Exception e )
133            {
134                throw new ServerSystemPreferenceException( I18n.err( I18n.ERR_270 ), e );
135            }
136        }
137    
138    
139        // ------------------------------------------------------------------------
140        // Utility Methods
141        // ------------------------------------------------------------------------
142    
143        /**
144         * Wraps this ServerPreferences object as a Dictionary.
145         *
146         * @return a Dictionary that uses this ServerPreferences object as the underlying backing store
147         */
148        public Dictionary<String, String> wrapAsDictionary()
149        {
150            return new PreferencesDictionary( this );
151        }
152    
153    
154        // ------------------------------------------------------------------------
155        // Protected SPI Methods
156        // ------------------------------------------------------------------------
157    
158        protected void flushSpi() throws BackingStoreException
159        {
160            if ( changes.isEmpty() )
161            {
162                return;
163            }
164    
165            try
166            {
167                directoryService.getAdminSession().modify( dn, changes );
168            }
169            catch ( Exception e )
170            {
171                throw new BackingStoreException( e );
172            }
173    
174            changes.clear();
175            keyToChange.clear();
176        }
177    
178    
179        protected void removeNodeSpi() throws BackingStoreException
180        {
181            try
182            {
183                directoryService.getAdminSession().delete( dn );
184            }
185            catch ( Exception e )
186            {
187                throw new BackingStoreException( e );
188            }
189    
190            changes.clear();
191            keyToChange.clear();
192        }
193    
194    
195        protected void syncSpi() throws BackingStoreException
196        {
197            if ( changes.isEmpty() )
198            {
199                return;
200            }
201    
202            try
203            {
204                directoryService.getAdminSession().modify( dn, changes );
205            }
206            catch ( Exception e )
207            {
208                throw new BackingStoreException( e );
209            }
210    
211            changes.clear();
212            keyToChange.clear();
213        }
214    
215    
216        protected String[] childrenNamesSpi() throws BackingStoreException
217        {
218            List<String> children = new ArrayList<String>();
219            EntryFilteringCursor list;
220    
221            try
222            {
223                list = directoryService.getAdminSession().list( dn, AliasDerefMode.DEREF_ALWAYS, null );
224                list.beforeFirst();
225                while ( list.next() )
226                {
227                    ClonedServerEntry entry = list.get();
228                    children.add( ( String ) entry.getDn().getRdn().getNormValue() );
229                }
230            }
231            catch ( Exception e )
232            {
233                throw new BackingStoreException( e );
234            }
235    
236            return children.toArray( EMPTY_STRINGS );
237        }
238    
239    
240        protected String[] keysSpi() throws BackingStoreException
241        {
242            List<String> keys = new ArrayList<String>();
243    
244            try
245            {
246                ServerEntry entry = directoryService.getAdminSession().lookup( dn );
247    
248                for ( EntryAttribute attr : entry )
249                {
250                    String oid = attr.getAttributeType().getOid();
251                    
252                    if ( oid.equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
253                    {
254                        continue;
255                    }
256                    
257                    keys.add( attr.getUpId() );
258                }
259            }
260            catch ( Exception e )
261            {
262                throw new BackingStoreException( e );
263            }
264    
265            return keys.toArray( EMPTY_STRINGS );
266        }
267    
268    
269        protected void removeSpi( String key ) 
270        {
271            AttributeType at;
272            try
273            {
274                at = directoryService.getSchemaManager().lookupAttributeTypeRegistry( key );
275                EntryAttribute attr = new DefaultServerAttribute( at );
276                Modification mi = new ServerModification( ModificationOperation.REMOVE_ATTRIBUTE, attr );
277                addDelta( mi );
278            }
279            catch ( LdapException e )
280            {
281                e.printStackTrace();
282            }
283        }
284    
285    
286        private void addDelta( Modification mi )
287        {
288            String key = mi.getAttribute().getUpId();
289            List<Modification> deltas;
290            changes.add( mi );
291            
292            if ( keyToChange.containsKey( key ) )
293            {
294                deltas = keyToChange.get( key );
295            }
296            else
297            {
298                deltas = new ArrayList<Modification>();
299            }
300    
301            deltas.add( mi );
302            keyToChange.put( key, deltas );
303        }
304    
305    
306        protected String getSpi( String key )
307        {
308            try
309            {
310                EntryAttribute attr = directoryService.getAdminSession().lookup( dn ).get( key );
311                
312                if ( keyToChange.containsKey( key ) )
313                {
314                    for ( Modification mod : keyToChange.get( key ) )
315                    {
316                        if ( mod.getOperation() == ModificationOperation.REMOVE_ATTRIBUTE )
317                        {
318                            attr = null;
319                        }
320                        else
321                        {
322                            attr = mod.getAttribute();
323                        }
324                    }
325                }
326    
327                if ( attr == null )
328                {
329                    return null;
330                }
331                else
332                {
333                    return attr.getString();
334                }
335            }
336            catch ( Exception e )
337            {
338                throw new ServerSystemPreferenceException( I18n.err( I18n.ERR_271 ), e );
339            }
340        }
341    
342    
343        protected void putSpi( String key, String value )
344        {
345            AttributeType at;
346            try
347            {
348                at = directoryService.getSchemaManager().lookupAttributeTypeRegistry( key );
349                EntryAttribute attr = new DefaultServerAttribute( at, value );
350                Modification mi = new ServerModification( ModificationOperation.REPLACE_ATTRIBUTE, attr );
351                addDelta( mi );
352            }
353            catch ( LdapException e )
354            {
355                e.printStackTrace();
356            }
357        }
358    
359    
360        protected AbstractPreferences childSpi( String name )
361        {
362            return new ServerSystemPreferences( this, name );
363        }
364    }