/** * 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.mapred; import org.apache.hadoop.fs.*; import org.apache.hadoop.io.*; import org.apache.hadoop.mapred.UtilsForTests.RandomInputFormat; import org.apache.hadoop.mapreduce.MRConfig; import junit.framework.TestCase; import java.io.*; import java.util.*; /** * TestCollect checks if the collect can handle simultaneous invocations. */ public class TestCollect extends TestCase { final static Path OUTPUT_DIR = new Path("build/test/test.collect.output"); static final int NUM_FEEDERS = 10; static final int NUM_COLLECTS_PER_THREAD = 1000; /** * Map is a Mapper that spawns threads which simultaneously call collect. * Each thread has a specific range to write to the buffer and is unique to * the thread. This is a synchronization test for the map's collect. */ static class Map implements Mapper<Text, Text, IntWritable, IntWritable> { public void configure(JobConf job) { } public void map(Text key, Text val, final OutputCollector<IntWritable, IntWritable> out, Reporter reporter) throws IOException { // Class for calling collect in separate threads class CollectFeeder extends Thread { int id; // id for the thread public CollectFeeder(int id) { this.id = id; } public void run() { for (int j = 1; j <= NUM_COLLECTS_PER_THREAD; j++) { try { out.collect(new IntWritable((id * NUM_COLLECTS_PER_THREAD) + j), new IntWritable(0)); } catch (IOException ioe) { } } } } CollectFeeder [] feeders = new CollectFeeder[NUM_FEEDERS]; // start the feeders for (int i = 0; i < NUM_FEEDERS; i++) { feeders[i] = new CollectFeeder(i); feeders[i].start(); } // wait for them to finish for (int i = 0; i < NUM_FEEDERS; i++) { try { feeders[i].join(); } catch (InterruptedException ie) { throw new IOException(ie.toString()); } } } public void close() { } } static class Reduce implements Reducer<IntWritable, IntWritable, IntWritable, IntWritable> { static int numSeen; static int actualSum; public void configure(JobConf job) { } public void reduce(IntWritable key, Iterator<IntWritable> val, OutputCollector<IntWritable, IntWritable> out, Reporter reporter) throws IOException { actualSum += key.get(); // keep the running count of the seen values numSeen++; // number of values seen so far // using '1+2+3+...n = n*(n+1)/2' to validate int expectedSum = numSeen * (numSeen + 1) / 2; if (expectedSum != actualSum) { throw new IOException("Collect test failed!! Ordering mismatch."); } } public void close() { } } public void configure(JobConf conf) throws IOException { conf.setJobName("TestCollect"); conf.setJarByClass(TestCollect.class); conf.setInputFormat(RandomInputFormat.class); // for self data generation conf.setOutputKeyClass(IntWritable.class); conf.setOutputValueClass(IntWritable.class); FileOutputFormat.setOutputPath(conf, OUTPUT_DIR); conf.set(MRConfig.FRAMEWORK_NAME, MRConfig.LOCAL_FRAMEWORK_NAME); conf.setMapperClass(Map.class); conf.setReducerClass(Reduce.class); conf.setNumMapTasks(1); conf.setNumReduceTasks(1); } public void testCollect() throws IOException { JobConf conf = new JobConf(); configure(conf); try { JobClient.runJob(conf); // check if all the values were seen by the reducer if (Reduce.numSeen != (NUM_COLLECTS_PER_THREAD * NUM_FEEDERS)) { throw new IOException("Collect test failed!! Total does not match."); } } catch (IOException ioe) { throw ioe; } finally { FileSystem fs = FileSystem.get(conf); fs.delete(OUTPUT_DIR, true); } } public static void main(String[] args) throws IOException { new TestCollect().testCollect(); } }