fr.fastconnect.factory.tibco.bw.maven.AbstractBWMojo.java Source code

Java tutorial

Introduction

Here is the source code for fr.fastconnect.factory.tibco.bw.maven.AbstractBWMojo.java

Source

/**
 * (C) Copyright 2011-2015 FastConnect SAS
 * (http://www.fastconnect.fr/) and others.
 *
 * 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 fr.fastconnect.factory.tibco.bw.maven;

import static org.apache.commons.io.FileUtils.copyFile;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.exec.ShutdownHookProcessDestroyer;
import org.apache.commons.exec.launcher.CommandLauncher;
import org.apache.commons.exec.launcher.CommandLauncherFactory;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.SystemUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Profile;
import org.apache.maven.settings.Settings;
import org.apache.tools.ant.taskdefs.optional.ReplaceRegExp;

import fr.fastconnect.factory.tibco.bw.maven.exception.BinaryMissingException;

/**
 * 
 * <p>
 * This abstract class is aimed at preparing a TIBCO build environment in order
 * to build the project specified in the POM project on a clean environment
 * which is absolutely independent of the current system.
 * 
 * It implies :
 * <ul>
 * <li>checking the BW project exists (TODO : and is correct)</li>
 * <li>creating a Designer5.prefs with actual file aliases referencing other
 * artifacts handled by Maven</li>
 * <li>creating a .designtimelibs referencing the dependencies declared in the
 * POM project</li>
 * </ul>
 * 
 * The concrete children classes will call the launchTIBCOBinary with specific
 * arguments (use 'buildear' or 'designer' or 'buildlibrary' or ...)
 * </p>
 * 
 * @author Mathieu Debove
 * 
 */
public class AbstractBWMojo extends AbstractMojo {

    protected final static String BWPROJECT_NOTFOUND = "The BusinessWorks project can't be found.";
    protected final static String BWPROJECT_PREF_LIB_ERROR_MSG = "The BusinessWorks project Designer5.prefs/.designtimelibs can't be copied.";
    protected final static String SKIPPING = "Skipping.";

    public final static String BWEAR_EXTENSION = ".ear";
    public final static String BWEAR_TYPE = "bw-ear";
    public final static String BWEAR_DEPLOY_TYPE = "bw-ear-deploy";
    protected final static String PROJLIB_EXTENSION = ".projlib";
    public final static String PROJLIB_TYPE = "projlib";
    protected final static String JAR_EXTENSION = ".jar";
    public final static String JAR_TYPE = "jar";

    protected final static String PROPERTIES_EXTENSION = ".properties";

    public final static String POM_TYPE = "pom";
    protected final static String POM_EXTENSION = ".xml";
    public final static String XML_TYPE = "xml";
    protected final static String XML_EXTENSION = ".xml";

    protected final static String TIBCO_HOME_DIR = ".TIBCO";
    protected final static String TIBCO_ALIAS_PREFIX = "tibco.alias.";
    protected final static String FILE_ALIAS_PREFIX = "filealias.pref.";
    protected final static String FILE_ALIAS_PREFIX_ESCAPED = "filealias\\.pref\\.";
    protected final static String ALIASES_FILE = "aliases.properties";
    protected final static String DTL_FILE_NAME = ".designtimelibs";
    protected final static String DESIGNER5_PREFS = "Designer5.prefs";

    /**
     * Timeout for the execution of TIBCO commands to build artifacts.
     * This time is given in seconds.
     */
    @Parameter(property = "timeOut", defaultValue = "180")
    protected int timeOut;

    /**
     * Path to the TIBCO home directory.
     */
    @Parameter(property = "tibco.home")
    protected File tibcoHome;

    @Parameter(property = "tibco.hawk.version")
    protected String hawkVersion;

    /*
     * TRA configuration files
     */

    /**
     * Path to the TIBCO "AppManage" TRA configuration file.
     */
    @Parameter(property = "appmanage.tra.path")
    protected File tibcoAppManageTRAPath;

    /**
     * Path to the TIBCO "buildear" TRA configuration file.
     */
    @Parameter(property = "buildear.tra.path")
    protected File tibcoBuildEARTRAPath;

    /**
     * Path to the BusinessWorks Engine TRA configuration file.
     */
    @Parameter(property = "bwengine.tra.path", required = true)
    protected File tibcoBWEngineTRAPath;

    /**
     * Path to the TIBCO Designer "buildlibrary" TRA configuration file.
     */
    @Parameter(property = "buildlibrary.tra.path")
    protected File tibcoBuildLibraryTRAPath;

    /**
     * Path to the TIBCO Designer TRA configuration file.
     */
    @Parameter(property = "designer.tra.path")
    protected File tibcoDesignerTRAPath;

