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 }