Skip to content

Commit ae432e6

Browse files
author
Jeff Bornemann
committed
issue-157 follow references support
1 parent 60df08c commit ae432e6

25 files changed

+661
-199
lines changed

src/main/groovy/com/twcable/grabbit/client/batch/ClientBatchJobExecutionListener.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package com.twcable.grabbit.client.batch
1818

19-
import com.twcable.grabbit.jcr.JcrUtil
19+
import com.twcable.grabbit.jcr.JCRUtil
2020
import groovy.transform.CompileStatic
2121
import groovy.util.logging.Slf4j
2222
import org.apache.sling.jcr.api.SlingRepository
@@ -58,7 +58,7 @@ class ClientBatchJobExecutionListener implements JobExecutionListener {
5858
void beforeJob(JobExecution jobExecution) {
5959
log.debug "SlingRepository : ${slingRepository}"
6060
final clientUsername = jobExecution.jobParameters.getString(ClientBatchJob.CLIENT_USERNAME)
61-
final Session session = JcrUtil.getSession(slingRepository, clientUsername)
61+
final Session session = JCRUtil.getSession(slingRepository, clientUsername)
6262

6363
ClientBatchJobContext.setSession(session)
6464
log.info "Starting job : ${jobExecution}\n\n"

src/main/groovy/com/twcable/grabbit/jcr/ACLProtoNodeDecorator.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class ACLProtoNodeDecorator extends ProtoNodeDecorator {
166166
final ProtoPropertyDecorator privilegeProperty = node.propertiesList.collect { new ProtoPropertyDecorator(it) }.find { ProtoPropertyDecorator property ->
167167
property.isPrivilege()
168168
}
169-
return privilegeProperty.getPropertyValues().collect { Value value -> value.string }.toArray() as String[]
169+
return privilegeProperty.getStringValues().toArray() as String[]
170170
}
171171

172172

src/main/groovy/com/twcable/grabbit/jcr/AuthorizableProtoNodeDecorator.groovy

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import groovy.util.logging.Slf4j
2525
import java.lang.reflect.Field
2626
import java.lang.reflect.Method
2727
import java.lang.reflect.ReflectPermission
28-
import java.util.regex.Pattern
2928
import javax.annotation.Nonnull
3029
import javax.jcr.Session
3130
import org.apache.jackrabbit.api.security.user.Authorizable
@@ -35,7 +34,6 @@ import org.apache.jackrabbit.api.security.user.UserManager
3534
import org.apache.jackrabbit.value.StringValue
3635
import org.apache.sling.jcr.base.util.AccessControlUtil
3736

38-
3937
/**
4038
* This class wraps a serialized node that represents an Authorizable. Authorizables are special system protected nodes, that can only be written under certain
4139
* trees, and can not be written directly by a client.
@@ -59,8 +57,8 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
5957
}
6058
Authorizable existingAuthorizable = findAuthorizable(session)
6159
Authorizable newAuthorizable = existingAuthorizable ? updateAuthorizable(existingAuthorizable, session) : createNewAuthorizable(session)
62-
writeAuthorizablePieces(newAuthorizable, session)
63-
return new JCRNodeDecorator(session.getNode(newAuthorizable.getPath()))
60+
61+
return new JCRNodeDecorator(session.getNode(newAuthorizable.getPath()), getID())
6462
}
6563

6664

@@ -86,11 +84,20 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
8684
setPasswordForUser(newUser, session)
8785
}
8886
session.save()
87+
writeMandatoryPieces(session, newUser.getPath())
8988
return newUser
9089
}
91-
final Group group = userManager.createGroup(authorizableID, new AuthorizablePrincipal(authorizableID), getParentPath())
90+
final Group newGroup = userManager.createGroup(authorizableID, new AuthorizablePrincipal(authorizableID), getParentPath())
91+
/*
92+
* Write all mandatory pieces, and find those that are authorizables. We then need to see if any of them have membership in this group, and add them.
93+
*/
94+
final Collection<JCRNodeDecorator> authorizablePieces = writeMandatoryPieces(session, newGroup.getPath()).findAll { it.isAuthorizableType() }
95+
final Collection<JCRNodeDecorator> members = authorizablePieces.findAll { getMembershipIDs().contains(it.getTransferredID()) }
96+
members.each { JCRNodeDecorator member ->
97+
newGroup.addMember(member as Authorizable)
98+
}
9299
session.save()
93-
return group
100+
return newGroup
94101
}
95102

96103

@@ -100,22 +107,19 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
100107
* @return new instance of updated authorizable
101108
*/
102109
private Authorizable updateAuthorizable(final Authorizable authorizable, final Session session) {
110+
//We get all the declared groups of this authorizable so that we can add them back to the new, updated authorizable
111+
final Collection<Group> declaredGroups = authorizable.declaredMemberOf().toList()
112+
for(Group group : declaredGroups) {
113+
group.removeMember(authorizable)
114+
}
103115
authorizable.remove()
104116
session.save()
105-
createNewAuthorizable(session)
106-
}
107-
108-
109-
/**
110-
* Authorizable pieces (nodes that live under Authorizables - profile, preferences, etc) get sent with the authorizable node instead of streamed independently because we do not know the client's new
111-
* authorizable UUID node name at runtime. In other words, authorizables can live under different node names from server to server
112-
*/
113-
private void writeAuthorizablePieces(final Authorizable authorizable, final Session session) {
114-
innerProtoNode.mandatoryChildNodeList.each {
115-
//We replace the incoming server authorizable path, with the new authorizable path
116-
createFrom(it, it.name.replaceFirst(Pattern.quote(getName()), authorizable.getPath())).writeToJcr(session)
117+
final Authorizable newAuthorizable = createNewAuthorizable(session)
118+
for(Group group: declaredGroups) {
119+
group.addMember(newAuthorizable)
117120
}
118121
session.save()
122+
return newAuthorizable
119123
}
120124

121125

@@ -135,6 +139,11 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
135139
}
136140

137141

142+
private Collection<String> getMembershipIDs() {
143+
return hasProperty('rep:members') ? getStringValuesFrom('rep:members') : []
144+
}
145+
146+
138147
/**
139148
* Some JVM's have a SecurityManager set, which based on configuration, can potentially inhibit our hack {@code setPasswordForUser(User, Session)} from working.
140149
* We need to check security permissions before proceeding
@@ -245,13 +254,4 @@ class AuthorizableProtoNodeDecorator extends ProtoNodeDecorator {
245254
*/
246255
setPasswordMethod.invoke(userManagerDelegate, getTreeMethod.invoke(authorizable), getAuthorizableID(), getStringValueFrom('rep:password'), false)
247256
}
248-
249-
250-
/**
251-
* An instance wrapper for ease of mocking
252-
* @see super.createFrom
253-
*/
254-
ProtoNodeDecorator createFrom(final ProtoNode protoNode, final String nameOverride) {
255-
super.createFrom(protoNode, nameOverride)
256-
}
257257
}

