/** * 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.bootstrap; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.security.AccessController; import java.security.PrivilegedAction; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.ServiceLoader; import java.util.TreeMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; import com.asakusafw.yaess.core.Extension; import com.asakusafw.yaess.core.ExtensionHandler; import com.asakusafw.yaess.core.YaessLogger; /** * Utilities for command line interfaces. * @since 0.2.3 * @version 0.8.0 */ public final class CommandLineUtil { static final YaessLogger YSLOG = new YaessBootstrapLogger(CommandLineUtil.class); static final Logger LOG = LoggerFactory.getLogger(CommandLineUtil.class); /** * Prefix of system properties used for log context. */ public static final String LOG_CONTEXT_PREFIX = "com.asakusafw.yaess.log."; /** * The scheme name of Java class path. */ public static final String SCHEME_CLASSPATH = "classpath"; /** * Prepares log context. * If current system properties contain keys with a special prefix * (described in {@link #LOG_CONTEXT_PREFIX}), * then this method will put each key-value pairs into log MDC. */ public static void prepareLogContext() { Map<String, String> registered = new TreeMap<>(); Properties properties = System.getProperties(); for (Map.Entry<Object, Object> entry : properties.entrySet()) { if ((entry.getKey() instanceof String) == false || (entry.getValue() instanceof String) == false) { continue; } String key = (String) entry.getKey(); if (key.startsWith(LOG_CONTEXT_PREFIX) == false) { continue; } String value = (String) entry.getValue(); String name = key.substring(LOG_CONTEXT_PREFIX.length()); MDC.put(name, value); registered.put(name, value); } LOG.debug("Log context is prepared: {}", registered); } /** * Loads properties from the specified file. * @param path path to the target properties * @return the loaded properties * @throws IOException if failed to load the properties * @throws IllegalArgumentException if any parameter is {@code null} */ public static Properties loadProperties(File path) throws IOException { if (path == null) { throw new IllegalArgumentException("path must not be null"); //$NON-NLS-1$ } LOG.debug("Loading properties: {}", path); try (FileInputStream in = new FileInputStream(path)) { Properties properties = new Properties(); properties.load(in); return properties; } } /** * Parses a string of file list separated by the platform dependent path separator. * @param fileListOrNull target string, or {@code null} * @return the represented file list, or an empty list if not specified */ public static List<File> parseFileList(String fileListOrNull) { if (fileListOrNull == null || fileListOrNull.isEmpty()) { return Collections.emptyList(); } List<File> results = new ArrayList<>(); int start = 0; while (true) { int index = fileListOrNull.indexOf(File.pathSeparatorChar, start); if (index < 0) { break; } if (start != index) { results.add(new File(fileListOrNull.substring(start, index).trim())); } start = index + 1; } results.add(new File(fileListOrNull.substring(start).trim())); return results; } /** * Creates a class loader for loading plug-ins. * @param parent parent class loader, or {@code null} to use the system class loader * @param files plug-in class paths (*.jar file or class path directory) * @return the created class loader * @throws IllegalArgumentException if some parameters were {@code null} */ public static ClassLoader buildPluginLoader(ClassLoader parent, List<File> files) { if (files == null) { throw new IllegalArgumentException("files must not be null"); //$NON-NLS-1$ } List<URL> pluginLocations = new ArrayList<>(); for (File file : files) { try { if (file.exists() == false) { throw new FileNotFoundException(MessageFormat.format( "Plug-in file/directory is not found \"{0}\"", file.getAbsolutePath())); } URL url = file.toURI().toURL(); pluginLocations.add(url); } catch (IOException e) { YSLOG.warn(e, "W99001", file.getAbsolutePath()); } } ClassLoader serviceLoader = AccessController.doPrivileged((PrivilegedAction<ClassLoader>) () -> { URLClassLoader loader = new URLClassLoader( pluginLocations.toArray(new URL[pluginLocations.size()]), parent); return loader; }); return serviceLoader; } /** * Loads extensions from extended arguments. * @param classLoader the extension class loader * @param arguments the target arguments * @return the loaded extensions * @throws IllegalArgumentException if extensions are something wrong */ public static List<Extension> loadExtensions(ClassLoader classLoader, List<ExtendedArgument> arguments) { List<Extension> results = new ArrayList<>(); ServiceLoader<ExtensionHandler> services = ServiceLoader.load(ExtensionHandler.class, classLoader); for (ExtendedArgument argument : arguments) { String name = argument.getName(); String value = argument.getValue(); boolean consumed = false; for (ExtensionHandler handler : services) { try { Extension extension = handler.handle(name, value); if (extension != null) { results.add(extension); consumed = true; break; } } catch (IOException e) { YSLOG.error(e, "E99001", name, value); for (Extension ext : results) { try { ext.close(); } catch (IOException inner) { e.addSuppressed(inner); } } throw new IllegalArgumentException(MessageFormat.format( "invalid extended argument \"{0}\"", argument), e); } } if (consumed == false) { YSLOG.error("E99001", name, value); throw new IllegalArgumentException(MessageFormat.format( "there is no available extension handler \"{0}\"", argument)); } } return results; } private CommandLineUtil() { return; } }