Creating JsonContentProperties through Java API

Hi,

I need to store ContentProperties on pages, which do not limit to being a single Long or String. Additionally, I’ll need to be able to search for these properties using CQL. I am aware of how to create this kind of ContentProperty through the REST API based on this source: https://developer.atlassian.com/confdev/confluence-server-rest-api/content-properties-in-the-rest-api

This works just fine, but I hate the though of having to go through the REST API in a server plugin. Is there some way of achieving the same thing through direct use of Java? Something akin to this How can I read/fetch content properties over java ... comes to mind, but when I try to inject ContentService into my plugin, I get errors indicating that no such beans exist, while String properties containing JSON are not indexed properly.

I’d appreciate any help in this regard!
Best regards,
Christopher

You need to show us your POM and the class you’re trying to inject ContentService into. It’s difficult to point out your issue with spring scanner without knowing what’s going wrong.

Hi,

thanks for your reply. Here you go:

[details=pom.xml]```

<?xml version="1.0" encoding="UTF-8"?>
<modelVersion>4.0.0</modelVersion>
<groupId>tld.example.confluence</groupId>
<artifactId>testplugin</artifactId>
<version>0.1</version>

<organization>
    <name>Example Company</name>
    <url>http://www.example.com/</url>
</organization>

<name>testplugin</name>
<description>This is the tld.example.confluence:testplugin plugin for Atlassian Confluence.</description>
<packaging>atlassian-plugin</packaging>

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.10</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.atlassian.confluence</groupId>
        <artifactId>confluence</artifactId>
        <version>${confluence.version}</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>com.atlassian.plugin</groupId>
        <artifactId>atlassian-spring-scanner-annotation</artifactId>
        <version>${atlassian.spring.scanner.version}</version>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>com.atlassian.plugin</groupId>
        <artifactId>atlassian-spring-scanner-runtime</artifactId>
        <version>${atlassian.spring.scanner.version}</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
        <scope>provided</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>provided</scope>
    </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.2.2-atlassian-1</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>com.atlassian.maven.plugins</groupId>
            <artifactId>maven-confluence-plugin</artifactId>
            <version>${amps.version}</version>
            <extensions>true</extensions>
            <configuration>
                <productVersion>${confluence.version}</productVersion>
                <productDataVersion>${confluence.data.version}</productDataVersion>
                <enableQuickReload>true</enableQuickReload>
                <enableFastdev>false</enableFastdev>
                <skipRestDocGeneration>true</skipRestDocGeneration>
                <allowGoogleTracking>false</allowGoogleTracking>
                <skipManifestValidation>true</skipManifestValidation>
                <extractDependencies>false</extractDependencies>

                <!-- 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 -->
                    <Export-Package>
                        tld.example.confluence.testplugin.*,
                        tld.example.confluence.testplugin.content,
                        tld.example.confluence.testplugin.content.query,
                        tld.example.confluence.testplugin.macro,
                    </Export-Package>

                    <!-- Add package import here -->
                    <Import-Package>
                        org.springframework.osgi.*;resolution:="optional",
                        org.eclipse.gemini.blueprint.*;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-compiler-plugin</artifactId>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugin>
    </plugins>
</build>

<properties>
    <confluence.version>6.3.2</confluence.version>
    <confluence.data.version>6.3.2</confluence.data.version>
    <amps.version>6.2.11</amps.version>
    <plugin.testrunner.version>1.2.3</plugin.testrunner.version>
    <atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
    <!-- This key is used to keep the consistency between the key in atlassian-plugin.xml and the key to generate bundle. -->
    <atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key>
</properties>
```[/details]
class.java
package tld.example.confluence.testplugin.rest;

import com.atlassian.confluence.api.model.content.Content;
import com.atlassian.confluence.api.model.content.JsonContentProperty;
import com.atlassian.confluence.api.model.content.id.ContentId;
import com.atlassian.confluence.api.service.content.ContentPropertyService;
import com.atlassian.confluence.api.service.content.ContentService;
import com.atlassian.confluence.json.json.JsonString;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import org.springframework.beans.factory.annotation.Autowired;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class RestService {

    private final ContentPropertyService contentPropertyService;

    private final ContentService contentService;


    @Autowired
    public RestService(@ComponentImport final ContentPropertyService contentPropertyService,
                       @ComponentImport final ContentService contentService) {
        this.contentPropertyService = contentPropertyService;
        this.contentService = contentService;
    }

    @GET
    @Path("test")
    public Response test(@QueryParam("pageId") String pageId) {

        Content page = contentService.find().withId(ContentId.valueOf(pageId)).fetchOneOrNull();
        JsonString properties = new JsonString("{titles:['test']}");

        JsonContentProperty jsonContentProperty = JsonContentProperty.builder().content(page).key("testkey").value(properties).build();
        contentPropertyService.create(jsonContentProperty);

        return Response.ok().build();
    }
}

Change the name of the ContentService field to ‘apiContentService’ - there is an unfortunate naming mistake which meant that there’s already a bean called ‘contentService’ (but not of the correct type).

1 Like