src/main/groovy/com/twcable/grabbit/jcr/DefaultProtoNodeDecorator.groovy

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ import com.twcable.grabbit.proto.NodeProtos.Node as ProtoNode
1919
import com.twcable.grabbit.proto.NodeProtos.Value as ProtoValue
2020
import groovy.transform.CompileStatic
2121
import groovy.util.logging.Slf4j
22-
import java.util.regex.Pattern
2322
import javax.annotation.Nonnull
2423
import javax.jcr.Node as JCRNode
2524
import javax.jcr.Session
25+
import javax.jcr.Value
2626
import org.apache.jackrabbit.commons.JcrUtils
27+
import org.apache.jackrabbit.value.StringValue
2728

2829

2930
import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES
@@ -49,16 +50,34 @@ class DefaultProtoNodeDecorator extends ProtoNodeDecorator {
4950
if(mixinProperty) {
5051
addMixins(mixinProperty, jcrNode)
5152
}
52-
//Then add other properties
53-
writableProperties.each { it.writeToNode(jcrNode) }
5453

55-
if(innerProtoNode.mandatoryChildNodeList && innerProtoNode.mandatoryChildNodeList.size() > 0) {
56-
for(ProtoNode childNode: innerProtoNode.mandatoryChildNodeList) {
57-
//Mandatory children must inherit any name overrides from their parent (if they exist)
58-
createFrom(childNode, childNode.getName().replaceFirst(Pattern.quote(innerProtoNode.name), getName())).writeToJcr(session)
54+
final Collection<ProtoPropertyDecorator> referenceTypeProperties = writableProperties.findAll { it.isReferenceType() }
55+
final Collection<ProtoPropertyDecorator> nonReferenceTypeProperties = writableProperties.findAll { !it.isReferenceType() }
56+
57+
//These can be written now. Reference properties can be written after we write the referenced nodes
58+
nonReferenceTypeProperties.each { it.writeToNode(jcrNode) }
59+
60+
final Collection<JCRNodeDecorator> referenceables = writeMandatoryPieces(session, getName()).findAll { it.isReferenceable() }
61+
62+
/*
63+
* Nodes that are referenced from reference properties are written above in writeMandatoryPieces(). We can now map each
64+
* reference pointer to a transferred id from a node above, and write the pointer with a mapped nodes new identifier.
65+
* The transferred id is what the identifier was on sending server, and the current identifier is what it is now that it is
66+
* written to this server. Identifiers only apply to referenceable nodes (i.e nodes with mix:referenceable)
67+
*/
68+
referenceTypeProperties.each { ProtoPropertyDecorator property ->
69+
final Collection<Value> newReferenceIDValues = property.getStringValues().findResults { String referenceID ->
70+
final JCRNodeDecorator match = referenceables.find { it.transferredID == referenceID }
71+
if(match) {
72+
return new StringValue(match.getIdentifier())
73+
}
74+
} as Collection<Value>
75+
if(!newReferenceIDValues.isEmpty()) {
76+
property.writeToNode(jcrNode, (newReferenceIDValues.toArray() as Value[]))
5977
}
6078
}
61-
return new JCRNodeDecorator(jcrNode)
79+
80+
return new JCRNodeDecorator(jcrNode, getID())
6281
}
6382

6483

0 commit comments

Comments
 (0)