View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.tools;
21  
22  
23  import java.util.Hashtable;
24  
25  import javax.naming.CommunicationException;
26  import javax.naming.ldap.InitialLdapContext;
27  import javax.naming.ldap.LdapContext;
28  
29  import org.apache.commons.cli.CommandLine;
30  import org.apache.commons.cli.Option;
31  import org.apache.commons.cli.Options;
32  import org.apache.directory.daemon.AvailablePortFinder;
33  import org.apache.directory.shared.ldap.message.extended.GracefulShutdownRequest;
34  
35  
36  /**
37   * A command used to send a graceful disconnect to established clients 
38   * while allowing them time to complete operations already in progress.
39   * 
40   * @see <a href="http://docs.safehaus.org/display/APACHEDS/LDAP+Extensions+for+Graceful+Shutdown">
41   * Graceful Shutdown</a>
42   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
43   * @version $Rev: 434420 $
44   */
45  public class GracefulShutdownCommand extends ToolCommand
46  {
47      public static final String PORT_RANGE = "(" + AvailablePortFinder.MIN_PORT_NUMBER + ", "
48          + AvailablePortFinder.MAX_PORT_NUMBER + ")";
49  
50      private static final int DELAY_MAX = 86400;
51  
52      private static final int TIME_OFFLINE_MAX = 720;
53  
54      private int port = 10389;
55      private String host = "localhost";
56      private String password = "secret";
57      private int delay;
58      private int timeOffline;
59  
60  
61      protected GracefulShutdownCommand()
62      {
63          super( "graceful" );
64      }
65  
66      private boolean isWaiting;
67      private boolean isSuccess = false;
68      private Thread executeThread = null;
69  
70  
71      public void execute( CommandLine cmd ) throws Exception
72      {
73          executeThread = Thread.currentThread();
74          processOptions( cmd );
75  
76          if ( isDebugEnabled() )
77          {
78              System.out.println( "Parameters for GracefulShutdown extended request:" );
79              System.out.println( "port = " + port );
80              System.out.println( "host = " + host );
81              System.out.println( "password = " + password );
82              System.out.println( "delay = " + delay );
83              System.out.println( "timeOffline = " + timeOffline );
84          }
85  
86          Hashtable env = new Hashtable();
87          env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" );
88          env.put( "java.naming.provider.url", "ldap://" + host + ":" + port );
89          env.put( "java.naming.security.principal", "uid=admin,ou=system" );
90          env.put( "java.naming.security.credentials", password );
91          env.put( "java.naming.security.authentication", "simple" );
92  
93          LdapContext ctx = new InitialLdapContext( env, null );
94          if ( !isQuietEnabled() )
95          {
96              System.out.println( "Connection to the server established.\n"
97                  + "Sending extended request and blocking for shutdown:" );
98              isWaiting = true;
99              Thread t = new Thread( new Ticker() );
100             t.start();
101         }
102         try
103         {
104             ctx.extendedOperation( new GracefulShutdownRequest( 0, timeOffline, delay ) );
105             isSuccess = true;
106         }
107         catch ( Throwable t )
108         {
109             /*
110              * Sometimes because of timing issues we show a failure when the 
111              * shutdown has succeeded so we should check if the server is up
112              * before we set success to false.
113              */
114             try
115             {
116                 new InitialLdapContext( env, null );
117                 isSuccess = false;
118                 System.err.print( "shutdown request failed with error: " + t.getMessage() );
119             }
120             catch ( CommunicationException e )
121             {
122                 isSuccess = true;
123             }
124         }
125         isWaiting = false;
126         ctx.close();
127     }
128 
129     class Ticker implements Runnable
130     {
131         public void run()
132         {
133             if ( !isQuietEnabled() )
134                 System.out.print( "[waiting for shutdown] " );
135             while ( isWaiting )
136             {
137                 try
138                 {
139                     Thread.sleep( 1000 );
140                 }
141                 catch ( InterruptedException e )
142                 {
143                     // TODO Auto-generated catch block
144                     e.printStackTrace();
145                 }
146                 if ( !isQuietEnabled() )
147                     System.out.print( "." );
148             }
149             if ( isSuccess )
150             {
151                 if ( !isQuietEnabled() )
152                     System.out.println( "\n[shutdown complete]" );
153                 try
154                 {
155                     executeThread.join( 1000 );
156                 }
157                 catch ( InterruptedException e )
158                 {
159                     e.printStackTrace();
160                 }
161                 System.exit( 0 );
162             }
163             else
164             {
165                 if ( !isQuietEnabled() )
166                     System.out.println( "\n[shutdown failed]" );
167                 try
168                 {
169                     executeThread.join( 1000 );
170                 }
171                 catch ( InterruptedException e )
172                 {
173                     e.printStackTrace();
174                 }
175                 System.exit( 1 );
176             }
177         }
178     }
179 
180 
181     private void processOptions( CommandLine cmd )
182     {
183         if ( isDebugEnabled() )
184         {
185             System.out.println( "Processing options for graceful shutdown ..." );
186         }
187 
188         // -------------------------------------------------------------------
189         // figure out and error check the port value
190         // -------------------------------------------------------------------
191 
192         if ( cmd.hasOption( 'p' ) ) // - user provided port w/ -p takes precedence
193         {
194             String val = cmd.getOptionValue( 'p' );
195             try
196             {
197                 port = Integer.parseInt( val );
198             }
199             catch ( NumberFormatException e )
200             {
201                 System.err.println( "port value of '" + val + "' is not a number" );
202                 System.exit( 1 );
203             }
204 
205             if ( port > AvailablePortFinder.MAX_PORT_NUMBER )
206             {
207                 System.err.println( "port value of '" + val + "' is larger than max port number: "
208                     + AvailablePortFinder.MAX_PORT_NUMBER );
209                 System.exit( 1 );
210             }
211             else if ( port < AvailablePortFinder.MIN_PORT_NUMBER )
212             {
213                 System.err.println( "port value of '" + val + "' is smaller than the minimum port number: "
214                     + AvailablePortFinder.MIN_PORT_NUMBER );
215                 System.exit( 1 );
216             }
217 
218             if ( isDebugEnabled() )
219             {
220                 System.out.println( "port overriden by -p option: " + port );
221             }
222         }
223         else if ( getApacheDS() != null )
224         {
225             port = getApacheDS().getLdapService().getIpPort();
226 
227             if ( isDebugEnabled() )
228             {
229                 System.out.println( "port overriden by server.xml configuration: " + port );
230             }
231         }
232         else if ( isDebugEnabled() )
233         {
234             System.out.println( "port set to default: " + port );
235         }
236 
237         // -------------------------------------------------------------------
238         // figure out the host value
239         // -------------------------------------------------------------------
240 
241         if ( cmd.hasOption( 'h' ) )
242         {
243             host = cmd.getOptionValue( 'h' );
244 
245             if ( isDebugEnabled() )
246             {
247                 System.out.println( "host overriden by -h option: " + host );
248             }
249         }
250         else if ( isDebugEnabled() )
251         {
252             System.out.println( "host set to default: " + host );
253         }
254 
255         // -------------------------------------------------------------------
256         // figure out the password value
257         // -------------------------------------------------------------------
258 
259         if ( cmd.hasOption( 'w' ) )
260         {
261             password = cmd.getOptionValue( 'w' );
262 
263             if ( isDebugEnabled() )
264             {
265                 System.out.println( "password overriden by -w option: " + password );
266             }
267         }
268         else if ( isDebugEnabled() )
269         {
270             System.out.println( "password set to default: " + password );
271         }
272 
273         // -------------------------------------------------------------------
274         // figure out the delay value
275         // -------------------------------------------------------------------
276 
277         if ( cmd.hasOption( 'e' ) )
278         {
279             String val = cmd.getOptionValue( 'e' );
280             try
281             {
282                 delay = Integer.parseInt( val );
283             }
284             catch ( NumberFormatException e )
285             {
286                 System.err.println( "delay value of '" + val + "' is not a number" );
287                 System.exit( 1 );
288             }
289 
290             if ( delay > DELAY_MAX )
291             {
292                 System.err.println( "delay value of '" + val + "' is larger than max delay (seconds) allowed: "
293                     + DELAY_MAX );
294                 System.exit( 1 );
295             }
296             else if ( delay < 0 )
297             {
298                 System.err.println( "delay value of '" + val + "' is less than zero and makes no sense" );
299                 System.exit( 1 );
300             }
301 
302             if ( isDebugEnabled() )
303             {
304                 System.out.println( "delay seconds overriden by -e option: " + delay );
305             }
306         }
307         else if ( isDebugEnabled() )
308         {
309             System.out.println( "Using default delay value of " + delay );
310         }
311 
312         // -------------------------------------------------------------------
313         // figure out the timeOffline value
314         // -------------------------------------------------------------------
315 
316         if ( cmd.hasOption( 't' ) )
317         {
318             String val = cmd.getOptionValue( 't' );
319             try
320             {
321                 timeOffline = Integer.parseInt( val );
322             }
323             catch ( NumberFormatException e )
324             {
325                 System.err.println( "timeOffline value of '" + val + "' is not a number" );
326                 System.exit( 1 );
327             }
328 
329             if ( timeOffline > TIME_OFFLINE_MAX )
330             {
331                 System.err.println( "timeOffline value of '" + val
332                     + "' is larger than max timeOffline (minutes) allowed: " + TIME_OFFLINE_MAX );
333                 System.exit( 1 );
334             }
335             else if ( timeOffline < 0 )
336             {
337                 System.err.println( "timeOffline value of '" + val + "' is less than zero and makes no sense" );
338                 System.exit( 1 );
339             }
340 
341             if ( isDebugEnabled() )
342             {
343                 System.out.println( "timeOffline seconds overriden by -t option: " + timeOffline );
344             }
345         }
346         else if ( isDebugEnabled() )
347         {
348             System.out.println( "Using default timeOffline value of " + delay );
349         }
350     }
351 
352 
353     public Options getOptions()
354     {
355         Options opts = new Options();
356         Option op = new Option( "h", "host", true, "server host: defaults to localhost" );
357         op.setRequired( false );
358         opts.addOption( op );
359         op = new Option( "p", "port", true, "server port: defaults to 10389 or server.xml specified port" );
360         op.setRequired( false );
361         opts.addOption( op );
362         op = new Option( "e", "delay", true, "delay (seconds) before shutdown: defaults to 0" );
363         op.setRequired( false );
364         opts.addOption( op );
365         op = new Option( "w", "password", true, "the apacheds administrator's password: defaults to secret" );
366         op.setRequired( false );
367         opts.addOption( op );
368         op = new Option( "t", "time-offline", true, "server offline time (minutes): defaults to 0 (indefinate)" );
369         op.setRequired( false );
370         opts.addOption( op );
371         op = new Option( "i", "install-path", true, "path to apacheds installation directory" );
372         op.setRequired( false );
373         opts.addOption( op );
374         return opts;
375     }
376 }