Preparing for Confluence 9.0 - EAP out now

Hi @adam.labus,

Did you manage to solve BUG 2? If so, can you explain how you worked around that?

Hi @nunoMSsantos

unfortunately I don’t know which operation to exactly fix this problem, but after full migration to Platform 7, switching to REST v2 and migrating to Spring Java configuration, all my REST APIs work fine.

Cheers
Adam

Hi @aorlov,

Thanks for providing EmptyList#size(). We also found it using an internal static analysis tool yesterday. We have created a rough tooling that gives us a strict and loose list of items from velocity files to mark on the allowlist. Upon review of the list, we are adding another round of allowlist items.
Given this feedback, I will evaluate with my team if we can make this script public to simplify plugin dev life.

Thanks,
Ganesh

3 Likes

A minor bug which Atlassian may want to track: macros implementing the EditorImagePlaceholder will fail in PDF export just showing a link “bad URL” pointing to the image placeholder.

Steps to reproduce:

  1. Create an app with a macro implementing EditorImagePlaceholder
  2. Create a page holding this macro
  3. Export the page to PDF

Same here. We are also stuck with the same issue coming from the MacroManager.

Hi @andreas1

Double check that you have the Atlassian-Plugin-Key set in your app descriptor. Otherwise many APIs just won’t work due to invalid OSGi imports created.

See Scott’s answer: Preparing for Confluence 9.0 - EAP out now - #272 by scott.dudley

We also switched to the Spring Java style config and removed all and in the atlassian-plugin.xml. However, I afaik having the Atlassian-Plugin-Key set in the jar’s manifest is the most important bit.

Dear @RomanStoffel ,

Yes we have it since a long time. Did this solve your issue?

Thank you

Andreas

Yes, for us the missing Atlassian-Plugin-Key in the plugins manifest was the main cuprid of many issues.