    /**
     * Sometimes a TRA can reference another TRA, for instance 'buildear.tra'
     * will reference 'designer.tra'.
     * This field specifies whether we will use the default referenced TRA or
     * override with a provided TRA. 
     */
    @Parameter(property = "tra.buildear.uses.designer.tra.path")
    protected boolean tibcoBuildEARUseDesignerTRA;

    /**
     * Sometimes a TRA can reference another TRA, for instance
     * 'buildlibrary.tra' will reference 'designer.tra'.
     * This field specifies whether we will use the default referenced TRA or
     * override with a provided TRA. 
     */
    @Parameter(property = "tra.buildlibrary.uses.designer.tra.path")
    protected boolean tibcoBuildLibraryUseDesignerTRA;

    /**
     * Path to the TIBCO RendezVous folder.
     */
    @Parameter(property = "tibrv.home.path")
    protected File tibcoRvHomePath;

    /*
     * TIBCO binaries 
     */

    protected final static String APPMANAGE_BINARY_NOTFOUND = "The TIBCO AppManage binary can't be found.";
    protected final static String DESIGNER_BINARY_NOTFOUND = "The TIBCO Designer binary can't be found.";
    protected final static String HAWK_BINARY_NOTFOUND = "The TIBCO Hawk binary folder can't be found.";

    /**
     * Path to the TIBCO "AppManage" binary.
     */
    @Parameter(property = "appmanage.path")
    protected File tibcoAppManagePath;

    protected void checkAppManage() throws MojoExecutionException {
        if (tibcoAppManagePath == null || !tibcoAppManagePath.exists() || !tibcoAppManagePath.isFile()) {
            tibcoAppManagePathNotFound();
        }
    }

    private void tibcoAppManagePathNotFound() throws MojoExecutionException {
        throw new BinaryMissingException(APPMANAGE_BINARY_NOTFOUND);
    }

    /**
     * Path to the TIBCO Designer binary.
     */
    @Parameter(property = "designer.path")
    protected File tibcoDesignerPath;

    protected void checkDesigner() throws MojoExecutionException {
        if (tibcoDesignerPath == null || !tibcoDesignerPath.exists() || !tibcoDesignerPath.isFile()) {
            tibcoDesignerPathNotFound();
        }
    }

    private void tibcoDesignerPathNotFound() throws MojoExecutionException {
        throw new BinaryMissingException(DESIGNER_BINARY_NOTFOUND);
    }

    /**
     * Allows to ignore any alias when building EAR or Projlib.
     * 
     * This will mimic the "Hide Library Resources" behaviour of the TIBCO
     * Designer.
     */
    @Parameter(property = "bw.hide.library.resources")
    protected boolean hideLibraryResources;

    /**
     * List of "groupId:artifactId" to ignore when building
     */
    @Parameter
    protected ArrayList<String> dependenciesIgnored;

    /*
     * Paths
     */

    /**
     * Path to the output folder.
     * 
     * Default is "target/"
     */
    @Parameter(property = "project.build.directory", required = true, readonly = true)
    protected File directory;

    /**
     * Path to the test folder.
     * 
     * Default is "target/test"
     */
    @Parameter(property = "project.build.test.directory", defaultValue = "${project.build.directory}/test", required = true)
    protected File testDirectory;

    /**
     * Path to the dependencies (Projlibs, JARs...) for the test.
     * 
     * Default is "target/test/lib"
     */
    @Parameter(property = "project.build.test.directory.lib", defaultValue = "${project.build.test.directory}/lib", required = true)
    protected File testLibDirectory;

    /**
     * Path to the BusinessWorks sources for the test.
     * 
     * Default is "target/test/src"
     */
    @Parameter(property = "project.build.test.directory.src", defaultValue = "${project.build.test.directory}/src", required = true)
    protected File testSrcDirectory;

    /**
     * Path to the package folder.
     * 
     * Default is "target/package"
     */
    @Parameter(property = "project.package.directory", defaultValue = "${project.build.directory}/package", required = true)
    protected File packageDirectory;

    /**
     * Directory containing the generated artifact.
     */
    @Parameter(property = "project.build.outputDirectory", required = true)
    protected File outputDirectory;

    /**
     * Path to the BusinessWorks project.
     */
    @Parameter(property = "bw.project.location")
    protected File projectDirectory;

    /**
     * Path to the dependencies (Projlibs, JARs...) for the build.
     * 
     * Default is "target/lib"
     */
    @Parameter(property = "project.build.directory.lib", defaultValue = "${project.build.directory}/lib", required = true)
    protected File buildLibDirectory;

    /**
     * Path to the BusinessWorks sources for the build.
     * 
     * Default is "target/src"
     */
    @Parameter(property = "project.build.directory.src", defaultValue = "${project.build.directory}/src", required = true)
    protected File buildSrcDirectory;

    /*
     * Hawk configuration (optional)
     */

    @Parameter(property = "hawk.domain", defaultValue = "${tibco.domain.name}")
    protected String hawkDomain;

    @Parameter(property = "hawk.subscribe.interval", defaultValue = "10")
    protected Integer hawkSubscribeInterval; // in seconds

