Active Objects NoSuchBeanDefinitionException

I get the typical no such bean exception when injecting an AO object… all classes are in the same package!!

I use this dependency

		<dependency>
			<groupId>com.atlassian.activeobjects</groupId>
			<artifactId>activeobjects-plugin</artifactId>
			<version>5.2.0</version>
			<scope>provided</scope>
		</dependency>

This is my Servlet class

@Scanned
public class MyServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	private static final String TEMPLATE = "home.vm";
	
	private final String user;

	Map<String, Object> context;

	private SearchService searchService;
	private TemplateRenderer templateRenderer;
	private final UserManager userManager;
	private final CustomFieldManager customFieldManager;
	private final WorklogManager worklogManager;
	private final IssueLinkManager issueLinkManager;
	private final LoginUriProvider loginUriProvider;
	private final PageBuilderService pageBuilderService;
	private BookedHoursTransactions bookedHoursTransactions;

	private final I18nResolver i18nResolver;

	@Inject
	public MyServlet(@ComponentImport SearchService searchService, @ComponentImport TemplateRenderer templateRenderer,
			@ComponentImport UserManager userManager, @ComponentImport CustomFieldManager customFieldManager, @ComponentImport WorklogManager worklogManager
			, @ComponentImport IssueLinkManager issueLinkManager, @ComponentImport LoginUriProvider loginUriProvider,
			@ComponentImport PageBuilderService pageBuilderService, @ComponentImport I18nResolver i18nResolver, @ComponentImport BookedHoursTransactions bookedHoursTransactions) {
		super();
		this.searchService = searchService;
		this.templateRenderer = templateRenderer;
		this.userManager = userManager;
		this.customFieldManager = customFieldManager;
		this.worklogManager = worklogManager;
		this.issueLinkManager = issueLinkManager;
		this.loginUriProvider = loginUriProvider;
		this.pageBuilderService = pageBuilderService;
		this.i18nResolver = i18nResolver;
		this.user = userManager.getRemoteUser().getUsername();
		this.bookedHoursTransactions = bookedHoursTransactions;
	}
	

Here the Interface

@Transactional
public interface BookedHoursTransactions {
	
	public BookedHours add(String clientId, int yearOfIssue, double spentHours, double spentTransferedHours, double transferedHours, LocalDate deadlineDate, int bookedHours, double availableHours, double remainingHours, double expiredHours);
	public void updateBookedHours(String projectKey, int newValue);
	public void updateFeatures(String projectKey, double transferedHours, double spentTransferedHours, double availableHours, double remainingHours, double expiredHours);
	public void updateDeadlineDate(String projectKey, LocalDate deadlineDate);
	public void updateAvailableHours(String projectKey, double availableHours);
	public void updateRemainingHours(String projectKey, double remainingHours);
	public void updateSpentHours(String projectKey, double spentHours);
	public List<BookedHours> all();
	
}

Here the implementation

@Scanned
@Named
public class BookedHoursTransactionsImpl implements BookedHoursTransactions {
	
	private ActiveObjects ao;
	private TransactionTemplate transactionTemplate;

    public BookedHoursTransactionsImpl(@ComponentImport ActiveObjects ao, @ComponentImport TransactionTemplate transactionTemplate) {
        this.ao = ao;
        this.transactionTemplate = transactionTemplate;
    }
//methods
}

Here the plugin.xml

atlassian-plugin key="${atlassian.plugin.key}"
	name="${project.name}" plugins-version="2">
	<plugin-info>
		<description>${project.description}</description>
		<version>${project.version}</version>
		<vendor name="${project.organization.name}"
			url="${project.organization.url}" />
		<param name="plugin-icon">images/pluginIcon.png</param>
		<param name="plugin-logo">images/pluginLogo.png</param>
	</plugin-info>

	<!-- add our i18n resource -->
	<resource type="i18n" name="i18n" location="xx" />
	<!-- add our web resources -->
	<web-resource key="xx-resources"
		name="xx Web Resources">
		<dependency>com.atlassian.auiplugin:ajs</dependency>
		<resource type="download" name="xx.css"
			location="/css/xx.css" />
		<resource type="download" name="xx.js"
			location="/js/xx.js" />
		<resource type="download" name="images/" location="/images" />
		<context>xx</context>
	</web-resource>
	<!-- publish our component -->

	<servlet name="myplugin" class="xxx.MyPlugin"
		key="xx">
		<url-pattern>/xx</url-pattern>
	</servlet>

	<web-section key="xx-section" name="xx Section"
		location="xx-section" weight="10" />

	<web-item key="xx_link" name="Link to xxServlet"
		section="system.top.navigation.bar" weight="47">
		<label>xx</label>
		<link linkId="xx_link">/xx</link>
	</web-item>

	<ao key="ao-module">
		<description>The module configuring the Active Objects service used by
			this plugin</description>
		<entity>xx.BookedHours</entity>
	</ao>
	
