All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sealights.onpremise.agents.plugin.surefire.PluginsHandler Maven / Gradle / Ivy

The newest version!
package io.sealights.onpremise.agents.plugin.surefire;

import static io.sealights.onpremise.agents.infra.constants.Constants.SPACE;
import static io.sealights.onpremise.agents.plugin.jacoco.JacocoPluginHandler.JACOCO_ARTIFACT_ID;
import static io.sealights.onpremise.agents.plugin.surefire.PluginCfgValidator.SUREFIRE_OR_FAILSAFE_NOT_FOUND;
import static io.sealights.onpremise.agents.plugin.surefire.PluginConfiguration.ARG_LINE;
import static io.sealights.onpremise.agents.plugin.surefire.PluginConfiguration.FAILSAFE_ARTIFACT_ID;
import static io.sealights.onpremise.agents.plugin.surefire.PluginConfiguration.SUREFIRE_ARTIFACT_ID;

import io.sealights.onpremise.agents.plugin.LogUtil;
import io.sealights.onpremise.agents.plugin.jacoco.JacocoPluginConfiguration;
import io.sealights.onpremise.agents.plugin.jacoco.JacocoPluginHandler;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import java.util.Properties;
import lombok.Getter;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;

import io.sealights.onpremise.agents.infra.constants.Constants;
import io.sealights.onpremise.agents.infra.constants.SLProperties;
import io.sealights.onpremise.agents.infra.utils.StringUtils;
import io.sealights.onpremise.agents.plugin.SeaLightsMojo;
import io.sealights.plugins.engine.lifecycle.BuildLifeCycle;
import lombok.Setter;
import lombok.experimental.UtilityClass;
import org.slf4j.Logger;

/**
 * Encapsulates surefire/failsafe configuration discovery and handling
 * 
 * @author AlaSchneider
 *
 */
public class PluginsHandler {
	public static final String SL_TEST_LISTENER = "sl-test-listener";
	public static final String JAVA_TEST_LISTENER = "java-test-listener";
	public static final String DEV_SL_TEST_LISTENER = "java-agent-bootstrapper-1.0.0-SNAPSHOT";
	public final static String SEALIGHTS_ARG_LINE = "sealightsArgLine";
	public final static String SEALIGHTS_DISABLED = SLProperties.toJvmAgr(SLProperties.ENABLED, Constants.FALSE);  
	
	@Setter
	private MavenProject project;
	@Setter
	private Logger logger;
	private boolean pluginsDiscovered;
	
	private JacocoPluginConfiguration jacocoPluginConfiguration;
	@Getter // ugly - for testing
	private PluginCfgValidator surefireValidator;
	@Getter // ugly - for testing
	private PluginCfgValidator failsafeValidator;
	@Getter // ugly - for testing
	private Properties projectProperties;
	private String initialArgLinePropertyValue;
	
	public PluginsHandler(MavenProject project, Logger logger) {
		setProject(project);
		setLogger(logger);
		project.getPlugin("");
		surefireValidator = PluginCfgValidator.createSurefireCfgValidator();
		failsafeValidator = PluginCfgValidator.createFailsafeCfgValidator();
		pluginsDiscovered = false;
		projectProperties = project.getProperties();
	}
	
	public void detectConfigurations() {
		if (pluginsDiscovered) {
			return;
		}
		
		lookForPlugins(project.getBuildPlugins());
		if (!bothPluginsFound() && project.getPluginManagement() != null) {
			// If surefire/failsafe are not defined in build plugins, try the PluginManagement
			lookForPlugins(project.getPluginManagement().getPlugins());
		}
		pluginsDiscovered = true;
		logger.info("Done discovery of surefire and failsafe plugins: surefire {}, failsafe {}", 
				buildFoundString(isSurefireExists()), buildFoundString(isFailsafeExists()));
	}
	
	public void updateProperties(String slArgument, String token, boolean withSealights) {
		logger.info("Updating the properties: '{}', '{}'", LogUtil.maskToken(slArgument), withSealights);
		notifyConfiguration();
		handleArgLineProperty(slArgument, withSealights);
		projectProperties.setProperty(SEALIGHTS_ARG_LINE, slArgument);
		PluginCfgLogger.logPluginSettings(
				logger, 
				project.getProperties(), 
				surefireValidator.getConfiguration(), 
				failsafeValidator.getConfiguration(),
				token, 
				withSealights);
	}
	
	public boolean isSurefireExists() {
		return surefireValidator.isPluginExists();
	}
	
	public boolean isFailsafeExists() {
		return failsafeValidator.isPluginExists();
	}
	
	public boolean isValidConfiguration() {
		return surefireValidator.isValidConfiguration() && failsafeValidator.isValidConfiguration();
	}
	
