ActiveObjects not persisting data in Confluence server

Hi,

I have developed RefApp plugin which will be used in Jira, Bitbucket and Confluence products. One of the functionality of this plugin will be to store auth data after successful automated oAuth dance for each user in the local h2 db. Storing this data is performed by this method:

@Scanned
@Named
@Component
public class ClientAtlasUserServiceImpl implements ClientAtlasUserService {

    private final ActiveObjects activeObjects;

    @Autowired
    public ClientAtlasUserServiceImpl(@ComponentImport ActiveObjects activeObjects) {
        this.activeObjects = activeObjects;
    }

    @Override
    public ClientAtlasUser add(Map<String, String> properties) {

        List<ClientAtlasUser> usersByClientId = getUserByClientId(properties.get(CLIENT_ID));
        final ClientAtlasUser user = (usersByClientId.isEmpty()) 
                         ? activeObjects.create(ClientAtlasUser.class) 
                         : retrieveFirstClientAtlasUser(usersByClientId);

        for (Map.Entry entry : properties.entrySet()) {
            if (ACCESS_TOKEN.equals(entry.getKey()))
                user.setAtlasAccessToken((String) entry.getValue());
            else if (REQUEST_TOKEN.equals(entry.getKey()))
                user.setAtlasRequestToken((String) entry.getValue());
            else if (SECRET.equals(entry.getKey()))
                user.setAtlasSecret((String) entry.getValue());
            else if (CLIENT_ID.equals(entry.getKey()))
                user.setClientUserId((String) entry.getValue());
            else
                LOG.debug("Wrong property name for user entity");
        }
        user.save();
    }

This method is implemented from the interface:

@Transactional
public interface ClientAtlasUserService {
    ClientAtlasUser add(Map<String, String> properties);

    List<ClientAtlasUser> all();

    List<ClientAtlasUser> getUserByClientId(String clientId);

And the Entity interface is:

public interface ClientAtlasUser extends Entity {

    String getClientUserId();

    void setClientUserId(String clientUserId);

    String getAtlasSecret();

    void setAtlasSecret(String atlasSecret);

    String getAtlasRequestToken();

    void setAtlasRequestToken(String atlasRequestToken);

    String getAtlasAccessToken();

    void setAtlasAccessToken(String atlasAccessToken);

Dependency in the POM file:

<dependency>
    <groupId>com.atlassian.activeobjects</groupId>
    <artifactId>activeobjects-plugin</artifactId>
    <version>3.0.0</version>
    <scope>provided</scope>
</dependency>

Module in the atlassian-plugin.xml:

<ao key="ao-module">
    <description>The module configuring the Active Objects service used by this plugin</description>
    <entity>com.microsoft.client.ao.ClientAtlasUser</entity>
</ao>

So the question is, when I’m adding new ClientAtlasUser entity through the basic UI modified from the example:

@Scanned
public class ClientAtlasUserServlet extends HttpServlet {

    private final ClientAtlasUserService userService;

    public ClientAtlasUserServlet(ClientAtlasUserService userService) {
        this.userService = userService;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        try {
            final PrintWriter w = resp.getWriter();
            w.write("<h1>Client-Atlas Mapping</h1>");
            w.write("<form method=\"post\">");
            w.write("ClientUserID:<br>");
            w.write("<input type=\"text\" name=\"ClientUserId\" size=\"25\"/><br>");
            w.write("AtlasSecret:<br>");
            w.write("<input type=\"text\" name=\"AtlasSecret\" size=\"25\"/><br>");
            w.write("AtlasRequestToken:<br>");
            w.write("<input type=\"text\" name=\"AtlasRequestToken\" size=\"25\"/><br>");
            w.write("AtlasAccessToken:<br>");
            w.write("<input type=\"text\" name=\"AtlasAccessToken\" size=\"25\"/><br>");
            w.write("  ");
            w.write("<input type=\"submit\" name=\"submit\" value=\"Add\"/>");
            w.write("</form>");

            w.write("<table>");
            w.write("<tr>");
            w.write("<th>ClientUserId</th>");
            w.write("<th>AtlasSecret</th>");
            w.write("<th>AtlasRequestToken</th>");
            w.write("<th>AtlasAccessToken</th>");
            w.write("</tr>");
            for (ClientAtlasUser user : userService.all()) {
                w.write("<tr>");
                w.printf("<td> %s </td>", user.getClientUserId());
                w.printf("<td> %s </td>", user.getAtlasSecret());
                w.printf("<td> %s </td>", user.getAtlasRequestToken());
                w.printf("<td> %s </td>", user.getAtlasAccessToken());
                w.write("</tr>");
            }

            w.write("</table>");
            w.write("<script language='javascript'>document.forms[0].elements[0].focus();</script>");

            w.close();
        } catch (IOException e) {
            LOG.error(e.getMessage());
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        try {
            Map<String, String> properties = new HashMap<>();
            properties.put(CLIENT_ID, req.getParameter("ClientUserId"));
            properties.put(SECRET, req.getParameter("AtlasSecret"));
            properties.put(REQUEST_TOKEN, req.getParameter("AtlasRequestToken"));
            properties.put(ACCESS_TOKEN, req.getParameter("AtlasAccessToken"));
            userService.add(properties);

            resp.sendRedirect(req.getContextPath() + "/plugins/servlet/user/mapping");
        } catch (IOException e) {
            LOG.error(e.getMessage());
        }
    }
}

It persists to the db and I can verify it exists on this ui. But when I call userService.add() method from my code, after I have successfully obtained access token with the same set of Map<String, String> properties arguments it doesn’t save data to db. Also I don’t have this problem when running this RefApp plugin in Jira version 7.12.0, or Bitbucket version 5.15.0 only with Confluence (version 6.12.0 tested only).
Also when I’m trying to modify user which I have previously created through ui passing parameters with existing CLIENT_ID through the code (not ui), I’m getting correct entity of user from db through:

getUserByClientId(properties.get(CLIENT_ID))
then passing new values to this user entity in ClientAtlasUserServiceImpl.add() method and after save I verify that this user hasn’t changed.

I assume this is something due to base URL which I use for access. When I run this refapp on confluence with: atlas-debug --product confluence --version 6.12.0 it starts as usual on localhost:1990/confluence. Then when I enter its Dashboard I’m getting message " Your URL doesn’t match Confluence’s base url is set to http://macmini8383:1990/confluence but you are accessing Confluence from http://localhost:1990/confluence"
Also I have noticed that if I create new record with active object through UI accessing it with localhost:1990 I’m not able to persist new entity to ao just as I wasn’t able to do this from my code. But if I accessing this UI from macmini8383:1990 persisting works just fine. I also try to change basic URL in settings from macmini8383 to localhost just as it’s proposed, behavior hasn’t changed. After all I still not able to persist record from my plugin code workflow.

Does Confluence still require to work with ao in transaction?

Thanks in advance for your help

Confluence requires the transactions to be able to write to the db. I believe that your problem is that you’re defining the @Transactional annotation on the interface and not on the implementation. I would shift that over there. If that doesn’t work, then inject TransactionTemplate from SAL and wrap the code with that.

Thanks Daniel. You were right and this solved the problem.