Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Next challenges

Recent achievements

  • Global
  • Personal

Recognition

  • Give kudos
  • Received
  • Given

Leaderboard

  • Global

Trophy case

Kudos (beta program)

Kudos logo

You've been invited into the Kudos (beta program) private group. Chat with others in the program, or give feedback to Atlassian.

View group

It's not the same without you

Join the community to find out what other Atlassian users are discussing, debating and creating.

Atlassian Community Hero Image Collage

Active Object save() method doesn't persist data under certain conditions Edited

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.

Thanks advance for your help

 

2 answers

1 accepted

2 votes
Answer accepted

After deeper investigation it turned out that all interaction with ActiveObjects executed in Confluence need to be performed in Transaction. So after rewriting my add method looks like this:

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

activeObjects.executeInTransaction((TransactionCallback<Object>) () -> {
List<TeamsAtlasUser> usersByTeamsId = getUserByTeamsId(properties.get(TEAMS_ID));
final TeamsAtlasUser user = (usersByTeamsId.isEmpty()) ? activeObjects.create(TeamsAtlasUser.class)
: retrieveFirstTeamsAtlasUser(usersByTeamsId);

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 (TEAMS_ID.equals(entry.getKey()))
user.setMsTeamsUserId((String) entry.getValue());
else
LOG.debug("Wrong property name for user entity");
}
user.save();
return user;
});
}

And now data is persisted just fine. Also this approach is valid for Jira and Bitbucket.

Thank you for sharing this! I have been struggling with this for 3+ hours... 

Just a crazy stuff! (especially when you see the code is working when it's called from an xwork action but does not work when is executed from a servlet).

The most frustrating part is that you can actually see the update been executed (SQL is printed - https://developer.atlassian.com/server/framework/atlassian-sdk/enabling-sql-logging/)... but no actual update is happening

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.

Suggest an answer

Log in or Sign up to answer
TAGS
Community showcase
Published in Confluence

Announcing Team Calendars in Confluence Data Center

Hi Community! We're thrilled to share that Team Calendars for Confluence is now a built-in feature for Confluence Data Center releases 7.11 and beyond.  A long time favorite,  Team Cale...

156 views 0 5
Read article

Community Events

Connect with like-minded Atlassian users at free events near you!

Find an event

Connect with like-minded Atlassian users at free events near you!

Unfortunately there are no Community Events near you at the moment.

Host an event

You're one step closer to meeting fellow Atlassian users at your local event. Learn more about Community Events

Events near you