Пример #1
0
  /** @since 3.1 */
  @Override
  public <T extends Persistent> T localObject(T objectFromAnotherContext) {

    if (objectFromAnotherContext == null) {
      throw new NullPointerException("Null object argument");
    }

    ObjectId id = objectFromAnotherContext.getObjectId();

    // first look for the ID in the local GraphManager
    synchronized (getGraphManager()) {
      T localObject = (T) getGraphManager().getNode(id);
      if (localObject != null) {
        return localObject;
      }

      // create a hollow object, optimistically assuming that the ID we
      // got from
      // 'objectFromAnotherContext' is a valid ID either in the parent
      // context or in
      // the DB. This essentially defers possible FaultFailureExceptions.

      ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(id.getEntityName());
      Persistent persistent = (Persistent) descriptor.createObject();

      persistent.setObjectContext(this);
      persistent.setObjectId(id);
      persistent.setPersistenceState(PersistenceState.HOLLOW);

      getGraphManager().registerNode(id, persistent);

      return (T) persistent;
    }
  }
Пример #2
0
  /**
   * Instantiates a new object and registers it with this context. Object class is determined from
   * the mapped entity. Object class must have a default constructor.
   *
   * <p><i>Note: in most cases {@link #newObject(Class)} method should be used, however this method
   * is helpful when generic persistent classes are used.</i>
   *
   * @since 3.0
   */
  public Persistent newObject(String entityName) {
    ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entityName);
    if (descriptor == null) {
      throw new IllegalArgumentException("Invalid entity name: " + entityName);
    }

    Persistent object;
    try {
      object = (Persistent) descriptor.createObject();
    } catch (Exception ex) {
      throw new CayenneRuntimeException("Error instantiating object.", ex);
    }

    // this will initialize to-many lists
    descriptor.injectValueHolders(object);

    ObjectId id = new ObjectId(entityName);

    // note that the order of initialization of persistence artifacts below
    // is
    // important - do not change it lightly
    object.setObjectId(id);

    injectInitialValue(object);

    return object;
  }
Пример #3
0
  @Override
  public void nodeRemoved(Object nodeId) {
    ObjectId id = (ObjectId) nodeId;

    final MutableObjectChange objectChangeSet = changeSet.getOrCreate(id, ObjectChangeType.DELETE);

    // TODO: rewrite with SelectById query after Cayenne upgrade
    ObjectIdQuery query = new ObjectIdQuery(id, true, ObjectIdQuery.CACHE);
    QueryResponse result = channel.onQuery(null, query);

    @SuppressWarnings("unchecked")
    List<DataRow> rows = result.firstList();

    if (rows.isEmpty()) {
      LOGGER.warn(
          "No DB snapshot for object to be deleted, no changes will be recorded. ID: " + id);
      return;
    }

    final DataRow row = rows.get(0);

    ClassDescriptor descriptor = channel.getEntityResolver().getClassDescriptor(id.getEntityName());
    final PostCommitEntity entity = entityFactory.getEntity(id);

    descriptor.visitProperties(
        new PropertyVisitor() {

          @Override
          public boolean visitAttribute(AttributeProperty property) {

            if (!entity.isIncluded(property.getName())) {
              return true;
            }

            Object value;
            if (entity.isConfidential(property.getName())) {
              value = Confidential.getInstance();
            } else {
              String key = property.getAttribute().getDbAttributeName();
              value = row.get(key);
            }

            if (value != null) {
              objectChangeSet.attributeChanged(property.getName(), value, null);
            }
            return true;
          }

          @Override
          public boolean visitToOne(ToOneProperty property) {
            // TODO record FK changes?
            return true;
          }

          @Override
          public boolean visitToMany(ToManyProperty property) {
            return true;
          }
        });
  }
  private PostCommitEntity createDescriptor(String entityName) {
    EntityResolver entityResolver = getEntityResolver();
    ClassDescriptor classDescriptor = entityResolver.getClassDescriptor(entityName);

    Auditable annotation = classDescriptor.getObjectClass().getAnnotation(Auditable.class);
    if (annotation == null) {
      return BLOCKED_ENTITY;
    }

    ObjEntity entity = entityResolver.getObjEntity(entityName);
    return new DefaultPostCommitEntity(
        entity, annotation.ignoredProperties(), annotation.confidential());
  }
Пример #5
0
  /**
   * Returns a map key for a given to-many map relationship and a target object.
   *
   * @since 3.0
   */
  protected Object getMapKey(String relationshipName, Object value) {

    EntityResolver resolver = objectContext.getEntityResolver();
    ClassDescriptor descriptor = resolver.getClassDescriptor(objectId.getEntityName());

    if (descriptor == null) {
      throw new IllegalStateException("DataObject's entity is unmapped, objectId: " + objectId);
    }

    PropertyDescriptor property = descriptor.getProperty(relationshipName);
    if (property instanceof ToManyMapProperty) {
      return ((ToManyMapProperty) property).getMapKey(value);
    }

    throw new IllegalArgumentException(
        "Relationship '" + relationshipName + "' is not a to-many Map");
  }
Пример #6
0
  /**
   * An internal version of {@link #localObject(Object)} that operates on ObjectId instead of
   * Persistent, and wouldn't attempt to look up an object in the parent channel.
   *
   * @since 3.1
   */
  Persistent findOrCreateObject(ObjectId id) {

    if (id == null) {
      throw new IllegalArgumentException("Null ObjectId");
    }

    // have to synchronize almost the entire method to prevent multiple
    // threads from
    // messing up dataobjects per CAY-845. Originally only parts of "else"
    // were
    // synchronized, but we had to expand the lock scope to ensure
    // consistent
    // behavior.
    synchronized (getGraphManager()) {
      Persistent cachedObject = (Persistent) getGraphManager().getNode(id);

      // return an existing object
      if (cachedObject != null) {

        int state = cachedObject.getPersistenceState();

        // TODO: Andrus, 1/24/2006 implement smart merge for modified
        // objects...
        if (state != PersistenceState.MODIFIED && state != PersistenceState.DELETED) {

          ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(id.getEntityName());

          descriptor.injectValueHolders(cachedObject);
        }

        return cachedObject;
      }

      // create and register a hollow object
      ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(id.getEntityName());
      Persistent localObject = (Persistent) descriptor.createObject();

      localObject.setObjectContext(this);
      localObject.setObjectId(id);

      getGraphManager().registerNode(id, localObject);
      localObject.setPersistenceState(PersistenceState.HOLLOW);

      return localObject;
    }
  }
