/**
* 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.runtime.stage.optimizer;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapreduce.Job;
import com.asakusafw.runtime.stage.StageConfigurator;
import com.asakusafw.runtime.stage.StageUtil;
import com.asakusafw.runtime.stage.launcher.ApplicationLauncher;
/**
* Configures to suppress library copies in local mode.
* @since 0.7.0
*/
public class LibraryCopySuppressionConfigurator extends StageConfigurator {
static final Log LOG = LogFactory.getLog(LibraryCopySuppressionConfigurator.class);
/**
* The configuration key of whether this feature is enabled or not.
*/
public static final String KEY_ENABLED =
"com.asakusafw.runtime.stage.optimizer.libraryCopySuppression.enabled"; //$NON-NLS-1$
/**
* The default configuration value of {@link #KEY_ENABLED}.
*/
public static final boolean DEFAULT_ENABLED = false;
static final String KEY_CONF_LIBRARIES = "tmpjars"; //$NON-NLS-1$
static final Method CONFIGURATION_UNSET;
static {
Method method;
try {
method = Configuration.class.getMethod("unset", String.class); //$NON-NLS-1$
} catch (Exception e) {
method = null;
}
CONFIGURATION_UNSET = method;
}
@Override
public void configure(Job job) throws IOException, InterruptedException {
Configuration conf = job.getConfiguration();
if (conf.getBoolean(KEY_ENABLED, DEFAULT_ENABLED) == false) {
return;
}
// activates only if application launcher is used
if (conf.getBoolean(ApplicationLauncher.KEY_LAUNCHER_USED, false) == false) {
return;
}
if (StageUtil.isLocalMode(job) == false) {
return;
}
String libraries = conf.get(KEY_CONF_LIBRARIES);
if (libraries == null || libraries.isEmpty()) {
return;
}
Set<String> loaded = new HashSet<>();
ClassLoader loader = conf.getClassLoader();
if (loader instanceof URLClassLoader) {
for (URL url : ((URLClassLoader) loader).getURLs()) {
try {
loaded.add(url.toURI().toString());
} catch (URISyntaxException e) {
LOG.warn(MessageFormat.format(
"Failed to analyze classpath: {0}",
url));
}
}
}
if (loaded.isEmpty()) {
return;
}
StringBuilder result = new StringBuilder();
for (String library : libraries.split(",")) { //$NON-NLS-1$
if (loaded.contains(library)) {
if (LOG.isDebugEnabled()) {
LOG.debug(MessageFormat.format("Keep library: {0}", library)); //$NON-NLS-1$
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug(MessageFormat.format("Suppress library: {0}", library)); //$NON-NLS-1$
}
if (result.length() != 0) {
result.append(',');
}
result.append(library);
}
}
if (result.length() > 0) {
conf.set(KEY_CONF_LIBRARIES, result.toString());
} else {
if (CONFIGURATION_UNSET != null) {
try {
CONFIGURATION_UNSET.invoke(conf, KEY_CONF_LIBRARIES);
return;
} catch (Exception e) {
LOG.warn(MessageFormat.format(
"Failed to invoke {0}",
CONFIGURATION_UNSET), e);
}
}
String newLibraries = selectLibraries(libraries);
conf.set(KEY_CONF_LIBRARIES, newLibraries);
}
}
static String selectLibraries(String libraries) {
String minLibrary = null;
long minSize = Long.MAX_VALUE;
for (String library : libraries.split(",")) { //$NON-NLS-1$
Path path = new Path(library);
String scheme = path.toUri().getScheme();
if (scheme != null && scheme.equals("file")) { //$NON-NLS-1$
File file = new File(path.toUri());
long size = file.length();
if (size < minSize) {
minLibrary = library;
minSize = size;
}
}
}
if (minLibrary != null) {
return minLibrary;
}
return libraries;
}
}