All Downloads are FREE. Search and download functionalities are using the official Maven repository.

iverse.capsule-shield.0.2.0.source-code.ShieldedCapsule Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2015, Prallel Universe Software Co. and Contributors. All rights reserved.
 *
 * This program and the accompanying materials are licensed under the terms
 * of the Eclipse Public License v1.0, available at
 * http://www.eclipse.org/legal/epl-v10.html
 */

import capsule.LXC;
import capsule.Log4J2SocketServer;
import capsule.ShieldedCapsuleAPI;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.slf4j.bridge.SLF4JBridgeHandler;
import sun.net.spi.nameservice.NameService;

import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import javax.net.ServerSocketFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.lang.reflect.AccessibleObject;
import java.net.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.RMIServerSocketFactory;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author pron
 * @author circlespainter
 */
public class ShieldedCapsule extends Capsule implements NameService, RMIServerSocketFactory, ShieldedCapsuleAPI {
    /*
     * See:
     *
     * https://lwn.net/Articles/531114/#series_index
     *
     * http://doger.io/
     *
     * https://www.stgraber.org/2013/12/20/lxc-1-0-blog-post-series/
     * http://man7.org/linux/man-pages/man5/lxc.container.conf.5.html
     * http://www.linuxcertif.com/man/5/lxc.conf/
     * https://help.ubuntu.com/lts/serverguide/lxc.html
     * https://wiki.archlinux.org/index.php/Linux_Containers
     * https://github.com/docker/docker/blob/v1.3.1/daemon/execdriver/lxc/lxc_template.go
     * https://github.com/docker/docker/blob/v1.3.1/daemon/execdriver/lxc/driver.go
     * https://github.com/docker/docker/blob/v0.8.1/execdriver/lxc/lxc_template.go
     * https://github.com/docker/docker/blob/v0.4.4/lxc_template.go
     * https://docs.oracle.com/cd/E37670_01/E37355/html/ol_app_containers.html
     *
     * https://www.stgraber.org/2014/01/17/lxc-1-0-unprivileged-containers/
     * http://unix.stackexchange.com/questions/177030/what-is-an-unprivileged-lxc-container
     * https://www.flockport.com/lxc-using-unprivileged-containers/
     *
     * http://www.freedesktop.org/software/systemd/man/systemd-nspawn.html
     *
     * https://github.com/p8952/bocker
     */

    //
    // private static final String PROP_DOMAIN = "sun.net.spi.nameservice.domain";
    // private static final String PROP_IPV6 = "java.net.preferIPv6Addresses";

    private static final String PROP_PREFIX_NAMESERVICE = "sun.net.spi.nameservice.provider.";

    private static final String CONTAINER_NET_IFACE_NAME = ShieldedCapsuleAPI.CONTAINER_NET_IFACE_NAME;
    //

    //
    private static final String OPT_REDIRECT_LOG = OPTION("capsule.redirectLog", "true", null, false, "Whether logging events should be redirected to the capsule process");

    private static final String OPT_DESTROY_ONLY = OPTION("capsule.destroyOnly", "false", null, false, "Whether the container should be only destroyed without booting it afterwards");

    @SuppressWarnings("unused")
    private static final String OPT_UID_MAP_START = OPTION(ShieldedCapsuleAPI.OPT_UID_MAP_START, "100000", null, false, "The first user ID in an unprivileged container");
    @SuppressWarnings("unused")
    private static final String OPT_GID_MAP_START = OPTION(ShieldedCapsuleAPI.OPT_GID_MAP_START, "100000", null, false, "The first group ID in an unprivileged container");
    @SuppressWarnings("unused")
    private static final String OPT_UID_MAP_SIZE = OPTION(ShieldedCapsuleAPI.OPT_UID_MAP_SIZE, "65536", null, false, "The size of the consecutive user ID map in an unprivileged container");
    @SuppressWarnings("unused")
    private static final String OPT_GID_MAP_SIZE = OPTION(ShieldedCapsuleAPI.OPT_GID_MAP_SIZE, "65536", null, false, "The size of the consecutive group ID map in an unprivileged container");
    @SuppressWarnings("unused")
    private static final String OPT_PRIVILEGED = OPTION(ShieldedCapsuleAPI.OPT_PRIVILEGED, "false", null, false, "Whether the container should be privileged");
    @SuppressWarnings("unused")
    private static final String OPT_SYSSHAREDIR = OPTION(ShieldedCapsuleAPI.OPT_SYSSHAREDIR, "/usr/share", null, false, "The location of the system-wide `share` directory where container toolchains can be found");

