/* * 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.openejb.maven.plugin.runner; import org.apache.openejb.config.RemoteServer; import org.apache.openejb.loader.Files; import org.apache.openejb.loader.IO; import org.apache.openejb.loader.Zips; import org.apache.tomee.util.QuickServerXmlParser; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import static java.util.Arrays.asList; public class ExecRunner { private static final String SH_BAT_AUTO = "[.sh|.bat]"; public static void main(final String[] rawArgs) throws Exception { final String[] args; if (rawArgs == null || rawArgs.length == 0) { args = new String[]{"run"}; } else { args = rawArgs; } final Properties config = new Properties(); final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); final InputStream is = contextClassLoader.getResourceAsStream("configuration.properties"); if (is != null) { config.load(new InputStreamReader(is, "UTF-8")); is.close(); } else { throw new IllegalArgumentException("Config not found"); } final String distrib = config.getProperty("distribution"); final String workingDir = config.getProperty("workingDir"); final InputStream distribIs = contextClassLoader.getResourceAsStream(distrib); final File distribOutput = new File(workingDir); final File timestampFile = new File(distribOutput, "timestamp.txt"); final boolean forceDelete = Boolean.getBoolean("tomee.runner.force-delete"); if (forceDelete || !timestampFile.exists() || Long.parseLong(IO.slurp(timestampFile).replace(System.getProperty("line.separator"), "")) < Long.parseLong(config.getProperty("timestamp"))) { if (forceDelete || timestampFile.exists()) { System.out.println("Deleting " + distribOutput.getAbsolutePath()); Files.delete(distribOutput); } System.out.println("Extracting tomee to " + distribOutput.getAbsolutePath()); Zips.unzip(distribIs, distribOutput, false); IO.writeString(timestampFile, config.getProperty("timestamp", Long.toString(System.currentTimeMillis()))); } final File[] scripts = new File(distribOutput, "bin").listFiles(); if (scripts != null) { // dont use filefilter to avoid dependency issue for (final File f : scripts) { setExecutable(f); } } String cmd = config.getProperty("command"); if (cmd.endsWith(SH_BAT_AUTO)) { final int lastSlash = cmd.lastIndexOf('/'); if (lastSlash > 0) { final String dir = cmd.substring(0, lastSlash); final boolean isWin = System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win"); final String script = cmd.substring(lastSlash + 1, cmd.length() - SH_BAT_AUTO.length()).replace('/', isWin ? '\\' : '/') + (isWin ? ".bat" : ".sh"); final File scriptFile = new File(distribOutput, dir + File.separator + script); if (!scriptFile.exists()) { throw new IllegalArgumentException("Can't find " + cmd); } cmd = scriptFile.getAbsolutePath(); setExecutable(scriptFile); // in case it is not in bin/ } } final String additionalArgs = System.getProperty("additionalSystemProperties"); // build also post here to avoid surprises final Map<?, ?> map = new HashMap<>(); final Collection<Runnable> preTasks = buildRunnables(config.getProperty("preTasks"), map); final Collection<Runnable> postTasks = buildRunnables(config.getProperty("postTasks"), map); final boolean doWait = Boolean.parseBoolean(config.getProperty("waitFor")); if (!doWait && !postTasks.isEmpty()) { throw new IllegalArgumentException("You can't use post task if you dont wait for the process."); } for (final Runnable r : preTasks) { r.run(); } try { final Collection<String> params = new ArrayList<>(); if ("java".equals(cmd)) { final File base = findBase(distribOutput); final QuickServerXmlParser parser = QuickServerXmlParser.parse(new File(base, "conf/server.xml")); System.setProperty("openejb.home", base.getAbsolutePath()); System.setProperty("server.shutdown.port", parser.stop()); System.setProperty("server.shutdown.command", config.getProperty("shutdownCommand")); final RemoteServer server = new RemoteServer(); server.setPortStartup(Integer.parseInt(parser.http())); if (config.containsKey("additionalClasspath")) { server.setAdditionalClasspath(config.getProperty("additionalClasspath")); } final List<String> jvmArgs = new LinkedList<String>(); if (additionalArgs != null) { Collections.addAll(jvmArgs, additionalArgs.split(" ")); } for (final String k : config.stringPropertyNames()) { if (k.startsWith("jvmArg.")) { jvmArgs.add(config.getProperty(k)); } } final String userProps = String.class.cast(map.get("jvmArgs")); if (userProps != null) { Collections.addAll(jvmArgs, userProps.split(" ")); } if ("run".equals(args[0])) { args[0] = "start"; } try { server.start(jvmArgs, args[0], true); } catch (final Exception e) { server.destroy(); throw e; } if (doWait) { server.getServer().waitFor(); } } else { // TODO: split cmd correctly to support multiple inlined segments in cmd if (cmd.endsWith(".bat") && !cmd.startsWith("cmd.exe")) { params.add("cmd.exe"); params.add("/c"); } // else suppose the user knows what he does params.add(cmd); params.addAll(asList(args)); final ProcessBuilder builder = new ProcessBuilder(params.toArray(new String[params.size()])) .inheritIO() .directory(findBase(distribOutput)); final String existingOpts = System.getenv("CATALINA_OPTS"); final String catalinaOpts = config.getProperty("catalinaOpts"); if (catalinaOpts != null || existingOpts != null || additionalArgs != null) { // inherit from existing env builder.environment() .put("CATALINA_OPTS", identityOrEmpty(catalinaOpts) + " " + identityOrEmpty(existingOpts) + " " + identityOrEmpty(additionalArgs) + " " + identityOrEmpty(String.class.cast(map.get("jvmArgs")))); } if (doWait) { builder.start().waitFor(); } } System.out.flush(); System.err.flush(); System.out.println("Exited Successfully!"); } finally { for (final Runnable r : postTasks) { r.run(); } } } private static Collection<Runnable> buildRunnables(final String classes, final Map<?, ?> state) { final Collection<Runnable> tasks = new ArrayList<>(); if (classes == null || classes.trim().isEmpty()) { return tasks; } for (final String className : classes.split(" *, *")) { try { final Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(className); try { final Constructor<?> cons = clazz.getConstructor(Map.class); tasks.add(Runnable.class.cast(cons.newInstance(state))); } catch (final Throwable th) { tasks.add(Runnable.class.cast(clazz.newInstance())); } } catch (final Exception e) { throw new IllegalArgumentException(e); } } return tasks; } private static void setExecutable(final File f) { if (f.getName().endsWith(".sh") && !f.canExecute()) { if (!f.setExecutable(true, true)) { System.err.println("Failed make file executable: " + f); } } } private static File findBase(final File distribOutput) { final File[] extracted = distribOutput.listFiles(); if (extracted != null) { File newRoot = null; for (final File e : extracted) { if (e.isDirectory()) { if (newRoot == null) { newRoot = e; } else { newRoot = null; break; } } } if (newRoot != null) { return newRoot; } } return distribOutput; } private static String identityOrEmpty(final String value) { return (value != null ? value : ""); } private ExecRunner() { // no-op } }