Author: reschke
Date: Wed Apr 10 13:17:06 2019
New Revision: 1857247

URL: http://svn.apache.org/viewvc?rev=1857247&view=rev
Log:
OAK-8146: oak-run support for inspecting clusterNodeInfo

Added:
    
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java
   (with props)
Modified:
    
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java

Modified: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java?rev=1857247&r1=1857246&r2=1857247&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java
 Wed Apr 10 13:17:06 2019
@@ -33,6 +33,7 @@ public final class AvailableModes {
             .put("checkpoints", new CheckpointsCommand())
             .put("check", new CheckCommand())
             .put("datastorecacheupgrade", new DataStoreCacheUpgradeCommand())
+            .put("clusternodes", new ClusterNodesCommand())
             .put("compact", new CompactCommand())
             .put("composite-prepare", new CompositePrepareCommand())
             .put("console", new ConsoleCommand())

Added: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java?rev=1857247&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java
 Wed Apr 10 13:17:06 2019
@@ -0,0 +1,232 @@
+/*
+ * 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.jackrabbit.oak.run;
+
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.jackrabbit.oak.plugins.document.ClusterNodeInfoDocument;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStoreBuilder;
+import org.apache.jackrabbit.oak.plugins.document.DocumentStore;
+import org.apache.jackrabbit.oak.plugins.document.rdb.RDBJSONSupport;
+import org.apache.jackrabbit.oak.run.commons.Command;
+
+import com.google.common.io.Closer;
+
+import joptsimple.OptionSpec;
+
+class ClusterNodesCommand implements Command {
+
+    @Override
+    public void execute(String... args) throws Exception {
+        Closer closer = Closer.create();
+        try {
+            String h = "clusternodes mongodb://host:port/database|jdbc:...";
+            ClusterNodesOptions options = new 
ClusterNodesOptions(h).parse(args);
+            if (options.isHelp()) {
+                options.printHelpOn(System.out);
+                System.exit(0);
+            }
+
+            DocumentNodeStoreBuilder<?> builder = 
Utils.createDocumentMKBuilder(options, closer);
+
+            if (builder == null) {
+                System.err
+                        .println("Clusternodes command only available for 
DocumentNodeStore backed by MongoDB or RDB persistence");
+                System.exit(1);
+            }
+
+            builder.setReadOnlyMode();
+            DocumentStore ds = builder.getDocumentStore();
+
+            try {
+                List<ClusterNodeInfoDocument> all = new 
ArrayList<>(ClusterNodeInfoDocument.all(ds));
+                Collections.sort(all, new 
Comparator<ClusterNodeInfoDocument>() {
+                    @Override
+                    public int compare(ClusterNodeInfoDocument one, 
ClusterNodeInfoDocument two) {
+                        return Integer.compare(one.getClusterId(), 
two.getClusterId());
+                    }
+                });
+                if (options.isRaw()) {
+                    printRaw(all);
+                } else {
+                    print(all, options.isVerbose());
+                }
+            } catch (Throwable e) {
+                e.printStackTrace(System.err);
+            }
+        } catch (Throwable e) {
+            throw closer.rethrow(e);
+        } finally {
+            closer.close();
+        }
+    }
+
+    private static void print(List<ClusterNodeInfoDocument> docs, boolean 
verbose) {
+
+        String sId = "Id";
+        String sState = "State";
+        String sStarted = "Started";
+        String sLeaseEnd = "LeaseEnd";
+        String sRecoveryBy = "RecoveryBy";
+        String sLeft = "Left";
+        String sLastRootRev = "LastRootRev";
+        String sOakVersion = "OakVersion";
+
+        long now = System.currentTimeMillis();
+
+        List<String> header = new ArrayList<>();
+        header.addAll(Arrays.asList(new String[] { sId, sState, sStarted, 
sLeaseEnd, sLeft, sRecoveryBy }));
+        if (verbose) {
+            header.add(sLastRootRev);
+            header.add(sOakVersion);
+        }
+
+        SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
+        df.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+        List<Map<String, String>> body = new ArrayList<>();
+
+        for (ClusterNodeInfoDocument c : docs) {
+
+            long start = c.getStartTime();
+            long leaseEnd;
+            long left;
+            try {
+                leaseEnd = c.getLeaseEndTime();
+                left = (leaseEnd - now) / 1000;
+            } catch (Exception ex) {
+                leaseEnd = 0;
+                left = Long.MIN_VALUE;
+            }
+
+            Map<String, String> e = new HashMap<>();
+            e.put(sId, Integer.toString(c.getClusterId()));
+            e.put(sState, c.isActive() ? "ACTIVE" : "INACTIVE");
+            e.put(sStarted, start <= 0 ? "-" : df.format(new Date(start)));
+            e.put(sLeaseEnd, leaseEnd == 0 ? "-" : df.format(new 
Date(leaseEnd)));
+            e.put(sLeft, (left < -999) ? "-" : (Long.toString(left) + "s"));
+            e.put(sRecoveryBy,
+                    c.getRecoveryBy() == null ? (c.isRecoveryNeeded(now) ? "!" 
: "-") : Long.toString(c.getRecoveryBy()));
+
+            if (verbose) {
+                e.put(sLastRootRev, c.getLastWrittenRootRev());
+                Object oakVersion = c.get("oakVersion");
+                e.put(sOakVersion,  oakVersion == null ? "-" : 
oakVersion.toString());
+            }
+            body.add(e);
+        }
+        list(System.out, header, body);
+    }
+
+    /**
+     * A generic method to print a table, choosing column widths automatically
+     * based both on column title and values.
+     * 
+     * @param out
+     *            output target
+     * @param header
+     *            list of column titles
+     * @param body
+     *            list of rows, where each row is a map from column title to
+     *            value
+     */
+    private static void list(PrintStream out, List<String> header, 
List<Map<String, String>> body) {
+        // find column widths
+        Map<String, Integer> widths = new HashMap<>();
+        for (String h : header) {
+            widths.put(h, h.length());
+        }
+        for (Map<String, String> m : body) {
+            for (Map.Entry<String, String> e : m.entrySet()) {
+                int current = widths.get(e.getKey());
+                int thisone = e.getValue().length();
+                widths.put(e.getKey(), Math.max(current, thisone));
+            }
+        }
+        StringBuilder sformat = new StringBuilder();
+        for (String h : header) {
+            if (sformat.length() != 0) {
+                sformat.append(' ');
+            }
+            sformat.append("%" + widths.get(h) + "s");
+        }
+        String format = sformat.toString();
+
+        out.println(String.format(format, header.toArray()));
+        for (Map<String, String> m : body) {
+            List<String> l = new ArrayList<>();
+            for (String h : header) {
+                l.add(m.get(h));
+            }
+            out.println(String.format(format, l.toArray()));
+        }
+    }
+
+    private static void printRaw(Iterable<ClusterNodeInfoDocument> docs) {
+        Map<Object, Object> rawEntries = new HashMap<>();
+        for (ClusterNodeInfoDocument c : docs) {
+            Map<Object, Object> entries = new HashMap<>();
+            for (String k : c.keySet()) {
+                entries.put(k, c.get(k));
+            }
+            rawEntries.put(Integer.toString(c.getClusterId()), entries);
+        }
+        StringBuilder sb = new StringBuilder();
+        RDBJSONSupport.appendJsonMap(sb, rawEntries);
+        System.out.println(sb);
+    }
+
+    private static final class ClusterNodesOptions extends 
Utils.NodeStoreOptions {
+
+        final OptionSpec<Void> raw;
+        final OptionSpec<Void> verbose;
+
+        ClusterNodesOptions(String usage) {
+            super(usage);
+            raw = parser.accepts("raw", "List raw entries in JSON format");
+            verbose = parser.accepts("verbose", "Be more verbose");
+        }
+
+        @Override
+        public ClusterNodesOptions parse(String[] args) {
+            super.parse(args);
+            return this;
+        }
+
+        boolean isRaw() {
+            return options.has(raw);
+        }
+
+        boolean isVerbose() {
+            return options.has(verbose);
+        }
+
+        boolean isHelp() {
+            return options.has(help);
+        }
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to