    @Parameter(property = "hawk.subscribe.retry.count", defaultValue = "30")
    protected Integer hawkSubscribeNumberOfRetry;

    @Parameter(property = "hawk.rv.service", defaultValue = "7474")
    protected String hawkRvService;

    @Parameter(property = "hawk.rv.network", defaultValue = ";")
    protected String hawkRvNetwork;

    @Parameter(property = "hawk.rv.daemon", defaultValue = "tcp:7474")
    protected String hawkRvDaemon;

    protected boolean initHawk(boolean failIfNotFound) throws BinaryMissingException {
        if (tibcoRvHomePath == null || !tibcoRvHomePath.exists()) {
            if (failIfNotFound) {
                throw new BinaryMissingException(HAWK_BINARY_NOTFOUND);
            } else {
                getLog().info("Unable to init Hawk.");
                return false;
            }
        }

        File tibrvj;
        if (SystemUtils.IS_OS_WINDOWS) {
            getLog().debug("Windows OS");
            tibcoRvHomePath = new File(tibcoRvHomePath, "bin/");
            tibrvj = new File(tibcoRvHomePath, "tibrvj.dll");
            System.load(tibrvj.getAbsolutePath());
        } else {
            getLog().debug("Not Windows OS");
            tibcoRvHomePath = new File(tibcoRvHomePath, "lib/");
            String osArch = System.getProperty("os.arch");
            tibrvj = null;
            if (osArch.equals("x86")) {
                getLog().debug("x86");
                tibrvj = new File(tibcoRvHomePath, "libtibrvj.so");
                System.loadLibrary("tibrvj");
            } else if (osArch.contains("64")) {
                getLog().debug("64");
                tibrvj = new File(tibcoRvHomePath, "libtibrvj64.so");
                System.loadLibrary("tibrvj64");
            }
        }
        getLog().debug("Loading system library : " + tibrvj.getAbsolutePath());

        return true;
    }

    /*
     * Properties
     */
    public static final Pattern mavenPropertyPattern = Pattern.compile("\\$\\{([^}]*)\\}"); // ${prop} regex pattern

    /**
     * <p>
     * Instantiate a minimalistic {@link AbstractCommonMojo} to use properties
     * management as a standalone object.
     * </p>
     *
     * @param session
     * @param mavenProject
     * @return
     */
    public static AbstractBWMojo propertiesManager(MavenSession session, MavenProject mavenProject) {
        AbstractBWMojo mojo = new AbstractBWMojo();
        mojo.setProject(mavenProject);
        mojo.setSession(session);
        mojo.setSettings(session.getSettings());

        return mojo;
    }

    @SuppressWarnings("unchecked") // because of Maven poor typing
    public String getPropertyValueInSettings(String propertyName, Settings settings) {
        if (settings == null) {
            return null;
        }

        List<String> activeProfiles = settings.getActiveProfiles();

        for (Object _profileWithId : settings.getProfilesAsMap().entrySet()) {
            Entry<String, Profile> profileWithId = (Entry<String, Profile>) _profileWithId;
            if (activeProfiles.contains(profileWithId.getKey())) {
                Profile profile = profileWithId.getValue();

                String value = profile.getProperties().getProperty(propertyName);
                if (value != null) {
                    return value;
                }
            }
        }

        return null;
    }

    private List<String> getActiveProfiles(Settings settings) {
        if (settings == null)
            return null;

        List<String> result = settings.getActiveProfiles();

        for (org.apache.maven.settings.Profile profile : settings.getProfiles()) {
            if (profile.getActivation().isActiveByDefault() && !result.contains(profile.getId())) {
                result.add(profile.getId());
            }
        }
        return result;
    }

    @SuppressWarnings("unchecked") // because of Maven poor typing
    public boolean propertyExistsInSettings(String propertyName, Settings settings) {
        if (settings == null) {
            return false;
        }

        List<String> activeProfiles = getActiveProfiles(settings);

        for (Object _profileWithId : settings.getProfilesAsMap().entrySet()) {
            Entry<String, Profile> profileWithId = (Entry<String, Profile>) _profileWithId;
            if (activeProfiles.contains(profileWithId.getKey())) {
                Profile profile = profileWithId.getValue();

                boolean result = profile.getProperties().containsKey(propertyName);
                if (result) {
                    return result;
                }
            }
        }

        return false;
    }

    private String getPropertyValueInCommandLine(String propertyName, MavenSession session) {
        if (session == null) {
            return null;
        }

        return session.getRequest().getUserProperties().getProperty(propertyName);
    }

    public boolean propertyExistsInSettings(String propertyName) {
        return propertyExistsInSettings(propertyName, session.getSettings());
    }

    public boolean propertyExists(String propertyName) {
        return propertyExists(project, propertyName);
    }

