Preparing for Confluence 9.0 - EAP out now

Hi @turehoefner_appfire

In short: we try to detect annotation, first on method, then on class and finally on package. Based on detected annotation and current user access is allowed or revoked.

I suspect in your case it all boils down to detection issue. Both REST v1 and v2 define their own set of auth annotations. (com.atlassian.plugins.rest.common.security.* vs com.atlassian.plugins.rest.api.security.annotation.*). REST v2 is respecting only its own annotations.

What is more, the type of annotation has to match exactly - in a multi classloader world it means that your plugin has to use the types provided by REST v2 and not some local copy of them.

In Platform 7, REST v2 was enhanced. We support annotations defined in com.atlassian.plugins.rest.api.security.annotation.* but also com.atlassian.annotations.security.*. The second set is preferred. We will remove the original one at some point, but we don’t want to disrupt your migration any more and we don’t have pressure to remove them quickly. What is more on Platform 7 we are detecting not exact types, but only comparing full names of the annotations. That way a plugin can use a different copy of annotations (e.g. local one) and authentication filter will still detect that.

Lastly, AFAIK annotations are resolved quite differently than rest of the code. If your class was compiled with specific annotation and it is missing at runtime, Java will simply ignore it.

I don’t know details of your code, but some actions that you can consider:

  1. remove rest-migration and rely on REST v1 in Confluence 8 - all plugins are automatically attached to REST v2 in Confluence 9, even if migration tag is not present
  2. if plugin is used on Confluence 8 and you want it to use REST v2, then perhaps you could use 2 sets of annotations (from REST v1 and REST v2); you should ensure that your plugin has access to those annotations exported from Confluence - I generally don’t recommend it, but an explicit optional import should do the trick

Please note I didn’t validate the second option on actual instance.

I hope that explanation will give you enough context to resolve your issues!

Cheers,
Marek

1 Like

Hello,

I’m having these kind of warning messages in Confluence 9:

[confluence.search.v2.FieldMappings] addMapping Mapping for 'content-property-comalaworkflowsglobalmodelversion-version' (DoubleFieldMapping{name='content-property-comalaworkflowsglobalmodelversion-version', stored=false, indexed=true}) conflicts with existing mapping (LongFieldMapping{name='content-property-comalaworkflowsglobalmodelversion-version', stored=false, indexed=true})

This happens with two properties declared in the descriptor within the module content-property-index-schema, one as date and the other one as number.

These properties are not populated in any custom Extractor, just created/updated via JsonContentPropertyService component.

I’m not sure if the ContentPropertyExtractor could be affecting because when this extractor is getting the fields, it is obtaining a StringFieldMapping instead the DateFieldMapping declared in the descriptor. Then, it appears the warn log message, printed by FieldMappings class.

So, am I missing something in the configuration ? Could you make a quick test configuring a content-property-index-schema with a property date type?

Thx!

1 Like

Hiya @GorkaGalloBustinza! Were you ever able to solve this issue?

I was running into a similar problem recently with mywork-api with Jackson.

We wanted to bundle com.fasterxml.jackson to support 7.19 - 9.0, which then conflicts with mywork-api’s Jackson.

Strangely I never saw the OSGi BundleException logged, only Spring’s UnsatisfiedDependencyException (noting the failed injection of LocalNotificationService).

I even tried using maven-shade-plugin when bundling Jackson, with not much success (albeit with not many attempts either).

Luckily we were only using Jackson once for something rather trivial, which I could replace with some more basic parsing. So I don’t desperately need a solution anymore, but I’m curious if you managed to fix the problem. Thank you!

Hi @AndrewMorton
Have you been able to fix the LocalNotificationService dependency and get an instance of it in the code that runs on Confluence 7.19-8.x?
We got hit hard by

Caused by: java.lang.IllegalArgumentException: org.codehaus.jackson.node.ObjectNode referenced from a method is not visible from class loader

And really a blocker for us to get one binary to support Confluence 7.19+ to 9.x :frowning:

Cannot remove the Jackson library as it is used by CCMA related code for cloud migration (ObjectMapper thing)

Hey @AndrewMorton,

Yes, finally I resolved it, loading dynamically , not injecting it via @ComponentImport annotation. It looks that it has a conflict with Jackson dependencies, so this is the only way I got to make it work with Confluence 8.5.x - 9.

Hi @GorkaGalloBustinza

How do you dynamically load it, if I may ask?
Hitting the same wall and it is the final blocker for us to get one binary for 8.x-9.x…

HI @sash011,

