Package org.apache.directory.studio.connection.core.io.jndi

Source Code of org.apache.directory.studio.connection.core.io.jndi.JNDIConnectionWrapper$InnerRunnable

/*
*  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.studio.connection.core.io.jndi;


import java.util.Hashtable;

import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.InsufficientResourcesException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.ServiceUnavailableException;
import javax.naming.directory.Attributes;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.StartTlsRequest;
import javax.naming.ldap.StartTlsResponse;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

import org.apache.directory.studio.connection.core.Connection;
import org.apache.directory.studio.connection.core.ConnectionCorePlugin;
import org.apache.directory.studio.connection.core.ConnectionParameter;
import org.apache.directory.studio.connection.core.StudioProgressMonitor;
import org.apache.directory.studio.connection.core.IAuthHandler;
import org.apache.directory.studio.connection.core.ICredentials;
import org.apache.directory.studio.connection.core.Messages;
import org.apache.directory.studio.connection.core.io.ConnectionWrapper;


/**
* A connection wrapper that uses JNDI.
*
* - asychron + cancelable
* - SSL certificate
* - manages broken/closed connections
* - delete old RDN
* - exception handling
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$, $Date$
*/
public class JNDIConnectionWrapper implements ConnectionWrapper
{
    private Connection connection;

    private boolean useLdaps;

    private boolean useStartTLS;

    private String authMethod;

    private String bindPrincipal;

    private String bindCredentials;

    private Hashtable<String, String> environment;

    private InitialLdapContext context;

    private boolean isConnected;

    private Thread jobThread;


    /**
     * Creates a new instance of JNDIConnectionContext.
     *
     * @param connection the connection
     */
    public JNDIConnectionWrapper( Connection connection )
    {
        this.connection = connection;
    }


    /**
     * @see org.apache.directory.studio.connection.core.io.ConnectionWrapper#connect(org.apache.directory.studio.connection.core.StudioProgressMonitor)
     */
    public void connect( StudioProgressMonitor monitor )
    {
        context = null;
        isConnected = false;
        jobThread = null;

        try
        {
            doConnect( monitor );
        }
        catch ( NamingException ne )
        {
            disconnect();
            monitor.reportError( ne.getMessage(), ne );
        }
    }


    /**
     * @see org.apache.directory.studio.connection.core.io.ConnectionWrapper#disconnect()
     */
    public void disconnect()
    {
        if ( jobThread != null )
        {
            Thread t = jobThread;
            jobThread = null;
            t.interrupt();
        }
        if ( context != null )
        {
            try
            {
                context.close();
            }
            catch ( NamingException e )
            {
                // ignore
            }
            context = null;
        }
        isConnected = false;
        System.gc();
    }


    /**
     * @see org.apache.directory.studio.connection.core.io.ConnectionWrapper#bind(org.apache.directory.studio.connection.core.StudioProgressMonitor)
     */
    public void bind( StudioProgressMonitor monitor )
    {
        try
        {
            doBind( monitor );
        }
        catch ( NamingException ne )
        {
            disconnect();
            monitor.reportError( ne.getMessage(), ne );
        }
    }


    /**
     * @see org.apache.directory.studio.connection.core.io.ConnectionWrapper#unbind()
     */
    public void unbind()
    {
        disconnect();
    }


    /**
     * @see org.apache.directory.studio.connection.core.io.ConnectionWrapper#isConnected()
     */
    public boolean isConnected()
    {
        return context != null;
    }