    public boolean propertyExists(MavenProject mavenProject, String propertyName) {
        return mavenProject.getOriginalModel().getProperties().containsKey(propertyName)
                || mavenProject.getModel().getProperties().containsKey(propertyName)
                || session.getRequest().getUserProperties().containsKey(propertyName)
                || propertyExistsInSettings(propertyName, session.getSettings());
    }

    public String getPropertyValue(MavenProject mavenProject, String propertyName, boolean lookInSettingsProperties,
            boolean lookInCommandLine, boolean onlyInOriginalModel) {
        if (mavenProject == null)
            return null;

        String result = null;

        if (onlyInOriginalModel) {
            result = mavenProject.getOriginalModel().getProperties().getProperty(propertyName);
        } else {
            result = mavenProject.getModel().getProperties().getProperty(propertyName);
        }
        if (lookInCommandLine && (result == null || result.isEmpty())) {
            result = getPropertyValueInCommandLine(propertyName, session);
        }
        if (lookInSettingsProperties && (result == null || result.isEmpty())) {
            result = getPropertyValueInSettings(propertyName, settings);
        }

        return result;
    }

    public String getPropertyValue(String propertyName, boolean onlyInOriginalModel) {
        return getPropertyValue(project, propertyName, true, true, onlyInOriginalModel);
    }

    public String getPropertyValue(String propertyName) {
        return getPropertyValue(propertyName, false);
    }

    public String getRootProjectProperty(MavenProject mavenProject, String propertyName) {
        return mavenProject == null ? ""
                : (mavenProject.getParent() == null
                        ? getPropertyValue(mavenProject, propertyName, false, false, false)
                        : getRootProjectProperty(mavenProject.getParent(), propertyName));
    }

    public String getRootProjectProperty(MavenProject mavenProject, String propertyName,
            boolean onlyInOriginalModel) {
        return mavenProject == null ? ""
                : (mavenProject.getParent() == null
                        ? getPropertyValue(mavenProject, propertyName, false, false, onlyInOriginalModel)
                        : getRootProjectProperty(mavenProject.getParent(), propertyName, onlyInOriginalModel));
    }

    public String getPropertyValue(String modelPropertyName, boolean propertyInRootProject,
            boolean onlyInOriginalModel, boolean lookInSettings) {
        String value = null;
        if (lookInSettings) {
            value = getPropertyValueInSettings(modelPropertyName, settings);
        }
        if (value == null) {
            if (propertyInRootProject) {
                value = getRootProjectProperty(project, modelPropertyName, onlyInOriginalModel);
            } else {
                value = getPropertyValue(modelPropertyName, onlyInOriginalModel);
            }
        }
        return value;
    }

    public String replaceProperties(String string) {
        if (string == null)
            return null;

        Matcher m = mavenPropertyPattern.matcher(string);

        StringBuffer sb = new StringBuffer();

        while (m.find()) {
            String propertyName = m.group(1);
            String propertyValue = getPropertyValue(propertyName);
            if (propertyValue != null) {
                m.appendReplacement(sb, Matcher.quoteReplacement(propertyValue));
            }
        }
        m.appendTail(sb);
        string = sb.toString();

        return string;
    }
    //

    /*
     * Maven
     */

    /**
     * The Maven project.
     */
    @Parameter(property = "project", required = true, readonly = true)
    private MavenProject project;

    protected MavenProject getProject() {
        return project;
    }

    public void setProject(MavenProject project) {
        this.project = project;
    }

    /**
     * The current Maven session.
     *
     */
    @Parameter(property = "session", required = true, readonly = true)
    protected MavenSession session;

    protected final MavenSession getSession() {
        return session;
    }

    public void setSession(MavenSession session) {
        this.session = session;
    }

    @Parameter(defaultValue = "${settings}", readonly = true)
    private Settings settings;

    public void setSettings(Settings settings) {
        this.settings = settings;
    }

    /**
     * The source enconding.
     */
    @Parameter(property = "project.build.sourceEncoding", required = true, readonly = true)
    protected String sourceEncoding;

    protected boolean isCurrentGoal(String goal) {
        return getSession().getRequest().getGoals().contains(goal);
    }

    /*
     * BW project
     */

    /**
     * This will check that the BW project specified in the POM project exists.
     * 
     * TODO : add additional checks about BW project integrity
     * 
     * @throws MojoExecutionException
     */
    private void checkBWProject() throws MojoExecutionException {
        if (projectDirectory == null) {
            projectNotFound();
        } else if (!projectDirectory.exists() || !projectDirectory.isDirectory()) {
            projectNotFound();
        }
    }

    /**
     * This will throw an error if the BW project is not found.
     * 
     * @throws MojoExecutionException
     */
    private void projectNotFound() throws MojoExecutionException {
        throw new MojoExecutionException(BWPROJECT_NOTFOUND, new FileNotFoundException());
    }

    /*
     * Dependencies
     */