Пример #7
0
  /**
   * Registers a transient object with the context, recursively registering all transient persistent
   * objects attached to this object via relationships.
   *
   * <p><i>Note that since 3.0 this method takes Object as an argument instead of a {@link
   * DataObject}.</i>
   *
   * @param object new object that needs to be made persistent.
   */
  @Override
  public void registerNewObject(Object object) {
    if (object == null) {
      throw new NullPointerException("Can't register null object.");
    }

    ObjEntity entity = getEntityResolver().getObjEntity((Persistent) object);
    if (entity == null) {
      throw new IllegalArgumentException(
          "Can't find ObjEntity for Persistent class: "
              + object.getClass().getName()
              + ", class is likely not mapped.");
    }

    final Persistent persistent = (Persistent) object;

    // sanity check - maybe already registered
    if (persistent.getObjectId() != null) {
      if (persistent.getObjectContext() == this) {
        // already registered, just ignore
        return;
      } else if (persistent.getObjectContext() != null) {
        throw new IllegalStateException(
            "Persistent is already registered with another DataContext. "
                + "Try using 'localObjects()' instead.");
      }
    } else {
      persistent.setObjectId(new ObjectId(entity.getName()));
    }

    ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName());
    if (descriptor == null) {
      throw new IllegalArgumentException("Invalid entity name: " + entity.getName());
    }

    injectInitialValue(object);

    // now we need to find all arc changes, inject missing value holders and
    // pull in
    // all transient connected objects

    descriptor.visitProperties(
        new PropertyVisitor() {

          public boolean visitToMany(ToManyProperty property) {
            property.injectValueHolder(persistent);

            if (!property.isFault(persistent)) {

              Object value = property.readProperty(persistent);
              Collection<Map.Entry> collection =
                  (value instanceof Map) ? ((Map) value).entrySet() : (Collection) value;

              Iterator<Map.Entry> it = collection.iterator();
              while (it.hasNext()) {
                Object target = it.next();

                if (target instanceof Persistent) {
                  Persistent targetDO = (Persistent) target;

                  // make sure it is registered
                  registerNewObject(targetDO);
                  getObjectStore()
                      .arcCreated(
                          persistent.getObjectId(), targetDO.getObjectId(), property.getName());
                }
              }
            }
            return true;
          }

          public boolean visitToOne(ToOneProperty property) {
            Object target = property.readPropertyDirectly(persistent);

            if (target instanceof Persistent) {

              Persistent targetDO = (Persistent) target;

              // make sure it is registered
              registerNewObject(targetDO);
              getObjectStore()
                  .arcCreated(persistent.getObjectId(), targetDO.getObjectId(), property.getName());
            }
            return true;
          }

          public boolean visitAttribute(AttributeProperty property) {
            return true;
          }
        });
  }
Пример #8
0
  /**
   * Returns a DataRow reflecting current, possibly uncommitted, object state.
   *
   * <p><strong>Warning:</strong> This method will return a partial snapshot if an object or one of
   * its related objects that propagate their keys to this object have temporary ids. DO NOT USE
   * this method if you expect a DataRow to represent a complete object state.
   *
   * @since 1.1
   */
  public DataRow currentSnapshot(final Persistent object) {

    // for a HOLLOW object return snapshot from cache
    if (object.getPersistenceState() == PersistenceState.HOLLOW
        && object.getObjectContext() != null) {

      return getObjectStore().getSnapshot(object.getObjectId());
    }

    ObjEntity entity = getEntityResolver().getObjEntity(object);
    final ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName());
    final DataRow snapshot = new DataRow(10);
    snapshot.setEntityName(entity.getName());

    descriptor.visitProperties(
        new PropertyVisitor() {

          public boolean visitAttribute(AttributeProperty property) {
            ObjAttribute objAttr = property.getAttribute();

            // processing compound attributes correctly
            snapshot.put(objAttr.getDbAttributePath(), property.readPropertyDirectly(object));
            return true;
          }

          public boolean visitToMany(ToManyProperty property) {
            // do nothing
            return true;
          }

          public boolean visitToOne(ToOneProperty property) {
            ObjRelationship rel = property.getRelationship();

            // if target doesn't propagates its key value, skip it
            if (rel.isSourceIndependentFromTargetChange()) {
              return true;
            }

            Object targetObject = property.readPropertyDirectly(object);
            if (targetObject == null) {
              return true;
            }

            // if target is Fault, get id attributes from stored snapshot
            // to avoid unneeded fault triggering
            if (targetObject instanceof Fault) {
              DataRow storedSnapshot = getObjectStore().getSnapshot(object.getObjectId());
              if (storedSnapshot == null) {
                throw new CayenneRuntimeException(
                    "No matching objects found for ObjectId "
                        + object.getObjectId()
                        + ". Object may have been deleted externally.");
              }

              DbRelationship dbRel = rel.getDbRelationships().get(0);
              for (DbJoin join : dbRel.getJoins()) {
                String key = join.getSourceName();
                snapshot.put(key, storedSnapshot.get(key));
              }

              return true;
            }

            // target is resolved and we have an FK->PK to it,
            // so extract it from target...
            Persistent target = (Persistent) targetObject;
            Map<String, Object> idParts = target.getObjectId().getIdSnapshot();

            // this may happen in uncommitted objects - see the warning in
            // the JavaDoc
            // of
            // this method.
            if (idParts.isEmpty()) {
              return true;
            }

            DbRelationship dbRel = rel.getDbRelationships().get(0);
            Map<String, Object> fk = dbRel.srcFkSnapshotWithTargetSnapshot(idParts);
            snapshot.putAll(fk);
            return true;
          }
        });

    // process object id map
    // we should ignore any object id values if a corresponding attribute
    // is a part of relationship "toMasterPK", since those values have been
    // set above when db relationships where processed.
    Map<String, Object> thisIdParts = object.getObjectId().getIdSnapshot();
    if (thisIdParts != null) {

      // put only those that do not exist in the map
      for (Map.Entry<String, Object> entry : thisIdParts.entrySet()) {
        String nextKey = entry.getKey();
        if (!snapshot.containsKey(nextKey)) {
          snapshot.put(nextKey, entry.getValue());
        }
      }
    }

    return snapshot;
  }
Пример #9
0
  @Override
  public void prepareForAccess(Persistent object, String property, boolean lazyFaulting) {
    if (object.getPersistenceState() == PersistenceState.HOLLOW) {

      ObjectId oid = object.getObjectId();
      List<?> objects = performQuery(new ObjectIdQuery(oid, false, ObjectIdQuery.CACHE));

      if (objects.size() == 0) {
        throw new FaultFailureException(
            "Error resolving fault, no matching row exists in the database for ObjectId: " + oid);
      } else if (objects.size() > 1) {
        throw new FaultFailureException(
            "Error resolving fault, more than one row exists in the database for ObjectId: " + oid);
      }

      // 5/28/2013 - Commented out this block to allow for modifying
      // objects in the postLoad callback
      // sanity check...
      // if (object.getPersistenceState() != PersistenceState.COMMITTED) {
      //
      // String state =
      // PersistenceState.persistenceStateName(object.getPersistenceState());
      //
      // // TODO: andrus 4/13/2006, modified and deleted states are
      // // possible due to
      // // a race condition, should we handle them here?
      // throw new
      // FaultFailureException("Error resolving fault for ObjectId: " +
      // oid + " and state (" + state
      // +
      // "). Possible cause - matching row is missing from the database.");
      // }
    }

    // resolve relationship fault
    if (lazyFaulting && property != null) {
      ClassDescriptor classDescriptor =
          getEntityResolver().getClassDescriptor(object.getObjectId().getEntityName());
      PropertyDescriptor propertyDescriptor = classDescriptor.getProperty(property);

      // If we don't have a property descriptor, there's not much we can
      // do.
      // Let the caller know that the specified property could not be
      // found and list
      // all of the properties that could be so the caller knows what can
      // be used.
      if (propertyDescriptor == null) {
        final StringBuilder errorMessage = new StringBuilder();

        errorMessage.append(
            String.format(
                "Property '%s' is not declared for entity '%s'.",
                property, object.getObjectId().getEntityName()));

        errorMessage.append(" Declared properties are: ");

        // Grab each of the declared properties.
        final List<String> properties = new ArrayList<String>();
        classDescriptor.visitProperties(
            new PropertyVisitor() {
              @Override
              public boolean visitAttribute(final AttributeProperty property) {
                properties.add(property.getName());

                return true;
              }

              @Override
              public boolean visitToOne(final ToOneProperty property) {
                properties.add(property.getName());

                return true;
              }

              @Override
              public boolean visitToMany(final ToManyProperty property) {
                properties.add(property.getName());

                return true;
              }
            });

        // Now add the declared property names to the error message.
        boolean first = true;
        for (String declaredProperty : properties) {
          if (first) {
            errorMessage.append(String.format("'%s'", declaredProperty));

            first = false;
          } else {
            errorMessage.append(String.format(", '%s'", declaredProperty));
          }
        }

        errorMessage.append(".");

        throw new CayenneRuntimeException(errorMessage.toString());
      }

      // this should trigger fault resolving
      propertyDescriptor.readProperty(object);
    }
  }