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.tools;
021    
022    
023    import java.util.Hashtable;
024    
025    import javax.naming.directory.SearchControls;
026    import javax.naming.event.EventContext;
027    import javax.naming.event.NamingExceptionEvent;
028    import javax.naming.ldap.InitialLdapContext;
029    import javax.naming.ldap.LdapContext;
030    import javax.naming.ldap.UnsolicitedNotification;
031    import javax.naming.ldap.UnsolicitedNotificationEvent;
032    import javax.naming.ldap.UnsolicitedNotificationListener;
033    
034    import org.apache.commons.cli.CommandLine;
035    import org.apache.commons.cli.Option;
036    import org.apache.commons.cli.Options;
037    import org.apache.directory.daemon.AvailablePortFinder;
038    import org.apache.directory.server.i18n.I18n;
039    import org.apache.directory.shared.asn1.codec.DecoderException;
040    import org.apache.directory.shared.ldap.message.extended.GracefulDisconnect;
041    import org.apache.directory.shared.ldap.message.extended.NoticeOfDisconnect;
042    
043    
044    /**
045     * Responds to unsolicited notifications by launching an external process.  Also 
046     * reconnects to the server an launches another process to notify that the server
047     * is back up.
048     * 
049     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
050     * @version $Rev: 434420 $
051     */
052    public class DisconnectNotificationCommand extends ToolCommand implements UnsolicitedNotificationListener
053    {
054        UnsolicitedNotification notification;
055        boolean canceled = false;
056        private String host = "localhost";
057        private int port = 10389;
058        private String bindDn = "uid=admin,ou=system";
059        private String password = "secret";
060    
061    
062        //    private String shutdownCommand = "echo"; 
063        //    private String[] shutdownCommandArgs = new String[] { 
064        //        "server $HOST:$PORT will shutdown for $OFFLINE minutes in $DELAY seconds" };
065    
066        protected DisconnectNotificationCommand()
067        {
068            super( "notifications" );
069        }
070    
071    
072        public void notificationReceived( UnsolicitedNotificationEvent evt )
073        {
074            notification = evt.getNotification();
075    
076            if ( notification.getID().equals( NoticeOfDisconnect.EXTENSION_OID ) )
077            {
078                System.out.println( "\nRecieved NoticeOfDisconnect: " + NoticeOfDisconnect.EXTENSION_OID );
079                System.out.println( "Expect to loose this connection without further information." );
080                canceled = true;
081            }
082            else if ( notification.getID().equals( GracefulDisconnect.EXTENSION_OID ) )
083            {
084                System.out.println( "Recieved GracefulDisconnect: " + GracefulDisconnect.EXTENSION_OID );
085                GracefulDisconnect gd = null;
086    
087                try
088                {
089                    gd = new GracefulDisconnect( notification.getEncodedValue() );
090                }
091                catch ( DecoderException de )
092                {
093                    // TODO Auto-generated catch block
094                    de.printStackTrace();
095                }
096    
097                System.out.println( "LDAP server will shutdown in " + gd.getDelay() + " seconds." );
098                System.out.println( "LDAP server will be back online in " + gd.getTimeOffline() + " minutes." );
099    
100                if ( gd.getDelay() > 0 )
101                {
102                    Thread t = new Thread( new Counter( gd.getDelay() ) );
103                    t.start();
104                }
105            }
106            else
107            {
108                System.out.println( "Unknown event recieved with OID: " + evt.getNotification().getID() );
109            }
110        }
111    
112    
113        public void namingExceptionThrown( NamingExceptionEvent evt )
114        {
115            canceled = true;
116            System.out.println( "Got an excption event: " + evt.getException().getLocalizedMessage() );
117            System.out.println( "Process shutting down abruptly." );
118            System.exit( 1 );
119        }
120    
121        class Counter implements Runnable
122        {
123            int delay;
124    
125    
126            Counter( int delay )
127            {
128                this.delay = delay;
129            }
130    
131    
132            public void run()
133            {
134                System.out.println( "Starting countdown until server shutdown:" );
135                System.out.print( "[" );
136                long delayMillis = delay * 1000 - 1000; // 1000 is for setup costs
137                long startTime = System.currentTimeMillis();
138                while ( System.currentTimeMillis() - startTime < delayMillis && !canceled )
139                {
140                    try
141                    {
142                        Thread.sleep( 1000 );
143                    }
144                    catch ( InterruptedException e )
145                    {
146                    }
147                    System.out.print( "." );
148                }
149    
150                if ( canceled )
151                {
152                    System.out.println( " -- countdown canceled -- " );
153                }
154                else
155                {
156                    System.out.println( "]" );
157                    System.out.println( "Client shutting down gracefully." );
158                    System.exit( 0 );
159                }
160            }
161        }
162    
163    
164        public void execute( CommandLine cmd ) throws Exception
165        {
166            processOptions( cmd );
167    
168            Hashtable env = new Hashtable();
169            env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
170            env.put( "java.naming.provider.url", "ldap://" + host + ":" + port );
171            env.put( "java.naming.security.principal", bindDn );
172            env.put( "java.naming.security.credentials", password );
173            env.put( "java.naming.security.authentication", "simple" );
174    
175            LdapContext ctx = new InitialLdapContext( env, null );
176            ctx = ctx.newInstance( null );
177            UnsolicitedNotificationListener listener = new DisconnectNotificationCommand();
178            ( ( EventContext ) ctx ).addNamingListener( "", SearchControls.SUBTREE_SCOPE, listener );
179    
180            System.out.println( "Listening for notifications." );
181            System.out.println( "Press any key to terminate." );
182            System.in.read();
183            ctx.close();
184            System.out.println( "Process terminated!!!" );
185        }
186    
187    
188        private void processOptions( CommandLine cmd )
189        {
190            if ( isDebugEnabled() )
191            {
192                System.out.println( "Processing options for disconnect notifications ..." );
193            }
194    
195            // -------------------------------------------------------------------
196            // figure out and error check the port value
197            // -------------------------------------------------------------------
198    
199            if ( cmd.hasOption( 'p' ) ) // - user provided port w/ -p takes precedence
200            {
201                String val = cmd.getOptionValue( 'p' );
202                try
203                {
204                    port = Integer.parseInt( val );
205                }
206                catch ( NumberFormatException e )
207                {
208                    System.err.println( I18n.err( I18n.ERR_193, val ) );
209                    System.exit( 1 );
210                }
211    
212                if ( port > AvailablePortFinder.MAX_PORT_NUMBER )
213                {
214                    System.err.println( I18n.err( I18n.ERR_194, val, AvailablePortFinder.MAX_PORT_NUMBER ) );
215                    System.exit( 1 );
216                }
217                else if ( port < AvailablePortFinder.MIN_PORT_NUMBER )
218                {
219                    System.err.println( I18n.err( I18n.ERR_195, val, AvailablePortFinder.MIN_PORT_NUMBER ) );
220                    System.exit( 1 );
221                }
222    
223                if ( isDebugEnabled() )
224                {
225                    System.out.println( "port overriden by -p option: " + port );
226                }
227            }
228            else if ( getApacheDS() != null )
229            {
230                port = getApacheDS().getLdapServer().getPort();
231    
232                if ( isDebugEnabled() )
233                {
234                    System.out.println( "port overriden by server.xml configuration: " + port );
235                }
236            }
237            else if ( isDebugEnabled() )
238            {
239                System.out.println( "port set to default: " + port );
240            }
241    
242            // -------------------------------------------------------------------
243            // figure out the host value
244            // -------------------------------------------------------------------
245    
246            if ( cmd.hasOption( 'h' ) )
247            {
248                host = cmd.getOptionValue( 'h' );
249    
250                if ( isDebugEnabled() )
251                {
252                    System.out.println( "host overriden by -h option: " + host );
253                }
254            }
255            else if ( isDebugEnabled() )
256            {
257                System.out.println( "host set to default: " + host );
258            }
259    
260            // -------------------------------------------------------------------
261            // figure out the password value
262            // -------------------------------------------------------------------
263    
264            if ( cmd.hasOption( 'w' ) )
265            {
266                password = cmd.getOptionValue( 'w' );
267    
268                if ( isDebugEnabled() )
269                {
270                    System.out.println( "password overriden by -w option: " + password );
271                }
272            }
273            else if ( isDebugEnabled() )
274            {
275                System.out.println( "password set to default: " + password );
276            }
277    
278            // -------------------------------------------------------------------
279            // figure out the binddn value
280            // -------------------------------------------------------------------
281    
282            if ( cmd.hasOption( 'u' ) )
283            {
284                bindDn = cmd.getOptionValue( 'u' );
285    
286                if ( isDebugEnabled() )
287                {
288                    System.out.println( "binddn overriden by -u option: " + bindDn );
289                }
290            }
291            else if ( isDebugEnabled() )
292            {
293                System.out.println( "binddn set to default: " + bindDn );
294            }
295        }
296    
297    
298        public Options getOptions()
299        {
300            Options opts = new Options();
301            Option op = new Option( "h", "host", true, "server host: defaults to localhost" );
302            op.setRequired( false );
303            opts.addOption( op );
304            op = new Option( "p", "port", true, "server port: defaults to 10389 or server.xml specified port" );
305            op.setRequired( false );
306            opts.addOption( op );
307            op = new Option( "w", "password", true, "the apacheds administrator's password: defaults to secret" );
308            op.setRequired( false );
309            opts.addOption( op );
310            op = new Option( "u", "binddn", true, "an apacheds user's dn: defaults to " + bindDn );
311            op.setRequired( false );
312            opts.addOption( op );
313            op = new Option( "i", "install-path", true, "path to apacheds installation directory" );
314            op.setRequired( false );
315            opts.addOption( op );
316            return opts;
317        }
318    }