    /**
     * Search.
     *
     * @param searchBase the search base
     * @param filter the filter
     * @param searchControls the controls
     * @param derefAliasMethod the deref alias method
     * @param handleReferralsMethod the handle referrals method
     * @param controls the ldap controls
     * @param monitor the progress monitor
     *
     * @return the naming enumeration or null if an exception occurs.
     */
    public NamingEnumeration search( final String searchBase, final String filter, final SearchControls searchControls,
        final String derefAliasMethod, final String handleReferralsMethod, final Control[] controls,
        final StudioProgressMonitor monitor )
    {
        // start
        InnerRunnable runnable = new InnerRunnable()
        {
            private NamingEnumeration namingEnumeration = null;
            private NamingException namingException = null;


            public void run()
            {
                try
                {
                    LdapContext searchCtx = context.newInstance( controls );
                    try
                    {
                        searchCtx.addToEnvironment( "java.naming.ldap.derefAliases", derefAliasMethod ); //$NON-NLS-1$
                        searchCtx.addToEnvironment( Context.REFERRAL, handleReferralsMethod );

                    }
                    catch ( NamingException e )
                    {
                        namingException = e;
                    }

                    try
                    {
                        namingEnumeration = searchCtx.search( new LdapName( searchBase ), filter, searchControls );
                    }
                    catch ( NamingException ne )
                    {
                        namingException = ne;
                    }

                }
                catch ( NamingException e )
                {
                    namingException = e;
                }
            }


            public NamingException getException()
            {
                return namingException;
            }


            public Object getResult()
            {
                return namingEnumeration;
            }


            public void reset()
            {
                namingEnumeration = null;
                namingException = null;
            }

        };

        try
        {
            checkConnectionAndRunAndMonitor( runnable, monitor );
        }
        catch ( NamingException ne )
        {
            monitor.reportError( ne.getMessage(), ne );
            return null;
        }

        if ( runnable.getException() != null )
        {
            monitor.reportError( runnable.getException().getMessage(), runnable.getException() );
            return null;
        }
        else if ( runnable.getResult() != null && runnable.getResult() instanceof NamingEnumeration )
        {
            return ( NamingEnumeration ) runnable.getResult();
        }
        else
        {
            return null;
        }
    }


    /**
     * Modify attributes.
     *
     * @param dn the dn
     * @param modificationItems the modification items
     * @param controls the controls
     * @param monitor the progress monitor
     */
    public void modifyAttributes( final String dn, final ModificationItem[] modificationItems,
        final Control[] controls, final StudioProgressMonitor monitor )
    {
        InnerRunnable runnable = new InnerRunnable()
        {
            private NamingException namingException = null;


            public void run()
            {
                try
                {
                    LdapContext modCtx = context.newInstance( controls );
                    modCtx.addToEnvironment( Context.REFERRAL, "throw" ); //$NON-NLS-1$

                    modCtx.modifyAttributes( new LdapName( dn ), modificationItems );
                }
                catch ( NamingException ne )
                {
                    namingException = ne;
                }
            }


            public NamingException getException()
            {
                return namingException;
            }


            public Object getResult()
            {
                return null;
            }


            public void reset()
            {
                namingException = null;
            }
        };

        try
        {
            checkConnectionAndRunAndMonitor( runnable, monitor );
        }
        catch ( NamingException ne )
        {
            monitor.reportError( ne.getMessage(), ne );
        }

        if ( runnable.getException() != null )
        {
            monitor.reportError( runnable.getException().getMessage(), runnable.getException() );
        }
    }


    /**
     * Renames an entry.
     *
     * @param oldDn the old dn
     * @param newDn the new dn
     * @param deleteOldRdn the delete old rdn flag
     * @param controls the controls
     * @param monitor the monitor
     */
    public void rename( final String oldDn, final String newDn, final boolean deleteOldRdn, final Control[] controls,
        final StudioProgressMonitor monitor )
    {
        InnerRunnable runnable = new InnerRunnable()
        {
            private NamingException namingException = null;


            // TODO: delteOldRdn

            public void run()
            {
                try
                {
                    LdapContext modCtx = context.newInstance( controls );
                    modCtx.addToEnvironment( Context.REFERRAL, "throw" ); //$NON-NLS-1$
                    modCtx.rename( new LdapName( oldDn ), new LdapName( newDn ) );
                }
                catch ( NamingException ne )
                {
                    namingException = ne;
                }
            }


            public NamingException getException()
            {
                return namingException;
            }


            public Object getResult()
            {
                return null;
            }


            public void reset()
            {
                namingException = null;
            }
        };

        try
        {
            checkConnectionAndRunAndMonitor( runnable, monitor );
        }
        catch ( NamingException ne )
        {
            monitor.reportError( ne.getMessage(), ne );
        }

        if ( runnable.getException() != null )
        {
            monitor.reportError( runnable.getException().getMessage(), runnable.getException() );
        }
    }


