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

io.openliberty.tools.maven.server.StartDebugMojoSupport Maven / Gradle / Ivy

Go to download

Liberty Maven Plugin : Install, Start/Stop, Package, Create Server, Deploy/Undeploy applications

There is a newer version: 3.11.1
Show newest version
/**
 * (C) Copyright IBM Corporation 2014, 2024.
 *
 * 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 io.openliberty.tools.maven.server;

import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration;
import static org.twdata.maven.mojoexecutor.MojoExecutor.element;
import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo;
import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment;
import static org.twdata.maven.mojoexecutor.MojoExecutor.goal;
import static org.twdata.maven.mojoexecutor.MojoExecutor.name;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.versioning.ComparableVersion;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.tools.ant.taskdefs.Copy;
import org.apache.tools.ant.types.FileSet;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.twdata.maven.mojoexecutor.MojoExecutor.Element;

import io.openliberty.tools.ant.ServerTask;
import io.openliberty.tools.common.plugins.config.ServerConfigXmlDocument;
import io.openliberty.tools.maven.ServerFeatureSupport;
import io.openliberty.tools.maven.applications.LooseWarApplication;
import io.openliberty.tools.maven.utils.ExecuteMojoUtil;

/**
 * Start/Debug server support.
 */
public abstract class StartDebugMojoSupport extends ServerFeatureSupport {

    protected static final String HEADER = "# Generated by liberty-maven-plugin";
    private static final String LIBERTY_CONFIG_MAVEN_PROPS = "(^liberty\\.(env|jvm|bootstrap|var|defaultVar)\\.).+";
    private static final Pattern pattern = Pattern.compile(LIBERTY_CONFIG_MAVEN_PROPS);
    private static final String LATE_PROP_RESOLUTION_SYNTAX = "@\\{(.+?)\\}";
    private static final Pattern LATE_PROP_PATTERN = Pattern.compile(LATE_PROP_RESOLUTION_SYNTAX);

    private boolean configFilesCopied = false;

    protected final String PLUGIN_VARIABLE_CONFIG_OVERRIDES_XML = "configDropins/overrides/liberty-plugin-variable-config.xml";
    protected final String PLUGIN_VARIABLE_CONFIG_DEFAULTS_XML = "configDropins/defaults/liberty-plugin-variable-config.xml";

    protected Map bootstrapMavenProps = new HashMap();  
    protected Map envMavenProps = new HashMap();  
    protected List jvmMavenPropNames = new ArrayList();  // only used for tracking overriding properties - not included in the generated jvm.options file
    protected List jvmMavenPropValues = new ArrayList();  
    protected Map varMavenProps = new HashMap();  
    protected Map defaultVarMavenProps = new HashMap();  

    protected Map combinedBootstrapProperties = null;
    protected List combinedJvmOptions = null;
    
    // the following collections are copies of the originals with any @{xxx} references resolved in the values
    protected Map bootstrapPropertiesResolved = null; // original collection is bootstrapProperties
    protected List jvmOptionsResolved = null; // original collection is jvmOptions

    @Component
    protected BuildPluginManager pluginManager;

    /* 
     * Define a set of dependencies to copy to the target Liberty server.
     */
    @Parameter
    protected CopyDependencies copyDependencies;

    /**
     * Location of bootstrap.properties file.
     */
    @Parameter(property = "bootstrapPropertiesFile")
    protected File bootstrapPropertiesFile;

    @Parameter
    protected Map bootstrapProperties;

    /**
     * Location of jvm.options file.
     */
    @Parameter(property = "jvmOptionsFile")
    protected File jvmOptionsFile;

    @Parameter
    protected List jvmOptions;

    private enum PropertyType {
        BOOTSTRAP("liberty.bootstrap."),
        ENV("liberty.env."),
        JVM("liberty.jvm."),
        VAR("liberty.var."),
        DEFAULTVAR("liberty.defaultVar.");

        private final String prefix;

        private PropertyType(final String prefix) {
            this.prefix = prefix;
        }

        private static final Map lookup = new HashMap();

        static {
            for (PropertyType s : EnumSet.allOf(PropertyType.class)) {
               lookup.put(s.prefix, s);
            }
        }

        public static PropertyType getPropertyType(String propertyName) {
            // get a matcher object from pattern 
            Matcher matcher = pattern.matcher(propertyName); 
  
            // check whether Regex string is found in propertyName or not 
            if (matcher.find()) {
                // strip off the end of the property name to get the prefix
                String prefix = matcher.group(1);
                return lookup.get(prefix);
            }
            return null;
        } 

        public String getPrefix() {
            return prefix;
        }

    }

    protected ServerTask initializeJava() {
        ServerTask serverTask = (ServerTask) ant.createTask("antlib:io/openliberty/tools/ant:server");
        if (serverTask == null) {
            throw new IllegalStateException(MessageFormat.format(messages.getString("error.dependencies.not.found"), "server"));
        }
        serverTask.setInstallDir(installDirectory);
        serverTask.setServerName(serverName);
        serverTask.setUserDir(userDirectory);
        serverTask.setOutputDir(outputDirectory);
        return serverTask;
    }
    
