svn commit: r1863024 - in /ofbiz/ofbiz-framework/trunk/framework: base/ base/src/main/java/org/apache/ofbiz/base/container/ start/src/main/java/org/apache/ofbiz/base/start/

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1863024 - in /ofbiz/ofbiz-framework/trunk/framework: base/ base/src/main/java/org/apache/ofbiz/base/container/ start/src/main/java/org/apache/ofbiz/base/start/

mthl
Author: mthl
Date: Sat Jul 13 22:27:04 2019
New Revision: 1863024

URL: http://svn.apache.org/viewvc?rev=1863024&view=rev
Log:
Improved: Move ‘AdminServer’ inside a container
(OFBIZ-11136)

‘AdminServer’ provides a portable way to manage life-cycle of the OFBiz
process remotely by allowing administrator to check its running status
or shutting it down.

Previously the ‘AdminServer’ class was a special thread opening a
socket and launched at startup.  However since this class is about
managing some run-time resources with a life-cycle, it matches perfectly
the container abstraction.

A benefit of making ‘AdminServer’ a container is that the startup
process is now simpler and more uniform.

Administrators can now prevent remote shutdown of OFBiz for security
reasons by removing the container declaration.  Additionally They can
delegate the process management job to the init process (PID 0) of the
hosting system like Systemd [1] by replacing this container with
another one.

[1] https://www.freedesktop.org/software/systemd/man/systemd-notify.html

Added:
    ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/AdminServerContainer.java   (with props)
Removed:
    ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/AdminServer.java
Modified:
    ofbiz/ofbiz-framework/trunk/framework/base/ofbiz-component.xml
    ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/AdminClient.java
    ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/Start.java
    ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/StartupControlPanel.java

Modified: ofbiz/ofbiz-framework/trunk/framework/base/ofbiz-component.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/ofbiz-component.xml?rev=1863024&r1=1863023&r2=1863024&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/base/ofbiz-component.xml (original)
+++ ofbiz/ofbiz-framework/trunk/framework/base/ofbiz-component.xml Sat Jul 13 22:27:04 2019
@@ -32,5 +32,7 @@ under the License.
         <property name="host" value="0.0.0.0"/>
         <property name="port" value="1099"/>
     </container>
+    <!-- load the administration server -->
+    <container name="admin-container" loaders="main" class="org.apache.ofbiz.base.container.AdminServerContainer"/>
 
 </ofbiz-component>

