/*
* 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 com.aliyun.odps.mapred.bridge.utils;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import com.aliyun.odps.data.Record;
import com.aliyun.odps.data.TableInfo;
import com.aliyun.odps.mapred.Mapper;
import com.aliyun.odps.mapred.Reducer;
import com.aliyun.odps.mapred.TaskContext;
import com.aliyun.odps.utils.ReflectionUtils;
public class MapReduceUtils {
/**
* Get method from class <code>clz</code> whose name is specified as
* <code>m</code> and arguments are specified as <code>args</code>. Note that
* the function will traverse parent classes if the declared method is not
* exist in current class.
*
* @param clz
* @param m
* @param args
* @return the finding method, or null if non-exist.
*/
static Method getOverriddenMethod(Class<?> clz, String m, Class<?>... args) {
Method method = null;
while (clz != null) {
method = ReflectionUtils.findUserClassMethod(clz, m);
if (method != null && isAssignable(method.getParameterTypes(), args)) {
return method;
}
clz = clz.getSuperclass();
}
return null;
}
/**
* Check if is parameters are assignable.
*
* @param parameterTypes
* @param args
* @return
*/
private static boolean isAssignable(Class<?>[] parameterTypes, Class<?>[] args) {
if (parameterTypes == null || args == null || parameterTypes.length != args.length) {
return false;
}
for (int i = 0; i < parameterTypes.length; i++) {
if (!args[i].isAssignableFrom(parameterTypes[i])) {
return false;
}
}
return true;
}
private static void handleException(InvocationTargetException e) throws IOException {
if (e.getTargetException() instanceof IOException) {
throw (IOException) e.getTargetException();
} else if (e.getTargetException() instanceof Error) {
throw (Error) e.getTargetException();
} else if (e.getTargetException() instanceof RuntimeException) {
throw (RuntimeException) e.getTargetException();
} else {
throw new RuntimeException(e.getTargetException());
}
}
/**
* Run mapper using reflection.
*
* @param clz
* @param ctx
* @throws IOException
*/
public static void runMapper(Class<Mapper> clz, Mapper.TaskContext ctx) throws IOException {
Mapper mapper = (Mapper) ReflectionUtils.newInstance(clz, ctx.getJobConf());
try {
// should add a flag to this for compatibility
if (ctx.getJobConf().getBoolean("odps.mapred.run.interface.enable", true)) {
Method m = getOverriddenMethod(clz, "run", TaskContext.class);
if (m != null) {
m.invoke(mapper, ctx);
return;
}
}
Method m = getOverriddenMethod(clz, "setup", TaskContext.class);
if (m != null) {
m.invoke(mapper, ctx);
}
m = getOverriddenMethod(clz, "map", long.class, Record.class, TaskContext.class);
if (m != null) {
while (ctx.nextRecord()) {
m.invoke(mapper, ctx.getCurrentRecordNum(), ctx.getCurrentRecord(), ctx);
}
}
m = getOverriddenMethod(clz, "cleanup", TaskContext.class);
if (m != null) {
m.invoke(mapper, ctx);
}
} catch (InvocationTargetException e) {
handleException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Run combiner/reducer using reflection
*
* @param clz
* @param ctx
* @throws IOException
*/
public static void runReducer(Class<Reducer> clz, Reducer.TaskContext ctx) throws IOException {
Reducer combiner = (Reducer) ReflectionUtils.newInstance(clz, ctx.getJobConf());
try {
// should add a flag to this for compatibility
if (ctx.getJobConf().getBoolean("odps.mapred.run.interface.enable", true)) {
Method m = getOverriddenMethod(clz, "run", TaskContext.class);
if (m != null) {
m.invoke(combiner, ctx);
return;
}
}
Method m = getOverriddenMethod(clz, "setup", TaskContext.class);
if (m != null) {
m.invoke(combiner, ctx);
}
m = getOverriddenMethod(clz, "reduce", Record.class, Iterator.class, TaskContext.class);
if (m != null) {
while (ctx.nextKeyValue()) {
m.invoke(combiner, ctx.getCurrentKey(), ctx.getValues(), ctx);
}
}
m = getOverriddenMethod(clz, "cleanup", TaskContext.class);
if (m != null) {
m.invoke(combiner, ctx);
}
} catch (InvocationTargetException e) {
handleException(e);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Check if TableInfo existing is semantically included by desc.
*
* @param desc
* @param existing
* @return
*/
public static boolean partSpecInclusive(TableInfo desc, TableInfo existing) {
if (!desc.getProjectName().equalsIgnoreCase(existing.getProjectName()) || !desc.getTableName()
.equalsIgnoreCase(existing.getTableName())) {
return false;
}
if (desc.getPartitionSpec() != null) {
for (String key : desc.getPartSpec().keySet()) {
if (!desc.getPartSpec().get(key).equals(existing.getPartSpec().get(key))) {
return false;
}
}
}
return true;
}
}