    private static final String OPT_JMX = OPTION("capsule.jmx", "true", null, false, "Whether JMX will be proxied from the capsule parent process to the container");

    private static final String OPT_PREFIX_LINK_IP = OPTION("capsule.internal.link.ip.", null, null, false, "INTERNAL USE ONLY: a `capsule.internal.link.ip.=` option will create an  DNS entry towards ");
    private static final String OPT_PREFIX_LINK_ID = OPTION("capsule.link.", null, null, false, "A `capsule.link.=` option will create an  DNS entry towards a shield container ");

    private static final String NETWORK_BRIDGE_DESC = "The name of the host bridge adapter for container networking";
    private static final String OPT_NETWORK_BRIDGE = OPTION("capsule.networkBridge", null, null, false, NETWORK_BRIDGE_DESC);
    private static final Entry ATTR_NETWORK_BRIDGE = ATTRIBUTE("Network-Bridge", T_STRING(), "lxcbr0", true, NETWORK_BRIDGE_DESC);

    private static final String IP_DESC = "An optional static IP to be assigned to the container (the default is using DHCP)";
    private static final String OPT_IP = OPTION("capsule.ip", null, null, false, IP_DESC);
    private static final Entry ATTR_IP = ATTRIBUTE("IP", T_STRING(), null, true, IP_DESC);

    private static final String SET_DEFAULT_GW_DESC = "Whether the default gateway should be set in order to grant internet access to the container";
    private static final String OPT_SET_DEFAULT_GW = OPTION("capsule.setDefaultGW", null, null, false, SET_DEFAULT_GW_DESC);
    private static final Entry ATTR_SET_DEFAULT_GW = ATTRIBUTE("Set-Default-Gateway", T_BOOL(), true, true, SET_DEFAULT_GW_DESC);

    private static final String ID_DESC = "An optional shield ID (defaults to the capsule app ID)";
    private static final String OPT_ID = OPTION("capsule.id", null, null, false, ID_DESC);
    private static final Entry ATTR_ID = ATTRIBUTE("ID", T_STRING(), null, true, ID_DESC);

    private static final String HOSTNAME_DESC = "The internal host name assigned to the container";
    private static final String OPT_HOSTNAME = OPTION("capsule.hostname", null, null, false, HOSTNAME_DESC);
    private static final Entry ATTR_HOSTNAME = ATTRIBUTE("Hostname", T_STRING(), null, true, HOSTNAME_DESC);

    private static final String ALLOWED_DEVICES_DESC = "a list of additional allowed devices in an unprivileged container (example: `\"c 136:* rwm\" \"\"`";
    private static final String OPT_ALLOWED_DEVICES = OPTION("capsule.allowedDevices", null, null, false, ALLOWED_DEVICES_DESC);
    private static final Entry> ATTR_ALLOWED_DEVICES = ATTRIBUTE("Allowed-Devices", T_LIST(T_STRING()), null, true, ALLOWED_DEVICES_DESC);

    private static final String CPU_SHARES_DESC = "`cgroup` CPU shares";
    private static final String OPT_CPU_SHARES = OPTION("capsule.cpuShares", null, null, false, CPU_SHARES_DESC);
    private static final Entry ATTR_CPU_SHARES = ATTRIBUTE("CPU-Shares", T_LONG(), null, true, CPU_SHARES_DESC);

    private static final String MEM_SHARES_DESC = "`cgroup` memory limit";
    private static final String OPT_MEMORY_LIMIT = OPTION("capsule.memoryLimit", null, null, false, MEM_SHARES_DESC);
    private static final Entry ATTR_MEMORY_LIMIT = ATTRIBUTE("Memory-Limit", T_LONG(), null, true, MEM_SHARES_DESC);
    private static final String PROP_CAPSULE_SHIELD_INTERNAL_SOCKETNODE = "capsule.internal.socketNode";
    //


