You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by lu...@apache.org on 2015/07/10 19:20:29 UTC

svn commit: r1690291 - in /directory/apacheds/trunk: installers-maven-plugin/src/main/resources/org/apache/directory/server/installers/archive/apacheds.sh service/src/main/java/org/apache/directory/server/UberjarMain.java

Author: lucastheisen
Date: Fri Jul 10 17:20:29 2015
New Revision: 1690291

URL: http://svn.apache.org/r1690291
Log:
DIRSERVER-2080: Add a way to politely stop apacheds from apacheds.sh

Modified:
    directory/apacheds/trunk/installers-maven-plugin/src/main/resources/org/apache/directory/server/installers/archive/apacheds.sh
    directory/apacheds/trunk/service/src/main/java/org/apache/directory/server/UberjarMain.java

Modified: directory/apacheds/trunk/installers-maven-plugin/src/main/resources/org/apache/directory/server/installers/archive/apacheds.sh
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/installers-maven-plugin/src/main/resources/org/apache/directory/server/installers/archive/apacheds.sh?rev=1690291&r1=1690290&r2=1690291&view=diff
==============================================================================
--- directory/apacheds/trunk/installers-maven-plugin/src/main/resources/org/apache/directory/server/installers/archive/apacheds.sh (original)
+++ directory/apacheds/trunk/installers-maven-plugin/src/main/resources/org/apache/directory/server/installers/archive/apacheds.sh Fri Jul 10 17:20:29 2015
@@ -23,8 +23,8 @@
 #
 # Environment Variable Prerequisites
 #
-#   Do not set the variables in this script. Instead put them into a script
-#   setenv.sh in CATALINA_BASE/bin to keep your customizations separate.
+#   Do not set the variables in this script. Instead put them into 
+#   $ADS_HOME/bin/setenv.sh to keep your customizations separate.
 #
 #   ADS_HOME        (Optional) The directory that contains your apacheds 
 #                   install.  Defaults to the parent directory of the
@@ -37,12 +37,19 @@
 #
 #   ADS_EXTENDED_OPERATIONS
 #                   Extended operations to register.
+#   ADS_SHUTDOWN_PORT
+#                   (Optional) If specified, it must be a valid port number
+#                   on which ApacheDS will listen for a connection to trigger
+#                   a polite shutdown.  Defaults to 10390.
 #
 #   JAVA_HOME       (Optional) The java installation directory.  If not
 #                   not specified, the java from $PATH will be used.
 #
 #   JAVA_OPTS       (Optional) Any additional java options (ex: -Xms:256m)
 
+# Defaults
+ADS_SHUTDOWN_PORT=10390
+
 # Detect ads home (http://stackoverflow.com/a/630387/516433)
 PROGRAM_DIR="`dirname \"$0\"`"
 [ -z "$ADS_HOME" ] && ADS_HOME="`(cd \"$PROGRAM_DIR/..\" && pwd)`"
@@ -143,6 +150,7 @@ if [ "$ADS_ACTION" = "start" ]; then
     # Launching ApacheDS
     eval "\"$RUN_JAVA\"" $JAVA_OPTS $ADS_CONTROLS $ADS_EXTENDED_OPERATIONS \
         -Dlog4j.configuration="\"file:$ADS_INSTANCE/conf/log4j.properties\"" \
+        -Dapacheds.shutdown.port="\"$ADS_SHUTDOWN_PORT\"" \
         -Dapacheds.log.dir="\"$ADS_INSTANCE/log\"" \
         -classpath "\"$CLASSPATH\"" \
         org.apache.directory.server.UberjarMain "\"$ADS_INSTANCE\"" \
@@ -156,6 +164,7 @@ elif [ "$ADS_ACTION" = "run" ]; then
     eval "\"$RUN_JAVA\"" $JAVA_OPTS $ADS_CONTROLS $ADS_EXTENDED_OPERATIONS \
         -Dlog4j.configuration="\"file:$ADS_INSTANCE/conf/log4j.properties\"" \
         -Dapacheds.log.dir="\"$ADS_INSTANCE/log\"" \
+        -Dapacheds.shutdown.port="\"$ADS_SHUTDOWN_PORT\"" \
         -classpath "\"$CLASSPATH\"" \
         org.apache.directory.server.UberjarMain "\"$ADS_INSTANCE\""
 elif [ "$ADS_ACTION" = "status" ]; then