    protected void runMojo(String groupId, String artifactId, String goal) throws MojoExecutionException {
        Plugin plugin = getPlugin(groupId, artifactId);
        Xpp3Dom config = ExecuteMojoUtil.getPluginGoalConfig(plugin, goal, getLog());
        getLog().info("Running " + artifactId + ":" + goal);
        getLog().debug("configuration:\n" + config);
        executeMojo(plugin, goal(goal), config,
                executionEnvironment(project, session, pluginManager));
    }
    
    /**
     * Run the maven-war-plugin's exploded goal. This method should only be 
     * called for WAR type applications. 
     * 
     * @throws MojoExecutionException
     */
    protected void runExplodedMojo() throws MojoExecutionException {
        Plugin warPlugin = getPlugin("org.apache.maven.plugins", "maven-war-plugin");
        Xpp3Dom explodedConfig = ExecuteMojoUtil.getPluginGoalConfig(warPlugin, "exploded", getLog());
        
        if (explodedConfig.getChild("outdatedCheckPath") == null) {
            if (validatePluginVersion(warPlugin.getVersion(), "3.3.2")) {
                explodedConfig.addChild(element(name("outdatedCheckPath"), "/").toDom());
            } else if (validatePluginVersion(warPlugin.getVersion(), "3.3.1")) {
                explodedConfig.addChild(element(name("outdatedCheckPath"), "WEB-INF").toDom());
            }
        }

        getLog().info("Running maven-war-plugin:exploded");
        getLog().debug("configuration:\n" + explodedConfig);
        session.getRequest().setStartTime(new Date());
        executeMojo(warPlugin, goal("exploded"), explodedConfig, executionEnvironment(project, session, pluginManager));
    }

    protected void runMojoForProject(String groupId, String artifactId, String goal, MavenProject project)
            throws MojoExecutionException {
        Plugin plugin = getPluginForProject(groupId, artifactId, project);
        Xpp3Dom config = ExecuteMojoUtil.getPluginGoalConfig(plugin, goal, getLog());
        getLog().info("Running " + artifactId + ":" + goal + " on " + project.getFile());
        getLog().debug("configuration:\n" + config);
        MavenSession tempSession = session.clone();
        tempSession.setCurrentProject(project);
        executeMojo(plugin, goal(goal), config, executionEnvironment(project, tempSession, pluginManager));
    }
    
    protected boolean validatePluginVersion(String version, String minVersion) {
    	
    	ComparableVersion ver = new ComparableVersion(version);
    	ComparableVersion minVer = new ComparableVersion(minVersion);
    	
    	if (ver.compareTo(minVer) < 0) {
    		return false;
    	} else {
    		return true;
    	}
    }

    /**
     * Given the groupId and artifactId get the corresponding plugin
     * 
     * @param groupId
     * @param artifactId
     * @return Plugin
     */
    protected Plugin getPlugin(String groupId, String artifactId) {
       return getPluginForProject(groupId, artifactId, project);
    }

    protected Plugin getLibertyPlugin() {
        return getLibertyPluginForProject(project);
    }

    protected void runLibertyMojoCreate() throws MojoExecutionException {
        Xpp3Dom config = ExecuteMojoUtil.getPluginGoalConfig(getLibertyPlugin(), "create", getLog());
        runLibertyMojo("create", config);
    }

    protected void runLibertyMojoDeploy() throws MojoExecutionException {
        runLibertyMojoDeploy(true);
    }
    
    protected void runLibertyMojoDeploy(boolean forceLooseApp) throws MojoExecutionException {
        Xpp3Dom config = ExecuteMojoUtil.getPluginGoalConfig(getLibertyPlugin(), "deploy", getLog());
        if(forceLooseApp) {
            Xpp3Dom looseApp = config.getChild("looseApplication");
            if (looseApp != null && "false".equals(looseApp.getValue())) {
                getLog().warn("Overriding liberty plugin parameter, \"looseApplication\" to \"true\" and deploying application in looseApplication format");
                looseApp.setValue("true");
            }
        }
        if (project.getPackaging().equals("war")) {
            if (LooseWarApplication.isExploded(project)) {
			    runMojo("org.apache.maven.plugins", "maven-resources-plugin", "resources");
            }
        }
        runLibertyMojo("deploy", config);
    }

    protected void runLibertyMojoInstallFeature(Element features, File serverDir, String containerName) throws MojoExecutionException {
        Xpp3Dom config = ExecuteMojoUtil.getPluginGoalConfig(getLibertyPlugin(), "install-feature", getLog());
        if (features != null) {
            config = Xpp3Dom.mergeXpp3Dom(configuration(features), config);
        }
        if (containerName != null) {
            config.addChild(element(name("containerName"), containerName).toDom());
        }
        if (serverDir != null && serverDir.exists()) {
            try {
                config.addChild(element(name("serverDir"), serverDir.getCanonicalPath()).toDom());
            } catch (IOException e) {
                getLog().warn("Unable to pass 'serverDir' configuration parameter to liberty:install-feature: "
                        + serverDir);
            }
        }
        runLibertyMojo("install-feature", config);
    }

