/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* Licensed 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.asakusafw.yaess.core;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.asakusafw.yaess.core.util.PropertiesUtil;
/**
* A skeletal implementation of {@link ExecutionScriptHandler}.
* Note that this class does not implement {@link ExecutionScriptHandler},
* so that subclasses must implement {@link HadoopScriptHandler} or {@link CommandScriptHandler}.
* @since 0.2.3
* @version 0.2.6
*/
public abstract class ExecutionScriptHandlerBase implements Service {
static final YaessLogger YSLOG = new YaessCoreLogger(ExecutionScriptHandlerBase.class);
static final Logger LOG = LoggerFactory.getLogger(ExecutionScriptHandlerBase.class);
private volatile String prefix;
private volatile String resourceId;
private volatile Map<String, String> properties;
private volatile Map<String, String> environmentVariables;
@Override
public final void configure(ServiceProfile<?> profile) throws InterruptedException, IOException {
this.prefix = profile.getPrefix();
try {
configureResourceId(profile);
Map<String, String> desiredProperties = getDesiredProperties(profile);
Map<String, String> desiredEnvironmentVariables = getDesiredEnvironmentVariables(profile);
this.properties = Collections.unmodifiableMap(desiredProperties);
this.environmentVariables = Collections.unmodifiableMap(desiredEnvironmentVariables);
doConfigure(profile, desiredProperties, desiredEnvironmentVariables);
} catch (IllegalArgumentException e) {
throw new IOException(MessageFormat.format(
"Failed to configure \"{0}\" ({1})",
profile.getPrefix(),
profile.getPrefix()), e);
}
}
/**
* Returns the ID of this handler.
* @return the ID
*/
public final String getHandlerId() {
return prefix;
}
private void configureResourceId(ServiceProfile<?> profile) {
assert profile != null;
String override = profile.getConfiguration(ExecutionScriptHandler.KEY_RESOURCE, false, true);
if (override == null) {
LOG.debug("resourceId is not override in {}",
profile.getPrefix());
resourceId = ExecutionScriptHandler.DEFAULT_RESOURCE_ID;
} else {
LOG.debug("resourceId is overriden in {}: {}",
profile.getPrefix(),
override);
resourceId = override;
}
}
private Map<String, String> getDesiredProperties(ServiceProfile<?> profile) throws IOException {
assert profile != null;
NavigableMap<String, String> vars = PropertiesUtil.createPrefixMap(
profile.getConfiguration(),
ExecutionScriptHandler.KEY_PROP_PREFIX);
Map<String, String> resolved = new TreeMap<>();
for (Map.Entry<String, String> entry : vars.entrySet()) {
String key = entry.getKey();
String unresolved = entry.getValue();
try {
String value = profile.getContext().getContextParameters().replace(unresolved, true);
resolved.put(key, value);
} catch (IllegalArgumentException e) {
YSLOG.error(e, "E10001",
profile.getPrefix(),
ExecutionScriptHandler.KEY_PROP_PREFIX,
key,
unresolved);
throw new IOException(MessageFormat.format(
"Failed to resolve a property: {0}.{1}.{2} = {3}",
profile.getPrefix(),
ExecutionScriptHandler.KEY_PROP_PREFIX,
key,
unresolved), e);
}
}
LOG.debug("Desired properties for {}: {}",
profile.getPrefix(),
resolved);
return resolved;
}
private Map<String, String> getDesiredEnvironmentVariables(ServiceProfile<?> profile) throws IOException {
assert profile != null;
NavigableMap<String, String> vars = PropertiesUtil.createPrefixMap(
profile.getConfiguration(),
ExecutionScriptHandler.KEY_ENV_PREFIX);
Map<String, String> resolved = new TreeMap<>();
for (Map.Entry<String, String> entry : vars.entrySet()) {
String key = entry.getKey();
String unresolved = entry.getValue();
try {
String value = profile.getContext().getContextParameters().replace(unresolved, true);
resolved.put(key, value);
} catch (IllegalArgumentException e) {
YSLOG.error(e, "E10001",
profile.getPrefix(),
ExecutionScriptHandler.KEY_ENV_PREFIX,
key,
unresolved);
throw new IOException(MessageFormat.format(
"Failed to resolve environment variable: {0}.{1}.{2} = {3}",
profile.getPrefix(),
ExecutionScriptHandler.KEY_ENV_PREFIX,
key,
unresolved), e);
}
}
LOG.debug("Desired environment variables for {}: {}",
profile.getPrefix(),
resolved);
return resolved;
}
/**
* Configures this handler internally (extention point).
* @param profile the profile of this service
* @param desiredProperties the current desired system/hadoop properties (configurable)
* @param desiredEnvironmentVariables the current desired environment variables (configurable)
* @throws InterruptedException if interrupted in configuration
* @throws IOException if failed to configure this service
*/
protected abstract void doConfigure(
ServiceProfile<?> profile,
Map<String, String> desiredProperties,
Map<String, String> desiredEnvironmentVariables) throws InterruptedException, IOException;
/**
* Returns the ID of a resource which is used for executing this handler.
* @param context the current execution context
* @param script the target script (nullable)
* @return the required resource ID
* @throws InterruptedException if this operation is interrupted
* @throws IOException if failed to setup the target environment
*/
public final String getResourceId(
ExecutionContext context,
ExecutionScript script) throws InterruptedException, IOException {
return resourceId;
}
/**
* Returns desired system/hadoop properties to execute scripts using this handler.
* @param context the current execution context
* @param script the target script (nullable)
* @return desired system or hadoop properties
* @throws InterruptedException if this operation is interrupted
* @throws IOException if failed to setup the target environment
*/
public Map<String, String> getProperties(
ExecutionContext context,
ExecutionScript script) throws InterruptedException, IOException {
return properties;
}
/**
* Returns desired environment variables to execute scripts using this handler.
* @param context the current execution context
* @param script the target script (nullable)
* @return desired environment variables
* @throws InterruptedException if this operation is interrupted
* @throws IOException if failed to setup the target environment
*/
public Map<String, String> getEnvironmentVariables(
ExecutionContext context,
ExecutionScript script) throws InterruptedException, IOException {
return environmentVariables;
}
/**
* Setup the target environment.
* @param monitor the progress monitor of the operation
* @param context the current execution context
* @throws InterruptedException if this operation is interrupted
* @throws IOException if failed to setup the target environment
*/
public void setUp(
ExecutionMonitor monitor,
ExecutionContext context) throws InterruptedException, IOException {
monitor.open(1);
try {
voidSetUp(context);
} finally {
monitor.close();
}
}
/**
* Cleanup the target environment.
* @param monitor the progress monitor of the operation
* @param context the current execution context
* @throws InterruptedException if this operation is interrupted
* @throws IOException if failed to setup the target environment
*/
public void cleanUp(
ExecutionMonitor monitor,
ExecutionContext context) throws InterruptedException, IOException {
monitor.open(1);
try {
voidCleanUp(context);
} finally {
monitor.close();
}
}
/**
* Performs as {@link #setUp(ExecutionMonitor, ExecutionContext)} that does nothing.
* @param context current context
* @since 0.4.0
*/
protected final void voidSetUp(ExecutionContext context) {
YSLOG.info("I51001",
context.getBatchId(),
context.getFlowId(),
context.getExecutionId(),
context.getPhase(),
getHandlerId());
}
/**
* Performs as {@link #cleanUp(ExecutionMonitor, ExecutionContext)} that does nothing.
* @param context current context
* @since 0.4.0
*/
protected final void voidCleanUp(ExecutionContext context) {
YSLOG.info("I51002",
context.getBatchId(),
context.getFlowId(),
context.getExecutionId(),
context.getPhase(),
getHandlerId());
}
}