| /* |
| * 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 java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.OutputStream; |
| import java.io.PrintStream; |
| import java.net.Socket; |
| import java.net.URL; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.nio.file.attribute.PosixFilePermission; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Scanner; |
| import java.util.Set; |
| import java.util.TreeMap; |
| |
| import org.apache.felix.utils.properties.InterpolationHelper; |
| import org.apache.felix.utils.properties.Properties; |
| 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.instance.main.Execute; |
| import org.apache.karaf.jpm.Process; |
| import org.apache.karaf.jpm.impl.ProcessBuilderFactoryImpl; |
| import org.apache.karaf.jpm.impl.ScriptUtils; |
| import org.apache.karaf.profile.Profile; |
| import org.apache.karaf.profile.ProfileBuilder; |
| import org.apache.karaf.profile.ProfileService; |
| import org.apache.karaf.shell.support.ansi.SimpleAnsi; |
| import org.apache.karaf.util.StreamUtils; |
| import org.apache.karaf.util.config.PropertiesLoader; |
| import org.apache.karaf.util.locks.FileLockUtils; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.FrameworkUtil; |
| import org.osgi.framework.ServiceReference; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| 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 String RESOURCE_BASE = "org/apache/karaf/instance/resources/"; |
| |
| 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_TIMEOUT = "karaf.shutdown.timeout"; |
| |
| private static final String DEFAULT_SHUTDOWN_COMMAND = "SHUTDOWN"; |
| |
| public static final String DEFAULT_JAVA_OPTS = "-Xmx512M -Dcom.sun.management.jmxremote -XX:+UnlockDiagnosticVMOptions"; |
| |
| private LinkedHashMap<String, InstanceImpl> proxies = new LinkedHashMap<>(); |
| |
| 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 State() { |
| //read port start value from the root instance configuration |
| try { |
| Properties shellProperty = new Properties(); |
| shellProperty.load(new File(System.getProperty("karaf.etc"), "org.apache.karaf.shell.cfg")); |
| defaultSshPortStart = getInt(shellProperty,"sshPort", 8101); |
| Properties managementProperty = new Properties(); |
| managementProperty.load(new File(System.getProperty("karaf.etc"), "org.apache.karaf.management.cfg")); |
| defaultRmiRegistryPortStart = getInt(managementProperty, "rmiRegistryPort", 1099); |
| defaultRmiServerPortStart = getInt(managementProperty, "rmiServerPort", 1099); |
| } catch (Exception e) { |
| LOGGER.debug("Could not read port start value from the root instance configuration.", e); |
| } |
| } |
| |
| |
| } |
| |
| public InstanceServiceImpl() { |
| String prop = System.getProperty("karaf.instances"); |
| if (prop != null) { |
| storageLocation = new File(prop); |
| } |
| } |
| |
| 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(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<>(); |
| |
| 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<>(this.proxies.keySet()); |
| for (String name : names) { |
| if (!state.instances.containsKey(name)) { |
| this.proxies.remove(name); |
| } |
| } |
| return state; |
| } |
| |
| private void saveData(State state, 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 static boolean getBool(Properties storage, String name, boolean def) { |
| Object value = storage.get(name); |
| if (value instanceof Boolean) { |
| return (Boolean) value; |
| } else if (value != null) { |
| return Boolean.parseBoolean(value.toString()); |
| } else { |
| return def; |
| } |
| } |
| |
| private static int getInt(Properties storage, String name, int def) { |
| Object value = storage.get(name); |
| if (value instanceof Number) { |
| return ((Number) value).intValue(); |
| } else if (value != null) { |
| return Integer.parseInt(value.toString()); |
| } else { |
| return def; |
| } |
| } |
| |
| private static String getString(Properties storage, String name, String def) { |
| Object value = storage.get(name); |
| return value != null ? value.toString() : def; |
| } |
| |
| interface Task<U, T> { |
| T call(U state) throws IOException; |
| } |
| |
| synchronized <T> T execute(final Task<State, T> callback, final boolean writeToFile) { |
| 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, properties -> { |
| State state = loadData(properties); |
| T t = callback.call(state); |
| if (writeToFile) { |
| saveData(state, properties); |
| } |
| return t; |
| }, writeToFile); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } else { |
| throw new IllegalStateException("Instance storage location does not exist: " + storageFile); |
| } |
| } |
| |
| private static 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); |
| } |
| } |
| } |
| |
| private static void logDebug(String message, boolean printOutput, Object... args) { |
| if (LOGGER.isDebugEnabled() || printOutput) { |
| String formatted = String.format(message, args); |
| LOGGER.debug(formatted); |
| if (printOutput) { |
| println(formatted); |
| } |
| } |
| } |
| |
| public synchronized Instance createInstance(final String name, final InstanceSettings settings, final boolean printOutput) throws Exception { |
| return execute(state -> { |
| if (state.instances.get(name) != null) { |
| throw new IllegalArgumentException("Instance '" + name + "' already exists"); |
| } |
| if (!settings.getProfiles().isEmpty()) { |
| try { |
| ProfileApplier.verify(); |
| } catch (NoClassDefFoundError error) { |
| throw new IllegalArgumentException("Profile service package is not available"); |
| } |
| } |
| |
| 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; |
| } |
| String sshHost = settings.getAddress(); |
| 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, "etc/scripts", printOutput); |
| mkdir(karafBase, "system", printOutput); |
| mkdir(karafBase, "deploy", printOutput); |
| mkdir(karafBase, "data", printOutput); |
| |
| Map<String, URL> textResources = new HashMap<>(settings.getTextResources()); |
| Map<String, URL> binaryResources = new HashMap<>(settings.getBinaryResources()); |
| |
| String[] resources = |
| { |
| "etc/all.policy", |
| "etc/config.properties", |
| "etc/custom.properties", |
| "etc/distribution.info", |
| "etc/equinox-debug.properties", |
| "etc/java.util.logging.properties", |
| "etc/jmx.acl.cfg", |
| "etc/jmx.acl.java.lang.Memory.cfg", |
| "etc/jmx.acl.org.apache.karaf.bundle.cfg", |
| "etc/jmx.acl.org.apache.karaf.config.cfg", |
| "etc/jmx.acl.org.apache.karaf.security.jmx.cfg", |
| "etc/jmx.acl.osgi.compendium.cm.cfg", |
| "etc/jre.properties", |
| "etc/keys.properties", |
| "etc/org.apache.felix.eventadmin.impl.EventAdmin.cfg", |
| "etc/org.apache.felix.fileinstall-deploy.cfg", |
| "etc/org.apache.karaf.command.acl.bundle.cfg", |
| "etc/org.apache.karaf.command.acl.config.cfg", |
| "etc/org.apache.karaf.command.acl.feature.cfg", |
| "etc/org.apache.karaf.command.acl.jaas.cfg", |
| "etc/org.apache.karaf.command.acl.kar.cfg", |
| "etc/org.apache.karaf.command.acl.scope_bundle.cfg", |
| "etc/org.apache.karaf.command.acl.shell.cfg", |
| "etc/org.apache.karaf.command.acl.system.cfg", |
| "etc/org.apache.karaf.features.repos.cfg", |
| "etc/org.apache.karaf.jaas.cfg", |
| "etc/org.apache.karaf.kar.cfg", |
| "etc/org.apache.karaf.log.cfg", |
| "etc/org.ops4j.pax.logging.cfg", |
| "etc/org.ops4j.pax.url.mvn.cfg", |
| "etc/shell.init.script", |
| "etc/users.properties", |
| "etc/scripts/shell.completion.script", |
| FEATURES_CFG |
| }; |
| copyResourcesToDir(resources, karafBase, textResources, 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")); |
| |
| // align child with any bundles we have overriden in the root instance |
| File rootOverrides = new File(rootEtc, "overrides.properties"); |
| if (rootOverrides.exists()) { |
| copy(rootOverrides, new File(karafBase, "etc/overrides.properties")); |
| } |
| |
| HashMap<String, String> props = new HashMap<>(); |
| 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-SSH-HOST}", sshHost); |
| props.put("${SUBST-RMI-REGISTRY-PORT}", Integer.toString(rmiRegistryPort)); |
| props.put("${SUBST-RMI-SERVER-PORT}", Integer.toString(rmiServerPort)); |
| |
| String[] filteredResources = |
| { |
| "etc/system.properties", |
| "etc/org.apache.karaf.shell.cfg", |
| "etc/org.apache.karaf.management.cfg", |
| "bin/karaf", |
| "bin/start", |
| "bin/stop", |
| "bin/karaf.bat", |
| "bin/start.bat", |
| "bin/stop.bat" |
| }; |
| copyFilteredResourcesToDir(filteredResources, karafBase, textResources, props, printOutput); |
| |
| try { |
| makeFileExecutable(new File(karafBase, "bin/karaf")); |
| makeFileExecutable(new File(karafBase, "bin/start")); |
| makeFileExecutable(new File(karafBase, "bin/stop")); |
| } catch (IOException e) { |
| LOGGER.debug("Could not set file mode on scripts.", e); |
| } |
| |
| for (String resource : textResources.keySet()) { |
| copyFilteredResourceToDir(resource, karafBase, textResources, props, printOutput); |
| } |
| |
| for (String resource : binaryResources.keySet()) { |
| copyBinaryResourceToDir(resource, karafBase, binaryResources, printOutput); |
| } |
| |
| if (!settings.getProfiles().isEmpty()) { |
| ProfileApplier.applyProfiles(karafBase, settings.getProfiles(), printOutput); |
| } |
| |
| 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; |
| }, true); |
| } |
| |
| void addFeaturesFromSettings(File featuresCfg, final InstanceSettings settings) throws IOException { |
| FileLockUtils.execute(featuresCfg, properties -> { |
| appendToPropList(properties, "featuresBoot", settings.getFeatures()); |
| appendToPropList(properties, "featuresRepositories", settings.getFeatureURLs()); |
| }, true); |
| } |
| |
| private static void appendToPropList(Properties p, String key, List<String> elements) { |
| if (elements == null) { |
| return; |
| } |
| StringBuilder sb = new StringBuilder(p.get(key).trim()); |
| for (String f : elements) { |
| if (sb.length() > 0) { |
| sb.append(','); |
| } |
| sb.append(f); |
| } |
| p.put(key, sb.toString()); |
| } |
| |
| public Instance[] getInstances() { |
| return execute(state -> proxies.values().toArray(new Instance[proxies.size()]), false); |
| } |
| |
| public Instance getInstance(final String name) { |
| return execute(state -> proxies.get(name), false); |
| } |
| |
| public void startInstance(final String name, final String javaOpts) { |
| execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| checkPid(instance); |
| if (instance.pid != 0) { |
| throw new IllegalStateException("Instance already started"); |
| } |
| doStart(instance, name, javaOpts); |
| return null; |
| }, true); |
| } |
| |
| private void doStart(InstanceState instance, String name, String javaOpts) throws IOException { |
| String opts = javaOpts; |
| if (opts == null || opts.length() == 0) { |
| opts = instance.opts; |
| } |
| if (opts == null || opts.length() == 0) { |
| opts = DEFAULT_JAVA_OPTS; |
| } |
| |
| // fallback and read karafOpts from KARAF_OPTS environment if no System property present |
| String karafOptsEnv = System.getenv("KARAF_OPTS"); |
| String karafOpts = System.getProperty("karaf.opts", karafOptsEnv != null ? karafOptsEnv : ""); |
| |
| String location = instance.loc; |
| |
| File libDir = new File(System.getProperty("karaf.home"), "lib"); |
| File bootLibDir = new File(libDir, "boot"); |
| File childLibDir = new File(location, "lib"); |
| |
| StringBuilder classpath = classpathFromLibDir(bootLibDir); |
| StringBuilder childClasspath = classpathFromLibDir(childLibDir); |
| if (childClasspath.length() > 0 && !bootLibDir.equals(childLibDir)) { |
| classpath.append(System.getProperty("path.separator")); |
| classpath.append(childClasspath); |
| } |
| |
| String jdkOpts; |
| if (!System.getProperty("java.version").startsWith("1.")) { |
| StringBuilder jdk9Classpath = classpathFromLibDir(new File(new File(System.getProperty("karaf.home"), "lib"), "jdk9plus")); |
| if (jdk9Classpath.length() > 0) { |
| classpath.append(System.getProperty("path.separator")); |
| classpath.append(jdk9Classpath); |
| } |
| jdkOpts = " --add-reads=java.xml=java.logging" + |
| " --add-exports=java.base/org.apache.karaf.specs.locator=java.xml,ALL-UNNAMED" + |
| " --patch-module java.base=lib/endorsed/org.apache.karaf.specs.locator-" + System.getProperty("karaf.version") + ".jar" + |
| " --patch-module java.xml=lib/endorsed/org.apache.karaf.specs.java.xml-" + System.getProperty("karaf.version") + ".jar" + |
| " --add-opens java.base/java.security=ALL-UNNAMED" + |
| " --add-opens java.base/java.net=ALL-UNNAMED" + |
| " --add-opens java.base/java.lang=ALL-UNNAMED" + |
| " --add-opens java.base/java.util=ALL-UNNAMED" + |
| " --add-opens java.naming/javax.naming.spi=ALL-UNNAMED" + |
| " --add-opens java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED" + |
| " --add-exports=java.base/sun.net.www.protocol.file=ALL-UNNAMED" + |
| " --add-exports=java.base/sun.net.www.protocol.ftp=ALL-UNNAMED" + |
| " --add-exports=java.base/sun.net.www.protocol.http=ALL-UNNAMED" + |
| " --add-exports=java.base/sun.net.www.protocol.https=ALL-UNNAMED" + |
| " --add-exports=java.base/sun.net.www.protocol.jar=ALL-UNNAMED" + |
| " --add-exports=java.base/sun.net.www.content.text=ALL-UNNAMED" + |
| " --add-exports=jdk.xml.dom/org.w3c.dom.html=ALL-UNNAMED" + |
| " --add-exports=jdk.naming.rmi/com.sun.jndi.url.rmi=ALL-UNNAMED"; |
| } else { |
| jdkOpts = " -Djava.endorsed.dirs=\"" + new File(new File(new File(System.getProperty("java.home"), "jre"), "lib"), "endorsed") + System.getProperty("path.separator") + new File(new File(System.getProperty("java.home"), "lib"), "endorsed") + System.getProperty("path.separator") + new File(libDir, "endorsed").getCanonicalPath() + "\"" |
| + " -Djava.ext.dirs=\"" + new File(new File(new File(System.getProperty("java.home"), "jre"), "lib"), "ext") + System.getProperty("path.separator") + new File(new File(System.getProperty("java.home"), "lib"), "ext") + System.getProperty("path.separator") + new File(libDir, "ext").getCanonicalPath() + "\""; |
| } |
| String command = "\"" |
| + new File(System.getProperty("java.home"), ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java").getCanonicalPath() |
| + "\" " + opts |
| + " " + karafOpts |
| + " " + jdkOpts |
| + " -Djava.util.logging.config.file=\"" + new File(location, "etc/java.util.logging.properties").getCanonicalPath() + "\"" |
| + " -Dkaraf.home=\"" + System.getProperty("karaf.home") + "\"" |
| + " -Dkaraf.base=\"" + new File(location).getCanonicalPath() + "\"" |
| + " -Dkaraf.data=\"" + new File(new File(location).getCanonicalPath(), "data") + "\"" |
| + " -Dkaraf.etc=\"" + new File(new File(location).getCanonicalPath(), "etc") + "\"" |
| + " -Dkaraf.log=\"" + new File(new File(new File(location).getCanonicalFile(), "data"), "log") + "\"" |
| + " -Djava.io.tmpdir=\"" + new File(new File(new File(location).getCanonicalFile(), "data") , "tmp") + "\"" |
| + " -Dkaraf.restart.jvm.supported=true" |
| + " -Dkaraf.startLocalConsole=false" |
| + " -Dkaraf.startRemoteShell=true" |
| + " -classpath \"" + classpath.toString() + "\"" |
| + " org.apache.karaf.main.Main server"; |
| if (System.getenv("KARAF_REDIRECT") != null && !System.getenv("KARAF_REDIRECT").isEmpty()) { |
| command = command + " >> " + System.getenv("KARAF_REDIRECT"); |
| } |
| |
| LOGGER.debug("Starting instance " + name + " with command: " + command); |
| Process process = new ProcessBuilderFactoryImpl().newBuilder() |
| .directory(new File(location)) |
| .command(command) |
| .start(); |
| instance.pid = process.getPid(); |
| } |
| |
| private StringBuilder classpathFromLibDir(File libDir) throws IOException { |
| File[] jars = libDir.listFiles((dir, name) -> name.endsWith(".jar")); |
| StringBuilder classpath = new StringBuilder(); |
| if (jars != null) { |
| for (File jar : jars) { |
| if (classpath.length() > 0) { |
| classpath.append(System.getProperty("path.separator")); |
| } |
| classpath.append(jar.getCanonicalPath()); |
| } |
| } |
| return classpath; |
| } |
| |
| private void addJar(StringBuilder sb, String groupId, String artifactId) { |
| File artifactDir = new File(System.getProperty("karaf.home") + File.separator + |
| "system" + File.separator + |
| groupId.replaceAll("\\.", File.separator) + File.separator + |
| artifactId + File.separator); |
| TreeMap<String, File> jars = new TreeMap<>(); |
| String[] versions = artifactDir.list(); |
| if (versions != null) { |
| for (String version : versions) { |
| File jar = new File(artifactDir, version + File.separator + artifactId + "-" + version + ".jar"); |
| if (jar.exists()) { |
| jars.put(version, jar); |
| } |
| } |
| } |
| if (jars.isEmpty()) { |
| throw new IllegalStateException("Cound not find jar for " + groupId + "/" + artifactId); |
| } |
| if (sb.length() > 0) { |
| sb.append(File.pathSeparator); |
| } |
| sb.append(jars.lastEntry().getValue().getAbsolutePath()); |
| } |
| |
| public void restartInstance(final String name, final String javaOpts) { |
| execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| String current = System.getProperty("karaf.name"); |
| if (name.equals(current)) { |
| String location = System.getProperty("karaf.home"); |
| StringBuilder classpath = new StringBuilder(); |
| addJar(classpath, "org.apache.karaf.instance", "org.apache.karaf.instance.core"); |
| addJar(classpath, "org.apache.karaf.shell", "org.apache.karaf.shell.core"); |
| addJar(classpath, "org.ops4j.pax.logging", "pax-logging-api"); |
| addJar(classpath, "jline", "jline"); |
| String command = "\"" |
| + new File(System.getProperty("java.home"), ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java").getCanonicalPath() |
| + "\" " |
| + " -Djava.util.logging.config.file=\"" + new File(location, "etc/java.util.logging.properties").getCanonicalPath() + "\"" |
| + " -Dkaraf.home=\"" + System.getProperty("karaf.home") + "\"" |
| + " -Dkaraf.base=\"" + new File(location).getCanonicalPath() + "\"" |
| + " -Dkaraf.data=\"" + new File(new File(location).getCanonicalPath(), "data") + "\"" |
| + " -Dkaraf.etc=\"" + new File(new File(location).getCanonicalPath(), "etc") + "\"" |
| + " -Dkaraf.log=\"" + new File(new File(new File(location).getCanonicalFile(), "data"), "log") + "\"" |
| + " -Dkaraf.instances=\"" + System.getProperty("karaf.instances") + "\"" |
| + " -classpath \"" + classpath.toString() + "\"" |
| + " " + Execute.class.getName() |
| + " restart --java-opts \"" + javaOpts + "\" " + name; |
| new ProcessBuilderFactoryImpl().newBuilder() |
| .directory(new File(System.getProperty("karaf.home"))) |
| .command(command) |
| .start(); |
| } else { |
| checkPid(instance); |
| if (instance.pid != 0) { |
| cleanShutdown(instance); |
| } |
| doStart(instance, name, javaOpts); |
| } |
| return null; |
| }, true); |
| } |
| |
| public void stopInstance(final String name) { |
| Integer pid = execute(state -> { |
| int rootInstancePID = 0; |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| checkPid(instance); |
| if (instance.pid == 0) { |
| throw new IllegalStateException("Instance already stopped"); |
| } |
| cleanShutdown(instance); |
| if (instance.pid > 0) { |
| if (!instance.root) { |
| Process process = new ProcessBuilderFactoryImpl().newBuilder().attach(instance.pid); |
| process.destroy(); |
| } else { |
| //can't simply destroy root instance here |
| //as it will lose the update in instances.properties |
| //because of no chance to run the saveData |
| rootInstancePID = instance.pid; |
| } |
| instance.pid = 0; |
| |
| } |
| return rootInstancePID; |
| }, true); |
| if (pid != 0 && isInstanceRoot(name)) { |
| Process process; |
| try { |
| process = new ProcessBuilderFactoryImpl().newBuilder().attach(pid); |
| process.destroy(); |
| } catch (IOException e) { |
| LOGGER.debug("Unable to cleanly shutdown root instance ", e); |
| } |
| } |
| } |
| |
| public void destroyInstance(final String name) { |
| execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| checkPid(instance); |
| if (instance.pid != 0) { |
| throw new IllegalStateException("Instance not stopped"); |
| } |
| deleteFile(new File(instance.loc)); |
| state.instances.remove(name); |
| InstanceServiceImpl.this.proxies.remove(name); |
| return null; |
| }, true); |
| } |
| |
| public void renameInstance(final String oldName, final String newName, final boolean printOutput) throws Exception { |
| execute(state -> { |
| if (state.instances.get(newName) != null) { |
| throw new IllegalArgumentException("Instance " + newName + " already exists"); |
| } |
| InstanceState instance = state.instances.get(oldName); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + oldName + " not found"); |
| } |
| if (instance.root) { |
| throw new IllegalArgumentException("Root instance cannot be renamed"); |
| } |
| checkPid(instance); |
| if (instance.pid != 0) { |
| throw new IllegalStateException("Instance not stopped"); |
| } |
| |
| println("Renaming instance " |
| + SimpleAnsi.INTENSITY_BOLD + oldName + SimpleAnsi.INTENSITY_NORMAL |
| + " to " |
| + SimpleAnsi.INTENSITY_BOLD + newName + SimpleAnsi.INTENSITY_NORMAL); |
| // rename directory |
| String oldLocationPath = instance.loc; |
| File oldLocation = new File(oldLocationPath); |
| String basedir = oldLocation.getParent(); |
| File newLocation = new File(basedir, newName); |
| oldLocation.renameTo(newLocation); |
| // create the properties map including the instance name and instance location |
| // TODO: replacing is bad, we should re-extract the needed files |
| HashMap<String, String> props = new HashMap<>(); |
| props.put(oldName, newName); |
| props.put(oldLocationPath, newLocation.getPath()); |
| // replace all references to the "old" name by the new one in etc/system.properties |
| // NB: it's replacement to avoid to override the user's changes |
| filterResource(newLocation, "etc/system.properties", props); |
| // replace all references to the "old" name by the new one in bin/karaf |
| filterResource(newLocation, "bin/karaf", props); |
| filterResource(newLocation, "bin/start", props); |
| filterResource(newLocation, "bin/stop", props); |
| filterResource(newLocation, "bin/karaf.bat", props); |
| filterResource(newLocation, "bin/start.bat", props); |
| filterResource(newLocation, "bin/stop.bat", props); |
| // update instance |
| instance.name = newName; |
| instance.loc = newLocation.getPath(); |
| state.instances.put(newName, instance); |
| state.instances.remove(oldName); |
| InstanceImpl proxy = InstanceServiceImpl.this.proxies.remove(oldName); |
| if (proxy == null) { |
| proxy = new InstanceImpl(InstanceServiceImpl.this, newName); |
| } else { |
| proxy.doSetName(newName); |
| } |
| InstanceServiceImpl.this.proxies.put(newName, proxy); |
| return null; |
| }, true); |
| } |
| |
| public synchronized Instance cloneInstance(final String name, final String cloneName, final InstanceSettings settings, final boolean printOutput) throws Exception { |
| final int instanceSshPort = getInstanceSshPort(name); |
| final int instanceRmiRegistryPort = getInstanceRmiRegistryPort(name); |
| final int instanceRmiServerPort = getInstanceRmiServerPort(name); |
| return execute(state -> { |
| if (state.instances.get(cloneName) != null) { |
| throw new IllegalArgumentException("Instance " + cloneName + " already exists"); |
| } |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| |
| // define the clone instance location |
| String cloneLocationPath = settings.getLocation() != null ? settings.getLocation() : cloneName; |
| File cloneLocation = new File(cloneLocationPath); |
| if (!cloneLocation.isAbsolute()) { |
| cloneLocation = new File(storageLocation, cloneLocationPath); |
| } |
| // copy instance directory |
| String locationPath = instance.loc; |
| File location = new File(locationPath); |
| copy(location, cloneLocation); |
| // create the properties map including the instance name, location, ssh and rmi port numbers |
| // TODO: replacing stuff anywhere is not really good, we might end up replacing unwanted stuff |
| // TODO: if no ports are overriden, shouldn't we choose new ports ? |
| HashMap<String, String> props = new HashMap<>(); |
| props.put(name, cloneName); |
| props.put(locationPath, cloneLocationPath); |
| if (settings.getSshPort() > 0) |
| props.put(Integer.toString(instanceSshPort), Integer.toString(settings.getSshPort())); |
| if (settings.getRmiRegistryPort() > 0) |
| props.put(Integer.toString(instanceRmiRegistryPort), Integer.toString(settings.getRmiRegistryPort())); |
| if (settings.getRmiServerPort() > 0) |
| props.put(Integer.toString(instanceRmiServerPort), Integer.toString(settings.getRmiServerPort())); |
| |
| // filtering clone files |
| filterResource(cloneLocation, "etc/custom.properties", props); |
| filterResource(cloneLocation, "etc/org.apache.karaf.management.cfg", props); |
| filterResource(cloneLocation, "etc/org.apache.karaf.shell.cfg", props); |
| filterResource(cloneLocation, "etc/system.properties", props); |
| filterResource(cloneLocation, "bin/karaf", props); |
| filterResource(cloneLocation, "bin/start", props); |
| filterResource(cloneLocation, "bin/stop", props); |
| filterResource(cloneLocation, "bin/karaf.bat", props); |
| filterResource(cloneLocation, "bin/start.bat", props); |
| filterResource(cloneLocation, "bin/stop.bat", props); |
| // create and add the clone instance in the registry |
| String javaOpts = settings.getJavaOpts(); |
| if (javaOpts == null || javaOpts.length() == 0) { |
| javaOpts = DEFAULT_JAVA_OPTS; |
| } |
| InstanceState is = new InstanceState(); |
| is.name = cloneName; |
| is.loc = cloneLocation.toString(); |
| is.opts = javaOpts; |
| state.instances.put(cloneName, is); |
| InstanceImpl cloneInstance = new InstanceImpl(InstanceServiceImpl.this, cloneName); |
| InstanceServiceImpl.this.proxies.put(cloneName, cloneInstance); |
| return cloneInstance; |
| }, true); |
| } |
| |
| private void checkPid(InstanceState instance) throws IOException { |
| if (instance.pid != 0) { |
| Process process = new ProcessBuilderFactoryImpl().newBuilder().attach(instance.pid); |
| if (!process.isRunning()) { |
| instance.pid = 0; |
| } |
| } |
| } |
| |
| protected void cleanShutdown(InstanceState instance) { |
| try { |
| File file = new File(new File(instance.loc, "etc"), CONFIG_PROPERTIES_FILE_NAME); |
| Properties props = PropertiesLoader.loadPropertiesFile(file.toURI().toURL(), false); |
| props.put("karaf.base", new File(instance.loc).getCanonicalPath()); |
| props.put("karaf.home", System.getProperty("karaf.home")); |
| props.put("karaf.data", new File(new File(instance.loc), "data").getCanonicalPath()); |
| props.put("karaf.etc", new File(new File(instance.loc), "etc").getCanonicalPath()); |
| props.put("karaf.log", new File(new File(new File(instance.loc), "data"), "log").getCanonicalPath()); |
| InterpolationHelper.performSubstitution(props, null, true, false, true); |
| int port = Integer.parseInt(props.getProperty(KARAF_SHUTDOWN_PORT, "0")); |
| String host = props.getProperty(KARAF_SHUTDOWN_HOST, "localhost"); |
| String portFile = props.getProperty(KARAF_SHUTDOWN_PORT_FILE); |
| String shutdown = props.getProperty(KARAF_SHUTDOWN_COMMAND, DEFAULT_SHUTDOWN_COMMAND); |
| if (port == 0 && portFile != null) { |
| try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(portFile)))) { |
| String portStr = r.readLine(); |
| port = Integer.parseInt(portStr); |
| } |
| } |
| // We found the port, try to send the command |
| if (port > 0) { |
| Socket s = new Socket(host, port); |
| s.getOutputStream().write(shutdown.getBytes()); |
| s.close(); |
| long stopTimeout = Long.parseLong(props.getProperty(KARAF_SHUTDOWN_TIMEOUT, |
| Long.toString(getStopTimeout()))); |
| long t = System.currentTimeMillis() + stopTimeout; |
| do { |
| Thread.sleep(100); |
| checkPid(instance); |
| } while (System.currentTimeMillis() < t && instance.pid > 0); |
| } |
| } catch (Exception e) { |
| LOGGER.debug("Unable to cleanly shutdown instance " + instance.name, e); |
| } |
| } |
| |
| int getInstanceSshPort(String name) { |
| return getKarafPort(name, "etc/org.apache.karaf.shell.cfg", "sshPort"); |
| } |
| |
| void changeInstanceSshPort(String name, final int port) throws Exception { |
| setKarafPort(name, "etc/org.apache.karaf.shell.cfg", "sshPort", port); |
| } |
| |
| String getInstanceSshHost(String name) { |
| return getKarafHost(name, "etc/org.apache.karaf.shell.cfg", "sshHost"); |
| } |
| |
| int getInstanceRmiRegistryPort(String name) { |
| return getKarafPort(name, "etc/org.apache.karaf.management.cfg", "rmiRegistryPort"); |
| } |
| |
| void changeInstanceRmiRegistryPort(String name, final int port) throws Exception { |
| setKarafPort(name, "etc/org.apache.karaf.management.cfg", "rmiRegistryPort", port); |
| } |
| |
| String getInstanceRmiRegistryHost(String name) { |
| return getKarafHost(name, "etc/org.apache.karaf.management.cfg", "rmiRegistryHost"); |
| } |
| |
| int getInstanceRmiServerPort(String name) { |
| return getKarafPort(name, "etc/org.apache.karaf.management.cfg", "rmiServerPort"); |
| } |
| |
| void changeInstanceRmiServerPort(String name, int port) throws Exception { |
| setKarafPort(name, "etc/org.apache.karaf.management.cfg", "rmiServerPort", port); |
| } |
| |
| String getInstanceRmiServerHost(String name) { |
| return getKarafHost(name, "etc/org.apache.karaf.management.cfg", "rmiServerHost"); |
| } |
| |
| private int getKarafPort(final String name, final String path, final String key) { |
| return execute(state -> getKarafPort(state, name, path, key), false); |
| } |
| |
| |
| private Integer getKarafPort(State state, String name, String path, final String key) { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| File f = new File(instance.loc, path); |
| try { |
| return FileLockUtils.execute(f, properties -> { |
| Object obj = properties.get(key); |
| return obj instanceof Number ? ((Number) obj).intValue() : Integer.parseInt(obj.toString()); |
| }, false); |
| } catch (IOException e) { |
| return 0; |
| } |
| } |
| |
| private void setKarafPort(final String name, final String path, final String key, final int port) throws IOException { |
| execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| checkPid(instance); |
| if (instance.pid != 0) { |
| throw new IllegalStateException("Instance is not stopped"); |
| } |
| File f = new File(instance.loc, path); |
| FileLockUtils.execute(f, properties -> { |
| properties.put(key, String.valueOf(port)); |
| }, true); |
| return null; |
| }, true); |
| } |
| |
| private String getKarafHost(final String name, final String path, final String key) { |
| return execute(state -> InstanceServiceImpl.this.getKarafHost(state, name, path, key), false); |
| } |
| |
| private String getKarafHost(State state, String name, String path, final String key) { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| File f = new File(instance.loc, path); |
| try { |
| return FileLockUtils.execute(f, (Properties properties) -> properties.get(key).toString(), false); |
| } catch (IOException e) { |
| return "0.0.0.0"; |
| } |
| } |
| |
| boolean isInstanceRoot(final String name) { |
| return execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| return instance.root; |
| }, false); |
| } |
| |
| String getInstanceLocation(final String name) { |
| return execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| return instance.loc; |
| }, true); |
| } |
| |
| int getInstancePid(final String name) { |
| boolean updateInstanceProperties = isInstancePidNeedUpdate(name); |
| return execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| checkPid(instance); |
| return instance.pid; |
| }, updateInstanceProperties); |
| } |
| |
| String getInstanceJavaOpts(final String name) { |
| return execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| return instance.opts; |
| }, false); |
| } |
| |
| void changeInstanceJavaOpts(final String name, final String opts) { |
| execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| instance.opts = opts; |
| return null; |
| }, true); |
| } |
| |
| String getInstanceState(final String name) { |
| boolean updateInstanceProperties = isInstancePidNeedUpdate(name); |
| return execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| int port = getKarafPort(state, name, "etc/org.apache.karaf.shell.cfg", "sshPort"); |
| String host = getKarafHost(state, name, "etc/org.apache.karaf.shell.cfg", "sshHost"); |
| if (host.equals("0.0.0.0")) { |
| host = "localhost"; |
| } |
| if (!new File(instance.loc).isDirectory() || port <= 0) { |
| return Instance.ERROR; |
| } |
| checkPid(instance); |
| if (instance.pid == 0) { |
| return Instance.STOPPED; |
| } else { |
| try { |
| Socket s = new Socket(host, port); |
| s.close(); |
| return Instance.STARTED; |
| } catch (Exception e) { |
| // ignore |
| } |
| return Instance.STARTING; |
| } |
| }, updateInstanceProperties); |
| } |
| |
| private boolean deleteFile(File fileToDelete) { |
| if (fileToDelete == null || !fileToDelete.exists()) { |
| return true; |
| } |
| boolean result = true; |
| if (fileToDelete.isDirectory()) { |
| File[] files = fileToDelete.listFiles(); |
| if (files == null) { |
| result = false; |
| } else { |
| for (File file : files) { |
| if (file.getName().equals(".") || file.getName().equals("..")) { |
| continue; |
| } |
| if (file.isDirectory()) { |
| result &= deleteFile(file); |
| } else { |
| result &= file.delete(); |
| } |
| } |
| } |
| } |
| result &= fileToDelete.delete(); |
| return result; |
| } |
| |
| private void copyResourcesToDir(String[] resourcesToCopy, File target, Map<String, URL> resources, boolean printOutput) throws IOException { |
| for (String resource : resourcesToCopy) { |
| copyResourceToDir(resource, target, resources, printOutput); |
| } |
| } |
| |
| private void copyResourceToDir(String resource, File target, Map<String, URL> resources, boolean printOutput) throws IOException { |
| File outFile = new File(target, resource); |
| if( !outFile.exists() ) { |
| logDebug("Creating file: %s", printOutput, outFile.getPath()); |
| try ( |
| InputStream is = getResourceStream(resource, resources); |
| OutputStream os = new FileOutputStream(outFile) |
| ) { |
| if (is == null) { |
| logInfo("\tWARNING: unable to find %s", true, resource); |
| } else { |
| copyStream(is, os); |
| } |
| } |
| } |
| } |
| |
| private InputStream getResourceStream(String resource, Map<String, URL> resources) throws IOException { |
| return resources.containsKey(resource) |
| ? resources.remove(resource).openStream() |
| : getClass().getClassLoader().getResourceAsStream(RESOURCE_BASE + resource); |
| } |
| |
| private static void println(String st) { |
| System.out.println(st); |
| } |
| |
| protected static Properties loadPropertiesFile(URL configPropURL) throws Exception { |
| try ( |
| InputStream is = configPropURL.openConnection().getInputStream() |
| ){ |
| Properties configProps = new Properties(); |
| configProps.load(is); |
| return configProps; |
| } |
| catch (Exception ex) { |
| System.err.println("Error loading config properties from " + configPropURL); |
| System.err.println("Main: " + ex); |
| return null; |
| } |
| } |
| |
| /** |
| * Read stream one line at a time so that we can use the platform |
| * line ending when we write it out. |
| * @param is |
| * @param os |
| */ |
| private void copyStream(InputStream is, OutputStream os) { |
| PrintStream out = new PrintStream(os); |
| Scanner scanner = new Scanner(is); |
| while (scanner.hasNextLine() ) { |
| String line = scanner.nextLine(); |
| out.println(line); |
| } |
| scanner.close(); |
| } |
| |
| private void filterResource(File basedir, String path, HashMap<String, String> props) throws IOException { |
| File file = new File(basedir, path); |
| File bak = new File(basedir, path + BACKUP_EXTENSION); |
| if (!file.exists()) { |
| return; |
| } |
| // rename the file to the backup one |
| file.renameTo(bak); |
| // copy and filter the bak file back to the original name |
| copyAndFilterResource(new FileInputStream(bak), new FileOutputStream(file), props); |
| // remove the bak file |
| bak.delete(); |
| } |
| |
| private void copyFilteredResourcesToDir(String[] resourcesToCopy, File target, Map<String, URL> resources, Map<String, String> props, boolean printOutput) throws IOException { |
| for (String resource : resourcesToCopy) { |
| copyFilteredResourceToDir(resource, target, resources, props, printOutput); |
| } |
| } |
| |
| private void copyFilteredResourceToDir(String resource, File target, Map<String, URL> resources, Map<String, String> props, boolean printOutput) throws IOException { |
| File outFile = new File(target, resource); |
| if (!outFile.exists()) { |
| logDebug("Creating file: %s", printOutput, outFile.getPath()); |
| try ( |
| InputStream is = getResourceStream(resource, resources); |
| OutputStream os = new FileOutputStream(outFile) |
| ) { |
| copyAndFilterResource(is, os, props); |
| } |
| } |
| } |
| |
| private void copyAndFilterResource(InputStream source, OutputStream target, Map<String, String> props) throws IOException { |
| // read it line at a time so that we can use the platform line ending when we write it out. |
| PrintStream out = new PrintStream(target); |
| Scanner scanner = new Scanner(source); |
| while (scanner.hasNextLine()) { |
| String line = scanner.nextLine(); |
| line = filter(line, props); |
| out.println(line); |
| } |
| scanner.close(); |
| } |
| |
| private void copyBinaryResourceToDir(String resource, File target, Map<String, URL> resources, boolean printOutput) throws IOException { |
| File outFile = new File(target, resource); |
| if( !outFile.exists() ) { |
| logDebug("Creating file: %s", printOutput, outFile.getPath()); |
| try ( |
| InputStream is = getResourceStream(resource, resources); |
| OutputStream os = new FileOutputStream(outFile) |
| ) { |
| StreamUtils.copy(is, os); |
| } |
| } |
| } |
| |
| private String filter(String line, Map<String, String> props) { |
| for (Map.Entry<String, String> i : props.entrySet()) { |
| int p1 = line.indexOf(i.getKey()); |
| if( p1 >= 0 ) { |
| String l1 = line.substring(0, p1); |
| String l2 = line.substring(p1+i.getKey().length()); |
| line = l1+i.getValue()+l2; |
| } |
| } |
| return line; |
| } |
| |
| private void mkdir(File karafBase, String path, boolean printOutput) { |
| File file = new File(karafBase, path); |
| if( !file.exists() ) { |
| logDebug("Creating dir: %s", printOutput, file.getPath()); |
| file.mkdirs(); |
| } |
| } |
| |
| private void makeFileExecutable(File serviceFile) throws IOException { |
| try { |
| Set<PosixFilePermission> permissions = new HashSet<>(); |
| permissions.add(PosixFilePermission.OWNER_EXECUTE); |
| permissions.add(PosixFilePermission.GROUP_EXECUTE); |
| permissions.add(PosixFilePermission.OTHERS_EXECUTE); |
| |
| // Get the existing permissions and add the executable permissions to them |
| Set<PosixFilePermission> filePermissions = Files.getPosixFilePermissions(serviceFile.toPath()); |
| filePermissions.addAll(permissions); |
| Files.setPosixFilePermissions(serviceFile.toPath(), filePermissions); |
| } |
| catch (UnsupportedOperationException ex) |
| { |
| serviceFile.setExecutable(true, false); |
| } |
| } |
| |
| private void copy(File source, File destination) throws IOException { |
| if (source.getName().equals("cache.lock")) { |
| // ignore cache.lock file |
| return; |
| } |
| if (source.getName().equals("lock")) { |
| // ignore lock file |
| return; |
| } |
| if (source.getName().matches("transaction_\\d+\\.log")) { |
| // ignore active txlog files |
| return; |
| } |
| if (source.getName().endsWith(".instance")) { |
| // ignore instance bundles cache |
| return; |
| } |
| if (source.isDirectory()) { |
| if (!destination.exists()) { |
| destination.mkdirs(); |
| } |
| String[] children = source.list(); |
| for (String child : children) { |
| if (!child.contains("instances") && !child.contains("lib")) |
| copy(new File(source, child), new File(destination, child)); |
| } |
| } else { |
| try( |
| InputStream in = new FileInputStream(source); |
| OutputStream out = new FileOutputStream(destination) |
| ) { |
| StreamUtils.copy(in, out); |
| } |
| } |
| } |
| |
| public void changeInstanceSshHost(String name, String host) throws Exception { |
| setKarafHost(name, "etc/org.apache.karaf.shell.cfg", "sshHost", host); |
| } |
| |
| private void setKarafHost(final String name, final String path, final String key, final String host) throws IOException { |
| execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| checkPid(instance); |
| if (instance.pid != 0) { |
| throw new IllegalStateException("Instance is not stopped"); |
| } |
| File f = new File(instance.loc, path); |
| FileLockUtils.execute(f, properties -> { properties.put(key, host); }, true); |
| return null; |
| }, true); |
| } |
| |
| private boolean isInstancePidNeedUpdate(final String name) { |
| return execute(state -> { |
| InstanceState instance = state.instances.get(name); |
| if (instance == null) { |
| throw new IllegalArgumentException("Instance " + name + " not found"); |
| } |
| int originalPid = instance.pid; |
| checkPid(instance); |
| int newPid = instance.pid; |
| return originalPid != newPid; |
| }, false); |
| } |
| |
| private static class ProfileApplier { |
| |
| // Verify that profile package is wired correctly |
| static void verify() { |
| Profile.class.getName(); |
| } |
| |
| static void applyProfiles(File karafBase, List<String> profiles, boolean printOutput) throws IOException { |
| BundleContext bundleContext = FrameworkUtil.getBundle(ProfileApplier.class).getBundleContext(); |
| ServiceReference<ProfileService> reference = bundleContext.getServiceReference(ProfileService.class); |
| ProfileService service = bundleContext.getService(reference); |
| |
| Profile profile = ProfileBuilder.Factory.create("temp") |
| .addParents(profiles) |
| .getProfile(); |
| Profile overlay = service.getOverlayProfile(profile); |
| final Profile effective = service.getEffectiveProfile(overlay, false); |
| |
| Map<String, byte[]> configs = effective.getFileConfigurations(); |
| for (Map.Entry<String, byte[]> config : configs.entrySet()) { |
| String pid = config.getKey(); |
| if (!pid.equals(Profile.INTERNAL_PID + Profile.PROPERTIES_SUFFIX)) { |
| Path configFile = Paths.get(karafBase.toString(), "etc", pid); |
| logDebug("Creating file: %s", printOutput, configFile.toString()); |
| Files.write(configFile, config.getValue()); |
| } |
| } |
| FileLockUtils.execute(new File(karafBase, FEATURES_CFG), properties -> { |
| appendToPropList(properties, "featuresBoot", effective.getFeatures()); |
| appendToPropList(properties, "featuresRepositories", effective.getRepositories()); |
| }, true); |
| |
| bundleContext.ungetService(reference); |
| } |
| |
| |
| } |
| |
| } |