    protected void runLibertyMojoGenerateFeatures(Element classFiles, boolean optimize) throws MojoExecutionException {
        Xpp3Dom config = ExecuteMojoUtil.getPluginGoalConfig(getLibertyPlugin(), "generate-features", getLog());
        if (classFiles != null) {
            config = Xpp3Dom.mergeXpp3Dom(configuration(classFiles), config);
        }
        config.addChild(element(name("optimize"), Boolean.toString(optimize)).toDom());
        runLibertyMojo("generate-features", config);
    }

    private void runLibertyMojo(String goal, Xpp3Dom config) throws MojoExecutionException {
        getLog().info("Running liberty:" + goal);
        getLog().debug("configuration:\n" + config);
        getLog().debug("project:\n" + project);
        MavenSession tempSession = session.clone();
        tempSession.setCurrentProject(project);
        executeMojo(getLibertyPlugin(), goal(goal), config, executionEnvironment(project, tempSession, pluginManager));
    }

    private void copyDependencies() throws MojoExecutionException, IOException {
        if (copyDependencies != null) {
            List deps = copyDependencies.getDependencies();
            boolean defaultStripVersion = copyDependencies.isStripVersion();
            String defaultLocation = copyDependencies.getLocation();
            File dftLocationFile = new File(defaultLocation);

            if (!dftLocationFile.isAbsolute()) {
                // relative path
                dftLocationFile = new File(serverDirectory,defaultLocation);
            }

            if (!dftLocationFile.exists()) {
                dftLocationFile.mkdirs();
            } else if (!dftLocationFile.isDirectory()) {
                // send config error
                throw new MojoExecutionException("The copyDependencies location "+ dftLocationFile.getCanonicalPath() +" is not a directory.");
            }

            String dftLocationPath = dftLocationFile.getCanonicalPath();

            if (!deps.isEmpty()) {      
                getLog().debug("copyDependencies to location: "+dftLocationPath);
            }

            for (Dependency dep : deps) {
                copyDependencies(dep, null, dftLocationPath, defaultStripVersion);                
            }

            List depGroups = copyDependencies.getDependencyGroups();

            for (DependencyGroup depGroup : depGroups) {
                String overrideLocation = depGroup.getLocation();
                if (overrideLocation != null) {
                    getLog().debug("copyDependencies to location: "+ overrideLocation);
                } else {
                    getLog().debug("copyDependencies to location: "+dftLocationPath);
                }
                boolean stripVersion = defaultStripVersion;
                Boolean overrideStripVersion = depGroup.getStripVersion();
                if (overrideStripVersion != null) {
                    stripVersion = overrideStripVersion.booleanValue();
                }
                List groupDeps = depGroup.getDependencies();
                for (Dependency dep : groupDeps) {
                    copyDependencies(dep, overrideLocation, dftLocationPath, stripVersion);                
                }
            }

        }
    }

    private void copyDependencies(Dependency dep, String overrideLocation, String defaultLocation, boolean stripVersion) throws MojoExecutionException, IOException {

        String location = defaultLocation;

        if (overrideLocation != null) {
            File overrideLocationFile = new File(overrideLocation);
            if (!overrideLocationFile.isAbsolute()) {
                // relative path
                overrideLocationFile = new File(serverDirectory, overrideLocation);
            }

            location = overrideLocationFile.getCanonicalPath();

            if (!overrideLocationFile.exists()) {
                overrideLocationFile.mkdirs();
            } else if (!overrideLocationFile.isDirectory()) {
                // send config error
                getLog().warn("The specified dependency location "+ overrideLocationFile.getCanonicalPath() +" is not a directory. Using default copyDependencies location "+ defaultLocation +" instead.");
                location = defaultLocation;
            }
        }

        Set artifactsToCopy = getResolvedDependencyWithTransitiveDependencies(dep.getGroupId(), dep.getArtifactId(), dep.getVersion(), dep.getType(), dep.getClassifier());

        if (artifactsToCopy.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            sb.append("copyDependencies failed for dependency with groupId "+ dep.getGroupId());
            String artifactId = dep.getArtifactId();
            if (artifactId != null) {
                sb.append(", artifactId "+artifactId);
            }
            String version = dep.getVersion();
            if (version != null) {
                sb.append(", version "+ version);
            }
            sb.append(" and type "+dep.getType());
            sb.append(". No matching resolved dependencies were found.");

            getLog().warn(sb.toString());
        } else {
            for (Artifact nextArtifact : artifactsToCopy) {
                File nextFile = nextArtifact.getFile();
                String targetFileName = nextFile.getName();
                if (stripVersion) {
                    targetFileName = stripVersionFromName(targetFileName, nextArtifact.getVersion());
                }

                File fileToCopyTo = new File(location, targetFileName);

                Copy copy = (Copy) ant.createTask("copy");
                copy.setFile(nextFile);
                copy.setTofile(fileToCopyTo);
                copy.setOverwrite(true);
                copy.execute();

                getLog().info("copyDependencies copied file "+nextFile.getName()+" to location "+location+"/"+targetFileName+".");
            }
        }
    }