I have a factory, NotificationServiceFactory, that gets the Service of NotificationService extending this class:

public abstract class OptionalService<T> implements DisposableBean {
    private final Class<T> type;

    private final ServiceTracker tracker;

    public OptionalService(final Class<T> type) {
        this.type = checkNotNull(type, "type");

        final BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
        tracker = new ServiceTracker(bundleContext, type.getName(), null);
        tracker.open();
    }

    /**
     * Returns the service (of type {@code T}) if it exists, or {@code null} if not
     *
     * @return the service (of type {@code T}) if it exists, or {@code null} if not
     */
    protected final T getService() {
        return type.cast(tracker.getService());
    }

    @Override
    public final void destroy() {
        tracker.close();
    }

}

This factory has the method that returns the NotificationService:

public NotificationServiceHolder get(){
        final NotificationService notificationService = getService();
        return new NotificationServiceHolder(notificationService);
    }

where the holder has an instance of NotificacionService:

 public NotificationServiceHolder(final NotificationService notificationService)
    {
        this.notificationService = checkNotNull(notificationService, "notificationService");
    }

    public Object getNotificationService()
    {
        return notificationService;
    }

Finally, you need to declare a component having the notificationServiceHolder where you initialise the notification factory.
It’s very old code when mywork library probably did not exist, this way we checked if the notification service existed or not. So, as we had problems with Jackson dependencies we decided to maintain it.

2 Likes

That was super helpful
Thanks a lot @GorkaGalloBustinza

Is this a bug of Confluence 9? :face_with_spiral_eyes:

I have set the system property “confluence.pdfexport.allow.local.hosts=true” on Confluence 9.0.1 Data Center, but the pdf export stylesheet still doesn’t work.
(But it works well on Confluence 8.9 )

pdf export stylesheet:

@page{background:url('http://localhost:8090/rest/my-app/1/xxxxx') repeat center;}
@page{background:url('http://127.0.0.1:8090/rest/my-app/1/xxxxx') repeat center;}
@page{background:url('http://my-Confluence9-base-url/rest/my-app/1/xxxxx') repeat center;}

The rest method from my app is shown below, and when I visit ‘http://my-Confluence9-base-url/rest/my-app/1/xxxxx’ in the browser, it returns the image successfully:

    @Path("/xxxxx")
    @GET
    @AnonymousSiteAccess
    @CorsAllowed
    @UnrestrictedAccess
    @XsrfProtectionExcluded 
    public Response xxxxx(@Context HttpServletResponse response, @Context HttpServletRequest request) {
        ......
        response.setHeader("Content-Type", "image/png");
        ImageIO.write(bi, "png", response.getOutputStream());
}

atlassian-plugin.xml:

<rest key="some-rest" path="/my-app" version="1"/>
<rest-migration key="rest-migration-key">
        <rest-v2/>
</rest-migration>

@MarekTokarski Hi, may I ask if this is a bug of Confluecne 9?

Porting from 7.0 to 9.0.3 I have several package that cannot be imported.

When installing my plugin I get these errors:
Package org.apache.commons.lang is internal and is not available for export
Package org.apache.commons.lang.builder is internal and is not available for export
Package org.apache.tools.ant is internal and is not available for export
Package org.apache.tools.ant.types is internal and is not available for export
Package com.google.common.base is internal and is not available for export

Looking at just the first package (org.apache.commons.lang) using dependency:tree its reference by the com.atlassian.confluence:confluence:jar:9.0.3:provided

How do I solve this?