    private static Path hostAbsoluteOwnJarFile;
    private static Path localRepo;
    private static Inet4Address vnetHostIPv4;
    private static Inet4Address vnetContainerIPv4;
    private static int log4j2TcpSocketServerPort;
    private static Map hostnameToInets = new ConcurrentHashMap<>();
    private static Map ipToHostname = new ConcurrentHashMap<>();

    private static LXC lxc;

    public ShieldedCapsule(Capsule pred) {
        super(pred);
    }

    @Override
    protected final ProcessBuilder prelaunch(List jvmArgs, List args) {
        localRepo = getLocalRepo();
        lxc = new LXC(this);

        try {
            if (emptyOrTrue(getProperty(OPT_DESTROY_ONLY))) {
                lxc.destroyContainer();
                lxc.cleanup();
                return null;
            }

            if (lxc.isBuildNeeded())
                lxc.createContainer();
        } catch (final IOException | InterruptedException e) {
            throw new RuntimeException(e);
        }

        final ProcessBuilder pb = super.prelaunch(jvmArgs, args);
        setupProps(pb.command());
        try {
            pb.command().addAll(0, lxc.commandPrefix());
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        return pb;
    }

    @Override
    protected final void liftoff() {
        lxc.setupDefaultGW();
    }

    //

    /**
     * Resolve relative to the container
     */
    @Override
    protected Map.Entry chooseJavaHome() {
        return lxc.chooseJavaHome(super.chooseJavaHome());
    }
    //

    //

    /**
     * Resolve relative to the container
     */
    @SuppressWarnings("deprecation")
    @Override
    protected List resolve0(Object x) {
        if (x instanceof Path && ((Path) x).isAbsolute()) {
            Path p = (Path) x;
            p = move(p);
            return super.resolve0(p);
        }
        return super.resolve0(x);
    }

    private Path move(Path p) {
        if (p == null)
            return null;

        p = p.normalize().toAbsolutePath();
        if (p.startsWith(Paths.get("/capsule")) || p.startsWith(Paths.get("/java")))
            return p;
        if (p.equals(getJarFile()))
            return moveJarFile(p);
        if (p.equals(findOwnJarFile()))
            return moveWrapperFile(p);
        else if (getAppDir() != null && p.startsWith(getAppDir()))
            return move(p, getAppDir(), LXC.CONTAINER_ABSOLUTE_CAPSULE_HOME);
        else if (localRepo != null && p.startsWith(localRepo))
            return move(p, localRepo, LXC.CONTAINER_ABSOLUTE_DEP_HOME);
        else if (getPlatformNativeLibraryPath().contains(p))
            return p;
        else if (p.startsWith(getJavaHome()))
            return p; // already moved in chooseJavaHome
        else
            throw new IllegalArgumentException("Unexpected file " + p);
    }

    private Path moveJarFile(Path p) {
        return LXC.CONTAINER_ABSOLUTE_JAR_HOME.resolve(p.getFileName());
    }

    private Path moveWrapperFile(Path p) {
        return LXC.CONTAINER_ABSOLUTE_WRAPPER_HOME.resolve(p.getFileName());
    }
    //

    //
    //////////////////////////// MAIN CAPSULE //////////////////////////////

    @SuppressWarnings("unchecked")
    private  T setupLog4jRedirJvmFlags(Entry attr) {
        if (emptyOrTrue(getProperty(OPT_REDIRECT_LOG))) {
            log(LOG_VERBOSE, "Requested Log4J2 redirection to the capsule process's SocketNode");
            setupLog4j2TcpSocketServer();
            List l = (List) attr.getValue();
            if (l == null) l = new ArrayList<>();
            l.add("-Duser.home=/");
            try {
                l.add("-D" + PROP_CAPSULE_SHIELD_INTERNAL_SOCKETNODE + "=" + getVNetHostIPv4().getHostAddress() + ":" + log4j2TcpSocketServerPort);
            } catch (final SocketException e) {
                throw new RuntimeException(e);
            }
            return (T) l;
        }
        return null;
    }

    private void setupLog4j2TcpSocketServer() {
        try {
            log(LOG_VERBOSE, "Loading application's Log4J2 configuration");
            LogManager.getLogger(ShieldedCapsule.class).info("Log4J2 configuration loaded");
            startLog4j2TcpSocketServer();
        } catch (final IOException e) {
            log(LOG_QUIET, "Couldn't enable Log4J2 redirect: " + e.getMessage());
            log(LOG_QUIET, e);
            throw new RuntimeException((e));
        }
    }

    private void startLog4j2TcpSocketServer() throws IOException {
        final ServerSocket tmp = new ServerSocket(0);
        log4j2TcpSocketServerPort = tmp.getLocalPort();
        tmp.close();
        log(LOG_VERBOSE, "Starting Log4J2 SocketServer on port " + log4j2TcpSocketServerPort);
        new Thread(new Log4J2SocketServer(this, log4j2TcpSocketServerPort), "capsule-shield-log4j2-socketnode").start();
    }

    //////////////////////////// CAPSULE AGENT //////////////////////////////
    @Override
    protected final void agent(Instrumentation inst) {
        try {
            setupLinkNameService(); // must be done before call to super
            redirectJUL(); // must be done before call to super
            redirectLog4j(); // must be done before call to super
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
        super.agent(inst);
    }

    private void redirectJUL() {
        log(LOG_VERBOSE, "Setting up JUL redirection -> SLF4J (-> Log4J2)");
        SLF4JBridgeHandler.removeHandlersForRootLogger();
        SLF4JBridgeHandler.install();
    }

    private void redirectLog4j() throws Exception {
        if (emptyOrTrue(getProperty(OPT_REDIRECT_LOG)) && isAgent()) {
            final String[] addr = System.getProperty(PROP_CAPSULE_SHIELD_INTERNAL_SOCKETNODE).split(":");
            try {
                log(LOG_VERBOSE, "Setting up Log4J2 SocketAppender for root logger ->  " + addr[0] + ":" + addr[1]);
                final LoggerContext context = (LoggerContext) LogManager.getContext(false);
                final Path tmpFile = addTempFile(Files.createTempFile("capsule-shield-log4j2-socketappend-", ".xml"));
                try (final PrintWriter pw = new PrintWriter(tmpFile.toFile())) {
                    pw.println("");
                    pw.println("");
                    pw.println("  ");
                    pw.println("    ");
                    pw.println("      ");
                    pw.println("    ");
                    pw.println("  ");
                    pw.println("  ");
                    pw.println("    ");
                    pw.println("      ");
                    pw.println("    ");
                    pw.println("  ");
                    pw.println("");
                }
                context.setConfigLocation(tmpFile.toUri());
            } catch (final Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    //

    //
    /////////// NameService ///////////////////////////////////
    private static void setupLinkNameService() throws IOException {
        // Find the lowest priority provider idx
        log(LOG_VERBOSE, "Setting up link service");
        int lastProviderIdx = -1;
        for (int i = 1; ; i++) {
            final String v = System.getProperty(PROP_PREFIX_NAMESERVICE + i);
            if (v == null || v.isEmpty())
                break;
            lastProviderIdx = i;
        }

        // Install default provider if not found
        if (lastProviderIdx == -1) {
            log(LOG_VERBOSE, "No name provider found, installing the default one");
            System.setProperty(PROP_PREFIX_NAMESERVICE + 1, "dns,sun");
            lastProviderIdx = 1;
        }

        // Shift down existing providers, if any
        for (int i = lastProviderIdx; i > 0; i--) {
            final String v = System.getProperty(PROP_PREFIX_NAMESERVICE + i);
            log(LOG_VERBOSE, "Shifting down name provider " + v);
            System.setProperty(PROP_PREFIX_NAMESERVICE + (i + 1), v);
        }

        // Add shield resolution as a top-proprity provider
        log(LOG_VERBOSE, "Setting first name provider: dns,shield");
        System.setProperty(PROP_PREFIX_NAMESERVICE + 1, "dns,shield");

        buildLinkNameServiceTables();
    }

    private static void buildLinkNameServiceTables() throws IOException {
        for (final Object o : System.getProperties().keySet()) {
            if (o instanceof String) {
                final String k = (String) o;
                if (k.startsWith(OPT_PREFIX_LINK_IP)) {
                    intoLinkNameServiceTables(k.substring(OPT_PREFIX_LINK_IP.length()), InetAddress.getAllByName(System.getProperty(k)));
                }
            }
        }
    }

    private static void intoLinkNameServiceTables(String name, InetAddress[] addrs) {
        log(LOG_VERBOSE, "Adding name mapping " + name + " -> " + Arrays.toString(addrs));
        hostnameToInets.put(name, addrs);
        for (final InetAddress a : addrs)
            ipToHostname.put(a.getAddress(), name);
    }

    /**
     * Look up all hosts by name.
     *
     * @param hostname the host name
     * @return an array of addresses for the host name
     * @throws UnknownHostException if there are no names for this host, or if resolution fails
     */
    public InetAddress[] lookupAllHostAddr(final String hostname) throws UnknownHostException {
        final InetAddress[] ips = hostnameToInets.get(hostname);
        if (ips != null) return ips;
        throw new UnknownHostException("Failed to resolve hostname " + hostname);
    }

    /**
     * Get the name of the host with the given IP address.
     *
     * @param bytes the address bytes
     * @return the host name
     * @throws UnknownHostException if there is no host name for this IP address
     */
    public String getHostByAddr(final byte[] bytes) throws UnknownHostException {
        final String hostname = ipToHostname.get(bytes);
        if (hostname != null) return hostname;
        throw new UnknownHostException("Failed to resolve inet address " + Arrays.toString(bytes));
    }
    //

    //
    @Override
    @SuppressWarnings("unchecked")
    protected  T attribute(Map.Entry attr) {
        if (ATTR_AGENT == attr && emptyOrTrue(getProperty(OPT_JMX)))
            return (T) Boolean.TRUE;
        if (ATTR_JVM_ARGS == attr) {
            return setupLog4jRedirJvmFlags(attr);
        }
        return super.attribute(attr);
    }

    @Override
    @SuppressWarnings("NullableProblems")
    public ServerSocket createServerSocket(int pPort) throws IOException {
        return ServerSocketFactory.getDefault().createServerSocket(pPort, 0, getVNetContainerIPv4());
    }

    @SuppressWarnings("deprecation")
    @Override
    protected final JMXServiceURL startJMXServer() {
        // http://vafer.org/blog/20061010091658/
        try {
            int namingPort;
            try (final ServerSocket s = new ServerSocket(0)) {
                namingPort = s.getLocalPort();
            }
            log(LOG_VERBOSE, "Starting JMXConnectorServer");
            LocateRegistry.createRegistry(namingPort, null, this);

            final StringBuilder url =
                    new StringBuilder()
                            .append("service:jmx:").append("rmi://").append("/jndi/")
                            .append("rmi://").append(getVNetContainerIPv4().getHostAddress()).append(':').append(namingPort)
                            .append("/").append(UUID.randomUUID().toString());

            log(LOG_VERBOSE, "Starting management agent at " + url);

            final JMXServiceURL jmxServiceURL = new JMXServiceURL(url.toString());
            final Map env = new HashMap<>();
            env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, this);
            final RMIConnectorServer rmiServer = new RMIConnectorServer(jmxServiceURL, env, ManagementFactory.getPlatformMBeanServer());
            rmiServer.start();
            return jmxServiceURL;
        } catch (final Exception e) {
            log(LOG_VERBOSE, "JMXConnectorServer failed: " + e.getMessage());
            log(LOG_VERBOSE, e);
            return null;
        }
    }

    private void setupProps(List command) {
        setupCommProps(command);
        setupLinkProps(command);
    }

    private void setupCommProps(List command) {
        for (int i = 0; i < command.size(); i++) {
            if (command.get(i).startsWith("-Dcapsule.address=")) {
                try {
                    command.set(i, "-Dcapsule.address=" + getVNetHostIPv4().getHostAddress());
                } catch (final SocketException e) {
                    log(LOG_QUIET, "Couldn't setup the agent communication link: " + e.getMessage());
                    log(LOG_QUIET, e);
                }
                break;
            }
        }
    }

    public void setupLinkProps(List command) {
        final String prefix = "-D" + OPT_PREFIX_LINK_ID;
        for (int i = 0; i < command.size(); i++) {
            final String opt = command.get(i);
            final int eqIdx = opt.indexOf('=');
            if (opt.startsWith(prefix)) {
                final String hostname = opt.substring(prefix.length(), eqIdx);
                final String shieldID = opt.substring(eqIdx + 1);
                try {
                    final String replacement = "-D" + OPT_PREFIX_LINK_IP + hostname + "=" + lxc.getRunningInet(shieldID);
                    log(LOG_VERBOSE, "Replacing link property " + opt + " with " + replacement);
                    command.set(i, replacement);
                } catch (final IOException e) {
                    log(LOG_QUIET, "Couldn't setup the agent communication link: " + e.getMessage());
                    log(LOG_QUIET, e);
                }
                break;
            }
        }
    }
    //

    // TODO Factor with Capsule
    //
    private static long getMemorySwap(long maxMem, boolean swap) {
        return swap ? maxMem * 2 : 0;
    }
    //

    // TODO Factor with Capsule
    //
    public Inet4Address getVNetHostIPv4() throws SocketException {
        // TODO IPv6
        if (vnetHostIPv4 == null) {
            final Enumeration vas = NetworkInterface.getByName(getAttribute(ATTR_NETWORK_BRIDGE)).getInetAddresses();
            while (vas.hasMoreElements()) {
                final InetAddress ia = vas.nextElement();
                if (ia instanceof Inet4Address)
                    vnetHostIPv4 = (Inet4Address) ia;
            }
        }
        return vnetHostIPv4;
    }

    public Inet4Address getVNetContainerIPv4() throws SocketException {
        // TODO IPv6
        if (vnetContainerIPv4 == null) {
            final Enumeration vas = NetworkInterface.getByName(CONTAINER_NET_IFACE_NAME).getInetAddresses();
            while (vas.hasMoreElements()) {
                final InetAddress ia = vas.nextElement();
                if (ia instanceof Inet4Address)
                    vnetContainerIPv4 = (Inet4Address) ia;
            }
        }
        return vnetContainerIPv4;
    }

    @Override
    public String getProp(String prop) {
        return getProperty(prop);
    }
    //

    // TODO Factor with Capsule
    //
    public Path getLocalRepo() {
        final Capsule mavenCaplet = sup("MavenCapsule");
        if (mavenCaplet == null)
            return null;
        try {
            return (Path) accessible(mavenCaplet.getClass().getDeclaredMethod("getLocalRepo")).invoke(mavenCaplet);
        } catch (final ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Path getJavaDir() {
        return getJavaHome();
    }

    @Override
    public String getMemorySwap(int maxMem, boolean b) {
        return "" + getMemorySwap((long) maxMem, b);
    }

    @Override
    public Boolean shouldSetDefaultGateway() {
        return getOptionOrAttributeBool(OPT_SET_DEFAULT_GW, ATTR_SET_DEFAULT_GW);
    }

    @Override
    public String getNetworkBridge() {
        return getOptionOrAttributeString(OPT_NETWORK_BRIDGE, ATTR_NETWORK_BRIDGE);
    }

    @Override
    public String getHostname() {
        return getOptionOrAttributeString(OPT_HOSTNAME, ATTR_HOSTNAME);
    }

    @Override
    public String getIP() {
        return getOptionOrAttributeString(OPT_IP, ATTR_IP);
    }

    @Override
    public List getAllowedDevices() {
        return getOptionOrAttributeStringList(OPT_ALLOWED_DEVICES, ATTR_ALLOWED_DEVICES);
    }

    @Override
    public String getId() {
        return getOptionOrAttributeString(OPT_ID, ATTR_ID);
    }

    @Override
    public Long getCPUShares() {
        return getOptionOrAttributeLong(OPT_CPU_SHARES, ATTR_CPU_SHARES);
    }

    @Override
    public Long getMemLimit() {
        return getOptionOrAttributeLong(OPT_MEMORY_LIMIT, ATTR_MEMORY_LIMIT);
    }

    public String getOptionOrAttributeString(String propName, Map.Entry attr) {
        final String propValue = getProperty(propName);
        if (propValue == null)
            return getAttribute(attr);
        return propValue;
    }

    public List getOptionOrAttributeStringList(String propName, Map.Entry> attr) {
        final String propValue = getProperty(propName);
        if (propValue == null)
            return getAttribute(attr);
        return Arrays.asList(propValue.split(":"));
    }

    public Long getOptionOrAttributeLong(String propName, Map.Entry attr) {
        final String propValue = getProperty(propName);
        if (propValue == null)
            return getAttribute(attr);
        try {
            return Long.parseLong(propValue);
        } catch (final Throwable t) {
            return getAttribute(attr);
        }
    }

    public Boolean getOptionOrAttributeBool(String propName, Map.Entry attr) {
        final String propValue = getProperty(propName);
        if (propValue == null)
            return getAttribute(attr);
        try {
            return Boolean.parseBoolean(propValue);
        } catch (final Throwable t) {
            return getAttribute(attr);
        }
    }
    //

    // TODO Factor with Capsule
    //
    public Path findOwnJarFile() {
        if (hostAbsoluteOwnJarFile == null) {
            final URL url = ShieldedCapsule.class.getClassLoader().getResource(ShieldedCapsule.class.getName().replace('.', '/') + ".class");
            if (url != null) {
                if (!"jar".equals(url.getProtocol()))
                    throw new IllegalStateException("The Capsule class must be in a JAR file, but was loaded from: " + url);
                final String path = url.getPath();
                if (path == null) //  || !path.startsWith("file:")
                    throw new IllegalStateException("The Capsule class must be in a local JAR file, but was loaded from: " + url);

                try {
                    final URI jarUri = new URI(path.substring(0, path.indexOf('!')));
                    hostAbsoluteOwnJarFile = Paths.get(jarUri);
                } catch (final URISyntaxException e) {
                    throw new AssertionError(e);
                }
            } else
                throw new RuntimeException("Can't locate Capsule's own class");
        }
        return hostAbsoluteOwnJarFile;
    }

    private static boolean emptyOrTrue(String value) {
        return value != null && (value.isEmpty() || Boolean.parseBoolean(value));
    }

    private static  T accessible(T obj) {
        if (obj == null)
            return null;
        obj.setAccessible(true);
        return obj;
    }
    //

    //
    @Override
    public void logVerbose(String str) {
        Capsule.log(LOG_VERBOSE, str);
    }

    @Override
    public void logDebug(Throwable t) {
        Capsule.log(LOG_DEBUG, t);
    }

    @Override
    public void logQuiet(String str) {
        Capsule.log(LOG_QUIET, str);
    }

    @Override
    public void logQuiet(Throwable t) {
        Capsule.log(LOG_QUIET, t);
    }

    @Override
    public String getLogLevelString() {
        switch (getLogLevel()) {
            case LOG_DEBUG:
                return "LOG_DEBUG";
            case LOG_NONE:
                return "LOG_DEBUG";
            case LOG_QUIET:
                return "LOG_DEBUG";
            case LOG_VERBOSE:
                return "LOG_VERBOSE";
            default:
                return null;
        }
    }

    @Override
    public boolean isWrapper() {
        return isWrapperCapsule();
    }

    @Override
    public Iterable execute(String... cmd) throws IOException {
        return Capsule.exec(cmd);
    }

    @Override
    public boolean isWin() {
        return Capsule.isWindows();
    }

    @Override
    public Path getCapsuleJarFile() {
        return super.getJarFile();
    }

    @Override
    public Path getWAppCache() {
        return super.getWritableAppCache();
    }

    @Override
    public String getAppID() {
        return getAppId();
    }

    @Override
    public String getEnv(String s) {
        return Capsule.getenv(s);
    }
    //
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy