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?
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!
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi!
Absolutely, please see my accepted answer.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.