    /**
     * Creates an entry.
     *
     * @param dn the dn
     * @param attributes the attributes
     * @param controls the controls
     * @param monitor the monitor
     */
    public void createEntry( final String dn, final Attributes attributes, final Control[] controls,
        final StudioProgressMonitor monitor )
    {
        InnerRunnable runnable = new InnerRunnable()
        {
            private NamingException namingException = null;


            public void run()
            {
                try
                {
                    LdapContext modCtx = context.newInstance( controls );
                    modCtx.addToEnvironment( Context.REFERRAL, "throw" ); //$NON-NLS-1$
                    modCtx.createSubcontext( new LdapName( dn ), attributes );
                }
                catch ( NamingException ne )
                {
                    namingException = ne;
                }
            }


            public NamingException getException()
            {
                return namingException;
            }


            public Object getResult()
            {
                return null;
            }


            public void reset()
            {
                namingException = null;
            }
        };

        try
        {
            checkConnectionAndRunAndMonitor( runnable, monitor );
        }
        catch ( NamingException ne )
        {
            monitor.reportError( ne.getMessage(), ne );
        }

        if ( runnable.getException() != null )
        {
            monitor.reportError( runnable.getException().getMessage(), runnable.getException() );
        }
    }


    /**
     * Deletes an entry.
     *
     * @param dn the dn
     * @param controls the controls
     * @param monitor the monitor
     */
    public void deleteEntry( final String dn, final Control[] controls, final StudioProgressMonitor monitor )
    {
        InnerRunnable runnable = new InnerRunnable()
        {
            private NamingException namingException = null;


            public void run()
            {
                try
                {
                    LdapContext modCtx = context.newInstance( controls );
                    modCtx.addToEnvironment( Context.REFERRAL, "throw" ); //$NON-NLS-1$

                    modCtx.destroySubcontext( new LdapName( dn ) );
                }
                catch ( NamingException ne )
                {
                    namingException = ne;
                }
            }


            public NamingException getException()
            {
                return namingException;
            }


            public Object getResult()
            {
                return null;
            }


            public void reset()
            {
                namingException = null;
            }
        };

        try
        {
            checkConnectionAndRunAndMonitor( runnable, monitor );
        }
        catch ( NamingException ne )
        {
            monitor.reportError( ne.getMessage(), ne );
        }

        if ( runnable.getException() != null )
        {
            monitor.reportError( runnable.getException().getMessage(), runnable.getException() );
        }
    }