Did you double check that the com.atlassian.util.concurrent is not in your manifest? Because MacroManager.createLazyMacroReference uses stuff from `com.atlassian.util.concurrent’. So, you can’t use that method.

And after checking, I’ve noticed that we also wrap the MacroManager in our own MacroManagerWrapper. So, we import the MacroManager and only put into that wrapper. The rest of the system doesn’t use the MacroManager, to reduce the likelihood of triggering ‘some-magic’. However, not sure if that added to the solution =). Its one thing we did before adding the Atlassian-Plugin-Key fixed a lot of issues.

// In the spring Java Config, we import the Atlassian MacroManager
@Bean MacroManager macroManager(){return importOsgiService(MacroManager.class);}
// Then we wrap it and do not touch the MacroManager again.
@Bean public MacroManagerWrapper macroManagerWrapper(){return new MacroManagerWrapper(macroManager());}


// the wrapper is dead boring, exposing the things we actually use:

public class MacroManagerWrapper {
    private final MacroManager macroManager;

    public MacroManagerWrapper(MacroManager macroManager) {
        this.macroManager = macroManager;
    }

    public Macro getMacroByName(String name) {
        return macroManager.getMacroByName(name);
    }
}

I am having Jackson issues on m97(EDIT: and still on m116) now that I’m doing rest-v2 migration. We have been using jackson-databind for a while and have historically bundled our jackson packages. When instantiating our ObjectMapper we’re modifying it, also putting in additional modules like AfterBurnerModule and jsr310.JavaTimeModule.

According to the rest v2 upgrade guide, it seems like it is advising to change scope to provided, and can add provided scope to all jackson stuff like the modules:

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
  <scope>provided</scope>
</dependency>

However, when adding osgi imports for com.fasterxml.jackson.module/datatype, it fails. I also see that jsr310 is tagged as private:


2024-06-11 12:59:35,893 WARN [UpmAsynchronousTaskManager:thread-4] [osgi.hook.dmz.DmzResolverHook] filterMatches Package com.fasterxml.jackson.datatype.jsr310 is internal and is not available for export t
o plugin no.kantega.kerberosauth.kerberosauth-plugin
2024-06-11 12:59:35,894 WARN [UpmAsynchronousTaskManager:thread-4] [osgi.hook.dmz.DmzResolverHook] filterMatches Package com.fasterxml.jackson.module.paramnames is internal and is not available for export
 to plugin no.kantega.kerberosauth.kerberosauth-plugin
[........]
Caused by: org.osgi.framework.BundleException: Unable to resolve no.kantega.kerberosauth.plugin [300](R 300.0): missing requirement [no.kantega.kerberosauth.plugin [300](R 300.0)] osgi.wiring.package; (os
gi.wiring.package=com.fasterxml.jackson.module) Unresolved requirements: [[no.kantega.kerberosauth.plugin [300](R 300.0)] osgi.wiring.package; (osgi.wiring.package=com.fasterxml.jackson.module)]
        at org.apache.felix.framework.Felix.resolveBundleRevision(Felix.java:4398)
        at org.apache.felix.framework.Felix.startBundle(Felix.java:2308)
        at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:1006)
        at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:992)
        at com.atlassian.plugin.osgi.factory.OsgiPlugin.enableInternal(OsgiPlugin.java:408)

Edit: checked with m116, same issues still there. We don’t have the available osgi exports for the needed jackson modules:

2024-06-11 13:12:25,815 INFO [ThreadPoolAsyncTaskExecutor::Thread 3] [atlassian.ratelimiting.internal.RateLimitingConfiguration] userRequestHandler Rate Limited Request Handler ENABLED
2024-06-11 13:12:25,879 WARN [Catalina-utility-1] [osgi.hook.dmz.DmzResolverHook] filterMatches Package com.atlassian.rest.annotation is internal and is not available for export to plugin no.kantega.kerbe
rosauth.kerberosauth-plugin
2024-06-11 13:12:25,905 WARN [Catalina-utility-1] [osgi.hook.dmz.DmzResolverHook] filterMatches Package com.fasterxml.jackson.datatype.jsr310 is internal and is not available for export to plugin no.kante
ga.kerberosauth.kerberosauth-plugin
2024-06-11 13:12:25,908 WARN [Catalina-utility-1] [osgi.hook.dmz.DmzResolverHook] filterMatches Package com.fasterxml.jackson.module.paramnames is internal and is not available for export to plugin no.kan
tega.kerberosauth.kerberosauth-plugin
2024-06-11 13:12:25,945 WARN [Catalina-utility-1] [osgi.hook.dmz.DmzResolverHook] filterMatches Package net.sf.cglib.proxy is internal and is not available for export to plugin no.kantega.kerberosauth.ker
berosauth-plugin
2024-06-11 13:12:25,946 WARN [Catalina-utility-1] [osgi.hook.dmz.DmzResolverHook] filterMatches Package org.apache.commons.lang is internal and is not available for export to plugin no.kantega.kerberosaut
h.kerberosauth-plugin
2024-06-11 13:12:25,946 WARN [Catalina-utility-1] [osgi.hook.dmz.DmzResolverHook] filterMatches Package org.apache.commons.lang.time is internal and is not available for export to plugin no.kantega.kerber
osauth.kerberosauth-plugin
2024-06-11 13:12:25,954 WARN [Catalina-utility-1] [osgi.hook.dmz.DmzResolverHook] filterMatches Package org.codehaus.jackson.annotate is internal and is not available for export to plugin no.kantega.kerbe
rosauth.kerberosauth-plugin

Edit #2: I was stuck between two choices. Either like above, be stuck without ability to use modules Atlassian chooses not to osgi export, or bundle jackson like we used to but suffer a conflict with Atlassian’s own jackson databind.

If bundle the jackson artifacts into the plugin, the ChainingClassLoader in the rest-v2 code grabs our plugin’s classloader and invokes a ClassNotFoundException looking for jackson databind modules for the rest-v2 ObjectMapper.

Solution
Maven-shade-plugin. The above bundling of jackon was only solved by using maven-shade-plugin to relocate our plugin’s com.fasterxml.jackson classes.

1 Like

I have a somewhat related problem. Some of our apps need Jackson 2 also for non-REST stuff and therefore we have been bundling it.
We still need to do this for Confluence 8 support where Jackson 2 is not OSGi-exported by Confluence.

Now the mess starts with the POJOs used in REST APIs:

  • I can’t use jackson 1 annotations because they’re no longer exposed in Confluence 9
  • I can’t use jackson 2 annotations because they’re not exposed in Confluence 8.0
  • I could use JAXB annotations but it’s a pain to convert everything
  • A custom javax.ws.rs.ext.MessageBodyReader / Writer backed by my bundled Jackson 2 lib doesn’t seem to be used for whatever reason.
  • Right now I’m using a dumb mix of JAXB annotations and manual JSON conversion so Jersey only sees plain strings. Not nice but gets the job done if there are only few affected endpoints.

What’s the best approach here? Shouldn’t it be possible to get the custom MessageBodyReader running on both Confluence 8 and 9?

@jens I have not tried this, but can you OSGi optionally-import both the Jackson 1 and Jackson 2 bindings, then apply both to your classes? Annotations are not required to be present at runtime and their absence should not generate ClassNotFoundExceptions. Additionally, an OSGi import of a package that is provided locally should also cause that import to override the local package.

@scott.dudley Yeah that should also work, but then I still need to touch all the POJOs. And If I have to do this I’d rather use the JAXB annotations and avoid the hack with the optional imports.

Even the manual JSON conversion would be less work as long as there are fewer endpoints than POJOs (and fields).

@remie sha files have only been recently introduced, so older milestone releases don’t have them. What you can do is:

  • remove share download and validation from Dockerfile
  • or user either m108 or m116 release

And some good news, arm64 tags are coming soon.

Update on arm64 tags. They are available now https://hub.docker.com/r/atlassian/confluence/tags
We don’t update existing eap tags though, so the next published eap will have arm64 manifest.

2 Likes

Sharing some pitfalls that we ran into while going through this prep exercise. Hope this is helpful.

  • Atlassian-Plugin-Key missing from the plugins manifest. It is a requirement when using Spring Java Config

    • Marks your plugin as providing its own Spring configuration, rather than one being created during the transformation process. This is so-called ‘transformerless’, and results in significantly faster deployment and startup time of your plugin.Source and related comment.
    • Example:
      <instructions>
          <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>
          <Spring-Context>*</Spring-Context>
      </instructions>
      
  • Missing PermittedMethods from the action definition in the atlassian-plugin.xml Docs and related comment.

    • Example:
      <action name="getAttachments" class="com.example.actions.GetAttachmentsAction">
          <param name="permittedMethods">GET</param>
          <result name="success" type="json"/>
      </action>
      
  • Missing or misconfigured allowlist method invocations for Velocity templates in atlassian-plugin.xml file Docs

    • Example:
      <velocity-allowlist key="velocity-allowlist-example">
          <method>com.example.confluence.macros.views.FileView#getName()</method>
      </velocity-allowlist>
      
    • Watch out because if you have one error it will invalidate the whole allowlist module is ignored. See related comment
  • Finally, I found this example of a POM file helpful

4 Likes

I tried bundling jackson again, which only lead to classloader issues loading the Jackson modules since the ChainingClassloader user our plugin’s classloader to grab some jackson stuff.

Now to just get the endpoints working, nevermind getting jackson to work, I commented out all the jackson modules I need for my ObjectMapper and set jackson back to provided scope. Now I get another error instantiating my REST resources. Could someone shed some light on why this is happening?

java.lang.NoSuchMethodException: Could not find a suitable constructor in org.kantega.atlaskerb.rest.resource.api.info.PingResource class.\n\nat org.jvnet.hk2.internal.Collector.throw
IfErrors(Collector.java:65)\ncaused by: java.lang.NoSuchMethodException: Could not find a suitable constructor in org.kantega.atlaskerb.rest.resource.api.info.PingResource class.\nat org.glassfish.jersey.inject.
hk2.JerseyClassAnalyzer.getConstructor(JerseyClassAnalyzer.java:168)\n
2024-06-13 15:16:34,904 ERROR [https-jsse-nio-8015-exec-8 url: /500page.jsp; user: admin] [atlassian.confluence.status.SystemErrorInformationLogger] logException Unhandled exception dad065f1-a63f-44da-9b4a-acf20
26aedeb: A MultiException has 1 exceptions.  They are:\n1. java.lang.NoSuchMethodException: Could not find a suitable constructor in org.kantega.atlaskerb.rest.resource.api.info.PingResource class.\n
 -- traceId: bcfdb034e3087f13
javax.servlet.ServletException: A MultiException has 1 exceptions.  They are:
1. java.lang.NoSuchMethodException: Could not find a suitable constructor in org.kantega.atlaskerb.rest.resource.api.info.PingResource class.

        at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:410)
        at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)
        at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:359)
        at com.atlassian.plugins.rest.v2.jersey.JerseyOsgiServletContainer.doFilter(JerseyOsgiServletContainer.java:74)
        at org.glassfish.jersey.servlet.ServletContainer.doFilter(ServletContainer.java:432)
        at com.atlassian.plugins.rest.v2.servlet.RestDelegatingServletFilter.doFilter(RestDelegatingServletFilter.java:80)

Looks like it can’t inject dependencies to your class PingResource. Did you try to use Inject annotation?

Some examples:

public class PingResource

   @javax.inject.Inject
    public PingResource(SomeDependency someDependency) {
    ........ // initialization in constructor

   }
}

Kind regards

I tried to show confluence editor in my app - Confluence m116.

I use atlassian-editor.vm .

In my vm-file

#set ($helper = $action.helper)
#set ($contentId = $parameters.contentId)
#set ($versionMismatch = false)
#set ($webInterfaceContext = $action.webInterfaceContext)
#set ($locale = $action.locale)
#set ($textareaName = "wysiwygContent")
#set ($req = $request)
#set ($id = "rte")
#set ($webResourceManager = $action.webResourceManager)
#set ($webInterfaceManager = $action.webInterfaceManager)
#set ($i18n = $action.i18n)

#*
//Required Context Parameters from atlassian-editor.vm
//* id - unique id of the editor +
//* webResourceManager - instance of a WebResourceManager +
//* webInterfaceManager - instance of a WebInterfaceManager +
//* webInterfaceContext - a map/context to be used when calling the WebInterfaceManager
//* i18n - a bean that has a getText method for i18n
//* locale - the current user's locale as a string
//* req - the request object
//* textareaName - name of the texarea*#

......

#requireResource("confluence.web.resources:editor-templates")

........

#parse("/templates/atlassian-editor.vm")

I added dependency in atlassian-plugin.xml:
<dependency>confluence.web.resources:editor-templates</dependency>

But I have error in my logs and this error happens “inside” the Confluence:

com.google.template.soy.base.SoySyntaxException: In file /includes/soy/content-editor.soy, template 
Confluence.Templates.ContentEditor.contentEditor: Failed to find SoyJsSrcFunction with name 
'putMetadata' (function call "putMetadata('page-id', $pageId)").

I didn’t find this function putMetadata for soy-templates in Confluence m116 .

Does anybody tried to show Confluence editor myself? Did I forget include some additional resources? Maybe any advices?

Kind regards

I did use Inject. I was wondering whether the jakarta / javax change might be the reason for this… So I was under the impression platform 7 would have a jakarta migration, however it was just a package switch, but not import switch in namespace. Here’s the mistake I made. Adding jakarta, I accidentally settled for version 2.x which has migrated the namespace from javax to jakarta:

<dependency>
      <groupId>jakarta.inject</groupId>
      <artifactId>jakarta.inject-api</artifactId>
      <version>2.0.1</version>
      <scope>provided</scope>
    </dependency>

Then all imports were changed from javax.inject → jakarta.inject. This worked perfectly initializing the plugin with the Spring system, but broke down only when the rest-v2 stuff tried to initialize my REST resources.
What I did to fix it was revert to jakarta 1.x, which I figured out Atlassian is using. I wish this was clearer in the documentation:

<dependency>
      <groupId>jakarta.inject</groupId>
      <artifactId>jakarta.inject-api</artifactId>
      <version>1.0.5</version>
      <scope>provided</scope>
    </dependency>
1 Like

@AndreasEbert Did you find a solution to this? We’re also extending AbstractUserProfileAction and we’re running into the same issue.