summaryrefslogtreecommitdiffstats
path: root/feature-server-pool/src/test/java/org/onap/policy/drools/serverpooltest/Adapter.java
diff options
context:
space:
mode:
Diffstat (limited to 'feature-server-pool/src/test/java/org/onap/policy/drools/serverpooltest/Adapter.java')
-rw-r--r--feature-server-pool/src/test/java/org/onap/policy/drools/serverpooltest/Adapter.java353
1 files changed, 353 insertions, 0 deletions
diff --git a/feature-server-pool/src/test/java/org/onap/policy/drools/serverpooltest/Adapter.java b/feature-server-pool/src/test/java/org/onap/policy/drools/serverpooltest/Adapter.java
new file mode 100644
index 00000000..03b970b5
--- /dev/null
+++ b/feature-server-pool/src/test/java/org/onap/policy/drools/serverpooltest/Adapter.java
@@ -0,0 +1,353 @@
+/*
+ * ============LICENSE_START=======================================================
+ * feature-server-pool
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.serverpooltest;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.io.PrintStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.kie.api.runtime.KieSession;
+import org.onap.policy.common.utils.network.NetworkUtil;
+import org.onap.policy.drools.serverpool.Util;
+
+/**
+ * This is a common base class for 6 'AdapterImpl' instances, all running
+ * with their own copies of the server pool classes, and a number of the ONAP
+ * classes. The purpose is to simulate 6 separate hosts in a server pool.
+ * Note that there is potentially a 7th copy of any of these classes, which is
+ * the one loaded with the system class loader. Ideally, those classes
+ * shouldn't be referred to, but there were some problems during testing,
+ * where they unexpectedly were (prompting a change in
+ * 'ExtendedObjectInputStream'). This is referred to as the 'null' host,
+ * where the classes may exist, but have not gone through initialization.
+ */
+public abstract class Adapter {
+ // 'true' indicates that initialization is still needed
+ private static boolean initNeeded = true;
+
+ // Each 'Adapter' instance is implemented by 'AdapterImpl', but loaded
+ // with a different class loader that provides each with a different copy
+ // of the set of classes with packages in the list below (see references to
+ // 'BlockingClassLoader').
+ public static Adapter[] adapters = new Adapter[6];
+
+ /**
+ * Ensure that all adapters have been initialized.
+ */
+ public static void ensureInit() throws Exception {
+ synchronized (Adapter.class) {
+ if (initNeeded) {
+ initNeeded = false;
+
+ // start DMAAP Simulator
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ SimDmaap.start();
+ }
+ }, "DMAAP Simulator").start();
+
+ // wait 1 second to allow time for the port 3904 listener
+ assertTrue(NetworkUtil.isTcpPortOpen(SimDmaap.HOSTNAME, 3904, 50, 1000));
+
+ // build 'BlockingClassLoader'
+ BlockingClassLoader bcl = new BlockingClassLoader(
+ Adapter.class.getClassLoader(),
+ // All 'org.onap.policy.*' classes are adapter-specific, except
+ // for the exclusions listed below.
+ "org.onap.policy.*"
+ );
+ bcl.addExclude("org.onap.policy.drools.core.DroolsRunnable");
+ bcl.addExclude("org.onap.policy.drools.serverpooltest.*");
+
+ // build URL list for class loader
+ URL[] urls = {};
+
+ // iterate through 'adapter' entries
+ ClassLoader saveClassLoader =
+ Thread.currentThread().getContextClassLoader();
+ if (saveClassLoader instanceof URLClassLoader) {
+ urls = ((URLClassLoader)saveClassLoader).getURLs();
+ } else {
+ // the parent is not a 'URLClassLoader' --
+ // try to get this information from 'java.class.path'
+ ArrayList<URL> tmpUrls = new ArrayList<>();
+ for (String entry : System.getProperty("java.class.path").split(
+ File.pathSeparator)) {
+ if (new File(entry).isDirectory()) {
+ tmpUrls.add(new URL("file:" + entry + "/"));
+ } else {
+ tmpUrls.add(new URL("file:" + entry));
+ }
+ }
+ urls = tmpUrls.toArray(new URL[0]);
+ }
+ try {
+ for (int i = 0 ; i < adapters.length ; i += 1) {
+ // Build a new 'ClassLoader' for this adapter. The
+ // 'ClassLoader' hierarchy is:
+ //
+ // AdapterClassLoader(one copy per Adapter) ->
+ // BlockingClassLoader ->
+ // base ClassLoader (with the complete URL list)
+ ClassLoader classLoader =
+ new AdapterClassLoader(i, urls, bcl);
+
+ // set the current thread class loader, which should be
+ // inherited by any child threads created during
+ // the initialization of the adapter
+ Thread.currentThread().setContextClassLoader(classLoader);
+
+ // now, build the adapter -- it is not just a new instance,
+ // but a new copy of class 'AdapterImpl'
+ Adapter adapter = (Adapter) classLoader.loadClass(
+ "org.onap.policy.drools.serverpool.AdapterImpl")
+ .newInstance();
+
+ // initialize the adapter
+ adapter.init(i);
+ adapters[i] = adapter;
+ }
+ } finally {
+ // restore the class loader to that used during the Junit tests
+ Thread.currentThread().setContextClassLoader(saveClassLoader);
+ }
+ }
+ }
+ }
+
+ /**
+ * Shut everything down.
+ */
+ public static void ensureShutdown() {
+ for (Adapter adapter : adapters) {
+ adapter.shutdown();
+ }
+ SimDmaap.stop();
+ // not sure why the following is started
+ Util.shutdown();
+ }
+
+ /**
+ * Runs server pool initialization for a particular host.
+ *
+ * @param index the index of the adapter (0-5)
+ */
+ public abstract void init(int index) throws Exception;
+
+ /**
+ * Shuts down the server pool for this host.
+ */
+ public abstract void shutdown();
+
+ /**
+ * Return a 'LinkedBlockingQueue' instance, which is used as a way for
+ * Drools code to signal back to running Junit tests.
+ *
+ * @return a 'LinkedBlockingQueue' instance, which is used as a way for
+ * Drools code to signal back to running Junit tests
+ */
+ public abstract LinkedBlockingQueue<String> notificationQueue();
+
+ /**
+ * This method blocks and waits for all buckets to have owners, or for
+ * a timeout, whichever occurs first.
+ *
+ * @param endTime the point at which timeout occurs
+ * @return 'true' if all buckets have owners, 'false' if a timeout occurred
+ */
+ public abstract boolean waitForInit(long endTime) throws InterruptedException;
+
+ /**
+ * Return an object providing indirect references to a select set of
+ * static 'Server' methods.
+ *
+ * @return an object providing indirect references to a select set of
+ * static 'Server' methods
+ */
+ public abstract ServerWrapper.Static getServerStatic();
+
+ /**
+ * Return an object providing an indirect reference to the lead 'Server'
+ * object.
+ *
+ * @return an object providing an indirect reference to the lead 'Server'
+ * object
+ */
+ public abstract ServerWrapper getLeader();
+
+ /**
+ * Return an object providing indirect references to a select set of
+ * static 'Bucket' methods.
+ *
+ * @return an object providing indirect references to a select set of
+ * static 'Bucket' methods
+ */
+ public abstract BucketWrapper.Static getBucketStatic();
+
+ /**
+ * Create a new 'TargetLock' instance, returning an indirect reference.
+ *
+ * @param key string key identifying the lock
+ * @param ownerKey string key identifying the owner, which must hash to
+ * a bucket owned by the current host (it is typically a 'RequestID')
+ * @param owner owner of the lock (will be notified when going from
+ * WAITING to ACTIVE)
+ * @param waitForLock this controls the behavior when 'key' is already
+ * locked - 'true' means wait for it to be freed, 'false' means fail
+ */
+ public abstract TargetLockWrapper newTargetLock(
+ String key, String ownerKey, TargetLockWrapper.Owner owner,
+ boolean waitForLock);
+
+ /**
+ * Create a new 'TargetLock' instance, returning an indirect reference.
+ *
+ * @param key string key identifying the lock
+ * @param ownerKey string key identifying the owner, which must hash to
+ * a bucket owned by the current host (it is typically a 'RequestID')
+ * @param owner owner of the lock (will be notified when going from
+ * WAITING to ACTIVE)
+ */
+ public abstract TargetLockWrapper newTargetLock(
+ String key, String ownerKey, TargetLockWrapper.Owner owner);
+
+ /**
+ * Call 'TargetLock.DumpLocks.dumpLocks'
+ *
+ * @param out where the output should be displayed
+ * @param detail 'true' provides additional bucket and host information
+ * (but abbreviates all UUIDs in order to avoid excessive
+ * line length)
+ */
+ public abstract void dumpLocks(PrintStream out, boolean detail);
+
+ /**
+ * Create and initialize PolicyController 'TestController', and start
+ * the associated Drools container and session.
+ *
+ * @return a string containing controller session information
+ */
+ public abstract String createController();
+
+ /**
+ * Send an event in the form of a JSON message string. The message is
+ * sent to JUNIT-TEST-TOPIC, and the JSON object is converted to a
+ * 'TestDroolsObject' (all compatible with the Drools session created by
+ * 'createController'.
+ *
+ * @param key determines the bucket number, which affects which host the
+ * message is eventually routed to
+ */
+ public abstract void sendEvent(String key);
+
+ /**
+ * Return the one-and-only 'KieSession' on this host.
+ *
+ * @return the one-and-only 'KieSession' on this host
+ */
+ public abstract KieSession getKieSession();
+
+ /**
+ * Insert an object into the one-and-only Drools session.
+ *
+ * @param object the object to insert
+ */
+ public abstract void insertDrools(Object object);
+
+ // some test utilities
+
+ /**
+ * Determine whether any of the objects passed as parameters are of a class
+ * that belongs to different adapter. Print messages are displayed
+ * for any that do occur.
+ *
+ * @param objects one or more objects to be tested
+ * @return 'true' if one or more are foreign
+ */
+ public abstract boolean isForeign(Object... objects);
+
+ /**
+ * This method is used to generate keys that hash to a bucket associated
+ * with a particular server. The algorithm generates a key using 'prefix'
+ * concatenated with a numeric value, and searches for the first one on
+ * the desired host. It will try up to 10000 indices before giving up --
+ * each host owns 1/6 of the buckets, should the 10000 number should be
+ * way more than enough. The tests are written with the assumption that
+ * a valid key will be returned, and 'NullPointerException' is an acceptable
+ * way to handle the situation if this doesn't work out somehow.
+ *
+ * @param prefix the first portion of the key
+ * @param startingIndex the first index to try
+ * @param host this indicates the 'Server' instance to locate, which must
+ * not be foreign to this adapter
+ * @return a key associated with 'host' ('null' if not found)
+ */
+ public abstract String findKey(String prefix, int startingIndex, ServerWrapper host);
+
+ /**
+ * Equivalent to 'findKey(prefix, startingIndex, THIS-SERVER)'.
+ *
+ * @param prefix the first portion of the key
+ * @param startingIndex the first index to try
+ * @return a key associated with 'host' ('null' if not found)
+ */
+ public abstract String findKey(String prefix, int startingIndex);
+
+ /**
+ * Equivalent to 'findKey(prefix, 1, THIS-SERVER)'.
+ *
+ * @param prefix the first portion of the key
+ * @return a key associated with 'host' ('null' if not found)
+ */
+ public abstract String findKey(String prefix);
+
+ /* ============================================================ */
+
+ /**
+ * This class is basically a 'URLClassLoader', but with a 'toString()'
+ * method that indicates the host and adapter number.
+ */
+ public static class AdapterClassLoader extends URLClassLoader {
+ private int index;
+
+ public AdapterClassLoader(int index, URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+ this.index = index;
+ }
+
+ @Override
+ public String toString() {
+ return "AdapterClassLoader(" + index + ")";
+ }
+ }
+}