/**
 * This file is part of PaxmlCore.
 *
 * PaxmlCore is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PaxmlCore is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with PaxmlCore.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.paxml.launch;

import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.paxml.core.Context;
import org.paxml.core.IEntity;
import org.paxml.core.PaxmlResource;
import org.paxml.core.PaxmlRuntimeException;
import org.paxml.tag.plan.PlanEntityFactory.Plan;
import org.paxml.tag.plan.PlanTagLibrary;

public class PaxmlRunner {

	private static final Log log = LogFactory.getLog(PaxmlRunner.class);

	public static void run(String paxmlOrPlanFileName, StaticConfig config) {

		Context.cleanCurrentThreadContext();

		PaxmlResource r = null;
		for (PaxmlResource res : config.getResources()) {
			if (paxmlOrPlanFileName.equals(res.getName())) {
				r = res;
				break;
			}
		}
		if (r == null) {
			throw new PaxmlRuntimeException("Paxml file not found: " + paxmlOrPlanFileName);
		}

		Properties properties = new Properties();
		properties.putAll(System.getProperties());
		Paxml paxml = new Paxml(0);
		paxml.addStaticConfig(config);
		paxml.getParser().addTagLibrary(new PlanTagLibrary(), false);

		Context context = new Context(new Context(properties, paxml.getProcessId()));

		IEntity entity = paxml.getParser().parse(r, true, null);
		if (entity instanceof Plan) {
			// a plan file
			if (log.isInfoEnabled()) {
				log.info("Starting plan file execution: " + entity.getResource().getPath());
			}
			LaunchModel model = new LaunchModel();			
			model.getConfig().add(config);

			model.setName(((Plan) entity).getTagName());
			model.setPlanEntity((Plan) entity);
			model.setResource(r.getSpringResource());

			Properties props = new Properties();
			props.put(LaunchModel.class, model);
			paxml.execute(entity, System.getProperties(), props);
			run(model);
			if (log.isInfoEnabled()) {
				log.info("Finished executing plan file: " + entity.getResource().getPath());
			}
		} else {
			// a paxml file
			logExecution(r, true);
			try {
				paxml.execute(entity, context, true, true);
			} finally {
				logExecution(r, false);
			}
		}

	}

	private final static void logExecution(PaxmlResource res, boolean trueStartFalseEnd) {
		if (log.isInfoEnabled()) {
			log.info((trueStartFalseEnd ? "Starting" : "Finished") + " Paxml execution: " + res.getPath());
		}

	}

	public static void run(LaunchModel model) {

		List<LaunchPoint> points = model.getLaunchPoints(false);
		if (log.isInfoEnabled()) {
			log.info("Found " + points.size() + " Paxml files to execute based on plan file: " + model.getPlanEntity().getResource().getPath());
		}
		final int poolSize = model.getConcurrency() <= 0 ? Math.min(4, points.size()) : model.getConcurrency();
		ExecutorService pool = Executors.newFixedThreadPool(poolSize);
		for (final LaunchPoint point : points) {
			pool.execute(new Runnable() {

				@Override
				public void run() {
					// this will only run for scenario, never for the plan file.
					// the plan file's execution will be done in the test case
					// factory.
					try {
						Context.cleanCurrentThreadContext();

						logExecution(point.getResource(), true);

						point.execute();
					} catch (Throwable t) {
						if (log.isErrorEnabled()) {
							log.error(findMessage(t), t);
						}
					} finally {
						logExecution(point.getResource(), false);
					}
				}

			});
		}

		try {
			pool.shutdown();
			// wait for ever in a loop
			while (!pool.awaitTermination(1, TimeUnit.MINUTES)) {
				if (log.isDebugEnabled()) {
					log.debug("Waiting for all executors to finish...");
				}
			}

		} catch (InterruptedException e) {
			throw new PaxmlRuntimeException("Cannot wait for all executors to finish", e);
		} finally{
			pool.shutdownNow();
		}

	}

	private static String findMessage(Throwable t) {
		String msg = t.getMessage();
		for (; t != null && StringUtils.isBlank(msg); t = t.getCause()) {

		}
		return msg;
	}
}

@@ -1265,16 +1265,109 @@
 brary;%0A%0A
+/**%0A * This is a convenience tool for running paxml stuff.%0A * %0A * @author Xuetao Niu%0A * %0A */%0A
 public c
@@ -1457,16 +1457,258 @@
 lass);%0A%0A
+%09/**%0A%09 * Run either a plan file or a paxml file from the given resource set.%0A%09 * %0A%09 * @param paxmlOrPlanFileName%0A%09 *            resource name with file extension to run%0A%09 * @param config%0A%09 *            the config containing resource set%0A%09 */%0A
 %09public 
@@ -2650,19 +2650,16 @@
 Model();
-%09%09%09
 %0A%09%09%09mode
@@ -3482,16 +3482,244 @@
 %09%7D%0A%0A%09%7D%0A%0A
+%09/**%0A%09 * Run the computed launch model with thread pool. It will run with default%0A%09 * 4 threads if not specifically specified in the launch model.%0A%09 * %0A%09 * @param model%0A%09 *            the model containing the launch points%0A%09 */%0A
 %09public 
@@ -5078,16 +5078,16 @@
 h%22, e);%0A
-
 %09%09%7D fina
@@ -5089,16 +5089,17 @@
  finally
+ 
 %7B%0A%09%09%09poo

add doc