You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2020/07/09 14:25:52 UTC
[isis] branch master updated: ISIS-2340: smoketests: monkey patch
for EnhancementHelper
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new f647719 ISIS-2340: smoketests: monkey patch for EnhancementHelper
f647719 is described below
commit f6477199d58748cb734e4c91d4dab2ff40ef3a6a
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Jul 9 16:25:03 2020 +0200
ISIS-2340: smoketests: monkey patch for EnhancementHelper
fixes glitches with consecutive test runs
---
.../isis/testdomain/jdo/entities/JdoBook.java | 2 +-
.../datanucleus/enhancer/EnhancementHelper.java | 308 +++++++++++++++++++++
2 files changed, 309 insertions(+), 1 deletion(-)
diff --git a/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/jdo/entities/JdoBook.java b/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/jdo/entities/JdoBook.java
index 533c94f..d6681dc 100644
--- a/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/jdo/entities/JdoBook.java
+++ b/examples/smoketests/stable/src/main/java/org/apache/isis/testdomain/jdo/entities/JdoBook.java
@@ -47,7 +47,7 @@ import lombok.extern.log4j.Log4j2;
objectType = "testdomain.jdo.Book",
publishing=Publishing.ENABLED,
auditing = Auditing.ENABLED)
-@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@NoArgsConstructor(access = AccessLevel.PUBLIC)
@ToString(callSuper = true)
@Log4j2
public class JdoBook extends JdoProduct {
diff --git a/examples/smoketests/stable/src/test/java/org/datanucleus/enhancer/EnhancementHelper.java b/examples/smoketests/stable/src/test/java/org/datanucleus/enhancer/EnhancementHelper.java
new file mode 100644
index 0000000..2f521ee
--- /dev/null
+++ b/examples/smoketests/stable/src/test/java/org/datanucleus/enhancer/EnhancementHelper.java
@@ -0,0 +1,308 @@
+/**********************************************************************
+Copyright (c) 2014 Andy Jefferson and others. All rights reserved.
+Licensed 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
+
+ http://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.
+
+Contributors:
+ ...
+**********************************************************************/
+package org.datanucleus.enhancer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EventListener;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.datanucleus.enhancement.Persistable;
+import org.datanucleus.enhancement.StateManager;
+import org.datanucleus.exceptions.NucleusUserException;
+
+/**
+ * Helper class for the DN bytecode enhancement contract.
+ * It contains methods to register metadata for persistable classes and to perform common operations needed by implementations, not by end users.
+ * <P>
+ * It allows construction of instances of persistable classes without using reflection.
+ * <P>
+ * Persistable classes register themselves via a static method at class load time. There is no security restriction on this access.
+ *
+ * TODO Provide a mechanism to automatically deregister classes when their class loader exits? or register against the NucleusContext?
+ */
+public class EnhancementHelper extends java.lang.Object
+{
+ private static EnhancementHelper singletonHelper = new EnhancementHelper();
+
+ /**
+ * Static mapping of <code>Persistable</code> class to an instance of that class.
+ * New entries are added by the static method in each <code>Persistable</code> class initialisation. Entries are never removed.
+ */
+ private static Map<Class, Meta> registeredClasses = new ConcurrentHashMap<>();
+
+ /**
+ * This list contains the registered listeners for <code>RegisterClassEvent</code>s.
+ */
+ private static final List<RegisterClassListener> listeners = new ArrayList<RegisterClassListener>();
+
+ private EnhancementHelper()
+ {
+ }
+
+ public static EnhancementHelper getInstance()/* throws SecurityException*/
+ {
+ /*SecurityManager sec = System.getSecurityManager();
+ if (sec != null)
+ {
+ // throws exception if caller is not authorized
+ sec.checkPermission(JDOPermission.GET_METADATA);
+ }*/
+ return singletonHelper;
+ }
+
+ /**
+ * Create a new instance of the class and assign its StateManager.
+ * @see Persistable#dnNewInstance(StateManager sm)
+ * @param pcClass the <code>Persistable</code> class.
+ * @param sm the <code>StateManager</code> which will own the new instance.
+ * @return the new instance, or <code>null</code> if the class is not registered.
+ */
+ public Persistable newInstance(Class pcClass, StateManager sm)
+ {
+ Persistable pc = getPersistableForClass(pcClass);
+ return pc == null ? null : pc.dnNewInstance(sm);
+ }
+
+ /**
+ * Create a new instance of the class and assign its StateManager and key values from the ObjectId. If the oid parameter is <code>null</code>, no key values are copied.
+ * The new instance has its <code>dnFlags</code> set to <code>LOAD_REQUIRED</code>.
+ * @see Persistable#dnNewInstance(StateManager sm, Object oid)
+ * @param pcClass the <code>Persistable</code> class.
+ * @param sm the <code>StateManager</code> which will own the new instance.
+ * @return the new instance, or <code>null</code> if the class is not registered.
+ * @param oid the ObjectId instance from which to copy key field values.
+ */
+ public Persistable newInstance(Class pcClass, StateManager sm, Object oid)
+ {
+ Persistable pc = getPersistableForClass(pcClass);
+ return pc == null ? null : pc.dnNewInstance(sm, oid);
+ }
+
+ /**
+ * Create a new instance of the ObjectId class of this <code>Persistable</code> class.
+ * It is intended only for application identity.
+ * This method should not be called for classes that use single field identity; newObjectIdInstance(Class, Object) should be used instead.
+ * If the class has been enhanced for datastore identity, or if the class is abstract, null is returned.
+ * @param pcClass the <code>Persistable</code> class.
+ * @return the new ObjectId instance, or <code>null</code> if the class is not registered.
+ */
+ public Object newObjectIdInstance(Class pcClass)
+ {
+ Persistable pc = getPersistableForClass(pcClass);
+ return pc == null ? null : pc.dnNewObjectIdInstance();
+ }
+
+ /**
+ * Create a new instance of the class used by the parameter Class for JDO identity, using the key constructor of the object id class.
+ * It is intended for single field identity.
+ * The identity instance returned has no relationship with the values of the primary key fields of the persistence-capable instance on which the method is called.
+ * If the key is the wrong class for the object id class, null is returned.
+ * <P>
+ * For classes that use single field identity, if the parameter is of one of the following types, the behavior must be as specified:
+ * <ul>
+ * <li><code>Number</code> or <code>Character</code>: the parameter must be the single field type or the
+ * wrapper class of the primitive field type; the parameter is passed to the single field identity constructor</li>
+ * <li><code>ObjectIdFieldSupplier</code>: the field value is fetched from the
+ * <code>ObjectIdFieldSupplier</code> and passed to the single field identity constructor</li>
+ * <li><code>String</code>: the String is passed to the single field identity constructor</li>
+ * </ul>
+ * @return the new ObjectId instance, or <code>null</code> if the class is not registered.
+ * @param obj the <code>Object</code> form of the object id
+ * @param pcClass the <code>Persistable</code> class.
+ */
+ public Object newObjectIdInstance(Class pcClass, Object obj)
+ {
+ Persistable pc = getPersistableForClass(pcClass);
+ return (pc == null) ? null : pc.dnNewObjectIdInstance(obj);
+ }
+
+ public static interface RegisterClassListener extends EventListener
+ {
+ /**
+ * This method gets called when a Persistable class is registered.
+ * @param registeredClass The class that has just been initialised/registered
+ */
+ public void registerClass(Class registeredClass);
+ }
+
+ /**
+ * Register metadata by class.
+ * This is called by the enhanced constructor of the <code>Persistable</code> class.
+ * @param pcClass the <code>Persistable</code> class used as the key for lookup.
+ * @param pc an instance of the <code>Persistable</code> class. TODO We can just do pcClass.newInstance() to get one!
+ */
+ public static void registerClass(Class pcClass, Persistable pc)
+ {
+ if (pcClass == null)
+ {
+ throw new NullPointerException("Attempt to register class with null class type");
+ }
+
+ registeredClasses.put(pcClass, new Meta(pc));
+
+ // Notify all listeners
+ synchronized (listeners)
+ {
+ if (!listeners.isEmpty())
+ {
+ for (Iterator i = listeners.iterator(); i.hasNext();)
+ {
+ RegisterClassListener crl = (RegisterClassListener) i.next();
+ if (crl != null)
+ {
+ crl.registerClass(pcClass);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Unregister all classes for the specified class loader.
+ * @param cl ClassLoader
+ */
+ public void unregisterClasses (ClassLoader cl)
+ {
+ synchronized(registeredClasses)
+ {
+ for (Iterator i = registeredClasses.keySet().iterator(); i.hasNext();)
+ {
+ Class pcClass = (Class)i.next();
+ // Note, the pc class was registered by calling the static method EnhancementHelper.registerClass.
+ // This means the EnhancementHelper class loader is the same as or an ancestor of the class loader of the pc class.
+ if ((pcClass != null) && (pcClass.getClassLoader() == cl))
+ {
+ // unregister pc class, if its class loader is the specified one.
+ i.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Add the specified <code>RegisterClassListener</code> to the listener list.
+ * @param crl the listener to be added
+ */
+ public void addRegisterClassListener(RegisterClassListener crl)
+ {
+ Set<Class> alreadyRegisteredClasses = null;
+ synchronized (listeners)
+ {
+ listeners.add(crl);
+
+ // Make a copy of the existing set of registered classes.
+ // Between these two lines of code, any number of new class registrations might occur, and will then all wait until this synchronized block completes.
+ // Some of the class registrations might be delivered twice to the newly registered listener.
+ alreadyRegisteredClasses = new HashSet<Class>(registeredClasses.keySet());
+ }
+
+ // new registrations will call the new listener while the following occurs notify the new listener about already-registered classes
+ for (Class pcClass : alreadyRegisteredClasses)
+ {
+ crl.registerClass(pcClass);
+ }
+ }
+
+ /**
+ * Remove the specified <code>RegisterClassListener</code> from the listener list.
+ * @param crl the listener to be removed
+ */
+ public void removeRegisterClassListener(RegisterClassListener crl)
+ {
+ synchronized (listeners)
+ {
+ listeners.remove(crl);
+ }
+ }
+
+ /**
+ * Returns a collection of class objects of the registered persistable classes.
+ * @return registered persistable classes
+ */
+ public Collection<Class> getRegisteredClasses()
+ {
+ return Collections.unmodifiableCollection(registeredClasses.keySet());
+ }
+
+ /**
+ * Look up the instance for a Persistable class.
+ * @param pcClass the <code>Class</code>.
+ * @return the Persistable instance for the <code>Class</code>.
+ */
+ private static Persistable getPersistableForClass(Class pcClass)
+ {
+ Meta ret = registeredClasses.get(pcClass);
+ if (ret == null)
+ {
+ // Commented out code that we could enable, particularly if we update registerClass to not pass an instance
+ if (Persistable.class.isAssignableFrom(pcClass))
+ {
+ System.err.println(String.format("MONKEY PATCH INTERCEPTING %s", pcClass));
+ // Not yet registered but this is Persistable, so create an instance
+ Persistable pc;
+ try
+ {
+ pc = (Persistable) pcClass.newInstance();
+ registeredClasses.put(pcClass, new Meta(pc));
+ return pc;
+ }
+ catch (InstantiationException | IllegalAccessException e)
+ {
+ System.err.println(e.getMessage());
+ }
+ }
+ throw new NucleusUserException("Cannot lookup meta info for " + pcClass + " - nothing found").setFatal();
+ }
+ return ret.getPC();
+ }
+
+ /**
+ * Helper class to manage persistable classes.
+ */
+ static class Meta
+ {
+ /** Instance of <code>Persistable</code>, used at runtime to create new instances. */
+ Persistable pc;
+
+ /**
+ * Construct an instance of <code>Meta</code>.
+ * @param pc An instance of the <code>Persistable</code> class
+ */
+ Meta(Persistable pc)
+ {
+ this.pc = pc;
+ }
+
+ Persistable getPC()
+ {
+ return pc;
+ }
+
+ public String toString()
+ {
+ return "Meta-" + pc.getClass().getName();
+ }
+ }
+}
\ No newline at end of file