    private void doConnect( final StudioProgressMonitor monitor ) throws NamingException
    {
        context = null;
        isConnected = true;

        // setup connection parameters
        String host = connection.getConnectionParameter().getHost();
        int port = connection.getConnectionParameter().getPort();
       
        useLdaps = connection.getConnectionParameter().getEncryptionMethod() == ConnectionParameter.EncryptionMethod.LDAPS;
        useStartTLS = connection.getConnectionParameter().getEncryptionMethod() == ConnectionParameter.EncryptionMethod.START_TLS;
       
        environment = new Hashtable<String, String>();
        environment.put( Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory" ); //$NON-NLS-1$
        environment.put( "java.naming.ldap.version", "3" ); //$NON-NLS-1$ //$NON-NLS-2$
       
        // timeouts
        if ( !useLdaps )
        {
            environment.put( "com.sun.jndi.ldap.connect.timeout", "10000" ); //$NON-NLS-1$ //$NON-NLS-2$
        }
        environment.put( "com.sun.jndi.dns.timeout.initial", "2000" ); //$NON-NLS-1$ //$NON-NLS-2$
        environment.put( "com.sun.jndi.dns.timeout.retries", "3" ); //$NON-NLS-1$ //$NON-NLS-2$
       
        // ldaps://
        if ( useLdaps )
        {
            environment.put( Context.PROVIDER_URL, "ldaps://" + host + ":" + port ); //$NON-NLS-1$ //$NON-NLS-2$
            environment.put( Context.SECURITY_PROTOCOL, "ssl" ); //$NON-NLS-1$
            environment.put( "java.naming.ldap.factory.socket", DummySSLSocketFactory.class.getName() ); //$NON-NLS-1$
        }
        else
        {
            environment.put( Context.PROVIDER_URL, "ldap://" + host + ":" + port ); //$NON-NLS-1$ //$NON-NLS-2$
        }
       
        InnerRunnable runnable = new InnerRunnable()
        {
            private NamingException namingException = null;


            public void run()
            {
                try
                {
                    context = new InitialLdapContext( environment, null );

                    if ( useStartTLS )
                    {
                        try
                        {
                            StartTlsResponse tls = ( StartTlsResponse ) context
                                .extendedOperation( new StartTlsRequest() );
                            tls.setHostnameVerifier( new HostnameVerifier()
                            {
                                public boolean verify( String arg0, SSLSession arg1 )
                                {
                                    return true;
                                }
                            } );
                            tls.negotiate( new DummySSLSocketFactory() );

                        }
                        catch ( Exception e )
                        {
                            namingException = new NamingException( e.getMessage() != null ? e.getMessage()
                                : "Error while establishing TLS session" ); //$NON-NLS-1$
                            namingException.setRootCause( e );
                            context.close();
                        }
                    }
                }
                catch ( NamingException ne )
                {
                    namingException = ne;
                }
            }


            public NamingException getException()
            {
                return namingException;
            }


            public Object getResult()
            {
                return null;
            }


            public void reset()
            {
                namingException = null;
            }
        };

        runAndMonitor( runnable, monitor );

        if ( runnable.getException() != null )
        {
            throw runnable.getException();
        }
        else if ( context != null )
        {
            // all OK
        }
        else
        {
            throw new NamingException( "???" ); //$NON-NLS-1$
        }
    }


    private void doBind( final StudioProgressMonitor monitor ) throws NamingException
    {
        if ( context != null && isConnected )
        {
            // setup authentication methdod
            authMethod = "none";
            if ( connection.getConnectionParameter().getAuthMethod() == ConnectionParameter.AuthenticationMethod.SIMPLE )
            {
                authMethod = "simple";
            }
            else if ( connection.getConnectionParameter().getAuthMethod() == ConnectionParameter.AuthenticationMethod.SASL_DIGEST_MD5 )
            {
                authMethod = "DIGEST-MD5";
            }
            else if ( connection.getConnectionParameter().getAuthMethod() == ConnectionParameter.AuthenticationMethod.SASL_CRAM_MD5 )
            {
                authMethod = "CRAM-MD5";
            }

            // setup credentials
            IAuthHandler authHandler = ConnectionCorePlugin.getDefault().getAuthHandler();
            if ( authHandler == null )
            {
                monitor.reportError( Messages.model__no_auth_handler, new Exception() );
            }
            ICredentials credentials = authHandler.getCredentials( connection.getConnectionParameter() );
            if ( credentials == null )
            {
                monitor.reportError( Messages.model__no_credentials, new Exception() );
            }
            bindPrincipal = credentials.getBindPrincipal();
            bindCredentials = credentials.getBindPassword();
           
            InnerRunnable runnable = new InnerRunnable()
            {
                private NamingException namingException = null;


                public void run()
                {
                    try
                    {
                        context.removeFromEnvironment( Context.SECURITY_AUTHENTICATION );
                        context.removeFromEnvironment( Context.SECURITY_PRINCIPAL );
                        context.removeFromEnvironment( Context.SECURITY_CREDENTIALS );

                        context.addToEnvironment( Context.SECURITY_PRINCIPAL, bindPrincipal );
                        context.addToEnvironment( Context.SECURITY_CREDENTIALS, bindCredentials );
                        context.addToEnvironment( Context.SECURITY_AUTHENTICATION, authMethod );

                        context.reconnect( context.getConnectControls() );
                    }
                    catch ( NamingException ne )
                    {
                        namingException = ne;
                    }
                }


                public NamingException getException()
                {
                    return namingException;
                }


                public Object getResult()
                {
                    return null;
                }


                public void reset()
                {
                    namingException = null;
                }
            };

            runAndMonitor( runnable, monitor );

            if ( runnable.getException() != null )
            {
                throw runnable.getException();
            }
            else if ( context != null )
            {
                // all OK
            }
            else
            {
                throw new NamingException( "???" ); //$NON-NLS-1$
            }

        }
        else
        {
            throw new NamingException( "No connection" );
        }
    }


    private void checkConnectionAndRunAndMonitor( final InnerRunnable runnable, final StudioProgressMonitor monitor )
        throws NamingException
    {
        // check connection
        if ( !isConnected || context == null )
        {
            doConnect( monitor );
            doBind( monitor );
        }
        if ( context == null )
        {
            throw new NamingException( "No connection" );
        }

        // loop for reconnection
        for ( int i = 0; i <= 1; i++ )
        {
            runAndMonitor( runnable, monitor );

            // check reconnection
            if ( i == 0
                && runnable.getException() != null
                && ( ( runnable.getException() instanceof CommunicationException )
                    || ( runnable.getException() instanceof ServiceUnavailableException ) || ( runnable.getException() instanceof InsufficientResourcesException ) ) )
            {

                doConnect( monitor );
                doBind( monitor );
                runnable.reset();
            }
            else
            {
                break;
            }
        }
    }


    private void runAndMonitor( final InnerRunnable runnable, final StudioProgressMonitor monitor )
        throws CancelException
    {
        if ( !monitor.isCanceled() )
        {
            // monitor
            StudioProgressMonitor.CancelListener listener = new StudioProgressMonitor.CancelListener()
            {
                public void cancelRequested( StudioProgressMonitor.CancelEvent event )
                {
                    if ( monitor.isCanceled() )
                    {
                        if ( jobThread.isAlive() )
                        {
                            jobThread.interrupt();
                        }
                        if ( context != null )
                        {
                            try
                            {
                                context.close();
                            }
                            catch ( NamingException ne )
                            {
                            }
                            isConnected = false;
                            context = null;
                            System.gc();
                        }
                        isConnected = false;
                    }
                }
            };
            monitor.addCancelListener( listener );
            jobThread = Thread.currentThread();

            // run
            try
            {
                // try {
                // Thread.sleep(5000);
                // } catch (InterruptedException e) {
                // System.out.println(System.currentTimeMillis() + ": sleep
                // interrupted!");
                // }
                // System.out.println(System.currentTimeMillis() + ": " +
                // runnable);

                runnable.run();
            }
            finally
            {
                monitor.removeCancelListener( listener );
                jobThread = null;
            }

            if ( monitor.isCanceled() )
            {
                throw new CancelException();
            }
        }
    }

    interface InnerRunnable extends Runnable
    {

        /**
         * Gets the exception.
         *
         * @return the exception
         */
        NamingException getException();


        /**
         * Gets the result.
         *
         * @return the result
         */
        Object getResult();


        /**
         * Reset.
         */
        void reset();
    }

}
TOP

Related Classes of org.apache.directory.studio.connection.core.io.jndi.JNDIConnectionWrapper$InnerRunnable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.