/*
* 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;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.aliyun.odps.OdpsException;
import com.aliyun.odps.mapred.conf.JobConf;
import com.aliyun.odps.mapred.conf.SessionState;
import com.aliyun.odps.pipeline.Pipeline;
import com.aliyun.odps.utils.ReflectionUtils;
/**
* ODPS MapReduce作业的客户端.
*
* 提供两个方法用于提交MapReduce作业:<br/>
* (1)阻塞(同步)方式:{@link #runJob(JobConf)},提交作业并等待作业结束。<br/>
* (2)非阻塞(异步)方式:{@link #submitJob(JobConf)},提交作业立即返回。<br/>
*
* @see JobConf
*/
public class JobClient {
private static final Log LOG = LogFactory.getLog(JobClient.class);
/**
* 阻塞(同步)方式提交MapReduce作业并等待作业结束,作业成功则返回作业运行时的对象 {@link RunningJob}.
*
* <p>
* 以下情况发生时抛{@link OdpsException}:
* <ul>
* <li>提交作业时异常
* <li>轮询作业状态异常
* <li>作业失败,注意:这与{@link #submitJob(JobConf)}异常行为不同
* </ul>
* </p>
*
* <p>
* 作业主程序(main函数)需要谨慎处理该异常,因为会影响到console的返回值: <br/>
* 如果不catch异常,作业失败时会抛出异常,console返回值为非0;如果catch异常且不再向外抛出,即使作业失败,console返回值也为0。<br/>
* </p>
*
* 示例代码:
*
* <pre>
* Job job = new JobConf();
* ... //config job
* JobClient.runJob(job); // will throw exception when job failed
* </pre>
*
* @param job
* MapReduce作业配置
* @return MapReduce作业运行时对象
* @throws OdpsException
* 作业提交或运行失败时抛异常
* @see #submitJob(JobConf)
*/
public static RunningJob runJob(JobConf job) throws OdpsException {
Long baseTime = new Date().getTime();
RunningJob rJob = submitJob(job, baseTime);
rJob.waitForCompletion();
if (!rJob.isSuccessful()) {
throw new OdpsException(rJob.getDiagnostics());
}
return rJob;
}
/**
* 阻塞(同步)方式提交Pipeline作业并等待作业结束,作业成功则返回作业运行时的对象 {@link RunningJob}.
*
* @param job
* 作业全局配置
* @param pipeline
* pipeline实例
* @return 作业运行时对象
* @throws OdpsException
* 作业提交或运行失败时抛异常
*/
public static RunningJob runJob(JobConf job, Pipeline pipeline) throws OdpsException {
Pipeline.toJobConf(job, pipeline);
return runJob(job);
}
/**
* 非阻塞(异步)方式提交MapReduce作业后立即返回,作业提交成功即返回作业运行时对象 {@link RunningJob}.
*
* <p>
* 只有当提交作业发生异常抛{@link OdpsException}(注意:这与 {@link #runJob(JobConf)} 异常行为不同,
* {@link #runJob(JobConf)} 在作业失败时会抛异常),所以如果使用 JobClient.submitJob,需要通过返回的 {@link RunningJob}
* 对象提供的方法判断作业是否运行成功。
* </p>
* <p>
* 取得{@link RunningJob}对象后,可以轮询作业状态,示例代码:
*
* <pre>
* Job job = new JobConf();
* ... //config job
* RunningJob rJob = JobClient.submitJob(job);
* while (!rJob.isComplete()) {
* Thread.sleep(4000); // do your work or sleep
* }
* if (rJob.isSuccessful()) {
* System.out.println("Job Success!");
* } else {
* System.err.println("Job Failed!");
* }
* </pre>
*
* 或者直接使用 {@link RunningJob#waitForCompletion()} 轮询作业直至作业结束,示例代码:
*
* <pre>
* Job job = new JobConf();
* ... //config job
* RunningJob rJob = JobClient.submitJob(job);
* rJob.waitForCompletion();
* if (rJob.isSuccessful()) {
* System.out.println("Job Success!");
* } else {
* System.err.println("Job Failed!");
* }
* </pre>
*
* </p>
*
* @param job
* MapReduce作业配置
* @return MapReduce作业运行时对象
* @throws OdpsException
* 作业提交失败时抛异常
* @see #runJob(JobConf)
*/
public static RunningJob submitJob(JobConf job) throws OdpsException {
return submitJob(job, 0L);
}
public static RunningJob submitJob(JobConf job, Pipeline pipeline) throws OdpsException {
Pipeline.toJobConf(job, pipeline);
return submitJob(job);
}
@SuppressWarnings("unchecked")
private static RunningJob submitJob(JobConf job, Long baseTime) throws OdpsException {
SessionState ss = SessionState.get();
String runner = null;
if (ss.isLocalRun()) {
runner = "com.aliyun.odps.mapred.LocalJobRunner";
} else {
runner = "com.aliyun.odps.mapred.LotBridgeJobRunner";
}
JobRunner jobrunner = null;
try {
Class<? extends JobRunner> clz = (Class<? extends JobRunner>) Class.forName(runner);
jobrunner = ReflectionUtils.newInstance(clz, job);
} catch (ClassNotFoundException e) {
LOG.fatal("Internal error: corrupted installation.", e);
throw new RuntimeException(e);
}
RunningJob rj = jobrunner.submit();
System.out.println("InstanceId: " + rj.getInstanceID());
return rj;
}
}