@Override
  public <T> void setProperty(final PropertyKey<T> key, final T value) throws FrameworkException {

    // check for read-only properties
    // if (StructrApp.getConfiguration().isReadOnlyProperty(type, key) ||
    // (StructrApp.getConfiguration().isWriteOnceProperty(type, key) && (dbRelationship != null) &&
    // dbRelationship.hasProperty(key.name()))) {
    if (key.isReadOnly()
        || (key.isWriteOnce()
            && (dbRelationship != null)
            && dbRelationship.hasProperty(key.dbName()))) {

      if (readOnlyPropertiesUnlocked || securityContext.isSuperUser()) {

        // permit write operation once and
        // lock read-only properties again
        readOnlyPropertiesUnlocked = false;

      } else {

        throw new FrameworkException(getClass().getSimpleName(), new ReadOnlyPropertyToken(key));
      }
    }

    key.setProperty(securityContext, this, value);
  }
  public void updateChangeLog(
      final Principal user,
      final Verb verb,
      final PropertyKey key,
      final Object previousValue,
      final Object newValue) {

    if (changeLogEnabled && changeLog != null && key != null) {

      final String name = key.jsonName();

      if (!hiddenPropertiesInAuditLog.contains(name)
          && !(key.isUnvalidated() || key.isReadOnly())) {

        final JsonObject obj = new JsonObject();

        obj.add("time", toElement(System.currentTimeMillis()));
        obj.add("userId", toElement(user.getUuid()));
        obj.add("userName", toElement(user.getName()));
        obj.add("verb", toElement(verb));
        obj.add("key", toElement(key.jsonName()));
        obj.add("prev", toElement(previousValue));
        obj.add("val", toElement(newValue));

        changeLog.append(obj.toString());
        changeLog.append("\n");
      }
    }
  }
  private synchronized <
          A extends NodeInterface, B extends NodeInterface, R extends Relation<A, B, ?, ?>>
      R createRelationship(
          final A fromNode, final B toNode, final Class<R> relType, final PropertyMap properties)
          throws FrameworkException {

    final RelationshipFactory<R> factory = new RelationshipFactory(securityContext);
    final R template = instantiate(relType);
    final Node startNode = fromNode.getNode();
    final Node endNode = toNode.getNode();
    final Relationship rel = startNode.createRelationshipTo(endNode, template);
    final R newRel = factory.instantiate(rel);
    final Date now = new Date();

    // logger.log(Level.INFO, "CREATING relationship {0}-[{1}]->{2}", new Object[] {
    // fromNode.getType(), newRel.getRelType(), toNode.getType() } );

    if (newRel != null) {

      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(GraphObject.type, relType.getSimpleName());

      // set UUID
      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(GraphObject.id, getNextUuid());

      // set created date
      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(AbstractRelationship.createdDate, now);

      // set last modified date
      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(AbstractRelationship.lastModifiedDate, now);

      // Try to get the cascading delete flag from the domain specific relationship type
      newRel.unlockReadOnlyPropertiesOnce();
      newRel.setProperty(
          AbstractRelationship.cascadeDelete, factory.instantiate(rel).getCascadingDeleteFlag());

      // notify transaction handler
      TransactionCommand.relationshipCreated(newRel);

      if (properties != null) {

        for (Entry<PropertyKey, Object> entry : properties.entrySet()) {

          PropertyKey key = entry.getKey();

          // on creation, writing of read-only properties should be possible
          if (key.isReadOnly() || key.isWriteOnce()) {
            newRel.unlockReadOnlyPropertiesOnce();
          }

          newRel.setProperty(entry.getKey(), entry.getValue());
        }
      }

      // notify relationship of its creation
      newRel.onRelationshipCreation();

      // iterate post creation transformations
      for (Transformation<GraphObject> transformation :
          StructrApp.getConfiguration().getEntityCreationTransformations(newRel.getClass())) {

        transformation.apply(securityContext, newRel);
      }
    }

    return newRel;
  }