    /**
     * @throws IOException
     * @throws MojoExecutionException
     */
    protected void copyConfigFiles() throws IOException, MojoExecutionException {

        String jvmOptionsPath = null;
        String bootStrapPropertiesPath = null;
        String serverEnvPath = null;
        String serverXMLPath = null;

        // First check for Liberty configuration specified by Maven properties.
        loadLibertyConfigFromProperties();

        if (configDirectory != null && configDirectory.exists()) {
            // copy configuration files from configuration directory to server directory if end-user set it
            Copy copydir = (Copy) ant.createTask("copy");
            FileSet fileset = new FileSet();
            fileset.setDir(configDirectory);

            // If mergeServerEnv is true, don't overwrite generated server.env
            File configDirServerEnv = new File(configDirectory, "server.env");
            if(mergeServerEnv && configDirServerEnv.exists()){
                // set excludes pattern 
                fileset.setExcludes("server.env");
            }

            copydir.addFileset(fileset);
            copydir.setTodir(serverDirectory);
            copydir.setOverwrite(true);
            copydir.execute();

            File configDirServerXML = new File(configDirectory, "server.xml");
            if (configDirServerXML.exists()) {
                serverXMLPath = configDirServerXML.getCanonicalPath();
            }

            File configDirJvmOptionsFile = new File(configDirectory, "jvm.options");
            if (configDirJvmOptionsFile.exists()) {
                jvmOptionsPath = configDirJvmOptionsFile.getCanonicalPath();
            }

            File configDirBootstrapFile = new File(configDirectory, "bootstrap.properties");
            if (configDirBootstrapFile.exists()) {
                bootStrapPropertiesPath = configDirBootstrapFile.getCanonicalPath();
            }

            if (configDirServerEnv.exists()) {
                serverEnvPath = configDirServerEnv.getCanonicalPath();
            }
        }

        // copy server.xml file to server directory if end-user explicitly set it.
        if (serverXmlFile != null && serverXmlFile.exists()) {
            if (serverXMLPath != null && ! serverXmlFile.getCanonicalPath().equals(serverXMLPath)) {
                getLog().info("The " + serverXMLPath + " file is overwritten by the "+serverXmlFile.getCanonicalPath()+" file.");
            }
            Copy copy = (Copy) ant.createTask("copy");
            copy.setFile(serverXmlFile);
            copy.setTofile(new File(serverDirectory, "server.xml"));
            copy.setOverwrite(true);
            copy.execute();
            serverXMLPath = serverXmlFile.getCanonicalPath();
        }

        // copy jvm.options to server directory if end-user explicitly set it
        File optionsFile = new File(serverDirectory, "jvm.options");
        if (optionsFile.exists() && jvmOptionsPath == null) {
            // if using pre-existing installation, do not delete file
            if (installType != InstallType.ALREADY_EXISTS) {
                getLog().info(optionsFile.getCanonicalPath() + " file deleted before processing plugin configuration.");
                optionsFile.delete();
            }
        }
        if (jvmOptions != null || !jvmMavenPropValues.isEmpty()) {
            if (jvmOptionsPath != null) {
                getLog().info("The " + jvmOptionsPath + " file is overwritten by inlined configuration.");
            }
            jvmOptionsResolved = handleLatePropertyResolution(jvmOptions);
            writeJvmOptions(optionsFile, jvmOptionsResolved, jvmMavenPropValues);
            jvmOptionsPath = "inlined configuration";
        } else if (jvmOptionsFile != null && jvmOptionsFile.exists()) {
            if (jvmOptionsPath != null) {
                getLog().info("The " + jvmOptionsPath + " file is overwritten by the "+jvmOptionsFile.getCanonicalPath()+" file.");
            }
            Copy copy = (Copy) ant.createTask("copy");
            copy.setFile(jvmOptionsFile);
            copy.setTofile(optionsFile);
            copy.setOverwrite(true);
            copy.execute();
            jvmOptionsPath = jvmOptionsFile.getCanonicalPath();
        }

        // copy bootstrap.properties to server directory if end-user explicitly set it
        File bootstrapFile = new File(serverDirectory, "bootstrap.properties");
        if (bootstrapFile.exists() && bootStrapPropertiesPath == null) {
            // if using pre-existing installation, do not delete file
            if (installType != InstallType.ALREADY_EXISTS) {
                getLog().info(bootstrapFile.getCanonicalPath() + " file deleted before processing plugin configuration.");
                bootstrapFile.delete();
            }
        } 
        if (bootstrapProperties != null || !bootstrapMavenProps.isEmpty()) {
            if (bootStrapPropertiesPath != null) {
                getLog().info("The " + bootStrapPropertiesPath + " file is overwritten by inlined configuration.");
            }
            bootstrapPropertiesResolved = handleLatePropertyResolution(bootstrapProperties);
            writeBootstrapProperties(bootstrapFile, bootstrapPropertiesResolved, bootstrapMavenProps);
            bootStrapPropertiesPath = "inlined configuration";
        } else if (bootstrapPropertiesFile != null && bootstrapPropertiesFile.exists()) {
            if (bootStrapPropertiesPath != null) {
                getLog().info("The " + bootStrapPropertiesPath + " file is overwritten by the "+ bootstrapPropertiesFile.getCanonicalPath()+" file.");
            }
            Copy copy = (Copy) ant.createTask("copy");
            copy.setFile(bootstrapPropertiesFile);
            copy.setTofile(bootstrapFile);
            copy.setOverwrite(true);
            copy.execute();
            bootStrapPropertiesPath = bootstrapPropertiesFile.getCanonicalPath();
        }

        // copy server.env to server directory if end-user explicitly set it
        File envFile = new File(serverDirectory, "server.env");
        if(mergeServerEnv) {
            serverEnvPath = mergeServerEnvFileAndEnvMavenProps(serverEnvPath);
        }
        else {
            if (!envMavenProps.isEmpty()) {
                Map envPropsToWrite = envMavenProps;
                if (serverEnvFile == null && serverEnvPath == null) {
                    // Do a special case merge but ONLY if there are no other config options present
                    envPropsToWrite = mergeSpecialPropsFromInstallServerEnvIfAbsent(envMavenProps);
                } else if (serverEnvPath != null) {
                    getLog().info("The " + serverEnvPath + " file is overwritten by inlined configuration.");
                }
                writeServerEnvProperties(envFile, envPropsToWrite);
                serverEnvPath = "inlined configuration";
            } else if (serverEnvFile != null && serverEnvFile.exists()) {
                Copy copy = (Copy) ant.createTask("copy");
                copy.setFile(serverEnvFile);
                copy.setTofile(envFile);
                copy.setOverwrite(true);
                copy.execute();
                serverEnvPath = serverEnvFile.getCanonicalPath();
            }
        }

        File pluginVariableConfig = new File(serverDirectory, PLUGIN_VARIABLE_CONFIG_OVERRIDES_XML);
        if (pluginVariableConfig.exists()) {
            getLog().debug(pluginVariableConfig.getCanonicalPath() + " file deleted before processing plugin configuration.");
            pluginVariableConfig.delete();
        }
        if (!varMavenProps.isEmpty()) {
            writeConfigDropinsServerVariables(pluginVariableConfig, varMavenProps, false);  
        }

        pluginVariableConfig = new File(serverDirectory, PLUGIN_VARIABLE_CONFIG_DEFAULTS_XML);
        if (pluginVariableConfig.exists()) {
            getLog().debug(pluginVariableConfig.getCanonicalPath() + " file deleted before processing plugin configuration.");
            pluginVariableConfig.delete();
        }
        if (!defaultVarMavenProps.isEmpty()) {
            writeConfigDropinsServerVariables(pluginVariableConfig, defaultVarMavenProps, true);  
        }

        // log info on the configuration files that get used
        if (serverXMLPath != null && !serverXMLPath.isEmpty()) {
            getLog().info(MessageFormat.format(messages.getString("info.server.start.update.config"),
                "server.xml", serverXMLPath));
        }
        if (jvmOptionsPath != null && !jvmOptionsPath.isEmpty()) {
            getLog().info(MessageFormat.format(messages.getString("info.server.start.update.config"),
                "jvm.options", jvmOptionsPath));
        }
        if (bootStrapPropertiesPath != null && !bootStrapPropertiesPath.isEmpty()) {
            getLog().info(MessageFormat.format(messages.getString("info.server.start.update.config"),
                "bootstrap.properties", bootStrapPropertiesPath));
        }
        if (serverEnvPath != null && !serverEnvPath.isEmpty()) {
            getLog().info(MessageFormat.format(messages.getString("info.server.start.update.config"),
                "server.env", serverEnvPath));
        }

        configFilesCopied = true;

        // Now process the copyDependencies configuration
        copyDependencies();
    }

