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 }