    /**
     * 
     * @param bwEARDependency, a BW EAR dependency from Maven point-of-view
     * @return the name of the BW EAR artifact (without path but with
     * extension) : artifactId-version.ear
     */
    protected String getBWEARName(Dependency bwEARDependency) {
        assert (bwEARDependency != null);

        return bwEARDependency.getArtifactId() + "-" + bwEARDependency.getVersion() + BWEAR_EXTENSION;
    }

    /**
     * 
     * @param bwEARDependency, a BW EAR dependency from Maven point-of-view
     * @return groupId:artifactId:version:bw-ear of the BW EAR dependency
     */
    protected String getBWEARAlias(Dependency bwEARDependency) {
        assert (bwEARDependency != null);

        return bwEARDependency.getGroupId() + ":" + bwEARDependency.getArtifactId() + ":"
                + bwEARDependency.getVersion() + ":" + BWEAR_TYPE;
    }

    /**
     * 
     * @param jarDependency, a JAR dependency from Maven point-of-view
     * @param replaceDot, allows to replace dots in the version of the artifact
     * by underscores. This is because Maven will do so on the generated
     * artifact.
     * 
     * @return the name of the Jar artifact (without path but with extension) :
     * artifactId-version.jar
     */
    protected String getJarName(Dependency jarDependency, boolean replaceDot) {
        assert (jarDependency != null);
        String version = jarDependency.getVersion();
        if (replaceDot) {
            version = version.replace('.', '_');
        }

        return jarDependency.getArtifactId() + "-" + version + JAR_EXTENSION;
    }

    /**
     * 
     * @param jarDependency, a JAR dependency from Maven point-of-view
     * @param replaceDot, allows to replace dots in the version of the artifact
     * by underscores. This is because Maven will do so on the generated
     * artifact.
     * 
     * @return groupId:artifactId:version:jar of the JAR dependency
     */
    protected String getJarAlias(Dependency jarDependency, boolean replaceDot) {
        assert (jarDependency != null);
        String version = jarDependency.getVersion();
        if (replaceDot) {
            version = version.replace('.', '_');
        }

        return jarDependency.getGroupId() + ":" + jarDependency.getArtifactId() + ":" + version + ":" + JAR_TYPE;
    }

    /**
     * 
     * @param projlibDependency, a Projlib dependency from Maven point-of-view
     * @return the name of the Projlib artifact (without path but with
     * extension) : artifactId-version.projlib
     */
    protected String getProjlibName(Dependency projlibDependency) {
        assert (projlibDependency != null);

        return projlibDependency.getArtifactId() + "-" + projlibDependency.getVersion() + PROJLIB_EXTENSION;
    }

    /**
     * 
     * @param projlibDependency, a Projlib dependency from Maven point-of-view
     * @return groupId:artifactId:version:projlib of the Projlib dependency
     */
    protected String getProjlibAlias(Dependency projlibDependency) {
        assert (projlibDependency != null);

        return projlibDependency.getGroupId() + ":" + projlibDependency.getArtifactId() + ":"
                + projlibDependency.getVersion() + ":" + PROJLIB_TYPE;
    }

    /**
     * 
     * @param dependency, a dependency from Maven point-of-view, retrieved with
     * getJarName or getProjlibName
     * @return the absolute path of the dependency file (usually in "target/lib")
     */
    protected String getDependencyPath(String dependencyName) {
        return buildLibDirectory.getAbsolutePath().replace('\\', '/') + "/" + dependencyName;
    }

    /**
     * This will read the dependencies from the 'resolved' file found in the
     * build directory. This file was created earlier in the build by the
     * 'resolve-bw-dependencies' execution of the 'process-resources' phase.
     * 
     * @return The list of dependencies of type {@link dependencyType}
     * @throws IOException
     */
    protected List<Dependency> readDependenciesFromFile(String resolvedFileName, String dependencyType)
            throws IOException {
        List<Dependency> dependencies = new ArrayList<Dependency>();

        File resolvedFile = new File(resolvedFileName);
        if (!resolvedFile.exists()) {
            return dependencies;
        }

        FileInputStream fstream = new FileInputStream(resolvedFile);
        DataInputStream ds = new DataInputStream(fstream);
        BufferedReader br = new BufferedReader(new InputStreamReader(ds));
        Pattern p = Pattern.compile("   (.*):(.*):" + dependencyType + ":(.*):(.*)"); // keep only selected type (projlib or jar or bw-ear) dependencies
        String strLine;
        while ((strLine = br.readLine()) != null) {
            Matcher m = p.matcher(strLine);
            if (m.matches()) {
                getLog().debug(m.group(0));

                String groupId = m.group(1);
                String artifactId = m.group(2);
                String version = m.group(3);
                String scope = m.group(4);

                // create the dependency
                Dependency dependency = new Dependency();
                dependency.setGroupId(groupId);
                dependency.setArtifactId(artifactId);
                dependency.setVersion(version);
                dependency.setType(dependencyType);
                dependency.setScope(scope);

                dependencies.add(dependency);
            }
        }
        br.close();

        return dependencies;
    }

    /**
     * This will retrieve only the dependencies of type dependencyType defined
     * in POM project as /dependencies/dependency/type="dependencyType".
     * 
     * The list is retrieved from an external file generated by the
     * 'resolve-bw-dependencies' execution of the 'process-resources' phase.
     * 
     * @param dependencyType, the type of dependencies to retrieve
     * @param doIgnoreDependencies, specifies if the dependencies in the 
     * {@link dependenciesIgnored} must be ignored
     * @return
     * @throws IOException
     */
    protected List<Dependency> getDependencies(String dependencyType, boolean doIgnoreDependencies)
            throws IOException {
        ArrayList<Dependency> result = new ArrayList<Dependency>();
        List<Dependency> dependencies = readDependenciesFromFile(directory + "/resolved", dependencyType);

        for (Dependency dependency : dependencies) {
            if (doIgnoreDependencies) {
                if (dependenciesIgnored.indexOf(dependency.getGroupId() + ':' + dependency.getArtifactId()) >= 0) {
                    continue;
                }
            }
            if (dependency.getType().equals(dependencyType)) {
                result.add(dependency);
            }
        }

        return result;
    }

    /**
     * @return the list of BW EARs Dependencies from the POM project
     * @throws IOException 
     */
    protected List<Dependency> getBWEARsDependencies() throws IOException {
        return getDependencies(BWEAR_TYPE, false);
    }

    /**
     * @return the list of Projlib Dependencies from the POM project
     * @throws IOException 
     */
    protected List<Dependency> getProjlibsDependencies() throws IOException {
        return getDependencies(PROJLIB_TYPE, false);
    }

    /**
     * @return the list of Jar Dependencies from the POM project
     * @throws IOException 
     */
    protected List<Dependency> getJarDependencies() throws IOException {
        return getDependencies(JAR_TYPE, true);
    }

    /**
     * Some configuration file must escape the ':' character of Maven
     * dependency coordinates syntax.
     * 
     * @param alias
     * @return alias but with '\\:' replaced by ':'
     */
    private String formatAlias(String alias) {
        if (alias != null) {
            return alias.replace(":", "\\:");
        }
        return null;
    }

    public File getAliasesFile() {
        return new File(directory, ALIASES_FILE);
    }

    /**
     * This will create an aliases file ('aliases.properties') that can be
     * provided to 'buildear' for instance to specify the JAR aliases.
     * It seems that the JAR aliases are not recognized by 'buildear' from
     * 'Designer5.prefs' whereas they are by TIBCO Designer.
     * 
     * @throws IOException
     */
    private void copyAliasesFile() throws IOException {
        // Create the aliases.properties in the target folder
        File aliasesFile = getAliasesFile();
        FileWriter file = null;
        BufferedWriter buffer = null;
        PrintWriter aliasesFileOut = null;
        try {
            file = new FileWriter(aliasesFile);
            buffer = new BufferedWriter(file);
            aliasesFileOut = new PrintWriter(buffer);

            // Copy all of the required aliases for the project, and map them with their path in Designer5.prefs
            for (Dependency dependency : getJarDependencies()) {
                String jarName = getJarName(dependency, false);
                String jarAlias = formatAlias(getJarAlias(dependency, false));
                String jarPath = getDependencyPath(jarName);

                if (new File(jarPath).exists()) {
                    aliasesFileOut.println(TIBCO_ALIAS_PREFIX + jarAlias + "=" + jarPath);
                }
            }

            for (Dependency dependency : getProjlibsDependencies()) {
                String projlibName = getProjlibName(dependency);
                String projlibAlias = formatAlias(getProjlibAlias(dependency));
                String projlibPath = getDependencyPath(projlibName);

                aliasesFileOut.println(TIBCO_ALIAS_PREFIX + projlibAlias + "=" + projlibPath);
            }
        } finally {
            aliasesFileOut.close();
            buffer.close();
            file.close();
        }
    }

    protected File getDesigner5Prefs() throws IOException {
        // create a ".TIBCO" user_dir-like in build directory
        File homeTIBCO = new File(directory, TIBCO_HOME_DIR);
        if (!homeTIBCO.exists()) {
            homeTIBCO.mkdir();
        }

        File designer5Prefs = new File(homeTIBCO, DESIGNER5_PREFS);

        return designer5Prefs;
    }