    /**
     * Merges envProps with special properties found in the install (target) server.env.  We return a clone/copy of
     * envProps, to which any of a list of special properties found in server.env have been added.  We give precedence
     * to properties already in envProps.
     */
    private Map mergeSpecialPropsFromInstallServerEnvIfAbsent(Map envProps) throws IOException {

        String[] specialProps = { "keystore_password" };

        // Clone to avoid side effects 
        Map mergedProps = new HashMap(envProps);
        
        // From install (target) dir
        File serverEnv = new File(serverDirectory, "server.env");
        Map serverEnvProps = convertServerEnvToProperties(serverEnv);
        
        for (String propertyName : specialProps) {
            if (serverEnvProps.containsKey(propertyName)) {
                mergedProps.putIfAbsent(propertyName,serverEnvProps.get(propertyName));
            }
        }

        return mergedProps;
    }

    // Merges configured serverEnvFile with envMavenProps if specified, and returns the updated serverEnvPath
    private String mergeServerEnvFileAndEnvMavenProps(String serverEnvPath) throws IOException {
        String modifiedServerEnvPath = serverEnvPath;
        boolean mergeRequired = serverEnvPath != null || 
                                (serverEnvFile != null && serverEnvFile.exists()) || 
                                !envMavenProps.isEmpty();

        if (mergeRequired) {
            File serverEnv = new File(serverDirectory, "server.env");
            Map serverEnvProps = convertServerEnvToProperties(serverEnv);

            // merge configDir serverEnv if present
            if (serverEnvPath!= null) {
                File configDirServerEnv = new File(configDirectory, "server.env");
                Map configDirServerEnvProps = convertServerEnvToProperties(configDirServerEnv);
                serverEnvProps.putAll(configDirServerEnvProps);
            }

            //merge specified server.env if present
            if (serverEnvFile != null && serverEnvFile.exists()) {
                Map serverEnvFileProps = convertServerEnvToProperties(serverEnvFile);
                serverEnvProps.putAll(serverEnvFileProps);
            }

            //merge server env props
            if (!envMavenProps.isEmpty()) {
                serverEnvProps.putAll(envMavenProps);
            }

            writeServerEnvProperties(serverEnv, serverEnvProps);
            modifiedServerEnvPath = getMergedServerEnvPath(serverEnvPath);
        }

        return modifiedServerEnvPath;
    }

    private String getMergedServerEnvPath(String serverEnvPath) throws IOException {
        boolean configDirEnvMerged = serverEnvPath != null;
        boolean serverEnvFileMerged = serverEnvFile != null && serverEnvFile.exists();
        boolean inlineEnvPropsMerged = !envMavenProps.isEmpty();

        StringBuilder updatedServerEnvPath = new StringBuilder("merging");

        if(configDirEnvMerged) {
            updatedServerEnvPath.append(" configDir server.env " +  serverEnvPath + ", ");
        }
        if (serverEnvFileMerged) {
            updatedServerEnvPath.append(" serverEnvFile " +  serverEnvFile.getCanonicalPath() + ", ");
        }
        if (inlineEnvPropsMerged) {
            updatedServerEnvPath.append(" env properties, ");
        }
        // remove excess comma and space
        int lastCommaIndex = updatedServerEnvPath.lastIndexOf(", ");
        updatedServerEnvPath = updatedServerEnvPath.replace(lastCommaIndex, lastCommaIndex + 2, ".");
        
        //replace last comma and space with and
        lastCommaIndex = updatedServerEnvPath.lastIndexOf(", ");
        if(lastCommaIndex > 0) {
            updatedServerEnvPath = updatedServerEnvPath.replace(lastCommaIndex, lastCommaIndex + 2, "");
            updatedServerEnvPath = updatedServerEnvPath.insert(lastCommaIndex, " and");
        }

        return updatedServerEnvPath.toString();
    }

    private Map convertServerEnvToProperties(File serverEnv) throws IOException {
        Map mavenProperties = new HashMap();

        if ((serverEnv == null) || !serverEnv.exists()) {
            return mavenProperties;
        }

        BufferedReader bf = new BufferedReader(new FileReader(serverEnv));
        String line;
        while((line = bf.readLine()) != null) {
            
            //Skip comments
            if(!line.startsWith("#")) {
                String[] keyValue = line.split("=", 2);
                if (keyValue.length == 2) {
                    String key = keyValue[0];
                    String value = keyValue[1];

                    mavenProperties.put(key,value);
                }
            }
        }
        bf.close();

        return mavenProperties;
    }

    private void loadLibertyConfigFromProperties() {

        loadLibertyConfigFromProperties(project.getProperties());
        loadLibertyConfigFromProperties(System.getProperties());

    }

    private void loadLibertyConfigFromProperties(Properties props) {
        Set> entries = props.entrySet();
        for (Entry entry : entries) {
            String key = (String) entry.getKey();
            PropertyType propType = PropertyType.getPropertyType(key);

            if (propType != null) {
                String suffix = key.substring(propType.getPrefix().length());
                String value = (String) entry.getValue();
                // Check the value for late property resolution with @{xxx} syntax.
                value = resolveLatePropertyReferences(value);
                
                getLog().debug("Processing Liberty configuration from property with key "+key+" and value "+value);
                switch (propType) {
                    case ENV:        envMavenProps.put(suffix, value);
                                     break;
                    case BOOTSTRAP:  bootstrapMavenProps.put(suffix, value);
                                     break;
                    case JVM:        if (jvmMavenPropNames.contains(suffix)) {
                                        int index = jvmMavenPropNames.indexOf(suffix);
                                        getLog().debug("Remove duplicate property with name: "+suffix+" at position: "+index);
                                        jvmMavenPropNames.remove(index);
                                        jvmMavenPropValues.remove(index);
                                     }
                                     jvmMavenPropNames.add(suffix);  // need to keep track of names so that a system prop can override a project prop
                                     jvmMavenPropValues.add(value);
                                     break;
                    case VAR:        varMavenProps.put(suffix, value);
                                     break;
                    case DEFAULTVAR: defaultVarMavenProps.put(suffix, value);
                                     break;
                }
            }
        }
    }

    // Search the value parameter for any properties referenced with @{xxx} syntax and replace those with their property value if defined.
    private String resolveLatePropertyReferences(String value) {
        String returnValue = value;

        if (value != null) {
            Matcher m = LATE_PROP_PATTERN.matcher(value);
            while (m.find()) {
                String varName = m.group(1);
                if (project.getProperties().containsKey(varName)) {
                    String replacementValue = project.getProperties().getProperty(varName);
                    if (replacementValue != null) {
                        returnValue = returnValue.replace("@{"+varName+"}", replacementValue);
                        getLog().debug("Replaced Liberty configuration property value @{"+varName+"} with value "+replacementValue);
                    }
                }
            }
        }
        
        return returnValue;
    }

