/**
 * (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;

/**
 *
 *

 * 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 :
 *

 *
checking the BW project exists (TODO : and is correct)

 *
creating a Designer5.prefs with actual file aliases referencing other
 * artifacts handled by Maven

 *
creating a .designtimelibs referencing the dependencies declared in the
 * POM project

 *

 *
 * The concrete children classes will call the launchTIBCOBinary with specific
 * arguments (use 'buildear' or 'designer' or 'buildlibrary' or ...)
 *

 *
 * @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

	/**
	 *

	 * Instantiate a minimalistic {@link AbstractCommonMojo} to use properties
	 * management as a standalone object.
	 *

	 *
	 * @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 : gérer erreurs des exécutables (é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"
	}

	/**
	 *

	 * The execute method of this Mojo will :
	 *

	 *
check that the BusinessWorks project exists

	 *
copy the Designer5.prefs file

	 *
copy the .designtimelibs file useful for setting a TIBCO environment
	 * for the project being built.

	 *

	 *

	 */
	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()
		}
	}

}


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 in JDK 6
// See http://java.sun.com/xml/jaxb
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2014.01.23 at 10:11:20 AM CET
//

package com.tibco.xmlns.applicationmanagement;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
 *
Java class for anonymous complex type.
 *
 *
The following schema fragment specifies the expected content contained within this class.
 *
 *

 * <complexType>
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element ref="{http://www.tibco.com/xmlns/ApplicationManagement}action" maxOccurs="unbounded" minOccurs="0"/>
 *       </sequence>
 *     </restriction>
 *   </complexContent>
 * </complexType>
 *

 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "action"
})
@XmlRootElement(name = "actions")
public class Actions {

    @XmlElementRef(name = "action", namespace = "http://www.tibco.com/xmlns/ApplicationManagement", type = JAXBElement.class)
    protected List<JAXBElement<? extends ActionType>> action;

    /**
     * Gets the value of the action property.
     *
     *

     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a set method for the action property.
     *
     *

     * For example, to add a new item, do as follows:
     *

     *    getAction().add(newItem);
     *

     *
     *
     *

     * Objects of the following type(s) are allowed in the list
     * {@link JAXBElement }{@code <}{@link AlertAction }{@code >}
     * {@link JAXBElement }{@code <}{@link ActionType }{@code >}
     * {@link JAXBElement }{@code <}{@link CustomAction }{@code >}
     * {@link JAXBElement }{@code <}{@link EmailAction }{@code >}
     *
     *
     */
    public List<JAXBElement<? extends ActionType>> getAction() {
        if (action == null) {
            action = new ArrayList<JAXBElement<? extends ActionType>>();
        }
        return this.action;
    }

}


/**
 * (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.compile;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProjectHelper;

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

/**
 *

 * This goal creates a TIBCO BusinessWorks EAR based on the configuration given in the project's POM.
 *

 *

 * The most important parameters are:
 *

 *

 *
bw.project.archive.builder : the location of the Enterprise Archive file inside the TIBCO BusinessWorks project

 *
bw.project.location : the TIBCO BusinessWorks sources location relatively to the POM

 *

 *

 * Other parameters have default values set with the global values of "settings.xml"
 * file.
 *

 *
 * @author Mathieu Debove
 *
 */
@Mojo(name = "compile-bw-ear",
        defaultPhase = LifecyclePhase.COMPILE)
public class CompileEARMojo extends AbstractBWArtifactMojo {

    protected final static String BUILDEAR_BINARY_NOTFOUND = "The TIBCO 'buildear' binary can't be found.";
    protected final static String BUILD_EAR_FAILED = "The build of the EAR file has failed.";
    protected final static String COPY_EAR_FAILED = "Unable to copy the EAR to the package directory.";
    protected final static String BUILDING_EAR = "Building the EAR...";
    protected final static String EAR_LOCATION = "Output EAR location: ";

    /**
     * Path to the Archive descriptor relatively to the BusinessWorks project
     * path.
     */
    @Parameter(property = "bw.project.archive.builder", required = true)
    protected String archiveFile;

    /**
     * Path to the TIBCO "buildear" binary.
     */
    @Parameter(property = "buildear.path", required = true)
    protected File tibcoBuildEARPath;

    /**
     *

     * Whether to validate or not the EAR before building
     * (-v switch of buildear).
     *

     */
    @Parameter(property = "buildear.validate", defaultValue = "true")
    protected Boolean tibcoBuildEARValidation;

    /**
     * Allow to delete override variables from projlib
     */
    @Parameter(property = "bw.clean.projlib.defaultVars")
    private boolean cleanDefaultVars;

    @Component
    private MavenProjectHelper projectHelper;

    private void checkBuildEAR() throws MojoExecutionException {
        if (tibcoBuildEARPath == null ||
                !tibcoBuildEARPath.exists() ||
                !tibcoBuildEARPath.isFile()) {
            tibcoBuildEARPathNotFound();
        }
    }

    private void tibcoBuildEARPathNotFound() throws MojoExecutionException {
        throw new BinaryMissingException(BUILDEAR_BINARY_NOTFOUND);
    }

    protected String getArtifactFileExtension() {
        return BWEAR_EXTENSION;
    }

    /**
     * This calls the "buildear" binary found in TIBCO TRA to build an EAR for
     * the {@link AbstractBWMojo#project}, defined by the {@link CompileEARMojo#archiveFile}.
     *
     * @param outputFile
     *            , the path where the EAR output will be created
     * @throws MojoExecutionException
     * @throws IOException
     */
    private void buildEAR(File outputFile) throws MojoExecutionException, IOException {
        checkBuildEAR();
        // assert (outputFile != null);

        ArrayList<String> arguments = new ArrayList<String>();
        arguments.add("-ear"); // path of the Enterprise Archive "builder" relative to the BW project path
        arguments.add(archiveFile);
        arguments.add("-p"); // BW project path
        arguments.add(buildSrcDirectory.getAbsolutePath());
        arguments.add("-o"); // output file
        arguments.add(outputFile.getAbsolutePath());
        arguments.add("-x"); // overwrite the output
        if (tibcoBuildEARValidation) {
            arguments.add("-v"); // validate the project
        }
        File aliasesFile = new File(directory, ALIASES_FILE);
        if (aliasesFile.exists()) {
            arguments.add("-a");
            arguments.add(aliasesFile.getAbsolutePath());
        }
        getLog().info(BUILDING_EAR);

        ArrayList<File> tras = new ArrayList<File>();
        tras.add(tibcoBuildEARTRAPath);
        if (tibcoBuildEARUseDesignerTRA) {
            tras.add(tibcoDesignerTRAPath);
        }
        launchTIBCOBinary(tibcoBuildEARPath, tras, arguments, directory, BUILD_EAR_FAILED);
    }

    /**
     * Delete "default vars" directory of override projlib's variables
     *
     * @throws IOException
     */
    private void cleanDefaultVars() throws IOException {
        File varsDirectory = new File(buildSrcDirectory, "defaultVars");
        cleanVarsDirectory(varsDirectory, getProjlibsDependencies());
    }

    protected void cleanVarsDirectory(File varsDirectory, List<Dependency> projlibList) {
        File[] listFiles = varsDirectory.listFiles();
        ArrayList<String> projlibNames = new ArrayList<String>();
        for (Dependency dependency : projlibList) {
            projlibNames.add(dependency.getArtifactId());
        }
        for (File dir : listFiles) {
            if (projlibNames.contains(dir.getName()) && dir.isDirectory()) {
                for (File defaultVarsFile : dir.listFiles()) {
                    cleanDirectory(defaultVarsFile);
                }
                dir.delete();
            }
        }
    }

    /**
     * Delete all file of a directory and then delete this directory
     *
     * @param directory
     * @return
     */
    protected boolean cleanDirectory(File directory) {
        if (directory.isDirectory() && directory.listFiles().length != 0) {
            for (File file : directory.listFiles()) {
                if (file.isFile()) {
                    file.delete();
                }
            }
        }
        return directory.delete();
    }

    private void doCleanDefaultVars() throws MojoExecutionException {
        if (cleanDefaultVars) {
            try {
                cleanDefaultVars();
            } catch (IOException e) {
                throw new MojoExecutionException(BUILD_EAR_FAILED, e);
            }
        }
    }

    public void execute() throws MojoExecutionException {
    	if (skipCompile || skipEARCompile) {
    		getLog().info(SKIPPING);

            File outputFile = getOutputFile();
           	if (outputFile != null && !outputFile.exists() && touchEARIfSkipped) {
           		// EAR was not created because compilation is skipped
           		// however we "touch" the EAR file so there is an empty EAR file created
           		try {
           			outputFile.getParentFile().mkdirs();
					outputFile.createNewFile();
				} catch (IOException e) {
					throw new MojoExecutionException(e.getLocalizedMessage(), e);
				}
           	}
            if (outputFile != null && outputFile.exists()) {
            	attachArtifact(outputFile);
            } else {
            	getLog().warn(WARN_NO_ARTIFACT_ATTACHED);
            }
    		return;
    	}

        if (isCurrentGoal("bw:launch-designer")) {
            return; // ignore
        }

        doCleanDefaultVars();

        super.execute();
        checkOutputDirectory();
        File outputFile = getOutputFile();
        getLog().debug(EAR_LOCATION + outputFile.getAbsolutePath());

        try {
            buildEAR(outputFile);
        } catch (IOException e) {
            throw new MojoExecutionException(BUILD_EAR_FAILED, e);
        }

        try {
    		if (!packageDirectory.exists()) {
    			packageDirectory.mkdirs();
    		}
			FileUtils.copyFile(outputFile, new File(packageDirectory + File.separator + outputFile.getName()));
		} catch (IOException e) {
			throw new MojoExecutionException(COPY_EAR_FAILED, e);
		}

        attachArtifact(outputFile);
    }

    private void attachArtifact(File outputFile) {
        if (classifier != null) {
            projectHelper.attachArtifact(getProject(), BWEAR_TYPE, classifier, outputFile);
        } else {
            getProject().getArtifact().setFile(outputFile);
        }
    }

}


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// See http://java.sun.com/xml/jaxb
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2014.06.02 at 04:25:47 PM CEST
//

package org.w3._2001.xmlschema;

import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

/**
 *
Java class for simpleType complex type.
 *
 *
The following schema fragment specifies the expected content contained within this class.
 *
 *

 * <complexType name="simpleType">
 *   <complexContent>
 *     <extension base="{http://www.w3.org/2001/XMLSchema}annotated">
 *       <group ref="{http://www.w3.org/2001/XMLSchema}simpleDerivation"/>
 *       <attribute name="final" type="{http://www.w3.org/2001/XMLSchema}simpleDerivationSet" />
 *       <attribute name="name" type="{http://www.w3.org/2001/XMLSchema}NCName" />
 *       <anyAttribute processContents='lax' namespace='##other'/>
 *     </extension>
 *   </complexContent>
 * </complexType>
 *

 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "simpleType", propOrder = {
    "restriction",
    "list",
    "union"
})
@XmlSeeAlso({
    TopLevelSimpleType.class,
    LocalSimpleType.class
})
public abstract class SimpleType
    extends Annotated
{

    protected Restriction restriction;
    protected org.w3._2001.xmlschema.List list;
    protected Union union;
    @XmlAttribute(name = "final")
    @XmlSchemaType(name = "simpleDerivationSet")
    protected java.util.List<String> _final;
    @XmlAttribute(name = "name")
    @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
    @XmlSchemaType(name = "NCName")
    protected String name;

    /**
     * Gets the value of the restriction property.
     *
     * @return
     *     possible object is
     *     {@link Restriction }
     *
     */
    public Restriction getRestriction() {
        return restriction;
    }

    /**
     * Sets the value of the restriction property.
     *
     * @param value
     *     allowed object is
     *     {@link Restriction }
     *
     */
    public void setRestriction(Restriction value) {
        this.restriction = value;
    }

    /**
     * Gets the value of the list property.
     *
     * @return
     *     possible object is
     *     {@link org.w3._2001.xmlschema.List }
     *
     */
    public org.w3._2001.xmlschema.List getList() {
        return list;
    }

    /**
     * Sets the value of the list property.
     *
     * @param value
     *     allowed object is
     *     {@link org.w3._2001.xmlschema.List }
     *
     */
    public void setList(org.w3._2001.xmlschema.List value) {
        this.list = value;
    }

    /**
     * Gets the value of the union property.
     *
     * @return
     *     possible object is
     *     {@link Union }
     *
     */
    public Union getUnion() {
        return union;
    }

    /**
     * Sets the value of the union property.
     *
     * @param value
     *     allowed object is
     *     {@link Union }
     *
     */
    public void setUnion(Union value) {
        this.union = value;
    }

    /**
     * Gets the value of the final property.
     *
     *

     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a set method for the final property.
     *
     *

     * For example, to add a new item, do as follows:
     *

     *    getFinal().add(newItem);
     *

     *
     *
     *

     * Objects of the following type(s) are allowed in the list
     * {@link String }
     *
     *
     */
    public java.util.List<String> getFinal() {
        if (_final == null) {
            _final = new ArrayList<String>();
        }
        return this._final;
    }

    /**
     * Gets the value of the name property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *
     */
    public String getName() {
        return name;
    }

    /**
     * Sets the value of the name property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *
     */
    public void setName(String value) {
        this.name = value;
    }

}


/**
 * (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.source;

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import com.tibco.xmlns.bw.process._2003.ObjectFactory;
import com.tibco.xmlns.bw.process._2003.ProcessDefinition;

/**
 *

 * This class will

 *
unmarshall XML file of the schema with
 * "http://xmlns.tibco.com/bw/process/2003" namespace to JAXB objects.
 *

 *

 * marshall the {@link ProcessDefinition} object back to XML file with the same
 * schema.
 *

 *

 *

 *
 * @author Mathieu Debove
 *
 */
public class ProcessModel {
//	private static final String PROCESS_MODEL_NAMESPACE = "http://xmlns.tibco.com/bw/process/2003";

	private ProcessDefinition process;
	public ProcessDefinition getProcess() {
		return process;
	}

	private JAXBContext jaxbContext;
	private File xmlFile;

	public ProcessModel(File xmlFile) throws JAXBException {
		this.xmlFile = xmlFile;
		initProcessModel();
	}

	/**
	 *

	 * This will initialize the {@link ProcessDefinition} object which is a JAXB
	 * representation of the "ProcessDefinition" root-element of TIBCO processes
	 * files using the schema with "http://xmlns.tibco.com/bw/process/2003"
	 * namespace.
	 *

	 *
	 * @throws JAXBException
	 */
	private void initProcessModel() throws JAXBException {
		jaxbContext = JAXBContext.newInstance(ObjectFactory.class);

		Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
		Object o =  jaxbUnmarshaller.unmarshal(xmlFile);
		this.process = (ProcessDefinition) o;
	}

	/**
	 *

	 * This will marshall the object back to the XML file.
	 *

	 *
	 * @throws JAXBException
	 */
	public void save() throws JAXBException {
		Marshaller m = jaxbContext.createMarshaller();
		m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		m.marshal(process, xmlFile);
	}
}


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 in JDK 6
// See http://java.sun.com/xml/jaxb
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2014.01.23 at 10:11:20 AM CET
//

package com.tibco.xmlns.applicationmanagement;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
 *
Java class for anonymous complex type.
 *
 *
The following schema fragment specifies the expected content contained within this class.
 *
 *

 * <complexType>
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="rulebase" maxOccurs="unbounded" minOccurs="0">
 *           <complexType>
 *             <complexContent>
 *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *                 <sequence>
 *                   <element name="uri" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *                   <element name="data" type="{http://www.w3.org/2001/XMLSchema}base64Binary" minOccurs="0"/>
 *                 </sequence>
 *               </restriction>
 *             </complexContent>
 *           </complexType>
 *         </element>
 *       </sequence>
 *     </restriction>
 *   </complexContent>
 * </complexType>
 *

 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "rulebase"
})
@XmlRootElement(name = "rulebases")
public class Rulebases {

    protected List<Rulebases.Rulebase> rulebase;

    /**
     * Gets the value of the rulebase property.
     *
     *

     * This accessor method returns a reference to the live list,
     * not a snapshot. Therefore any modification you make to the
     * returned list will be present inside the JAXB object.
     * This is why there is not a set method for the rulebase property.
     *
     *

     * For example, to add a new item, do as follows:
     *

     *    getRulebase().add(newItem);
     *

     *
     *
     *

     * Objects of the following type(s) are allowed in the list
     * {@link Rulebases.Rulebase }
     *
     *
     */
    public List<Rulebases.Rulebase> getRulebase() {
        if (rulebase == null) {
            rulebase = new ArrayList<Rulebases.Rulebase>();
        }
        return this.rulebase;
    }

    /**
     *
Java class for anonymous complex type.
     *
     *
The following schema fragment specifies the expected content contained within this class.
     *
     *

     * <complexType>
     *   <complexContent>
     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
     *       <sequence>
     *         <element name="uri" type="{http://www.w3.org/2001/XMLSchema}string"/>
     *         <element name="data" type="{http://www.w3.org/2001/XMLSchema}base64Binary" minOccurs="0"/>
     *       </sequence>
     *     </restriction>
     *   </complexContent>
     * </complexType>
     *

     *
     *
     */
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
        "uri",
        "data"
    })
    public static class Rulebase {

        @XmlElement(required = true)
        protected String uri;
        protected byte[] data;

        /**
         * Gets the value of the uri property.
         *
         * @return
         *     possible object is
         *     {@link String }
         *
         */
        public String getUri() {
            return uri;
        }

        /**
         * Sets the value of the uri property.
         *
         * @param value
         *     allowed object is
         *     {@link String }
         *
         */
        public void setUri(String value) {
            this.uri = value;
        }

        /**
         * Gets the value of the data property.
         *
         * @return
         *     possible object is
         *     byte[]
         */
        public byte[] getData() {
            return data;
        }

        /**
         * Sets the value of the data property.
         *
         * @param value
         *     allowed object is
         *     byte[]
         */
        public void setData(byte[] value) {
            this.data = ((byte[]) value);
        }

    }

}


/**
 * (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.deployment;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBException;

import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

import COM.TIBCO.hawk.console.hawkeye.ConsoleInitializationException;
import COM.TIBCO.hawk.talon.MicroAgentData;
import COM.TIBCO.hawk.talon.MicroAgentException;
import COM.TIBCO.hawk.talon.Subscription;
import fr.fastconnect.factory.tibco.bw.maven.hawk.DefaultSubscriptionHandler;
import fr.fastconnect.factory.tibco.bw.maven.hawk.MethodSubscriber;
import fr.fastconnect.factory.tibco.bw.maven.hawk.MicroAgent;
import fr.fastconnect.factory.tibco.bw.maven.packaging.ApplicationManagement;

/**
 *
 *

 * This goal starts a TIBCO BusinessWorks application deployed on a TIBCO domain.
 *

 *
 * @author Mathieu Debove
 *
 */
@Mojo( name="start-bw",
defaultPhase=LifecyclePhase.DEPLOY ) // FIXME: should be deployEAR
public class StartEARMojo extends AbstractBWDeployMojo {

	protected final static String STARTING_INSTANCES_FAILED = "Some instances failed to be started.";
	protected final static String STARTING_INSTANCES = "Starting instances of the application...";
	protected final static String WAITING_FOR_INSTANCES = "Waiting for instances to be started...";
	protected final static String SUBSCRIBING_TO_HAWK_METHODS = "Subscribing to Hawk methods:";
	protected final static String ALL_INSTANCES_STARTED = "All instances successfully started.";
	protected final static String SOME_INSTANCES_NOT_STARTED = "Some instances failed to be started (or timeout was reached).";

	/**
	 * Whether to wait for instances to be started or not.
	 *

	 * The waiting process is performed with Hawk methods subscriptions.
	 *

	 * NB: it is mandatory to configure Hawk properly.
	 */
	@Parameter (property="waitForRunningInstances", defaultValue = "false")
	private Boolean waitForRunningInstances;

	/**
	 * Whether to fail the Maven build when timeout is reached or not.
	 */
	@Parameter (property="failWhenTimeoutReached", defaultValue = "false")
	private Boolean failWhenTimeoutReached;

	@Parameter (property="bw.start.running.statuses.number", defaultValue = "3")
	private int numberOfRunningStatuses;
	private MicroAgent microAgent;

	private static class RunningInstanceSubscriptionHandler extends DefaultSubscriptionHandler {
		private Log logger;
		private MethodSubscriber methodSubscriber;

		private enum StatusHistory {
			STOPPED, INITIALIZING, RUNNING
		}
		private Map<Integer, StatusHistory> statusesHistory;
		private int numberOfRunningStatuses;

		public RunningInstanceSubscriptionHandler(Log log, MethodSubscriber methodSubscriber, int numberOfRunningStatuses) {
			this.logger = log;
			this.methodSubscriber = methodSubscriber;
			this.statusesHistory = new LinkedHashMap<Integer, StatusHistory>();
			if (numberOfRunningStatuses <= 0) {
				numberOfRunningStatuses = 1;
			}
			this.numberOfRunningStatuses = numberOfRunningStatuses;
		}

		protected Log getLog() {
			return logger;
		}

		@Override
		public Object getResult() {
			return result;
		}

		@Override
		public void onSubscribe() {
			String instance =
			methodSubscriber.getArguments().get(0).getValue() +
			"-" +
			methodSubscriber.getArguments().get(1).getValue();

			Integer timeout = methodSubscriber.getInterval() * methodSubscriber.getNumberOfRetry() / 1000;
			getLog().info("Subscription to the Hawk method started for instance '" + instance + "' (timeout is set to " + timeout + (timeout > 1 ? " seconds" : " second") + ").");
		}

		@Override
		public void onData(Subscription s, MicroAgentData mad) {
			super.onData(s, mad);
			String data = formatData(mad.getData());
			getLog().debug(data);

			if (data != null && data.contains("STOPPED")) {
				statusesHistory.put(statusesHistory.size(), StatusHistory.STOPPED);
			} else	if (data != null && data.contains("INITIALIZING")) {
				statusesHistory.put(statusesHistory.size(), StatusHistory.INITIALIZING);
			} else	if (data != null && data.contains("RUNNING")) {
				statusesHistory.put(statusesHistory.size(), StatusHistory.RUNNING);
			}

			getLog().debug(statusesHistory.toString());
			if (failedToStart()) {
				s.cancel();
				result = "STOPPED";
				decreaseReferenceCount();
			} else if (isStarted()) {
				s.cancel();
				result = "RUNNING";
				decreaseReferenceCount();
				getLog().debug("A subscription was canceled");
				getLog().debug(numberOfSubscriptions.toString());
				getLog().debug(RunningInstanceSubscriptionHandler.numberOfSubscriptions.toString());
			}
		}

		private Integer getLastKeyForValue(Map<Integer, StatusHistory> map, StatusHistory value) {
			Integer result = -1;

			for (Integer key : map.keySet()) {
				if (value.equals(map.get(key))) {
					result = key;
				}

			}
			return result;
		}

		private boolean failedToStart() {
			if (statusesHistory.containsValue(StatusHistory.RUNNING) &&
				statusesHistory.containsValue(StatusHistory.STOPPED)) {
					Integer stoppedIndex = getLastKeyForValue(statusesHistory, StatusHistory.STOPPED);
					getLog().debug(stoppedIndex.toString());
					Integer runningIndex = getLastKeyForValue(statusesHistory, StatusHistory.RUNNING);
					getLog().debug(runningIndex.toString());
					return stoppedIndex > runningIndex;
				}
			return false;
		}

		private boolean isStarted() {
			return Collections.frequency(statusesHistory.values(), StatusHistory.RUNNING) > numberOfRunningStatuses;
		}

		@Override
		public void decreaseReferenceCount() {
			super.decreaseReferenceCount();
			getLog().debug(numberOfSubscriptions.toString());
		}

		@Override
		public void increaseReferenceCount() {
			super.increaseReferenceCount();
			getLog().debug(numberOfSubscriptions.toString());
		}

		@Override
		public void onError(Subscription s, MicroAgentException e) {
			getLog().debug("A subscription caused an error");
		}

		@Override
		public void onErrorCleared(Subscription s) {
			getLog().debug("A subscription cleared an error");
		}

		@Override
		public void onTermination(Subscription s, MicroAgentException e) {
			getLog().debug("A subscription terminated");
		}

	}

	/**
	 * This methods subscribe to the Hawk method
	 * "COM.TIBCO.admin.TRA:getComponentInstanceStatus" for all
	 * {@code instances} being started.
	 *
	 * @param instances
	 * @return a list of {@link MethodSubscriber} objects
	 * @throws ConsoleInitializationException
	 * @throws MicroAgentException
	 */
	private List<MethodSubscriber> monitorInstances(List<ImmutablePair<String, String>> instances) throws ConsoleInitializationException, MicroAgentException {
		List<MethodSubscriber> result = new ArrayList<MethodSubscriber>();

    	microAgent = new MicroAgent(hawkDomain, hawkRvService, hawkRvNetwork, hawkRvDaemon, "COM.TIBCO.admin.TRA");

    	getLog().info("");
		getLog().info(SUBSCRIBING_TO_HAWK_METHODS);

    	for (ImmutablePair<String, String> instance : instances) {
    		MethodSubscriber ms = new MethodSubscriber(microAgent);
    		ms.setMethodName("getComponentInstanceStatus");
    		ms.setArguments(instance.left, instance.right);
    		ms.setInterval(hawkSubscribeInterval);
    		ms.setNumberOfRetry(hawkSubscribeNumberOfRetry);
    		ms.subscribe(new RunningInstanceSubscriptionHandler(getLog(), ms, numberOfRunningStatuses));

    		result.add(ms);
		}

		return result;
	}

	private boolean doWaitForRunningInstances(List<ImmutablePair<String, String>> instances) throws ConsoleInitializationException, MicroAgentException {
		List<MethodSubscriber> result = monitorInstances(instances); // start monitoring of "RUNNING" statuses for instances

		getLog().info("");
		getLog().info(WAITING_FOR_INSTANCES);

		while (RunningInstanceSubscriptionHandler.numberOfSubscriptions > 0) {
			getLog().debug(RunningInstanceSubscriptionHandler.numberOfSubscriptions.toString());
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			/*
			 *  RunningInstanceSubscriptionHandler.numberOfSubscriptions will
			 * always come down to 0. In worst case after timeout.
			 */
		}

		microAgent.shutdown();

		// check that all instances are in "RUNNING" state
		for (MethodSubscriber ms : result) {
			if (ms.getResult() == null || !ms.getResult().equals("RUNNING")) {
				return false;
			}
		}
		return true;
	}

	@Override
	public String getInitMessage() {
		return STARTING_INSTANCES;
	}

	@Override
	public String getFailureMessage() {
		return STARTING_INSTANCES_FAILED;
	}

	@Override
	public ArrayList<String> arguments() {
		ArrayList<String> arguments = super.commonArguments();
		arguments.add("-start");

		return arguments;
	}

	@Override
	public void postAction() throws MojoExecutionException {
		try {
			if (waitForRunningInstances) {
				if (!initHawk(false)) {
					return;
				}

				List<ImmutablePair<String, String>> instances = new ArrayList<ImmutablePair<String,String>>();

				ApplicationManagement application = new ApplicationManagement(deploymentDescriptorFinal);
				String applicationName = application.getName();
				List<String> servicesNames = application.getInstancesNames(true);

				for (String serviceName : servicesNames) {
					instances.add(new ImmutablePair<String, String>(applicationName, serviceName));
				}

				if (doWaitForRunningInstances(instances)) {
					getLog().info("");
					getLog().info(ALL_INSTANCES_STARTED);
				} else {
					getLog().info("");
					if (failWhenTimeoutReached) {
						throw new MojoExecutionException(SOME_INSTANCES_NOT_STARTED);
					} else {
						getLog().info(SOME_INSTANCES_NOT_STARTED);
					}
				}
			}
		} catch (JAXBException e) {
			throw new MojoExecutionException(SOME_INSTANCES_NOT_STARTED);
		} catch (ConsoleInitializationException e) {
			throw new MojoExecutionException(SOME_INSTANCES_NOT_STARTED);
		} catch (MicroAgentException e) {
			throw new MojoExecutionException(SOME_INSTANCES_NOT_STARTED);
		}

	}

}


/**
 * (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.source.alias;

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import fr.fastconnect.factory.tibco.bw.maven.source.alias.jaxb.ObjectFactory;
import fr.fastconnect.factory.tibco.bw.maven.source.alias.jaxb.Repository;

/**
 *

 * This class will

 *
unmarshall XML file of the schema with
 * "http://www.tibco.com/xmlns/repo/types/2002" namespace to JAXB objects.
 *

 *

 * marshall the {@link Repository} object back to XML file with the same
 * schema.
 *

 *

 *

 *
 * @author Mathieu Debove
 *
 */
public class RepositoryModel {
//	private static final String REPOSITORY_NAMESPACE = "http://www.tibco.com/xmlns/repo/types/2002";

	private Repository repository;
	public Repository getRepository() {
		return repository;
	}

	private JAXBContext jaxbContext;
	private File xmlFile;

	public RepositoryModel(File xmlFile) throws JAXBException {
		this.xmlFile = xmlFile;
		initRepositoryModel();
	}

	/**
	 *

	 * This will initialize the {@link Repository} object which is a JAXB
	 * representation of the "Repository" root-element of TIBCO ".aliaslib"
	 * files using the schema with "http://www.tibco.com/xmlns/repo/types/2002"
	 * namespace.
	 *

	 *
	 * @throws JAXBException
	 */
	private void initRepositoryModel() throws JAXBException {
		jaxbContext = JAXBContext.newInstance(ObjectFactory.class);

		Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
		Object o =  jaxbUnmarshaller.unmarshal(xmlFile);
		this.repository = (Repository) o;
	}

	/**
	 *

	 * This will marshall the object back to the XML file.
	 *

	 *
	 * @throws JAXBException
	 */
	public void save() throws JAXBException {
		Marshaller m = jaxbContext.createMarshaller();
		m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
		m.marshal(repository, xmlFile);
	}
}


//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, vJAXB 2.1.10 in JDK 6
// See http://java.sun.com/xml/jaxb
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2014.01.23 at 10:11:20 AM CET
//

package com.tibco.xmlns.dd;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;

/**
 * This object contains factory methods for each
 * Java content interface and Java element interface
 * generated in the com.tibco.xmlns.dd package.
 *
An ObjectFactory allows you to programatically
 * construct new instances of the Java representation
 * for XML content. The Java representation of XML
 * content can consist of schema derived interfaces
 * and classes representing the binding of schema
 * type definitions, element declarations and model
 * groups.  Factory methods for each of these are
 * provided in this class.
 *
 */
@XmlRegistry
public class ObjectFactory {

    private final static QName _NameValuePairInteger_QNAME = new QName("http://www.tibco.com/xmlns/dd", "NameValuePairInteger");
    private final static QName _DiffExcludeFilter_QNAME = new QName("http://www.tibco.com/xmlns/dd", "DiffExcludeFilter");
    private final static QName _NameValuePairs_QNAME = new QName("http://www.tibco.com/xmlns/dd", "NameValuePairs");
    private final static QName _ClassLoaderExtension_QNAME = new QName("http://www.tibco.com/xmlns/dd", "ClassLoaderExtension");
    private final static QName _NVPair_QNAME = new QName("http://www.tibco.com/xmlns/dd", "NVPair");
    private final static QName _DeploymentDescriptor_QNAME = new QName("http://www.tibco.com/xmlns/dd", "DeploymentDescriptor");
    private final static QName _CustomTSMPlugInConfiguration_QNAME = new QName("http://www.tibco.com/xmlns/dd", "CustomTSMPlugInConfiguration");
    private final static QName _NameValuePairBoolean_QNAME = new QName("http://www.tibco.com/xmlns/dd", "NameValuePairBoolean");
    private final static QName _StartAsOneOf_QNAME = new QName("http://www.tibco.com/xmlns/dd", "StartAsOneOf");
    private final static QName _Modules_QNAME = new QName("http://www.tibco.com/xmlns/dd", "Modules");
    private final static QName _DeploymentDescriptorFactory_QNAME = new QName("http://www.tibco.com/xmlns/dd", "DeploymentDescriptorFactory");
    private final static QName _NameValuePair_QNAME = new QName("http://www.tibco.com/xmlns/dd", "NameValuePair");
    private final static QName _SupportsFaultTolerance_QNAME = new QName("http://www.tibco.com/xmlns/dd", "SupportsFaultTolerance");
    private final static QName _NameValuePairPassword_QNAME = new QName("http://www.tibco.com/xmlns/dd", "NameValuePairPassword");
    private final static QName _RequiredComponents_QNAME = new QName("http://www.tibco.com/xmlns/dd", "RequiredComponents");

    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.tibco.xmlns.dd
     *
     */
    public ObjectFactory() {
    }

    /**
     * Create an instance of {@link CustomTSMPlugInConfiguration }
     *
     */
    public CustomTSMPlugInConfiguration createCustomTSMPlugInConfiguration() {
        return new CustomTSMPlugInConfiguration();
    }

    /**
     * Create an instance of {@link DeploymentDescriptorType }
     *
     */
    public DeploymentDescriptorType createDeploymentDescriptorType() {
        return new DeploymentDescriptorType();
    }

    /**
     * Create an instance of {@link ClassLoaderExtension }
     *
     */
    public ClassLoaderExtension createClassLoaderExtension() {
        return new ClassLoaderExtension();
    }

    /**
     * Create an instance of {@link NameValuePairs }
     *
     */
    public NameValuePairs createNameValuePairs() {
        return new NameValuePairs();
    }

    /**
     * Create an instance of {@link DiffExcludeFilter }
     *
     */
    public DiffExcludeFilter createDiffExcludeFilter() {
        return new DiffExcludeFilter();
    }

    /**
     * Create an instance of {@link NameValuePair }
     *
     */
    public NameValuePair createNameValuePair() {
        return new NameValuePair();
    }

    /**
     * Create an instance of {@link ComponentSoftwareReference }
     *
     */
    public ComponentSoftwareReference createComponentSoftwareReference() {
        return new ComponentSoftwareReference();
    }

    /**
     * Create an instance of {@link NameValuePairInteger }
     *
     */
    public NameValuePairInteger createNameValuePairInteger() {
        return new NameValuePairInteger();
    }

    /**
     * Create an instance of {@link DeploymentDescriptors }
     *
     */
    public DeploymentDescriptors createDeploymentDescriptors() {
        return new DeploymentDescriptors();
    }

    /**
     * Create an instance of {@link RequiredComponents }
     *
     */
    public RequiredComponents createRequiredComponents() {
        return new RequiredComponents();
    }

    /**
     * Create an instance of {@link SupportsFaultTolerance }
     *
     */
    public SupportsFaultTolerance createSupportsFaultTolerance() {
        return new SupportsFaultTolerance();
    }

    /**
     * Create an instance of {@link NVPairType }
     *
     */
    public NVPairType createNVPairType() {
        return new NVPairType();
    }

    /**
     * Create an instance of {@link DeploymentDescriptorFactory }
     *
     */
    public DeploymentDescriptorFactory createDeploymentDescriptorFactory() {
        return new DeploymentDescriptorFactory();
    }

    /**
     * Create an instance of {@link NameValuePairBoolean }
     *
     */
    public NameValuePairBoolean createNameValuePairBoolean() {
        return new NameValuePairBoolean();
    }

    /**
     * Create an instance of {@link StartAsOneOf }
     *
     */
    public StartAsOneOf createStartAsOneOf() {
        return new StartAsOneOf();
    }

    /**
     * Create an instance of {@link Modules }
     *
     */
    public Modules createModules() {
        return new Modules();
    }

    /**
     * Create an instance of {@link NameValuePairPassword }
     *
     */
    public NameValuePairPassword createNameValuePairPassword() {
        return new NameValuePairPassword();
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link NameValuePairInteger }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "NameValuePairInteger", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "NVPair")
    public JAXBElement<NameValuePairInteger> createNameValuePairInteger(NameValuePairInteger value) {
        return new JAXBElement<NameValuePairInteger>(_NameValuePairInteger_QNAME, NameValuePairInteger.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link DiffExcludeFilter }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "DiffExcludeFilter", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "DeploymentDescriptor")
    public JAXBElement<DiffExcludeFilter> createDiffExcludeFilter(DiffExcludeFilter value) {
        return new JAXBElement<DiffExcludeFilter>(_DiffExcludeFilter_QNAME, DiffExcludeFilter.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link NameValuePairs }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "NameValuePairs", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "DeploymentDescriptor")
    public JAXBElement<NameValuePairs> createNameValuePairs(NameValuePairs value) {
        return new JAXBElement<NameValuePairs>(_NameValuePairs_QNAME, NameValuePairs.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link ClassLoaderExtension }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "ClassLoaderExtension", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "DeploymentDescriptor")
    public JAXBElement<ClassLoaderExtension> createClassLoaderExtension(ClassLoaderExtension value) {
        return new JAXBElement<ClassLoaderExtension>(_ClassLoaderExtension_QNAME, ClassLoaderExtension.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link NVPairType }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "NVPair")
    public JAXBElement<NVPairType> createNVPair(NVPairType value) {
        return new JAXBElement<NVPairType>(_NVPair_QNAME, NVPairType.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link DeploymentDescriptorType }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "DeploymentDescriptor")
    public JAXBElement<DeploymentDescriptorType> createDeploymentDescriptor(DeploymentDescriptorType value) {
        return new JAXBElement<DeploymentDescriptorType>(_DeploymentDescriptor_QNAME, DeploymentDescriptorType.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link CustomTSMPlugInConfiguration }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "CustomTSMPlugInConfiguration", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "DeploymentDescriptor")
    public JAXBElement<CustomTSMPlugInConfiguration> createCustomTSMPlugInConfiguration(CustomTSMPlugInConfiguration value) {
        return new JAXBElement<CustomTSMPlugInConfiguration>(_CustomTSMPlugInConfiguration_QNAME, CustomTSMPlugInConfiguration.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link NameValuePairBoolean }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "NameValuePairBoolean", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "NVPair")
    public JAXBElement<NameValuePairBoolean> createNameValuePairBoolean(NameValuePairBoolean value) {
        return new JAXBElement<NameValuePairBoolean>(_NameValuePairBoolean_QNAME, NameValuePairBoolean.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link StartAsOneOf }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "StartAsOneOf", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "DeploymentDescriptor")
    public JAXBElement<StartAsOneOf> createStartAsOneOf(StartAsOneOf value) {
        return new JAXBElement<StartAsOneOf>(_StartAsOneOf_QNAME, StartAsOneOf.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link Modules }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "Modules", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "DeploymentDescriptor")
    public JAXBElement<Modules> createModules(Modules value) {
        return new JAXBElement<Modules>(_Modules_QNAME, Modules.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link DeploymentDescriptorFactory }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "DeploymentDescriptorFactory", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "DeploymentDescriptor")
    public JAXBElement<DeploymentDescriptorFactory> createDeploymentDescriptorFactory(DeploymentDescriptorFactory value) {
        return new JAXBElement<DeploymentDescriptorFactory>(_DeploymentDescriptorFactory_QNAME, DeploymentDescriptorFactory.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link NameValuePair }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "NameValuePair", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "NVPair")
    public JAXBElement<NameValuePair> createNameValuePair(NameValuePair value) {
        return new JAXBElement<NameValuePair>(_NameValuePair_QNAME, NameValuePair.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link SupportsFaultTolerance }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "SupportsFaultTolerance", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "DeploymentDescriptor")
    public JAXBElement<SupportsFaultTolerance> createSupportsFaultTolerance(SupportsFaultTolerance value) {
        return new JAXBElement<SupportsFaultTolerance>(_SupportsFaultTolerance_QNAME, SupportsFaultTolerance.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link NameValuePairPassword }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "NameValuePairPassword", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "NVPair")
    public JAXBElement<NameValuePairPassword> createNameValuePairPassword(NameValuePairPassword value) {
        return new JAXBElement<NameValuePairPassword>(_NameValuePairPassword_QNAME, NameValuePairPassword.class, null, value);
    }

    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link RequiredComponents }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "http://www.tibco.com/xmlns/dd", name = "RequiredComponents", substitutionHeadNamespace = "http://www.tibco.com/xmlns/dd", substitutionHeadName = "DeploymentDescriptor")
    public JAXBElement<RequiredComponents> createRequiredComponents(RequiredComponents value) {
        return new JAXBElement<RequiredComponents>(_RequiredComponents_QNAME, RequiredComponents.class, null, value);
    }

}