/* * Licensed 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.ngrinder.infra; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.Context; import ch.qos.logback.core.joran.spi.JoranException; import net.grinder.util.NetworkUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.ngrinder.common.constants.AgentConstants; import org.ngrinder.common.constants.CommonConstants; import org.ngrinder.common.constants.MonitorConstants; import org.ngrinder.common.util.PropertiesKeyMapper; import org.ngrinder.common.util.PropertiesWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import java.util.Set; import static net.grinder.util.NetworkUtils.DEFAULT_LOCAL_HOST_ADDRESS; import static org.apache.commons.lang.StringUtils.trimToEmpty; import static org.ngrinder.common.util.ExceptionUtils.processException; import static org.ngrinder.common.util.NoOp.noOp; import static org.ngrinder.common.util.Preconditions.checkNotNull; /** * Spring component which is responsible to get the nGrinder config which is stored * ${NGRINDER_AGENT_HOME}. * * @author JunHo Yoon * @since 3.0 */ public class AgentConfig implements AgentConstants, MonitorConstants, CommonConstants { private static final String NGRINDER_DEFAULT_FOLDER = ".ngrinder_agent"; private static final Logger LOGGER = LoggerFactory.getLogger("agent config"); protected AgentHome home = null; private PropertiesWrapper agentProperties; private PropertiesWrapper monitorProperties; private PropertiesWrapper commonProperties; private PropertiesWrapper internalProperties; private PropertiesKeyMapper internalPropertyMapper = PropertiesKeyMapper.create("internal-properties.map"); private PropertiesKeyMapper agentPropertyMapper = PropertiesKeyMapper.create("agent-properties.map"); private PropertiesKeyMapper monitorPropertyMapper = PropertiesKeyMapper.create("monitor-properties.map"); private PropertiesKeyMapper commonPropertyMapper = PropertiesKeyMapper.create("common-properties.map"); /** * Initialize. * * @return initialized AgentConfig */ public AgentConfig init() { home = resolveHome(); copyDefaultConfigurationFiles(); loadProperties(); loadInternalProperties(); configureLogging(); return this; } private void copyDefaultConfigurationFiles() { checkNotNull(home); final File agentConfig = home.getFile("agent.conf"); File newAgentConfig = new File(getCurrentDirectory(), "__agent.conf"); if (agentConfig.exists()) { if (System.getProperty(CommonConstants.PROP_OVERWRITE_CONFIG) != null) { LOGGER.info("Overwrite the existing agent.conf with __agent.conf"); } else if (newAgentConfig.exists()) { LOGGER.warn("The agent configuration file '{}' already exists.", agentConfig.getAbsolutePath()); LOGGER.warn("If you want to use the '{}' file", newAgentConfig.getAbsolutePath()); LOGGER.warn("Please run agent with -o option"); return; } } if (newAgentConfig.exists()) { home.copyFileTo(newAgentConfig, "agent.conf"); agentConfig.setLastModified(newAgentConfig.lastModified()); } else { try { home.writeFileTo(loadResource("/agent.conf"), "agent.conf"); } catch (IOException e) { throw processException(e); } } } protected void loadProperties() { checkNotNull(home); Properties properties = home.getProperties("agent.conf"); properties.put("NGRINDER_AGENT_HOME", home.getDirectory().getAbsolutePath()); properties.putAll(System.getProperties()); agentProperties = new PropertiesWrapper(properties, agentPropertyMapper); monitorProperties = new PropertiesWrapper(properties, monitorPropertyMapper); commonProperties = new PropertiesWrapper(properties, commonPropertyMapper); } protected void loadInternalProperties() { InputStream inputStream = null; Properties properties = new Properties(); try { inputStream = AgentConfig.class.getResourceAsStream("/internal.properties"); properties.load(inputStream); internalProperties = new PropertiesWrapper(properties, internalPropertyMapper); } catch (IOException e) { LOGGER.error("Error while load internal.properties", e); internalProperties = new PropertiesWrapper(properties, internalPropertyMapper); } finally { IOUtils.closeQuietly(inputStream); } } public String loadResource(String name) throws IOException { InputStream inputStream = null; try { inputStream = AgentConfig.class.getResourceAsStream(name); if (inputStream != null) { return IOUtils.toString(inputStream); } else { return ""; } } finally { IOUtils.closeQuietly(inputStream); } } private void configureLogging() { File logDirectory = getHome().getLogDirectory(); String level = "INFO"; if (getCommonProperties().getPropertyBoolean(PROP_COMMON_VERBOSE)) { level = "TRACE"; } if (isSilentMode()) { level = "ERROR"; } final Context context = (Context) LoggerFactory.getILoggerFactory(); final JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(context); context.putProperty("LOG_LEVEL", level); context.putProperty("LOG_DIRECTORY", logDirectory.getAbsolutePath()); try { configurator.doConfigure(getClass().getResource("/logback-agent.xml")); } catch (JoranException e) { LOGGER.error("Error while configuring logger", e); } } /** * Save the agent pid. * * @param agentPid agent pid * @param startMode startMode */ public void saveAgentPidProperties(String agentPid, String startMode) { checkNotNull(home); Properties properties = home.getProperties("pid"); if ("agent".equalsIgnoreCase(startMode)) { properties.put("agent.pid", agentPid); } else { properties.put("monitor.pid", agentPid); } home.saveProperties("pid", properties); } /** * Update agent pid file. * * @param startMode startMode */ public void updateAgentPidProperties(String startMode) { checkNotNull(home); Properties properties = home.getProperties("pid"); Set<String> names = properties.stringPropertyNames(); if (names.size() > 1) { properties.remove(startMode + ".pid"); home.saveProperties("pid", properties); } else if (names.contains(startMode + ".pid")) { removeAgentPidProperties(); } } /** * Get the agent pid in the form of string. * * @param startMode agent or monitor * @return pid */ public String getAgentPidProperties(String startMode) { checkNotNull(home); Properties properties = home.getProperties("pid"); if ("agent".equalsIgnoreCase(startMode)) { return (String) properties.get("agent.pid"); } else { return (String) properties.get("monitor.pid"); } } /** * Remove agent pid properties. */ public void removeAgentPidProperties() { checkNotNull(home); File file = home.getFile("pid"); FileUtils.deleteQuietly(file); } /** * Resolve NGrinder agent home path. * * @return resolved {@link AgentHome} */ protected AgentHome resolveHome() { String userHomeFromEnv = trimToEmpty(System.getenv("NGRINDER_AGENT_HOME")); //printLog(" System Environment: NGRINDER_AGENT_HOME={}", userHomeFromEnv); String userHomeFromProperty = trimToEmpty(System.getProperty("ngrinder.agent.home")); //printLog(" Java System Property: ngrinder.agent.home={}", userHomeFromEnv); if (StringUtils.isNotEmpty(userHomeFromEnv) && !StringUtils.equals(userHomeFromEnv, userHomeFromProperty)) { printLog("The path to ngrinder agent home is ambiguous:"); printLog(" '{}' is accepted.", userHomeFromProperty); } String userHome = StringUtils.defaultIfEmpty(userHomeFromProperty, userHomeFromEnv); if (StringUtils.isEmpty(userHome)) { userHome = System.getProperty("user.home") + File.separator + NGRINDER_DEFAULT_FOLDER; } else if (StringUtils.startsWith(userHome, "~" + File.separator)) { userHome = System.getProperty("user.home") + File.separator + userHome.substring(2); } else if (StringUtils.startsWith(userHome, "." + File.separator)) { userHome = System.getProperty("user.dir") + File.separator + userHome.substring(2); } userHome = FilenameUtils.normalize(userHome); printLog("NGRINDER_AGENT_HOME : {}", userHome); File homeDirectory = new File(userHome); try { if (homeDirectory.mkdirs()) { noOp(); } if (!homeDirectory.canWrite()) { throw processException("home directory " + userHome + " is not writable."); } } catch (Exception e) { throw processException("Error while resolve the home directory.", e); } return new AgentHome(homeDirectory); } private void printLog(String template, Object... var) { if (!isSilentMode()) { LOGGER.info(template, var); } } /** * If there is test mode property in system.properties.. return true * * @return true is test mode */ public boolean isDevMode() { return getCommonProperties().getPropertyBoolean(PROP_COMMON_DEV_MODE); } public AgentHome getHome() { return this.home; } /** * Get agent properties. * * @return agent properties */ public PropertiesWrapper getAgentProperties() { return checkNotNull(agentProperties); } /** * Get monitor properties. * * @return monitor properties */ public PropertiesWrapper getMonitorProperties() { return checkNotNull(monitorProperties); } /** * Get internal properties. * * @return internalProperties */ public PropertiesWrapper getInternalProperties() { return internalProperties; } public File getCurrentDirectory() { return new File(System.getProperty("user.dir")); } public String getMonitorBindingIP() { return getMonitorProperties().getProperty(PROP_MONITOR_BINDING_IP); } public String getControllerIP() { return getAgentProperties().getProperty(PROP_AGENT_CONTROLLER_HOST, DEFAULT_LOCAL_HOST_ADDRESS); } public void setControllerHost(String host) { getAgentProperties().addProperty(PROP_AGENT_CONTROLLER_HOST, host); } public int getControllerPort() { return getAgentProperties().getPropertyInt(PROP_AGENT_CONTROLLER_PORT); } public String getRegion() { return getAgentProperties().getProperty(PROP_AGENT_REGION); } public String getAgentHostID() { return getAgentProperties().getProperty(PROP_AGENT_HOST_ID, NetworkUtils.DEFAULT_LOCAL_HOST_NAME); } public boolean isServerMode() { return getAgentProperties().getPropertyBoolean(PROP_AGENT_SERVER_MODE); } public boolean isSilentMode() { final PropertiesWrapper properties = getCommonProperties(); if (properties == null) { return Boolean.getBoolean(System.getProperty(PROP_COMMON_SILENT_MODE, "false")); } else { return properties.getPropertyBoolean(PROP_COMMON_SILENT_MODE); } } public PropertiesWrapper getCommonProperties() { return commonProperties; } public static class NullAgentConfig extends AgentConfig { public int counter = 0; private int controllerPort = 0; public NullAgentConfig(int i) { counter = i; home = resolveHome(); loadProperties(); loadInternalProperties(); } public int getControllerPort() { return (this.controllerPort == 0) ? super.getControllerPort() : this.controllerPort; } public void setControllerPort(int controllerPort) { this.controllerPort = controllerPort; } public boolean isSilentMode() { return true; } @Override protected AgentHome resolveHome() { AgentHome resolveHome = super.resolveHome(); File directory = new File(resolveHome.getDirectory(), "tmp_" + String.valueOf(counter)); resolveHome = new AgentHome(directory); try { FileUtils.forceDeleteOnExit(directory); } catch (IOException e) { noOp(); } return resolveHome; } } }