/** * 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.util.hadoop; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import java.util.Properties; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.mapreduce.JobContext; /** * Utilities for Hadoop installations. * @since 0.9.0 */ public final class InstallationUtil { static final Log LOG = LogFactory.getLog(InstallationUtil.class); private static final String PATH_HADOOP_INFO = "META-INF/asakusa-runtime/hadoop.properties"; //$NON-NLS-1$ private static final String KEY_TARGET_VERSION = "version"; //$NON-NLS-1$ /** * Represents the target version. */ static final FrameworkVersion TARGET; static { FrameworkVersion detected = FrameworkVersion.DONT_CARE; try (InputStream input = InstallationUtil.class.getClassLoader().getResourceAsStream(PATH_HADOOP_INFO)) { if (input == null) { throw new FileNotFoundException(PATH_HADOOP_INFO); } Properties p = new Properties(); try { p.load(input); } finally { input.close(); } String value = p.getProperty(KEY_TARGET_VERSION); FrameworkVersion version = FrameworkVersion.find(value); if (value == null) { LOG.warn("target Hadoop version is not defined"); } else if (version == null) { LOG.warn(MessageFormat.format( "unknown target Hadoop version: {0}", value)); } else { detected = version; LOG.debug(MessageFormat.format( "detect target Hadoop version: {1} ({0})", //$NON-NLS-1$ detected, value)); } } catch (IOException e) { LOG.warn("failed to detect current target Hadoop version", e); detected = FrameworkVersion.DONT_CARE; } TARGET = detected; } private InstallationUtil() { return; } /** * Verifies the running core framework version. */ public static void verifyFrameworkVersion() { FrameworkVersion running = FrameworkVersion.get(); if (TARGET.isCompatibleTo(running) == false) { throw new IllegalStateException(MessageFormat.format( "Inconsistent environment version: expected-version={0}, installed-version={1}", TARGET, running)); } } /** * Represents the core framework version. * @since 0.9.0 */ public enum FrameworkVersion { /** * Don't care for temporary. */ DONT_CARE("<don't-care>", "\\$\\{.*\\}") { //$NON-NLS-1$ //$NON-NLS-2$ @Override public boolean isCompatibleTo(FrameworkVersion running) { // don't care return true; } }, /** * Represents Hadoop {@code 1.x}. */ HADOOP_V1("hadoop-1.x", "1\\..*"), //$NON-NLS-1$ //$NON-NLS-2$ /** * Represents Hadoop {@code 2.x} with YARN. */ HADOOP_V2("hadoop-2.x", "2\\..*") { //$NON-NLS-1$ //$NON-NLS-2$ @Override public boolean isCompatibleTo(FrameworkVersion running) { return this == running || running == HADOOP_V2_MR1; } }, /** * Represents Hadoop {@code 2.x} with {@code MRv1}. */ HADOOP_V2_MR1("hadoop-2.x-MRv1"), //$NON-NLS-1$ /** * Represents unknown framework. */ UNKNOWN("hadoop-<UNKNOWN_VERSION>"), //$NON-NLS-1$ ; final String label; final Pattern pattern; FrameworkVersion(String label) { this(label, null); } FrameworkVersion(String label, String pattern) { this.label = label; this.pattern = pattern == null ? null : Pattern.compile(pattern); } /** * Returns whether or not this version is compatible to the specified running version. * @param running the running version * @return {@code true} if it is compatible for this version, otherwise {@code false} */ public boolean isCompatibleTo(FrameworkVersion running) { return this == running; } /** * Returns a version constant from its name. * @param name the version name * @return the related version, or {@code null} if there is no such a version */ public static FrameworkVersion find(String name) { for (FrameworkVersion version : values()) { if (version.isMember(name)) { return version; } } return null; } boolean isMember(String name) { if (pattern != null) { return pattern.matcher(name).matches(); } return false; } /** * Returns the currently linked framework version. * @return the currently linked version */ public static FrameworkVersion get() { return Lazy.VERSION; } @Override public String toString() { return label; } private static final class Lazy { static final FrameworkVersion VERSION; static { FrameworkVersion detected = UNKNOWN; if (isHadoopV2()) { detected = HADOOP_V2; } else if (isHadoopV2MR1()) { detected = HADOOP_V2_MR1; } else { detected = HADOOP_V1; } if (LOG.isDebugEnabled()) { LOG.debug(MessageFormat.format( "Detected installed framework: {0}", //$NON-NLS-1$ detected)); } VERSION = detected; } private static boolean isHadoopV2() { // v1 - DistributedCache exists in hadoop-core // v2 - DistributedCache exists in hadoop-mapreduce-client-core, and it is deprecated try { ClassLoader cl = InstallationUtil.class.getClassLoader(); Class<?> c = cl.loadClass("org.apache.hadoop.filecache.DistributedCache"); // //$NON-NLS-1$ return c.isAnnotationPresent(Deprecated.class); } catch (ClassNotFoundException e) { return true; } } private static boolean isHadoopV2MR1() { // JobContext is interface in v2 even if the current environment is MRv1 return JobContext.class.isInterface(); } } } }