Btw, my pom is:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.go2group</groupId>
  <artifactId>ccac</artifactId>
  <version>3.19.4</version>

  <packaging>atlassian-plugin</packaging>

  <name>Client Certificate Authenticator for Confluence</name>
  <description>CAC/PIV Authentication with Confluence</description>

  <organization>
    <name>Goldfinger Holdings</name>
    <url>http://www.goldfingerholdings.com/</url>
  </organization>

  <issueManagement>
    <system>Goldfinger Holdings JIRA Service Desk</system>
    <url>https://jira.goldfingerholdings.com/plugins/servlet/desk/portal/1</url>
  </issueManagement>

  <developers>
    <developer>
      <name>Timothy Chin</name>
      <email>tchin@goldfingerholdings.com</email>
      <roles></roles>
    </developer>
  </developers>

  <dependencies>
    <!-- Confluence Dependency -->
    <dependency>
      <groupId>com.atlassian.confluence</groupId>
      <artifactId>confluence</artifactId>
      <version>${confluence.version}</version>
      <scope>provided</scope>
    </dependency>
    <!-- Confluence License Dependency -->
    <dependency>
      <groupId>com.atlassian.extras</groupId>
      <artifactId>atlassian-extras-core</artifactId>
      <version>3.3.0</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>com.atlassian.sal</groupId>
      <artifactId>sal-api</artifactId>
      <version>3.0.2</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>com.atlassian.plugin</groupId>
      <artifactId>atlassian-spring-scanner-annotation</artifactId>
      <version>${atlassian.spring.scanner.version}</version>
      <scope>provided</scope>
    </dependency>
    <!--
    <dependency>
      <groupId>com.atlassian.plugin</groupId>
      <artifactId>atlassian-spring-scanner-runtime</artifactId>
      <version>${atlassian.spring.scanner.version}</version>
      <scope>compile</scope>
    </dependency>
    -->
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>2.0.16</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>net.sf.opencsv</groupId>
      <artifactId>opencsv</artifactId>
      <version>2.3</version>
    </dependency>
    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <scope>provided</scope>  <!--CCA-11 - Package in the jar per https://developer.atlassian.com/server/confluence/get-your-apps-ready-for-gray-api-removal/#runtime-changes-in-9-0-->
      <version>33.3.0-jre</version>
    </dependency>
    <!--<dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <scope>compile</scope>  &lt;!&ndash;CCA-11 - Package in the jar per https://developer.atlassian.com/server/confluence/get-your-apps-ready-for-gray-api-removal/#runtime-changes-in-9-0&ndash;&gt;
      <version>3.17.0</version>
    </dependency>-->
    <dependency>
      <groupId>com.atlassian.soy</groupId>
      <artifactId>soy-template-renderer-api</artifactId>
      <version>7.0.2</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.2.1</version>
    <scope>provided</scope>
  </dependency>
    <dependency>
      <groupId>com.atlassian.plugins.rest</groupId>
      <artifactId>atlassian-rest-common</artifactId>
      <version>1.0.2</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>2.1</version>
      <scope>compile</scope>
    </dependency>

    <!-- WIRED TEST RUNNER DEPENDENCIES -->
    <dependency>
      <groupId>com.atlassian.plugins</groupId>
      <artifactId>atlassian-plugins-osgi-testrunner</artifactId>
      <version>${plugin.testrunner.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.ws.rs</groupId>
      <artifactId>jsr311-api</artifactId>
      <version>1.1.1</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.11.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-all</artifactId>
      <version>1.10.19</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.14</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <distributionManagement>
    <repository>
        <id>nexus</id>
        <url>https://repo.connectall.com/repository/maven-releases/</url>
        <layout>default</layout>
    </repository>
    <snapshotRepository>
        <id>nexus</id>
        <url>https://repo.connectall.com/repository/maven-snapshots/</url>
    </snapshotRepository>
  </distributionManagement>

  <build>
    <plugins>
      <plugin>
        <groupId>com.atlassian.maven.plugins</groupId>
        <artifactId>confluence-maven-plugin</artifactId>
        <version>${amps.version}</version>
        <extensions>true</extensions>
        <configuration>
          <productVersion>${confluence.version}</productVersion>
          <productDataVersion>${confluence.data.version}</productDataVersion>
          <enableQuickReload>true</enableQuickReload>
          <allowGoogleTracking>false</allowGoogleTracking>
          <!-- See here for an explanation of default instructions: -->
          <!-- https://developer.atlassian.com/docs/advanced-topics/configuration-of-instructions-in-atlassian-plugins -->
          <instructions>
            <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>
            <!-- Add package to export here -->
            <!-- Add package import here -->
            <Import-Package>
              org.springframework.osgi.*;resolution:="optional",
              org.eclipse.gemini.blueprint.*;resolution:="optional",
              com.atlassian.sal.api.*;resolution:="optional",
              com.atlassian.soy.*;resolution:="optional",
              com.atlassian.inject.*;resolution:="optional",
              sun.misc.*;resolution:="optional",
              au.com.bytecode.opencsv.*;resolution:="optional",
              com.sun.grizzly.*;resolution:="optional",
              com.sun.xml.*;resolution:="optional",
              javax.*;resolution:="optional",
              org.apache.commons.*;resolution:="optional",
              org.apache.tools.*;resolution:="optional",
              org.jvnet.*;resolution:="optional",
              org.slf4j.*;resolution:="optional",
              *
            </Import-Package>
            <!-- Ensure plugin is spring powered -->
            <Spring-Context>*</Spring-Context>
          </instructions>
        </configuration>
      </plugin>
      <plugin>
        <groupId>com.atlassian.plugin</groupId>
        <artifactId>atlassian-spring-scanner-maven-plugin</artifactId>
        <version>${atlassian.spring.scanner.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>atlassian-spring-scanner</goal>
            </goals>
            <phase>process-classes</phase>
          </execution>
        </executions>
        <configuration>
          <scannedDependencies>
            <dependency>
              <groupId>com.atlassian.plugin</groupId>
              <artifactId>atlassian-spring-scanner-external-jar</artifactId>
            </dependency>
          </scannedDependencies>
          <verbose>false</verbose>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.9</version>
        <configuration>
          <argLine>-Xmx256M --add-opens java.base/java.lang=ALL-UNNAMED</argLine>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <properties>
    <!--<atlassian.plugin.key>G2G-CAC-CONF</atlassian.plugin.key>
    <confluence.version>7.5.1</confluence.version>
    <amps.version>8.0.2</amps.version>
    <plugin.testrunner.version>2.0.1</plugin.testrunner.version>
    <atlassian.spring.scanner.version>2.1.7</atlassian.spring.scanner.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>-->
    <confluence.version>9.0.3</confluence.version>
    <confluence.data.version>8.5.4</confluence.data.version>
    <amps.version>9.0.2</amps.version>
    <plugin.testrunner.version>2.0.9</plugin.testrunner.version>
    <atlassian.spring.scanner.version>2.2.4</atlassian.spring.scanner.version>
    <!-- This property ensures consistency between the key in atlassian-plugin.xml and the OSGi bundle's key. -->
    <atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>

    <debug.analytics>false</debug.analytics>
    <product.jvmArgs>-Xmx2048m -Datlassian.darkfeature.atlassian.authentication.include.stacktrace.in.error.messages=true -Datlassian.authentication.debug.analytics=${debug.analytics}</product.jvmArgs>
    <maven.javadoc.skip>true</maven.javadoc.skip>

    <gitlab.project.id>16618205</gitlab.project.id>
  </properties>
</project>

The fix for this issue can be tracked here: CONFSERVER-97878. It will be reinstated in Confluence 9.1.0 with appropriate allowlisting entries.

We’re also blocked with the same issue and. Did you find a solution on how to proceed this issue?

Hi,

Is there any plan to adapt atlassian-pocketknife-querydsl dependency for Confluence 9 ??. We are having problems when using it as it has dependencies as fugue, etc.

Gorka.

Hi @XingGuo

I’m not familiar with PDF Export feature. Based on your description that endpoint returns image successfully I would expect the issue is not related to REST, but rather to the PDF Export.

I will try to redirect your question to Confluence team. cc: @Kusal .

Hi Developers,

I am stuck with backwards compatibility for this class com.atlassian.plugin.web.descriptors, which has been moved to com.atlassian.plugin.web.api.descriptors in version 9.x

I have read only a few posts about this with no concrete answers moving forward other than maintaining 2 versions of code - what a crock.

However, I see that some vendors who use Web Sections seem to have been able to provide backward compatibility back to 7.19.x, for the same jar file???

So my question is - How do I make my plugin backward compatible when importing the class WebSectionModuleDescriptor when its class location changes after 9.x?

Any help will be greatly appreciated.

1 Like

@JnisVanags , can you share us some sample of code how you are differentiating in the code , that would be helpful.

I don’t use <web-sections>, but one general pattern that may be helpful is to ship two versions of the <web-section> with separate <restriction> tags on each that limit it to the specific version of Confluence that it supports, with each <web-section> targeting a class compiled appropriately. You will likely need to use Maven multi-module projects to accomplish this.

1 Like

Hi!

Does anybody know how to open own editor?

I opened my own editor. It’s working, but after 30 seconds I have this error message.

Looks like, it depends on the collaborative editing. If I override method AbstractEditPageAction.isCollaborativeContent then I don’t have this error .

public boolean isCollaborativeContent() {
        return false;
}

But two people can override changes each other and I can’t detect conflict.
Is there any way to solve this error?

Maybe I should provide some params or tokens for synchrony or something like that? (only my guess)

You can see my params, error and how I use editor macro.

I use editor macro

It works without additional params in Confluence 8.

Kind regards

I am getting the following error in Confluence 9.0.3. The app has confluence-create-content-plugin as dependency for the blueprints. Also the app didn’t have any active object entities configured in it.

plugin [{com.my.plugin}] invoking ActiveObjects before configuration module is enabled or plugin is missing an configuration module. Note that scanning of entities from the ao.model package is no longer supported.