Active objects issues

Hi;

I am trying to create a Confluence plugin that will store additional space attributes (such as space owner, backup space owners…). Initially I was storing the data in page labels and page content, but I would like to move it ideally to database.

I started playing with ActiveObject, but for some reason I am unable to store there object relationships.

Below is my code and console output with errors.

Any idea what could be wrong?


Space Attributes

package com.atlassian.confluence.msd.objects;

import com.atlassian.confluence.spaces.Space;
import com.atlassian.confluence.user.ConfluenceUser;
import net.java.ao.Entity;
import net.java.ao.OneToMany;
import net.java.ao.OneToOne;

public interface SpaceAttributes extends Entity {

    @OneToOne
    ConfluenceUser getSpaceOwner();
    void setSpaceOwner(ConfluenceUser spaceOwner);

    @OneToMany
    ConfluenceUser[] getSpaceBackupOwners();
    void setSpaceBackupOwners(ConfluenceUser[] spaceBackupOwners);

    String getDivision();
    void setDivision(String division);

    String getDataClassification();
    void setDataClassification(String dataClassification);

    String getBusinessClassification();
    void setBusinessClassification(String businessClassification);
    @OneToOne
    Space getSpace();
    void setSpace(Space space);

}

Space Attributes Service

package com.atlassian.confluence.msd.services;

import com.atlassian.activeobjects.tx.Transactional;
import com.atlassian.confluence.msd.objects.SpaceAttributes;
import com.atlassian.confluence.spaces.Space;
import com.atlassian.confluence.user.ConfluenceUser;

import java.util.List;

@Transactional
public interface SpaceAttributesService {

    SpaceAttributes add(Space space, ConfluenceUser spaceOwner, ConfluenceUser[] spaceBackupOwner, String division, String businessClassification, String dataClassification);

    List<SpaceAttributes> all();
}

Space Attributes Service Impl

package com.atlassian.confluence.msd.services;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.confluence.msd.objects.SpaceAttributes;
import com.atlassian.confluence.spaces.Space;
import com.atlassian.confluence.user.ConfluenceUser;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;

import javax.inject.Inject;
import javax.inject.Named;
import java.util.List;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.newArrayList;

@Scanned
@Named
public class SpaceAttributesServiceImpl implements SpaceAttributesService {
    @ComponentImport
    private final ActiveObjects ao;

    @Inject
    public SpaceAttributesServiceImpl(ActiveObjects ao)
    {
        this.ao = checkNotNull(ao);
    }

    @Override
    public SpaceAttributes add(Space space, ConfluenceUser spaceOwner, ConfluenceUser[] spaceBackupOwner, String division, String businessClassification, String dataClassification)
    {
        final SpaceAttributes spaceAttributes = ao.create(SpaceAttributes.class);
        spaceAttributes.setSpace(space);
        spaceAttributes.setSpaceOwner(spaceOwner);
        spaceAttributes.setSpaceBackupOwners(spaceBackupOwner);
        spaceAttributes.setDivision(division);
        spaceAttributes.setBusinessClassification(businessClassification);
        spaceAttributes.setDataClassification(dataClassification);
        spaceAttributes.save();
        return spaceAttributes;
    }

    @Override
    public List<SpaceAttributes> all()
    {
        return newArrayList(ao.find(SpaceAttributes.class));
    }
}

Listener

@EventListener
public void onSpaceBlueprintCreateEvent(SpaceBlueprintCreateEvent event) {
    // Get space owners
    String spaceOwner = event.getContext().get(GlobalConstants.SPACE_OWNER).toString();
    ConfluenceUser spaceOwnerObject = userAccessor.getUserByName(spaceOwner.split(",")[0]);

    // Get space backup owners
    String spaceBackupOwners = event.getContext().get(GlobalConstants.SPACE_BACKUP_OWNER).toString();
    ConfluenceUser[] spaceBackupOwnersObject = new ConfluenceUser[spaceBackupOwners.split(",").length];

    for(int i = 0; i < spaceBackupOwners.split(",").length; i++) {
        spaceBackupOwnersObject[i] = userAccessor.getUserByName(spaceBackupOwners.split(",")[i]);
    }

    String division = event.getContext().get(GlobalConstants.DIVISION).toString();
    String businessClassification = event.getContext().get(GlobalConstants.BUSINESS_CLASSIFICATION).toString();
    String dataClassification = event.getContext().get(GlobalConstants.DATA_CLASSIFICATION).toString();

    spaceAttributesService = new SpaceAttributesServiceImpl(ao);
    spaceAttributesService.add(event.getSpace(),spaceOwnerObject,spaceBackupOwnersObject,division,businessClassification,dataClassification);
}

Console Output

