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

org.openengsb.openengsbplugin.base.ConfiguredMojo Maven / Gradle / Ivy

/**
 * Licensed to the Austrian Association for Software Tool Integration (AASTI)
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. The AASTI 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.openengsb.openengsbplugin.base;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.apache.maven.plugin.MojoExecutionException;
import org.openengsb.openengsbplugin.tools.Tools;
import org.openengsb.openengsbplugin.xml.OpenEngSBMavenPluginNSContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public abstract class ConfiguredMojo extends MavenExecutorMojo {

    public enum PomRestoreMode {
        CLEAN, RESTORE_BACKUP
    }

    private static final Logger LOG = Logger.getLogger(ConfiguredMojo.class);

    /**
     * Stores references to poms corresponding to keys in {@link ConfiguredMojo#pomConfigs}
     */
    private HashMap poms = new HashMap();

    /**
     * points to backups of modified poms in the local tmp dir. The key is the original
     * pom in the project (e.g. "docs/pom.xml"), where the value is the
     * reference to the file in the tmp dir.
     */
    private HashMap pomBackups = new HashMap();

    // #################################
    // set these in subclass constructor
    // #################################

    /**
     * Defines which configs (from {@code resources/xxx}) to merge into which pom
     * where key = path to pom, relative to current dir (where the mojo is invoked).
     * For examples please see constructor of every direct subclass of
     * {@link ConfiguredMojo} ( e.g. {@link LicenseMojo#LicenseMojo()}.
     */
    protected HashMap> pomConfigs = new HashMap>();

    // #################################

    protected String cocProfile;
    protected String cocProfileXpath;

    protected static final OpenEngSBMavenPluginNSContext NS_CONTEXT = new OpenEngSBMavenPluginNSContext();
    protected static final String POM_NS_URI = NS_CONTEXT.getNamespaceURI("pom");
    private static final String POM_PROFILE_XPATH = "/pom:project/pom:profiles";

    private static final String CONFIGNODE_XPATH = "/c:config";
    private static final String PLUGINS_XPATH = CONFIGNODE_XPATH + "/pom:plugins/pom:plugin";
    private static final String MODULES_XPATH = CONFIGNODE_XPATH + "/pom:modules/pom:module";
    private static final String RESOURCES_XPATH = CONFIGNODE_XPATH + "/pom:resources/pom:resource";

    protected static final List FILES_TO_REMOVE_FINALLY = new ArrayList();

    private PomRestoreMode pomRestoreMode = PomRestoreMode.RESTORE_BACKUP;
    private boolean pomCleanedSuccessfully = false;

    protected Node licenseHeaderComment = null;

    /**
     * If set to "true" prints the temporary pom to the console.
     *
     * @parameter expression="${debugMode}" default-value="false"
     */
    private boolean debugMode;

    @Override
    protected final void configure() throws MojoExecutionException {
        LOG.trace("-> configure");
        cocProfile = UUID.randomUUID().toString();
        cocProfileXpath = String.format("/pom:project/pom:profiles/pom:profile[pom:id[text()='%s']]",
                cocProfile);
        configureCoCMojo();

        checkForPoms();
        for (String pomPath : poms.keySet()) {
            configureTmpPom(pomPath, poms.get(pomPath), cocProfile);
        }
    }

    private void checkForPoms() throws MojoExecutionException {
        for (String pomPath : pomConfigs.keySet()) {
            File pom = new File(pomPath);
            if (!pom.exists()) {
                throw new MojoExecutionException(String.format("pom doesn't exist: %s", pomPath));
            }
            poms.put(pomPath, pom);
        }
    }

    /**
     * Configure how to restore (remove the unnecessary temporary profile) the
     * pom after successful mojo execution.
     *
     * @param mode 
    *
  • {@link PomRestoreMode#RESTORE_BACKUP}: replace the pom with * the backup which has been created at mojo startup - this is the * default setting for all CoC mojos
  • *
  • {@link PomRestoreMode#CLEAN}: don't replace the pom with the * backup, only remove the profile which has been created for this * mojo run - this setting can be useful if the wrapped mojo also * modifies this pom (e.g. the maven-release-plugin changing version * numbers), so that these changes don't get lost
  • *
      */ protected final void setPomRestoreMode(PomRestoreMode mode) { pomRestoreMode = mode; } @Override protected final void postExec() throws MojoExecutionException { if (pomRestoreMode == PomRestoreMode.CLEAN) { cleanPoms(cocProfile); pomCleanedSuccessfully = true; afterPomCleaned(); } } /** * Template method which may be overwritten by subclasses. It gets executed * iff {@link PomRestoreMode} is set to {@link PomRestoreMode#CLEAN} and the * pom has been cleaned successfully. For usage example see * {@link ReleaseMojo#afterPomCleaned()} */ protected void afterPomCleaned() throws MojoExecutionException { } @Override protected final void postExecFinally() { if (pomRestoreMode == PomRestoreMode.RESTORE_BACKUP || pomRestoreMode == PomRestoreMode.CLEAN && !pomCleanedSuccessfully) { restoreOriginalPom(); } cleanUp(); } /** * remove profile with id {@code profile} from the pom (used to clean pom * from configuration which gets merged into it during runtime) */ private void cleanPoms(String profile) throws MojoExecutionException { LOG.trace("cleanPom()"); try { for (File pomFile : poms.values()) { Document docToClean = parsePom(pomFile); if (!Tools.removeNode(cocProfileXpath, docToClean, NS_CONTEXT, true)) { throw new MojoExecutionException("Couldn't clean the pom!"); } String cleanedContent = Tools.serializeXML(docToClean); writeIntoPom(getSession().getRequest().getPom(), cleanedContent); } } catch (Exception e) { if (e instanceof MojoExecutionException) { throw (MojoExecutionException) e; } else { throw new MojoExecutionException(e.getMessage(), e); } } LOG.trace("pom cleaned successfully"); } /** * restore backup of the pom which has been created on mojo start */ private void restoreOriginalPom() { LOG.trace("-> restoreOriginalPom"); try { for (File pomFile : poms.values()) { FileUtils.copyFile(pomBackups.get(pomFile), pomFile); } } catch (Exception e) { // do nothing } } /** * template method for subclasses where you can declare which goals should * be used and which configuration should be merged into the poms */ protected abstract void configureCoCMojo() throws MojoExecutionException; private void configureTmpPom(String pomPath, File pomFile, String profileName) throws MojoExecutionException { try { File backupPom = backupOriginalPom(pomFile); pomBackups.put(pomFile, backupPom); FILES_TO_REMOVE_FINALLY.add(backupPom); Document pomDocumentToConfigure = parsePom(pomFile); Document configDocument = collectConfigsAndBuildProfile(pomPath); insertConfigProfileIntoOrigPom(pomDocumentToConfigure, configDocument, profileName); modifyMojoConfiguration(pomPath, pomDocumentToConfigure); String serializedXml = Tools.serializeXML(pomDocumentToConfigure); if (debugMode) { System.out.print(serializedXml); } writeIntoPom(pomFile, serializedXml); } catch (Exception e) { LOG.warn(e.getMessage(), e); throw new MojoExecutionException("Couldn't configure temporary pom for this execution!", e); } } /** * If you want to modify the xml configuration of the mojo (e.g. add some * configuration which you is not known in * {@link ConfiguredMojo#configureTmpPom(String)}), then this is the place * where to do it. Simply overwrite this method in your subclass. */ protected void modifyMojoConfiguration(String pomPath, Document configuredPom) throws MojoExecutionException { } protected String addHeader(String content) throws IOException { String result = ""; StringReader stringReader = new StringReader(content); BufferedReader br = new BufferedReader(stringReader); String line; do { line = br.readLine(); result += line + "\n"; } while (!line.contains("\n\n"; line = br.readLine(); while (line != null) { result += line + "\n"; line = br.readLine(); } br.close(); licenseHeaderComment = null; return result; } /** * parse pom and remove license header if available */ private Document parsePom(File pom) throws Exception { Document doc = Tools.parseXMLFromString(FileUtils.readFileToString(pom)); tryExtractLicenseHeader(doc); return doc; } protected void tryExtractLicenseHeader(Document doc) { Node firstNode = doc.getChildNodes().item(0); if (firstNode.getNodeType() == Node.COMMENT_NODE) { LOG.trace(String.format("found license header with content:\n%s", firstNode.getNodeValue())); licenseHeaderComment = doc.removeChild(firstNode); } } /** * Build a profile of alle specified config files which later gets merged into * the pom. */ private Document collectConfigsAndBuildProfile(String pomPath) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException { Document profileDoc = Tools.newDOM(); Element profileElement = profileDoc.createElementNS(POM_NS_URI, "profile"); profileDoc.appendChild(profileElement); Element buildElement = profileDoc.createElementNS(POM_NS_URI, "build"); profileElement.appendChild(buildElement); Element pluginsElement = profileDoc.createElementNS(POM_NS_URI, "plugins"); buildElement.appendChild(pluginsElement); Element modulesElement = profileDoc.createElementNS(POM_NS_URI, "modules"); profileElement.appendChild(modulesElement); Element resourcesElement = profileDoc.createElementNS(POM_NS_URI, "resources"); buildElement.appendChild(resourcesElement); /* * if you want to support further profile configuration (current: * , , ) this is the place where to put it */ for (String configFilePath : pomConfigs.get(pomPath)) { Document configDocument = Tools.parseXMLFromString(IOUtils.toString(getClass().getClassLoader() .getResourceAsStream(configFilePath))); collectFromXPathAndImportConfigurations(PLUGINS_XPATH, configDocument, profileDoc, buildElement, pluginsElement); collectFromXPathAndImportConfigurations(MODULES_XPATH, configDocument, profileDoc, profileElement, modulesElement); collectFromXPathAndImportConfigurations(RESOURCES_XPATH, configDocument, profileDoc, buildElement, resourcesElement); } return profileDoc; } private void collectFromXPathAndImportConfigurations(String xpath, Document source, Document dest, Element parentParent, Element targetParent) throws XPathExpressionException { NodeList nl = Tools.evaluateXPath(xpath, source, NS_CONTEXT, XPathConstants.NODESET, NodeList.class); LOG.trace(String.format("Found nodes: %s", nl.getLength())); if (nl.getLength() == 0) { return; } importNodesFromNodeList(dest, targetParent, nl); } private void importNodesFromNodeList(Document doc, Element parentElement, NodeList nodes) { for (int i = 0; i < nodes.getLength(); i++) { Node importedNode = doc.importNode(nodes.item(i), true); parentElement.appendChild(importedNode); LOG.trace(String.format("importing node (parent=%s): %s", parentElement.getLocalName(), importedNode.getNodeName())); } } private void insertConfigProfileIntoOrigPom(Document originalPom, Document mojoConfiguration, String profileName) throws XPathExpressionException { Node profileNode = mojoConfiguration.getFirstChild(); Node idNode = mojoConfiguration.createElementNS(POM_NS_URI, "id"); idNode.setTextContent(profileName); profileNode.insertBefore(idNode, profileNode.getFirstChild()); Node importedProfileNode = originalPom.importNode(profileNode, true); Tools.insertDomNode(originalPom, importedProfileNode, POM_PROFILE_XPATH, NS_CONTEXT); } private void writeIntoPom(File pomFile, String content) throws IOException { if (licenseHeaderComment != null) { content = addHeader(content); } FileUtils.writeStringToFile(pomFile, content + "\n"); } private void cleanUp() { LOG.trace("-> cleanup()"); for (File f : FILES_TO_REMOVE_FINALLY) { FileUtils.deleteQuietly(f); } } private File backupOriginalPom(File originalPom) throws IOException { return Tools.generateTmpFile(FileUtils.readFileToString(originalPom), ".xml"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy