/*****************************************************************************
* Copyright (c) 2006-2013, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text of
* such license is available at www.eclipse.org.
*****************************************************************************/
package org.eclipse.buckminster.core.actor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;
import org.eclipse.buckminster.core.CorePlugin;
import org.eclipse.buckminster.core.KeyConstants;
import org.eclipse.buckminster.core.Messages;
import org.eclipse.buckminster.core.cspec.PathGroup;
import org.eclipse.buckminster.core.cspec.model.Action;
import org.eclipse.buckminster.core.cspec.model.ActionArtifact;
import org.eclipse.buckminster.core.cspec.model.Attribute;
import org.eclipse.buckminster.core.internal.actor.ActorFactory;
import org.eclipse.buckminster.core.metadata.model.IModelCache;
import org.eclipse.buckminster.runtime.BuckminsterException;
import org.eclipse.buckminster.runtime.Logger;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExecutableExtension;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.osgi.util.NLS;
/**
* @author kolwing
*
*/
public abstract class AbstractActor implements IActor, IExecutableExtension {
private static InheritableThreadLocal<Stack<IActionContext>> actionContext = new InheritableThreadLocal<Stack<IActionContext>>();
public static IActionContext getActiveContext() {
Stack<IActionContext> ctxStack = actionContext.get();
if (ctxStack == null || ctxStack.isEmpty())
throw new IllegalStateException(Messages.No_active_IActionContext);
return ctxStack.peek();
}
public static boolean getBooleanProperty(Map<String, String> properties, String key, boolean dflt) {
String propVal = properties.get(key);
if (propVal == null)
return dflt;
if ("true".equalsIgnoreCase(propVal)) //$NON-NLS-1$
return true;
if ("false".equalsIgnoreCase(propVal)) //$NON-NLS-1$
return false;
throw new IllegalArgumentException(NLS.bind(Messages._0_not_valid_value_of_boolean_property, propVal));
}
public static List<IPath> getPathList(IActionContext ctx, Attribute attr, boolean atBase) throws CoreException {
PathGroup[] groups = attr.getPathGroups(ctx, null);
if (groups.length == 0)
return Collections.emptyList();
ArrayList<IPath> pathList = new ArrayList<IPath>();
for (PathGroup pathGroup : groups) {
IPath base = pathGroup.getBase();
if (atBase) {
pathList.add(base);
continue;
}
for (IPath path : pathGroup.getPaths())
pathList.add(base.append(path));
}
return pathList;
}
public static IPath getProductPath(IActionContext ctx, String productAlias, boolean atBase) throws CoreException {
Action action = ctx.getAction();
for (ActionArtifact product : action.getProductArtifacts())
if (productAlias.equals(product.getAlias()))
return getSingleAttributePath(ctx, product, atBase);
throw BuckminsterException.fromMessage(NLS.bind(Messages.unable_to_find_product_0_for_action_1, productAlias, action.getQualifiedName()));
}
public static IPath getSingleAttributePath(IActionContext ctx, Attribute attr, boolean atBase) throws CoreException {
IPath productPath = null;
for (PathGroup pathGroup : attr.getPathGroups(ctx, null)) {
IPath pp = null;
if (atBase)
pp = pathGroup.getBase();
else {
IPath[] paths = pathGroup.getPaths();
if (paths.length == 1)
pp = pathGroup.getBase().append(paths[0]);
else if (paths.length == 0)
pp = pathGroup.getBase();
}
if (pp == null) {
productPath = null;
break;
}
if (productPath == null)
productPath = pp;
else if (!productPath.equals(pp)) {
productPath = null;
break;
}
}
if (productPath == null)
throw BuckminsterException.fromMessage(NLS.bind(Messages.product_for_action_0_must_be_single_path, attr.getQualifiedName()));
return productPath;
}
private String name;
private String id;
private final Logger logger;
private Action action;
public AbstractActor() {
logger = CorePlugin.getLogger();
}
@Override
public final String getId() {
return id;
}
@Override
public final String getName() {
return name;
}
@Override
public final void init(Action act) throws CoreException {
action = act;
if (logger.isDebugEnabled()) {
StringBuilder bld = new StringBuilder();
bld.append("init actor: "); //$NON-NLS-1$
bld.append(this.getId());
bld.append('[');
action.toString(bld);
bld.append(']');
loggableProps(bld, action.getActorProperties());
logger.debug(bld.toString());
}
this.internalInit();
}
@Override
public boolean isUpToDate(Action act, IModelCache ctx, long prerequisiteAge, long oldestTarget) throws CoreException {
// Return true if the oldest target is younger or the same age as the
// youngest prerequisite.
return oldestTarget >= prerequisiteAge;
}
@Override
public final synchronized IStatus perform(IActionContext ctx, IProgressMonitor monitor) throws CoreException {
// the stored context is per perform only; if referenced otherwise we
// ensure that
// null is received, triggering a NPE
//
Map<String, ? extends Object> props = ctx.getProperties();
Stack<IActionContext> ctxStack = actionContext.get();
if (ctxStack == null) {
ctxStack = new Stack<IActionContext>();
actionContext.set(ctxStack);
}
ctxStack.push(ctx);
try {
Action act = ctx.getAction();
boolean quiet = ctx.isQuiet();
boolean isDebug = logger.isDebugEnabled();
if (isDebug || !quiet) {
StringBuilder bld = new StringBuilder();
bld.append("[start "); //$NON-NLS-1$
act.toString(bld);
bld.append(']');
if (isDebug) {
loggableActionInfo(bld);
loggableProps(bld, props);
}
logger.info(bld.toString());
}
ctx.getGlobalContext().scheduleRemoval(new Path(props.get(KeyConstants.ACTION_TEMP).toString()));
IStatus status = internalPerform(ctx, monitor);
if (isDebug || !quiet) {
StringBuilder bld = new StringBuilder();
bld.append("[end "); //$NON-NLS-1$
act.toString(bld);
bld.append(']');
logger.info(bld.toString());
}
return status;
} catch (Throwable t) {
throw BuckminsterException.wrap(t);
} finally {
ctxStack.pop();
if (ctxStack.isEmpty())
actionContext.set(null);
}
}
@Override
public final void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
name = config.getAttribute(ActorFactory.ACTOR_NAME_ATTR);
id = config.getAttribute(ActorFactory.ACTOR_ID_ATTR);
}
protected final String getActorProperty(String key) {
return action.getActorProperties().get(key);
}
protected final Logger getLogger() {
return logger;
}
protected void internalInit() throws CoreException {
// noop
}
abstract protected IStatus internalPerform(IActionContext ctx, IProgressMonitor monitor) throws CoreException;
private void loggableActionInfo(StringBuilder sb) {
if (action.getPrerequisitesAlias() != null) {
sb.append("\n "); //$NON-NLS-1$
sb.append(Messages.Prerequisite_alias);
sb.append(action.getPrerequisitesAlias());
}
if (action.getPrerequisiteRebase() != null) {
sb.append("\n "); //$NON-NLS-1$
sb.append(Messages.Prerequisite_rebase);
sb.append(action.getPrerequisiteRebase().toOSString());
}
if (action.getProductAlias() != null) {
sb.append("\n "); //$NON-NLS-1$
sb.append(Messages.Product_alias);
sb.append(action.getProductAlias());
}
if (action.getProductBase() != null) {
sb.append("\n "); //$NON-NLS-1$
sb.append(Messages.Product_base);
sb.append(action.getProductBase().toOSString());
}
}
private void loggableProps(StringBuilder sb, Map<String, ? extends Object> props) {
Properties sysProps = System.getProperties();
for (Map.Entry<String, ? extends Object> entry : props.entrySet()) {
if (sysProps.getProperty(entry.getKey()) == null) {
sb.append("\n "); //$NON-NLS-1$
sb.append(entry.getKey()).append('=').append(entry.getValue());
}
}
}
}