Is there simple way how to replace (ordered) existing node in JCR 2.0? - jcr

Is there some simple way how to replace existing node with another node in JCR 2.0?
Due to the ordering of nodes, currently I am doing these steps:
step 1: Find sibling node which is right after existing node i want to replace:
if (preserveOrdering) {
NodeIterator iter = parent.getNodes();
boolean found = false;
while (iter.hasNext()) {
if (tempNode.equals(iter.nextNode())) {
found = true;
if (iter.hasNext()) {
tempNodeSibling = iter.nextNode();
break;
}
}
}
assert found;
}
step 2: delete existing node:
tempNode.remove();
step 3:
Create new node (I am doing clone, but probably node.addNode() method can be used,
new node is appended to the end of the child node list):
workspace.clone(workspace.getName(), existingNodePath, tempNodePath, false);
step 4:
Move new node before his old tempNode sibling (remebered in the first step)
parent.orderBefore(tempNodeName, tempNodeSibling.getName());
All these steps looks to me quite cumbersome. But I cannot find in JCR API better way. Mainly because there is only one method orderBefore() working with ordering.
Do you think there is some different/more simple approach for solving this problem?

Unfortunately, with JCR 2.0 new nodes are always added at the end and using javax.jcr.Node.reorder(...) is the only way to change the position of a child node within the parent's list of children. It is inconvenient to say the least, but I suspect adding such methods would have added too much complexity to an already complicated API.

Related

Cytoscape.js dynamically add nodes without moving the others

I am working on an application that fetches data from a database and I would like to show them as graph.
I managed the "tap" event on a node by showing their neighbors (nodes and connection links).
The problem is that, every time I want to show the neighbors, all the graph is re-rendered and if some nodes were moved before, they lose their previous position.
Is there a way to add only the neighbors without affecting the position of the node already present in the layout?
Important: the constraint is that all the nodes should be "movable": the number of nodes in the graph can, easily, increase and I would like to have the availability to move/organize them without losing the result when I add new ones (by clicking on a node)
I am using cola-layout in my project.
Here the way I managed to add neighbors:
function addNeighbour(node, link) {
cy.startBatch();
addNode(link.otherNode.type, link.otherNode.name, link.otherNode.properties);
cy.add([
{
group: 'edges',
data:
{
id: node + ":" + link.type + ":" + link.otherNode.type + ":" + link.otherNode.name,
source: source,
target: target,
type: link.type,
properties: linkproperties
}
}
]);
refreshLayout()
cy.endBatch();
}
}
var layoutOpts = {
name: 'cola',
refresh: 2,
edgeLength: 200,
fit: false
}
function refreshLayout() {
layout.stop();
layout = cy.elements().makeLayout(layoutOpts);
layout.run();
}
Thanks in advance
(1) You can lock a node to make its position immutable, via nodes.lock().
(2) You can run a layout on a subset of the graph to exclude certain elements, via eles.layout().
Either of these strategies can be used in general, or they can be used in tandem.
For your case, it sounds like you should use (1).
Lock the existing nodes.
Add the new nodes.
Run Cola on the entire graph.
When Cola is done, free the locked nodes.
Note, however, that this won't always give a good result. You could over-constrain the system. If you want a good layout result, it's probably best to just run the layout on everything without locking, as Stephan T. suggested.

How to use result returned from before or background scenarios in cucumber java testing?

