com.asakusafw.runtime.windows.WinUtilsInstaller.java Source code

Java tutorial

Introduction

Here is the source code for com.asakusafw.runtime.windows.WinUtilsInstaller.java

Source

/**
 * 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.windows;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.util.Shell;
import org.xerial.snappy.OSInfo;

import com.asakusafw.runtime.io.util.TemporaryFileInstaller;

/**
 * Installs {@code winutils.exe} into the current environment.
 * @since 0.9.0
 */
public final class WinUtilsInstaller {

    static final Log LOG = LogFactory.getLog(WinUtilsInstaller.class);

    private static final Field WINUTILS_PATH;
    static {
        Field f = null;
        if (Shell.WINDOWS) {
            try {
                // only Hadoop 2.x has 'Shell.WINUTILS:String'
                f = Shell.class.getField("WINUTILS"); //$NON-NLS-1$
                f.setAccessible(true);
                if (f.getType() != String.class) {
                    throw new IllegalStateException(MessageFormat
                            .format("incompatible Shell.WINUTILS return type: {0}", f.getType().getName()));
                }

                // Hack: we try to remove final flag from 'Shell.WINUTILS'
                Field modifiers = Field.class.getDeclaredField("modifiers"); //$NON-NLS-1$
                modifiers.setAccessible(true);
                modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL);
            } catch (Exception e) {
                LOG.debug("current Hadoop version does not support winutils.exe", e); //$NON-NLS-1$
            }
        }
        WINUTILS_PATH = f;
    }

    private static final String ARCH_32 = "x86"; //$NON-NLS-1$

    private static final String ARCH_64 = "x86_64"; //$NON-NLS-1$

    private static final String SOURCE = getArchitecture() + "/winutils.exe"; //$NON-NLS-1$

    private static final String TARGET_PREFIX = "winutils-" + getUserHash(); //$NON-NLS-1$

    private static final String TARGET_SUFFIX = ".exe"; //$NON-NLS-1$

    private static final AtomicReference<TemporaryFileInstaller> CACHE = new AtomicReference<>();

    private WinUtilsInstaller() {
        return;
    }

    /**
     * Returns whether the current environment requires {@code winutils.exe} or not.
     * @return {@code true} if the current environment requires {@code winutils.exe}, otherwise {@code false}
     */
    public static boolean isTarget() {
        return WINUTILS_PATH != null;
    }

    /**
     * Returns whether the current environment already has {@code winutils.exe} or not.
     * @return {@code true} if the current environment already has {@code winutils.exe}, otherwise {@code false}
     */
    public static boolean isAlreadyInstalled() {
        if (isTarget() == false) {
            return false;
        }
        try {
            String path = (String) WINUTILS_PATH.get(null);
            return path != null && new File(path).canExecute();
        } catch (Exception e) {
            LOG.debug("exception occurred while checking winutils.exe", e); //$NON-NLS-1$
            return false;
        }
    }

    /**
     * Installs {@code winutils.exe} into the target directory.
     * @param directory the target directory
     * @return installed location (never null)
     * @throws IOException if error occurred while installing
     */
    public static File put(File directory) throws IOException {
        TemporaryFileInstaller installer = prepare();
        assert installer != null;

        // install into the default location
        File file = new File(directory, TARGET_PREFIX + TARGET_SUFFIX);
        try {
            LOG.info(MessageFormat.format("installing winutils.exe into default location: {0}", //$NON-NLS-1$
                    file));
            installer.install(file, true);
            return file;
        } catch (IOException e) {
            LOG.debug(MessageFormat.format("failed to install winutils into the default location: {0}", file), e);
        }

        // install into a temporary location
        File temp = null;
        boolean success = false;
        try {
            temp = File.createTempFile(TARGET_PREFIX + '-', TARGET_SUFFIX, directory);
            LOG.info(MessageFormat.format("installing winutils.exe into temporary location: {0}", temp));
            installer.install(temp, false);
            temp.deleteOnExit();
            success = true;
            return temp;
        } catch (IOException e) {
            LOG.debug(MessageFormat.format("failed to install winutils into a temporary location: {0}", //$NON-NLS-1$
                    temp), e);
        } finally {
            if (success == false) {
                if (temp != null && temp.delete() == false) {
                    LOG.warn(MessageFormat.format("failed to delete a temporary file: {0}", //$NON-NLS-1$
                            temp));
                }
            }
        }

        throw new IOException(MessageFormat.format("error occurred while installing winutils: {0}", //$NON-NLS-1$
                file));
    }

    /**
     * Registers a {@code winutils.exe} file.
     * @param executable the executable file path, or {@code null} to remove it
     */
    public static void register(File executable) {
        try {
            WINUTILS_PATH.set(null, executable == null ? null : executable.getAbsolutePath());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    private static TemporaryFileInstaller prepare() throws IOException {
        TemporaryFileInstaller installer = CACHE.get();
        if (installer != null) {
            return installer;
        }
        try (InputStream input = WinUtilsInstaller.class.getResourceAsStream(SOURCE)) {
            if (input == null) {
                throw new IllegalStateException(MessageFormat.format("missing {0} in the classpath", //$NON-NLS-1$
                        SOURCE));
            }
            installer = TemporaryFileInstaller.newInstance(input, true);
            CACHE.compareAndSet(null, installer);
        }
        return CACHE.get();
    }

    private static String getArchitecture() {
        String name = OSInfo.getArchName();
        if (name.equals(ARCH_32) || name.equals(ARCH_64)) {
            return name;
        }
        return ARCH_64;
    }

    private static String getUserHash() {
        String base = System.getProperty("user.name", UUID.randomUUID().toString()); //$NON-NLS-1$
        StringBuilder buf = new StringBuilder();
        for (char c : base.toCharArray()) {
            if ('0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '_' || c == '-') {
                buf.append(c);
            }
        }
        if (buf.length() < 4) {
            buf.append('-');
            buf.append(Integer.toHexString(base.hashCode()));
        }
        return buf.toString();
    }
}