Index: oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java =================================================================== --- oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java (Revision 1857099) +++ oak-run/src/main/java/org/apache/jackrabbit/oak/run/AvailableModes.java (Arbeitskopie) @@ -33,6 +33,7 @@ .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()) Index: oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java =================================================================== --- oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java (nicht existent) +++ oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java (Arbeitskopie) @@ -0,0 +1,152 @@ +/* + * 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.text.SimpleDateFormat; +import java.util.ArrayList; +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.DocumentNodeStore; +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; + +import static java.util.Comparator.comparingInt; + +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(); + + DocumentNodeStore dns = builder.build(); + closer.register(Utils.asCloseable(dns)); + + DocumentStore ds = builder.getDocumentStore(); + + try { + List all + = new ArrayList<>(ClusterNodeInfoDocument.all(ds)); + all.sort(comparingInt(ClusterNodeInfoDocument::getClusterId)); + + if (options.isRaw()) { + printRaw(all); + } else { + print(all); + } + } catch (Throwable e) { + e.printStackTrace(System.err); + } + } catch (Throwable e) { + throw closer.rethrow(e); + } finally { + closer.close(); + } + } + + private static void print(List docs) { + int idLen = docs.isEmpty() ? 2 : Math.max(2, Integer.toString(docs.get(docs.size() - 1).getClusterId()).length()); + + String format = "%" + idLen + "s %8s %16s %16s %10s"; + System.out.println(String.format(format, "Id", "State", "Started", "LeaseEnd", "RecoveryBy")); + + SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); + df.setTimeZone(TimeZone.getTimeZone("UTC")); + + for (ClusterNodeInfoDocument c : docs) { + long start = c.getStartTime(); + long leaseEnd; + try { + leaseEnd = c.getLeaseEndTime(); + } catch (Exception ex) { + leaseEnd = 0; + } + + System.out.println( + String.format(format, Integer.toString(c.getClusterId()), + c.isActive() ? "ACTIVE" : "INACTIVE", + start <= 0 ? "-" : df.format(new Date(start)), + leaseEnd == 0 ? "-" : df.format(new Date(leaseEnd)), + c.getRecoveryBy() == null ? "-" : Long.toString(c.getRecoveryBy()))); + } + } + + private static void printRaw(Iterable docs) { + Map rawEntries = new HashMap<>(); + for (ClusterNodeInfoDocument c : docs) { + Map 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 raw; + + ClusterNodesOptions(String usage) { + super(usage); + raw = parser.accepts("raw", "List raw entries in JSON format"); + } + + @Override + public ClusterNodesOptions parse(String[] args) { + super.parse(args); + return this; + } + + boolean isRaw() { + return options.has(raw); + } + + boolean isHelp() { + return options.has(help); + } + } +} Property changes on: oak-run/src/main/java/org/apache/jackrabbit/oak/run/ClusterNodesCommand.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property