Added: ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/AdminServerContainer.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/AdminServerContainer.java?rev=1863024&view=auto
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/AdminServerContainer.java (added)
+++ ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/AdminServerContainer.java Sat Jul 13 22:27:04 2019
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * 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.ofbiz.base.container;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+import org.apache.ofbiz.base.start.Config;
+import org.apache.ofbiz.base.start.Start;
+import org.apache.ofbiz.base.start.Start.ServerState;
+import org.apache.ofbiz.base.start.StartupCommand;
+import org.apache.ofbiz.base.util.UtilValidate;
+
+/**
+ * The AdminServer provides a way to communicate with a running
+ * OFBiz instance after it has started and send commands to that instance
+ * such as inquiring on server status or requesting system shutdown
+ */
+public final class AdminServerContainer implements Container {
+    /**
+     * Commands communicated between AdminClient and AdminServer
+     */
+    public enum OfbizSocketCommand {
+        SHUTDOWN, STATUS, FAIL
+    }
+
+    private String name;
+    private Thread serverThread;
+    private ServerSocket serverSocket;
+    private Config cfg = Start.getInstance().getConfig();
+
+    @Override
+    public void init(List<StartupCommand> ofbizCommands, String name, String configFile) throws ContainerException {
+        this.name = name;
+        try {
+            serverSocket = new ServerSocket(cfg.adminPort, 1, cfg.adminAddress);
+        } catch (IOException e) {
+            String msg = "Couldn't create server socket(" + cfg.adminAddress + ":" + cfg.adminPort + ")";
+            throw new ContainerException(msg, e);
+        }
+
+        if (cfg.adminPort > 0) {
+            serverThread = new Thread(this::run, "OFBiz-AdminServer");
+        } else {
+            serverThread = new Thread("OFBiz-AdminServer"); // Dummy thread
+            System.out.println("Admin socket not configured; set to port 0");
+        }
+        serverThread.setDaemon(false);
+    }
+
+    // Listens for administration commands.
+    private void run() {
+        System.out.println("Admin socket configured on - " + cfg.adminAddress + ":" + cfg.adminPort);
+        while (!Thread.interrupted()) {
+            try (Socket client = serverSocket.accept()) {
+                System.out.println("Received connection from - " + client.getInetAddress() + " : " + client.getPort());
+                processClientRequest(client);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public boolean start() throws ContainerException {
+        serverThread.start();
+        return true;
+    }
+
+    @Override
+    public void stop() throws ContainerException {
+        if (serverThread.isAlive()) {
+            serverThread.interrupt();
+        }
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    private void processClientRequest(Socket client) throws IOException {
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8));
+             PrintWriter writer = new PrintWriter(new OutputStreamWriter(client.getOutputStream(), StandardCharsets.UTF_8), true)) {
+
+            // read client request and prepare response
+            String clientRequest = reader.readLine();
+            OfbizSocketCommand clientCommand = determineClientCommand(clientRequest);
+            String serverResponse = prepareResponseToClient(clientCommand);
+
+            // send response back to client
+            writer.println(serverResponse);
+
+            // if the client request is shutdown, execute shutdown sequence
+            if(clientCommand.equals(OfbizSocketCommand.SHUTDOWN)) {
+                writer.flush();
+                Start.getInstance().stop();
+            }
+        }
+    }
+
+    private OfbizSocketCommand determineClientCommand(String request) {
+        if(!isValidRequest(request)) {
+            return OfbizSocketCommand.FAIL;
+        }
+        return OfbizSocketCommand.valueOf(request.substring(request.indexOf(':') + 1));
+    }
+
+    /**
+     * Validates if request is a suitable String
+     * @param request
+     * @return boolean which shows if request is suitable
+     */
+    private boolean isValidRequest(String request) {
+        return UtilValidate.isNotEmpty(request)
+                && request.contains(":")
+                && request.substring(0, request.indexOf(':')).equals(cfg.adminKey)
+                && !request.substring(request.indexOf(':') + 1).isEmpty();
+    }
+
+
+    private static String prepareResponseToClient(OfbizSocketCommand control) {
+        String response = null;
+        ServerState state = Start.getInstance().getCurrentState();
+        switch(control) {
+            case SHUTDOWN:
+                if (state == ServerState.STOPPING) {
+                    response = "IN-PROGRESS";
+                } else {
+                    response = "OK";
+                }
+                break;
+            case STATUS:
+                response = state.toString();
+                break;
+            case FAIL:
+                response = "FAIL";
+                break;
+        }
+        return response;
+    }
+}

Propchange: ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/AdminServerContainer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/AdminServerContainer.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/container/AdminServerContainer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/AdminClient.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/AdminClient.java?rev=1863024&r1=1863023&r2=1863024&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/AdminClient.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/AdminClient.java Sat Jul 13 22:27:04 2019
@@ -27,7 +27,7 @@ import java.net.ConnectException;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
 
-import org.apache.ofbiz.base.start.AdminServer.OfbizSocketCommand;
+import org.apache.ofbiz.base.container.AdminServerContainer.OfbizSocketCommand;
 
 /**
  * The AdminClient communicates with a running OFBiz server instance

Modified: ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/Start.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/Start.java?rev=1863024&r1=1863023&r2=1863024&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/Start.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/Start.java Sat Jul 13 22:27:04 2019
@@ -24,6 +24,8 @@ import java.util.Set;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.stream.Collectors;
 
+import org.apache.ofbiz.base.container.ContainerLoader;
+
 /**
  * OFBiz startup class.
  *
@@ -44,6 +46,7 @@ import java.util.stream.Collectors;
 public final class Start {
 
     private Config config = null;
+    private ContainerLoader loader = new ContainerLoader();
     private final AtomicReference<ServerState> serverState = new AtomicReference<>(ServerState.STARTING);
 
     // Singleton, do not change
@@ -84,7 +87,7 @@ public final class Start {
             break;
         case START:
             try {
-                StartupControlPanel.start(instance.config, instance.serverState, ofbizCommands);
+                StartupControlPanel.start(instance.config, instance.serverState, ofbizCommands, instance.loader);
             } catch (StartupException e) {
                 StartupControlPanel.fullyTerminateSystem(e);
             }
@@ -113,6 +116,11 @@ public final class Start {
         return serverState.get();
     }
 
+    public void stop() {
+        StartupControlPanel.shutdownServer(loader, serverState);
+        System.exit(0);
+    }
+
     /**
      * This enum contains the possible OFBiz server states.
      */

Modified: ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/StartupControlPanel.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/StartupControlPanel.java?rev=1863024&r1=1863023&r2=1863024&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/StartupControlPanel.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/start/src/main/java/org/apache/ofbiz/base/start/StartupControlPanel.java Sat Jul 13 22:27:04 2019
@@ -58,13 +58,8 @@ final class StartupControlPanel {
     /**
      * Execute the startup sequence for OFBiz
      */
-    static void start(Config config,
-            AtomicReference<ServerState> serverState,
-            List<StartupCommand> ofbizCommands) throws StartupException {
-
-        ContainerLoader loader = new ContainerLoader();
-        Thread adminServer = createAdminServer(config, serverState, loader);
-
+    static void start(Config config, AtomicReference<ServerState> serverState, List<StartupCommand> ofbizCommands,
+            ContainerLoader loader) throws StartupException {
         createLogDirectoryIfMissing(config.logDir);
 
         if (config.useShutdownHook) {
@@ -76,7 +71,7 @@ final class StartupControlPanel {
         loadContainers(config, loader, ofbizCommands, serverState);
 
         if (config.shutdownAfterLoad) {
-            shutdownServer(loader, serverState, adminServer);
+            shutdownServer(loader, serverState);
             System.exit(0);
         } else {
             // Print startup message.
@@ -110,7 +105,7 @@ final class StartupControlPanel {
         System.exit(1);
     }
 
-    static void shutdownServer(ContainerLoader loader, AtomicReference<ServerState> serverState, Thread adminServer) {
+    static void shutdownServer(ContainerLoader loader, AtomicReference<ServerState> serverState) {
         ServerState currentState;
         do {
             currentState = serverState.get();
@@ -123,9 +118,6 @@ final class StartupControlPanel {
         } catch (Exception e) {
             e.printStackTrace();
         }
-        if (adminServer != null && adminServer.isAlive()) {
-            adminServer.interrupt();
-        }
     }
 
     private static void loadGlobalOfbizSystemProperties(String globalOfbizPropertiesFileName) throws StartupException {
@@ -139,21 +131,6 @@ final class StartupControlPanel {
         }
     }
 
-    private static Thread createAdminServer(
-            Config config,
-            AtomicReference<ServerState> serverState,
-            ContainerLoader loader) throws StartupException {
-
-        Thread adminServer = null;
-        if (config.adminPort > 0) {
-            adminServer = new AdminServer(loader, serverState, config);
-            adminServer.start();
-        } else {
-            System.out.println("Admin socket not configured; set to port 0");
-        }
-        return adminServer;
-    }
-
     private static void createLogDirectoryIfMissing(String logDirName) {
         File logDir = new File(logDirName);
         if (!logDir.exists()) {
@@ -167,7 +144,7 @@ final class StartupControlPanel {
         Runtime.getRuntime().addShutdownHook(new Thread() {
             @Override
             public void run() {
-                shutdownServer(loader, serverState, this);
+                shutdownServer(loader, serverState);
             }
         });
     }