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.ldap;
021    
022    
023    import java.io.FileInputStream;
024    import java.io.IOException;
025    import java.security.KeyStore;
026    import java.security.KeyStoreSpi;
027    import java.security.Provider;
028    import java.security.Security;
029    import java.util.ArrayList;
030    import java.util.Collection;
031    import java.util.HashMap;
032    import java.util.HashSet;
033    import java.util.Iterator;
034    import java.util.List;
035    import java.util.Map;
036    import java.util.Set;
037    
038    import javax.naming.NamingException;
039    
040    import org.apache.directory.server.core.DirectoryService;
041    import org.apache.directory.server.core.partition.PartitionNexus;
042    import org.apache.directory.server.core.security.CoreKeyStoreSpi;
043    import org.apache.directory.server.i18n.I18n;
044    import org.apache.directory.server.ldap.handlers.AbandonHandler;
045    import org.apache.directory.server.ldap.handlers.AddHandler;
046    import org.apache.directory.server.ldap.handlers.BindHandler;
047    import org.apache.directory.server.ldap.handlers.CompareHandler;
048    import org.apache.directory.server.ldap.handlers.DeleteHandler;
049    import org.apache.directory.server.ldap.handlers.ExtendedHandler;
050    import org.apache.directory.server.ldap.handlers.LdapRequestHandler;
051    import org.apache.directory.server.ldap.handlers.ModifyDnHandler;
052    import org.apache.directory.server.ldap.handlers.ModifyHandler;
053    import org.apache.directory.server.ldap.handlers.SearchHandler;
054    import org.apache.directory.server.ldap.handlers.UnbindHandler;
055    import org.apache.directory.server.ldap.handlers.bind.MechanismHandler;
056    import org.apache.directory.server.ldap.handlers.extended.StartTlsHandler;
057    import org.apache.directory.server.ldap.handlers.ssl.LdapsInitializer;
058    import org.apache.directory.server.ldap.replication.ReplicationSystem;
059    import org.apache.directory.server.protocol.shared.DirectoryBackedService;
060    import org.apache.directory.server.protocol.shared.transport.TcpTransport;
061    import org.apache.directory.server.protocol.shared.transport.Transport;
062    import org.apache.directory.server.protocol.shared.transport.UdpTransport;
063    import org.apache.directory.shared.ldap.codec.controls.CascadeControl;
064    import org.apache.directory.shared.ldap.codec.controls.ManageDsaITControl;
065    import org.apache.directory.shared.ldap.codec.controls.replication.syncDoneValue.SyncDoneValueControl;
066    import org.apache.directory.shared.ldap.codec.controls.replication.syncInfoValue.SyncInfoValueControl;
067    import org.apache.directory.shared.ldap.codec.controls.replication.syncRequestValue.SyncRequestValueControl;
068    import org.apache.directory.shared.ldap.codec.controls.replication.syncStateValue.SyncStateValueControl;
069    import org.apache.directory.shared.ldap.codec.search.controls.entryChange.EntryChangeControl;
070    import org.apache.directory.shared.ldap.codec.search.controls.pagedSearch.PagedResultsControl;
071    import org.apache.directory.shared.ldap.codec.search.controls.persistentSearch.PersistentSearchControl;
072    import org.apache.directory.shared.ldap.codec.search.controls.subentries.SubentriesControl;
073    import org.apache.directory.shared.ldap.constants.SaslQoP;
074    import org.apache.directory.shared.ldap.exception.LdapConfigurationException;
075    import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
076    import org.apache.directory.shared.ldap.message.internal.InternalAbandonRequest;
077    import org.apache.directory.shared.ldap.message.internal.InternalAddRequest;
078    import org.apache.directory.shared.ldap.message.internal.InternalBindRequest;
079    import org.apache.directory.shared.ldap.message.internal.InternalCompareRequest;
080    import org.apache.directory.shared.ldap.message.internal.InternalDeleteRequest;
081    import org.apache.directory.shared.ldap.message.internal.InternalExtendedRequest;
082    import org.apache.directory.shared.ldap.message.internal.InternalModifyDnRequest;
083    import org.apache.directory.shared.ldap.message.internal.InternalModifyRequest;
084    import org.apache.directory.shared.ldap.message.internal.InternalSearchRequest;
085    import org.apache.directory.shared.ldap.message.internal.InternalUnbindRequest;
086    import org.apache.directory.shared.ldap.util.StringTools;
087    import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
088    import org.apache.mina.core.filterchain.IoFilterChainBuilder;
089    import org.apache.mina.core.future.WriteFuture;
090    import org.apache.mina.core.service.IoHandler;
091    import org.apache.mina.core.session.IoEventType;
092    import org.apache.mina.core.session.IoSession;
093    import org.apache.mina.filter.codec.ProtocolCodecFactory;
094    import org.apache.mina.filter.codec.ProtocolCodecFilter;
095    import org.apache.mina.filter.executor.ExecutorFilter;
096    import org.apache.mina.filter.executor.UnorderedThreadPoolExecutor;
097    import org.apache.mina.handler.demux.MessageHandler;
098    import org.apache.mina.transport.socket.SocketAcceptor;
099    import org.slf4j.Logger;
100    import org.slf4j.LoggerFactory;
101    
102    
103    /**
104     * An LDAP protocol provider implementation which dynamically associates
105     * handlers.
106     *
107     * @org.apache.xbean.XBean
108     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
109     * @version $Rev: 688548 $
110     */
111    public class LdapServer extends DirectoryBackedService
112    {
113        private static final long serialVersionUID = 3757127143811666817L;
114    
115        /** logger for this class */
116        private static final Logger LOG = LoggerFactory.getLogger( LdapServer.class.getName() );
117    
118        /** Value (0) for configuration where size limit is unlimited. */
119        public static final long NO_SIZE_LIMIT = 0;
120    
121        /** Value (0) for configuration where time limit is unlimited. */
122        public static final int NO_TIME_LIMIT = 0;
123    
124        /** the constant service name of this ldap protocol provider **/
125        public static final String SERVICE_NAME = "ldap";
126        
127        /** The default maximum size limit. */
128        private static final long MAX_SIZE_LIMIT_DEFAULT = 100;
129    
130        /** The default maximum time limit. */
131        private static final int MAX_TIME_LIMIT_DEFAULT = 10000;
132    
133        /** The default service pid. */
134        private static final String SERVICE_PID_DEFAULT = "org.apache.directory.server.ldap";
135    
136        /** The default service name. */
137        private static final String SERVICE_NAME_DEFAULT = "ApacheDS LDAP Service";
138    
139        /** The default IP port. */
140        private static final int DEFAULT_IP_PORT = 389;
141    
142        /** the session manager for this LdapServer */
143        private LdapSessionManager ldapSessionManager = new LdapSessionManager();
144        
145        /** a set of supported controls */
146        private Set<String> supportedControls;
147    
148        /** 
149         * The maximum size limit. 
150         * @see {@link LdapServer#MAX_SIZE_LIMIT_DEFAULT }
151         */
152        private long maxSizeLimit = MAX_SIZE_LIMIT_DEFAULT; 
153    
154        /** 
155         * The maximum time limit.
156         * @see {@link LdapServer#MAX_TIME_LIMIT_DEFAULT }
157         */
158        private int maxTimeLimit = MAX_TIME_LIMIT_DEFAULT; 
159    
160        /** If LDAPS is activated : the external Keystore file, if defined */
161        private String keystoreFile;
162        
163        /** If LDAPS is activated : the certificate password */
164        private String certificatePassword;
165    
166        /** Whether to allow anonymous access: enabled by default. */
167        private boolean allowAnonymousAccess = true;
168    
169        /** The extended operation handlers. */
170        private final Collection<ExtendedOperationHandler> extendedOperationHandlers =
171            new ArrayList<ExtendedOperationHandler>();
172    
173        /** The supported authentication mechanisms. */
174        private Map<String, MechanismHandler> saslMechanismHandlers =
175            new HashMap<String, MechanismHandler>();
176    
177        /** The name of this host, validated during SASL negotiation. */
178        private String saslHost = "ldap.example.com";
179    
180        /** The service principal, used by GSSAPI. */
181        private String saslPrincipal = "ldap/ldap.example.com@EXAMPLE.COM";
182    
183        /** The quality of protection (QoP), used by DIGEST-MD5 and GSSAPI. */
184        private Set<String> saslQop;
185        private String      saslQopString;
186    
187        /** The list of realms serviced by this host. */
188        private List<String> saslRealms;
189    
190        /** The protocol handlers */
191        private LdapRequestHandler<InternalAbandonRequest> abandonHandler;
192        private LdapRequestHandler<InternalAddRequest> addHandler;
193        private LdapRequestHandler<InternalBindRequest> bindHandler;
194        private LdapRequestHandler<InternalCompareRequest> compareHandler;
195        private LdapRequestHandler<InternalDeleteRequest> deleteHandler;
196        private LdapRequestHandler<InternalExtendedRequest> extendedHandler;
197        private LdapRequestHandler<InternalModifyRequest> modifyHandler;
198        private LdapRequestHandler<InternalModifyDnRequest> modifyDnHandler;
199        private LdapRequestHandler<InternalSearchRequest> searchHandler;
200        private LdapRequestHandler<InternalUnbindRequest> unbindHandler;
201    
202    
203        /** the underlying provider codec factory */
204        private ProtocolCodecFactory codecFactory;
205    
206        /** the MINA protocol handler */
207        private final LdapProtocolHandler handler = new LdapProtocolHandler(this);
208    
209        /** tracks start state of the server */
210        private boolean started;
211    
212        /** 
213         * Whether or not confidentiality (TLS secured connection) is required: 
214         * disabled by default. 
215         */
216        private boolean confidentialityRequired;
217    
218        
219        private ReplicationSystem replicationSystem;
220    
221        private KeyStore keyStore = null;
222    
223        private IoFilterChainBuilder chainBuilder;
224        
225        /**
226         * Creates an LDAP protocol provider.
227         */
228        public LdapServer()
229        {
230            super.setEnabled( true );
231            super.setServiceId( SERVICE_PID_DEFAULT );
232            super.setServiceName( SERVICE_NAME_DEFAULT );
233    
234            saslQop = new HashSet<String>();
235            saslQop.add( SaslQoP.QOP_AUTH );
236            saslQop.add( SaslQoP.QOP_AUTH_INT );
237            saslQop.add( SaslQoP.QOP_AUTH_CONF );
238            saslQopString = SaslQoP.QOP_AUTH + ',' + SaslQoP.QOP_AUTH_INT + ',' + SaslQoP.QOP_AUTH_CONF;
239    
240            saslRealms = new ArrayList<String>();
241            saslRealms.add( "example.com" );
242    
243            this.supportedControls = new HashSet<String>();
244            this.supportedControls.add( PersistentSearchControl.CONTROL_OID );
245            this.supportedControls.add( EntryChangeControl.CONTROL_OID );
246            this.supportedControls.add( SubentriesControl.CONTROL_OID );
247            this.supportedControls.add( ManageDsaITControl.CONTROL_OID );
248            this.supportedControls.add( CascadeControl.CONTROL_OID );
249            this.supportedControls.add( PagedResultsControl.CONTROL_OID );
250            // Replication controls
251            this.supportedControls.add( SyncDoneValueControl.CONTROL_OID );
252            this.supportedControls.add( SyncInfoValueControl.CONTROL_OID );
253            this.supportedControls.add( SyncRequestValueControl.CONTROL_OID );
254            this.supportedControls.add( SyncStateValueControl.CONTROL_OID );
255        }
256    
257    
258        /**
259         * Install the LDAP request handlers.
260         */
261        private void installDefaultHandlers()
262        {
263            if ( getAbandonHandler() == null )
264            {
265                setAbandonHandler( new AbandonHandler() );
266            }
267            
268            if ( getAddHandler() == null )
269            {
270                setAddHandler( new AddHandler() );
271            }
272            
273            if ( getBindHandler() == null )
274            {
275                BindHandler handler = new BindHandler();
276                handler.setSaslMechanismHandlers( saslMechanismHandlers );
277                setBindHandler( handler );
278            }
279            
280            if ( getCompareHandler() == null )
281            {
282                setCompareHandler( new CompareHandler() );
283            }
284            
285            if ( getDeleteHandler() == null )
286            {
287                setDeleteHandler( new DeleteHandler() );
288            }
289            
290            if ( getExtendedHandler() == null )
291            {
292                setExtendedHandler( new ExtendedHandler() );
293            }
294            
295            if ( getModifyHandler() == null )
296            {
297                setModifyHandler( new ModifyHandler() );
298            }
299            
300            if ( getModifyDnHandler() == null )
301            {
302                setModifyDnHandler( new ModifyDnHandler() );
303            }
304            
305            if ( getSearchHandler() == null )
306            {
307                setSearchHandler( new SearchHandler() );
308            }
309            
310            if ( getUnbindHandler() == null )
311            {
312                setUnbindHandler( new UnbindHandler() );
313            }
314        }
315    
316        
317        private class AdsKeyStore extends KeyStore
318        {
319            public AdsKeyStore( KeyStoreSpi keyStoreSpi, Provider provider, String type )
320            {
321                super( keyStoreSpi, provider, type );
322            }
323        }
324    
325        
326        /**
327         * loads the digital certificate either from a keystore file or from the admin entry in DIT
328         */
329        private void loadKeyStore() throws Exception
330        {
331            if ( StringTools.isEmpty( keystoreFile ) )
332            {
333                Provider provider = Security.getProvider( "SUN" );
334                LOG.debug( "provider = {}", provider );
335                CoreKeyStoreSpi coreKeyStoreSpi = new CoreKeyStoreSpi( getDirectoryService() );
336                keyStore = new KeyStore( coreKeyStoreSpi, provider, "JKS" ) {};
337                
338                try
339                {
340                    keyStore.load( null, null );
341                }
342                catch ( Exception e )
343                {
344                    // nothing really happens with this keystore
345                }
346            }
347            else
348            {
349                keyStore = KeyStore.getInstance( KeyStore.getDefaultType() );
350                FileInputStream fis = new FileInputStream( keystoreFile );
351                
352                keyStore.load( fis, null );
353            }
354        }
355    
356        
357        /**
358         * reloads the SSL context by replacing the existing SslFilter
359         * with a new SslFilter after reloading the keystore.
360         * 
361         * Note: should be called to reload the keystore after changing the digital certificate.
362         */
363        public void reloadSslContext() throws Exception
364        {
365            if( !started )
366            {
367                return;
368            }
369    
370            LOG.info( "reloading SSL context..." );
371            
372            loadKeyStore();
373            
374            DefaultIoFilterChainBuilder dfcb = ( ( DefaultIoFilterChainBuilder ) chainBuilder );
375            String sslFilterName = "sslFilter";
376            if( dfcb.contains( sslFilterName ) )
377            {
378                DefaultIoFilterChainBuilder newChain = ( DefaultIoFilterChainBuilder ) LdapsInitializer.init( keyStore, certificatePassword );
379                dfcb.replace( sslFilterName, newChain.get( sslFilterName ) );
380                newChain = null;
381            }
382    
383            StartTlsHandler handler = ( StartTlsHandler ) getExtendedOperationHandler( StartTlsHandler.EXTENSION_OID );
384            if( handler != null )
385            {
386                //FIXME dirty hack. IMO StartTlsHandler's code requires a cleanup
387                // cause the keystore loading and sslcontext creation code is duplicated
388                // both in the LdapService as well as StatTlsHandler
389                handler.setLdapServer( this );
390            }
391            
392            LOG.info( "reloaded SSL context successfully" );
393        }
394    
395        
396        /**
397         * @throws IOException if we cannot bind to the specified port
398         * @throws NamingException if the LDAP server cannot be started
399         */
400        public void start() throws Exception
401        {
402            if ( ! isEnabled() )
403            {
404                return;
405            }
406    
407            for ( Transport transport:transports )
408            {
409                if ( !(transport instanceof TcpTransport ) )
410                {
411                    LOG.warn( "Cannot listen on an UDP transport : {}", transport );
412                    continue;
413                }
414                
415                IoFilterChainBuilder chain;
416                
417                if ( transport.isSSLEnabled() )
418                {
419                    loadKeyStore();
420                    chain = LdapsInitializer.init( keyStore, certificatePassword );
421                }
422                else
423                {
424                    chain = new DefaultIoFilterChainBuilder();
425                }
426                
427                // Inject the codec into the chain
428                ((DefaultIoFilterChainBuilder)chain).addLast( "codec", 
429                        new ProtocolCodecFilter( this.getProtocolCodecFactory() ) );
430                
431                // Now inject an ExecutorFilter for the write operations
432                // We use the same number of thread than the number of IoProcessor
433                // (NOTE : this has to be double checked)
434                ((DefaultIoFilterChainBuilder)chain).addLast( "executor", 
435                        new ExecutorFilter( 
436                            new UnorderedThreadPoolExecutor( transport.getNbThreads() ),
437                            IoEventType.MESSAGE_RECEIVED ) );
438    
439                /*
440                 * The server is now initialized, we can
441                 * install the default requests handlers, which need 
442                 * access to the DirectoryServer instance.
443                 */ 
444                installDefaultHandlers();      
445    
446                startNetwork( transport, chain );
447            }
448            
449            started = true;
450            
451            LOG.info( "Ldap service started." );
452        }
453    
454    
455        /**
456         * {@inheritDoc}
457         */
458        public void stop()
459        {
460            try
461            {
462                for ( Transport transport:transports )
463                {
464                    if ( !(transport instanceof TcpTransport ) )
465                    {
466                        continue;
467                    }
468                    
469                    // we should unbind the service before we begin sending the notice
470                    // of disconnect so new connections are not formed while we process
471                    List<WriteFuture> writeFutures = new ArrayList<WriteFuture>();
472        
473                    // If the socket has already been unbound as with a successful
474                    // GracefulShutdownRequest then this will complain that the service
475                    // is not bound - this is ok because the GracefulShutdown has already
476                    // sent notices to to the existing active sessions
477                    List<IoSession> sessions;
478        
479                    try
480                    {
481                        sessions = new ArrayList<IoSession>(
482                                getSocketAcceptor( transport ).getManagedSessions().values() );
483                    }
484                    catch ( IllegalArgumentException e )
485                    {
486                        LOG.warn( "Seems like the LDAP service (" + getPort() + ") has already been unbound." );
487                        return;
488                    }
489        
490                    getSocketAcceptor( transport ).dispose();
491        
492                    if ( LOG.isInfoEnabled() )
493                    {
494                        LOG.info( "Unbind of an LDAP service (" + getPort() + ") is complete." );
495                        LOG.info( "Sending notice of disconnect to existing clients sessions." );
496                    }
497        
498                    // Send Notification of Disconnection messages to all connected clients.
499                    if ( sessions != null )
500                    {
501                        for ( IoSession session:sessions )
502                        {
503                            writeFutures.add( session.write( NoticeOfDisconnect.UNAVAILABLE ) );
504                        }
505                    }
506        
507                    // And close the connections when the NoDs are sent.
508                    Iterator<IoSession> sessionIt = sessions.iterator();
509        
510                    for ( WriteFuture future:writeFutures )
511                    {
512                        future.await( 1000L );
513                        sessionIt.next().close( true );
514                    }
515                }
516            }
517            catch ( Exception e )
518            {
519                LOG.warn( "Failed to sent NoD.", e );
520            }
521    
522            LOG.info( "Ldap service stopped." );
523        }
524    
525    
526        private void startNetwork( Transport transport, IoFilterChainBuilder chainBuilder )
527            throws Exception
528        {
529            if ( transport.getBackLog() < 0 ) 
530            {
531                // Set the backlog to the default value when it's below 0
532                transport.setBackLog( 50 );
533            }
534            
535            this.chainBuilder = chainBuilder;
536            
537            PartitionNexus nexus = getDirectoryService().getPartitionNexus();
538    
539            for ( ExtendedOperationHandler h : extendedOperationHandlers )
540            {
541                LOG.info( "Added Extended Request Handler: " + h.getOid() );
542                h.setLdapServer( this );
543                nexus.registerSupportedExtensions( h.getExtensionOids() );
544            }
545    
546            nexus.registerSupportedSaslMechanisms( saslMechanismHandlers.keySet() );
547    
548            try
549            {
550                SocketAcceptor acceptor = getSocketAcceptor( transport );
551                
552                // Now, configure the acceptor
553                // Disable the disconnection of the clients on unbind
554                acceptor.setCloseOnDeactivation( false );
555                
556                // Allow the port to be reused even if the socket is in TIME_WAIT state
557                acceptor.setReuseAddress( true );
558                
559                // No Nagle's algorithm
560                acceptor.getSessionConfig().setTcpNoDelay( true );
561                
562                // Inject the chain
563                acceptor.setFilterChainBuilder( chainBuilder );
564                
565                // Inject the protocol handler
566                acceptor.setHandler( getHandler() );
567                
568                // Bind to the configured address
569                acceptor.bind();
570                
571                // We are done !
572                started = true;
573    
574                if ( LOG.isInfoEnabled() )
575                {
576                    LOG.info( "Successful bind of an LDAP Service (" + transport.getPort() + ") is completed." );
577                }
578            }
579            catch ( IOException e )
580            {
581                String msg = I18n.err( I18n.ERR_171, transport.getPort() );
582                LdapConfigurationException lce = new LdapConfigurationException( msg );
583                lce.setCause( e );
584                LOG.error( msg, e );
585                throw lce;
586            }
587        }
588    
589    
590        public String getName()
591        {
592            return SERVICE_NAME;
593        }
594    
595    
596        public IoHandler getHandler()
597        {
598            return handler;
599        }
600        
601        
602        public LdapSessionManager getLdapSessionManager()
603        {
604            return ldapSessionManager;
605        }
606        
607        
608        public ProtocolCodecFactory getProtocolCodecFactory()
609        {
610            return codecFactory;
611        }
612    
613        
614        // ------------------------------------------------------------------------
615        // Configuration Methods
616        // ------------------------------------------------------------------------
617    
618    
619        /**
620         * Registeres the specified {@link ExtendedOperationHandler} to this
621         * protocol provider to provide a specific LDAP extended operation.
622         *
623         * @param eoh an extended operation handler
624         * @throws NamingException on failure to add the handler
625         */
626        public void addExtendedOperationHandler( ExtendedOperationHandler eoh ) throws Exception
627        {
628            if ( started )
629            {
630                eoh.setLdapServer( this );
631                PartitionNexus nexus = getDirectoryService().getPartitionNexus();
632                nexus.registerSupportedExtensions( eoh.getExtensionOids() );
633            }
634            else
635            {
636                extendedOperationHandlers.add( eoh );
637            }
638        }
639    
640    
641        /**
642         * Deregisteres an {@link ExtendedOperationHandler} with the specified <tt>oid</tt>
643         * from this protocol provider.
644         *
645         * @param oid the numeric identifier for the extended operation associated with
646         * the handler to remove
647         */
648        public void removeExtendedOperationHandler( String oid )
649        {
650            // need to do something like this to make this work right
651            //            DefaultPartitionNexus nexus = getDirectoryService().getPartitionNexus();
652            //            nexus.unregisterSupportedExtensions( eoh.getExtensionOids() );
653    
654            ExtendedOperationHandler handler = null;
655            for ( ExtendedOperationHandler h : extendedOperationHandlers )
656            {
657                if ( h.getOid().equals( oid ) )
658                {
659                    handler = h;
660                    break;
661                }
662            }
663            extendedOperationHandlers.remove( handler );
664        }
665    
666    
667        /**
668         * Returns an {@link ExtendedOperationHandler} with the specified <tt>oid</tt>
669         * which is registered to this protocol provider.
670         *
671         * @param oid the oid of the extended request of associated with the extended
672         * request handler
673         * @return the exnteded operation handler
674         */
675        public ExtendedOperationHandler getExtendedOperationHandler( String oid )
676        {
677            for ( ExtendedOperationHandler h : extendedOperationHandlers )
678            {
679                if ( h.getOid().equals( oid ) )
680                {
681                    return h;
682                }
683            }
684    
685            return null;
686        }
687    
688    
689        /**
690         * Sets the mode for this LdapServer to accept requests with or without a
691         * TLS secured connection via either StartTLS extended operations or using
692         * LDAPS.
693         * 
694         * @param confidentialityRequired true to require confidentiality
695         */
696        public void setConfidentialityRequired( boolean confidentialityRequired )
697        {
698            this.confidentialityRequired = confidentialityRequired;
699        }
700    
701    
702        /**
703         * Gets whether or not TLS secured connections are required to perform 
704         * operations on this LdapServer.
705         * 
706         * @return true if TLS secured connections are required, false otherwise
707         */
708        public boolean isConfidentialityRequired()
709        {
710            return confidentialityRequired;
711        }
712    
713        
714        /**
715         * Returns <tt>true</tt> if LDAPS is enabled.
716         *
717         * @return True if LDAPS is enabled.
718         */
719        public boolean isEnableLdaps( Transport transport )
720        {
721            return transport.isSSLEnabled();
722        }
723    
724    
725        /**
726         * Returns <code>true</code> if anonymous access is allowed.
727         *
728         * @return True if anonymous access is allowed.
729         */
730        public boolean isAllowAnonymousAccess()
731        {
732            return allowAnonymousAccess;
733        }
734    
735    
736        /**
737         * Sets whether to allow anonymous access or not.
738         *
739         * @param enableAnonymousAccess Set <code>true</code> to allow anonymous access.
740         */
741        public void setAllowAnonymousAccess( boolean enableAnonymousAccess )
742        {
743            this.allowAnonymousAccess = enableAnonymousAccess;
744        }
745    
746    
747        /**
748         * Sets the maximum size limit in number of entries to return for search.
749         *
750         * @param maxSizeLimit the maximum number of entries to return for search
751         */
752        public void setMaxSizeLimit( long maxSizeLimit )
753        {
754            this.maxSizeLimit = maxSizeLimit;
755        }
756    
757    
758        /**
759         * Returns the maximum size limit in number of entries to return for search.
760         *
761         * @return The maximum size limit.
762         */
763        public long getMaxSizeLimit()
764        {
765            return maxSizeLimit;
766        }
767    
768    
769        /**
770         * Sets the maximum time limit in milliseconds to conduct a search.
771         *
772         * @param maxTimeLimit the maximum length of time in milliseconds for search
773         */
774        public void setMaxTimeLimit( int maxTimeLimit )
775        {
776            this.maxTimeLimit = maxTimeLimit;
777        }
778    
779    
780        /**
781         * Returns the maximum time limit in milliseconds to conduct a search.
782         *
783         * @return The maximum time limit in milliseconds for search
784         */
785        public int getMaxTimeLimit()
786        {
787            return maxTimeLimit;
788        }
789    
790    
791        /**
792         * Gets the {@link ExtendedOperationHandler}s.
793         *
794         * @return A collection of {@link ExtendedOperationHandler}s.
795         */
796        public Collection<ExtendedOperationHandler> getExtendedOperationHandlers()
797        {
798            return new ArrayList<ExtendedOperationHandler>( extendedOperationHandlers );
799        }
800    
801    
802        /**
803         * Sets the {@link ExtendedOperationHandler}s.
804         *
805         * @org.apache.xbean.Property nestedType="org.apache.directory.server.ldap.ExtendedOperationHandler"
806         *
807         * @param handlers A collection of {@link ExtendedOperationHandler}s.
808         */
809        public void setExtendedOperationHandlers( Collection<ExtendedOperationHandler> handlers )
810        {
811            this.extendedOperationHandlers.clear();
812            this.extendedOperationHandlers.addAll( handlers );
813        }
814    
815    
816        /**
817         * Returns the FQDN of this SASL host, validated during SASL negotiation.
818         *
819         * @return The FQDN of this SASL host, validated during SASL negotiation.
820         */
821        public String getSaslHost()
822        {
823            return saslHost;
824        }
825    
826    
827        /**
828         * Sets the FQDN of this SASL host, validated during SASL negotiation.
829         *
830         * @param saslHost The FQDN of this SASL host, validated during SASL negotiation.
831         */
832        public void setSaslHost( String saslHost )
833        {
834            this.saslHost = saslHost;
835        }
836    
837    
838        /**
839         * Returns the Kerberos principal name for this LDAP service, used by GSSAPI.
840         *
841         * @return The Kerberos principal name for this LDAP service, used by GSSAPI.
842         */
843        public String getSaslPrincipal()
844        {
845            return saslPrincipal;
846        }
847    
848    
849        /**
850         * Sets the Kerberos principal name for this LDAP service, used by GSSAPI.
851         *
852         * @param saslPrincipal The Kerberos principal name for this LDAP service, used by GSSAPI.
853         */
854        public void setSaslPrincipal( String saslPrincipal )
855        {
856            this.saslPrincipal = saslPrincipal;
857        }
858    
859    
860        /**
861         * Returns the quality-of-protection, used by DIGEST-MD5 and GSSAPI.
862         *
863         * @return The quality-of-protection, used by DIGEST-MD5 and GSSAPI.
864         */
865        public String getSaslQopString()
866        {
867            return saslQopString;
868        }
869    
870    
871        /**
872         * Returns the Set of quality-of-protection, used by DIGEST-MD5 and GSSAPI.
873         *
874         * @return The quality-of-protection, used by DIGEST-MD5 and GSSAPI.
875         */
876        public Set<String> getSaslQop()
877        {
878            return saslQop;
879        }
880    
881    
882        /**
883         * Returns the realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI.
884         *
885         * @return The realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI.
886         */
887        public List<String> getSaslRealms()
888        {
889            return saslRealms;
890        }
891    
892    
893        /**
894         * Sets the realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI.
895         *
896         * @org.apache.xbean.Property nestedType="java.lang.String"
897         *
898         * @param saslRealms The realms serviced by this SASL host, used by DIGEST-MD5 and GSSAPI.
899         */
900        public void setSaslRealms( List<String> saslRealms )
901        {
902            this.saslRealms = saslRealms;
903        }
904    
905    
906        /**
907         * @org.apache.xbean.Map flat="true" dups="replace" keyName="mech-name"
908         */
909        public Map<String, MechanismHandler> getSaslMechanismHandlers()
910        {
911            return saslMechanismHandlers;
912        }
913    
914        public void setSaslMechanismHandlers( Map<String, MechanismHandler> saslMechanismHandlers )
915        {
916            this.saslMechanismHandlers = saslMechanismHandlers;
917        }
918    
919    
920        public MechanismHandler addSaslMechanismHandler( String mechanism, MechanismHandler handler )
921        {
922            return this.saslMechanismHandlers.put( mechanism, handler );
923        }
924    
925    
926        public MechanismHandler removeSaslMechanismHandler( String mechanism )
927        {
928            return this.saslMechanismHandlers.remove( mechanism );
929        }
930    
931    
932        public MechanismHandler getMechanismHandler( String mechanism )
933        {
934            return this.saslMechanismHandlers.get( mechanism );
935        }
936    
937    
938        public Set<String> getSupportedMechanisms()
939        {
940            return saslMechanismHandlers.keySet();
941        }
942    
943    
944        public void setDirectoryService( DirectoryService directoryService )
945        {
946            super.setDirectoryService( directoryService );
947            this.codecFactory = new LdapProtocolCodecFactory( directoryService );
948        }
949    
950    
951        public Set<String> getSupportedControls()
952        {
953            return supportedControls;
954        }
955    
956    
957        /**
958         * @org.apache.xbean.Property hidden="true"
959         */
960        public void setSupportedControls( Set<String> supportedControls )
961        {
962            this.supportedControls = supportedControls;
963        }
964    
965    
966        public MessageHandler<InternalAbandonRequest> getAbandonHandler()
967        {
968            return abandonHandler;
969        }
970    
971    
972        /**
973         * @org.apache.xbean.Property hidden="true"
974         * @param abandonHandler The AbandonRequest handler
975         */
976        public void setAbandonHandler( LdapRequestHandler<InternalAbandonRequest> abandonHandler )
977        {
978            this.handler.removeReceivedMessageHandler( InternalAbandonRequest.class );
979            this.abandonHandler = abandonHandler;
980            this.abandonHandler.setLdapServer( this );
981            this.handler.addReceivedMessageHandler( InternalAbandonRequest.class, this.abandonHandler );
982        }
983    
984    
985        public LdapRequestHandler<InternalAddRequest> getAddHandler()
986        {
987            return addHandler;
988        }
989    
990    
991        /**
992         * @org.apache.xbean.Property hidden="true"
993         * @param abandonHandler The AddRequest handler
994         */
995        public void setAddHandler( LdapRequestHandler<InternalAddRequest> addHandler )
996        {
997            this.handler.removeReceivedMessageHandler( InternalAddRequest.class );
998            this.addHandler = addHandler;
999            this.addHandler.setLdapServer( this );
1000            this.handler.addReceivedMessageHandler( InternalAddRequest.class, this.addHandler );
1001        }
1002    
1003    
1004        public LdapRequestHandler<InternalBindRequest> getBindHandler()
1005        {
1006            return bindHandler;
1007        }
1008    
1009    
1010        /**
1011         * @org.apache.xbean.Property hidden="true"
1012         * @param abandonHandler The BindRequest handler
1013         */
1014        public void setBindHandler( LdapRequestHandler<InternalBindRequest> bindHandler )
1015        {
1016            this.bindHandler = bindHandler;
1017            this.bindHandler.setLdapServer( this );
1018    
1019            handler.removeReceivedMessageHandler( InternalBindRequest.class );
1020            handler.addReceivedMessageHandler( InternalBindRequest.class, this.bindHandler );
1021        }
1022    
1023    
1024        public LdapRequestHandler<InternalCompareRequest> getCompareHandler()
1025        {
1026            return compareHandler;
1027        }
1028    
1029    
1030        /**
1031         * @org.apache.xbean.Property hidden="true"
1032         * @param abandonHandler The CompareRequest handler
1033         */
1034        public void setCompareHandler( LdapRequestHandler<InternalCompareRequest> compareHandler )
1035        {
1036            this.handler.removeReceivedMessageHandler( InternalCompareRequest.class );
1037            this.compareHandler = compareHandler;
1038            this.compareHandler.setLdapServer( this );
1039            this.handler.addReceivedMessageHandler( InternalCompareRequest.class, this.compareHandler );
1040        }
1041    
1042    
1043        public LdapRequestHandler<InternalDeleteRequest> getDeleteHandler()
1044        {
1045            return deleteHandler;
1046        }
1047    
1048    
1049        /**
1050         * @org.apache.xbean.Property hidden="true"
1051         * @param abandonHandler The DeleteRequest handler
1052         */
1053        public void setDeleteHandler( LdapRequestHandler<InternalDeleteRequest> deleteHandler )
1054        {
1055            this.handler.removeReceivedMessageHandler( InternalDeleteRequest.class );
1056            this.deleteHandler = deleteHandler;
1057            this.deleteHandler.setLdapServer( this );
1058            this.handler.addReceivedMessageHandler( InternalDeleteRequest.class, this.deleteHandler );
1059        }
1060    
1061    
1062        public LdapRequestHandler<InternalExtendedRequest> getExtendedHandler()
1063        {
1064            return extendedHandler;
1065        }
1066    
1067    
1068        /**
1069         * @org.apache.xbean.Property hidden="true"
1070         * @param abandonHandler The ExtendedRequest handler
1071         */
1072        public void setExtendedHandler( LdapRequestHandler<InternalExtendedRequest> extendedHandler )
1073        {
1074            this.handler.removeReceivedMessageHandler( InternalExtendedRequest.class );
1075            this.extendedHandler = extendedHandler;
1076            this.extendedHandler.setLdapServer( this );
1077            this.handler.addReceivedMessageHandler( InternalExtendedRequest.class, this.extendedHandler );
1078        }
1079    
1080    
1081        public LdapRequestHandler<InternalModifyRequest> getModifyHandler()
1082        {
1083            return modifyHandler;
1084        }
1085    
1086    
1087        /**
1088         * @org.apache.xbean.Property hidden="true"
1089         * @param abandonHandler The ModifyRequest handler
1090         */
1091        public void setModifyHandler( LdapRequestHandler<InternalModifyRequest> modifyHandler )
1092        {
1093            this.handler.removeReceivedMessageHandler( InternalModifyRequest.class );
1094            this.modifyHandler = modifyHandler;
1095            this.modifyHandler.setLdapServer( this );
1096            this.handler.addReceivedMessageHandler( InternalModifyRequest.class, this.modifyHandler );
1097        }
1098    
1099    
1100        public LdapRequestHandler<InternalModifyDnRequest> getModifyDnHandler()
1101        {
1102            return modifyDnHandler;
1103        }
1104    
1105    
1106        /**
1107         * @org.apache.xbean.Property hidden="true"
1108         * @param abandonHandler The ModifyDNRequest handler
1109         */
1110        public void setModifyDnHandler( LdapRequestHandler<InternalModifyDnRequest> modifyDnHandler )
1111        {
1112            this.handler.removeReceivedMessageHandler( InternalModifyDnRequest.class );
1113            this.modifyDnHandler = modifyDnHandler;
1114            this.modifyDnHandler.setLdapServer( this );
1115            this.handler.addReceivedMessageHandler( InternalModifyDnRequest.class, this.modifyDnHandler );
1116        }
1117    
1118    
1119        public LdapRequestHandler<InternalSearchRequest> getSearchHandler()
1120        {
1121            return searchHandler;
1122        }
1123    
1124    
1125        /**
1126         * @org.apache.xbean.Property hidden="true"
1127         * @param abandonHandler The SearchRequest handler
1128         */
1129        public void setSearchHandler( LdapRequestHandler<InternalSearchRequest> searchHandler )
1130        {
1131            this.handler.removeReceivedMessageHandler( InternalSearchRequest.class );
1132            this.searchHandler = searchHandler;
1133            this.searchHandler.setLdapServer( this );
1134            this.handler.addReceivedMessageHandler( InternalSearchRequest.class, this.searchHandler );
1135        }
1136    
1137    
1138        public LdapRequestHandler<InternalUnbindRequest> getUnbindHandler()
1139        {
1140            return unbindHandler;
1141        }
1142        
1143        
1144        /**
1145         * @return The underlying TCP transport port, or -1 if no transport has been 
1146         * initialized
1147         */
1148        public int getPort()
1149        {
1150            if ( transports == null )
1151            {
1152                return -1;
1153            }
1154            
1155            for ( Transport transport:transports )
1156            {
1157                if ( transport instanceof UdpTransport )
1158                {
1159                    continue;
1160                }
1161                
1162                if ( !transport.isSSLEnabled() )
1163                {
1164                    return transport.getPort();
1165                }
1166            }
1167            
1168            return -1;
1169        }
1170    
1171    
1172        /**
1173         * @return The underlying SSL enabled TCP transport port, or -1 if no transport has been 
1174         * initialized
1175         */
1176        public int getPortSSL()
1177        {
1178            if ( transports == null )
1179            {
1180                return -1;
1181            }
1182            
1183            for ( Transport transport:transports )
1184            {
1185                if ( transport instanceof UdpTransport )
1186                {
1187                    continue;
1188                }
1189                
1190                if ( transport.isSSLEnabled() )
1191                {
1192                    return transport.getPort();
1193                }
1194            }
1195            
1196            return -1;
1197        }
1198    
1199        /**
1200         * @org.apache.xbean.Property hidden="true"
1201         * @param abandonHandler The UnbindRequest handler
1202         */
1203        public void setUnbindHandler( LdapRequestHandler<InternalUnbindRequest> unbindHandler )
1204        {
1205            this.handler.removeReceivedMessageHandler( InternalUnbindRequest.class );
1206            this.unbindHandler = unbindHandler;
1207            this.unbindHandler.setLdapServer( this );
1208            this.handler.addReceivedMessageHandler( InternalUnbindRequest.class, this.unbindHandler );
1209        }
1210    
1211    
1212        public boolean isStarted()
1213        {
1214            return started;
1215        }
1216    
1217    
1218        /**
1219         * @org.apache.xbean.Property hidden="true"
1220         */
1221        public void setStarted( boolean started )
1222        {
1223            this.started = started;
1224        }
1225    
1226    
1227        /**
1228         * @return The keystore path
1229         */
1230        public String getKeystoreFile()
1231        {
1232            return keystoreFile;
1233        }
1234    
1235    
1236        /**
1237         * Set the external keystore path
1238         * @param keystoreFile The external keystore path
1239         */
1240        public void setKeystoreFile( String keystoreFile )
1241        {
1242            this.keystoreFile = keystoreFile;
1243        }
1244    
1245    
1246        /**
1247         * @return The certificate passord
1248         */
1249        public String getCertificatePassword()
1250        {
1251            return certificatePassword;
1252        }
1253    
1254    
1255        /**
1256         * Set the certificate passord.
1257         * @param certificatePassword the certificate passord
1258         */
1259        public void setCertificatePassword( String certificatePassword )
1260        {
1261            this.certificatePassword = certificatePassword;
1262        }
1263    
1264    
1265        /**
1266         * @param replicationSystem the replicationSystem to set
1267         */
1268        public void setReplicationSystem( ReplicationSystem replicationSystem )
1269        {
1270            this.replicationSystem = replicationSystem;
1271        }
1272    
1273    
1274        /**
1275         * @return the replicationSystem
1276         */
1277        public ReplicationSystem getReplicationSystem()
1278        {
1279            return replicationSystem;
1280        }
1281        
1282        
1283        /**
1284         * @see Object#toString()
1285         */
1286        public String toString()
1287        {
1288            StringBuilder sb = new StringBuilder();
1289            
1290            sb.append( "LdapServer[" ).append( getServiceName() ).append( "], listening on :" ).append( '\n' );
1291            
1292            if ( getTransports() != null )
1293            {
1294                for ( Transport transport:getTransports() )
1295                {
1296                    sb.append( "    " ).append( transport ).append( '\n' );
1297                }
1298            }
1299            
1300            return sb.toString();
1301        }
1302    }