/*
* 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 gobblin.aws;
import java.util.Collections;
import java.util.List;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.hadoop.fs.Path;
import org.apache.helix.HelixManager;
import org.apache.helix.NotificationContext;
import org.apache.helix.messaging.handling.HelixTaskResult;
import org.apache.helix.messaging.handling.MessageHandler;
import org.apache.helix.messaging.handling.MessageHandlerFactory;
import org.apache.helix.model.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amazonaws.util.EC2MetadataUtils;
import com.google.common.base.Optional;
import com.google.common.util.concurrent.Service;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import gobblin.annotation.Alpha;
import gobblin.cluster.GobblinClusterConfigurationKeys;
import gobblin.cluster.GobblinClusterManager;
import gobblin.cluster.GobblinHelixTask;
import gobblin.cluster.GobblinHelixTaskFactory;
import gobblin.cluster.GobblinTaskRunner;
import gobblin.cluster.HelixMessageSubTypes;
import gobblin.util.JvmUtils;
/**
* Class running on worker nodes managing services for executing Gobblin
* {@link gobblin.source.workunit.WorkUnit}s.
*
* <p>
* This class makes use of super class {@link GobblinTaskRunner} to run:
* 1. {@link GobblinHelixTaskFactory} for creating {@link GobblinHelixTask}s that Helix manages
* to run Gobblin data ingestion tasks.
* 2. {@link HelixManager} to work with Helix and act as Helix participant to execute tasks.
*
* More AWS specific services can be added in future to this class that are required to be
* run on Gobblin cluster worker.
* </p>
*
* <p>
* Note: Shutdown initiated by {@link GobblinClusterManager} via a Helix message of subtype
* {@link HelixMessageSubTypes#WORK_UNIT_RUNNER_SHUTDOWN} is handled by super class {@link GobblinTaskRunner}
* </p>
*
* @author Abhishek Tiwari
*/
@Alpha
public class GobblinAWSTaskRunner extends GobblinTaskRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(GobblinTaskRunner.class);
public GobblinAWSTaskRunner(String applicationName, String helixInstanceName, Config config,
Optional<Path> appWorkDirOptional)
throws Exception {
super(applicationName, helixInstanceName, getApplicationId(), getTaskRunnerId(), config,
appWorkDirOptional);
}
@Override
public List<Service> getServices() {
return Collections.emptyList();
}
@Override
public MessageHandlerFactory getUserDefinedMessageHandlerFactory() {
return new ParticipantUserDefinedMessageHandlerFactory();
}
/**
* A custom {@link MessageHandlerFactory} for {@link ParticipantUserDefinedMessageHandler}s that
* handle messages of type {@link org.apache.helix.model.Message.MessageType#USER_DEFINE_MSG}.
*/
private static class ParticipantUserDefinedMessageHandlerFactory implements MessageHandlerFactory {
@Override
public MessageHandler createHandler(Message message, NotificationContext context) {
return new ParticipantUserDefinedMessageHandler(message, context);
}
@Override
public String getMessageType() {
return Message.MessageType.USER_DEFINE_MSG.toString();
}
public List<String> getMessageTypes() {
return Collections.singletonList(getMessageType());
}
@Override
public void reset() {
}
/**
* A custom {@link MessageHandler} for handling user-defined messages to the controller.
*
* <p>
* Currently does not handle any user-defined messages. If this class is passed a custom message, it will simply
* print out a warning and return successfully.
* </p>
*/
private static class ParticipantUserDefinedMessageHandler extends MessageHandler {
public ParticipantUserDefinedMessageHandler(Message message, NotificationContext context) {
super(message, context);
}
@Override
public HelixTaskResult handleMessage() throws InterruptedException {
LOGGER.warn(String
.format("No handling setup for %s message of subtype: %s", Message.MessageType.USER_DEFINE_MSG.toString(),
this._message.getMsgSubType()));
final HelixTaskResult helixTaskResult = new HelixTaskResult();
helixTaskResult.setSuccess(true);
return helixTaskResult;
}
@Override
public void onError(Exception e, ErrorCode code, ErrorType type) {
LOGGER.error(
String.format("Failed to handle message with exception %s, error code %s, error type %s", e, code, type));
}
}
}
private static String getApplicationId() {
return "1";
}
private static String getTaskRunnerId() {
return EC2MetadataUtils.getNetworkInterfaces().get(0).getPublicIPv4s().get(0);
}
public static Options buildOptions() {
final Options options = new Options();
options.addOption("a", GobblinClusterConfigurationKeys.APPLICATION_NAME_OPTION_NAME, true, "Application name");
options.addOption("i", GobblinClusterConfigurationKeys.HELIX_INSTANCE_NAME_OPTION_NAME, true, "Helix instance name");
options.addOption("d", GobblinAWSConfigurationKeys.APP_WORK_DIR, true, "Application work directory");
return options;
}
public static void main(String[] args) throws Exception {
final Options options = buildOptions();
try {
final CommandLine cmd = new DefaultParser().parse(options, args);
if (!cmd.hasOption(GobblinClusterConfigurationKeys.APPLICATION_NAME_OPTION_NAME) ||
!cmd.hasOption(GobblinClusterConfigurationKeys.HELIX_INSTANCE_NAME_OPTION_NAME) ||
!cmd.hasOption(GobblinAWSConfigurationKeys.APP_WORK_DIR)) {
printUsage(options);
System.exit(1);
}
Log4jConfigHelper.updateLog4jConfiguration(GobblinTaskRunner.class,
GobblinAWSConfigurationKeys.GOBBLIN_AWS_LOG4J_CONFIGURATION_FILE);
LOGGER.info(JvmUtils.getJvmInputArguments());
final String applicationName = cmd.getOptionValue(GobblinClusterConfigurationKeys.APPLICATION_NAME_OPTION_NAME);
final String helixInstanceName = cmd.getOptionValue(GobblinClusterConfigurationKeys.HELIX_INSTANCE_NAME_OPTION_NAME);
final String appWorkDir = cmd.getOptionValue(GobblinAWSConfigurationKeys.APP_WORK_DIR);
final GobblinTaskRunner gobblinTaskRunner =
new GobblinAWSTaskRunner(applicationName, helixInstanceName, ConfigFactory.load(),
Optional.of(new Path(appWorkDir)));
gobblinTaskRunner.start();
} catch (ParseException pe) {
printUsage(options);
System.exit(1);
}
}
}