This is an automated email from the ASF dual-hosted git repository.
ntimofeev pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git
The following commit(s) were added to refs/heads/master by this push:
new 4c6c726b1 CAY-2830 Cleanup DataContext - extract couple utility
classes - more comment cleanup
4c6c726b1 is described below
commit 4c6c726b17c8ede1760282d83891f515750402a2
Author: stariy95 <[email protected]>
AuthorDate: Wed Dec 6 12:39:33 2023 +0400
CAY-2830 Cleanup DataContext
- extract couple utility classes
- more comment cleanup
---
.../access/CollectingNamePropertyVisitor.java | 60 ++++++
.../org/apache/cayenne/access/DataContext.java | 228 ++------------------
.../cayenne/access/DataContextObjectCreator.java | 229 +++++++++++++++++++++
3 files changed, 302 insertions(+), 215 deletions(-)
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/CollectingNamePropertyVisitor.java
b/cayenne/src/main/java/org/apache/cayenne/access/CollectingNamePropertyVisitor.java
new file mode 100644
index 000000000..8137fe774
--- /dev/null
+++
b/cayenne/src/main/java/org/apache/cayenne/access/CollectingNamePropertyVisitor.java
@@ -0,0 +1,60 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.access;
+
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class CollectingNamePropertyVisitor implements PropertyVisitor {
+ private final List<String> properties;
+
+ CollectingNamePropertyVisitor() {
+ this.properties = new ArrayList<>();
+ }
+
+ @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;
+ }
+
+ List<String> allProperties(ClassDescriptor classDescriptor) {
+ classDescriptor.visitProperties(this);
+ return properties;
+ }
+}
diff --git a/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
b/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
index cb57e0f9e..4faf2ef52 100644
--- a/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
+++ b/cayenne/src/main/java/org/apache/cayenne/access/DataContext.java
@@ -49,23 +49,16 @@ import org.apache.cayenne.cache.NestedQueryCache;
import org.apache.cayenne.cache.QueryCache;
import org.apache.cayenne.di.Injector;
import org.apache.cayenne.event.EventManager;
-import org.apache.cayenne.exp.ValueInjector;
-import org.apache.cayenne.graph.ArcId;
import org.apache.cayenne.graph.ChildDiffLoader;
import org.apache.cayenne.graph.CompoundDiff;
import org.apache.cayenne.graph.GraphDiff;
import org.apache.cayenne.graph.GraphEvent;
import org.apache.cayenne.graph.GraphManager;
import org.apache.cayenne.map.EntityResolver;
-import org.apache.cayenne.map.LifecycleEvent;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.query.*;
-import org.apache.cayenne.reflect.AttributeProperty;
import org.apache.cayenne.reflect.ClassDescriptor;
import org.apache.cayenne.reflect.PropertyDescriptor;
-import org.apache.cayenne.reflect.PropertyVisitor;
-import org.apache.cayenne.reflect.ToManyProperty;
-import org.apache.cayenne.reflect.ToOneProperty;
import org.apache.cayenne.runtime.CayenneRuntime;
import org.apache.cayenne.tx.TransactionFactory;
import org.apache.cayenne.util.EventUtil;
@@ -152,6 +145,8 @@ public class DataContext implements ObjectContext {
protected boolean validatingObjectsOnCommit = true;
+ protected final DataContextObjectCreator objectCreator;
+
/**
* Creates a new DataContext that is not attached to the Cayenne stack.
*/
@@ -167,6 +162,7 @@ public class DataContext implements ObjectContext {
public DataContext(DataChannel channel, ObjectStore objectStore) {
graphAction = new ObjectContextGraphAction(this);
+ objectCreator = new DataContextObjectCreator(this);
// inject self as parent context
if (objectStore != null) {
@@ -566,10 +562,8 @@ public class DataContext implements ObjectContext {
@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);
@@ -577,16 +571,7 @@ public class DataContext implements ObjectContext {
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());
- // // 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.");
- // }
+ // here once was a sanity check for the COMMITTED state, that was
faulty due to the race condition
}
// resolve relationship fault
@@ -597,55 +582,13 @@ public class DataContext implements ObjectContext {
// 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.
+ // all 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<>();
- 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());
+ List<String> properties = new
CollectingNamePropertyVisitor().allProperties(classDescriptor);
+ String errorMessage = String.format("Property '%s' is not
declared for entity '%s'.",
+ property, object.getObjectId().getEntityName()) +
+ " Declared properties are: '" + String.join("', '",
properties) + "'.";
+ throw new CayenneRuntimeException(errorMessage);
}
// this should trigger fault resolving
@@ -712,16 +655,7 @@ public class DataContext implements ObjectContext {
*/
@Override
public <T> T newObject(Class<T> persistentClass) {
- if (persistentClass == null) {
- throw new NullPointerException("Null 'persistentClass'");
- }
-
- ObjEntity entity = getEntityResolver().getObjEntity(persistentClass);
- if (entity == null) {
- throw new IllegalArgumentException("Class is not mapped with
Cayenne: " + persistentClass.getName());
- }
-
- return (T) newObject(entity.getName());
+ return objectCreator.newObject(persistentClass);
}
/**
@@ -736,27 +670,7 @@ public class DataContext implements ObjectContext {
* @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);
-
- // NOTE: the order of initialization of persistence artifacts below is
important - do not change it lightly
- object.setObjectId(ObjectId.of(entityName));
-
- injectInitialValue(object);
-
- return object;
+ return objectCreator.newObject(entityName);
}
/**
@@ -771,123 +685,7 @@ public class DataContext implements ObjectContext {
*/
@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(ObjectId.of(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);
- @SuppressWarnings("unchecked")
- Collection<Map.Entry<?,?>> collection = (value instanceof
Map)
- ? ((Map) value).entrySet()
- : (Collection<Map.Entry<?, ?>>) value;
-
- for (Object target : collection) {
- if (target instanceof Persistent) {
- Persistent targetDO = (Persistent) target;
-
- // make sure it is registered
- registerNewObject(targetDO);
-
getObjectStore().arcCreated(persistent.getObjectId(), targetDO.getObjectId(),
new ArcId(property));
- }
- }
- }
- 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(), new ArcId(property));
- }
- return true;
- }
-
- public boolean visitAttribute(AttributeProperty property) {
- return true;
- }
- });
- }
-
- /**
- * If ObjEntity qualifier is set, asks it to inject initial value to an
object.
- * Also performs all Persistent initialization operations
- */
- protected void injectInitialValue(Object obj) {
- // must follow this exact order of property initialization per CAY-653,
- // i.e. have
- // the id and the context in place BEFORE setPersistence is called
-
- Persistent object = (Persistent) obj;
-
- object.setObjectContext(this);
- object.setPersistenceState(PersistenceState.NEW);
-
- GraphManager graphManager = getGraphManager();
- synchronized (graphManager) {
- graphManager.registerNode(object.getObjectId(), object);
- graphManager.nodeCreated(object.getObjectId());
- }
-
- ObjEntity entity;
- try {
- entity = getEntityResolver().getObjEntity(object.getClass());
- } catch (CayenneRuntimeException ex) {
- // ObjEntity cannot be fetched, ignored
- entity = null;
- }
-
- if (entity != null) {
- if (entity.getDeclaredQualifier() instanceof ValueInjector) {
- ((ValueInjector)
entity.getDeclaredQualifier()).injectValue(object);
- }
- }
-
- // invoke callbacks
-
getEntityResolver().getCallbackRegistry().performCallbacks(LifecycleEvent.POST_ADD,
object);
+ objectCreator.registerNewObject(object);
}
/**
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/DataContextObjectCreator.java
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextObjectCreator.java
new file mode 100644
index 000000000..0c3ec5af6
--- /dev/null
+++
b/cayenne/src/main/java/org/apache/cayenne/access/DataContextObjectCreator.java
@@ -0,0 +1,229 @@
+/*****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.access;
+
+import org.apache.cayenne.CayenneRuntimeException;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.PersistenceState;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.exp.ValueInjector;
+import org.apache.cayenne.graph.ArcId;
+import org.apache.cayenne.graph.GraphManager;
+import org.apache.cayenne.map.LifecycleEvent;
+import org.apache.cayenne.map.ObjEntity;
+import org.apache.cayenne.reflect.AttributeProperty;
+import org.apache.cayenne.reflect.ClassDescriptor;
+import org.apache.cayenne.reflect.PropertyVisitor;
+import org.apache.cayenne.reflect.ToManyProperty;
+import org.apache.cayenne.reflect.ToOneProperty;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * {@link DataContext} delegates creation and registration of new objects to
this class
+ */
+class DataContextObjectCreator {
+
+ private final DataContext context;
+
+ DataContextObjectCreator(DataContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Create new object for the given persistent class
+ *
+ * @param persistentClass to create object from
+ * @return a new persistent object
+ * @param <T> type of the object
+ * @see DataContext#newObject(Class)
+ */
+ <T> T newObject(Class<T> persistentClass) {
+ if (persistentClass == null) {
+ throw new NullPointerException("Null 'persistentClass'");
+ }
+
+ ObjEntity entity =
context.getEntityResolver().getObjEntity(persistentClass);
+ if (entity == null) {
+ throw new IllegalArgumentException("Class is not mapped with
Cayenne: " + persistentClass.getName());
+ }
+
+ @SuppressWarnings("unchecked")
+ T object = (T) newObject(entity.getName());
+ return object;
+ }
+
+ /**
+ * Create new object for the given entity name
+ * @param entityName name of the ObjEntity
+ * @return a new persistent object
+ * @see DataContext#newObject(String)
+ */
+ Persistent newObject(String entityName) {
+ ClassDescriptor descriptor =
context.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);
+
+ // NOTE: the order of initialization of persistence artifacts below is
important - do not change it lightly
+ object.setObjectId(ObjectId.of(entityName));
+
+ injectInitialValue(object);
+
+ return object;
+ }
+
+ /**
+ * Register new object created outside the context
+ * @param object to register
+ * @see DataContext#registerNewObject(Object)
+ */
+ void registerNewObject(Object object) {
+ if (object == null) {
+ throw new NullPointerException("Can't register null object.");
+ }
+
+ ObjEntity entity =
context.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() == context) {
+ // 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(ObjectId.of(entity.getName()));
+ }
+
+ ClassDescriptor descriptor =
context.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);
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ Collection<Map.Entry<?,?>> collection = (value instanceof
Map)
+ ? ((Map) value).entrySet()
+ : (Collection<Map.Entry<?, ?>>) value;
+
+ for (Object target : collection) {
+ if (target instanceof Persistent) {
+ Persistent targetDO = (Persistent) target;
+
+ // make sure it is registered
+ registerNewObject(targetDO);
+
context.getObjectStore().arcCreated(persistent.getObjectId(),
targetDO.getObjectId(), new ArcId(property));
+ }
+ }
+ }
+ 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);
+
context.getObjectStore().arcCreated(persistent.getObjectId(),
targetDO.getObjectId(), new ArcId(property));
+ }
+ return true;
+ }
+
+ public boolean visitAttribute(AttributeProperty property) {
+ return true;
+ }
+ });
+ }
+
+ /**
+ * If ObjEntity qualifier is set, asks it to inject initial value to an
object.
+ * Also performs all Persistent initialization operations
+ */
+ @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
+ protected void injectInitialValue(Object obj) {
+ // must follow this exact order of property initialization per CAY-653,
+ // i.e. have the id and the context in place BEFORE setPersistence is
called
+
+ Persistent object = (Persistent) obj;
+
+ object.setObjectContext(context);
+ object.setPersistenceState(PersistenceState.NEW);
+
+ GraphManager graphManager = context.getGraphManager();
+ synchronized (graphManager) {
+ graphManager.registerNode(object.getObjectId(), object);
+ graphManager.nodeCreated(object.getObjectId());
+ }
+
+ ObjEntity entity;
+ try {
+ entity =
context.getEntityResolver().getObjEntity(object.getClass());
+ } catch (CayenneRuntimeException ex) {
+ // ObjEntity cannot be fetched, ignored
+ entity = null;
+ }
+
+ if (entity != null) {
+ if (entity.getDeclaredQualifier() instanceof ValueInjector) {
+ ((ValueInjector)
entity.getDeclaredQualifier()).injectValue(object);
+ }
+ }
+
+ // invoke callbacks
+ context.getEntityResolver()
+
.getCallbackRegistry().performCallbacks(LifecycleEvent.POST_ADD, object);
+ }
+}