	public void notifyConfiguration() {
		StringBuilder notifMessageBuilder = new StringBuilder();
		notifMessageBuilder.append("module:").append(project.getName()).append(", ");
		boolean surefireNotified = logAndNotifyPluginConfiguration(surefireValidator, notifMessageBuilder, false);
		boolean failsafeNotified = logAndNotifyPluginConfiguration(failsafeValidator, notifMessageBuilder, surefireNotified);
		if (!SeaLightsMojo.POM_PACKAGING_VALUE.equals(project.getPackaging()) && !surefireNotified && !failsafeNotified) { 
			BuildLifeCycle.notifyWarning(SUREFIRE_OR_FAILSAFE_NOT_FOUND);
			logger.warn(SUREFIRE_OR_FAILSAFE_NOT_FOUND);
		}
		else {		
			BuildLifeCycle.notifyInfo(notifMessageBuilder.toString());
		}
	}
	
	String getSurefireArgLine() {
		return surefireValidator.getConfiguration().getArgLine();
	}
	
	String getFailsafeArgLine() {
		return failsafeValidator.getConfiguration().getArgLine();
	}
	
	private void lookForPlugins(List plugins) {
		Plugin surefirePlugin = null;
		Plugin failsafePlugin = null;
		Plugin jacocoPlugin = null;
		for (Plugin plugin : plugins) {
			if (SUREFIRE_ARTIFACT_ID.equals(plugin.getArtifactId()) && !surefireValidator.isPluginExists()) {
				surefirePlugin = plugin;
			}
			if (FAILSAFE_ARTIFACT_ID.equals(plugin.getArtifactId()) && !failsafeValidator.isPluginExists()) {
				failsafePlugin = plugin;
			}
			
			if(JACOCO_ARTIFACT_ID.equals(plugin.getArtifactId())){
				jacocoPlugin = plugin;
			}
		}
		if (jacocoPlugin != null) {
			jacocoPluginConfiguration = JacocoPluginHandler.parse(jacocoPlugin);
		}
		if (surefirePlugin != null) {
			surefireValidator.fillAndValidateConfiguration(surefirePlugin, jacocoPluginConfiguration);
		}
		if (failsafePlugin != null) {
			failsafeValidator.fillAndValidateConfiguration(failsafePlugin, jacocoPluginConfiguration);
		}
	}
	
	private boolean bothPluginsFound() {
		return isSurefireExists() && isFailsafeExists();
	}
	
    private boolean logAndNotifyPluginConfiguration(PluginCfgValidator validator, StringBuilder builder, boolean nextEntry) {
    	if (!validator.isPluginExists()) {
    		return false;
    	}
    	PluginCfgLogger.logValidationResults(logger, 
    			validator.getConfiguration().getArtifactId(), 
    			validator.getValidationResult());
    	if (nextEntry) {
    		builder.append(", ");
    	}
		builder.append(validator.getConfiguration().toString());
		String validationResult = validator.getValidationResult().asString();
		if (validationResult != null) {
			builder.append(", validation result: ").append(validationResult);
		}
		return true;
    }
    
	private void handleArgLineProperty(String slArgument, boolean onSuccess) {
		Properties projectProperties = project.getProperties();
		initialArgLinePropertyValue = projectProperties.getProperty(ARG_LINE);
		setArgLineToEmptyIfNotInitialized(projectProperties);
		if (onSuccess || StringUtils.isNotEmpty(initialArgLinePropertyValue)) {
			// On failure, only not-null original argLine property should be updated
			// On success relevance of argLine update is detected by surefireConfigHandler
			String newArgLineProperty = resolveNewArgLineProperty(initialArgLinePropertyValue, slArgument);
			if (newArgLineProperty != null) {
				logger.info("argLine property set to {}", LogUtil.maskToken(newArgLineProperty));
				projectProperties.setProperty(ARG_LINE, newArgLineProperty);
			}
		}
	}

	private void setArgLineToEmptyIfNotInitialized(Properties projectProperties) {
		PluginConfiguration surefireValidatorConfiguration = surefireValidator.getConfiguration();
		PluginConfiguration failsafeValidatorConfiguration = failsafeValidator.getConfiguration();
		if (initialArgLinePropertyValue == null && (surefireValidatorConfiguration.hasArgLinePlaceholder() || failsafeValidatorConfiguration.hasArgLinePlaceholder())) {
			projectProperties.setProperty(ARG_LINE, "");
		}
	}

