content: /*
   * 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.hadoop.chukwa.extraction.demux;
  
  
  import java.io.File;
  import java.io.IOException;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Date;
  import java.util.Iterator;
  import java.util.List;
  
  import org.apache.hadoop.chukwa.ChukwaArchiveKey;
  import org.apache.hadoop.chukwa.ChunkImpl;
  import org.apache.hadoop.chukwa.conf.ChukwaConfiguration;
  import org.apache.hadoop.chukwa.extraction.demux.processor.ChukwaOutputCollector;
  import org.apache.hadoop.chukwa.extraction.demux.processor.mapper.MapProcessor;
  import org.apache.hadoop.chukwa.extraction.demux.processor.mapper.MapProcessorFactory;
  import org.apache.hadoop.chukwa.extraction.demux.processor.reducer.ReduceProcessorFactory;
  import org.apache.hadoop.chukwa.extraction.demux.processor.reducer.ReduceProcessor;
  import org.apache.hadoop.chukwa.extraction.engine.ChukwaRecord;
  import org.apache.hadoop.chukwa.extraction.engine.ChukwaRecordKey;
  import org.apache.hadoop.chukwa.util.ExceptionUtil;
  import org.apache.hadoop.conf.Configuration;
  import org.apache.hadoop.conf.Configured;
  import org.apache.hadoop.filecache.DistributedCache;
  import org.apache.hadoop.fs.FileStatus;
  import org.apache.hadoop.fs.FileSystem;
  import org.apache.hadoop.fs.Path;
  import org.apache.hadoop.mapred.FileInputFormat;
  import org.apache.hadoop.mapred.FileOutputFormat;
  import org.apache.hadoop.mapred.JobClient;
  import org.apache.hadoop.mapred.JobConf;
  import org.apache.hadoop.mapred.JobPriority;
  import org.apache.hadoop.mapred.MapReduceBase;
  import org.apache.hadoop.mapred.Mapper;
  import org.apache.hadoop.mapred.OutputCollector;
  import org.apache.hadoop.mapred.Reducer;
  import org.apache.hadoop.mapred.Reporter;
  import org.apache.hadoop.mapred.SequenceFileInputFormat;
  import org.apache.hadoop.util.Tool;
  import org.apache.hadoop.util.ToolRunner;
  import org.apache.log4j.Logger;
  
  public class Demux extends Configured implements Tool {
    static Logger log = Logger.getLogger(Demux.class);
    public static Configuration jobConf = null;
    protected static void setJobConf(JobConf jobConf) {
      Demux.jobConf = jobConf;
    }
  
    protected Configuration getJobConf() {
      return Demux.jobConf;
    }
  
    public static class MapClass extends MapReduceBase implements
            Mapper<ChukwaArchiveKey, ChunkImpl, ChukwaRecordKey, ChukwaRecord> {
  
      private Configuration jobConf = null;
  
      @Override
      public void configure(JobConf jobConf) {
        super.configure(jobConf);
        setJobConf(jobConf);
      }
  
      private void setJobConf(JobConf jobConf) {
        this.jobConf = jobConf;
      }
  
      public void map(ChukwaArchiveKey key, ChunkImpl chunk,
                      OutputCollector<ChukwaRecordKey, ChukwaRecord> output, Reporter reporter)
              throws IOException {
  
        ChukwaOutputCollector chukwaOutputCollector = new ChukwaOutputCollector(
                "DemuxMapOutput", output, reporter);
        try {
          long duration = System.currentTimeMillis();
          if (log.isDebugEnabled()) {
            log.debug("Entry: [" + String.valueOf(chunk.getData()) + "] EventType: ["
                    + chunk.getDataType() + "]");
          }
  
          String defaultProcessor = jobConf.get(
                  "chukwa.demux.mapper.default.processor",
                  "org.apache.hadoop.chukwa.extraction.demux.processor.mapper.DefaultProcessor");
  
          String processorClass_pri = jobConf.get(chunk.getDataType(),
                  defaultProcessor);
  
          String processorClass = processorClass_pri.split(",")[0];
          if (!processorClass.equalsIgnoreCase("Drop")) {
            reporter.incrCounter("DemuxMapInput", "total chunks", 1);
            reporter.incrCounter("DemuxMapInput",
                    chunk.getDataType() + " chunks", 1);
  
            MapProcessor processor = MapProcessorFactory
                    .getProcessor(processorClass);
            processor.process(key, chunk, chukwaOutputCollector, reporter);
            if (log.isDebugEnabled()) {
              duration = System.currentTimeMillis() - duration;
              log.debug("Demux:Map dataType:" + chunk.getDataType()
                      + " duration:" + duration + " processor:" + processorClass
                      + " recordCount:" + chunk.getRecordOffsets().length);
            }
  
          } else {
            log.info("action:Demux, dataType:" + chunk.getDataType()
                    + " duration:0 processor:Drop recordCount:"
                    + chunk.getRecordOffsets().length);
          }
  
        } catch (Exception e) {
          log.warn("Exception in Demux:MAP", e);
          e.printStackTrace();
        }
      }
    }
  
    public static class ReduceClass extends MapReduceBase implements
            Reducer<ChukwaRecordKey, ChukwaRecord, ChukwaRecordKey, ChukwaRecord> {
  
      private Configuration jobConf = null;
  
      public void configure(JobConf jobConf) {
        super.configure(jobConf);
        this.jobConf = jobConf;
      }
  
      public void reduce(ChukwaRecordKey key, Iterator<ChukwaRecord> values,
                         OutputCollector<ChukwaRecordKey, ChukwaRecord> output, Reporter reporter)
              throws IOException {
        ChukwaOutputCollector chukwaOutputCollector = new ChukwaOutputCollector(
                "DemuxReduceOutput", output, reporter);
        try {
          long duration = System.currentTimeMillis();
          reporter.incrCounter("DemuxReduceInput", "total distinct keys", 1);
          reporter.incrCounter("DemuxReduceInput", key.getReduceType()
                  + " total distinct keys", 1);
  
          String defaultProcessor_classname = "org.apache.hadoop.chukwa.extraction.demux.processor.reducer" +
                  ".IdentityReducer";
          String defaultProcessor = jobConf.get("chukwa.demux.reducer.default.processor",
                  "," + defaultProcessor_classname);
  
          String processClass_pri = jobConf.get(key.getReduceType(), defaultProcessor);
          String[] processClass_tmps = processClass_pri.split(",");
          String processClass = null;
          if (processClass_tmps.length != 2)
            processClass = defaultProcessor_classname;
          else
            processClass = processClass_tmps[1];
  
          ReduceProcessor processor = ReduceProcessorFactory.getProcessor(processClass);
          System.out.println(processor.getClass().getName());
          processor.process(key, values, chukwaOutputCollector, reporter);
  
          if (log.isDebugEnabled()) {
            duration = System.currentTimeMillis() - duration;
            log.debug("Demux:Reduce, dataType:" + key.getReduceType()
                    + " duration:" + duration);
          }
  
        } catch (Exception e) {
          log.warn("Exception in Demux:Reduce", e);
          e.printStackTrace();
        }
      }
    }
  
    static int printUsage() {
      System.out.println("Demux [-m <maps>] [-r <reduces>] <input> <output>");
      ToolRunner.printGenericCommandUsage(System.out);
      return -1;
    }
  
    public static void addParsers(Configuration conf) {
      String parserPath = conf.get("chukwa.data.dir") + File.separator + "demux";
      try {
        FileSystem fs = FileSystem.get(new Configuration());
        FileStatus[] fstatus = fs.listStatus(new Path(parserPath));
        if (fstatus != null) {
          String hdfsUrlPrefix = conf.get("fs.defaultFS");
  
          for (FileStatus parser : fstatus) {
            Path jarPath = new Path(parser.getPath().toString().replace(hdfsUrlPrefix, ""));
            log.debug("Adding parser JAR path " + jarPath);
            DistributedCache.addFileToClassPath(jarPath, conf);
          }
        }
      } catch (IOException e) {
        log.error(ExceptionUtil.getStackTrace(e));
      }
    }
  
    public int run(String[] args) throws Exception {
      JobConf conf = new JobConf(new ChukwaConfiguration(), Demux.class);
  
      SimpleDateFormat day = new java.text.SimpleDateFormat("yyyyMMdd_HH_mm");
      conf.setJobName("Chukwa-Demux_" + day.format(new Date()));
      conf.setInputFormat(SequenceFileInputFormat.class);
      conf.setMapperClass(Demux.MapClass.class);
      conf.setPartitionerClass(ChukwaRecordPartitioner.class);
      conf.setReducerClass(Demux.ReduceClass.class);
  
      conf.setOutputKeyClass(ChukwaRecordKey.class);
      conf.setOutputValueClass(ChukwaRecord.class);
      conf.setOutputFormat(ChukwaRecordOutputFormat.class);
      conf.setJobPriority(JobPriority.VERY_HIGH);
      addParsers(conf);
  
      List<String> other_args = new ArrayList<String>();
      for (int i = 0; i < args.length; ++i) {
        try {
          if ("-m".equals(args[i])) {
            conf.setNumMapTasks(Integer.parseInt(args[++i]));
          } else if ("-r".equals(args[i])) {
            conf.setNumReduceTasks(Integer.parseInt(args[++i]));
          } else {
            other_args.add(args[i]);
          }
        } catch (NumberFormatException except) {
          System.out.println("ERROR: Integer expected instead of " + args[i]);
          return printUsage();
        } catch (ArrayIndexOutOfBoundsException except) {
          System.out.println("ERROR: Required parameter missing from "
                  + args[i - 1]);
          return printUsage();
        }
      }
      // Make sure there are exactly 2 parameters left.
      if (other_args.size() != 2) {
        System.out.println("ERROR: Wrong number of parameters: "
                + other_args.size() + " instead of 2.");
        return printUsage();
      }
  
      FileInputFormat.setInputPaths(conf, other_args.get(0));
      FileOutputFormat.setOutputPath(conf, new Path(other_args.get(1)));
  
      JobClient.runJob(conf);
      return 0;
    }
  
    public static void main(String[] args) throws Exception {
      int res = ToolRunner.run(new Configuration(), new Demux(), args);
      return;
    }
  
  }
  