[INFO] [talledLocalContainer] Caused by: com.atlassian.activeobjects.internal.ActiveObjectsInitException: bundle [com.atlassian.confluence.msd.SpaceTemplates]
[INFO] [talledLocalContainer] at com.atlassian.activeobjects.osgi.TenantAwareActiveObjects$1$1$1.call(TenantAwareActiveObjects.java:95)
[INFO] [talledLocalContainer] at com.atlassian.activeobjects.osgi.TenantAwareActiveObjects$1$1$1.call(TenantAwareActiveObjects.java:86)
[INFO] [talledLocalContainer] at com.atlassian.sal.core.executor.ThreadLocalDelegateCallable.call(ThreadLocalDelegateCallable.java:38)
[INFO] [talledLocalContainer] at com.atlassian.confluence.impl.vcache.VCacheRequestContextManager.doInRequestContextInternal(VCacheRequestContextManager.java:87)
[INFO] [talledLocalContainer] at com.atlassian.confluence.impl.vcache.VCacheRequestContextManager.doInRequestContext(VCacheRequestContextManager.java:71)
[INFO] [talledLocalContainer] at com.atlassian.confluence.vcache.VCacheRequestContextOperations.lambda$withRequestContext$1(VCacheRequestContextOperations.java:59)
[INFO] [talledLocalContainer] at java.util.concurrent.FutureTask.run(FutureTask.java:266)
[INFO] [talledLocalContainer] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
[INFO] [talledLocalContainer] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
[INFO] [talledLocalContainer] ... 1 more
[INFO] [talledLocalContainer] Caused by: java.lang.RuntimeException: Unrecognized type: [Lcom.atlassian.confluence.user.ConfluenceUser;
[INFO] [talledLocalContainer] at net.java.ao.types.TypeManager.getType(TypeManager.java:73)
[INFO] [talledLocalContainer] at net.java.ao.schema.SchemaGenerator.getSQLTypeFromMethod(SchemaGenerator.java:406)
[INFO] [talledLocalContainer] at net.java.ao.schema.SchemaGenerator.parseFields(SchemaGenerator.java:311)
[INFO] [talledLocalContainer] at net.java.ao.schema.SchemaGenerator.parseInterface(SchemaGenerator.java:277)
[INFO] [talledLocalContainer] at net.java.ao.schema.SchemaGenerator.parseDDLRoots(SchemaGenerator.java:159)
[INFO] [talledLocalContainer] at net.java.ao.schema.SchemaGenerator.parseDDL(SchemaGenerator.java:141)
[INFO] [talledLocalContainer] at net.java.ao.schema.SchemaGenerator.generateImpl(SchemaGenerator.java:106)
[INFO] [talledLocalContainer] at net.java.ao.schema.SchemaGenerator.migrate(SchemaGenerator.java:84)
[INFO] [talledLocalContainer] at net.java.ao.EntityManager.migrate(EntityManager.java:128)
[INFO] [talledLocalContainer] at com.atlassian.activeobjects.internal.EntityManagedActiveObjects.migrate(EntityManagedActiveObjects.java:51)
[INFO] [talledLocalContainer] at com.atlassian.activeobjects.internal.AbstractActiveObjectsFactory$1.doInTransaction(AbstractActiveObjectsFactory.java:77)
[INFO] [talledLocalContainer] at com.atlassian.activeobjects.internal.AbstractActiveObjectsFactory$1.doInTransaction(AbstractActiveObjectsFactory.java:72)
[INFO] [talledLocalContainer] at com.atlassian.sal.core.transaction.HostContextTransactionTemplate$1.doInTransaction(HostContextTransactionTemplate.java:21)
[INFO] [talledLocalContainer] at com.atlassian.sal.spring.component.SpringHostContextAccessor$1.doInTransaction(SpringHostContextAccessor.java:71)
[INFO] [talledLocalContainer] at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
[INFO] [talledLocalContainer] at com.atlassian.sal.spring.component.SpringHostContextAccessor.doInTransaction(SpringHostContextAccessor.java:68)
[INFO] [talledLocalContainer] at com.atlassian.confluence.spring.transaction.interceptor.ConfluenceSpringHostContextAccessor.access$001(ConfluenceSpringHostContextAccessor.java:21)
[INFO] [talledLocalContainer] at com.atlassian.confluence.spring.transaction.interceptor.ConfluenceSpringHostContextAccessor.lambda$doInTransaction$3(ConfluenceSpringHostContextAccessor.java:72)
[INFO] [talledLocalContainer] at com.atlassian.confluence.impl.vcache.VCacheRequestContextManager.doInRequestContextInternal(VCacheRequestContextManager.java:87)
[INFO] [talledLocalContainer] at com.atlassian.confluence.impl.vcache.VCacheRequestContextManager.doInRequestContext(VCacheRequestContextManager.java:71)
[INFO] [talledLocalContainer] at com.atlassian.confluence.spring.transaction.interceptor.ConfluenceSpringHostContextAccessor.doInTransaction(ConfluenceSpringHostContextAccessor.java:72)
[INFO] [talledLocalContainer] at sun.reflect.GeneratedMethodAccessor242.invoke(Unknown Source)

Hi,

Unfortunately, ActiveObjects does not support using Hibernate entities from Confluence core in your entity declaration. Instead, I’d recommend that you work with either usernames or userkeys as strings directly.

Because of GDPR and data privacy I strongly recommend using technical identifiers only (i.e. currently in Confluence server it is user key).

Although there currently there is no marketplace procedures in place for this, sooner or later there will be …