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, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.procedure2.store.wal;
019
020import java.io.IOException;
021import java.io.PrintStream;
022import java.util.ArrayList;
023import java.util.List;
024import org.apache.hadoop.conf.Configuration;
025import org.apache.hadoop.conf.Configured;
026import org.apache.hadoop.fs.FSDataInputStream;
027import org.apache.hadoop.fs.FileStatus;
028import org.apache.hadoop.fs.FileSystem;
029import org.apache.hadoop.fs.Path;
030import org.apache.hadoop.hbase.HBaseConfiguration;
031import org.apache.hadoop.hbase.HBaseInterfaceAudience;
032import org.apache.hadoop.hbase.procedure2.Procedure;
033import org.apache.hadoop.hbase.procedure2.ProcedureUtil;
034import org.apache.hadoop.util.Tool;
035import org.apache.hadoop.util.ToolRunner;
036import org.apache.yetus.audience.InterfaceAudience;
037import org.apache.yetus.audience.InterfaceStability;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041import org.apache.hbase.thirdparty.org.apache.commons.cli.CommandLine;
042import org.apache.hbase.thirdparty.org.apache.commons.cli.DefaultParser;
043import org.apache.hbase.thirdparty.org.apache.commons.cli.HelpFormatter;
044import org.apache.hbase.thirdparty.org.apache.commons.cli.Options;
045import org.apache.hbase.thirdparty.org.apache.commons.cli.ParseException;
046
047import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos.ProcedureWALEntry;
048import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos.ProcedureWALHeader;
049
050/**
051 * ProcedureWALPrettyPrinter prints the contents of a given ProcedureWAL file
052 * @see WALProcedureStore#main(String[]) if you want to check parse of a directory of WALs.
053 * @deprecated Since 2.3.0, will be removed in 4.0.0. Keep here only for rolling upgrading, now we
054 *             use the new region based procedure store.
055 */
056@Deprecated
057@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
058@InterfaceStability.Evolving
059public class ProcedureWALPrettyPrinter extends Configured implements Tool {
060  private static final Logger LOG = LoggerFactory.getLogger(ProcedureWALPrettyPrinter.class);
061
062  private final PrintStream out;
063
064  public ProcedureWALPrettyPrinter() {
065    out = System.out;
066  }
067
068  /**
069   * Reads a log file and outputs its contents.
070   *
071   * @param conf   HBase configuration relevant to this log file
072   * @param p       path of the log file to be read
073   * @throws IOException  IOException
074   */
075  public void processFile(final Configuration conf, final Path p)
076      throws IOException {
077
078    FileSystem fs = p.getFileSystem(conf);
079    if (!fs.exists(p)) {
080      System.err.println("ERROR, file doesnt exist: " + p);
081      return;
082    }
083    if (!fs.isFile(p)) {
084      System.err.println(p + " is not a file");
085      return;
086    }
087
088    FileStatus logFile = fs.getFileStatus(p);
089    if (logFile.getLen() == 0) {
090      out.println("Zero length file: " + p);
091      return;
092    }
093
094    out.println("Opening procedure state-log: " + p);
095    ProcedureWALFile log = new ProcedureWALFile(fs, logFile);
096    processProcedureWALFile(log);
097  }
098
099  public void processProcedureWALFile(ProcedureWALFile log) throws IOException {
100    log.open();
101    ProcedureWALHeader header = log.getHeader();
102    printHeader(header);
103
104    FSDataInputStream stream = log.getStream();
105    try {
106      boolean hasMore = true;
107      while (hasMore) {
108        ProcedureWALEntry entry = ProcedureWALFormat.readEntry(stream);
109        if (entry == null) {
110          out.println("No more entry, exiting with missing EOF");
111          hasMore = false;
112          break;
113        }
114        switch (entry.getType()) {
115          case PROCEDURE_WAL_EOF:
116            hasMore = false;
117            break;
118          default:
119            printEntry(entry);
120        }
121      }
122    } catch (IOException e) {
123      out.println("got an exception while reading the procedure WAL " + e.getMessage());
124    }
125    finally {
126      log.close();
127    }
128  }
129
130  private void printEntry(final ProcedureWALEntry entry) throws IOException {
131    out.println("EntryType=" + entry.getType());
132    int procCount = entry.getProcedureCount();
133    for (int i = 0; i < procCount; i++) {
134      Procedure<?> proc = ProcedureUtil.convertToProcedure(entry.getProcedure(i));
135      printProcedure(proc);
136    }
137  }
138
139  private void printProcedure(Procedure<?> proc) {
140    out.println(proc.toStringDetails());
141  }
142
143  private void printHeader(ProcedureWALHeader header) {
144    out.println("ProcedureWALHeader: ");
145    out.println("  Version: " + header.getVersion());
146    out.println("  Type: " + header.getType());
147    out.println("  LogId: " + header.getLogId());
148    out.println("  MinProcId: " + header.getMinProcId());
149    out.println();
150  }
151
152  /**
153   * Pass one or more log file names and formatting options and it will dump out
154   * a text version of the contents on <code>stdout</code>.
155   *
156   * @param args
157   *          Command line arguments
158   * @throws IOException
159   *           Thrown upon file system errors etc.
160   */
161  @Override
162  public int run(final String[] args) throws IOException {
163    // create options
164    Options options = new Options();
165    options.addOption("h", "help", false, "Output help message");
166    options.addOption("f", "file", true, "File to print");
167
168    final List<Path> files = new ArrayList<>();
169    try {
170      CommandLine cmd = new DefaultParser().parse(options, args);
171
172      if (cmd.hasOption("f")) {
173        files.add(new Path(cmd.getOptionValue("f")));
174      }
175
176      if (files.isEmpty() || cmd.hasOption("h")) {
177        HelpFormatter formatter = new HelpFormatter();
178        formatter.printHelp("ProcedureWALPrettyPrinter ", options, true);
179        return(-1);
180      }
181    } catch (ParseException e) {
182      LOG.error("Failed to parse commandLine arguments", e);
183      HelpFormatter formatter = new HelpFormatter();
184      formatter.printHelp("ProcedureWALPrettyPrinter ", options, true);
185      return(-1);
186    }
187    // get configuration, file system, and process the given files
188    for (Path file : files) {
189      processFile(getConf(), file);
190    }
191    return(0);
192  }
193
194  public static void main(String[] args) throws Exception {
195    final Configuration conf = HBaseConfiguration.create();
196    int exitCode = ToolRunner.run(conf, new ProcedureWALPrettyPrinter(), args);
197    System.exit(exitCode);
198  }
199}