/**********************************************************************
* Copyright (c) 2005-2009 ant4eclipse project team.
*
* 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:
* Nils Hartmann, Daniel Kasmeroglu, Gerd Wuetherich
**********************************************************************/
package org.ant4eclipse.ant.pde;
import org.ant4eclipse.ant.core.FileListHelper;
import org.ant4eclipse.ant.platform.core.MacroExecutionValues;
import org.ant4eclipse.ant.platform.core.ScopedMacroDefinition;
import org.ant4eclipse.ant.platform.core.delegate.MacroExecutionValuesProvider;
import org.ant4eclipse.ant.platform.core.task.AbstractExecuteProjectTask;
import org.ant4eclipse.lib.core.exception.Ant4EclipseException;
import org.ant4eclipse.lib.core.logging.A4ELogging;
import org.ant4eclipse.lib.core.util.Pair;
import org.ant4eclipse.lib.core.util.Utilities;
import org.ant4eclipse.lib.pde.PdeExceptionCode;
import org.ant4eclipse.lib.pde.internal.tools.FeatureDescription;
import org.ant4eclipse.lib.pde.model.featureproject.FeatureManifest;
import org.ant4eclipse.lib.pde.model.featureproject.FeatureProjectRole;
import org.ant4eclipse.lib.pde.model.featureproject.FeatureManifest.Includes;
import org.ant4eclipse.lib.pde.model.featureproject.FeatureManifest.Plugin;
import org.ant4eclipse.lib.pde.model.pluginproject.BundleSource;
import org.ant4eclipse.lib.pde.tools.PdeBuildHelper;
import org.ant4eclipse.lib.pde.tools.PlatformConfiguration;
import org.ant4eclipse.lib.pde.tools.ResolvedFeature;
import org.ant4eclipse.lib.pde.tools.TargetPlatform;
import org.ant4eclipse.lib.platform.PlatformExceptionCode;
import org.ant4eclipse.lib.platform.model.resource.EclipseProject;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.MacroDef;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.osgi.framework.Version;
import java.io.File;
import java.util.Iterator;
import java.util.List;
/**
* <p>
* The {@link ExecuteFeatureTask} can be used to iterate over a feature. It implements a loop over all the bundles
* and/or plug-in-projects contained in a <code>feature.xml</code> file.
* <p>
*
* @author Gerd Wütherich (gerd@gerd-wuetherich.de)
*/
public class ExecuteFeatureTask extends AbstractExecuteProjectTask implements PdeExecutorValues,
TargetPlatformAwareComponent {
/** the plug-in scope */
private static final String SCOPE_PLUGIN = "SCOPE_PLUGIN";
/** the plug-in scope element name */
private static final String SCOPE_NAME_PLUGIN = "ForEachPlugin";
/** the root feature scope */
private static final String SCOPE_ROOT_FEATURE = "SCOPE_ROOT_FEATURE";
/** the root feature scope element name */
private static final String SCOPE_NAME_ROOT_FEATURE = "ForRootFeature";
/** the included feature scope */
private static final String SCOPE_INCLUDED_FEATURE = "SCOPE_INCLUDED_FEATURE";
/** the included feature scope element name */
private static final String SCOPE_NAME_INCLUDED_FEATURE = "ForEachIncludedFeature";
/** the id of the feature to build (either featureId or projectName has to be set) */
private String _featureId;
/** the version of the feature to build (must be used together with featureId) */
private Version _featureVersion;
/** the target platform delegate */
private TargetPlatformAwareDelegate _targetPlatformAwareDelegate;
/** the resolved feature */
private ResolvedFeature _resolvedFeature;
/** a semicolon separated list of bundle-symbolicNames and versions */
private String _resolvedBundleVersions;
/**
* <p>
* Creates a new instance of type {@link ExecuteFeatureTask}.
* </p>
*/
public ExecuteFeatureTask() {
super("executeFeature");
// create the target platform delegate
this._targetPlatformAwareDelegate = new TargetPlatformAwareDelegate();
}
/**
* {@inheritDoc}
*/
public final String getTargetPlatformId() {
return this._targetPlatformAwareDelegate.getTargetPlatformId();
}
/**
* {@inheritDoc}
*/
public final boolean isTargetPlatformIdSet() {
return this._targetPlatformAwareDelegate.isTargetPlatformIdSet();
}
/**
* {@inheritDoc}
*/
public final void requireTargetPlatformIdSet() {
this._targetPlatformAwareDelegate.requireTargetPlatformIdSet();
}
/**
* {@inheritDoc}
*/
public final void setTargetPlatformId(String targetPlatformId) {
this._targetPlatformAwareDelegate.setTargetPlatformId(targetPlatformId);
}
/**
* {@inheritDoc}
*/
public String getPlatformConfigurationId() {
return this._targetPlatformAwareDelegate.getPlatformConfigurationId();
}
/**
* {@inheritDoc}
*/
public boolean isPlatformConfigurationIdSet() {
return this._targetPlatformAwareDelegate.isPlatformConfigurationIdSet();
}
/**
* {@inheritDoc}
*/
public void setPlatformConfigurationId(String platformConfigurationId) {
this._targetPlatformAwareDelegate.setPlatformConfigurationId(platformConfigurationId);
}
/**
* <p>
* Returns the feature id.
* </p>
*
* @return the featureId
*/
public String getFeatureId() {
return this._featureId;
}
/**
* <p>
* Sets the feature id.
* </p>
*
* @param featureId
* the featureId to set
*/
public void setFeatureId(String featureId) {
if (Utilities.hasText(featureId)) {
this._featureId = featureId;
}
}
/**
* <p>
* Returns the feature version.
* </p>
*
* @return the version the version of the feature
*/
public Version getFeatureVersion() {
return this._featureVersion;
}
/**
* <p>
* Sets the feature version.
* </p>
*
* @param version
* the version to set
*/
public void setFeatureVersion(String version) {
if (Utilities.hasText(version)) {
this._featureVersion = new Version(version);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setProjectName(String projectName) {
if (Utilities.hasText(projectName)) {
super.setProjectName(projectName);
}
}
/**
* {@inheritDoc}
*/
public Object createDynamicElement(String name) {
// create macro definition for SCOPE_ROOT_FEATURE
if (SCOPE_NAME_ROOT_FEATURE.equalsIgnoreCase(name)) {
return createScopedMacroDefinition(SCOPE_ROOT_FEATURE);
}
// create macro definition for SCOPE_NAME_INCLUDED_FEATURE
else if (SCOPE_NAME_INCLUDED_FEATURE.equalsIgnoreCase(name)) {
return createScopedMacroDefinition(SCOPE_INCLUDED_FEATURE);
}
// create macro definition for SCOPE_NAME_PLUGIN
else if (SCOPE_NAME_PLUGIN.equalsIgnoreCase(name)) {
return createScopedMacroDefinition(SCOPE_PLUGIN);
}
// return null otherwise
return null;
}
/**
* {@inheritDoc}
*/
@Override
public void doExecute() {
if (A4ELogging.isDebuggingEnabled()) {
A4ELogging.debug("Executing feature");
}
// set a default version if feature version is not set
if (this._featureId != null && this._featureVersion == null) {
this._featureVersion = Version.emptyVersion;
}
// resolve the feature
this._resolvedFeature = resolveFeature();
if (A4ELogging.isDebuggingEnabled()) {
A4ELogging.debug("Resolved feature is...");
List<Pair<Plugin, BundleDescription>> list = this._resolvedFeature.getPluginToBundleDescptionList();
for (Pair<Plugin, BundleDescription> pair : list) {
A4ELogging.debug("Resolved plug-in '%s (%s)' to bundle '%s (%s)'.", pair.getFirst().getId(), pair.getFirst()
.getVersion(), pair.getSecond().getSymbolicName(), pair.getSecond().getVersion());
}
}
// extract the resolved bundle versions
this._resolvedBundleVersions = extractBundleVersions();
if (A4ELogging.isDebuggingEnabled()) {
A4ELogging.debug("Resolved bundle versions: '%s'", this._resolvedBundleVersions);
}
// execute scoped macro definitions
for (ScopedMacroDefinition<String> scopedMacroDefinition : getScopedMacroDefinitions()) {
// execute macro definition for SCOPE_ROOT_FEATURE
if (SCOPE_ROOT_FEATURE.equals(scopedMacroDefinition.getScope())) {
executeRootFeatureScopedMacroDef(scopedMacroDefinition.getMacroDef());
}
// execute macro definition for SCOPE_INCLUDED_FEATURE
else if (SCOPE_INCLUDED_FEATURE.equals(scopedMacroDefinition.getScope())) {
executeIncludedFeatureScopedMacroDef(scopedMacroDefinition.getMacroDef());
}
// execute macro definition for SCOPE_PLUGIN
else if (SCOPE_PLUGIN.equals(scopedMacroDefinition.getScope())) {
executePluginScopedMacroDef(scopedMacroDefinition.getMacroDef());
}
// unknown execution scope
else {
throw new Ant4EclipseException(PlatformExceptionCode.UNKNOWN_EXECUTION_SCOPE, scopedMacroDefinition.getScope());
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected void preconditions() throws BuildException {
// require workspace directory set...
requireWorkspaceDirectoryOrWorkspaceIdSet();
// require target platform id set...
requireTargetPlatformIdSet();
// require project name *or* featureId (and version) set...
if ((isProjectNameSet() && (this._featureId != null || this._featureVersion != null))) {
throw new Ant4EclipseException(PdeExceptionCode.ANT_ATTRIBUTE_X_OR_Y, "projectName",
"featureId' and 'featureVersion");
}
if ((!isProjectNameSet() && (this._featureId == null || this._featureVersion == null))) {
throw new Ant4EclipseException(PdeExceptionCode.ANT_ATTRIBUTE_X_OR_Y, "projectName",
"featureId' and 'featureVersion");
}
}
/**
* <p>
* Execute plug-in scoped macro definition.
* </p>
*
* @param macroDef
*/
private void executePluginScopedMacroDef(MacroDef macroDef) {
if (A4ELogging.isDebuggingEnabled()) {
A4ELogging.debug("executePluginScopedMacroDef");
}
for (final Pair<Plugin, BundleDescription> pluginAndBundleDescription : this._resolvedFeature
.getPluginToBundleDescptionList()) {
// execute macro
executeMacroInstance(macroDef, new MacroExecutionValuesProvider() {
public MacroExecutionValues provideMacroExecutionValues(MacroExecutionValues values) {
// TODO: References
Plugin plugin = pluginAndBundleDescription.getFirst();
BundleDescription bundleDescription = pluginAndBundleDescription.getSecond();
// add plug-in id
if (plugin.hasId()) {
values.getProperties().put(PLUGIN_ID, plugin.getId());
}
// the location of the bundle (either location of eclipse project or location in target platform)
BundleSource bundleSource = (BundleSource) bundleDescription.getUserObject();
if (bundleSource.isEclipseProject()) {
File bundleLocation = bundleSource.getAsEclipseProject().getFolder();
values.getProperties().put(PLUGIN_IS_SOURCE, "true");
values.getProperties().put(PLUGIN_FILE, bundleLocation.getAbsolutePath());
values.getProperties().put(PLUGIN_FILENAME, bundleLocation.getName());
} else {
File bundleLocation = bundleSource.getAsFile();
values.getProperties().put(PLUGIN_IS_SOURCE, "false");
values.getProperties().put(PLUGIN_FILE, bundleLocation.getAbsolutePath());
values.getProperties().put(PLUGIN_FILENAME, bundleLocation.getName());
values.getReferences().put(PLUGIN_FILELIST, FileListHelper.getFileList(bundleLocation));
}
if (plugin.hasVersion()) {
// plugin.version contains the version from plugin.xml
values.getProperties().put(PLUGIN_VERSION, plugin.getVersion().toString());
// PLUGIN_RESOLVED_VERSION contains the "resolved" version
// that is - if plugin version is 0.0.0 - the actual version
// of the bundle that has been found for this plug-in entry
values.getProperties().put(PLUGIN_RESOLVED_VERSION, bundleDescription.getVersion().toString());
}
if (plugin.hasDownloadSize()) {
values.getProperties().put(PLUGIN_DOWNLOADSIZE, plugin.getDownloadSize());
}
if (plugin.hasInstallSize()) {
values.getProperties().put(PLUGIN_INSTALLSIZE, plugin.getInstallSize());
}
if (plugin.hasWindowingSystem()) {
values.getProperties().put(PLUGIN_WINDOWINGSYSTEM, plugin.getWindowingSystem());
}
if (plugin.hasMachineArchitecture()) {
values.getProperties().put(PLUGIN_MACHINEARCHITECTURE, plugin.getMachineArchitecture());
}
if (plugin.hasOperatingSystem()) {
values.getProperties().put(PLUGIN_OPERATINGSYSTEM, plugin.getOperatingSystem());
}
if (plugin.hasLocale()) {
values.getProperties().put(PLUGIN_LOCALE, plugin.getLocale());
}
values.getProperties().put(PLUGIN_FRAGMENT, Boolean.toString(plugin.isFragment()));
values.getProperties().put(PLUGIN_UNPACK, Boolean.toString(plugin.isUnpack()));
// return the values
return values;
}
});
}
}
/**
* <p>
* </p>
*
* @param macroDef
*/
private void executeRootFeatureScopedMacroDef(MacroDef macroDef) {
if (A4ELogging.isDebuggingEnabled()) {
A4ELogging.debug("executeRootFeatureScopedMacroDef");
}
// execute macro
executeMacroInstance(macroDef, new MacroExecutionValuesProvider() {
public MacroExecutionValues provideMacroExecutionValues(MacroExecutionValues values) {
// feature is an eclipse feature project
if (ExecuteFeatureTask.this._resolvedFeature.getSource() instanceof EclipseProject) {
// get the eclipse project
EclipseProject eclipseProject = (EclipseProject) ExecuteFeatureTask.this._resolvedFeature.getSource();
// set the properties
values.getProperties().put(FEATURE_IS_SOURCE, "true");
values.getProperties().put(FEATURE_FILE, eclipseProject.getFolder().getAbsolutePath());
values.getProperties().put(FEATURE_FILE_NAME, eclipseProject.getSpecifiedName());
// FeatureProjectRole featureProjectRole = FeatureProjectRole.Helper.getFeatureProjectRole(eclipseProject);
// if (featureProjectRole.hasBuildProperties()) {
// FeatureBuildProperties buildProperties = featureProjectRole.getBuildProperties();
// values.getProperties().put(BUILD_PROPERTIES_BINARY_INCLUDES, buildProperties.getBinaryIncludesAsString());
// values.getProperties().put(BUILD_PROPERTIES_BINARY_EXCLUDES, buildProperties.getBinaryExcludesAsString());
// }
// set references
values.getReferences().put(FEATURE_FILE_PATH, convertToPath(eclipseProject.getFolder()));
}
// feature is a archive or a directory
else if (ExecuteFeatureTask.this._resolvedFeature.getSource() instanceof File) {
values.getProperties().put(FEATURE_IS_SOURCE, "false");
// get the source file
File file = (File) ExecuteFeatureTask.this._resolvedFeature.getSource();
// set properties
values.getProperties().put(FEATURE_FILE, file.getAbsolutePath());
values.getProperties().put(FEATURE_FILE_NAME, file.getName());
// set references
values.getReferences().put(FEATURE_FILE_PATH, convertToPath(file));
values.getReferences().put(FEATURE_FILELIST, FileListHelper.getFileList(file));
}
// get the feature manifest
FeatureManifest manifest = ExecuteFeatureTask.this._resolvedFeature.getFeatureManifest();
// set the properties
values.getProperties().put(FEATURE_ID, manifest.getId());
values.getProperties().put(FEATURE_VERSION, manifest.getVersion().toString());
Version resolvedFeatureVersion = PdeBuildHelper.resolveVersion(manifest.getVersion(), PdeBuildHelper
.getResolvedContextQualifier());
values.getProperties().put(FEATURE_RESOLVED_VERSION, resolvedFeatureVersion.toString());
if (Utilities.hasText(manifest.getLabel())) {
values.getProperties().put(FEATURE_LABEL, manifest.getLabel());
}
if (Utilities.hasText(manifest.getProviderName())) {
values.getProperties().put(FEATURE_PROVIDERNAME, manifest.getProviderName());
}
values.getProperties().put(FEATURE_PLUGINS_RESOLVED_VERSIONS, ExecuteFeatureTask.this._resolvedBundleVersions);
// return the values
return values;
}
});
}
/**
* <p>
* Execute the macro definitions for each included feature.
* </p>
*
* @param macroDef
* the macro definition to execute
*/
private void executeIncludedFeatureScopedMacroDef(MacroDef macroDef) {
if (A4ELogging.isDebuggingEnabled()) {
A4ELogging.debug("executeIncludedFeatureScopedMacroDef");
}
// iterate over the includes>
for (final Pair<Includes, FeatureDescription> pair : this._resolvedFeature.getIncludesToFeatureDescriptionList()) {
// execute macro definition
executeMacroInstance(macroDef, new MacroExecutionValuesProvider() {
public MacroExecutionValues provideMacroExecutionValues(MacroExecutionValues values) {
// add the feature id
values.getProperties().put(FEATURE_ID, pair.getFirst().getId());
// add the feature version
values.getProperties().put(FEATURE_VERSION, pair.getFirst().getVersion().toString());
// add the resolved version
Version resolvedFeatureVersion = PdeBuildHelper.resolveVersion(pair.getSecond().getFeatureManifest()
.getVersion(), PdeBuildHelper.getResolvedContextQualifier());
values.getProperties().put(FEATURE_RESOLVED_VERSION, resolvedFeatureVersion.toString());
// add the name
if (Utilities.hasText(pair.getFirst().getName())) {
values.getProperties().put(FEATURE_NAME, pair.getFirst().getName());
}
// add the optional value
values.getProperties().put(FEATURE_OPTIONAL, Boolean.toString(pair.getFirst().isOptional()));
// add the searchLocation
if (Utilities.hasText(pair.getFirst().getSearchLocation())) {
values.getProperties().put(FEATURE_SEARCH_LOCATION, pair.getFirst().getSearchLocation());
}
// add the operatingSystem
if (Utilities.hasText(pair.getFirst().getOperatingSystem())) {
values.getProperties().put(FEATURE_OS, pair.getFirst().getOperatingSystem());
}
// add the machineArchitecture
if (Utilities.hasText(pair.getFirst().getMachineArchitecture())) {
values.getProperties().put(FEATURE_ARCH, pair.getFirst().getMachineArchitecture());
}
// add the windowingSystem
if (Utilities.hasText(pair.getFirst().getWindowingSystem())) {
values.getProperties().put(FEATURE_WS, pair.getFirst().getWindowingSystem());
}
// add the locale
if (Utilities.hasText(pair.getFirst().getLocale())) {
values.getProperties().put(FEATURE_NL, pair.getFirst().getLocale());
}
// return the values
return values;
}
});
}
}
/**
* <p>
* Resolves the specified feature description by matching each entry in the feature to a 'real' bundle in the target
* platform.
* </p>
*
* @return the resolved feature
*/
private ResolvedFeature resolveFeature() {
// create a new target platform configuration
PlatformConfiguration configuration = new PlatformConfiguration();
configuration.setPreferProjects(true);
// fetch the target platform
TargetPlatform targetPlatform = this._targetPlatformAwareDelegate.getTargetPlatform(getWorkspace());
// let the target platform resolve the feature
// case 1: pde feature project
if (isProjectNameSet()) {
FeatureManifest featureManifest = getEclipseProject().getRole(FeatureProjectRole.class).getFeatureManifest();
return targetPlatform.resolveFeature(getEclipseProject(), featureManifest);
}
// case 2: feature taken from the target platform
else {
if (A4ELogging.isDebuggingEnabled()) {
A4ELogging.debug("Trying to get feature '%s_%s' from target platform.", this._featureId, this._featureVersion);
}
Version resolvedVersion = PdeBuildHelper.resolveVersion(this._featureVersion, PdeBuildHelper
.getResolvedContextQualifier());
FeatureDescription featureDescription = targetPlatform.getFeatureDescription(this._featureId, resolvedVersion);
FeatureManifest featureManifest = featureDescription.getFeatureManifest();
return targetPlatform.resolveFeature(featureDescription.getSource(), featureManifest);
}
}
/**
* <p>
* Creates a (comma-separated) list with bundles ids and resolved versions, e.g.
* <code><pre>testproject=1.0.0;org.eclipse.osgi=3.4.2.R34x_v20080826-1230;org.eclipse.osgi.util=3.1.300.v20080303;org.eclipse.
* osgi.services=3.1.200.v20071203;example_bundle=1.0.0.200907270913.
* </pre><code>
* </p>
* <p>
* This list can be used within the {@link PatchFeatureManifestTask} to patch each bundle that has a version '0.0.0'
* (the default version).
* </p>
*
* @return a (comma-separated) list with bundles ids and resolved versions.
*/
private String extractBundleVersions() {
// create result
StringBuilder result = new StringBuilder();
// iterate over all the resolved bundles and add them to the result...
for (Iterator<Pair<Plugin, BundleDescription>> iterator = this._resolvedFeature.getPluginToBundleDescptionList()
.iterator(); iterator.hasNext();) {
Pair<Plugin, BundleDescription> pair = iterator.next();
// add id and resolved version
result.append(pair.getFirst().getId());
result.append("=");
result.append(PdeBuildHelper.resolveVersion(pair.getSecond().getVersion(), PdeBuildHelper
.getResolvedContextQualifier()));
// append ';' if necessary
if (iterator.hasNext()) {
result.append(";");
}
}
// return the result
return result.toString();
}
}