</atlassian-plugin>

don’t think it’s necessary but my Entity class

ublic interface BookedHours extends Entity {
	
	public String getClientId();
	public void setClientId(String clientId);
	
	public int getYearOfIssue();
	public void setYearOfIssue(int yearOfIssue);
	
	public LocalDate getDeadlineDate();
	public void setDeadlineDate(LocalDate deadlineDate);
	
	public int getBookedHours();
	public void setBookedHours(int bookedHours);
	
	public double getSpentHours();
	public void setSpentHours(double spentHours);
	
	public double getTransferedHours();
	public void setTransferedHours(double transferedHours);
	
	public double getSpentTransferedHours();
	public void setSpentTransferedHours(double spentTransferedHours);
	
	public double getAvailableHours();
	public void setAvailableHours(double availableHours);
	
	public double getRemainingHours();
	public void setRemainingHours(double remainingHours);
	
	public double getExpiredHours();
	public void setExpiredHours(double expiredHours);
	
}

Remove the annotation from @ComponentImport from @ComponentImport BookedHoursTransactions bookedHoursTransactions within MyServlet.

You only add @ComponentImport to objects you are importing FROM Jira’s class loader.

Edit: Post your entire exception, it should clearly tell you it can’t find BookedHoursTransactions within Jira.

Thank you so much for answering me! Didn’t expect anyone to see this…

I did as you said, but I still get this error stack trace

2023-05-xx ThreadPoolAsyncTaskExecutor::Thread 132 ERROR zrt /rest/plugins/1.0/xx.jiraplugin.xx-key [o.e.g.b.e.internal.support.ExtenderConfiguration] Application context refresh failed (NonValidatingOsgiBundleXmlApplicationContext(bundle=xx, config=osgibundle:/META-INF/spring/*.xml))
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bookedHoursTransactionsImpl': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.atlassian.activeobjects.external.ActiveObjects' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:229)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1372)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1222)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:953)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.access$1600(AbstractDelegatedExecutionApplicationContext.java:57)
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext$4.run(AbstractDelegatedExecutionApplicationContext.java:322)
	at org.eclipse.gemini.blueprint.util.internal.PrivilegedUtils.executeWithCustomTCCL(PrivilegedUtils.java:85)
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.completeRefresh(AbstractDelegatedExecutionApplicationContext.java:287)
	at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor$CompleteRefreshTask.run(DependencyWaiterApplicationContextExecutor.java:137)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.base/java.lang.Thread.run(Unknown Source)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.atlassian.activeobjects.external.ActiveObjects' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1799)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1355)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1309)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)

Right, so in BookedHoursTransactionsImpl, you DO need to add @ComponentImport to private ActiveObjects ao; and private TransactionTemplate transactionTemplate; lines as they are components from Jira.

Also, use @Named, not @Scanned.

So MyServlet should look like this (NO annotation on class from your project: BookedHoursTransactions) :

@Named
public class MyServlet extends HttpServlet {
... 
        @Inject
	public MyServlet(@ComponentImport SearchService searchService, @ComponentImport TemplateRenderer templateRenderer,
			@ComponentImport UserManager userManager, @ComponentImport CustomFieldManager customFieldManager, @ComponentImport WorklogManager worklogManager
			, @ComponentImport IssueLinkManager issueLinkManager, @ComponentImport LoginUriProvider loginUriProvider,
			@ComponentImport PageBuilderService pageBuilderService, @ComponentImport I18nResolver i18nResolver, BookedHoursTransactions bookedHoursTransactions) 
        {
		super();
...

And BookedHoursTransactionsImpl should look like this (ADD annotation to classes from Jira container):

@Named
public class BookedHoursTransactionsImpl implements BookedHoursTransactions {
	@ComponentImport
	private ActiveObjects ao;
        @ComponentImport
	private TransactionTemplate transactionTemplate;

Thank you! Now I understood where to put the annotations but it still didn’t work .I removed the transaction template and the errors were gone. I didn’t quite understood why… I needed it to update data in the database but I think it will be possible with AO too (at least I hope so)