I have two scenarios named "X" and "Y" ( many others)
Before testing scenario "Y", I need to run Scenario "X" and return the results from scenario "X" to scenario "Y". How should I do that ?
I have tried 2 options
1: I have created background scenario for X but I am not able to return result from that background scenario X to scenario Y.
2: I have also tried using before hook up annotation.
(PS: I know while testing all test-cases should be independent of each other)
Perhaps the simplest way is to place both steps in the same class then use class instance variables.
public class SomeSteps {
private List<String> betweenStepsList;
#When("^I initialize a list in one step$")
public void iInitializeAListInOneStep() {
betweenStepsList = new ArrayList<String>(Arrays.asList("Peter","Piper","picked","a","peck","of","pickled","peppers"));
}
#Then("^I can access that list in a following step$")
public void iCanAccessThatListInAFollowingStep() {
System.out.println("Elements are:" + betweenStepsList);
}
}
EDIT
You did say two scenarios. When I face this issue I always find a way to combine the scenarios into one.
You could write the intermediate results to a file in the first scenario and then read them in the second. I never bother to do this myself but there are lots of tutorials on how to read and write files. Here is one for Java. Ruby or Python (you didn't specify) examples for writing then reading a file are also easily available.
Parallel execution is fine as long as both scenarios are in the same feature file.

Existing saga instances after applying the [Unique] attribute to IContainSagaData property

I have a bunch of existing sagas in various states of a long running process.
Recently we decided to make one of the properties on our IContainSagaData implementation unique by using the Saga.UniqueAttribute (about which more here http://docs.particular.net/nservicebus/nservicebus-sagas-and-concurrency).
After deploying the change, we realized that all our old saga instances were not being found, and after further digging (thanks Charlie!) discovered that by adding the unique attribute, we were required to data fix all our existing sagas in Raven.
Now, this is pretty poor, kind of like adding a index to a database column and then finding that all the table data no longer select-able, but being what it is, we decided to create a tool for doing this.
So after creating and running this tool we've now patched up the old sagas so that they now resemble the new sagas (sagas created since we went live with the change).
However, despite all the data now looking right we're still not able to find old instances of the saga!
The tool we wrote does two things. For each existing saga, the tool:
Adds a new RavenJToken called "NServiceBus-UniqueValue" to the saga metadata, setting the value to the same value as our unique property for that saga, and
Creates a new document of type NServiceBus.Persistence.Raven.SagaPersister.SagaUniqueIdentity, setting the SagaId, SagaDocId, and UniqueValue fields accordingly.
My questions are:
Is it sufficient to simply make the data look correct or is there something else we need to do?
Another option we have is to revert the change which added the unique attribute. However in this scenario, would those new sagas which have been created since the change went in be OK with this?
Code for adding metadata token:
var policyKey = RavenJToken.FromObject(saga.PolicyKey); // This is the unique field
sagaDataMetadata.Add("NServiceBus-UniqueValue", policyKey);
Code for adding new doc:
var policyKeySagaUniqueId = new SagaUniqueIdentity
{
Id = "Matlock.Renewals.RenewalSaga.RenewalSagaData/PolicyKey/" + Guid.NewGuid().ToString(),
SagaId = saga.Id,
UniqueValue = saga.PolicyKey,
SagaDocId = "RenewalSaga/" + saga.Id.ToString()
};
session.Store(policyKeySagaUniqueId);
Any help much appreciated.
EDIT
Thanks to David's help on this we have fixed our problem - the key difference was we used the SagaUniqueIdentity.FormatId() to generate our document IDs rather than a new guid - this was trivial tio do since we were already referencing the NServiceBus and NServiceBus.Core assemblies.
The short answer is that it is not enough to make the data resemble the new identity documents. Where you are using Guid.NewGuid().ToString(), that data is important! That's why your solution isn't working right now. I spoke about the concept of identity documents (specifically about the NServiceBus use case) during the last quarter of my talk at RavenConf 2014 - here are the slides and video.
So here is the long answer:
In RavenDB, the only ACID guarantees are on the Load/Store by Id operations. So if two threads are acting on the same Saga concurrently, and one stores the Saga data, the second thread can only expect to get back the correct saga data if it is also loading a document by its Id.
To guarantee this, the Raven Saga Persister uses an identity document like the one you showed. It contains the SagaId, the UniqueValue (mostly for human comprehension and debugging, the database doesn't technically need it), and the SagaDocId (which is a little duplication as its only the {SagaTypeName}/{SagaId} where we already have the SagaId.
With the SagaDocId, we can use the Include feature of RavenDB to do a query like this (which is from memory, probably wrong, and should only serve to illustrate the concept as pseudocode)...
var identityDocId = // some value based on incoming message
var idDoc = RavenSession
// Look at the identity doc's SagaDocId and pull back that document too!
.Include<SagaIdentity>(identityDoc => identityDoc.SagaDocId)
.Load(identityDocId);
var sagaData = RavenSession
.Load(idDoc.SagaDocId); // Already in-memory, no 2nd round-trip to database!
So then the identityDocId is very important because it describes the uniqueness of the value coming from the message, not just any old Guid will do. So what we really need to know is how to calculate that.
For that, the NServiceBus saga persister code is instructive:
void StoreUniqueProperty(IContainSagaData saga)
{
var uniqueProperty = UniqueAttribute.GetUniqueProperty(saga);
if (!uniqueProperty.HasValue) return;
var id = SagaUniqueIdentity.FormatId(saga.GetType(), uniqueProperty.Value);
var sagaDocId = sessionFactory.Store.Conventions.FindFullDocumentKeyFromNonStringIdentifier(saga.Id, saga.GetType(), false);
Session.Store(new SagaUniqueIdentity
{
Id = id,
SagaId = saga.Id,
UniqueValue = uniqueProperty.Value.Value,
SagaDocId = sagaDocId
});
SetUniqueValueMetadata(saga, uniqueProperty.Value);
}
The important part is the SagaUniqueIdentity.FormatId method from the same file.
public static string FormatId(Type sagaType, KeyValuePair<string, object> uniqueProperty)
{
if (uniqueProperty.Value == null)
{
throw new ArgumentNullException("uniqueProperty", string.Format("Property {0} is marked with the [Unique] attribute on {1} but contains a null value. Please make sure that all unique properties are set on your SagaData and/or that you have marked the correct properties as unique.", uniqueProperty.Key, sagaType.Name));
}
var value = Utils.DeterministicGuid.Create(uniqueProperty.Value.ToString());
var id = string.Format("{0}/{1}/{2}", sagaType.FullName.Replace('+', '-'), uniqueProperty.Key, value);
// raven has a size limit of 255 bytes == 127 unicode chars
if (id.Length > 127)
{
// generate a guid from the hash:
var key = Utils.DeterministicGuid.Create(sagaType.FullName, uniqueProperty.Key);
id = string.Format("MoreThan127/{0}/{1}", key, value);
}
return id;
}
This relies on Utils.DeterministicGuid.Create(params object[] data) which creates a Guid out of an MD5 hash. (MD5 sucks for actual security but we are only looking for likely uniqueness.)
static class DeterministicGuid
{
public static Guid Create(params object[] data)
{
// use MD5 hash to get a 16-byte hash of the string
using (var provider = new MD5CryptoServiceProvider())
{
var inputBytes = Encoding.Default.GetBytes(String.Concat(data));
var hashBytes = provider.ComputeHash(inputBytes);
// generate a guid from the hash:
return new Guid(hashBytes);
}
}
}
That's what you need to replicate to get your utility to work properly.
What's really interesting is that this code made it all the way to production - I'm surprised you didn't run into trouble before this, with messages creating new saga instances when they really shouldn't because they couldn't find the existing Saga data.
I almost think it might be a good idea if NServiceBus would raise a warning any time you tried to find Saga Data by anything other than a [Unique] marked property, because it's an easy thing to forget to do. I filed this issue on GitHub and submitted this pull request to do just that.

Yii CacheDependency based on cache value

This seems like a simple thing and should be part of the base code for Yii, but I can't find a solution anywhere. Here is my scenario.
1) User updates their record (use beforesave to set a cache value, changes with each new save, php unique())
public function beforeSave()
{
Yii::app()->cache->set('userupdate'.$this->id,uniqid());
return parent::beforeSave();
}
2) User data is cached using the cache value in step one as a dependency in the loadModel function of the model.
$model=Users::model()->cache(1800, $dependency)->findByPk($id);
3) User views a page that calls to retrieve their data. Yii evaluates the request to see if the cached valued from step 1 has changed, if it has not pull from cache, if it has pull from db.
While reading this page (http://www.yiiframework.com/doc/guide/1.1/en/caching.data) it has that function if a file date changes, but not one for it a variable changes. Any help in this matter would be great as I am at a loss of how to implement this.
NOTE: I need to use cache to hold the variable as I'm running multiple instances of my application and they need shared over each server and all users (thus session won't work).
After fighting with this I found the solution, don't feel it's completely pretty, but it does work. Any feedback on a cleaner way is much appreciated.
$cache = Yii::app()->cache;
$key1 = 'userupdate'.$id; //main cache value
$key2 = '2userupdate'.$id; //will equal main cache when query is cached
$cache1 = $cache['userupdate'.$id];
$cache2 = $cache['2userupdate'.$id];
$dependency = new CExpressionDependency("Yii::app()->cache->get('$key1') == Yii::app()->cache->get('$key2')");
$model=Users::model()->cache(1800,$dependency)->findByPk($id);
if($cache1 != $cache2)
$cache['2userupdate'.$id] = $cache['userupdate'.$id];
One of the dependency options is CExpressionDependency. You could compare the currently cached beforeSave value to the value you get from the loadModel call.

How to get Dynatree's parent node details

Is there any way to find dynatree's root node details? Supposed tree has 5 level and i selected fifth level's node . I want to get selected nodes and its parents details till root.
Thanks in advance.
The visitParents function on the node should do what you want:
node.visitParents(function (node) {
// do stuff with node
}, true);
Passing true as the second parameter includes the node as well as it's parents.
Try below method:
node.getKeyPath()
It should return string of keys from leaf to root node like below
/_3/_23/_26/_27
You should be then able to retrieve the any node details of that branch by parsing above string
var node = $("#tree").dynatree("getTree").getNodeByKey("3")
This might not be the exact solution but should give you the direction to get to your solution.
var parent = node.getParent();
var parentnode = parent.data.key;
In parentnode you will get the value of parent node.