TaskExporter.toSpecsEntity LinkageError

I’m having a problem with a Bamboo task app that I have developed. Using Bamboo version 6.10.2.
It is the TaskExporter class that is not working.
When I try to export a plan to bamboo specs using the menuitem View plan as Java Specs it throws a LinkageError as below:

Export to Bamboo Specs failed: java.lang.LinkageError: loader constraint violation in interface itable initialization: when resolving method “com.mmm.bamboo.plugins.releasenotes.exporter.ReleaseNotesTaskExporter.toSpecsEntity(Lcom/atlassian/bamboo/task/TaskDefinition;)Lcom/atlassian/bamboo/specs/api/builders/task/Task;” the class loader (instance of org/apache/felix/framework/BundleWiringImpl$BundleClassLoaderJava5) of the current class, com/mmm/bamboo/plugins/releasenotes/exporter/ReleaseNotesTaskExporter, and the class loader (instance of org/apache/catalina/loader/WebappClassLoader) for interface com/atlassian/bamboo/task/export/TaskDefinitionExporter have different Class objects for the type com/atlassian/bamboo/specs/api/builders/task/Task used in the signature

The code for the exporter:

package com.mmm.bamboo.plugins.releasenotes.exporter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.atlassian.bamboo.specs.api.builders.task.Task;
import com.atlassian.bamboo.specs.api.model.task.TaskProperties;
import com.atlassian.bamboo.specs.api.validators.common.ValidationContext;
import com.atlassian.bamboo.specs.api.validators.common.ValidationProblem;
import com.atlassian.bamboo.task.TaskContainer;
import com.atlassian.bamboo.task.TaskDefinition;
import com.atlassian.bamboo.task.export.TaskDefinitionExporter;
import com.atlassian.bamboo.task.export.TaskValidationContext;
import com.atlassian.bamboo.util.Narrow;
import com.atlassian.util.concurrent.NotNull;
import com.google.common.base.Preconditions;
import com.mmm.bamboo.plugins.releasenotes.ReleaseNotesTaskConfigurator;
import com.mmm.bamboo.specs.model.task.ReleaseNotesTaskProperties;

public class ReleaseNotesTaskExporter implements TaskDefinitionExporter {
	private static final ValidationContext VALIDATION_CONTEXT = ValidationContext.of("Release Notes task");

	@NotNull
	@Override
	public Task toSpecsEntity(final TaskDefinition taskDefinition) {
		final Map<String, String> configuration = taskDefinition.getConfiguration();
		return new com.mmm.bamboo.specs.builders.task.ReleaseNotesTask()
				.jiraFixVersion(configuration.get(ReleaseNotesTaskConfigurator.JIRA_FIX_VERSION_KEY))
				.jiraLabel(configuration.getOrDefault(ReleaseNotesTaskConfigurator.JIRA_LABEL_KEY, null))
				.jiraPassword(configuration.get(ReleaseNotesTaskConfigurator.JIRA_PASSWORD_KEY))
				.jiraProjectName(configuration.get(ReleaseNotesTaskConfigurator.JIRA_PROJECT_NAME_KEY))
				.jiraUser(configuration.get(ReleaseNotesTaskConfigurator.JIRA_USER_KEY))
				.nuspecFilePath(configuration.get(ReleaseNotesTaskConfigurator.NUSPEC_FILE_NAME_KEY))
				.pdfFilePath(configuration.get(ReleaseNotesTaskConfigurator.PDF_FILE_NAME_KEY))
				.exportToNuGet(
						Boolean.parseBoolean(configuration.get(ReleaseNotesTaskConfigurator.EXPORT_TO_NUGET_KEY)))
				.exportToPdf(Boolean.parseBoolean(configuration.get(ReleaseNotesTaskConfigurator.EXPORT_TO_PDF_KEY)));
	}

	@Override
	public Map<String, String> toTaskConfiguration(final TaskContainer taskContainer,
			final TaskProperties taskProperties) {
		final ReleaseNotesTaskProperties releaseNotesTaskProperties = Narrow.downTo(taskProperties,
				ReleaseNotesTaskProperties.class);
		Preconditions.checkState(releaseNotesTaskProperties != null,
				"Don't know how to import task properties of type: " + taskProperties.getClass().getName());

		final Map<String, String> cfg = new HashMap<>();
		cfg.put(ReleaseNotesTaskConfigurator.JIRA_FIX_VERSION_KEY, releaseNotesTaskProperties.getJiraFixVersion());
		cfg.put(ReleaseNotesTaskConfigurator.JIRA_PASSWORD_KEY, releaseNotesTaskProperties.getJiraPassword());
		cfg.put(ReleaseNotesTaskConfigurator.JIRA_PROJECT_NAME_KEY, releaseNotesTaskProperties.getJiraProjectName());
		cfg.put(ReleaseNotesTaskConfigurator.JIRA_USER_KEY, releaseNotesTaskProperties.getJiraUser());
		if (releaseNotesTaskProperties.getNuspecFilePath() != null) {
			cfg.put(ReleaseNotesTaskConfigurator.NUSPEC_FILE_NAME_KEY, releaseNotesTaskProperties.getNuspecFilePath());
		}
		if (releaseNotesTaskProperties.getJiraLabel() != null) {
			cfg.put(ReleaseNotesTaskConfigurator.JIRA_LABEL_KEY, releaseNotesTaskProperties.getJiraLabel());
		}
		if (releaseNotesTaskProperties.getPdfFilePath() != null) {
			cfg.put(ReleaseNotesTaskConfigurator.PDF_FILE_NAME_KEY, releaseNotesTaskProperties.getPdfFilePath());
		}
		cfg.put(ReleaseNotesTaskConfigurator.EXPORT_TO_NUGET_KEY,
				Boolean.toString(releaseNotesTaskProperties.getExportToNuGet()));
		cfg.put(ReleaseNotesTaskConfigurator.EXPORT_TO_PDF_KEY,
				Boolean.toString(releaseNotesTaskProperties.getExportToPdf()));
		return cfg;
	}

	@Override
	public List<ValidationProblem> validate(final TaskValidationContext taskValidationContext,
			final TaskProperties taskProperties) {
		final List<ValidationProblem> validationProblems = new ArrayList<>();
		final ReleaseNotesTaskProperties releaseNotesTaskProperties = Narrow.downTo(taskProperties,
				ReleaseNotesTaskProperties.class);
		if (releaseNotesTaskProperties != null) {
			if (releaseNotesTaskProperties.getExportToNuGet() == true) {
				if (releaseNotesTaskProperties.getNuspecFilePath() == null
						|| releaseNotesTaskProperties.getNuspecFilePath().isEmpty()) {
					validationProblems.add(new ValidationProblem(VALIDATION_CONTEXT, "Nuspec file path is not set!"));
				}
			}

			if (releaseNotesTaskProperties.getExportToPdf() == true) {
				if (releaseNotesTaskProperties.getPdfFilePath() == null
						|| releaseNotesTaskProperties.getPdfFilePath().isEmpty()) {
					validationProblems.add(new ValidationProblem(VALIDATION_CONTEXT, "PDF file path is not set!"));
				}
			}
		}
		return validationProblems;
	}

}

The code for the Task:

package com.mmm.bamboo.specs.builders.task;

import com.atlassian.bamboo.specs.api.builders.task.Task;
import com.atlassian.bamboo.specs.api.validators.common.ImporterUtils;
import com.atlassian.util.concurrent.NotNull;
import com.atlassian.util.concurrent.Nullable;
import com.mmm.bamboo.specs.model.task.ReleaseNotesTaskProperties;

public class ReleaseNotesTask extends Task<ReleaseNotesTask, ReleaseNotesTaskProperties> {

    @NotNull
    private String jiraFixVersion;
    @Nullable
    private String jiraLabel;
    @NotNull
    private String jiraUser;
    @NotNull
    private String jiraPassword;
    @NotNull
    private String jiraProjectName;
    @Nullable
	private String nuspecFilePath;
    @Nullable
    private String pdfFilePath;
    private boolean exportToNuGet;
    private boolean exportToPdf;

    public ReleaseNotesTask jiraFixVersion(@NotNull final String jiraFixVersion) {
        ImporterUtils.checkNotEmpty("jiraFixVersion", jiraFixVersion);
        this.jiraFixVersion = jiraFixVersion;
        return this;
    }

    public ReleaseNotesTask jiraLabel(@Nullable final String jiraLabel) {
        this.jiraLabel = jiraLabel;
        return this;
    }

    public ReleaseNotesTask jiraPassword(@NotNull final String jiraPassword) {
        ImporterUtils.checkNotEmpty("jiraPassword", jiraPassword);
        this.jiraPassword = jiraPassword;
        return this;
    }

    public ReleaseNotesTask jiraProjectName(@NotNull final String jiraProjectName) {
        ImporterUtils.checkNotEmpty("jiraProjectName", jiraProjectName);
        this.jiraProjectName = jiraProjectName;
        return this;
    }

    public ReleaseNotesTask jiraUser(@NotNull final String jiraUser) {
        ImporterUtils.checkNotEmpty("jiraUser", jiraUser);
        this.jiraUser = jiraUser;
        return this;
    }

    public ReleaseNotesTask nuspecFilePath(@Nullable final String nuspecFilePath) {
        this.nuspecFilePath = nuspecFilePath;
        return this;
    }
    
    public ReleaseNotesTask pdfFilePath(@Nullable final String pdfFilePath) {
        this.pdfFilePath = pdfFilePath;
        return this;
    }
    
    public ReleaseNotesTask exportToNuGet(final boolean exportToNuGet) {
        this.exportToNuGet = exportToNuGet;
        return this;
    }
    
    public ReleaseNotesTask exportToPdf(final boolean exportToPdf) {
        this.exportToPdf = exportToPdf;
        return this;
    }

    @Override
    protected ReleaseNotesTaskProperties build() {
        return new ReleaseNotesTaskProperties(this.description, this.taskEnabled, this.jiraFixVersion, this.jiraLabel,
                this.jiraUser, this.jiraPassword, this.jiraProjectName, this.nuspecFilePath, this.pdfFilePath,
                this.exportToNuGet, this.exportToPdf, this.requirements, this.conditions);
    }

}

What am I doing wrong?
The task is working fine when using it in the UI and running a plan build, but we use bamboo-specs, so I need this to work.

Please help!

/Jan Lovstrand

1 Like

I’m adding this in case someone in the future experiences the same issue. The solution at least for me was to add provided to the bamboo-specs-api dependency in the pom.xml file. This is what it now looks like:

    <dependency>
        <groupId>com.atlassian.bamboo</groupId>
        <artifactId>bamboo-specs-api</artifactId>
        <version>6.10.4</version>
        <scope>provided</scope>
    </dependency>
1 Like