/*
* Role.java
*
* Created on Mar 11, 2010, 2:16:21 PM
*
* Description: Provides a role in an Albus Hierarchical Control System node.
*
* Copyright (C) Mar 11, 2010 reed.
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.texai.ahcsSupport.domainEntity;
import java.util.Objects;
import org.texai.ahcsSupport.AbstractSkill;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import javax.persistence.Id;
import net.jcip.annotations.ThreadSafe;
import org.apache.log4j.Logger;
import org.openrdf.model.URI;
import org.openrdf.model.impl.URIImpl;
import org.texai.ahcsSupport.AHCSConstants;
import org.texai.ahcsSupport.AHCSConstants.State;
import org.texai.ahcsSupport.AbstractSubSkill;
import org.texai.ahcsSupport.AlbusMessageDispatcher;
import org.texai.ahcsSupport.ManagedSessionSkill;
import org.texai.ahcsSupport.Message;
import org.texai.ahcsSupport.NodeAccess;
import org.texai.ahcsSupport.NodeRuntime;
import org.texai.ahcsSupport.RoleInfo;
import org.texai.ahcsSupport.SessionManagerSkill;
import org.texai.kb.persistence.CascadePersistence;
import org.texai.kb.persistence.RDFEntity;
import org.texai.kb.persistence.RDFEntityManager;
import org.texai.kb.persistence.RDFPersistent;
import org.texai.kb.persistence.RDFProperty;
import org.texai.kb.persistence.RDFUtility;
import org.texai.kb.util.UUIDUtils;
import org.texai.util.StringUtils;
import org.texai.util.TexaiException;
import org.texai.x509.X509SecurityInfo;
import org.texai.x509.X509Utils;
/** Provides a role in an Albus Hierarchical Control System node.
*
* @author reed
*/
@ThreadSafe
@RDFEntity(context = "texai:AlbusHierarchicalControlSystemContext")
public class Role implements CascadePersistence, AlbusMessageDispatcher, Comparable<Role> {
/** the serial version UID */
private static final long serialVersionUID = 1L;
/** the logger */
private static final Logger LOGGER = Logger.getLogger(Role.class);
/** the id assigned by the persistence framework */
@Id
private URI id; // NOPMD
/** the role type URI, note that the role type is persisted into a different repository */
@RDFProperty()
private final URI roleTypeURI;
/** the role type name */
@RDFProperty()
private final String roleTypeName;
/** the containing node */
@RDFProperty(predicate = "texai:ahcsNode_role", inverse = true)
private Node node;
/** the parent role id string */
@RDFProperty()
private String parentRoleIdString;
/** the child role id strings */
@RDFProperty()
private final Set<String> childRoleIdStrings = new HashSet<>();
/** the state values */
@RDFProperty()
private final Set<StateValueBinding> stateValueBindings = new HashSet<>();
/** the role X509 certificate alias */
@RDFProperty
private String roleAlias;
/** the node state variable dictionary, state variable name --> state value binding */
private final Map<String, StateValueBinding> stateVariableDictionary = new HashMap<>();
/** the node runtime */
private transient NodeRuntime nodeRuntime;
/** the X.509 security information for this role */
private transient X509SecurityInfo x509SecurityInfo;
/** the role's skill dictionary, service (skill class name) --> skill */
private transient final Map<String, AbstractSkill> skillDictionary = new HashMap<>();
/** the role state */
private final AtomicReference<State> roleState = new AtomicReference<>(State.UNINITIALIZED);
/** the subskills dictionary, subskill class name --> subskill shared instance */
private final Map<String, AbstractSubSkill> subSkillsDictionary = new HashMap<>();
/** Constructs a new Role instance. */
public Role() {
roleTypeURI = null;
roleTypeName = null;
nodeRuntime = null;
}
/** Constructs a new Role instance.
*
* @param roleType the role type
* @param nodeRuntime the node runtime
*/
public Role(
RoleType roleType,
final NodeRuntime nodeRuntime) {
//Preconditions
assert roleType != null : "roleType must not be null";
assert roleType.getId() != null : "roleType id must not be null";
assert roleType.getTypeName() != null : "role type name must not be null";
assert !roleType.getTypeName().isEmpty() : "role type name must not be empty";
// nodeRuntime and nodeRuntimeRoleId may be null only for unit testing
this.roleTypeURI = roleType.getId();
this.roleTypeName = roleType.getTypeName();
this.nodeRuntime = nodeRuntime;
}
/** Gets the id assigned by the persistence framework.
*
* @return the id assigned by the persistence framework
*/
@Override
public URI getId() {
return id;
}
/** Sets the id for the persistence framework.
*
* @param id the id for the persistence framework
*/
public void setId(final URI id) {
//Preconditions
assert id != null : "id must not be null";
this.id = id;
}
/** Gets the role type URI.
*
* @return the role type URI
*/
public URI getRoleTypeURI() {
return roleTypeURI;
}
/** Gets the containing node.
*
* @return the containing node
*/
public synchronized Node getNode() {
return node;
}
/** Sets the containing node.
*
* @param node the containing node
*/
public synchronized void setNode(final Node node) {
this.node = node;
}
/** Gets the role state.
*
* @return the role state
*/
public synchronized State getRoleState() {
return roleState.get();
}
/** Installs the skills for this role.
*
* @param nodeAccess the node access object
*/
public void installSkills(final NodeAccess nodeAccess) {
//Preconditions
assert nodeAccess != null : "nodeAccess must not be null";
final Set<SkillClass> skillClasses = nodeAccess.getAllSkillClasses(this);
synchronized (skillDictionary) {
for (final SkillClass skillClass : skillClasses) {
final String skillClassName = skillClass.getSkillClassName();
assert !skillDictionary.containsKey(skillClassName) : "skill must not be previously installed: " + skillClassName;
final Class<?> clazz;
try {
clazz = Class.forName(skillClassName);
} catch (ClassNotFoundException ex) {
LOGGER.error("cannot find class for '" + skillClassName + "'");
throw new TexaiException(ex);
}
final AbstractSkill skill;
try {
skill = (AbstractSkill) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException ex) {
throw new TexaiException(ex);
}
if (clazz.isAnnotationPresent(ManagedSessionSkill.class)) {
// wrap the skill in a session managing skill
final SessionManagerSkill sessionManagerSkill = new SessionManagerSkill();
sessionManagerSkill.setRole(this);
sessionManagerSkill.setSkillClass(clazz);
skillDictionary.put(skillClassName, sessionManagerSkill);
LOGGER.info(getNode().getNodeNickname() + ": " + this + " constructed managed session skill: " + skill);
} else {
// ordinary skill that does not need sessions managed
skill.setRole(this);
skillDictionary.put(skillClassName, skill);
LOGGER.info(getNode().getNodeNickname() + ": " + this + " constructed skill: " + skill);
}
}
}
}
/** Gets an unmodifiable copy of the role's skills.
*
* @return the the role's skills
*/
public Set<AbstractSkill> getSkills() {
synchronized (skillDictionary.values()) {
final Set<AbstractSkill> skills = new HashSet<>();
skills.addAll(skillDictionary.values());
return Collections.unmodifiableSet(skills);
}
}
/** Finds the role's skill instance having the specified class name (service).
*
* @param subSkillClassName the specified class name (service)
* @return the skill
*/
public AbstractSkill getSkill(final String subSkillClassName) {
//Preconditions
assert StringUtils.isNonEmptyString(subSkillClassName) : "subSkillClassName must be a non-empty string";
synchronized (skillDictionary.values()) {
return skillDictionary.get(subSkillClassName);
}
}
/** Gets the parent role id string.
*
* @return the parent role id string
*/
public String getParentRoleIdString() {
return parentRoleIdString;
}
/** Sets the parent role id string.
*
* @param parentRoleIdString the parent role id string
*/
public void setParentRoleIdString(final String parentRoleIdString) {
//Preconditions
assert parentRoleIdString != null : "parentRoleIdString must not be null";
assert !parentRoleIdString.isEmpty() : "parentRoleIdString must not be empty";
this.parentRoleIdString = parentRoleIdString;
}
/** Gets the parent role id.
*
* @return the parent role id
*/
public URI getParentRoleId() {
if (parentRoleIdString == null) {
return null;
} else {
return new URIImpl(parentRoleIdString);
}
}
/** Gets an unmodifiable copy of the child role id strings.
*
* @return the child role id strings
*/
public Set<String> getChildRoleIdStrings() {
synchronized (childRoleIdStrings) {
return Collections.unmodifiableSet(childRoleIdStrings);
}
}
/** Adds a child role.
*
* @param childRoleIdString the child role id string
*/
public void addChildRole(final String childRoleIdString) {
//Preconditions
assert childRoleIdString != null : "childRoleIdString must not be null";
assert !childRoleIdString.isEmpty() : "childRoleIdString must not be empty";
synchronized (childRoleIdStrings) {
childRoleIdStrings.add(childRoleIdString);
}
final String childRoleClassName = RDFUtility.getDefaultClassFromIdString(childRoleIdString);
assert childRoleIdString != null;
}
/** Removes the given child role.
*
* @param childRoleIdString the given child role id string
*/
public void removeChildRole(final String childRoleIdString) {
//Preconditions
assert childRoleIdString != null : "childRoleIdString must not be null";
assert !childRoleIdString.isEmpty() : "childRoleIdString must not be empty";
synchronized (childRoleIdStrings) {
childRoleIdStrings.remove(childRoleIdString);
}
}
/** Gets the node runtime.
*
* @return the node runtime
*/
public NodeRuntime getNodeRuntime() {
return nodeRuntime;
}
/** Sets the node runtime.
*
* @param nodeRuntime the node runtime
*/
public void setNodeRuntime(final NodeRuntime nodeRuntime) {
//Preconditions
assert nodeRuntime != null : "nodeRuntime must not be null";
this.nodeRuntime = nodeRuntime;
}
/** Gets the X.509 security information.
*
* @return the X.509 security information
*/
public X509SecurityInfo getX509SecurityInfo() {
return x509SecurityInfo;
}
/** Sets the X.509 security information.
*
* @param x509SecurityInfo the X.509 security information
*/
public void setX509SecurityInfo(final X509SecurityInfo x509SecurityInfo) {
//Preconditions
assert x509SecurityInfo != null : "x509SecurityInfo must not be null";
assert id != null : "id must not be null";
assert X509Utils.getUUID(x509SecurityInfo.getX509Certificate()).equals(UUIDUtils.uriToUUID(id)) :
"X.509 certificate subject's UID must match this role id";
this.x509SecurityInfo = x509SecurityInfo;
}
/** Returns the X.509 certificate belonging to this role.
*
* @return the X.509 certificate belonging to this role
*/
public X509Certificate getX509Certificate() {
if (x509SecurityInfo == null) {
return null;
} else {
return x509SecurityInfo.getX509Certificate();
}
}
/** Gets the role state value associated with the given variable name.
*
* @param stateVariableName the given variable name
* @return the state value associated with the given variable name
*/
public Object getRoleStateValue(final String stateVariableName) {
//Preconditions
assert stateVariableName != null : "stateVariableName must not be null";
assert !stateVariableName.isEmpty() : "stateVariableName must not be empty";
synchronized (stateVariableDictionary) {
final StateValueBinding stateValueBinding = stateVariableDictionary.get(stateVariableName);
if (stateValueBinding == null) {
return null;
} else {
return stateValueBinding.getValue();
}
}
}
/** Sets the role state value associated with the given variable name.
*
* @param variableName the given variable name
* @param value the state value
*/
public void setRoleStateValue(final String variableName, final Object value) {
//Preconditions
assert variableName != null : "variableName must not be null";
assert !variableName.isEmpty() : "variableName must not be empty";
synchronized (stateVariableDictionary) {
StateValueBinding stateValueBinding = stateVariableDictionary.get(variableName);
if (stateValueBinding == null) {
stateValueBinding = new StateValueBinding(variableName, value);
stateVariableDictionary.put(variableName, stateValueBinding);
stateValueBindings.add(stateValueBinding);
} else {
stateValueBinding.setValue(value);
}
}
}
/** Removes the role state value binding for the given variable.
*
* @param variableName the variable name
*/
public void removeRoleStateValueBinding(final String variableName) {
//Preconditions
assert variableName != null : "variableName must not be null";
assert !variableName.isEmpty() : "variableName must not be empty";
synchronized (stateVariableDictionary) {
if (stateVariableDictionary.isEmpty() && !stateValueBindings.isEmpty()) {
// lazy population of the state value dictionary from the persistent state value bindings
for (final StateValueBinding stateValueBinding : stateValueBindings) {
stateVariableDictionary.put(stateValueBinding.getVariableName(), stateValueBinding);
}
}
final StateValueBinding stateValueBinding = stateVariableDictionary.remove(variableName);
if (stateValueBinding != null) {
final boolean isRemoved = stateValueBindings.remove(stateValueBinding);
assert isRemoved;
}
}
}
/** Gets the node state value associated with the given variable name.
*
* @param stateVariableName the given variable name
* @return the state value associated with the given variable name
*/
public Object getNodeStateValue(final String stateVariableName) {
//Preconditions
assert stateVariableName != null : "stateVariableName must not be null";
assert !stateVariableName.isEmpty() : "stateVariableName must not be empty";
return node.getNodeStateValue(stateVariableName);
}
/** Sets the node state value associated with the given variable name.
*
* @param variableName the given variable name
* @param value the state value
*/
public void setNodeStateValue(final String variableName, final Object value) {
//Preconditions
assert variableName != null : "variableName must not be null";
assert !variableName.isEmpty() : "variableName must not be empty";
node.setNodeStateValue(variableName, value);
}
/** Removes the node state value binding for the given variable.
*
* @param variableName the variable name
*/
public void removeNodeStateValueBinding(final String variableName) {
//Preconditions
assert variableName != null : "variableName must not be null";
assert !variableName.isEmpty() : "variableName must not be empty";
node.removeNodeStateValueBinding(variableName);
}
/** Gets the role type name.
* @return the role type name
*/
public String getRoleTypeName() {
return roleTypeName;
}
/** Sends the given message to the addressed sub-skill and returns the response message.
*
* @param message the message for the addressed sub-skill
* @return the response message
*/
public Message converseMessageWithSubSkill(final Message message) {
//Preconditions
assert message != null : "message must not be null";
AbstractSkill skill;
synchronized (skillDictionary) {
skill = skillDictionary.get(message.getService());
}
if (skill == null) {
LOGGER.info(getNode().getNodeNickname() + ": subskill not found for service: " + message.getService() + ", constructing it");
// ordinary skill that does not need sessions managed
try {
skill = (AbstractSkill) Class.forName(message.getService()).newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
throw new TexaiException(ex);
}
skill.setRole(this);
skillDictionary.put(message.getService(), skill);
LOGGER.info(getNode().getNodeNickname() + ": " + this + " constructed skill: " + skill);
}
return skill.converseMessage(message);
}
/** Receives an inbound message for this role.
*
* @param message the Albus message
*/
@Override
public void dispatchAlbusMessage(final Message message) {
//Preconditions
assert message != null : "message must not be null";
final String operation = message.getOperation();
if (operation.equals(AHCSConstants.MESSAGE_NOT_UNDERSTOOD_INFO)) {
LOGGER.warn(message);
return;
}
// the message service field optionally names a skill interface
AbstractSkill skill = null;
if (message.getService() != null) {
synchronized (skillDictionary) {
skill = skillDictionary.get(message.getService());
if (skill == null) {
// not a primary skill for this role, try the shared subskill dictionary
skill = findSubSkill(message.getService());
}
assert skill != null : "service not found " + message.getService() + "\n" + message.toString(getNodeRuntime()) + "\n skillDictionary: " + skillDictionary;
}
}
if (skill == null) {
// dispatch the message to any skill that understands the operation
boolean isSkillFound = false;
for (final AbstractSkill skill1 : getSkills()) {
if (skill1.isOperationUnderstood(message.getOperation())) {
isSkillFound = true;
LOGGER.info(this + ", skill " + skill1.toString() + " understands " + message.getOperation());
skill1.receiveMessage(message);
}
}
if (!isSkillFound) {
LOGGER.info(getNode().getNodeNickname() + ": skill not found for service: " + message.toString(getNodeRuntime()));
LOGGER.info(getNode().getNodeNickname() + ": skillDictionary:\n " + skillDictionary);
// not understood
final Message message1 = new Message(
id, // senderRoleId
getClass().getName(), // senderService,
message.getSenderRoleId(), // recipientRoleId
message.getSenderService(), // service
AHCSConstants.MESSAGE_NOT_UNDERSTOOD_INFO); // operation
message1.put(AHCSConstants.AHCS_ORIGINAL_MESSAGE, message);
sendMessage(message1);
}
} else {
skill.receiveMessage(message);
}
}
/** Sends the given message via the node runtime.
*
* @param message the given message
*/
public void sendMessage(final Message message) {
//Preconditions
assert message != null : "message must not be null";
assert nodeRuntime != null : "nodeRuntime must not be null";
final URI recipientRoleId = message.getRecipientRoleId();
if (!message.getSenderRoleId().equals(id)) {
LOGGER.warn("cannot send message for which this role is not the sender role " + message);
throw new TexaiException("cannot send message for which this role is not the sender " + message);
}
if (!getNodeRuntime().isLocalRole(recipientRoleId)) {
// sign messages sent between non-local roles
message.sign(x509SecurityInfo.getPrivateKey());
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(getNode().getNodeNickname() + ": sending message: " + message.toString(nodeRuntime));
}
nodeRuntime.dispatchAlbusMessage(message);
}
/** Propagates the given operation to the child roles.
*
* @param operation the given operation
* @param senderService the sender service
* @param service the recipient service, which if null indicates that any service that understands the operation will receive the message
*/
public void propagateOperationToChildRoles(
final String operation,
final String senderService,
final String service) {
//Preconditions
assert operation != null : "operation must not be null";
assert !operation.isEmpty() : "operation must not be empty";
assert senderService != null : "senderService must not be null";
assert !senderService.isEmpty() : "senderService must not be empty";
switch (operation) {
case AHCSConstants.AHCS_INITIALIZE_TASK:
if (roleState.get().equals(State.UNINITIALIZED)) {
LOGGER.info(getRoleTypeName() + " propagating initialize task to child roles");
} else {
// the child roles are already initialized
return;
}
break;
case AHCSConstants.AHCS_READY_TASK:
if (roleState.get().equals(State.INITIALIZED)) {
LOGGER.info(getRoleTypeName() + " propagating ready task to child roles");
} else {
// the child roles are already ready
return;
}
break;
}
for (final String childRoleIdString : getChildRoleIdStrings()) {
assert !childRoleIdString.equals(id.toString()) : "role " + this + " has itself as a child role";
sendMessage(new Message(
id, // senderRoleId
senderService,
new URIImpl(childRoleIdString), // recipientRoleId
null, // service
operation)); // operation
}
switch (operation) {
case AHCSConstants.AHCS_INITIALIZE_TASK:
roleState.set(State.INITIALIZED);
break;
case AHCSConstants.AHCS_READY_TASK:
roleState.set(State.READY);
break;
}
}
/** When implemented by a message router, registers the given SSL proxy.
*
* @param sslProxy the given SSL proxy
*/
@Override
public void registerSSLProxy(Object sslProxy) {
throw new UnsupportedOperationException("Not implemented.");
}
/** Enables this role for messaging with remote roles via the local message router. */
public void enableRemoteComunications() {
final RoleInfo roleInfo = new RoleInfo(
id, // roleId
x509SecurityInfo.getCertPath(),
x509SecurityInfo.getPrivateKey(),
nodeRuntime.getLocalAreaNetworkID(),
nodeRuntime.getExternalHostName(),
nodeRuntime.getExternalPort(),
nodeRuntime.getInternalHostName(),
nodeRuntime.getInternalPort());
nodeRuntime.registerRoleForRemoteCommunications(roleInfo);
}
/** Gets the role X509 certificate alias.
*
* @return the role X509 certificate alias
*/
public String getRoleAlias() {
return roleAlias;
}
/** Sets the role X509 certificate alias.
*
* @param roleAlias the roleAlias to set
*/
public void setRoleAlias(final String roleAlias) {
//Preconditions
assert roleAlias != null : "roleAlias must not be null";
assert !roleAlias.isEmpty() : "roleAlias must not be empty";
this.roleAlias = roleAlias;
}
/** Finds or creates a sharable subskill instance.
*
* @param subSkillClassName the class name of the sharable subskill
* @return a sharable subskill instance
*/
public AbstractSubSkill findOrCreateSubSkill(final String subSkillClassName) {
//Preconditions
assert StringUtils.isNonEmptyString(subSkillClassName) : "subSkillClassName must be a non-empty string";
final Class<?> clazz;
try {
clazz = Class.forName(subSkillClassName);
} catch (ClassNotFoundException ex) {
throw new TexaiException(ex);
}
AbstractSubSkill subSkill;
// find an existing sharable subskill instance or create a new one and initialize it
synchronized (subSkillsDictionary) {
subSkill = subSkillsDictionary.get(subSkillClassName);
if (subSkill == null) {
subSkill = instantiateSubSkill(clazz);
subSkill.setRole(this);
subSkill.initialization();
subSkillsDictionary.put(subSkillClassName, subSkill);
}
}
return subSkill;
}
/** Returns an instance of the given subskill class.
*
* @param clazz the given subskill class
* @return an instance of the given subskill class
*/
private AbstractSubSkill instantiateSubSkill(final Class<?> clazz) {
//Preconditions
assert clazz != null : "clazz must not be null";
try {
final AbstractSubSkill subSkill = (AbstractSubSkill) clazz.newInstance();
subSkill.setSkillState(State.READY);
return subSkill;
} catch (InstantiationException | IllegalAccessException ex) {
throw new TexaiException(ex);
}
}
/** Finds a sharable subskill instance.
*
* @param subSkillClassName the class name of the sharable subskill
* @return a sharable subskill instance, or null if not found
*/
public AbstractSubSkill findSubSkill(final String subSkillClassName) {
//Preconditions
assert StringUtils.isNonEmptyString(subSkillClassName) : "subSkillClassName must be a non-empty string";
synchronized (subSkillsDictionary) {
return subSkillsDictionary.get(subSkillClassName);
}
}
/** Returns whether the other object equals this one.
*
* @param obj the other object
* @return whether the other object equals this one
*/
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Role other = (Role) obj;
return Objects.equals(this.id, other.id);
}
/** Returns a hash code for this object.
*
* @return a hash code for this object
*/
@Override
public int hashCode() {
int hash = 7;
hash = 71 * hash + Objects.hashCode(this.id);
return hash;
}
/** Returns a string representation of this object.
*
* @return a string representation of this object
*/
@Override
public String toString() {
//Preconditions
assert roleTypeName != null : "role type name must not be null";
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append('[');
if (node != null) {
stringBuilder.append(node.getNodeNickname()).append(':');
}
stringBuilder.append(roleTypeName).append("]");
return stringBuilder.toString();
}
/** Ensures that this persistent object is fully instantiated. */
@Override
public void instantiate() {
for (final StateValueBinding stateValueBinding : stateValueBindings) {
stateValueBinding.instantiate();
}
for (final StateValueBinding stateValueBinding : stateValueBindings) {
LOGGER.info(this + " state value: " + stateValueBinding);
stateVariableDictionary.put(
stateValueBinding.getVariableName(),
stateValueBinding);
}
}
/** Recursively persists this RDF entity and all its components.
*
* @param rdfEntityManager the RDF entity manager
* @param overrideContext the user's belief context, or null to persist to each object's default context
*/
public void cascadePersist(
final RDFEntityManager rdfEntityManager,
final URI overrideContext) {
//Preconditions
assert rdfEntityManager != null : "rdfEntityManager must not be null";
cascadePersist(this, rdfEntityManager, overrideContext);
}
/** Recursively persists this RDF entity and all its components.
*
* @param rootRDFEntity the root RDF entity
* @param rdfEntityManager the RDF entity manager
* @param overrideContext the user's belief context, or null to persist to each object's default context
*/
@Override
public void cascadePersist(RDFPersistent rootRDFEntity, RDFEntityManager rdfEntityManager, URI overrideContext) {
//Preconditions
assert rdfEntityManager != null : "rdfEntityManager must not be null";
for (final StateValueBinding stateValueBinding : stateValueBindings) {
stateValueBinding.cascadePersist(
rootRDFEntity,
rdfEntityManager,
overrideContext);
}
rdfEntityManager.persist(this, overrideContext);
}
/** Recursively removes this RDF entity and all its unshared components.
*
* @param rootRDFEntity the root RDF entity
* @param rdfEntityManager the RDF entity manager
*/
@Override
public void cascadeRemove(RDFPersistent rootRDFEntity, RDFEntityManager rdfEntityManager) {
//Preconditions
assert rdfEntityManager != null : "rdfEntityManager must not be null";
for (final StateValueBinding stateValueBinding : stateValueBindings) {
stateValueBinding.cascadeRemove(
rootRDFEntity,
rdfEntityManager);
}
rdfEntityManager.remove(this);
}
/** Compares another role with this one.
*
* @param that the other role
* @return -1 if less than, 0 if equal, otherwise return +1
*/
@Override
public int compareTo(final Role that) {
//Preconditions
assert that != null : "that must not be null";
return this.roleTypeName.compareTo(that.roleTypeName);
}
}