001/*
002 * Copyright (c) 2012-2021 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package org.eclipse.golo.cli.command;
012
013import com.beust.jcommander.Parameter;
014import com.beust.jcommander.Parameters;
015import com.beust.jcommander.converters.IParameterSplitter;
016
017import java.io.File;
018import java.net.MalformedURLException;
019import java.net.URL;
020import java.net.URLClassLoader;
021import java.util.List;
022import java.util.LinkedList;
023import java.util.Collections;
024import java.util.Arrays;
025
026import org.eclipse.golo.compiler.GoloClassLoader;
027
028/**
029 * Manage the classpath and initialize the class loader.
030 */
031@Parameters(resourceBundle = "commands")
032public class ClasspathOption {
033
034  /**
035   * The system property to define the path.
036   */
037  public static final String PROPERTY = "golo.class.path";
038
039  /**
040   * The environment variable used to define the path.
041   */
042  public static final String ENV = "GOLOPATH";
043  private static final List<String> DEFAULT = Collections.singletonList(".");
044  private static final String SEP = System.getProperty("path.separator", ":");
045
046  private static final class ClasspathSplitter implements IParameterSplitter {
047    private static final String RE = String.format("[,%s]", SEP);
048    public List<String> split(String value) {
049      return Arrays.asList(value.split(RE));
050    }
051  }
052
053  @Parameter(names = "--classpath", variableArity = true, descriptionKey = "classpath", splitter = ClasspathSplitter.class)
054  List<String> classpath = new LinkedList<>();
055
056  private static URLClassLoader primaryClassLoader(List<String> classpath) throws MalformedURLException {
057    URL[] urls = new URL[classpath.size()];
058    for (int i = 0; i < classpath.size(); i++) {
059      urls[i] = new File(classpath.get(i)).toURI().toURL();
060    }
061    return new URLClassLoader(urls);
062  }
063
064  /**
065   * Create a golo classloader using the given classpath list.
066   * <p>
067   * If {@code classpath} is empty, packages from the {@code PROPERTY} system property are used, then from
068   * {@code ENV} environment variable if no property is defined. Finally, the current directory is used if no other path
069   * is defined.
070   * The current thread class loader is set to the created golo class loader.
071   *
072   * @param classpath a list of string representing classpath elements
073   * @return the corresponding class loader
074   */
075  public static GoloClassLoader initGoloClassLoader(List<String> classpath) throws MalformedURLException {
076    URLClassLoader primaryClassLoader = primaryClassLoader(initClassPath(classpath));
077    GoloClassLoader loader = new GoloClassLoader(primaryClassLoader);
078    Thread.currentThread().setContextClassLoader(loader);
079    return loader;
080  }
081
082  /**
083   * Init the class loader using the parsed command line option.
084   */
085  public GoloClassLoader initGoloClassLoader() throws MalformedURLException {
086    return initGoloClassLoader(this.classpath);
087  }
088
089  private static List<String> initClassPath(List<String> init) {
090    // priority CLI > property > env > default
091    List<String> classpath = init;
092    if (classpath.isEmpty()) {
093      classpath = getFromEnv(System.getProperty(PROPERTY));
094    }
095    if (classpath.isEmpty()) {
096      classpath = getFromEnv(System.getenv(ENV));
097    }
098    if (classpath.isEmpty()) {
099      classpath = DEFAULT;
100    }
101    System.setProperty(PROPERTY, String.join(SEP, classpath));
102    return classpath;
103  }
104
105  private static List<String> getFromEnv(String value) {
106    if (value == null) {
107      return Collections.emptyList();
108    }
109    return Arrays.asList(value.split(SEP));
110  }
111}