/***************************************************************************** * 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; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.TreeMap; import java.util.UUID; import org.eclipse.buckminster.core.common.model.Constant; import org.eclipse.buckminster.core.common.model.ExpandingProperties; import org.eclipse.buckminster.core.common.model.ValueHolder; import org.eclipse.buckminster.core.cspec.IAction; import org.eclipse.buckminster.core.cspec.IAttribute; import org.eclipse.buckminster.core.cspec.IComponentRequest; import org.eclipse.buckminster.core.cspec.QualifiedDependency; import org.eclipse.buckminster.core.cspec.model.Action; import org.eclipse.buckminster.core.cspec.model.ComponentName; import org.eclipse.buckminster.core.cspec.model.ComponentRequest; import org.eclipse.buckminster.core.helpers.BMProperties; import org.eclipse.buckminster.core.helpers.DateAndTimeUtils; import org.eclipse.buckminster.core.helpers.FilterUtils; import org.eclipse.buckminster.core.helpers.UnmodifiableMapUnion; import org.eclipse.buckminster.core.metadata.model.Resolution; import org.eclipse.buckminster.core.query.model.ComponentQuery; import org.eclipse.buckminster.core.resolver.NodeQuery; import org.eclipse.buckminster.core.version.BuildTimestampQualifierGenerator; import org.eclipse.buckminster.runtime.Buckminster; import org.eclipse.buckminster.runtime.Logger; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.IValueVariable; import org.eclipse.core.variables.VariablesPlugin; /** * The <i>Resolution and Materialization</i> context. Maintains information with * a lifecycle that lasts throughout the resolution and materialization process. * * @author Thomas Hallgren */ public class RMContext extends ExpandingProperties<Object> { public class TagInfo { private final int tagId; private final String infoString; private boolean used = false; private TagInfo(String infoString) { tagId = ++tagInfoSquenceNumber; this.infoString = infoString; } private void addTagId(StringBuilder bld) { String tagIdStr = Integer.toString(tagId); int len = tagIdStr.length(); bld.append("0000"); //$NON-NLS-1$ bld.setLength(bld.length() - len); bld.append(tagIdStr); } public String getTagId() { StringBuilder bld = new StringBuilder(); addTagId(bld); return bld.toString(); } public boolean isUsed() { return used; } public void setUsed() { used = true; } @Override public String toString() { StringBuilder bld = new StringBuilder(); bld.append("TAG-ID "); //$NON-NLS-1$ addTagId(bld); bld.append(" = "); //$NON-NLS-1$ bld.append(infoString); return bld.toString(); } } private static final Map<String, String> staticAdditions; static { Map<String, String> additions = new HashMap<String, String>(); File homeFile = TargetPlatform.getPlatformInstallLocation(); if (homeFile != null) { CorePlugin.getLogger().debug("Platform install location: %s", homeFile); //$NON-NLS-1$ additions.put("eclipse.home", homeFile.toString()); //$NON-NLS-1$ } else CorePlugin.getLogger().debug("Platform install location is NULL!"); //$NON-NLS-1$ additions.put("workspace.root", ResourcesPlugin.getWorkspace().getRoot().getLocation().toPortableString()); //$NON-NLS-1$ try { additions.put("localhost", InetAddress.getLocalHost().getHostName()); //$NON-NLS-1$ } catch (UnknownHostException e1) { // We'll just have to do without it. } staticAdditions = additions; } private static IStatus addTagId(String tagId, IStatus status) { if (status instanceof MultiStatus) { IStatus[] children = status.getChildren(); int idx = children.length; IStatus[] taggedChildren = new IStatus[idx]; while (--idx >= 0) taggedChildren[idx] = addTagId(tagId, children[idx]); return new MultiStatus(status.getPlugin(), status.getCode(), taggedChildren, addTagId(tagId, status.getMessage()), status.getException()); } return new Status(status.getSeverity(), status.getPlugin(), addTagId(tagId, status.getMessage()), status.getException()); } private static String addTagId(String tagId, String msg) { String prefix = '[' + tagId + "] : "; //$NON-NLS-1$ if (msg == null) msg = prefix; else if (!msg.startsWith(prefix)) msg = prefix + msg; return msg; } public static String formatStatus(IStatus status) { StringWriter bld = new StringWriter(); PrintWriter wrt = new PrintWriter(bld); try { formatStatus(wrt, 0, status); wrt.flush(); } catch (IOException e) { } return bld.toString(); } private static void formatStatus(PrintWriter wrt, int indent, IStatus status) throws IOException { for (int idx = 0; idx < indent; ++idx) wrt.append(' '); switch (status.getSeverity()) { case IStatus.INFO: wrt.append("INFO "); //$NON-NLS-1$ break; case IStatus.WARNING: wrt.append("WARNING "); //$NON-NLS-1$ break; case IStatus.ERROR: wrt.append("ERROR "); //$NON-NLS-1$ break; } wrt.append(status.getMessage()); for (IStatus child : status.getChildren()) { wrt.println(); formatStatus(wrt, indent + 2, child); } Throwable t = status.getException(); if (t != null) { if (t instanceof CoreException) { wrt.println(); formatStatus(wrt, indent + 2, ((CoreException) t).getStatus()); } if (Buckminster.getLogger().isDebugEnabled()) t.printStackTrace(wrt); } } public static Map<String, ? extends Object> getGlobalPropertyAdditions() { Map<String, String> sysProps = BMProperties.getSystemProperties(); IStringVariableManager varMgr = VariablesPlugin.getDefault().getStringVariableManager(); IValueVariable[] vars = varMgr.getValueVariables(); Map<String, Object> additions = new HashMap<String, Object>(staticAdditions.size() + sysProps.size() + vars.length + 6); additions.putAll(staticAdditions); try { ITargetPlatform tf = TargetPlatform.getInstance(); additions.put(TargetPlatform.TARGET_OS, tf.getOS()); additions.put(TargetPlatform.TARGET_WS, tf.getWS()); additions.put(TargetPlatform.TARGET_ARCH, tf.getArch()); additions.put(TargetPlatform.TARGET_NL, tf.getNL()); File location = tf.getLocation(); if (location != null) additions.put(TargetPlatform.TARGET_LOCATION, location.getAbsolutePath()); } catch (CoreException e) { e.printStackTrace(); } additions.put(BuildTimestampQualifierGenerator.FORMAT_PROPERTY, DateAndTimeUtils.toISOFormat(new Date())); for (IValueVariable var : varMgr.getValueVariables()) { Object value = var.getValue(); if (FilterUtils.MATCH_ALL.equals(value)) value = FilterUtils.MATCH_ALL_OBJ; additions.put(var.getName(), value); } for (Map.Entry<String, String> sysProp : sysProps.entrySet()) { Object value = sysProp.getValue(); if (FilterUtils.MATCH_ALL.equals(value)) value = FilterUtils.MATCH_ALL_OBJ; additions.put(sysProp.getKey(), value); } return additions; } private int tagInfoSquenceNumber = 0; private final Map<QualifiedDependency, NodeQuery> nodeQueries = new HashMap<QualifiedDependency, NodeQuery>(); private final Map<String, String[]> filterAttributeUsageMap = new HashMap<String, String[]>(); // Map that ensures that only one TagInfo is generated for each info string private final Map<String, TagInfo> knownTagInfos = new HashMap<String, TagInfo>(); private boolean continueOnError; private final Map<ComponentRequest, TagInfo> tagInfos = new HashMap<ComponentRequest, TagInfo>(); private final Map<UUID, Object> userCache = Collections.synchronizedMap(new HashMap<UUID, Object>()); private final Map<String, String> bindingProperties = Collections.synchronizedMap(new HashMap<String, String>()); private MultiStatus status; private boolean silentStatus; public RMContext(Map<String, ? extends Object> properties) { this(properties, null); } public RMContext(Map<String, ? extends Object> properties, RMContext source) { super(getGlobalPropertyAdditions()); if (properties != null) putAll(properties, true); if (source != null) { userCache.putAll(source.getUserCache()); tagInfos.putAll(source.getTagInfos()); bindingProperties.putAll(source.getBindingProperties()); filterAttributeUsageMap.putAll(source.getFilterAttributeUsageMap()); } } /** * This is where the exceptions that occur during processing will end up if * the {@link #isContinueOnError()} returns <code>true</code>. * * @param resolveStatus * A status that indicates an error during processing. */ public synchronized void addRequestStatus(IComponentRequest request, IStatus st) { Logger logger = CorePlugin.getLogger(); if (logger.isInfoEnabled()) st = addTagId(getTagId(request), st); if (!isSilentStatus()) { switch (st.getSeverity()) { case IStatus.ERROR: logger.error(formatStatus(st)); break; case IStatus.WARNING: logger.warning(formatStatus(st)); break; case IStatus.INFO: logger.info(formatStatus(st)); } } if (status == null) status = new MultiStatus(CorePlugin.getID(), IStatus.OK, new IStatus[] { st }, "Errors and Warnings", null); //$NON-NLS-1$ else status.merge(st); } public synchronized void addTagInfo(ComponentRequest request, String info) { TagInfo tagInfo = tagInfos.get(request); if (tagInfo == null) { // Check if a TagInfo has been generated for this particular info // String. If so // let this request share that TagInfo with other requests. // // The TagInfo represents the Path that leads to a request, but it // doesn't include // the request itself, hence several requests can share the same // TagInfo. // tagInfo = knownTagInfos.get(info); if (tagInfo == null) { tagInfo = new TagInfo(info); knownTagInfos.put(info, tagInfo); } tagInfos.put(request, tagInfo); } } public void clearStatus() { } private void emitTagInfos() { Logger logger = CorePlugin.getLogger(); if (!logger.isInfoEnabled()) return; Map<String, TagInfo> sorted = new TreeMap<String, TagInfo>(); // do NOT call initializeAllTagInfos() here. it won't produce any used // tags. for (TagInfo tagInfo : tagInfos.values()) if (tagInfo.isUsed()) sorted.put(tagInfo.getTagId(), tagInfo); if (sorted.size() == 0) return; tagInfos.clear(); StringWriter bld = new StringWriter(); BufferedWriter wrt = new BufferedWriter(bld); try { for (TagInfo tagInfo : sorted.values()) { wrt.write(tagInfo.toString()); wrt.newLine(); } wrt.flush(); } catch (IOException e) { // On a StringWriter? Don't think so. } logger.info(bld.toString()); } /** * Emit all tags for warnings and errors that have been added earlier. * * @return <code>true</code> if errors have been added, <code>false</code> * if not. */ public boolean emitWarningAndErrorTags() { IStatus st = getStatus(); switch (st.getSeverity()) { case IStatus.ERROR: emitTagInfos(); return true; case IStatus.WARNING: case IStatus.INFO: emitTagInfos(); } return false; } public String getBindingName(Resolution resolution, Map<String, ? extends Object> props) throws CoreException { ComponentRequest request = resolution.getRequest(); String name = null; IAttribute bindEntryPoint = resolution.getCSpec().getBindEntryPoint(); if (bindEntryPoint instanceof IAction) { if (props == null) props = getProperties(request); name = ((Action) bindEntryPoint).getBindingName(props); } if (name == null) name = request.getName(); return name; } public Map<String, String> getBindingProperties() { return bindingProperties; } public ComponentQuery getComponentQuery() { return null; } /** * Returns the map used for keeping track of that attributes that are used * by filters throughout the resolution and what values that are queried for * those attributes. * * @return An map, keyed by attribute name, that collects queried values for * each attribute. */ public Map<String, String[]> getFilterAttributeUsageMap() { return filterAttributeUsageMap; } public NodeQuery getNodeQuery(ComponentRequest request) { return getNodeQuery(new QualifiedDependency(request, getComponentQuery().getAttributes(request, this))); } public synchronized NodeQuery getNodeQuery(QualifiedDependency qualifiedDependency) { NodeQuery query = nodeQueries.get(qualifiedDependency); if (query == null) { query = new NodeQuery(this, qualifiedDependency); nodeQueries.put(qualifiedDependency, query); } return query; } public Map<String, ? extends Object> getProperties(ComponentName cName) { return new UnmodifiableMapUnion<String, Object>(cName.getProperties(), this); } public NodeQuery getRootNodeQuery() { return getNodeQuery(getComponentQuery().getExpandedRootRequest(this)); } /** * Returns the status that reflects the outcome of the process. If the * status is {@link org.eclipse.core.runtime.Status#OK_STATUS OK_STATUS} * everything went OK. * * @return The status of the process */ public IStatus getStatus() { IStatus st = status; if (st == null) return Status.OK_STATUS; return st; } private synchronized String getTagId(IComponentRequest request) { TagInfo tagInfo = tagInfos.get(request); if (tagInfo == null) initializeTagInfo(request); tagInfo = tagInfos.get(request); if (tagInfo != null) { tagInfo.setUsed(); return tagInfo.getTagId(); } return "0000"; //$NON-NLS-1$ } public synchronized Map<ComponentRequest, TagInfo> getTagInfos() { initializeAllTagInfos(); return tagInfos; } /** * Returns a map intended for caching purposes during resolution and * materialization. The map is synchronized. Users of the map must create * UUID's to use as keys in the map. * * @return A map to be used for caching purposes */ public Map<UUID, Object> getUserCache() { return userCache; } protected synchronized boolean hasTagInfo(IComponentRequest request) { // This method is called during TagInfo initialization. Do not // initialize here. return tagInfos.containsKey(request); } /** * Override in subclasses to perform lazy initialization of tag infos. */ protected void initializeAllTagInfos() { // nothing to to here. must be overriden by subclasses } /** * Override in subclasses to perform lazy initialization of tag info. */ protected void initializeTagInfo(IComponentRequest request) { // nothing to to here. must be overriden by subclasses } public boolean isContinueOnError() { return continueOnError; } public boolean isSilentStatus() { return silentStatus; } public void setContinueOnError(boolean flag) { continueOnError = flag; } @Override public ValueHolder<Object> setProperty(String key, ValueHolder<Object> propertyHolder) { if (propertyHolder instanceof Constant<?>) { Constant<Object> c = (Constant<Object>) propertyHolder; if ("*".equals(c.getConstantValue())) //$NON-NLS-1$ { propertyHolder = new Constant<Object>(FilterUtils.MATCH_ALL_OBJ); propertyHolder.setMutable(c.isMutable()); } } return super.setProperty(key, propertyHolder); } public void setSilentStatus(boolean flag) { silentStatus = flag; } }