blob: 076542aff09fffcf2d2b9821c6a2b2e5c27b3e30 [file] [log] [blame]
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed 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.ldap.server.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.BasicAttribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import org.apache.ldap.common.Lockable;
import org.apache.ldap.common.message.LockableAttributeImpl;
import org.apache.ldap.common.message.LockableAttributesImpl;
import org.apache.ldap.common.util.PreferencesDictionary;
import org.apache.ldap.server.configuration.MutableStartupConfiguration;
import org.apache.ldap.server.configuration.ShutdownConfiguration;
import org.apache.ldap.server.jndi.CoreContextFactory;
/**
* 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 ModificationItem[] EMPTY_MODS = new ModificationItem[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( ( Lockable ) attrs, "objectClass" );
attr.add( "top" );
attr.add( "prefNode" );
attr.add( "extensibleObject" );
attrs.put( attr );
attr = new LockableAttributeImpl( ( Lockable ) attrs, "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( "", ( ModificationItem[] ) 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( "", ( ModificationItem[] ) 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 BasicAttribute( key );
ModificationItem mi = new ModificationItem( DirContext.REMOVE_ATTRIBUTE, attr );
addDelta( mi );
}
private void addDelta( ModificationItem 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++ )
{
ModificationItem mi = ( ModificationItem ) 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 BasicAttribute( key );
attr.add( value );
ModificationItem mi = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr );
addDelta( mi );
}
protected AbstractPreferences childSpi( String name )
{
return new ServerSystemPreferences( this, name );
}
}