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

Can't create a new confluence page through scheduled job programmatically Edited

Hi!

I have now faced this issue for a couple of days now, and I can't seem to be able to fix it. I have tried to do the following code:

public boolean convertMailToPageProgrammatically(Message m, String parentPageTitle, String title, String spaceKey) {
try {
log.warn("convertMailToPage for message " + m.getSubject());
SpaceManager spaceManager = ComponentLocator.getComponent(SpaceManager.class);
PageManager pageManager = ComponentLocator.getComponent(PageManager.class);
ConfluenceIndexer confluenceIndexer = ComponentLocator.getComponent(ConfluenceIndexer.class);
Space spaceToUse = spaceManager.getSpace(spaceKey);

log.warn("Page: " + pageManager.getPage("SpaceKey", "New Page 1337").getTitle());

Page page = new Page();
Page parentPage = pageManager.getPage(spaceKey, parentPageTitle);
page.setSpace(spaceToUse);
page.setTitle(title);
page.setBodyAsString(getTextFromMessage(m));
page.setVersion(1);
page.setParentPage(parentPage);
log.warn("now we are here");
pageManager.saveContentEntity(page, null);
parentPage.addChild(page);
confluenceIndexer.reIndex(page);
return true;
} catch (MessagingException | IOException e) {
log.error("ERROR! " + e.getMessage());
return false;
}

}

This code has worked in a lot of my other plugins. However, the difference now is that I'm trying to run this through a Confluence Scheduled Job, which itself seems to run just fine. You can see the code below:

package net.teliacompany.diva.confluence.purgingbackend.job;

import javax.mail.MessagingException;

import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.scheduler.JobRunner;
import com.atlassian.scheduler.JobRunnerRequest;
import com.atlassian.scheduler.JobRunnerResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import net.teliacompany.diva.confluence.purgingbackend.mail.MailUtilities;
import net.teliacompany.diva.confluence.purgingbackend.utilities.PARUtilities;

@Component
@Scanned
public class PurgingAndRetentionJobv2 implements JobRunner {

private static final Logger log = LoggerFactory.getLogger(PurgingAndRetentionJobv2.class);
private final PARUtilities parUti = new PARUtilities();
private final MailUtilities mailUti = new MailUtilities();

@Override
public JobRunnerResponse runJob(JobRunnerRequest request) {
try {
int noOfMails = mailUti.checkIfThereAnyNewAvailableMails();
if (noOfMails > 0) {
log.warn("There was " + noOfMails + " mails to process");
return JobRunnerResponse.success("There was " + parUti.processMails() + " new mail, job complete!");
} else {
return JobRunnerResponse.success("There was no new mail, job complete!");
}
} catch (MessagingException ex) {
return JobRunnerResponse.failed("Job Failed!");
}
}
}

However, as soon as I try to execute, it runs fine (it can get pages just fine when using the PageManager). But when it it arrives at saving the actual page, I get the following error:

2019-10-29 09:52:30,696 WARN [Caesium-1-4] [confluence.impl.hibernate.ConfluenceHibernateTransactionManager] doRollback Performing rollback. Transactions:
->[com.atlassian.confluence.pages.DefaultPageManager.saveContentEntity]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT (Session #247582940)
2019-10-29 09:52:30,697 ERROR [Caesium-1-4] [confluence.purgingbackend.utilities.PARUtilities] processMails ERROR!
2019-10-29 09:52:30,698 ERROR [Caesium-1-4] [confluence.purgingbackend.utilities.PARUtilities] processMails A different object with the same identifier value was already associated with the session : [com.atlassian.confluence.spaces.Space#21594119]; nested exception is org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session : [com.atlassian.confluence.spaces.Space#21594119]
2019-10-29 09:52:30,698 ERROR [Caesium-1-4] [confluence.purgingbackend.utilities.PARUtilities] processMails ERROR:___ org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:265)

 I have tried using both Atlassian Spring Scanner version 1 and 2 to no avail, I always end up with the same error no matter what I do. What am I doing wrong?

2 answers

1 accepted

Here's the final solution of the code, if anyone has a need for it. What you have to do is that you need to create a transaction template for the page creation when doing it during a scheduled job. Also, it's important to note that the code has somewhat changed from the answer above, but this should help anyone that wants to create a page (or perhaps other things) during a schedule job.

Component imports and constructor:

@ComponentImport
private final SpaceManager spaceManager;
@ComponentImport
private final PageManager pageManager;
@ComponentImport
private final ConfluenceIndexer confluenceIndexer;
@ComponentImport
private final TransactionTemplate transactionTemplate;
@ComponentImport
private final XhtmlContent xhtmlContent;
@ComponentImport
private final UserAccessor userAccessor;

public Utilities(SpaceManager spaceManager, PageManager pageManager, ConfluenceIndexer confluenceIndexer, TransactionTemplate transactionTemplate, XhtmlContent xhtmlContent, UserAccessor userAccessor) {
this.spaceManager = spaceManager;
this.pageManager = pageManager;
this.confluenceIndexer = confluenceIndexer;
this.transactionTemplate = transactionTemplate;
this.xhtmlContent = xhtmlContent;
this.userAccessor = userAccessor;
}

createPageProgrammatically method:

public boolean createPageProgrammatically(String spaceKey, String title, String parentPageTitle) {
transactionFlag = false;
transactionTemplate.execute(() -> {
try {
Date dateToUse = new Date();
Page page = new Page();
Page parentPage = pageManager.getPage(spaceKey, parentPageTitle);
Space space = spaceManager.getSpace(spaceKey);

ConfluenceUser user = null;

if (userAccessor.exists("Support")) user = userAccessor.getUserByName("Support");

page.setCreator(user);
page.setLastModifier(user);
page.setSpace(space);
page.setTitle(title);
page.setCreationDate(dateToUse);
page.setLastModificationDate(dateToUse);
page.setBodyAsString("");
page.setVersion(1);
page.setParentPage(parentPage);
pageManager.updatePageInAncestorCollections(page, parentPage);
pageManager.saveContentEntity(page, new DefaultSaveContext.Builder().build());
parentPage.addChild(page);
confluenceIndexer.reIndex(page);
transactionFlag = true;
} catch (Exception e) {
log.error("ERROR! Page couldn't be created! Message: " + e.getMessage());
}
return null;
});
return transactionFlag;
}

Also, please note that I also received the same issue as above when passing a space object to this code. Turns out that you need to get all objects, such as pages or spaces, in the actual transaction, or else it will fail with a similar error to my opening post. If you follow the way that I did in this answer, you should be fine.

Also, I encountered an issue with an ancestors table error when running the code like this. Adding the "pageManager.updatePageInAncestorCollections(page, parentPage);" line at that exact spot fixed it for me, i.e. adding it after "page.setParentPage(parentPage);" and before "pageManager.saveContentEntity(page, new DefaultSaveContext.Builder().build());"

Hope it helps!

0 votes

Hi again @Johan Jonsson Nilsson ,

Confluence uses the "Session in view" pattern for managing Hibernate sessions. The SessionInViewFilter opens a Hibernate session which is then available for the entire web request.

As you mentioned before the difference now is that I'm trying to run this through a Confluence Scheduled Job which means that there is no a Hibernate session to work with. I suppose you need to create and manage a session manually:

HibernateTemplate template = new HibernateTemplate(sessionFactory, true);
template.execute(new HibernateCallback()
{
    @Override
    public Object doInHibernate(Session session) throws HibernateException, SQLException
    {
        // ... execute database-related code ...
        
        return null;
    }
});

The type of the sessionFactory field is net.sf.hibernate.SessionFactory. You can get this injected by Spring into your component.

This code will create a new session if one is not already bound to the thread, execute the callback code, then close the session and release the database connection back to the pool.

https://developer.atlassian.com/server/confluence/hibernate-sessions-and-transaction-management-guidelines/

Hi @Johan Jonsson Nilsson ,

Please let us know if the above answer helps you and if it's not too much trouble could you share your final solution for anybody future needs?

Hi!

Absolutely, please see my accepted answer.

Suggest an answer

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

What do you think is the most *delightful* Confluence feature? Comment for a prize!

- Create your own custom emoji 🔥 - "Shake for Feedback" on mobile 📱 - An endless supply of GIFs via GIPHY 🤩 Is there anything quite as nice as a pleasant surprise? Comment below with what...

402 views 23 8
Join discussion

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