Package org.kiji.schema.tools

Source Code of org.kiji.schema.tools.LayoutTool

/**
* (c) Copyright 2012 WibiData, Inc.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* Licensed 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.kiji.schema.tools;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;

import com.google.common.base.Preconditions;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.util.Bytes;

import org.kiji.annotations.ApiAudience;
import org.kiji.common.flags.Flag;
import org.kiji.schema.Kiji;
import org.kiji.schema.KijiURI;
import org.kiji.schema.avro.TableLayoutDesc;
import org.kiji.schema.layout.KijiTableLayout;
import org.kiji.schema.util.ResourceUtils;
import org.kiji.schema.util.ToJson;

/**
* Command-line tool for interacting with table layouts. Actions include reading a layout,
* setting a table layout, and viewing a table's layout history.
*
* <h2>Examples:</h2>
* Write the recent layout history of a table to a file:
* <pre>
*   kiji layout --table=kiji://my-hbase/my-instance/my-table/ \
*       --do=history --max-version=3 --write-to=my-output-file
* </pre>
* Set a new layout for a table:
* <pre>
*   kiji layout --table=kiji://my-hbase/my-instance/my-table/ \
*       --do=set --layout=my-input-file
* </pre>
* Perform a dry run of setting a table layout:
* <pre>
*   kiji layout --table=kiji://my-hbase/my-instance/my-table/ \
*       --do=set --layout=my-input-file --dry-run=true
* </pre>
*/
@ApiAudience.Private
public final class LayoutTool extends BaseTool {
  @Flag(name="do", usage="Action to perform: dump, set, or history.")
  private String mDo = "dump";

  @Flag(name="table", usage="The KijiURI of the table to use.")
  private String mTableURIFlag = null;

  @Flag(name="layout",
      usage="Path to the file containing the layout update, in JSON.")
  private String mLayout = "";

  @Flag(name="dry-run",
      usage="Prints what actions would be taken, but does not commit changes.")
  private boolean mDryRun = false;

  @Flag(name="max-versions",
      usage="Maximum number of layout versions to retrieve in the layout history.")
  private int mMaxVersions = 1;

  @Flag(name="write-to",
      usage="Write layout(s) to this file(s). "
          + "Empty means write to console output. "
          + "A '.json' extension is appended to this parameter.")
  private String mWriteTo = "";

  /** URI of the table to edit or print the layout of. */
  private KijiURI mTableURI;

  /** The Kiji to use to set layouts. */
  private Kiji mKiji;

  /** {@inheritDoc} */
  @Override
  public String getName() {
    return "layout";
  }

  /** {@inheritDoc} */
  @Override
  public String getDescription() {
    return "View or modify kiji table layouts.";
  }

  /** {@inheritDoc} */
  @Override
  public String getCategory() {
    return "DDL";
  }

  /** {@inheritDoc} */
  @Override
  protected void validateFlags() throws Exception {
    Preconditions.checkArgument((mTableURIFlag != null) && !mTableURIFlag.isEmpty(),
        "Specify a table with --table=kiji://hbase-address/kiji-instance/table");
    mTableURI = KijiURI.newBuilder(mTableURIFlag).build();
    Preconditions.checkArgument(mTableURI.getTable() != null,
        "Specify a table with --table=kiji://hbase-address/kiji-instance/table");

    Preconditions.checkArgument(mMaxVersions >= 1,
        "Invalid maximum number of versions: {}, --max-versions must be >= 1",
        mMaxVersions);
  }

  /**
   * Implements the --do=dump operation.
   *
   * @throws Exception on error.
   */
  private void dumpLayout() throws Exception {
    final KijiTableLayout layout = mKiji.getMetaTable().getTableLayout(mTableURI.getTable());
    final StringBuilder json = new StringBuilder();
    json.append("/*\n");
    json.append("   The raw JSON view of table layouts is intended for use by\n");
    json.append("   system administrators or for debugging purposes.\n");
    json.append("\n");
    json.append("   Most users should use the 'kiji-schema-shell' DDL tool to modify\n");
    json.append("   your table layouts instead.\n");
    json.append("*/\n");
    json.append(ToJson.toJsonString(layout.getDesc()));
    if (mWriteTo.isEmpty()) {
      System.out.println(json.toString());
    } else {
      final String fileName = String.format("%s.json", mWriteTo);
      final FileOutputStream fos = new FileOutputStream(fileName);
      try {
        fos.write(Bytes.toBytes(json.toString()));
      } finally {
        ResourceUtils.closeOrLog(fos);
      }
    }
  }