    /**
     * This will create the 'Designer5.prefs' file in 'target/.TIBCO' which will
     * override the platform TIBCO_HOME directory.
     * 
     * The content of the 'Designer5.prefs' will be a copy of the original
     * 'Designer5.prefs' file found on the current system (in the user home dir)
     * However all the references to alias files will be removed and replaced by
     * the actual alias files referencing Maven artifacts
     * 
     * @throws IOException
     */
    private void copyDesigner5Prefs() throws IOException {
        File designer5Prefs = getDesigner5Prefs();

        // copy system 'Designer5.prefs' to this ".TIBCO" directory
        File systemDesigner5Prefs = new File(
                System.getProperty("user.home") + "/" + TIBCO_HOME_DIR + "/" + DESIGNER5_PREFS);
        getLog().debug(DESIGNER5_PREFS + " : " + systemDesigner5Prefs.getAbsolutePath());
        if (systemDesigner5Prefs.exists()) {
            copyFile(systemDesigner5Prefs, designer5Prefs);
        } else {
            designer5Prefs.createNewFile();
        }

        // remove file aliases
        ReplaceRegExp replaceRegExp = new ReplaceRegExp();
        replaceRegExp.setFile(designer5Prefs);
        replaceRegExp.setMatch(FILE_ALIAS_PREFIX_ESCAPED + "([0-9]*)=(.*)");
        replaceRegExp.setReplace("");
        replaceRegExp.setByLine(true);
        replaceRegExp.execute();

        // replace with actual file aliases (which are Maven artifacts)
        FileWriter file = null;
        BufferedWriter buffer = null;
        PrintWriter designer5PrefsOut = null;
        try {
            file = new FileWriter(designer5Prefs, true);
            buffer = new BufferedWriter(file);
            designer5PrefsOut = new PrintWriter(buffer);
            designer5PrefsOut.println("");

            // first the Projlibs aliases
            int i = 0;
            if (!hideLibraryResources) { // implements the "Hide Library Resources" of TIBCO Designer

                for (Dependency dependency : getProjlibsDependencies()) {
                    String projlibName = getProjlibName(dependency);
                    String projlibAlias = getProjlibAlias(dependency);
                    String projlibPath = getDependencyPath(projlibName);
                    designer5PrefsOut.println(FILE_ALIAS_PREFIX + (i++) + "=" + projlibAlias + "\\=" + projlibPath);
                }
            }

            // then the Jar aliases
            for (Dependency dependency : getJarDependencies()) {
                String jarName = getJarName(dependency, false);
                String jarAlias = getJarAlias(dependency, false);
                String jarPath = getDependencyPath(jarName);
                // String jarNameUnderscored = getJarName(dependency, true);
                designer5PrefsOut.println(FILE_ALIAS_PREFIX + (i++) + "=" + jarAlias + "\\=" + jarPath);
            }
        } finally {
            designer5PrefsOut.close();
            buffer.close();
            file.close();
        }
    }

    /**
     * This will create the '.designtimelibs' file in {@link buildSrcDirectory}
     * which is basically the path to the temporary BusinessWorks project being
     * built.
     * 
     * The content of the '.designtimelibs' will be the Projlib dependencies of
     * the project, defined in the POM project and resolved with classic Maven
     * mechanism.
     * 
     * @throws IOException
     */
    private void copyDesignTimeLibs() throws IOException {
        File designTimeLibs = new File(buildSrcDirectory + "/" + DTL_FILE_NAME);
        getLog().debug(DTL_FILE_NAME + " : " + buildSrcDirectory + "/" + DTL_FILE_NAME);

        if (!designTimeLibs.exists()) {
            if (buildSrcDirectory.exists()) {
                designTimeLibs.createNewFile();
            } else {
                return;
            }
        }

        FileWriter file = null;
        BufferedWriter buffer = null;
        PrintWriter designTimeLibsOut = null;
        try {
            file = new FileWriter(designTimeLibs, false);
            buffer = new BufferedWriter(file);
            designTimeLibsOut = new PrintWriter(buffer);

            int i = getProjlibsDependencies().size();
            if (!hideLibraryResources) {
                for (Dependency dependency : getProjlibsDependencies()) {
                    //String projlibName = getProjlibName(dependency);
                    String projlibAlias = getProjlibAlias(dependency);
                    designTimeLibsOut.println((--i) + "=" + projlibAlias + "\\=");
                }
            }
        } finally {
            designTimeLibsOut.close();
            buffer.close();
            file.close();
        }
    }

    /*
     * Execution
     */