    protected Map handleLatePropertyResolution(Map properties) {
        Map propertiesResolved = null;
        if (properties != null) {
            propertiesResolved = new HashMap ();
            for (Map.Entry entry : properties.entrySet()) {
                String value = resolveLatePropertyReferences(entry.getValue());
                propertiesResolved.put(entry.getKey(), value);
            }
        }
        return propertiesResolved;
    }

    protected List handleLatePropertyResolution(List properties) {
        List propertiesResolved = null;
        if (properties != null) {
            propertiesResolved = new ArrayList ();
            for (String nextOption : properties) {
                String value = resolveLatePropertyReferences(nextOption);
                propertiesResolved.add(value);
            }
        }
        return propertiesResolved;
    }

    // The properties parameter comes from the  configuration in pom.xml and takes precedence over
    // the mavenProperties parameter, which comes from generic maven  configuration.
    // One of the passed in Maps must be not null and not empty
    private void writeBootstrapProperties(File file, Map properties, Map mavenProperties) throws IOException {
        if (!mavenProperties.isEmpty()) {
            if (properties == null) {
                combinedBootstrapProperties = mavenProperties;
            } else {
                combinedBootstrapProperties = new HashMap ();
                // add the maven properties first so that they do not take precedence over the properties specified with 
                combinedBootstrapProperties.putAll(mavenProperties);
                combinedBootstrapProperties.putAll(properties);
            }
        } else {
            combinedBootstrapProperties = properties;
        }

        makeParentDirectory(file);
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(file, "UTF-8");
            writer.println(HEADER);
            for (Map.Entry entry : combinedBootstrapProperties.entrySet()) {
                String key = entry.getKey();
                writer.print(key);
                writer.print("=");
                String value = entry.getValue();
       
                writer.println((value != null) ? value.replace("\\", "/") : "");
                if (value == null) {
                    getLog().warn("The value of the bootstrap property " + key + " is null. Verify if the needed POM properties are set correctly.");
                }
            }
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    private void writeServerEnvProperties(File file, Map mavenProperties) throws IOException {
        makeParentDirectory(file);
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(file, "UTF-8");
            writer.println(HEADER);
            for (Map.Entry entry : mavenProperties.entrySet()) {
                String key = entry.getKey();
                writer.print(key);
                writer.print("=");
                String value = entry.getValue();
                writer.println((value != null) ? value.replace("\\", "/") : "");
                if (value == null) {
                    getLog().warn("The value of the server.env property " + entry.getKey() + " is null. Verify if the needed POM properties are set correctly.");
                }
            }
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    // Remove any duplicate entries in the passed in List
    protected List getUniqueValues(List values) {
        List uniqueValues = new ArrayList ();
        if (values == null) {
            return uniqueValues;
        }
        
        for (String nextValue : values) {
            // by removing a matching existing value, it ensures there will not be a duplicate and that this current one will appear later in the List
            if (uniqueValues.contains(nextValue)) {
                getLog().debug("Remove duplicate value: "+nextValue+" at position: "+uniqueValues.indexOf(nextValue));
            }
            uniqueValues.remove(nextValue); // has no effect if the value is not present
            uniqueValues.add(nextValue);
        }
        return uniqueValues;
    }

    // One of the passed in Lists must be not null and not empty
    private void writeJvmOptions(File file, List options, List mavenProperties) throws IOException {
        List uniqueOptions = getUniqueValues(options);
        List uniqueMavenProps = getUniqueValues(mavenProperties);

        if (!uniqueMavenProps.isEmpty()) {
            if (uniqueOptions.isEmpty()) {
                combinedJvmOptions = uniqueMavenProps;
            } else {
                combinedJvmOptions = new ArrayList ();
                // add the maven properties (which consist of both project properties and system properties) first,
                // so that they do not take precedence over the options specified with jvmOptions config parameter
                combinedJvmOptions.addAll(uniqueMavenProps);
                combinedJvmOptions.removeAll(uniqueOptions); // remove any exact duplicates before adding all the jvmOptions
                combinedJvmOptions.addAll(uniqueOptions);
            }
        } else {
            combinedJvmOptions = uniqueOptions;
        }

        makeParentDirectory(file);
        PrintWriter writer = null;
        try {
            writer = new PrintWriter(file, "UTF-8");
            writer.println(HEADER);
            for (String option : combinedJvmOptions) {
                writer.println(option);
            }
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

    private void writeConfigDropinsServerVariables(File file, Map props, boolean isDefaultVar) throws IOException, MojoExecutionException {

        try {
            ServerConfigXmlDocument configDocument = ServerConfigXmlDocument.newInstance();

            configDocument.createComment(HEADER);
    
            for (Map.Entry entry : props.entrySet()) {
                configDocument.createVariableWithValue(entry.getKey(), entry.getValue(), isDefaultVar);
            }
    
            // write XML document to file
            makeParentDirectory(file);
            configDocument.writeXMLDocument(file);    
        } catch (ParserConfigurationException | TransformerException e) {
            throw new MojoExecutionException("Error writing configDropins variable file "+file.getCanonicalPath(), e);
        }
    }

    private void makeParentDirectory(File file) {
        File parentDir = file.getParentFile();
        if (parentDir != null) {
            parentDir.mkdirs();
        }
    }

    public boolean isConfigCopied() {
        return configFilesCopied;
    }

    /**
     * If the ear artifact is not in .m2, create an ear artifact as a workaround so that downstream modules can build.
     * Only needed if using loose application.
     * 
     * @param earProject
     */
    protected void getOrCreateEarArtifact(MavenProject earProject) {
        org.apache.maven.model.Dependency existingEarItem = createArtifactItem(earProject.getGroupId(), earProject.getArtifactId(), earProject.getPackaging(), earProject.getVersion());
        try {
            Artifact existingEarArtifact = getArtifact(existingEarItem);
            getLog().debug("EAR artifact already exists at " + existingEarArtifact.getFile());
        } catch (MojoExecutionException e) {
            getLog().debug("Installing empty EAR artifact to .m2 directory...");
            updateArtifactPathToOutputDirectory(earProject);
        }
    }

    private void installEmptyEAR(MavenProject earProject) throws MojoExecutionException {
        String goal = "install-file";
        Plugin plugin = getPlugin("org.apache.maven.plugins", "maven-install-plugin");
        getLog().debug("Running maven-install-plugin:" + goal);

        File tempFile;
        try {
            tempFile = File.createTempFile(earProject.getArtifactId(), ".ear");
            tempFile.deleteOnExit();
        } catch (IOException e) {
            String module = getModuleRelativePath(earProject);
            getLog().debug(e);
            throw new MojoExecutionException("Could not install placeholder EAR artifact for module " + module + ". Manually run the following command to resolve this issue: mvn install -pl " + module + " -am");
        }

        Xpp3Dom config = configuration(
            element(name("file"), tempFile.getAbsolutePath()),
            element(name("pomFile"), earProject.getFile().getAbsolutePath())
        );

        getLog().debug("configuration:\n" + config);
        try {
            executeMojo(plugin, goal(goal), config, executionEnvironment(project, session, pluginManager));
        } catch (MojoExecutionException e) {
            String module = getModuleRelativePath(earProject);
            getLog().debug(e);
            throw new MojoExecutionException("Could not install placeholder EAR artifact for module " + module + ". Manually run the following command to resolve this issue: mvn install -pl " + module + " -am");
        }
    }

    /**
     * Purge the installed artifact for the current project from the local .m2
     * repository, so that any downstream modules (when using loose application)
     * will not rely on the installed artifact for their compilation.
     * 
     * @throws MojoExecutionException If an exception occurred while running
     *                                dependency:purge-local-repository
     */
    protected void purgeLocalRepositoryArtifact() throws MojoExecutionException {
        Plugin plugin = getPlugin("org.apache.maven.plugins", "maven-dependency-plugin");
        String goal = "purge-local-repository";
        Xpp3Dom config = ExecuteMojoUtil.getPluginGoalConfig(plugin, goal, getLog());
        config = Xpp3Dom.mergeXpp3Dom(configuration(
            element(name("reResolve"), "false"), 
            element(name("actTransitively"), "false"), 
            element(name("manualIncludes"), project.getGroupId()+":"+project.getArtifactId())
            ), config);
        getLog().info("Running maven-dependency-plugin:" + goal);
        getLog().debug("configuration:\n" + config);
        executeMojo(plugin, goal(goal), config, executionEnvironment(project, session, pluginManager));
    }
    
    
    /**
     * Call {@link #updateArtifactPathToOutputDirectory(MavenProject,Artifact) updateArtifactPathToOutputDirectory(MavenProject mavenProject, Artifact artifactToUpdate)} 
     * with artifactToUpdate obtained from mavenProject
     * 
     * @param mavenProject
     */
    protected void updateArtifactPathToOutputDirectory(MavenProject mavenProject) {
    	updateArtifactPathToOutputDirectory(mavenProject, mavenProject.getArtifact());
    }    
    
    /**
     * Call artifactToUpdate.setFile() to build output directory (i.e. ".../target/classes") 
     * (or build directory ".../target" for EAR module) and also creates the directory if not present.  
     * Together these will help avoid the core Maven artifact resolution trying to resolve the artifact against the local .m2, 
     * fitting better into dev mode, "all-in-one" use cases.
     * 
     * @param mavenProject
     * @param artifactToUpdate
     */
    protected void updateArtifactPathToOutputDirectory(MavenProject mavenProject, Artifact artifactToUpdate) {
        Path outputDir = null; 
        if (artifactToUpdate.getType().equals("ear")) {
            outputDir = Paths.get(mavenProject.getBuild().getDirectory());
        } else {
            outputDir = Paths.get(mavenProject.getBuild().getOutputDirectory());
        }

        try {
            if (!Files.exists(outputDir)) {
                Files.createDirectory(outputDir);
            }
        } catch(IOException ioe) {
            // Since this is kind of a hack it seems to draw too much attention to issue a warning message here.
            getLog().debug("Failure creating output directory: " + outputDir, ioe);
        }
    
        artifactToUpdate.setFile(outputDir.toFile());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy