/******************************************************************************* * Copyright (c) 2005 - 2009 committers of openArchitectureWare and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * committers of openArchitectureWare - initial API and implementation *******************************************************************************/ package org.eclipse.emf.mwe.core; import java.net.URL; import java.util.Map; import java.util.jar.Manifest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.mwe.core.issues.Issues; import org.eclipse.emf.mwe.core.issues.IssuesImpl; import org.eclipse.emf.mwe.core.monitor.NullProgressMonitor; import org.eclipse.emf.mwe.core.monitor.ProgressMonitor; import org.eclipse.emf.mwe.internal.core.MWEPlugin; import org.eclipse.emf.mwe.internal.core.Workflow; import org.eclipse.emf.mwe.internal.core.ast.util.WorkflowFactory; import org.eclipse.emf.mwe.internal.core.ast.util.converter.Converter; public class WorkflowEngine { private WorkflowContextDefaultImpl wfContext = new WorkflowContextDefaultImpl(); private ProgressMonitor monitor; private static final Log logger = LogFactory.getLog(WorkflowEngine.class); private Map<String, String> params; private Workflow workflow; /** * @param workFlowFile * @param monitor * @param logger * @param params */ public boolean run(final String workFlowFile, final ProgressMonitor theMonitor, final Map<String, String> theParams, final Map<String, ?> externalSlotContents) { final boolean configOK = prepare(workFlowFile, theMonitor, theParams); final Issues issues = new IssuesImpl(); if (configOK) { return executeWorkflow(externalSlotContents, issues); } return false; } public boolean prepare(final String workFlowFile, final ProgressMonitor theMonitor, final Map<String, String> theParams) { if (workFlowFile == null) { throw new NullPointerException("workflowFile is null"); } if (theMonitor == null) { monitor = new NullProgressMonitor(); } else { monitor = theMonitor; } params = theParams; logger.info("--------------------------------------------------------------------------------------"); logger.info("EMF Modeling Workflow Engine " + getVersion()); logger.info("(c) 2005-2009 openarchitectureware.org and contributors"); logger.info("--------------------------------------------------------------------------------------"); logger.info("running workflow: " + workFlowFile); logger.info(""); if (logger.isDebugEnabled() && !params.isEmpty()) { logger.debug("Params:" + params.toString()); } final Issues issues = new IssuesImpl(); try { final WorkflowFactory factory = getWorkflowFactory(); // see Bug#155854: WorkflowFactory will throw an // IllegalArgumentException which indicates // a configuration problem. // Detect this very special case try { workflow = factory.parseInitAndCreate(workFlowFile, params, getConverters(), issues); } catch (final IllegalArgumentException illegalArg) { if (illegalArg.getMessage().startsWith("Couldn't load file")) { throw new ConfigurationException(illegalArg.getMessage()); } throw illegalArg; } logIssues(logger, issues); if (issues.hasErrors()) { logger.error("Workflow interrupted because of configuration errors."); return false; } if (workflow != null) { workflow.checkConfiguration(issues); } logIssues(logger, issues); if (issues.hasErrors()) { logger.error("Workflow interrupted because of configuration errors."); return false; } } catch (final ConfigurationException ex) { logger.fatal(ex.getMessage(), ex); logIssues(logger, issues); return false; } return true; } /** * Creates a new workflow factory * */ protected WorkflowFactory getWorkflowFactory() { return new WorkflowFactory(); } /** * This method delivers the Converter implementations currently used to inject values into the workflow components. * * @return A map between injection types and converter implementations. Not <code>null</code>. */ private Map<Class<?>, Converter<?>> getConverters() { Map<Class<?>, Converter<?>> result = getCustomConverters(); final Map<Class<?>, Converter<?>> defaults = WorkflowFactory.getDefaultConverter(); if (result == null) { // go with the default values result = defaults; } else { // add default Converter implementations if we don't have a custom // implementation yet for (final Map.Entry<Class<?>, Converter<?>> record : defaults.entrySet()) { if (!result.containsKey(record.getKey())) { result.put(record.getKey(), record.getValue()); } } } return result; } /** * Returns a map of custom Converter implementations used for the injection process. If the result is * <code>null</code> the default Converter implementations are used. It's not necessary to provide custom Converter * implementations for the default types since they will be added if they're missing (f.e. it might be useful to * support lists which are splitted using other characters than a comma). * * @return A map of custom Converter implementations. Maybe <code>null</code>. */ protected Map<Class<?>, Converter<?>> getCustomConverters() { return null; } public boolean executeWorkflow(final Map<?, ?> externalSlotContents, final Issues issues) { try { wfContext = new WorkflowContextDefaultImpl(); addExternalSlotContents(externalSlotContents); final long time = System.currentTimeMillis(); monitor.started(workflow, wfContext); workflow.invoke(wfContext, monitor, issues); monitor.finished(workflow, wfContext); final long duration = System.currentTimeMillis() - time; logger.info("workflow completed in " + duration + "ms!"); if (issues.getErrors().length > 0) { return false; } return true; } catch (final Exception e) { if (e.getClass().getName().indexOf("Interrupt") > -1) { logger.error("Workflow interrupted. Reason: " + e.getMessage()); } else { logger.error(e.getMessage(), e); } } finally { logIssues(logger, issues); } return false; } private void addExternalSlotContents(final Map<?, ?> slotContents) { if (slotContents == null) { return; } for (final Object name : slotContents.keySet()) { final String key = (String) name; wfContext.set(key, slotContents.get(key)); } } private void logIssues(final Log logger, final Issues issues) { // log any configuration warning messages final Diagnostic[] issueArray = issues.getIssues(); for (final Diagnostic issue : issueArray) { if (issue.getSeverity() == Diagnostic.ERROR) { logger.error(issue.toString()); } if (issue.getSeverity() == Diagnostic.WARNING) { logger.warn(issue.toString()); } if (issue.getSeverity() == Diagnostic.INFO) { logger.info(issue.toString()); } } } public WorkflowContext getContext() { return wfContext; } /** * Tries to read the exact build version from the Manifest of the core.workflow plugin. Therefore the Manifest file * is located and the version is read from the attribute 'Bundle-Version'. * * @return The build version string, format "4.1.1, Build 200609291913" */ private String getVersion() { try { URL url = new URL(MWEPlugin.INSTANCE.getBaseURL() + "META-INF/MANIFEST.MF"); final Manifest manifest = new Manifest(url.openStream()); // Original value : "4.1.1.200609291913" // Resulting value : "4.1.1, Build 200609291913" String version = manifest.getMainAttributes().getValue("Bundle-Version"); final int lastPoint = version.lastIndexOf('.'); return version.substring(0, lastPoint) + ", Build " + version.substring(lastPoint + 1); } catch (Exception e) { logger.warn("Couldn't compute version of mwe.core bundle.", e); return "unkown version"; } } }