	/**
     * Builds a new argLine property value, if it is relevant.
     * Returns null, if argLine is not relevant
     */
    private String resolveNewArgLineProperty(String origArgLine, String slArgument) {
		logger.info("Original  is: {} and slArgument {}", LogUtil.maskToken(origArgLine), LogUtil.maskToken(slArgument));
		if (surefireValidator.isArgLinePropertyRelevant() || failsafeValidator.isArgLinePropertyRelevant()) {
			String modifiedArgLine;
			if (StringUtils.isNotEmpty(origArgLine)) {
				logger.info("The '{}' property is already set to:'{}'", ARG_LINE, origArgLine);				
				modifiedArgLine = buildArgLine(origArgLine, slArgument);
			} else {
				logger.info("The modified : '{}'", LogUtil.maskToken(slArgument));
				modifiedArgLine = slArgument;
			}
			return modifiedArgLine;
		} else {
			if (StringUtils.isNotEmpty(origArgLine)) {
				//Not removed just commented
				//Because it prints warn when surefire.configuration.argLine contains ${argLine}.
				//but looks like in this case ${argLine} will be appended and not ignored
//				logger.warn("The defined property '{}={}' will be ignored by {}", ARG_LINE, origArgLine, SUREFIRE_ARTIFACT_ID);
			}
			logger.info("No  modifications will be done");
			return null;
		}
    }
    
    private String buildArgLine(String origArgLine, String slArgument) {
        String unduplicatedArgline = ArgLineNormalizer.removeArgLineDuplication(origArgLine);
        warnForOtherJavaAgents(unduplicatedArgline);

        StringBuilder newArgLine = new StringBuilder(slArgument);
        if (!StringUtils.isNullOrEmpty(unduplicatedArgline)) {
            newArgLine.append(SPACE);
            newArgLine.append(unduplicatedArgline);
        }

        return newArgLine.toString();
    }

    private void warnForOtherJavaAgents(String argLineVariable) {
    	if (argLineVariable != null && argLineVariable.contains(" -javaagent:")) {
    		PluginCfgLogger.logImportantInfo(logger, "Warning", 
    				"Another java agent is running with SeaLights.",
    				"This may interfere with the test-listener agent and cause the low code coverage.");
    	}
    }
    
	private String buildFoundString(boolean isFound) {
		return isFound ? "found" : "not found";
	}
	
	@UtilityClass
	static class ArgLineNormalizer {

		static int IGNORE_WHITE_SPACES = 0;
		static int IN_QUOTES = 1;
		static int AFTER_QUOTES = 2;
		static int IN_SEQUENCE = 3;
		static int ESCAPED = 4;

		public static String removeArgLineDuplication(String oldArgLine) {
			List args = toArgsArray(oldArgLine);
			return deleteSeaLightsLeftovers(args);
		}

		private static String deleteSeaLightsLeftovers(List args) {
			for (Iterator iterator = args.iterator(); iterator.hasNext();) {
				String argument = iterator.next();
				if (isSeaLightsProperty(argument) || isSeaLightsTestListener(argument)) {
					iterator.remove();
				}
			}
			String ret = StringUtils.join(args, ' ');
			return ret;
		}

		private static List toArgsArray(String argsAsString) {
			List args = new ArrayList<>();
			int state = 0;
			StringBuilder currentArg = new StringBuilder();

			for (char c : argsAsString.toCharArray()) {
				if (state == IGNORE_WHITE_SPACES) {
					if (!Character.isWhitespace(c)) {
						currentArg.append(c);
						if (isQuote(c)) {
							state = IN_QUOTES;
						} 
						else {
							state = IN_SEQUENCE;
						}
					}
				}

				else if (state == IN_QUOTES) {
					currentArg.append(c);
					if (isQuote(c)) {
						state = AFTER_QUOTES;
					}
				}

				else if (state == AFTER_QUOTES) {
					if (!Character.isWhitespace(c)) {
						currentArg.append(c);
						if (isQuote(c)) {
							state = IN_QUOTES;
						} 
						else {
							state = IN_SEQUENCE;
						}
					} 
					else {
						args.add(currentArg.toString());
						currentArg.setLength(0);
						state = IGNORE_WHITE_SPACES;
					}
				}

				else if (state == IN_SEQUENCE){
					if (!Character.isWhitespace(c)) {
						currentArg.append(c);
						if (isQuote(c)) {
							state = IN_QUOTES;
						}
					} else {
						args.add(currentArg.toString());
						currentArg.setLength(0);
						state = IGNORE_WHITE_SPACES;
					}
				}
			}
			args.add(currentArg.toString());
			return args;
		}

		private static boolean isQuote(char ch) {
			return ch == '"' || ch == '\'';
		}

		private static boolean isSeaLightsProperty(String argument){
			return argument.startsWith("-D" + SLProperties.PREFIX);
		}

		private static boolean isSeaLightsTestListener(String argument){
			return (argument.startsWith("-javaagent:") && ( isValidTestListenerName(argument)));
		}

		private static boolean isValidTestListenerName(String testListenerName) {
			return testListenerName != null && (
					testListenerName.contains(SL_TEST_LISTENER) ||
					testListenerName.contains(JAVA_TEST_LISTENER) ||
					testListenerName.contains(DEV_SL_TEST_LISTENER)
					);
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy