<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.karaf.instance.core.internal;

import org.apache.karaf.instance.core.Instance;
import org.apache.karaf.instance.core.InstanceService;
import org.apache.karaf.instance.core.InstanceSettings;
import org.apache.karaf.jpm.Process;
import org.apache.karaf.jpm.impl.ProcessBuilderFactoryImpl;
import org.apache.karaf.jpm.impl.ScriptUtils;
import org.apache.karaf.util.properties.FileLockUtils;
import org.fusesource.jansi.Ansi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;

public class InstanceServiceImpl implements InstanceService {

    public static final String STORAGE_FILE = "instance.properties";
    public static final String BACKUP_EXTENSION = ".bak";
    private static final String FEATURES_CFG = "etc/org.apache.karaf.features.cfg";

    private static final Logger LOGGER = LoggerFactory.getLogger(InstanceServiceImpl.class);

    private static final String CONFIG_PROPERTIES_FILE_NAME = "config.properties";

    private static final String KARAF_SHUTDOWN_PORT = "karaf.shutdown.port";

    private static final String KARAF_SHUTDOWN_HOST = "karaf.shutdown.host";

    private static final String KARAF_SHUTDOWN_PORT_FILE = "karaf.shutdown.port.file";

    private static final String KARAF_SHUTDOWN_COMMAND = "karaf.shutdown.command";

    private static final String KARAF_SHUTDOWN_PID_FILE = "karaf.shutdown.pid.file";

    private static final String DEFAULT_SHUTDOWN_COMMAND = "SHUTDOWN";

    public static final String DEFAULT_JAVA_OPTS = "-server -Xmx512M -Dcom.sun.management.jmxremote -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass";

    private LinkedHashMap<String, InstanceImpl> proxies = new LinkedHashMap<String, InstanceImpl>();

    private File storageLocation;

    private long stopTimeout = 30000;

    static class InstanceState {
        String name;
        String loc;
        String opts;
        int pid;
        boolean root;
    }

    static class State {
        int defaultSshPortStart = 8101;
        int defaultRmiRegistryPortStart = 1099;
        int defaultRmiServerPortStart = 44444;
        Map<String, InstanceState> instances;
    }

    public File getStorageLocation() {
        return storageLocation;
    }

    public void setStorageLocation(File storage) {
        this.storageLocation = storage;
    }

    public long getStopTimeout() {
        return stopTimeout;
    }

    public void setStopTimeout(long stopTimeout) {
        this.stopTimeout = stopTimeout;
    }

    private State loadData(org.apache.felix.utils.properties.Properties storage) {
        State state = new State();
        int count = getInt(storage, "count", 0);
        state.defaultSshPortStart = getInt(storage, "ssh.port", state.defaultSshPortStart);
        state.defaultRmiRegistryPortStart = getInt(storage, "rmi.registry.port", state.defaultRmiRegistryPortStart);
        state.defaultRmiServerPortStart = getInt(storage, "rmi.server.port", state.defaultRmiServerPortStart);
        state.instances = new LinkedHashMap<String, InstanceState>();

        for (int i = 0; i < count; i++) {
            InstanceState instance = new InstanceState();
            instance.name = getString(storage, "item." + i + ".name", null);
            instance.loc = getString(storage, "item." + i + ".loc", null);
            instance.opts = getString(storage, "item." + i + ".opts", null);
            instance.pid = getInt(storage, "item." + i + ".pid", 0);
            instance.root = getBool(storage, "item." + i + ".root", false);
            state.instances.put(instance.name, instance);
        }
        // Update proxies list
        for (InstanceState instance : state.instances.values()) {
            if (!this.proxies.containsKey(instance.name)) {
                proxies.put(instance.name, new InstanceImpl(this, instance.name));
            }
        }
        List<String> names = new ArrayList<String>(this.proxies.keySet());
        for (String name : names) {
            if (!state.instances.containsKey(name)) {
                this.proxies.remove(name);
            }
        }
        return state;
    }

    private void saveData(State state, org.apache.felix.utils.properties.Properties storage) {
        storage.put("ssh.port", Integer.toString(state.defaultSshPortStart));
        storage.put("rmi.registry.port", Integer.toString(state.defaultRmiRegistryPortStart));
        storage.put("rmi.server.port", Integer.toString(state.defaultRmiServerPortStart));
        storage.put("count", Integer.toString(state.instances.size()));
        int i = 0;
        for (InstanceState instance : state.instances.values()) {
            storage.put("item." + i + ".name", instance.name);
            storage.put("item." + i + ".root", Boolean.toString(instance.root));
            storage.put("item." + i + ".loc", instance.loc);
            storage.put("item." + i + ".pid", Integer.toString(instance.pid));
            storage.put("item." + i + ".opts", instance.opts != null ? instance.opts : "");
            i++;
        }
        while (storage.containsKey("item." + i + ".name")) {
            storage.remove("item." + i + ".name");
            storage.remove("item." + i + ".root");
            storage.remove("item." + i + ".loc");
            storage.remove("item." + i + ".pid");
            storage.remove("item." + i + ".opts");
            i++;
        }
    }

    private boolean getBool(org.apache.felix.utils.properties.Properties storage, String name, boolean def) {
        Object value = storage.get(name);
        if (value != null) {
            return Boolean.parseBoolean(value.toString());
        } else {
            return def;
        }
    }

    private int getInt(org.apache.felix.utils.properties.Properties storage, String name, int def) {
        Object value = storage.get(name);
        if (value != null) {
            return Integer.parseInt(value.toString());
        } else {
            return def;
        }
    }

    private String getString(org.apache.felix.utils.properties.Properties storage, String name, String def) {
        Object value = storage.get(name);
        return value != null ? value.toString() : def;
    }

    interface Task<T> {
        T call(State state) throws IOException;
    }

    synchronized <T> T execute(final Task<T> callback) {
        final File storageFile = new File(storageLocation, STORAGE_FILE);
        if (!storageFile.exists()) {
            storageFile.getParentFile().mkdirs();
            try {
                storageFile.createNewFile();
            } catch (IOException e) {
                // Ignore
            }
        }
        if (storageFile.exists()) {
            if (!storageFile.isFile()) {
                throw new IllegalStateException("Instance storage location should be a file: " + storageFile);
            }
            try {
                return FileLockUtils.execute(storageFile, new FileLockUtils.CallableWithProperties<T>() {
                    public T call(org.apache.felix.utils.properties.Properties properties) throws IOException {
                        State state = loadData(properties);
                        T t = callback.call(state);
                        saveData(state, properties);
                        return t;
                    }
                });
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            throw new IllegalStateException("Instance storage location does not exist: " + storageFile);
        }
    }

    public synchronized void refreshInstance() throws Exception {
    }

    private void logInfo(String message, boolean printOutput, Object... args) {
        if (LOGGER.isInfoEnabled() || printOutput) {
            String formatted = String.format(message, args);
            LOGGER.info(formatted);
            if (printOutput) {
                println(formatted);
            }
        }
    }

    public synchronized Instance createInstance(final String name, final InstanceSettings settings, final boolean printOutput) throws Exception {
        return execute(new Task<Instance>() {
            public Instance call(State state) throws IOException {
                if (state.instances.get(name) != null) {
                    throw new IllegalArgumentException("Instance '" + name + "' already exists");
                }
                String loc = settings.getLocation() != null ? settings.getLocation() : name;
                File karafBase = new File(loc);
                if (!karafBase.isAbsolute()) {
                    karafBase = new File(storageLocation, loc);
                }
                int sshPort = settings.getSshPort();
                if (sshPort <= 0) {
                    sshPort = ++state.defaultSshPortStart;
                }
                int rmiRegistryPort = settings.getRmiRegistryPort();
                if (rmiRegistryPort <= 0) {
                    rmiRegistryPort = ++state.defaultRmiRegistryPortStart;
                }
                int rmiServerPort = settings.getRmiServerPort();
                if (rmiServerPort <= 0) {
                    rmiServerPort = ++state.defaultRmiServerPortStart;
                }
                logInfo("Creating new instance on SSH port %d and registry port %d / RMI server port %d at: %s",
                        printOutput, sshPort, rmiRegistryPort, rmiServerPort, karafBase);

                mkdir(karafBase, "bin", printOutput);
                mkdir(karafBase, "etc", printOutput);
                mkdir(karafBase, "system", printOutput);
                mkdir(karafBase, "deploy", printOutput);
                mkdir(karafBase, "data", printOutput);

                copyResourceToDir(karafBase, "etc/all.policy", printOutput);
                copyResourceToDir(karafBase, "etc/config.properties", printOutput);
                copyResourceToDir(karafBase, "etc/custom.properties", printOutput);
                copyResourceToDir(karafBase, "etc/distribution.info", printOutput);
                copyResourceToDir(karafBase, "etc/equinox-debug.properties", printOutput);
                copyResourceToDir(karafBase, "etc/java.util.logging.properties", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.java.lang.Memory.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.org.apache.karaf.bundle.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.org.apache.karaf.config.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.org.apache.karaf.security.jmx.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.osgi.compendium.cm.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jre.properties", printOutput);
                copyResourceToDir(karafBase, "etc/keys.properties", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.felix.fileinstall-deploy.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.bundle.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.config.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.feature.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.jaas.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.kar.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.shell.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.system.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.features.obr.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.features.repos.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.jaas.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.kar.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.log.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.ops4j.pax.logging.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.ops4j.pax.url.mvn.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/regions-config.xml", printOutput);
                copyResourceToDir(karafBase, "etc/shell.init.script", printOutput);
                copyResourceToDir(karafBase, "etc/users.properties", printOutput);

                copyResourceToDir(karafBase, FEATURES_CFG, printOutput);
                addFeaturesFromSettings(new File(karafBase, FEATURES_CFG), settings);

                // The startup.properties is now generated by the karaf maven plugin, so
                // we use the one from the root instance instead of embedding it
                File rootEtc = new File(System.getProperty("karaf.etc"));
                copy(new File(rootEtc, "startup.properties"), new File(karafBase, "etc/startup.properties"));

                HashMap<String, String> props = new HashMap<String, String>();
                props.put("${SUBST-KARAF-NAME}", name);
                props.put("${SUBST-KARAF-HOME}", System.getProperty("karaf.home"));
                props.put("${SUBST-KARAF-BASE}", karafBase.getPath());
                props.put("${SUBST-SSH-PORT}", Integer.toString(sshPort));
                props.put("${SUBST-RMI-REGISTRY-PORT}", Integer.toString(rmiRegistryPort));
                props.put("${SUBST-RMI-SERVER-PORT}", Integer.toString(rmiServerPort));
                copyFilteredResourceToDir(karafBase, "etc/system.properties", props, printOutput);
                copyFilteredResourceToDir(karafBase, "etc/org.apache.karaf.shell.cfg", props, printOutput);
                copyFilteredResourceToDir(karafBase, "etc/org.apache.karaf.management.cfg", props, printOutput);
                // If we use batch files, use batch files, else use bash scripts (even on cygwin)
                boolean windows = System.getProperty("os.name").startsWith("Win");
                boolean cygwin = windows && new File(System.getProperty("karaf.home"), "bin/admin").exists();
                if (windows && !cygwin) {
                    copyFilteredResourceToDir(karafBase, "bin/karaf.bat", props, printOutput);
                    copyFilteredResourceToDir(karafBase, "bin/start.bat", props, printOutput);
                    copyFilteredResourceToDir(karafBase, "bin/stop.bat", props, printOutput);
                } else {
                    copyFilteredResourceToDir(karafBase, "bin/karaf", props, printOutput);
                    copyFilteredResourceToDir(karafBase, "bin/start", props, printOutput);
                    copyFilteredResourceToDir(karafBase, "bin/stop", props, printOutput);
                    if (!cygwin) {
                        chmod(new File(karafBase, "bin/karaf"), "a+x");
                        chmod(new File(karafBase, "bin/start"), "a+x");
                        chmod(new File(karafBase, "bin/stop"), "a+x");
                    }
                }

                String javaOpts = settings.getJavaOpts();
                if (javaOpts == null || javaOpts.length() == 0) {
                    javaOpts = DEFAULT_JAVA_OPTS;
                }
                InstanceState is = new InstanceState();
                is.name = name;
                is.loc = karafBase.toString();
                is.opts = javaOpts;
                state.instances.put(name, is);
                InstanceImpl instance = new InstanceImpl(InstanceServiceImpl.this, name);
                InstanceServiceImpl.this.proxies.put(name, instance);
                return instance;
            }
        });
    }

    void addFeaturesFromSettings(File featuresCfg, final InstanceSettings settings) throws IOException {
        FileLockUtils.execute(featuresCfg, new FileLockUtils.RunnableWithProperties() {
            public void run(org.apache.felix.utils.properties.Properties properties) throws IOException {
                appendToPropList(properties, "featuresBoot", settings.getFeatures());
                appendToPropList(properties, "featuresRepositories", settings.getFeatureURLs());
            }
        });<commit_msg>Copy both linux and win shell scripts when creating an instance<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.karaf.instance.core.internal;

import org.apache.karaf.instance.core.Instance;
import org.apache.karaf.instance.core.InstanceService;
import org.apache.karaf.instance.core.InstanceSettings;
import org.apache.karaf.jpm.Process;
import org.apache.karaf.jpm.impl.ProcessBuilderFactoryImpl;
import org.apache.karaf.jpm.impl.ScriptUtils;
import org.apache.karaf.util.properties.FileLockUtils;
import org.fusesource.jansi.Ansi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;

public class InstanceServiceImpl implements InstanceService {

    public static final String STORAGE_FILE = "instance.properties";
    public static final String BACKUP_EXTENSION = ".bak";
    private static final String FEATURES_CFG = "etc/org.apache.karaf.features.cfg";

    private static final Logger LOGGER = LoggerFactory.getLogger(InstanceServiceImpl.class);

    private static final String CONFIG_PROPERTIES_FILE_NAME = "config.properties";

    private static final String KARAF_SHUTDOWN_PORT = "karaf.shutdown.port";

    private static final String KARAF_SHUTDOWN_HOST = "karaf.shutdown.host";

    private static final String KARAF_SHUTDOWN_PORT_FILE = "karaf.shutdown.port.file";

    private static final String KARAF_SHUTDOWN_COMMAND = "karaf.shutdown.command";

    private static final String KARAF_SHUTDOWN_PID_FILE = "karaf.shutdown.pid.file";

    private static final String DEFAULT_SHUTDOWN_COMMAND = "SHUTDOWN";

    public static final String DEFAULT_JAVA_OPTS = "-server -Xmx512M -Dcom.sun.management.jmxremote -XX:+UnlockDiagnosticVMOptions -XX:+UnsyncloadClass";

    private LinkedHashMap<String, InstanceImpl> proxies = new LinkedHashMap<String, InstanceImpl>();

    private File storageLocation;

    private long stopTimeout = 30000;

    static class InstanceState {
        String name;
        String loc;
        String opts;
        int pid;
        boolean root;
    }

    static class State {
        int defaultSshPortStart = 8101;
        int defaultRmiRegistryPortStart = 1099;
        int defaultRmiServerPortStart = 44444;
        Map<String, InstanceState> instances;
    }

    public File getStorageLocation() {
        return storageLocation;
    }

    public void setStorageLocation(File storage) {
        this.storageLocation = storage;
    }

    public long getStopTimeout() {
        return stopTimeout;
    }

    public void setStopTimeout(long stopTimeout) {
        this.stopTimeout = stopTimeout;
    }

    private State loadData(org.apache.felix.utils.properties.Properties storage) {
        State state = new State();
        int count = getInt(storage, "count", 0);
        state.defaultSshPortStart = getInt(storage, "ssh.port", state.defaultSshPortStart);
        state.defaultRmiRegistryPortStart = getInt(storage, "rmi.registry.port", state.defaultRmiRegistryPortStart);
        state.defaultRmiServerPortStart = getInt(storage, "rmi.server.port", state.defaultRmiServerPortStart);
        state.instances = new LinkedHashMap<String, InstanceState>();

        for (int i = 0; i < count; i++) {
            InstanceState instance = new InstanceState();
            instance.name = getString(storage, "item." + i + ".name", null);
            instance.loc = getString(storage, "item." + i + ".loc", null);
            instance.opts = getString(storage, "item." + i + ".opts", null);
            instance.pid = getInt(storage, "item." + i + ".pid", 0);
            instance.root = getBool(storage, "item." + i + ".root", false);
            state.instances.put(instance.name, instance);
        }
        // Update proxies list
        for (InstanceState instance : state.instances.values()) {
            if (!this.proxies.containsKey(instance.name)) {
                proxies.put(instance.name, new InstanceImpl(this, instance.name));
            }
        }
        List<String> names = new ArrayList<String>(this.proxies.keySet());
        for (String name : names) {
            if (!state.instances.containsKey(name)) {
                this.proxies.remove(name);
            }
        }
        return state;
    }

    private void saveData(State state, org.apache.felix.utils.properties.Properties storage) {
        storage.put("ssh.port", Integer.toString(state.defaultSshPortStart));
        storage.put("rmi.registry.port", Integer.toString(state.defaultRmiRegistryPortStart));
        storage.put("rmi.server.port", Integer.toString(state.defaultRmiServerPortStart));
        storage.put("count", Integer.toString(state.instances.size()));
        int i = 0;
        for (InstanceState instance : state.instances.values()) {
            storage.put("item." + i + ".name", instance.name);
            storage.put("item." + i + ".root", Boolean.toString(instance.root));
            storage.put("item." + i + ".loc", instance.loc);
            storage.put("item." + i + ".pid", Integer.toString(instance.pid));
            storage.put("item." + i + ".opts", instance.opts != null ? instance.opts : "");
            i++;
        }
        while (storage.containsKey("item." + i + ".name")) {
            storage.remove("item." + i + ".name");
            storage.remove("item." + i + ".root");
            storage.remove("item." + i + ".loc");
            storage.remove("item." + i + ".pid");
            storage.remove("item." + i + ".opts");
            i++;
        }
    }

    private boolean getBool(org.apache.felix.utils.properties.Properties storage, String name, boolean def) {
        Object value = storage.get(name);
        if (value != null) {
            return Boolean.parseBoolean(value.toString());
        } else {
            return def;
        }
    }

    private int getInt(org.apache.felix.utils.properties.Properties storage, String name, int def) {
        Object value = storage.get(name);
        if (value != null) {
            return Integer.parseInt(value.toString());
        } else {
            return def;
        }
    }

    private String getString(org.apache.felix.utils.properties.Properties storage, String name, String def) {
        Object value = storage.get(name);
        return value != null ? value.toString() : def;
    }

    interface Task<T> {
        T call(State state) throws IOException;
    }

    synchronized <T> T execute(final Task<T> callback) {
        final File storageFile = new File(storageLocation, STORAGE_FILE);
        if (!storageFile.exists()) {
            storageFile.getParentFile().mkdirs();
            try {
                storageFile.createNewFile();
            } catch (IOException e) {
                // Ignore
            }
        }
        if (storageFile.exists()) {
            if (!storageFile.isFile()) {
                throw new IllegalStateException("Instance storage location should be a file: " + storageFile);
            }
            try {
                return FileLockUtils.execute(storageFile, new FileLockUtils.CallableWithProperties<T>() {
                    public T call(org.apache.felix.utils.properties.Properties properties) throws IOException {
                        State state = loadData(properties);
                        T t = callback.call(state);
                        saveData(state, properties);
                        return t;
                    }
                });
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            throw new IllegalStateException("Instance storage location does not exist: " + storageFile);
        }
    }

    public synchronized void refreshInstance() throws Exception {
    }

    private void logInfo(String message, boolean printOutput, Object... args) {
        if (LOGGER.isInfoEnabled() || printOutput) {
            String formatted = String.format(message, args);
            LOGGER.info(formatted);
            if (printOutput) {
                println(formatted);
            }
        }
    }

    public synchronized Instance createInstance(final String name, final InstanceSettings settings, final boolean printOutput) throws Exception {
        return execute(new Task<Instance>() {
            public Instance call(State state) throws IOException {
                if (state.instances.get(name) != null) {
                    throw new IllegalArgumentException("Instance '" + name + "' already exists");
                }
                String loc = settings.getLocation() != null ? settings.getLocation() : name;
                File karafBase = new File(loc);
                if (!karafBase.isAbsolute()) {
                    karafBase = new File(storageLocation, loc);
                }
                int sshPort = settings.getSshPort();
                if (sshPort <= 0) {
                    sshPort = ++state.defaultSshPortStart;
                }
                int rmiRegistryPort = settings.getRmiRegistryPort();
                if (rmiRegistryPort <= 0) {
                    rmiRegistryPort = ++state.defaultRmiRegistryPortStart;
                }
                int rmiServerPort = settings.getRmiServerPort();
                if (rmiServerPort <= 0) {
                    rmiServerPort = ++state.defaultRmiServerPortStart;
                }
                logInfo("Creating new instance on SSH port %d and registry port %d / RMI server port %d at: %s",
                        printOutput, sshPort, rmiRegistryPort, rmiServerPort, karafBase);

                mkdir(karafBase, "bin", printOutput);
                mkdir(karafBase, "etc", printOutput);
                mkdir(karafBase, "system", printOutput);
                mkdir(karafBase, "deploy", printOutput);
                mkdir(karafBase, "data", printOutput);

                copyResourceToDir(karafBase, "etc/all.policy", printOutput);
                copyResourceToDir(karafBase, "etc/config.properties", printOutput);
                copyResourceToDir(karafBase, "etc/custom.properties", printOutput);
                copyResourceToDir(karafBase, "etc/distribution.info", printOutput);
                copyResourceToDir(karafBase, "etc/equinox-debug.properties", printOutput);
                copyResourceToDir(karafBase, "etc/java.util.logging.properties", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.java.lang.Memory.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.org.apache.karaf.bundle.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.org.apache.karaf.config.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.org.apache.karaf.security.jmx.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jmx.acl.osgi.compendium.cm.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/jre.properties", printOutput);
                copyResourceToDir(karafBase, "etc/keys.properties", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.felix.fileinstall-deploy.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.bundle.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.config.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.feature.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.jaas.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.kar.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.shell.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.command.acl.system.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.features.obr.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.features.repos.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.jaas.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.kar.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.apache.karaf.log.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.ops4j.pax.logging.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/org.ops4j.pax.url.mvn.cfg", printOutput);
                copyResourceToDir(karafBase, "etc/regions-config.xml", printOutput);
                copyResourceToDir(karafBase, "etc/shell.init.script", printOutput);
                copyResourceToDir(karafBase, "etc/users.properties", printOutput);

                copyResourceToDir(karafBase, FEATURES_CFG, printOutput);
                addFeaturesFromSettings(new File(karafBase, FEATURES_CFG), settings);

                // The startup.properties is now generated by the karaf maven plugin, so
                // we use the one from the root instance instead of embedding it
                File rootEtc = new File(System.getProperty("karaf.etc"));
                copy(new File(rootEtc, "startup.properties"), new File(karafBase, "etc/startup.properties"));

                HashMap<String, String> props = new HashMap<String, String>();
                props.put("${SUBST-KARAF-NAME}", name);
                props.put("${SUBST-KARAF-HOME}", System.getProperty("karaf.home"));
                props.put("${SUBST-KARAF-BASE}", karafBase.getPath());
                props.put("${SUBST-SSH-PORT}", Integer.toString(sshPort));
                props.put("${SUBST-RMI-REGISTRY-PORT}", Integer.toString(rmiRegistryPort));
                props.put("${SUBST-RMI-SERVER-PORT}", Integer.toString(rmiServerPort));
                copyFilteredResourceToDir(karafBase, "etc/system.properties", props, printOutput);
                copyFilteredResourceToDir(karafBase, "etc/org.apache.karaf.shell.cfg", props, printOutput);
                copyFilteredResourceToDir(karafBase, "etc/org.apache.karaf.management.cfg", props, printOutput);
               
                copyFilteredResourceToDir(karafBase, "bin/karaf", props, printOutput);
                copyFilteredResourceToDir(karafBase, "bin/start", props, printOutput);
                copyFilteredResourceToDir(karafBase, "bin/stop", props, printOutput);

                copyFilteredResourceToDir(karafBase, "bin/karaf.bat", props, printOutput);
                copyFilteredResourceToDir(karafBase, "bin/start.bat", props, printOutput);
                copyFilteredResourceToDir(karafBase, "bin/stop.bat", props, printOutput);

                try {
                    chmod(new File(karafBase, "bin/karaf"), "a+x");
                    chmod(new File(karafBase, "bin/start"), "a+x");
                    chmod(new File(karafBase, "bin/stop"), "a+x");
                } catch (IOException e) {
                    LOGGER.debug("Could not set file mode on scripts.", e);
                }

                String javaOpts = settings.getJavaOpts();
                if (javaOpts == null || javaOpts.length() == 0) {
                    javaOpts = DEFAULT_JAVA_OPTS;
                }
                InstanceState is = new InstanceState();
                is.name = name;
                is.loc = karafBase.toString();
                is.opts = javaOpts;
                state.instances.put(name, is);
                InstanceImpl instance = new InstanceImpl(InstanceServiceImpl.this, name);
                InstanceServiceImpl.this.proxies.put(name, instance);
                return instance;
            }
        });
    }

    void addFeaturesFromSettings(File featuresCfg, final InstanceSettings settings) throws IOException {
        FileLockUtils.execute(featuresCfg, new FileLockUtils.RunnableWithProperties() {
            public void run(org.apache.felix.utils.properties.Properties properties) throws IOException {
                appendToPropList(properties, "featuresBoot", settings.getFeatures());
                appendToPropList(properties, "featuresRepositories", settings.getFeatureURLs());
            }
        });
{'fraction_non_alphanumeric': 0.11153358681875793, 'fraction_numerical': 0.0020842134910575975, 'mean_word_length': 2.4569175348067374, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3025705', 'n_tokens_mistral': 9849, 'n_tokens_neox': 9497, 'n_words': 2070}