/* * 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.geode.internal.process; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.internal.io.TeeOutputStream; import org.apache.geode.internal.io.TeePrintStream; import java.io.*; import java.util.Properties; /** * Thread based context for launching a process. GemFire internals can acquire optional * configuration details from a process launcher via this context. * * @since GemFire 7.0 */ public final class ProcessLauncherContext { public static final String OVERRIDDEN_DEFAULTS_PREFIX = DistributionConfig.GEMFIRE_PREFIX + "default."; /** * Default value for {@link #isRedirectingOutput()} */ private static final boolean REDIRECT_OUTPUT_DEFAULT = false; /** * Default value for {@link #getOverriddenDefaults()} */ private static final Properties OVERRIDDEN_DEFAULTS_DEFAULT = new Properties(); private static final ThreadLocal<ProcessLauncherContext> DATA = new ThreadLocal<ProcessLauncherContext>(); private static ProcessLauncherContext get() { return DATA.get(); } /** * Returns true if this process should redirect output to the system log. * * @return true if this process should redirect output to the system log */ public static boolean isRedirectingOutput() { final ProcessLauncherContext context = get(); if (context == null) { return REDIRECT_OUTPUT_DEFAULT; } return context.redirectOutput(); } /** * Returns the gemfire properties to be used if none of the specified properties are defined by * any other mechanism. This will only override default values. If a property is defined by System * property, API, or within gemfire.properties then the contingent value will be ignored. * * @return the contingent gemfire properties values to be used as an alternative default value */ public static Properties getOverriddenDefaults() { final ProcessLauncherContext context = get(); if (context == null) { return OVERRIDDEN_DEFAULTS_DEFAULT; } return context.overriddenDefaults(); } public static StartupStatusListener getStartupListener() { final ProcessLauncherContext context = get(); if (context == null) { return null; } return context.startupListener(); } /** * Sets the ProcessLauncherContext data for the calling thread. */ public static void set(final boolean redirectOutput, final Properties contingentProperties, final StartupStatusListener startupListener) { DATA.set(new ProcessLauncherContext(redirectOutput, contingentProperties, startupListener)); installLogListener(startupListener); } /** * Clears the current ProcessLauncherContext for the calling thread. */ public static void remove() { // DATA.get().restoreErrorStream(); DATA.remove(); clearLogListener(); } private static void installLogListener(StartupStatusListener startupListener) { if (startupListener != null) { StartupStatus.setListener(startupListener); } } private static void clearLogListener() { StartupStatus.clearListener(); } private final boolean redirectOutput; private final Properties overriddenDefaults; private final StartupStatusListener startupListener; private PrintStream err; private ProcessLauncherContext(final boolean redirectOutput, final Properties overriddenDefaults, final StartupStatusListener startupListener) { this.redirectOutput = redirectOutput; this.overriddenDefaults = overriddenDefaults; this.startupListener = startupListener; } private boolean redirectOutput() { return this.redirectOutput; } private Properties overriddenDefaults() { return this.overriddenDefaults; } private StartupStatusListener startupListener() { return this.startupListener; } @SuppressWarnings("unused") private void teeErrorStream() { final FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err); this.err = new PrintStream(new BufferedOutputStream(fdErr, 128), true); System.setErr(new TeePrintStream(new TeeOutputStream(new BufferedOutputStream(fdErr, 128)))); } @SuppressWarnings("unused") private void restoreErrorStream() { if (System.err instanceof TeePrintStream) { final TeePrintStream tee = ((TeePrintStream) System.err); final OutputStream branch = tee.getTeeOutputStream().getBranchOutputStream(); PrintStream newStdErr = null; if (branch == null) { newStdErr = this.err; } else if (branch instanceof PrintStream) { newStdErr = (PrintStream) branch; } else { newStdErr = new PrintStream(new BufferedOutputStream(branch, 128), true); } System.setErr(newStdErr); } } }