cancel
Showing results for 
Search instead for 
Did you mean: 

Create/Reuse a node

swithun
Champ in-the-making
Champ in-the-making
I'm having difficulty writing to the content of a node, if the node already exists. I have an action which tries create a node and write some content to it based on the node on which the action is run. This is fine if a node with the given name doesn't already exist. But if such a node already exists, because the same action has already been run on the original node, then I run into problems.

The code is a bit like this:

try
  {
    fileRef = fileFolderService.create(dirRef, fileName, PROP_QNAME_CONTENT).getNodeRef();
  }
catch (FileExistsException ffe)
  {
    // what should go here?
  }
ContentWriter writer = contentService.getWriter(fileRef, ContentModel.PROP_CONTENT, true);
writer.putContent(output);

I've tried to get a nodeRef for the node in various ways, and tried deleting it and then creating it again. Nothing gives me a usable nodeRef that I can use with getWriter. I always get a very long and unhelpful stack trace:

09:41:43,619 User:admin ERROR [org.hibernate.AssertionFailure] an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)
org.hibernate.AssertionFailure: null id in org.alfresco.repo.domain.hibernate.ChildAssocImpl entry (don't flush the Session after an exception occurs)
        at org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:55)
        at org.hibernate.event.def.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:164)
        at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:120)
        at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196)
        at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
        at org.alfresco.repo.domain.hibernate.DirtySessionMethodInterceptor.flushSession(DirtySessionMethodInterceptor.java:336)
        at org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl$SessionFlusher.doInHibernate(HibernateNodeDaoServiceImpl.java:5431)
        at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406)
        at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:339)
        at org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl.addNodePropertyImpl(HibernateNodeDaoServiceImpl.java:1463)
        at org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl.addNodeProperty(HibernateNodeDaoServiceImpl.java:1510)

Has anyone faced and solved a similar problem?
4 REPLIES 4

gyro_gearless
Champ in-the-making
Champ in-the-making
I suppose you should check the existence of that particular file with say FileFolderService#searchSimple() BEFORE you attempt to create a new node!

The approach of catching the FileExistsException will not work for the following reason: Usually all actions in Alfresco are executed within some Transaction; if some exception occurs in any of the service invocations, the underlying transaction is marked for rollback. Thus, even if you catch that exception and create some node afterwards, it will be all eventually undone by the Tranaction-Manager…  :mrgreen:

Which is usually a good thing, but in cases like yours i sometimes wish the programmer had more control on transaction handling….

HTH
Gyro

swithun
Champ in-the-making
Champ in-the-making
Good advice. Thanks.

This works:

fileRef = fileFolderService.searchSimple(dirRef, fileName);
if (null == fileRef)
  { 
    fileRef = fileFolderService.create(dirRef, fileName, PROP_QNAME_CONTENT).getNodeRef();
  }
ContentWriter writer = contentService.getWriter(fileRef, ContentModel.PROP_CONTENT, true);
writer.putContent(output);

neilm
Champ in-the-making
Champ in-the-making
Hi,

The previous poster is right: even if you catch exceptions, your transaction may have been marked for rollback, so that's not the way to go.

It sounds like you may have made progress on your problem, Swithun. But the description of your action makes me think: Are you using a custom content model? If you have an action that is creating new content - or updating existing content - based on some source node, then a common approach in such a scenario would be to use a custom content model to model that relationship.
For example, the RenditionService defines an aspect rn:renditioned that defines a child-association to an rn:rendition type. That way, the rendition node being generated/updated is still connected to its source node.

Cheers,
Neil.

swithun
Champ in-the-making
Champ in-the-making
I did think of using a child association to connect the generated node to the original node. But the relationship between these two nodes is complicated. Once the second node has been generated based on the first, it (the second node) becomes more important than the first. I might even want to keep it even if the first node is deleted for some reason. And the second node is only going to be generated once all edits have been made to the first node.

I do like the idea of using declarative code to model the relationship between the nodes, and getting cool stuff for free like cascading updates and deletions. But my particular use doesn't quite fit in. Maybe I should rethink my requirements, so that it does.