blob: e57071e1a2ef3c32f01a829792c971fdeaefc620 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.server.core.prefs;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.prefs.AbstractPreferences;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.naming.Context;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import org.apache.directory.server.core.configuration.MutableStartupConfiguration;
import org.apache.directory.server.core.configuration.ShutdownConfiguration;
import org.apache.directory.server.core.jndi.CoreContextFactory;
import org.apache.directory.shared.ldap.message.LockableAttributeImpl;
import org.apache.directory.shared.ldap.message.LockableAttributesImpl;
import org.apache.directory.shared.ldap.message.ModificationItemImpl;
import org.apache.directory.shared.ldap.util.PreferencesDictionary;
/**
* A server side system {@link Preferences} implementation. This implementation
* presumes the creation of a root system preferences node in advance. This
* should be included with the system.ldif that is packaged with the server.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$
*/
public class ServerSystemPreferences extends AbstractPreferences
{
/** an empty array of ModificationItems used to get array from list */
private static final ModificationItemImpl[] EMPTY_MODS = new ModificationItemImpl[0];
/** an empty array of Strings used to get array from list */
private static final String[] EMPTY_STRINGS = new String[0];
/** the LDAP context representing this preferences object */
private LdapContext ctx;
/** the changes (ModificationItems) representing cached alterations to preferences */
private ArrayList changes = new ArrayList( 3 );
/** maps changes based on key: key->list of mods (on same key) */
private HashMap keyToChange = new HashMap( 3 );
/**
* Creates a preferences object for the system preferences root.
*/
public ServerSystemPreferences()
{
super( null, "" );
super.newNode = false;
MutableStartupConfiguration cfg = new MutableStartupConfiguration();
cfg.setAllowAnonymousAccess( true );
Hashtable env = new Hashtable( cfg.toJndiEnvironment() );
env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
env.put( Context.PROVIDER_URL, PreferencesUtils.SYSPREF_BASE );
try
{
ctx = new InitialLdapContext( env, null );
}
catch ( Exception e )
{
throw new ServerSystemPreferenceException( "Failed to open.", e );
}
}
public synchronized void close()
{
if ( this.parent() != null )
{
throw new ServerSystemPreferenceException( "Cannot close child preferences." );
}
Hashtable env = new Hashtable( new ShutdownConfiguration().toJndiEnvironment() );
env.put( Context.INITIAL_CONTEXT_FACTORY, CoreContextFactory.class.getName() );
env.put( Context.PROVIDER_URL, PreferencesUtils.SYSPREF_BASE );
try
{
ctx = new InitialLdapContext( env, null );
}
catch ( Exception e )
{
throw new ServerSystemPreferenceException( "Failed to close.", e );
}
}
/**
* Creates a preferences object using a relative name.
*/
public ServerSystemPreferences(ServerSystemPreferences parent, String name)
{
super( parent, name );
LdapContext parentCtx = parent.getLdapContext();
try
{
ctx = ( LdapContext ) parentCtx.lookup( "prefNodeName=" + name );
super.newNode = false;
}
catch ( NamingException e )
{
super.newNode = true;
}
if ( super.newNode )
{
try
{
setUpNode( name );
}
catch ( Exception e )
{
throw new ServerSystemPreferenceException( "Failed to set up node.", e );
}
}
}
// ------------------------------------------------------------------------
// Utility Methods
// ------------------------------------------------------------------------
/**
* Wrapps this ServerPreferences object as a Dictionary.
*
* @return a Dictionary that uses this ServerPreferences object as the underlying backing store
*/
public Dictionary wrapAsDictionary()
{
return new PreferencesDictionary( this );
}
/**
* Gets access to the LDAP context associated with this ServerPreferences node.
*
* @return the LDAP context associate with this ServerPreferences node
*/
LdapContext getLdapContext()
{
return ctx;
}
/**
* Sets up a new ServerPreferences node by injecting the required information
* such as the node name attribute and the objectClass attribute.
*
* @param name the name of the new ServerPreferences node.
*/
private void setUpNode( String name ) throws NamingException
{
Attributes attrs = new LockableAttributesImpl();
Attribute attr = new LockableAttributeImpl( "objectClass" );
attr.add( "top" );
attr.add( "prefNode" );
attr.add( "extensibleObject" );
attrs.put( attr );
attr = new LockableAttributeImpl( "prefNodeName" );
attr.add( name );
attrs.put( attr );
LdapContext parent = ( ( ServerSystemPreferences ) parent() ).getLdapContext();
parent.bind( "prefNodeName=" + name, null, attrs );
ctx = ( LdapContext ) parent.lookup( "prefNodeName=" + name );
super.newNode = false;
}
// ------------------------------------------------------------------------
// Protected SPI Methods
// ------------------------------------------------------------------------
protected void flushSpi() throws BackingStoreException
{
if ( ctx == null )
{
throw new BackingStoreException( "Ldap context not available for " + super.absolutePath() );
}
if ( changes.isEmpty() )
{
return;
}
try
{
ctx.modifyAttributes( "", ( ModificationItemImpl[] ) changes.toArray( EMPTY_MODS ) );
}
catch ( NamingException e )
{
throw new BackingStoreException( e );
}
changes.clear();
keyToChange.clear();
}
protected void removeNodeSpi() throws BackingStoreException
{
try
{
ctx.destroySubcontext( "" );
}
catch ( NamingException e )
{
throw new BackingStoreException( e );
}
ctx = null;
changes.clear();
keyToChange.clear();
}
protected void syncSpi() throws BackingStoreException
{
if ( ctx == null )
{
throw new BackingStoreException( "Ldap context not available for " + super.absolutePath() );
}
if ( changes.isEmpty() )
{
return;
}
try
{
ctx.modifyAttributes( "", ( ModificationItemImpl[] ) changes.toArray( EMPTY_MODS ) );
}
catch ( NamingException e )
{
throw new BackingStoreException( e );
}
changes.clear();
keyToChange.clear();
}
protected String[] childrenNamesSpi() throws BackingStoreException
{
ArrayList children = new ArrayList();
NamingEnumeration list = null;
try
{
list = ctx.list( "" );
while ( list.hasMore() )
{
NameClassPair ncp = ( NameClassPair ) list.next();
children.add( ncp.getName() );
}
}
catch ( NamingException e )
{
throw new BackingStoreException( e );
}
return ( String[] ) children.toArray( EMPTY_STRINGS );
}
protected String[] keysSpi() throws BackingStoreException
{
Attributes attrs = null;
ArrayList keys = new ArrayList();
try
{
attrs = ctx.getAttributes( "" );
NamingEnumeration ids = attrs.getIDs();
while ( ids.hasMore() )
{
String id = ( String ) ids.next();
if ( id.equals( "objectClass" ) || id.equals( "prefNodeName" ) )
{
continue;
}
keys.add( id );
}
}
catch ( NamingException e )
{
throw new BackingStoreException( e );
}
return ( String[] ) keys.toArray( EMPTY_STRINGS );
}
protected void removeSpi( String key )
{
Attribute attr = new LockableAttributeImpl( key );
ModificationItemImpl mi = new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, attr );
addDelta( mi );
}
private void addDelta( ModificationItemImpl mi )
{
String key = mi.getAttribute().getID();
List deltas = null;
changes.add( mi );
if ( keyToChange.containsKey( key ) )
{
deltas = ( List ) keyToChange.get( key );
}
else
{
deltas = new ArrayList();
}
deltas.add( mi );
keyToChange.put( key, deltas );
}
protected String getSpi( String key )
{
String value = null;
try
{
Attribute attr = ctx.getAttributes( "" ).get( key );
if ( keyToChange.containsKey( key ) )
{
List mods = ( List ) keyToChange.get( key );
for ( int ii = 0; ii < mods.size(); ii++ )
{
ModificationItemImpl mi = ( ModificationItemImpl ) mods.get( ii );
if ( mi.getModificationOp() == DirContext.REMOVE_ATTRIBUTE )
{
attr = null;
}
else
{
attr = mi.getAttribute();
}
}
}
if ( attr == null )
{
return null;
}
value = ( String ) attr.get();
}
catch ( Exception e )
{
throw new ServerSystemPreferenceException( "Failed to get SPI.", e );
}
return value;
}
protected void putSpi( String key, String value )
{
Attribute attr = new LockableAttributeImpl( key );
attr.add( value );
ModificationItemImpl mi = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr );
addDelta( mi );
}
protected AbstractPreferences childSpi( String name )
{
return new ServerSystemPreferences( this, name );
}
}