    /**
     * This calls a TIBCO binary.
     * 
     * @param binary, the TIBCO binary file to execute
     * @param tras, the TRA files associated with the TIBCO binary
     * @param arguments, command-line arguments
     * @param workingDir, working directory from where the binary is launched
     * @param errorMsg, error message to display in case of a failure
     * @param fork, if true the chiild process will be detached from the caller
     * 
     * @throws IOException
     * @throws MojoExecutionException
     */
    protected int launchTIBCOBinary(File binary, List<File> tras, ArrayList<String> arguments, File workingDir,
            String errorMsg, boolean fork, boolean synchronous) throws IOException, MojoExecutionException {
        Integer result = 0;

        if (tras == null) { // no value specified as Mojo parameter, we use the .tra in the same directory as the binary
            String traPathFileName = binary.getAbsolutePath();
            traPathFileName = FilenameUtils.removeExtension(traPathFileName);
            traPathFileName += ".tra";
            tras = new ArrayList<File>();
            tras.add(new File(traPathFileName));
        }

        HashMap<File, File> trasMap = new HashMap<File, File>();
        for (File tra : tras) {
            // copy of ".tra" file in the working directory
            File tmpTRAFile = new File(directory, tra.getName());
            trasMap.put(tra, tmpTRAFile);
            copyFile(tra, tmpTRAFile);
        }

        for (File tra : trasMap.keySet()) {
            if (trasMap.containsKey(tibcoDesignerTRAPath)
                    && ((tibcoBuildEARUseDesignerTRA && tra == tibcoBuildEARTRAPath)
                            || (tibcoBuildLibraryUseDesignerTRA && tra == tibcoBuildLibraryTRAPath))) {
                if (tras.size() > 1) {
                    ReplaceRegExp replaceRegExp = new ReplaceRegExp();
                    replaceRegExp.setFile(trasMap.get(tra));
                    replaceRegExp.setMatch("tibco.include.tra (.*/designer.tra)");
                    replaceRegExp.setReplace(
                            "tibco.include.tra " + trasMap.get(tibcoDesignerTRAPath).toString().replace('\\', '/'));
                    replaceRegExp.setByLine(true);

                    replaceRegExp.execute();
                }
            }

            if (tra == tibcoBuildEARTRAPath || tra == tibcoDesignerTRAPath || tra == tibcoBWEngineTRAPath) { // FIXME: should check more properly
                // append user.home at the end to force the use of custom Designer5.prefs
                PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(trasMap.get(tra), true)));
                out.println("");
                out.println("java.property.user.home=" + directory.getAbsolutePath().replace("\\", "/"));
                out.close();
            }
        }

        CommandLine cmdLine = new CommandLine(binary);

        for (String argument : arguments) {
            cmdLine.addArgument(argument);
        }
        getLog().debug("launchTIBCOBinary command line : " + cmdLine.toString());
        getLog().debug("working dir : " + workingDir);

        DefaultExecutor executor = new DefaultExecutor();
        executor.setWorkingDirectory(workingDir);

        if (timeOut > 0) {
            ExecuteWatchdog watchdog = new ExecuteWatchdog(timeOut * 1000);
            executor.setWatchdog(watchdog);
        }

        executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());

        ByteArrayOutputStream stdOutAndErr = new ByteArrayOutputStream();
        executor.setStreamHandler(new PumpStreamHandler(stdOutAndErr));

        if (fork) {
            CommandLauncher commandLauncher = CommandLauncherFactory.createVMLauncher();
            commandLauncher.exec(cmdLine, null, workingDir);
        } else {
            try {
                if (synchronous) {
                    result = executor.execute(cmdLine);
                } else {
                    executor.execute(cmdLine, new DefaultExecuteResultHandler());
                }
            } catch (ExecuteException e) {
                // TODO : grer erreurs des excutables (ventuellement parser les erreurs classiques)
                getLog().info(cmdLine.toString());
                getLog().info(stdOutAndErr.toString());
                getLog().info(result.toString());
                throw new MojoExecutionException(errorMsg, e);
            } catch (IOException e) {
                throw new MojoExecutionException(e.getMessage(), e);
            }
        }

        return result;
    }

    /**
     * Same as launchTIBCOBinary with 'fork=false' and 'synchronous=true'
     */
    protected void launchTIBCOBinary(File binary, List<File> tras, ArrayList<String> arguments, File workingDir,
            String errorMsg) throws IOException, MojoExecutionException {
        launchTIBCOBinary(binary, tras, arguments, workingDir, errorMsg, false, true);
    }

    protected void enableTestScope() {
        directory = testDirectory; // set directory to "target/test" instead of "target"
        buildSrcDirectory = testSrcDirectory; // set buildSrcDirectory to "target/test/src" instead of "target/src"
        buildLibDirectory = testLibDirectory; // set buildLibDirectory to "target/test/lib" instead of "target/lib"
    }

    /**
     * <p>
     * The execute method of this Mojo will :
     * <ul>
     * <li>check that the BusinessWorks project exists</li>
     * <li>copy the Designer5.prefs file</li>
     * <li>copy the .designtimelibs file useful for setting a TIBCO environment
     * for the project being built.</li>
     * </ul>
     * </p>
     */
    public void execute() throws MojoExecutionException {
        if (dependenciesIgnored == null) {
            dependenciesIgnored = new ArrayList<String>();
        }

        if (!directory.exists()) {
            directory.mkdirs();
        }

        checkBWProject();
        try {
            copyAliasesFile();
            copyDesigner5Prefs();
            copyDesignTimeLibs();
        } catch (IOException e) {
            e.printStackTrace(); // FIXME : remove printStackTrace()
        }
    }

}