@Kusal, starting with 10.0.0-m28 we cannot resolve com.atlassian.confluence.user.persistence.dao.ConfluenceUserDao anymore. We use its getAll() for clean-up purposes because it also returns deleted users (i.e. userAccessor.exists(user.getName()) == false). Is there another way to access it? Or another way for getting all users, incl. deactivated and deleted ones?
I test 10.0.0-m28 use com.plugin.atlassian.plugins.rest:atlassian-rest-v2-plugin:9.0.0-jakarta-m008 (from confluence-plugins-platform-pom:10.0.0-m28), java 21 and have this error
Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 66
at org.objectweb.asm.ClassReader.<init>(ClassReader.java:199)
at org.objectweb.asm.ClassReader.<init>(ClassReader.java:180)
at org.objectweb.asm.ClassReader.<init>(ClassReader.java:166)
at org.objectweb.asm.ClassReader.<init>(ClassReader.java:287)
at com.atlassian.plugins.rest.v2.scanner.JarIndexer.getClassReader(JarIndexer.java:99)
... 112 more
Stacktrace shows this class com.atlassian.plugins.rest.v2.scanner.JarIndexerException. But java version 66 is java 22. Right? Did I get something wrong or this artifact was compiled for java 22?
Kind regards
Full Stacktrace
com.atlassian.plugins.rest.v2.scanner.JarIndexerException: Error accessing input stream of the jar file /var/atlassian/application-data/confluence/plugins-cache/1743502456117my-plugin-6.0.1-SNAPSHOT.jar
at com.atlassian.plugins.rest.v2.scanner.JarIndexer.getClassReader(JarIndexer.java:101)
at com.atlassian.plugins.rest.v2.scanner.JarIndexer.analyzeClassFile(JarIndexer.java:83)
at com.atlassian.plugins.rest.v2.scanner.JarIndexer.scanJar(JarIndexer.java:71)
at com.atlassian.plugins.rest.v2.scanner.AnnotatedClassScanner.scan(AnnotatedClassScanner.java:46)
at com.atlassian.plugins.rest.v2.jersey.ResourceConfigFactory.createConfig(ResourceConfigFactory.java:99)
at com.atlassian.plugins.rest.v2.servlet.RestDelegatingServletFilter.<init>(RestDelegatingServletFilter.java:69)
at com.atlassian.plugins.rest.v2.descriptor.RestServletFilterModuleDescriptor.<init>(RestServletFilterModuleDescriptor.java:42)
at com.atlassian.plugins.rest.v2.descriptor.RestModuleDescriptor.enabled(RestModuleDescriptor.java:171)
at com.atlassian.plugin.manager.DefaultPluginManager.lambda$notifyModuleEnabled$47(DefaultPluginManager.java:1994)
at com.atlassian.plugin.manager.PluginTransactionContext.wrap(PluginTransactionContext.java:64)
at com.atlassian.plugin.manager.DefaultPluginManager.notifyModuleEnabled(DefaultPluginManager.java:1990)
at com.atlassian.plugin.manager.DefaultPluginManager.lambda$enableConfiguredPluginModule$32(DefaultPluginManager.java:1718)
at com.atlassian.plugin.manager.PluginTransactionContext.wrap(PluginTransactionContext.java:73)
at com.atlassian.plugin.manager.DefaultPluginManager.enableConfiguredPluginModule(DefaultPluginManager.java:1700)
at com.atlassian.plugin.manager.DefaultPluginManager.lambda$enableConfiguredPluginModules$31(DefaultPluginManager.java:1688)
at com.atlassian.plugin.manager.PluginTransactionContext.wrap(PluginTransactionContext.java:73)
at com.atlassian.plugin.manager.DefaultPluginManager.enableConfiguredPluginModules(DefaultPluginManager.java:1685)
at com.atlassian.plugin.manager.DefaultPluginManager.lambda$enableDependentPlugins$24(DefaultPluginManager.java:1314)
at com.atlassian.plugin.manager.PluginTransactionContext.wrap(PluginTransactionContext.java:64)
at com.atlassian.plugin.manager.DefaultPluginManager.enableDependentPlugins(DefaultPluginManager.java:1280)
at com.atlassian.plugin.manager.DefaultPluginManager.lambda$addPlugins$22(DefaultPluginManager.java:1264)
at com.atlassian.plugin.manager.PluginTransactionContext.wrap(PluginTransactionContext.java:64)
at com.atlassian.plugin.manager.DefaultPluginManager.addPlugins(DefaultPluginManager.java:1151)
at com.atlassian.plugin.manager.DefaultPluginManager.lambda$scanForNewPlugins$14(DefaultPluginManager.java:953)
at com.atlassian.plugin.manager.PluginTransactionContext.wrap(PluginTransactionContext.java:64)
at com.atlassian.plugin.manager.DefaultPluginManager.scanForNewPlugins(DefaultPluginManager.java:907)
at com.atlassian.plugin.manager.DefaultPluginManager.lambda$installPlugins$13(DefaultPluginManager.java:866)
at com.atlassian.plugin.manager.PluginTransactionContext.wrap(PluginTransactionContext.java:64)
at com.atlassian.plugin.manager.DefaultPluginManager.installPlugins(DefaultPluginManager.java:853)
at com.atlassian.confluence.impl.plugin.EventDispatchingPluginController.installPlugins(EventDispatchingPluginController.java:74)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:380)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
at jdk.proxy4/jdk.proxy4.$Proxy471.installPlugins(Unknown Source)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26)
at jdk.proxy4/jdk.proxy4.$Proxy837.installPlugins(Unknown Source)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
at jdk.proxy24/jdk.proxy24.$Proxy1031.installPlugins(Unknown Source)
at com.atlassian.upm.core.install.AbstractPluginInstallHandler$1.doInTransaction(AbstractPluginInstallHandler.java:115)
at com.atlassian.upm.core.install.AbstractPluginInstallHandler$1.doInTransaction(AbstractPluginInstallHandler.java:112)
at com.atlassian.sal.core.transaction.HostContextTransactionTemplate$1.doInTransaction(HostContextTransactionTemplate.java:21)
at com.atlassian.sal.spring.component.SpringHostContextAccessor.lambda$doInTransaction$0(SpringHostContextAccessor.java:72)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)
at com.atlassian.sal.spring.component.SpringHostContextAccessor.doInTransaction(SpringHostContextAccessor.java:70)
at com.atlassian.sal.core.transaction.HostContextTransactionTemplate.execute(HostContextTransactionTemplate.java:18)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at com.atlassian.plugin.util.ContextClassLoaderSettingInvocationHandler.invoke(ContextClassLoaderSettingInvocationHandler.java:26)
at jdk.proxy4/jdk.proxy4.$Proxy525.execute(Unknown Source)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:359)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.doInvoke(ServiceInvoker.java:56)
at org.eclipse.gemini.blueprint.service.importer.support.internal.aop.ServiceInvoker.invoke(ServiceInvoker.java:60)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invokeUnprivileged(ServiceTCCLInterceptor.java:70)
at org.eclipse.gemini.blueprint.service.util.internal.aop.ServiceTCCLInterceptor.invoke(ServiceTCCLInterceptor.java:53)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.eclipse.gemini.blueprint.service.importer.support.LocalBundleContextAdvice.invoke(LocalBundleContextAdvice.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:137)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:124)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
at jdk.proxy24/jdk.proxy24.$Proxy929.execute(Unknown Source)
at com.atlassian.upm.core.install.AbstractPluginInstallHandler.installArtifacts(AbstractPluginInstallHandler.java:112)
at com.atlassian.upm.core.install.ObrPluginInstallHandler.installResources(ObrPluginInstallHandler.java:225)
at com.atlassian.upm.core.install.ObrPluginInstallHandler.installPluginInternal(ObrPluginInstallHandler.java:151)
at com.atlassian.upm.core.install.AbstractPluginInstallHandler.installPlugin(AbstractPluginInstallHandler.java:55)
at com.atlassian.upm.core.install.DefaultPluginInstallationService.execute(DefaultPluginInstallationService.java:173)
at com.atlassian.upm.core.install.DefaultPluginInstallationService.install(DefaultPluginInstallationService.java:113)
at com.atlassian.upm.install.UpmPluginInstallationService.install(UpmPluginInstallationService.java:129)
at com.atlassian.upm.core.rest.resources.install.InstallTask.installFromFile(InstallTask.java:132)
at com.atlassian.upm.core.rest.resources.install.InstallFromFileUploadTask.lambda$executeTask$0(InstallFromFileUploadTask.java:83)
at com.atlassian.upm.api.util.Either$Right.fold(Either.java:138)
at com.atlassian.upm.core.rest.resources.install.InstallFromFileUploadTask.executeTask(InstallFromFileUploadTask.java:81)
at com.atlassian.upm.core.rest.resources.install.InstallTask.run(InstallTask.java:80)
at com.atlassian.upm.core.async.AsynchronousTaskManager.executeTask(AsynchronousTaskManager.java:117)
at com.atlassian.upm.core.async.AsynchronousTaskManager$1.call(AsynchronousTaskManager.java:99)
at com.atlassian.upm.core.async.AsynchronousTaskManager$1.call(AsynchronousTaskManager.java:97)
at com.atlassian.sal.core.executor.ThreadLocalDelegateCallable.call(ThreadLocalDelegateCallable.java:38)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
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: java.lang.IllegalArgumentException: Unsupported class file major version 66
at org.objectweb.asm.ClassReader.<init>(ClassReader.java:199)
at org.objectweb.asm.ClassReader.<init>(ClassReader.java:180)
at org.objectweb.asm.ClassReader.<init>(ClassReader.java:166)
at org.objectweb.asm.ClassReader.<init>(ClassReader.java:287)
at com.atlassian.plugins.rest.v2.scanner.JarIndexer.getClassReader(JarIndexer.java:99)
... 112 more
Bruno means, do you have a date? I mean, it will take at least 2 months before the release, won’t it? We’re just trying to organize our priorities locally.
It’s a sensible suggestion and one which we intend to action at some point, although it may not be in 10.0.
I believe your question has been answered above. Unfortunately the AMPS plugin enforcer rule is not DMZ aware so you should add necessary exclusions to work around it.
Are either of the following APIs suitable for your use case?
@Kusal No, unfortunately not because they don’t return deleted users (the JavaDoc of getUsers() also mentions this).
But I did find an alternative after all by looking into how Confluence’s /admin/users/showallunsyncedusers.action does it: Using com.atlassian.confluence.api.service.people.PersonService.PersonSearcher#forUnsyncedUsers like so:
List<Person> deletedUsers = personService.search()
.forUnsyncedUsers("")
.fetchMany(new SimplePageRequest(0, 100))
.getResults();
// in the end, I need UserKeys of deleted users in order to clean-up my app's data
List<String> deletedUserKeys = deletedUsers.stream()
.map(p -> p.optionalUserKey().map(UserKey::getStringValue).orElse(null))
.toList();
I successfully tested this with Confluence 9.3 and 10.0.0-m28, so far so good.
Can you confirm that PersonService.PersonSearcher is public API and not being planned for removal soon? Wouldn’t wan to rework this code again soon…
Is it possible to look into fixing the XsrfTokenGenerator somehow so that it can be used without requiring compile-time visibility of the old javax.servlet package?
Presumably due to the new “default” methods added to the interface, I cannot compile the following client code against the 10.0.0-m28 milestone. I receive the following error on compilation, even though I do not reference the javax.servlet classes at all:
BadClass.java:21:27
java: cannot access javax.servlet.http.HttpServletRequest
class file for javax.servlet.http.HttpServletRequest not found
Code:
package mypackage;
import com.atlassian.struts.xsrf.XsrfTokenGenerator;
import jakarta.inject.Inject;
import org.springframework.stereotype.Component;
@Component
public class BadClass
{
private XsrfTokenGenerator xsrfTokenGenerator;
@Inject
public BadClass(XsrfTokenGenerator xsrfTokenGenerator)
{
this.xsrfTokenGenerator = xsrfTokenGenerator;
}
public void test()
{
jakarta.servlet.http.HttpServletRequest req = null;
xsrfTokenGenerator.generateToken(req);
}
}
The only way I can get this code to compile is to include the old javax.servlet-api in the pom with compile scope, but I fear that this will eventually stop working when this gets banned (and I would rather not have it on the compile classpath to prevent inadvertent errors).
The above code is just an example where req could be inferred to be null by the compiler, but the problem also occurs in production code where the HttpServletRequest is a valid jakarta servlet object.
Yes, the APIs in com.atlassian.confluence.api are part of our official Java API and are at significantly lower risk of breaking changes.
This is a known issue (with this and some other classes) and will be fixed soon. Having javax.servlet-api in provided scope should be sufficient as a workaround for now.
It should still be exported - there may be a different configuration issue with your plugin. Do also ensure you’re using Atlassian Spring Scanner 6.0.0.
The plugin is built with Spring Scanner v6.0.0-jakarta-m001, and I also confirmed that the output Spring MANIFEST.MF has an Import-Package for com.atlassian.struts.xsrf. The same app is correctly importing dozens of other beans (so this is the only one that fails).
I first noticed this problem within the -m28 milestone, whereas it was previously working in at least some earlier 10.0 milestones.
Any other ideas? Although anything is possible, I do not believe I changed anything in this regard.
EDIT: This only applies to Struts/XWork actions.
We use com.atlassian.confluence.struts.StrutsHelper#getUploadedFile in our app. We read about it in Preparing for Confluence 10.0 → Struts 7.0 upgrade. Sample code for a Struts action class:
We have an issue with our rest resources after switching to Jakarta. In our pom, we switched from Javax to the respective Jakarta packages in the provided scope and used the recommended DependencyManagement confluence-plugins-platform-pom. So we did not have to specify any Jakarta versions. Then we replaced all javax with jakarta imports, besides javax.ws.rs. It seems like the provided jakarta.ws.rs version 2.1.6 still exports everything as javax.ws.rs, is this correct?
The plugin enables successfully, but none of our resources seem to work, I only get 404s. Any ideas what could be the reason for this?
Testing in Struts actions is not the same as in a scheduled job.
And PersonService checks permissions of the currently logged-in user – in scheduled jobs there is no logged-in user:
2025-04-04 16:38:17,029 ERROR [Caesium-1-4] [impl.schedule.caesium.JobRunnerWrapper] runJob Scheduled job de.communardo.confluence.plugins.userprofile:purgeUserDataJob#purgeUserDataJobKey-runNow-1743777496958 failed to run
com.atlassian.confluence.api.service.exceptions.PermissionException: User not permitted to view user profiles
at com.atlassian.confluence.api.model.validation.SimpleValidationResult.lambda$static$0(SimpleValidationResult.java:30)
at com.atlassian.confluence.api.model.validation.SimpleValidationResult.convertToServiceException(SimpleValidationResult.java:81)
at com.atlassian.confluence.api.model.validation.ValidationResult.throwIfNotSuccessful(ValidationResult.java:195)
at com.atlassian.confluence.api.impl.service.people.PersonServiceImpl.search(PersonServiceImpl.java:297)
...
So, I don’t think PersonService is a viable alternative for us after all
Thinking of solutions, I know we can set the logged-in user (via AuthenticatedUserThreadLocal#set), but we wouldn’t know which admin-user to pick. Sure, we could make that configurable to the customer, but that feels clunky.
We’d like to have ConfluenceUserDao back – or another way to fetch deleted users in a scheduled job without user-context.
For whatever reason, the XsrfTokenGenerator bean can no longer be autowired (or at least not from the context of a <velocity-context-item>, and perhaps not elsewhere).
Despite this, I discovered that it can still be retrieved manually in the constructor like this:
Handling multipart uploads is recommended to be done in the following ways:
For REST APIs: Using the annotations in package com.atlassian.plugins.rest.api.multipart
For Struts Actions: Using the com.atlassian.confluence.struts.StrutsHelper OSGi service
For custom Servlets: Using the utilities in org.apache.commons.fileupload2.jakarta.servlet6
You should double check your POM configuration as the confluence-plugins-platform-pom is not providing that old version for jakarta.ws.rs-api. You should not have any javax.ws.rs imports in your plugin.
I’ll have to get back to you on this. It may be possible we need to introduce a new API.
I’ll have to test this myself and get back to you as I can’t at first glance see any recent changes that should prevent it.
That API uses a completely different mechanism. It retrieves a Spring bean by-name from the internal Confluence Spring context. It’s not officially supported.
The usual injection methods (<component-import> [transformed], @ComponentImport [Spring Scanner] and #importOsgiService [Spring Java config]) retrieves public OSGi services by type.