/**
* 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.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.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.ITagLibrary;
import org.paxml.tag.plan.PlanEntityFactory.Plan;
import org.paxml.tag.plan.PlanTagLibrary;
import org.paxml.util.PaxmlUtils;
/**
* This is a convenience tool for running paxml stuff.
*
* @author Xuetao Niu
*
*/
public class PaxmlRunner {
private static final Log log = LogFactory.getLog(PaxmlRunner.class);
public static final int DEFAULT_CONCURRENCY = 4;
public static Object run(String paxmlOrPlanFileName, Map<String, Object> params, String baseDir, Class<? extends ITagLibrary>... tagLibs) {
StaticConfig config = new StaticConfig();
for (Class<? extends ITagLibrary> lib : tagLibs) {
config.getTagLibs().add(lib);
}
config.getResources().addAll(LaunchModelBuilder.findResources(baseDir, null, null));
return run(paxmlOrPlanFileName, params, config);
}
/**
* Run either a plan file or a paxml file from the given resource set.
*
* @param paxmlOrPlanFileName
* resource name with file extension to run
* @param params
* the parameters, can be null
*
* @param config
* the config containing resource set
*
*/
public static Object run(String paxmlOrPlanFileName, Map<String, Object> params, 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());
final long execId = PaxmlUtils.getNextExecutionId();
Paxml paxml = new Paxml(LaunchModel.generateNextPid(), execId);
paxml.addStaticConfig(config);
paxml.getParser().addTagLibrary(new PlanTagLibrary(), false);
Context context = new Context(new Context(properties, paxml.getProcessId()));
if (params != null) {
context.setConsts(params, null, false);
}
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);
Object res = paxml.execute(entity, System.getProperties(), props);
run(model, execId);
if (log.isInfoEnabled()) {
log.info("Finished executing plan file: " + entity.getResource().getPath());
}
return res;
} else {
// a paxml file
logExecution(r, true);
try {
return 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());
}
}
/**
* Run the computed launch model with thread pool. It will run with default
* 4 threads if not specifically specified in the launch model.
*
* @param model
* the model containing the launch points
*/
public static void run(LaunchModel model, long executionId) {
List<LaunchPoint> points = model.getLaunchPoints(false, executionId);
if (log.isInfoEnabled()) {
log.info("Found " + points.size() + " Paxml files to execute based on plan file: " + model.getPlanEntity().getResource().getPath());
}
if (points.isEmpty()) {
return;
}
final int poolSize = model.getConcurrency() <= 0 ? Math.min(DEFAULT_CONCURRENCY, points.size()) : model.getConcurrency();
ExecutorService pool = Executors.newFixedThreadPool(poolSize);
for (final LaunchPoint point : points) {
pool.execute(new Runnable() {
@Override
public void run() {
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 forever 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;
}
}