  /**
   * Loads a table layout descriptor from a JSON-encoded file.
   *
   * @param filePath Path to a JSON-encoded table layout descriptor.
   * @return the table layout descriptor decoded from the file.
   * @throws Exception on error.
   */
  private TableLayoutDesc loadJsonTableLayoutDesc(String filePath) throws Exception {
    final Path path = new Path(filePath);
    final FileSystem fs = fileSystemSpecified(path)
        ? path.getFileSystem(getConf())
        : FileSystem.getLocal(getConf());
    final InputStream istream = fs.open(path);
    try {
      return KijiTableLayout.readTableLayoutDescFromJSON(istream);
    } finally {
      ResourceUtils.closeOrLog(istream);
      ResourceUtils.closeOrLog(fs);
    }
  }

  /**
   * Implements the --do=set operation.
   *
   * @param kiji Kiji instance.
   * @throws Exception on error.
   */
  private void setLayout(Kiji kiji) throws Exception {
    final TableLayoutDesc layoutDesc = loadJsonTableLayoutDesc(mLayout);
    Preconditions.checkArgument(mTableURI.getTable().equals(layoutDesc.getName()),
        "Descriptor table name '%s' does not match URI %s.",
        layoutDesc.getName(), mTableURI);
    kiji.modifyTableLayout(layoutDesc, mDryRun, getPrintStream());
  }

  /**
   * Dumps the history of layouts of a given table.
   *
   * @throws Exception on error.
   */
  private void history() throws Exception {
    // Gather all of the layouts stored in the metaTable.
    final NavigableMap<Long, KijiTableLayout> timedLayouts =
        mKiji.getMetaTable().getTimedTableLayoutVersions(mTableURI.getTable(), mMaxVersions);
    if (timedLayouts.isEmpty()) {
      throw new RuntimeException("No such table: " + mTableURI.getTable());
    }
    for (Map.Entry<Long, KijiTableLayout> entry: timedLayouts.entrySet()) {
      final long timestamp = entry.getKey();
      final KijiTableLayout layout = entry.getValue();
      final String json = ToJson.toJsonString(layout.getDesc());

      if (mWriteTo.isEmpty()) {
        // SCHEMA-14: Add a newline to separate the dumped JSON versions of the layout.
        System.out.printf("timestamp: %d:%n%s%n", timestamp, json);
      } else {
        final String fileName = String.format("%s-%d.json", mWriteTo, timestamp);
        final FileOutputStream fos = new FileOutputStream(fileName);
        try {
          fos.write(Bytes.toBytes(json));
        } finally {
          ResourceUtils.closeOrLog(fos);
        }
      }
    }
  }

  /** {@inheritDoc} */
  @Override
  protected void setup() throws Exception {
    super.setup();
    mKiji = Kiji.Factory.open(mTableURI, getConf());
  }

  /** {@inheritDoc} */
  @Override
  protected void cleanup() throws IOException {
    ResourceUtils.releaseOrLog(mKiji);
    mKiji = null;
    super.cleanup();
  }

  /** {@inheritDoc} */
  @Override
  protected int run(List<String> nonFlagArgs) throws Exception {
    if (mDo.equals("dump")) {
      dumpLayout();
    } else if (mDo.equals("set")) {
      Preconditions.checkArgument(!mLayout.isEmpty(),
          "Specify the layout with --layout=path/to/layout.json");
      setLayout(mKiji);
    } else if (mDo.equals("history")) {
      history();
    } else {
      System.err.println("Unknown layout action: " + mDo);
      System.err.println("Specify the action to perform with --do=(dump|set|history)");
      return FAILURE;
    }
    return SUCCESS;
  }

  /**
   * Determines whether a path has its filesystem explicitly specified.  Did it start
   * with "hdfs://" or "file://"?
   *
   * @param path The path to check.
   * @return Whether a file system was explicitly specified in the path.
   */
  protected static boolean fileSystemSpecified(Path path) {
    return null != path.toUri().getScheme();
  }

  /**
   * Program entry point.
   *
   * @param args The command-line arguments.
   * @throws Exception If there is an error.
   */
  public static void main(String[] args) throws Exception {
    System.exit(new KijiToolLauncher().run(new LayoutTool(), args));
  }
}
TOP

Related Classes of org.kiji.schema.tools.LayoutTool

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.