@@ -176,7 +185,17 @@ elif [ "$ADS_ACTION" = "stop" ]; then
         [ $HAVE_TTY -eq 1 ] && echo "Stopping ApacheDS instance '$ADS_INSTANCE_NAME' running as $PID"
 
         # Terminate the process
-        kill -15 $PID > /dev/null 2>&1
+        if [ $ADS_SHUTDOWN_PORT -gt 0 ]; then
+            eval "\"$RUN_JAVA\"" $JAVA_OPTS $ADS_CONTROLS $ADS_EXTENDED_OPERATIONS \
+                -Dlog4j.configuration="\"file:$ADS_INSTANCE/conf/log4j.properties\"" \
+                -Dapacheds.log.dir="\"$ADS_INSTANCE/log\"" \
+                -Dapacheds.shutdown.port="\"$ADS_SHUTDOWN_PORT\"" \
+                -classpath "\"$CLASSPATH\"" \
+                org.apache.directory.server.UberjarMain "\"$ADS_INSTANCE\"" stop
+        else
+            # No port specified so try term signal instead
+            kill -15 $PID > /dev/null 2>&1
+        fi
 
         ATTEMPTS_REMAINING=10
         while [ $ATTEMPTS_REMAINING -gt 0 ]; do

Modified: directory/apacheds/trunk/service/src/main/java/org/apache/directory/server/UberjarMain.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/service/src/main/java/org/apache/directory/server/UberjarMain.java?rev=1690291&r1=1690290&r2=1690291&view=diff
==============================================================================
--- directory/apacheds/trunk/service/src/main/java/org/apache/directory/server/UberjarMain.java (original)
+++ directory/apacheds/trunk/service/src/main/java/org/apache/directory/server/UberjarMain.java Fri Jul 10 17:20:29 2015
@@ -1,25 +1,36 @@
 /*
- *  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. 
- *  
+ * 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.server;
 
 
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+
 import org.apache.directory.server.core.api.InstanceLayout;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -28,41 +39,138 @@ import org.slf4j.LoggerFactory;
 /**
  * The command line main for the server.
  *
- * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory
+ *         Project</a>
  */
 public class UberjarMain
 {
     /** A logger for this class */
     private static final Logger LOG = LoggerFactory.getLogger( UberjarMain.class );
+    /** The key of the property use to specify the shutdown port */
+    private static final String PROPERTY_SHUTDOWN_PORT = "apacheds.shutdown.port";
 
     /** The ApacheDS service */
     private ApacheDsService service;
 
-
     /**
-     * Takes a single argument, the path to the installation home, which contains 
-     * the configuration to load with server startup settings.
+     * Takes a single argument, the path to the installation home, which
+     * contains the configuration to load with server startup settings.
      *
-     * @param args the arguments
+     * @param args
+     *            the arguments
      */
     public static void main( String[] args ) throws Exception
     {
-        if ( ( args != null ) && ( args.length == 1 ) )
+        if ( (args == null) || (args.length < 1) )
         {
-            UberjarMain uberjarMain = new UberjarMain();
+            throw new IllegalArgumentException(
+                    "Instance directory argument is missing" );
+        }
 
-            uberjarMain.start( args );
+        final String instanceDirectory = args[0];
+        Action action = (args.length == 2) ? Action.fromString( args[1] ) : Action.START;
+
+        final UberjarMain instance = new UberjarMain();
+        final int shutdownPort = getShutdownPort();
+        switch ( action )
+        {
+            case START:
+                LOG.debug( "Staring runtime" );
+                final String shutdownPassword = writeShutdownPassword( instanceDirectory,
+                        UUID.randomUUID().toString() );
+                try {
+                    new Thread( new Runnable()
+                    {
+                        @Override
+                        public void run()
+                        {
+                            try (ServerSocket shutdownSocket = new ServerSocket( shutdownPort ))
+                            {
+                                writeShutdownPort( instanceDirectory, shutdownSocket.getLocalPort() );
+
+                                Socket socket;
+                                LOG.info( "Start the shutdown listener on port [{}]", shutdownPort );
+                                while ( (socket = shutdownSocket.accept()) != null )
+                                {
+                                    if ( shutdownPassword == null || shutdownPassword.isEmpty() ) {
+                                        instance.stop();
+                                        break;
+                                    }
+                                    else
+                                    {
+                                        try (InputStreamReader reader = new InputStreamReader( socket.getInputStream() ))
+                                        {
+                                            CharBuffer buffer = CharBuffer.allocate( 2048 );
+                                            while ( reader.read( buffer ) >= 0 );
+                                            buffer.flip();
+                                            String password = buffer.toString();
+                                            if ( shutdownPassword.equals( password ) )
+                                            {
+                                                instance.stop();
+                                                break;
+                                            }
+                                            else
+                                            {
+                                                LOG.warn( "Illegal attempt to shutdown, incorrect password [{}]", password );
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                            catch ( IOException e )
+                            {
+                                e.printStackTrace();
+                                LOG.error( "Failed to start the shutdown listener.", e );
+                            }
+
+                        }
+                    } ).start();
+                }
+                catch ( Exception e ) {
+                    e.printStackTrace();
+                    LOG.error( "Failed to start the service.", e );
+                    System.exit( 1 );
+                }
+                instance.start( instanceDirectory );
+                break;
+            case STOP:
+                LOG.debug( "Stopping runtime" );
+                try (Socket socket = new Socket( "localhost", readShutdownPort( instanceDirectory ) );
+                        PrintWriter writer = new PrintWriter( socket.getOutputStream() ))
+                {
+                    writer.print( readShutdownPassword( instanceDirectory ) );
+                }
+                break;
         }
-        else
+    }
+
+    private static int getShutdownPort()
+    {
+        int shutdownPort = Integer.parseInt( System.getProperty( PROPERTY_SHUTDOWN_PORT, "0" ) );
+        if ( shutdownPort < 1024 || shutdownPort > 65536 )
         {
-            // TODO default to the current directory.
-            throw new IllegalArgumentException(
-                "Program must be launched with 1 arguement, the path to the instance directory." );
+            throw new IllegalArgumentException( "Shutdown port [" + shutdownPort + "] is an illegal port number" );
         }
+        return shutdownPort;
+    }
+
+    private static int readShutdownPort( String instanceDirectory ) throws IOException 
+    {
+        return Integer.parseInt( new String( Files.readAllBytes( 
+                Paths.get( new InstanceLayout( instanceDirectory ).getRunDirectory().getAbsolutePath(), 
+                        ".shutdown.port" ) ),
+                Charset.forName( "utf-8" ) ) );
     }
 
+    private static String readShutdownPassword( String instanceDirectory ) throws IOException 
+    {
+        return new String( Files.readAllBytes( 
+                Paths.get( new InstanceLayout( instanceDirectory ).getRunDirectory().getAbsolutePath(),
+                        ".shutdown.pwd" ) ),
+                Charset.forName( "utf-8" ) );
+    }
 
-    public void start( String[] args )
+    public void start( String... args )
     {
         // Creating ApacheDS service
         service = new ApacheDsService();
@@ -73,6 +181,7 @@ public class UberjarMain
         // Initializing the service
         try
         {
+            LOG.info( "Starting the service." );
             service.start( instanceLayout );
         }
         catch ( Exception e )
@@ -83,13 +192,13 @@ public class UberjarMain
         }
     }
 
-
     public void stop()
     {
         if ( service != null )
         {
             try
             {
+                LOG.info( "Stopping the service." );
                 service.stop();
             }
             catch ( Exception e )
@@ -100,4 +209,43 @@ public class UberjarMain
             }
         }
     }
+    
+    private static String writeShutdownPassword( String instanceDirectory, String password ) throws IOException 
+    {
+        Files.write(
+                Paths.get( new InstanceLayout( instanceDirectory ).getRunDirectory().getAbsolutePath(), 
+                        ".shutdown.pwd" ),
+                password.getBytes( Charset.forName( "utf-8" ) ) );
+        return password;
+    }
+    
+    private static int writeShutdownPort( String instanceDirectory, int portNumber ) throws IOException 
+    {
+        Files.write(
+                Paths.get( new InstanceLayout( instanceDirectory ).getRunDirectory().getAbsolutePath(), 
+                        ".shutdown.port" ),
+                Integer.toString( portNumber ).getBytes( Charset.forName( "utf-8" ) ) );
+        return portNumber;
+    }
+
+    private static enum Action
+    {
+        START, STOP;
+
+        private static Map<String, Action> lookup;
+
+        static
+        {
+            lookup = new HashMap<String, Action>();
+            for ( Action action : values() )
+            {
+                lookup.put( action.name(), action );
+            }
+        }
+
+        public static Action fromString( String actionString )
+        {
+            return lookup.get( actionString.toUpperCase() );
+        }
+    }
 }