diff options
20 files changed, 1569 insertions, 928 deletions
diff --git a/integrity-audit/src/main/java/org/onap/policy/common/ia/AuditThread.java b/integrity-audit/src/main/java/org/onap/policy/common/ia/AuditThread.java index f1839b12..58ed8b99 100644 --- a/integrity-audit/src/main/java/org/onap/policy/common/ia/AuditThread.java +++ b/integrity-audit/src/main/java/org/onap/policy/common/ia/AuditThread.java @@ -25,10 +25,7 @@ import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Properties; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; - import org.onap.policy.common.ia.jpa.IntegrityAuditEntity; import org.onap.policy.common.logging.eelf.MessageCodes; import org.onap.policy.common.logging.flexlogger.FlexLogger; @@ -108,18 +105,6 @@ public class AuditThread extends Thread { private IntegrityAudit integrityAudit; /** - * A latch is taken from this queue before starting an audit. May be {@code null}. Used by JUnit - * tests. - */ - private BlockingQueue<CountDownLatch> auditLatchQueue; - - /** - * Latch to be decremented when the next audit completes. May be {@code null}. Used by JUnit - * tests to wait for an audit to complete. - */ - private CountDownLatch auditCompletionLatch = null; - - /** * AuditThread constructor. * * @param resourceName the resource name @@ -133,7 +118,7 @@ public class AuditThread extends Thread { int integrityAuditPeriodSeconds, IntegrityAudit integrityAudit) throws IntegrityAuditException { this(resourceName, persistenceUnit, properties, TimeUnit.SECONDS.toMillis(integrityAuditPeriodSeconds), - integrityAudit, null); + integrityAudit); } /** @@ -148,13 +133,12 @@ public class AuditThread extends Thread { * @throws IntegrityAuditException if an error occurs */ public AuditThread(String resourceName, String persistenceUnit, Properties properties, long integrityAuditMillis, - IntegrityAudit integrityAudit, BlockingQueue<CountDownLatch> queue) throws IntegrityAuditException { + IntegrityAudit integrityAudit) throws IntegrityAuditException { this.resourceName = resourceName; this.persistenceUnit = persistenceUnit; this.properties = properties; this.integrityAuditPeriodMillis = integrityAuditMillis; this.integrityAudit = integrityAudit; - this.auditLatchQueue = queue; /* * The DbDAO Constructor registers this node in the IntegrityAuditEntity table. Each @@ -174,14 +158,8 @@ public class AuditThread extends Thread { logger.info("AuditThread.run: Entering"); try { - /* - * For JUnit testing: wait for the first latch, decrement it to indicate that the thread - * has started, and then wait for the next latch, before we actually start doing - * anything. These simply return if there is no latch queue defined. - */ - getNextLatch(); - decrementLatch(); - getNextLatch(); + // for junit testing + runStarted(); /* * Triggers change in designation, unless no other viable candidate. @@ -284,11 +262,8 @@ public class AuditThread extends Thread { * property, otherwise just sleep the normal interval. */ if (auditCompleted) { - // indicate that an audit has completed - decrementLatch(); - - // don't start the next audit cycle until a latch has been provided - getNextLatch(); + // for junit testing: indicate that an audit has completed + auditCompleted(); if (logger.isDebugEnabled()) { logger.debug("AuditThread.run: Audit completed; resourceName=" + this.resourceName @@ -342,29 +317,6 @@ public class AuditThread extends Thread { } /** - * Gets the next audit-completion latch from the queue. Blocks, if the queue is empty. - * - * @throws InterruptedException if interrupted while waiting - */ - private void getNextLatch() throws InterruptedException { - BlockingQueue<CountDownLatch> queue = this.auditLatchQueue; - if (queue != null) { - this.auditCompletionLatch = queue.take(); - } - } - - /** - * Decrements the current audit-completion latch, if any. - */ - private void decrementLatch() { - CountDownLatch latch = this.auditCompletionLatch; - if (latch != null) { - this.auditCompletionLatch = null; - latch.countDown(); - } - } - - /** * Determines if an exception is an InterruptedException or was caused by an * InterruptedException. * @@ -788,6 +740,26 @@ public class AuditThread extends Thread { } /** + * Indicates that the {@link #run()} method has started. This method simply returns, + * and may overridden by junit tests. + * + * @throws InterruptedException + */ + public void runStarted() throws InterruptedException { + // does nothing + } + + /** + * Indicates that an audit has completed. This method simply returns, and may + * overridden by junit tests. + * + * @throws InterruptedException + */ + public void auditCompleted() throws InterruptedException { + // does nothing + } + + /** * Adjusts the thread-sleep-interval to be used when an audit has <i>not</i> been completed. * Used by JUnit tests. * diff --git a/integrity-audit/src/main/java/org/onap/policy/common/ia/IntegrityAudit.java b/integrity-audit/src/main/java/org/onap/policy/common/ia/IntegrityAudit.java index 9abdbe52..b3330faf 100644 --- a/integrity-audit/src/main/java/org/onap/policy/common/ia/IntegrityAudit.java +++ b/integrity-audit/src/main/java/org/onap/policy/common/ia/IntegrityAudit.java @@ -21,9 +21,6 @@ package org.onap.policy.common.ia; import java.util.Properties; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; - import org.onap.policy.common.ia.IntegrityAuditProperties.NodeTypeEnum; import org.onap.policy.common.logging.flexlogger.FlexLogger; import org.onap.policy.common.logging.flexlogger.Logger; @@ -217,37 +214,20 @@ public class IntegrityAudit { * @throws IntegrityAuditException if an error occurs */ public void startAuditThread() throws IntegrityAuditException { - startAuditThread(null); - } - - /** - * Starts the audit thread. - * - * @param queue the queue - * @return {@code true} if the thread was started, {@code false} otherwise - * @throws IntegrityAuditException if an error occurs - */ - protected boolean startAuditThread(BlockingQueue<CountDownLatch> queue) throws IntegrityAuditException { - logger.info("startAuditThread: Entering"); - boolean success = false; - if (integrityAuditPeriodMillis >= 0) { - this.auditThread = new AuditThread(this.resourceName, this.persistenceUnit, this.properties, - integrityAuditPeriodMillis, this, queue); + this.auditThread = makeAuditThread(this.resourceName, this.persistenceUnit, this.properties, integrityAuditPeriodMillis); logger.info("startAuditThread: Audit started and will run every " + integrityAuditPeriodMillis / 1000 + " seconds"); this.auditThread.start(); - success = true; + } else { logger.info("startAuditThread: Suppressing integrity audit, integrityAuditPeriodSeconds=" + integrityAuditPeriodMillis / 1000); } logger.info("startAuditThread: Exiting"); - - return success; } /** @@ -299,4 +279,29 @@ public class IntegrityAudit { return !this.auditThread.isAlive(); } } + + /** + * + * @return {@code true} if an audit thread exists, {@code false} otherwise + */ + protected boolean haveAuditThread() { + return (this.auditThread != null); + } + + /** + * Creates an audit thread. May be overridden by junit tests. + * + * @param resourceName2 + * @param persistenceUnit2 + * @param properties2 + * @param integrityAuditPeriodMillis2 + * + * @return a new audit thread + * @throws IntegrityAuditException + */ + protected AuditThread makeAuditThread(String resourceName2, String persistenceUnit2, Properties properties2, + long integrityAuditPeriodMillis2) throws IntegrityAuditException { + + return new AuditThread(resourceName2, persistenceUnit2, properties2, integrityAuditPeriodMillis2, this); + } } diff --git a/integrity-audit/src/test/java/org/onap/policy/common/ia/IntegrityAuditTestBase.java b/integrity-audit/src/test/java/org/onap/policy/common/ia/IntegrityAuditTestBase.java index afbcc452..c9179908 100644 --- a/integrity-audit/src/test/java/org/onap/policy/common/ia/IntegrityAuditTestBase.java +++ b/integrity-audit/src/test/java/org/onap/policy/common/ia/IntegrityAuditTestBase.java @@ -22,10 +22,6 @@ package org.onap.policy.common.ia; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; - import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -34,21 +30,19 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Properties; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; - import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; - import org.onap.policy.common.utils.jpa.EntityMgrCloser; import org.onap.policy.common.utils.jpa.EntityMgrFactoryCloser; import org.onap.policy.common.utils.jpa.EntityTransCloser; import org.onap.policy.common.utils.test.log.logback.ExtractAppender; import org.slf4j.LoggerFactory; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; /** * All JUnits are designed to run in the local development environment where they have write @@ -464,26 +458,26 @@ public class IntegrityAuditTestBase { protected void runAudit(MyIntegrityAudit... auditors) throws InterruptedException { // start an audit cycle on each auditor - List<CountDownLatch> latches = new ArrayList<>(auditors.length); + List<Semaphore> semaphores = new ArrayList<>(auditors.length); for (MyIntegrityAudit p : auditors) { - latches.add(p.startAudit()); + semaphores.add(p.startAudit()); } // wait for each auditor to complete its cycle - for (CountDownLatch latch : latches) { - waitLatch(latch); + for (Semaphore sem : semaphores) { + waitSem(sem); } } /** - * Waits for a latch to reach zero. + * Waits for a semaphore to be released. * - * @param latch the latch to wait for + * @param sem the semaphore for which to wait * @throws InterruptedException if the thread is interrupted * @throws AssertionError if the latch did not reach zero in the allotted time */ - protected void waitLatch(CountDownLatch latch) throws InterruptedException { - assertTrue(latch.await(WAIT_MS, TimeUnit.SECONDS)); + protected void waitSem(Semaphore sem) throws InterruptedException { + assertTrue(sem.tryAcquire(WAIT_MS, TimeUnit.SECONDS)); } /** @@ -520,11 +514,16 @@ public class IntegrityAuditTestBase { * Manages audits by inserting latches into a queue for the AuditThread to count. */ protected class MyIntegrityAudit extends IntegrityAudit { - + + /** + * Semaphore on which the audit thread should wait. + */ + private Semaphore auditSem = null; + /** - * Queue from which the AuditThread will take latches. + * Semaphore on which the junit management thread should wait. */ - private BlockingQueue<CountDownLatch> queue = null; + private Semaphore junitSem = null; /** * Constructs an auditor and starts the AuditThread. @@ -550,16 +549,14 @@ public class IntegrityAuditTestBase { } /** - * Triggers an audit by adding a latch to the queue. + * Triggers an audit by releasing the audit thread's semaphore. * - * @return the latch that was added + * @return the semaphore on which to wait * @throws InterruptedException if the thread is interrupted */ - public CountDownLatch startAudit() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - queue.add(latch); - - return latch; + public Semaphore startAudit() throws InterruptedException { + auditSem.release(); + return junitSem; } /** @@ -567,25 +564,23 @@ public class IntegrityAuditTestBase { */ @Override public final void startAuditThread() throws IntegrityAuditException { - if (queue != null) { - // queue up a bogus latch, in case a thread is still running - queue.add(new CountDownLatch(1) { - @Override - public void countDown() { - throw new RuntimeException("auditor has multiple threads"); - } - }); + if (auditSem != null) { + // release a bunch of semaphores, in case a thread is still running + auditSem.release(1000); } + + auditSem = new Semaphore(0); + junitSem = new Semaphore(0); + + super.startAuditThread(); - queue = new LinkedBlockingQueue<>(); + if (haveAuditThread()) { + // tell the thread it can run + auditSem.release(); - if (super.startAuditThread(queue)) { // wait for the thread to start - CountDownLatch latch = new CountDownLatch(1); - queue.add(latch); - try { - waitLatch(latch); + waitSem(junitSem); } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -605,5 +600,31 @@ public class IntegrityAuditTestBase { assertTrue(waitThread(this)); } + + @Override + protected AuditThread makeAuditThread(String resourceName2, String persistenceUnit2, Properties properties2, + long integrityAuditPeriodMillis2) throws IntegrityAuditException { + + return new AuditThread(resourceName2, persistenceUnit2, properties2, integrityAuditPeriodMillis2, this) { + + private Semaphore auditSem = MyIntegrityAudit.this.auditSem; + private Semaphore junitSem = MyIntegrityAudit.this.junitSem; + + @Override + public void runStarted() throws InterruptedException { + auditSem.acquire(); + + junitSem.release(); + auditSem.acquire(); + } + + @Override + public void auditCompleted() throws InterruptedException { + junitSem.release(); + auditSem.acquire(); + } + + }; + } } } diff --git a/integrity-monitor/src/main/java/org/onap/policy/common/im/IntegrityMonitor.java b/integrity-monitor/src/main/java/org/onap/policy/common/im/IntegrityMonitor.java index c32a2213..38dc20dc 100644 --- a/integrity-monitor/src/main/java/org/onap/policy/common/im/IntegrityMonitor.java +++ b/integrity-monitor/src/main/java/org/onap/policy/common/im/IntegrityMonitor.java @@ -29,10 +29,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; - import javax.management.JMX; import javax.management.MBeanServerConnection; import javax.persistence.EntityManager; @@ -43,7 +40,6 @@ import javax.persistence.LockModeType; import javax.persistence.Persistence; import javax.persistence.Query; import javax.validation.constraints.NotNull; - import org.onap.policy.common.im.jmx.ComponentAdmin; import org.onap.policy.common.im.jmx.ComponentAdminMBean; import org.onap.policy.common.im.jmx.JmxAgentConnection; @@ -196,7 +192,7 @@ public class IntegrityMonitor { */ protected IntegrityMonitor(String resourceName, Properties properties) throws IntegrityMonitorException { - this(resourceName, properties, null); + this(resourceName, properties, new Factory()); } /** @@ -207,10 +203,10 @@ public class IntegrityMonitor { * * @param resourceName The resource name of the resource * @param properties a set of properties passed in from the resource - * @param queue queue to use to control the FPManager thread, or {@code null} + * @param factory Factory to use to control the FPManager thread * @throws IntegrityMonitorException if any errors are encountered in the constructor */ - protected IntegrityMonitor(String resourceName, Properties properties, BlockingQueue<CountDownLatch> queue) + protected IntegrityMonitor(String resourceName, Properties properties, Factory factory) throws IntegrityMonitorException { // singleton check since this constructor can be called from a child or @@ -357,7 +353,8 @@ public class IntegrityMonitor { logger.error("ComponentAdmin constructor exception: {}", e.toString(), e); } - fpManager = new FpManager(queue); + fpManager = new FpManager(factory); + fpManager.start(); } @@ -373,7 +370,7 @@ public class IntegrityMonitor { */ public static IntegrityMonitor getInstance(String resourceName, Properties properties) throws IntegrityMonitorException { - return getInstance(resourceName, properties, null); + return getInstance(resourceName, properties, new Factory()); } /** @@ -382,13 +379,13 @@ public class IntegrityMonitor { * * @param resourceName The resource name of the resource * @param properties a set of properties passed in from the resource - * @param queue queue to use to control the FPManager thread, or {@code null} + * @param factory Factory to use to control the FPManager thread * @return The new instance of IntegrityMonitor * @throws IntegrityMonitorException if unable to create jmx url or the constructor returns an * exception */ protected static IntegrityMonitor getInstance(String resourceName, Properties properties, - BlockingQueue<CountDownLatch> queue) throws IntegrityMonitorException { + Factory factory) throws IntegrityMonitorException { synchronized (getInstanceLock) { logger.debug("getInstance() called - resourceName= {}", resourceName); @@ -399,7 +396,7 @@ public class IntegrityMonitor { if (instance == null) { logger.debug("Creating new instance of IntegrityMonitor"); - instance = new IntegrityMonitor(resourceName, properties, queue); + instance = new IntegrityMonitor(resourceName, properties, factory); } return instance; } @@ -1740,18 +1737,15 @@ public class IntegrityMonitor { * dependencies, does a refresh state audit and runs the stateAudit. */ class FpManager extends Thread { - private final CountDownLatch stopper = new CountDownLatch(1); + private boolean stopRequested = false; - private BlockingQueue<CountDownLatch> queue; - private CountDownLatch progressLatch = null; + private final Factory factory; // Constructor - start FP manager thread - FpManager(BlockingQueue<CountDownLatch> queue) { - this.queue = queue; + FpManager(Factory factory) { + this.factory = factory; // set now as the last time the refreshStateAudit ran IntegrityMonitor.this.refreshStateAuditLastRunDate = new Date(); - // start thread - this.start(); } @Override @@ -1759,13 +1753,13 @@ public class IntegrityMonitor { logger.debug("FPManager thread running"); try { - getLatch(); - decrementLatch(); + factory.runStarted(); - while (!stopper.await(cycleIntervalMillis, TimeUnit.MILLISECONDS)) { - getLatch(); + while(!stopRequested) { + factory.doSleep(cycleIntervalMillis); + IntegrityMonitor.this.runOnce(); - decrementLatch(); + factory.monitorCompleted(); } } catch (InterruptedException e) { @@ -1775,31 +1769,9 @@ public class IntegrityMonitor { } public void stopAndExit() { - stopper.countDown(); + stopRequested = true; this.interrupt(); } - - /** - * Gets the next latch from the queue. - * - * @throws InterruptedException - * - */ - private void getLatch() throws InterruptedException { - if (queue != null) { - progressLatch = queue.take(); - } - } - - /** - * Decrements the current latch. - */ - private void decrementLatch() { - if (progressLatch != null) { - progressLatch.countDown(); - } - } - } private void runOnce() { @@ -1934,6 +1906,40 @@ public class IntegrityMonitor { return allNotWellMap; } + /** + * Used to access various objects. Overridden by junit tests. + */ + public static class Factory { + + /** + * Indicates that the {@link FpManager#run()} method has started. This method + * simply returns. + * + * @throws InterruptedException + */ + public void runStarted() throws InterruptedException { + // does nothing + } + + /** + * Sleeps for a period of time. + * @param sleepMs amount of time to sleep, in milliseconds + * @throws InterruptedException + */ + public void doSleep(long sleepMs) throws InterruptedException { + Thread.sleep(sleepMs); + } + + /** + * Indicates that a monitor activity has completed. This method simply returns. + * + * @throws InterruptedException + */ + public void monitorCompleted() throws InterruptedException { + // does nothing + } + } + /* * The remaining methods are used by JUnit tests. */ diff --git a/integrity-monitor/src/test/java/org/onap/policy/common/im/IntegrityMonitorTest.java b/integrity-monitor/src/test/java/org/onap/policy/common/im/IntegrityMonitorTest.java index 7f1e5516..091dcc91 100644 --- a/integrity-monitor/src/test/java/org/onap/policy/common/im/IntegrityMonitorTest.java +++ b/integrity-monitor/src/test/java/org/onap/policy/common/im/IntegrityMonitorTest.java @@ -22,23 +22,19 @@ package org.onap.policy.common.im; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; - import java.util.Date; import java.util.List; import java.util.Properties; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.LinkedBlockingQueue; - +import java.util.concurrent.Semaphore; import javax.persistence.EntityTransaction; import javax.persistence.Query; import javax.persistence.TemporalType; - import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.onap.policy.common.im.IntegrityMonitor.Factory; import org.onap.policy.common.im.jpa.ForwardProgressEntity; import org.onap.policy.common.im.jpa.ResourceRegistrationEntity; import org.onap.policy.common.im.jpa.StateManagementEntity; @@ -57,7 +53,8 @@ public class IntegrityMonitorTest extends IntegrityMonitorTestBase { private static EntityTransaction et; private static String resourceName; - private BlockingQueue<CountDownLatch> queue; + private Semaphore monitorSem; + private Semaphore junitSem; /** * Set up for test class. @@ -900,9 +897,36 @@ public class IntegrityMonitorTest extends IntegrityMonitorTestBase { private IntegrityMonitor makeMonitor(String resourceName, Properties myProp) throws Exception { IntegrityMonitor.deleteInstance(); - queue = new LinkedBlockingQueue<>(); + monitorSem = new Semaphore(0); + junitSem = new Semaphore(0); + + Factory factory = new IntegrityMonitor.Factory() { + + @Override + public void doSleep(long sleepMs) throws InterruptedException { + /* + * No need to sleep, as the thread won't progress until the + * semaphore is released. + */ + } + + @Override + public void runStarted() throws InterruptedException { + monitorSem.acquire(); + + junitSem.release(); + monitorSem.acquire(); + } + + @Override + public void monitorCompleted() throws InterruptedException { + junitSem.release(); + monitorSem.acquire(); + } + + }; - IntegrityMonitor im = IntegrityMonitor.getInstance(resourceName, myProp, queue); + IntegrityMonitor im = IntegrityMonitor.getInstance(resourceName, myProp, factory); // wait for the monitor thread to start waitStep(); @@ -916,8 +940,7 @@ public class IntegrityMonitorTest extends IntegrityMonitorTestBase { * @throws InterruptedException if the thread is interrupted */ private void waitStep() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - queue.offer(latch); - waitLatch(latch); + monitorSem.release(); + waitSem(junitSem); } } diff --git a/integrity-monitor/src/test/java/org/onap/policy/common/im/IntegrityMonitorTestBase.java b/integrity-monitor/src/test/java/org/onap/policy/common/im/IntegrityMonitorTestBase.java index 0c8259b7..e5562306 100644 --- a/integrity-monitor/src/test/java/org/onap/policy/common/im/IntegrityMonitorTestBase.java +++ b/integrity-monitor/src/test/java/org/onap/policy/common/im/IntegrityMonitorTestBase.java @@ -22,17 +22,14 @@ package org.onap.policy.common.im; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; - import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties; -import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; - import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; - import org.onap.policy.common.utils.jpa.EntityTransCloser; import org.onap.policy.common.utils.test.log.logback.ExtractAppender; import org.slf4j.Logger; @@ -243,14 +240,14 @@ public class IntegrityMonitorTestBase { } /** - * Waits for a latch to reach zero. + * Waits for a semaphore to be acquired * - * @param latch the latch + * @param sem the latch * @throws InterruptedException if the thread is interrupted * @throws AssertionError if the latch did not reach zero in the allotted time */ - protected void waitLatch(CountDownLatch latch) throws InterruptedException { - assertTrue(latch.await(WAIT_MS, TimeUnit.SECONDS)); + protected void waitSem(Semaphore sem) throws InterruptedException { + assertTrue(sem.tryAcquire(WAIT_MS, TimeUnit.MILLISECONDS)); } /** diff --git a/utils-test/pom.xml b/utils-test/pom.xml index a7c2eaee..933104b0 100644 --- a/utils-test/pom.xml +++ b/utils-test/pom.xml @@ -42,6 +42,11 @@ <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> </dependency> + <dependency> + <groupId>org.onap.policy.common</groupId> + <artifactId>utils</artifactId> + <version>${project.version}</version> + </dependency> </dependencies> <build> diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTime.java b/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTime.java new file mode 100644 index 00000000..3dfed4bd --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTime.java @@ -0,0 +1,59 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils-Test + * ================================================================================ + * Copyright (C) 2018 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.common.utils.time; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicLong; + +/** + * "Current" time, when running junit tests. This is intended to be injected into classes + * under test, to replace their {@link CurrentTime} objects. When {@link #sleep(long)} is + * invoked, it simply advances the notion of "current" time and returns immediately. + */ +public class TestTime extends CurrentTime { + + /** + * "Current" time, in milliseconds, used by tests. + */ + private AtomicLong tcur = new AtomicLong(System.currentTimeMillis()); + + /** + * + */ + public TestTime() { + super(); + } + + @Override + public long getMillis() { + return tcur.get(); + } + + @Override + public Date getDate() { + return new Date(tcur.get()); + } + + @Override + public void sleep(long sleepMs) throws InterruptedException { + tcur.addAndGet(sleepMs); + } +} diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTimeMulti.java b/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTimeMulti.java new file mode 100644 index 00000000..7a8277c7 --- /dev/null +++ b/utils-test/src/main/java/org/onap/policy/common/utils/time/TestTimeMulti.java @@ -0,0 +1,200 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils-Test + * ================================================================================ + * Copyright (C) 2018 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.common.utils.time; + +import java.util.Date; +import java.util.PriorityQueue; +import java.util.concurrent.CountDownLatch; + +/** + * "Current" time, when running junit tests in multiple threads. This is intended to be + * injected into classes under test, to replace their {@link CurrentTime} objects. The + * {@link #sleep(long)} method blocks until all threads enter and then it moves the notion + * of "current" time forward, allowing threads to resume, as the end of their sleep time + * is reached. Additional threads do not resume until all threads have once again entered + * {@link #sleep(long)} or when {@link #threadCompleted()} is invoked to indicate that a + * thread will not re-enter {@link #sleep(long)}. + */ +public class TestTimeMulti extends CurrentTime { + + /** + * Number of threads that will be sleeping simultaneously. + */ + private int nthreads; + + /** + * "Current" time, in milliseconds, used by tests. + */ + private long tcur = System.currentTimeMillis(); + + /** + * Queue of sleeping threads waiting to be awakened. + */ + private final PriorityQueue<Info> queue = new PriorityQueue<>(); + + /** + * Used to synchronize updates. + */ + private final Object locker = new Object(); + + /** + * + * @param nthreads number of threads that will be sleeping simultaneously + */ + public TestTimeMulti(int nthreads) { + this.nthreads = nthreads; + } + + @Override + public long getMillis() { + return tcur; + } + + @Override + public Date getDate() { + return new Date(tcur); + } + + @Override + public void sleep(long sleepMs) throws InterruptedException { + if (sleepMs <= 0) { + return; + } + + Info info = new Info(tcur + sleepMs); + + synchronized (locker) { + queue.add(info); + + if (queue.size() == nthreads) { + // all threads are now sleeping - wake one up + wakeThreads(); + } + } + + // this MUST happen outside of the "synchronized" block + info.await(); + } + + /** + * Indicates that a thread has terminated or that it will no longer be invoking + * {@link #sleep(long)}. Awakens the next sleeping thread, if the queue is full after + * removing the terminated thread. + * + * @throws IllegalStateException if the queue is already full + */ + public void threadCompleted() { + synchronized (locker) { + int sz = queue.size(); + if (sz >= nthreads) { + throw new IllegalStateException("too many threads still sleeping"); + } + + --nthreads; + + if (sz == nthreads) { + // after removing terminated thread - queue is now full; awaken something + wakeThreads(); + } + } + } + + /** + * Advances the "current" time and awakens any threads sleeping until that time. + */ + private void wakeThreads() { + Info info = queue.poll(); + if(info == null) { + return; + } + + tcur = info.getAwakenAtMs(); + info.wake(); + + while ((info = queue.poll()) != null) { + if (tcur == info.getAwakenAtMs()) { + info.wake(); + + } else { + // not ready to wake this thread - put it back in the queue + queue.add(info); + break; + } + } + } + + /** + * Info about a sleeping thread. + */ + private static class Info implements Comparable<Info> { + + /** + * Time, in milliseconds, at which the associated thread should awaken. + */ + private final long awakenAtMs; + + /** + * This is triggered when the associated thread should awaken. + */ + private final CountDownLatch latch = new CountDownLatch(1); + + /** + * @param awakenAtMs time, in milliseconds, at which the associated thread should + * awaken + */ + public Info(long awakenAtMs) { + this.awakenAtMs = awakenAtMs; + } + + public long getAwakenAtMs() { + return awakenAtMs; + } + + /** + * Awakens the associated thread by decrementing its latch. + */ + public void wake() { + latch.countDown(); + } + + /** + * Blocks the current thread until awakened (i.e., until its latch is + * decremented). + * + * @throws InterruptedException + */ + public void await() throws InterruptedException { + latch.await(); + } + + @Override + public int compareTo(Info o) { + int diff = Long.compare(awakenAtMs, o.awakenAtMs); + + // this assumes that Object.toString() is unique for each Info object + if (diff == 0) + diff = this.toString().compareTo(o.toString()); + + return diff; + } + + } +} diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeMultiTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeMultiTest.java new file mode 100644 index 00000000..206ab5f1 --- /dev/null +++ b/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeMultiTest.java @@ -0,0 +1,116 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils-Test + * ================================================================================ + * Copyright (C) 2018 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.common.utils.time; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import org.junit.Test; + +/** + * + */ +public class TestTimeMultiTest { + + private static final int NTHREADS = 10; + private static final int NTIMES = 100; + private static final long WAIT_SEC = 5L; + private static final long MIN_SLEEP_MS = 5L; + + private TestTimeMulti ttm; + private Semaphore done; + + @Test + public void test() throws Exception { + ttm = new TestTimeMulti(NTHREADS); + done = new Semaphore(0); + + long tbeg = ttm.getMillis(); + + // create threads + List<MyThread> threads = new ArrayList<>(NTHREADS); + for (int x = 0; x < NTHREADS; ++x) { + threads.add(new MyThread(x + MIN_SLEEP_MS)); + } + + // launch threads + for (MyThread thr : threads) { + thr.start(); + } + + // wait for each one to complete + for (MyThread thr : threads) { + assertTrue("complete " + thr.getSleepMs(), done.tryAcquire(WAIT_SEC, TimeUnit.SECONDS)); + ttm.threadCompleted(); + } + + // check results + for (MyThread thr : threads) { + assertEquals("time " + thr.getSleepMs(), thr.texpected, thr.tactual); + } + + assertTrue(ttm.getMillis() >= tbeg + NTIMES * MIN_SLEEP_MS); + } + + private class MyThread extends Thread { + + private final long sleepMs; + + private volatile long texpected; + private volatile long tactual; + + public MyThread(long sleepMs) { + this.sleepMs = sleepMs; + + this.setDaemon(true); + } + + public long getSleepMs() { + return sleepMs; + } + + @Override + public void run() { + try { + for (int x = 0; x < NTIMES; ++x) { + texpected = ttm.getMillis() + sleepMs; + ttm.sleep(sleepMs); + + if ((tactual = ttm.getMillis()) != texpected) { + break; + } + + if ((tactual = ttm.getDate().getTime()) != texpected) { + break; + } + } + + } catch (InterruptedException expected) { + Thread.currentThread().interrupt(); + } + + done.release(); + } + } +} diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeTest.java new file mode 100644 index 00000000..c1e15b38 --- /dev/null +++ b/utils-test/src/test/java/org/onap/policy/common/utils/time/TestTimeTest.java @@ -0,0 +1,66 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils-Test + * ================================================================================ + * Copyright (C) 2018 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.common.utils.time; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class TestTimeTest { + + @Test + public void test() throws Exception { + TestTime tm = new TestTime(); + TestTime tm2 = new TestTime(); + + long treal = System.currentTimeMillis(); + + long tcur = tm.getMillis(); + assertEquals(tcur, tm.getDate().getTime()); + + long tsleep = 10000L; + long tcur2 = tcur; + + // sleep a bit and then check values + tcur2 += tsleep; + tm2.sleep(tsleep); + assertEquals(tcur2, tm2.getMillis()); + assertEquals(tcur2, tm2.getDate().getTime()); + + // sleep some more and then check values + tcur2 += tsleep; + tm2.sleep(tsleep); + assertEquals(tcur2, tm2.getMillis()); + assertEquals(tcur2, tm2.getDate().getTime()); + + // check again - to ensure unchanged + assertEquals(tcur2, tm2.getMillis()); + assertEquals(tcur2, tm2.getDate().getTime()); + + // original should also be unchanged + assertEquals(tcur, tm.getMillis()); + assertEquals(tcur, tm.getDate().getTime()); + + // ensure that no real time has elapsed + assertTrue(System.currentTimeMillis() < treal + tsleep / 2); + } + +} diff --git a/utils/pom.xml b/utils/pom.xml index cf34bd7c..8375408e 100644 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -38,6 +38,11 @@ <dependencies> <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.4</version> + </dependency> + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyConfiguration.java b/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyConfiguration.java index 7253c746..e72ebaba 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyConfiguration.java +++ b/utils/src/main/java/org/onap/policy/common/utils/properties/PropertyConfiguration.java @@ -25,8 +25,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Properties; +import org.apache.commons.lang3.StringUtils; import org.onap.policy.common.utils.properties.exception.PropertyAccessException; import org.onap.policy.common.utils.properties.exception.PropertyException; import org.onap.policy.common.utils.properties.exception.PropertyInvalidException; @@ -35,7 +38,9 @@ import org.onap.policy.common.utils.properties.exception.PropertyMissingExceptio /** * Configuration whose fields are initialized by reading from a set of {@link Properties}, * as directed by the {@link Property} annotations that appear on fields within the - * subclass. + * subclass. The values of the fields are set via <i>setXxx()</i> methods. As a result, if + * a field is annotated and there is no corresponding <i>setXxx()</i> method, then an + * exception will be thrown. * <p> * It is possible that an invalid <i>defaultValue</i> is specified via the * {@link Property} annotation. This could remain undetected until an optional property is @@ -104,7 +109,10 @@ public class PropertyConfiguration { checkModifiable(field, prop); - if (setValue(field, props, prop)) { + Method setter = getSetter(field, prop); + checkSetter(setter, prop); + + if (setValue(setter, field, props, prop)) { return true; } @@ -112,15 +120,33 @@ public class PropertyConfiguration { } /** + * @param field field whose value is to be set + * @param prop property of interest + * @return the method to be used to set the field's value + * @throws PropertyAccessException if a "set" method cannot be identified + */ + private Method getSetter(Field field, Property prop) throws PropertyAccessException { + String nm = "set" + StringUtils.capitalize(field.getName()); + + try { + return this.getClass().getMethod(nm, field.getType()); + + } catch (NoSuchMethodException | SecurityException e) { + throw new PropertyAccessException(prop.name(), nm, e); + } + } + + /** * Sets a field's value from a particular property. * + * @param setter method to be used to set the field's value * @param field field whose value is to be set * @param props properties from which to get the value * @param prop property of interest * @return {@code true} if the property's value was set, {@code false} otherwise * @throws PropertyException if an error occurs */ - protected boolean setValue(Field field, Properties props, Property prop) throws PropertyException { + protected boolean setValue(Method setter, Field field, Properties props, Property prop) throws PropertyException { try { Object val = getValue(field, props, prop); @@ -128,23 +154,15 @@ public class PropertyConfiguration { return false; } else { - - /* - * According to java docs & blogs, "field" is our own copy, so we're free - * to change the flags without impacting the real permissions of the field - * within the real class. - */ - field.setAccessible(true); - - field.set(this, val); + setter.invoke(this, val); return true; } } catch (IllegalArgumentException e) { throw new PropertyInvalidException(prop.name(), field.getName(), e); - } catch (IllegalAccessException e) { - throw new PropertyAccessException(prop.name(), field.getName(), e); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new PropertyAccessException(prop.name(), setter.getName(), e); } } @@ -203,6 +221,21 @@ public class PropertyConfiguration { } /** + * Verifies that the setter method is not <i>static</i>. + * + * @param setter method to be checked + * @param prop property of interest + * @throws PropertyAccessException if the method is static + */ + private void checkSetter(Method setter, Property prop) throws PropertyAccessException { + int mod = setter.getModifiers(); + + if (Modifier.isStatic(mod)) { + throw new PropertyAccessException(prop.name(), setter.getName(), "method is 'static'"); + } + } + + /** * Gets a property value, coercing it to a String. * * @param fieldName field whose value is to be set diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/SpecProperties.java b/utils/src/main/java/org/onap/policy/common/utils/properties/SpecProperties.java new file mode 100644 index 00000000..0f416c3a --- /dev/null +++ b/utils/src/main/java/org/onap/policy/common/utils/properties/SpecProperties.java @@ -0,0 +1,117 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2018 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.common.utils.properties; + +import java.util.Properties; + +/** + * Properties with an optional specialization (e.g., session name, controller name). + */ +public class SpecProperties extends Properties { + private static final long serialVersionUID = 1L; + + /** + * The property prefix, ending with ".". + */ + private final String prefix; + + /** + * The specialized property prefix, ending with ".". + */ + private final String specPrefix; + + /** + * + * @param prefix the property name prefix that appears before any specialization, may + * be "" + * @param specialization the property name specialization (e.g., session name) + */ + public SpecProperties(String prefix, String specialization) { + this.prefix = withTrailingDot(prefix); + this.specPrefix = withTrailingDot(this.prefix + specialization); + } + + /** + * + * @param prefix the property name prefix that appears before any specialization, may + * be "" + * @param specialization the property name specialization (e.g., session name) + * @param props the default properties + */ + public SpecProperties(String prefix, String specialization, Properties props) { + super(props); + + this.prefix = withTrailingDot(prefix); + this.specPrefix = withTrailingDot(this.prefix + specialization); + } + + /** + * Adds a trailing "." to a String, if it doesn't already have one. + * + * @param text text to which the "." should be added + * @return the text, with a trailing "." + */ + private static String withTrailingDot(String text) { + return text.isEmpty() || text.endsWith(".") ? text : text + "."; + } + + /** + * Gets the property whose value has the given key, looking first for the specialized + * property name, and then for the generalized property name. + * + * @param key property name, without the specialization + * @return the value from the property set, or {@code null} if the property set does + * not contain the value + */ + @Override + public String getProperty(String key) { + if (!key.startsWith(prefix)) { + return super.getProperty(key); + } + + String suffix = key.substring(prefix.length()); + + String val = super.getProperty(specPrefix + suffix); + if (val != null) { + return val; + } + + return super.getProperty(key); + } + + protected String getPrefix() { + return prefix; + } + + protected String getSpecPrefix() { + return specPrefix; + } + + @Override + public final int hashCode() { + throw new UnsupportedOperationException("SpecProperties cannot be hashed"); + } + + @Override + public final boolean equals(Object obj) { + throw new UnsupportedOperationException("cannot compare SpecProperties"); + } +} diff --git a/utils/src/main/java/org/onap/policy/common/utils/properties/SpecPropertyConfiguration.java b/utils/src/main/java/org/onap/policy/common/utils/properties/SpecPropertyConfiguration.java deleted file mode 100644 index 9e3767c1..00000000 --- a/utils/src/main/java/org/onap/policy/common/utils/properties/SpecPropertyConfiguration.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * ONAP Policy Engine - Common Modules - * ================================================================================ - * Copyright (C) 2018 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.common.utils.properties; - -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.onap.policy.common.utils.properties.exception.PropertyException; - -/** - * PropertyConfiguration whose property names are specialized, using a specialization. A - * property name can take one of the following forms: - * <dl> - * <dt>aaa{$}Ddd</dt> - * <dd>if the specialization is "Xxx", then it looks for the value associated with the - * property named "aaaXxxDdd"</dd> - * <dt>aaa{Bbb?Ccc}Ddd</dt> - * <dd>if the specialization is "Xxx", then it looks for the value associated with the - * property named "aaaBbbXxxCccDdd". If the property does not exist, then it looks for the - * value associated with the property named "aaaDdd" (i.e., without the - * specialization)</dd> - * <dt>aaa</dt> - * <dd>simply looks for the value associated with the property named "aaa", without using - * the specialization</dd> - * </dl> - * <p> - * In the above examples, any of the components (e.g., "aaa") may be empty. - */ -public class SpecPropertyConfiguration extends PropertyConfiguration { - - /** - * Pattern to extract the specializer from a property name. Group 1 matches the form, - * "{$}", while groups 2 and 3 match the prefix and suffix, respectively, of the form, - * "{prefix?suffix}". - */ - private static final Pattern SPEC_PAT = Pattern.compile("" - // start of specialization info - + "\\{(?:" - // specialization type 1 - + "(\\$)" - // alternative - + "|" - // specialization type 2 - + "(?:" - // specialization type 2 prefix, may be empty - + "([^}?]*)" - // place-holder for the specialization, itself - + "\\?" - // specialization type 2 suffix, may be empty - + "([^}]*)" - // end of specialization type 2 - + ")" - // end of specialization info - + ")\\}"); - - /** - * The specialization to be used within property names. - */ - private final String specialization; - - /** - * Constructs a configuration, without populating any fields; fields should be - * populated later by invoking {@link #setAllFields(Properties)}. - * - * @param specialization specialization to be substituted within property names - */ - public SpecPropertyConfiguration(String specialization) { - super(); - - this.specialization = specialization; - } - - /** - * - * Initializes each "@Property" field with its value, as found in the properties. - * - * @param specialization specialization to be substituted within property names - * @param props properties from which to extract the values - * @throws PropertyException if an error occurs - */ - public SpecPropertyConfiguration(String specialization, Properties props) throws PropertyException { - super(); - - this.specialization = specialization; - - setAllFields(props); - } - - /** - * Gets a property's value, examining the property name for each of the types of - * specialization. - */ - @Override - protected String getRawPropertyValue(Properties props, String propnm) { - Matcher mat = SPEC_PAT.matcher(propnm); - - if (!mat.find()) { - // property name isn't specialized - use it as is - return super.getRawPropertyValue(props, propnm); - - } else if (mat.group(1) != null) { - // replace "{$}" with the specialization name - return super.getRawPropertyValue(props, specializeType1(propnm, specialization, mat)); - - } else { - // first try to get the property using the specialization info - String val = super.getRawPropertyValue(props, specializeType2(propnm, specialization, mat)); - if (val != null) { - return val; - } - - // wasn't found - try again, without any specialization info - return super.getRawPropertyValue(props, generalizeType2(propnm, mat)); - } - } - - /** - * Generalizes a property name by stripping any specialization info from it. This is - * typically used to construct property names for junit testing. - * - * @param propnm property name to be stripped of specialization info - * @return the generalized property name - * @throws IllegalArgumentException if the property name requires specialization - * (i.e., contains "{$}") - */ - public static String generalize(String propnm) { - Matcher mat = SPEC_PAT.matcher(propnm); - - if (!mat.find()) { - // property name has no specialization info - return propnm; - - } else if (mat.group(1) != null) { - // the "{$}" form requires specialization - throw new IllegalArgumentException("property requires specialization"); - - } else { - // property name has specialization info - strip it out - return generalizeType2(propnm, mat); - } - } - - /** - * - * Generalizes a property name of specialization type 2 (i.e., "{xxx?yyy}" form). - * - * @param propnm property name to be stripped of specialization info - * @param matcher the matcher that matched the "{xxx?yyy}" - * @return the generalized property name - */ - private static String generalizeType2(String propnm, Matcher mat) { - String prefix = propnm.substring(0, mat.start()); - String suffix = propnm.substring(mat.end()); - - return prefix + suffix; - } - - /** - * Specializes a property name by applying the specialization. This is typically used - * to construct property names for junit testing. - * - * @param propnm property name to be stripped of specialization info - * @param spec specialization to apply - * @return the specialized property name - */ - public static String specialize(String propnm, String spec) { - Matcher mat = SPEC_PAT.matcher(propnm); - - if (!mat.find()) { - // property name has no specialization info - leave it as is - return propnm; - - } else if (mat.group(1) != null) { - // the "{$}" form requires specialization - return specializeType1(propnm, spec, mat); - - } else { - // the "{xxx?yyy}" form requires specialization - return specializeType2(propnm, spec, mat); - } - } - - /** - * Specializes a property name of specialization type 1 (i.e., "{$}" form). - * - * @param propnm property name to be stripped of specialization info - * @param spec specialization to apply - * @param matcher the matcher that matched the "{$}" - * @return the specialized property name - */ - private static String specializeType1(String propnm, String spec, Matcher mat) { - String prefix = propnm.substring(0, mat.start()); - String suffix = propnm.substring(mat.end()); - - return prefix + spec + suffix; - } - - /** - * Specializes a property name of specialization type 2 (i.e., "{xxx?yyy}" form). - * - * @param propnm property name to be stripped of specialization info - * @param spec specialization to apply - * @param matcher the matcher that matched the "{xxx?yyy}" - * @return the specialized property name - */ - private static String specializeType2(String propnm, String spec, Matcher matcher) { - String prefix = propnm.substring(0, matcher.start()); - String suffix = propnm.substring(matcher.end()); - - String specPrefix = matcher.group(2); - String specSuffix = matcher.group(3); - - return (prefix + specPrefix + spec + specSuffix + suffix); - } -} diff --git a/utils/src/main/java/org/onap/policy/common/utils/time/CurrentTime.java b/utils/src/main/java/org/onap/policy/common/utils/time/CurrentTime.java new file mode 100644 index 00000000..cab469e5 --- /dev/null +++ b/utils/src/main/java/org/onap/policy/common/utils/time/CurrentTime.java @@ -0,0 +1,61 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils + * ================================================================================ + * Copyright (C) 2018 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.common.utils.time; + +import java.util.Date; + +/** + * Methods to access the current time. Classes can use objects of this type to get current + * time information, while allowing the objects to be overridden by junit tests. + */ +public class CurrentTime { + + /** + * + */ + public CurrentTime() { + super(); + } + + /** + * @return the current time, in milliseconds + */ + public long getMillis() { + return System.currentTimeMillis(); + } + + /** + * @return the current Date + */ + public Date getDate() { + return new Date(); + } + + /** + * Sleeps for a period of time. + * + * @param sleepMs amount of time to sleep, in milliseconds + * @throws InterruptedException + */ + public void sleep(long sleepMs) throws InterruptedException { + Thread.sleep(sleepMs); + } +} diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/PropertyConfigurationTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/PropertyConfigurationTest.java index cf823b5d..121ae384 100644 --- a/utils/src/test/java/org/onap/policy/common/utils/properties/PropertyConfigurationTest.java +++ b/utils/src/test/java/org/onap/policy/common/utils/properties/PropertyConfigurationTest.java @@ -68,15 +68,9 @@ public class PropertyConfigurationTest { @Test public void testPropertyConfiguration() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private String value; - }; - props.setProperty(THE_VALUE, STRING_VALUE); - Config cfg = new Config(); + PlainStringConfig cfg = new PlainStringConfig(); assertEquals(null, cfg.value); cfg.setAllFields(props); @@ -85,18 +79,8 @@ public class PropertyConfigurationTest { @Test public void testPropertyConfigurationProperties() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, STRING_VALUE); - Config cfg = new Config(props); + PlainStringConfig cfg = new PlainStringConfig(props); assertEquals(STRING_VALUE, cfg.value); } @@ -111,6 +95,11 @@ public class PropertyConfigurationTest { @Property(name = "grandparent.value") protected boolean grandparentValue; + + @SuppressWarnings("unused") + public void setGrandparentValue(boolean grandparentValue) { + this.grandparentValue = grandparentValue; + } }; /* @@ -120,12 +109,22 @@ public class PropertyConfigurationTest { @Property(name = "parent.value") protected long parentValue; + + @SuppressWarnings("unused") + public void setParentValue(long parentValue) { + this.parentValue = parentValue; + } }; class Config extends ParentConfig { @Property(name = THE_VALUE) private String value; + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; @@ -158,6 +157,11 @@ public class PropertyConfigurationTest { class Config extends PropertyConfiguration { private String value; + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; @@ -171,18 +175,8 @@ public class PropertyConfigurationTest { @Test public void testSetValueFieldProperties_FieldSet() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, STRING_VALUE); - Config cfg = new Config(props); + PlainStringConfig cfg = new PlainStringConfig(props); assertEquals(STRING_VALUE, cfg.value); } @@ -196,6 +190,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; props.setProperty(THE_VALUE, STRING_VALUE); @@ -215,14 +214,19 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Exception value) { + this.value = value; + } }; props.setProperty(THE_VALUE, STRING_VALUE); new Config(props); } - @Test(expected = PropertyMissingException.class) - public void testSetValueFieldPropertyProperties_NoProperty_NoDefault() throws PropertyException { + @Test(expected = PropertyAccessException.class) + public void testGetSetter_NoSetter() throws PropertyException { class Config extends PropertyConfiguration { @Property(name = THE_VALUE) @@ -233,15 +237,18 @@ public class PropertyConfigurationTest { } }; + props.setProperty(THE_VALUE, STRING_VALUE); new Config(props); } - @Test(expected = PropertyInvalidException.class) - public void testSetValueFieldPropertyProperties_InvalidValue() throws PropertyException { - class Config extends PropertyConfiguration { + @Test(expected = PropertyMissingException.class) + public void testSetValueMethodFieldPropertiesProperty_NoProperty_NoDefault() throws PropertyException { + new PlainStringConfig(props); + } - @Property(name = THE_VALUE) - private int value; + @Test(expected = PropertyInvalidException.class) + public void testSetValueMethodFieldPropertiesProperty_InvalidValue() throws PropertyException { + class Config extends PlainPrimIntConfig { public Config(Properties props) throws PropertyException { super(props); @@ -260,6 +267,24 @@ public class PropertyConfigurationTest { new Config(props); } + @Test(expected = PropertyAccessException.class) + public void testSetValueMethodFieldPropertiesProperty_MethodEx() throws PropertyException { + class Config extends PlainStringConfig { + + public Config(Properties props) throws PropertyException { + super(props); + } + + @Override + public void setValue(String value) { + throw new IllegalArgumentException("expected exception"); + } + }; + + props.setProperty(THE_VALUE, STRING_VALUE); + new Config(props); + } + @Test public void testGetValue() throws PropertyException { // this class contains all of the supported field types @@ -295,6 +320,96 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public String getStringValue() { + return stringValue; + } + + @SuppressWarnings("unused") + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } + + @SuppressWarnings("unused") + public Boolean getBoolTrueValue() { + return boolTrueValue; + } + + @SuppressWarnings("unused") + public void setBoolTrueValue(Boolean boolTrueValue) { + this.boolTrueValue = boolTrueValue; + } + + @SuppressWarnings("unused") + public Boolean getBoolFalseValue() { + return boolFalseValue; + } + + @SuppressWarnings("unused") + public void setBoolFalseValue(Boolean boolFalseValue) { + this.boolFalseValue = boolFalseValue; + } + + @SuppressWarnings("unused") + public boolean isPrimBoolTrueValue() { + return primBoolTrueValue; + } + + @SuppressWarnings("unused") + public void setPrimBoolTrueValue(boolean primBoolTrueValue) { + this.primBoolTrueValue = primBoolTrueValue; + } + + @SuppressWarnings("unused") + public boolean isPrimBoolFalseValue() { + return primBoolFalseValue; + } + + @SuppressWarnings("unused") + public void setPrimBoolFalseValue(boolean primBoolFalseValue) { + this.primBoolFalseValue = primBoolFalseValue; + } + + @SuppressWarnings("unused") + public Integer getIntValue() { + return intValue; + } + + @SuppressWarnings("unused") + public void setIntValue(Integer intValue) { + this.intValue = intValue; + } + + @SuppressWarnings("unused") + public int getPrimIntValue() { + return primIntValue; + } + + @SuppressWarnings("unused") + public void setPrimIntValue(int primIntValue) { + this.primIntValue = primIntValue; + } + + @SuppressWarnings("unused") + public Long getLongValue() { + return longValue; + } + + @SuppressWarnings("unused") + public void setLongValue(Long longValue) { + this.longValue = longValue; + } + + @SuppressWarnings("unused") + public long getPrimLongValue() { + return primLongValue; + } + + @SuppressWarnings("unused") + public void setPrimLongValue(long primLongValue) { + this.primLongValue = primLongValue; + } }; props.setProperty("string", "a string"); @@ -331,6 +446,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Exception value) { + this.value = value; + } }; props.setProperty(THE_VALUE, STRING_VALUE); @@ -354,6 +474,21 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setPublicString(String publicString) { + this.publicString = publicString; + } + + @SuppressWarnings("unused") + public void setPrivateString(String privateString) { + this.privateString = privateString; + } + + @SuppressWarnings("unused") + public void setProtectedString(String protectedString) { + this.protectedString = protectedString; + } }; props.setProperty("public", "a public string"); @@ -370,7 +505,7 @@ public class PropertyConfigurationTest { @Test(expected = PropertyAccessException.class) public void testCheckModifiable_Static() throws PropertyException { props.setProperty(THE_VALUE, STRING_VALUE); - new StaticConfig(props); + new StaticPropConfig(props); } @Test(expected = PropertyAccessException.class) @@ -390,38 +525,24 @@ public class PropertyConfigurationTest { new Config(props); } + @Test(expected = PropertyAccessException.class) + public void testCheckSetter_Static() throws PropertyException { + props.setProperty(THE_VALUE, STRING_VALUE); + new StaticMethodConfig(props); + } + @Test public void testGetStringValue() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, STRING_VALUE); - Config cfg = new Config(props); + PlainStringConfig cfg = new PlainStringConfig(props); assertEquals(STRING_VALUE, cfg.value); } @Test public void testGetBooleanValue_NoDefault() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private Boolean value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, "true"); - Config cfg = new Config(props); + PlainBooleanConfig cfg = new PlainBooleanConfig(props); assertEquals(true, cfg.value); } @@ -436,6 +557,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Boolean value) { + this.value = value; + } }; props.setProperty(THE_VALUE, "true"); @@ -452,6 +578,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Boolean value) { + this.value = value; + } }; // property not defined @@ -479,6 +610,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Boolean value) { + this.value = value; + } }; // property not defined @@ -506,6 +642,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Integer value) { + this.value = value; + } }; props.setProperty(THE_VALUE, "200"); @@ -524,6 +665,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Integer value) { + this.value = value; + } }; props.setProperty(THE_VALUE, "200"); @@ -540,6 +686,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Integer value) { + this.value = value; + } }; // property not defined @@ -562,6 +713,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Long value) { + this.value = value; + } }; props.setProperty(THE_VALUE, "20000"); @@ -580,6 +736,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Long value) { + this.value = value; + } }; props.setProperty(THE_VALUE, "20000"); @@ -596,6 +757,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(Long value) { + this.value = value; + } }; // property not defined @@ -610,18 +776,8 @@ public class PropertyConfigurationTest { @Test public void testGetPropValue_Prop_NoDefault() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private String value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, STRING_VALUE); - Config cfg = new Config(props); + PlainStringConfig cfg = new PlainStringConfig(props); assertEquals(STRING_VALUE, cfg.value); } @@ -636,6 +792,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; props.setProperty(THE_VALUE, STRING_VALUE); @@ -654,6 +815,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; props.setProperty(THE_VALUE, ""); @@ -672,6 +838,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; Config cfg = new Config(props); @@ -689,6 +860,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; Config cfg = new Config(props); @@ -706,6 +882,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; new Config(props); @@ -721,6 +902,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; Config cfg = new Config(props); @@ -730,10 +916,7 @@ public class PropertyConfigurationTest { @Test public void testGetRawPropertyValue() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private String value; + class Config extends PlainStringConfig { public Config(Properties props) throws PropertyException { super(props); @@ -747,144 +930,64 @@ public class PropertyConfigurationTest { Config cfg = new Config(props); - assertEquals(STRING_VALUE, cfg.value); + assertEquals(STRING_VALUE, cfg.getValue()); } @Test public void testMakeBoolean_True() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private Boolean value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, "true"); - Config cfg = new Config(props); + PlainBooleanConfig cfg = new PlainBooleanConfig(props); assertEquals(true, cfg.value); } @Test public void testMakeBoolean_False() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private Boolean value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, "false"); - Config cfg = new Config(props); + PlainBooleanConfig cfg = new PlainBooleanConfig(props); assertEquals(false, cfg.value); } @Test(expected = PropertyInvalidException.class) public void testMakeBoolean_Invalid() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private Boolean value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, INVALID_VALUE); - new Config(props); + new PlainBooleanConfig(props); } @Test public void testMakeInteger_Valid() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private int value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, "300"); - Config cfg = new Config(props); + PlainPrimIntConfig cfg = new PlainPrimIntConfig(props); assertEquals(300, cfg.value); } @Test(expected = PropertyInvalidException.class) public void testMakeInteger_Invalid() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private int value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, INVALID_VALUE); - new Config(props); + new PlainPrimIntConfig(props); } @Test(expected = PropertyInvalidException.class) public void testMakeInteger_TooBig() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private int value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, String.valueOf(Integer.MAX_VALUE + 10L)); - new Config(props); + new PlainPrimIntConfig(props); } @Test public void testMakeLong_Valid() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, "30000"); - Config cfg = new Config(props); + PlainPrimLongConfig cfg = new PlainPrimLongConfig(props); assertEquals(30000L, cfg.value); } @Test(expected = PropertyInvalidException.class) public void testMakeLong_Invalid() throws PropertyException { - class Config extends PropertyConfiguration { - - @Property(name = THE_VALUE) - private long value; - - public Config(Properties props) throws PropertyException { - super(props); - } - }; - props.setProperty(THE_VALUE, INVALID_VALUE); - new Config(props); + new PlainPrimLongConfig(props); } @Test @@ -897,6 +1000,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(long value) { + this.value = value; + } }; Config cfg = new Config(props); @@ -914,6 +1022,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(long value) { + this.value = value; + } }; new Config(props); @@ -929,6 +1042,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(long value) { + this.value = value; + } }; new Config(props); @@ -944,6 +1062,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; // missing property - should default to "" @@ -971,6 +1094,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(long value) { + this.value = value; + } }; new Config(props); @@ -986,6 +1114,11 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(String value) { + this.value = value; + } }; Config cfg = new Config(props); @@ -1003,23 +1136,126 @@ public class PropertyConfigurationTest { public Config(Properties props) throws PropertyException { super(props); } + + @SuppressWarnings("unused") + public void setValue(long value) { + this.value = value; + } }; new Config(props); } /** - * A config whose annotated property is "static". + * Config with a String value having no qualifiers. */ - public static class StaticConfig extends PropertyConfiguration { + public class PlainStringConfig extends PropertyConfiguration { + + @Property(name = THE_VALUE) + private String value; + + public PlainStringConfig() { + + } + + public PlainStringConfig(Properties props) throws PropertyException { + super(props); + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + }; + + /** + * Config with a Boolean value having no qualifiers. + */ + public class PlainBooleanConfig extends PropertyConfiguration { + + @Property(name = THE_VALUE) + private Boolean value; + + public PlainBooleanConfig(Properties props) throws PropertyException { + super(props); + } + + public void setValue(Boolean value) { + this.value = value; + } + }; + + /** + * Config with an int value having no qualifiers. + */ + public class PlainPrimIntConfig extends PropertyConfiguration { + + @Property(name = THE_VALUE) + private int value; + + public PlainPrimIntConfig(Properties props) throws PropertyException { + super(props); + } + + public void setValue(int value) { + this.value = value; + } + }; + + /** + * Config with a long value having no qualifiers. + */ + public class PlainPrimLongConfig extends PropertyConfiguration { + + @Property(name = THE_VALUE) + private long value; + + public PlainPrimLongConfig(Properties props) throws PropertyException { + super(props); + } + + public void setValue(long value) { + this.value = value; + } + }; + + /** + * A config whose field is "static". + */ + public static class StaticPropConfig extends PropertyConfiguration { // "static" field cannot be set @Property(name = THE_VALUE) private static String value; - public StaticConfig(Properties props) throws PropertyException { + public StaticPropConfig(Properties props) throws PropertyException { super(props); } + + public static void setValue(String value) { + StaticPropConfig.value = value; + } + }; + + /** + * A config whose method is "static". + */ + public static class StaticMethodConfig extends PropertyConfiguration { + + // "static" field cannot be set + @Property(name = THE_VALUE) + private String value; + + public StaticMethodConfig(Properties props) throws PropertyException { + super(props); + } + + public static void setValue(String value) { + + } }; /** diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertiesTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertiesTest.java new file mode 100644 index 00000000..01f096d1 --- /dev/null +++ b/utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertiesTest.java @@ -0,0 +1,224 @@ +/* + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2018 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.common.utils.properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import java.util.Properties; +import org.junit.Before; +import org.junit.Test; + +public class SpecPropertiesTest { + + /** + * Property prefix of interest. + */ + private static final String MY_PREFIX = "my.prefix"; + + /** + * Specialization, which follows the prefix. + */ + private static final String MY_SPEC = "my.spec"; + + /** + * Generalized prefix (i.e., without the spec). + */ + private static final String PREFIX_GEN = MY_PREFIX + "."; + + /** + * Specialized prefix (i.e., with the spec). + */ + private static final String PREFIX_SPEC = PREFIX_GEN + MY_SPEC + "."; + + /** + * Suffix to add to property names to generate names of properties that are not + * populated. + */ + private static final String SUFFIX = ".suffix"; + + /** + * Property name without a prefix. + */ + private static final String PROP_NO_PREFIX = "other"; + + /** + * Generalized property name (i.e., without the spec). + */ + private static final String PROP_GEN = PREFIX_GEN + "generalized"; + + // property names that include the spec + private static final String PROP_SPEC = PREFIX_SPEC + "specialized"; + private static final String PROP_UNKNOWN = PREFIX_SPEC + "unknown"; + + // property values + private static final String VAL_NO_PREFIX = "no-prefix"; + private static final String VAL_GEN = "gen"; + private static final String VAL_SPEC = "spec"; + + private static final String VAL_DEFAULT = "default value"; + + private Properties supportingProps; + private SpecProperties props; + + @Before + public void setUp() { + supportingProps = new Properties(); + + supportingProps.setProperty(PROP_NO_PREFIX, VAL_NO_PREFIX); + supportingProps.setProperty(PROP_GEN, VAL_GEN); + supportingProps.setProperty(PROP_SPEC, VAL_SPEC); + + props = new SpecProperties(MY_PREFIX, MY_SPEC); + + props.putAll(supportingProps); + } + + @Test + public void testSpecPropertiesStringString() { + + // no supporting properties + props = new SpecProperties(MY_PREFIX, MY_SPEC); + + assertEquals(PREFIX_GEN, props.getPrefix()); + assertEquals(PREFIX_SPEC, props.getSpecPrefix()); + + // everything is null + assertNull(props.getProperty(gen(PROP_NO_PREFIX))); + assertNull(props.getProperty(gen(PROP_GEN))); + assertNull(props.getProperty(gen(PROP_SPEC))); + assertNull(props.getProperty(gen(PROP_UNKNOWN))); + } + + @Test + public void testSpecPropertiesStringStringProperties() { + + // use supportingProps as default properties + props = new SpecProperties(MY_PREFIX, MY_SPEC, supportingProps); + + assertEquals(PREFIX_GEN, props.getPrefix()); + assertEquals(PREFIX_SPEC, props.getSpecPrefix()); + + assertEquals(VAL_NO_PREFIX, props.getProperty(gen(PROP_NO_PREFIX))); + assertEquals(VAL_GEN, props.getProperty(gen(PROP_GEN))); + assertEquals(VAL_SPEC, props.getProperty(gen(PROP_SPEC))); + assertNull(props.getProperty(gen(PROP_UNKNOWN))); + } + + @Test + public void testSpecPropertiesStringStringProperties_EmptyPrefix() { + supportingProps = new Properties(); + + supportingProps.setProperty(PROP_NO_PREFIX, VAL_NO_PREFIX); + supportingProps.setProperty("a.value", VAL_GEN); + supportingProps.setProperty("b.value", VAL_GEN); + supportingProps.setProperty(MY_SPEC + ".b.value", VAL_SPEC); + + // no supporting properties + props = new SpecProperties("", MY_SPEC, supportingProps); + + assertEquals(VAL_NO_PREFIX, props.getProperty(gen(PROP_NO_PREFIX))); + assertEquals(VAL_GEN, props.getProperty(gen("a.value"))); + assertEquals(VAL_SPEC, props.getProperty(MY_SPEC + ".b.value")); + assertNull(props.getProperty(gen(PROP_UNKNOWN))); + } + + @Test + public void testWithTrailingDot() { + // neither has trailing dot + assertEquals(PREFIX_GEN, props.getPrefix()); + assertEquals(PREFIX_SPEC, props.getSpecPrefix()); + + // both have trailing dot + props = new SpecProperties(PREFIX_GEN, MY_SPEC + "."); + assertEquals(PREFIX_GEN, props.getPrefix()); + assertEquals(PREFIX_SPEC, props.getSpecPrefix()); + + // first is empty + props = new SpecProperties("", MY_SPEC); + assertEquals("", props.getPrefix()); + assertEquals(MY_SPEC + ".", props.getSpecPrefix()); + + // second is empty + props = new SpecProperties(PREFIX_GEN, ""); + assertEquals(PREFIX_GEN, props.getPrefix()); + assertEquals(PREFIX_GEN, props.getSpecPrefix()); + } + + @Test + public void testGetPropertyString() { + // the key does contain the prefix + assertEquals(VAL_NO_PREFIX, props.getProperty(gen(PROP_NO_PREFIX))); + assertNull(props.getProperty(gen(PROP_NO_PREFIX + SUFFIX))); + + // specialized value exists + assertEquals(VAL_GEN, props.getProperty(gen(PROP_GEN))); + assertNull(props.getProperty(gen(PROP_GEN + SUFFIX))); + + // generalized value exists + assertEquals(VAL_SPEC, props.getProperty(gen(PROP_SPEC))); + assertNull(props.getProperty(gen(PROP_SPEC + SUFFIX))); + + // not found + assertNull(props.getProperty(gen(PROP_UNKNOWN))); + assertNull(props.getProperty(gen(PROP_UNKNOWN + SUFFIX))); + } + + @Test + public void testGetPropertyStringString() { + // the key does contain the prefix + assertEquals(VAL_NO_PREFIX, props.getProperty(gen(PROP_NO_PREFIX), VAL_DEFAULT)); + assertEquals(VAL_DEFAULT, props.getProperty(gen(PROP_NO_PREFIX + SUFFIX), VAL_DEFAULT)); + + // specialized value exists + assertEquals(VAL_GEN, props.getProperty(gen(PROP_GEN), VAL_DEFAULT)); + assertEquals(VAL_DEFAULT, props.getProperty(gen(PROP_GEN + SUFFIX), VAL_DEFAULT)); + + // generalized value exists + assertEquals(VAL_SPEC, props.getProperty(gen(PROP_SPEC), VAL_DEFAULT)); + assertEquals(VAL_DEFAULT, props.getProperty(gen(PROP_SPEC + SUFFIX), VAL_DEFAULT)); + + // not found + assertEquals(VAL_DEFAULT, props.getProperty(gen(PROP_UNKNOWN), VAL_DEFAULT)); + assertEquals(VAL_DEFAULT, props.getProperty(gen(PROP_UNKNOWN + SUFFIX), VAL_DEFAULT)); + + // can return null + assertNull(props.getProperty(gen(PROP_UNKNOWN), null)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testHashCode() { + props.hashCode(); + } + + @Test(expected = UnsupportedOperationException.class) + public void testEquals() { + props.equals(props); + } + + private String gen(String propnm) { + if (propnm.startsWith(PREFIX_SPEC)) { + return PREFIX_GEN + propnm.substring(PREFIX_SPEC.length()); + } + + return propnm; + } + +} diff --git a/utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertyConfigurationTest.java b/utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertyConfigurationTest.java deleted file mode 100644 index 39c8f01a..00000000 --- a/utils/src/test/java/org/onap/policy/common/utils/properties/SpecPropertyConfigurationTest.java +++ /dev/null @@ -1,331 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * ONAP Policy Engine - Common Modules - * ================================================================================ - * Copyright (C) 2018 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.common.utils.properties; - -import static org.junit.Assert.*; -import java.util.Properties; -import org.junit.Before; -import org.junit.Test; -import org.onap.policy.common.utils.properties.exception.PropertyException; -import org.onap.policy.common.utils.properties.exception.PropertyMissingException; -import static org.onap.policy.common.utils.properties.SpecPropertyConfiguration.*; - -/** - * - */ -public class SpecPropertyConfigurationTest { - - /** - * The specializer. - */ - private static final String SPEC = "my.name"; - - /** - * Properties used when invoking constructors. - */ - private Properties props; - - /** - * @throws java.lang.Exception - */ - @Before - public void setUp() throws Exception { - props = new Properties(); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#getRawPropertyValue(java.util.Properties, java.lang.String)}. - * @throws PropertyException - */ - @Test - public void testGetRawPropertyValue() throws PropertyException { - class Config extends SpecPropertyConfiguration { - - // no spec - @Property(name = "prefix.suffix") - private String noSpec; - - // no spec, other type - @Property(name = "no.spec.bool") - private boolean noSpecBool; - - // type 1, no prefix - @Property(name = "{$}.suffix") - private String type1NoPrefix; - - // type 1, no suffix - @Property(name = "prefix.{$}") - private String type1NoSuffix; - - // type 1, both prefix and suffix - @Property(name = "prefix.{$}.suffix") - private String type1Both; - - // type 1, other type - @Property(name = "an.{$}.int") - private int type1Int; - - // type 2, no prefix - @Property(name = "{abc.?.def}.suffix") - private String type2NoPrefix; - - // type 2, no suffix - @Property(name = "prefix.{abc.?.def}") - private String type2NoSuffix; - - // type 2, no spec prefix - @Property(name = "prefix.{?.def}.suffix") - private String type2NoSpecPrefix; - - // type 2, no spec suffix - @Property(name = "prefix{.abc.?}.suffix") - private String type2NoSpecSuffix; - - // type 2, all components - @Property(name = "prefix.{abc.?.def.}suffix") - private String type2Both; - - // type 2, other type - @Property(name = "a.{abc.?.def.}long") - private long type2Long; - - public Config(String specialization, Properties props) throws PropertyException { - super(specialization, props); - } - }; - - props.setProperty("prefix.suffix", "no.spec"); - props.setProperty("no.spec.bool", "true"); - props.setProperty("world.suffix", "type1.no.prefix"); - props.setProperty("prefix.world", "type1.no.suffix"); - props.setProperty("prefix.world.suffix", "type1.both"); - props.setProperty("an.world.int", "200"); - props.setProperty("abc.world.def.suffix", "type2.no.prefix"); - props.setProperty("prefix.abc.world.def", "type2.no.suffix"); - props.setProperty("prefix.world.def.suffix", "type2.no.spec.prefix"); - props.setProperty("prefix.abc.world.suffix", "type2.no.spec.suffix"); - props.setProperty("prefix.abc.world.def.suffix", "type2.both"); - props.setProperty("a.abc.world.def.long", "3000"); - - Config cfg = new Config("world", props); - - assertEquals("no.spec", cfg.noSpec); - assertEquals(true, cfg.noSpecBool); - assertEquals("type1.no.prefix", cfg.type1NoPrefix); - assertEquals("type1.no.suffix", cfg.type1NoSuffix); - assertEquals("type1.both", cfg.type1Both); - assertEquals(200, cfg.type1Int); - assertEquals("type2.no.prefix", cfg.type2NoPrefix); - assertEquals("type2.no.suffix", cfg.type2NoSuffix); - assertEquals("type2.no.spec.prefix", cfg.type2NoSpecPrefix); - assertEquals("type2.no.spec.suffix", cfg.type2NoSpecSuffix); - assertEquals("type2.both", cfg.type2Both); - assertEquals(3000L, cfg.type2Long); - } - @Test - public void testGetRawPropertyValue_Type2_Generalized() throws PropertyException { - class Config extends SpecPropertyConfiguration { - - // type 2, all components - @Property(name = "prefix.{abc.?.def.}suffix") - private String value; - - public Config(String specialization, Properties props) throws PropertyException { - super(specialization, props); - } - }; - - props.setProperty("prefix.suffix", "no.spec"); - - Config cfg = new Config("world", props); - - assertEquals("no.spec", cfg.value); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#getRawPropertyValue(java.util.Properties, java.lang.String)}. - * @throws PropertyException - */ - @Test(expected = PropertyMissingException.class) - public void testGetRawPropertyValue_NotFound() throws PropertyException { - class Config extends SpecPropertyConfiguration { - - @Property(name = "not.found") - private String notFound; - - public Config(String specialization, Properties props) throws PropertyException { - super(specialization, props); - } - }; - - new Config("not found", props); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#SpecPropertyConfiguration(java.lang.String)}. - * @throws PropertyException - */ - @Test - public void testSpecPropertyConfigurationString() throws PropertyException { - final String propnm = "string.{$}.prop"; - final String propval = "hello"; - - class Config extends SpecPropertyConfiguration { - - @Property(name = propnm) - private String value; - - public Config(String specialization) { - super(specialization); - } - }; - - props.setProperty(specialize(propnm, SPEC), propval); - - Config cfg = new Config(SPEC); - assertEquals(null, cfg.value); - - cfg.setAllFields(props); - assertEquals(propval, cfg.value); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#SpecPropertyConfiguration(java.lang.String, java.util.Properties)}. - * @throws PropertyException - */ - @Test - public void testSpecPropertyConfigurationStringProperties() throws PropertyException { - final String propnm = "int.{$}.prop"; - final int propval = 10; - - class Config extends SpecPropertyConfiguration { - - @Property(name = propnm) - private int value; - - public Config(String specialization, Properties props) throws PropertyException { - super(specialization, props); - } - }; - - props.setProperty(specialize(propnm, SPEC), String.valueOf(propval)); - - Config cfg = new Config(SPEC, props); - - assertEquals(propval, cfg.value); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#generalize(java.lang.String)}. - */ - @Test - public void testGeneralize_NoSpec() { - final String xyzPdq = "xyz.pdq"; - - // no spec - assertEquals(xyzPdq, generalize(xyzPdq)); - - // spec type 1 throws an exception - we'll test it separately - - // spec type 2 - assertEquals(xyzPdq, generalize("xyz.{xxx.?.yyy.}pdq")); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#generalize(java.lang.String)}. - */ - @Test(expected = IllegalArgumentException.class) - public void testGeneralize_Spec1() { - generalize("abc.{$}.def"); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#generalizeType2(java.lang.String, java.util.regex.Matcher)}. - */ - @Test - public void testGeneralizeType2() { - assertEquals("abc.def", generalize("abc.{xyz?pdq}def")); - - assertEquals("", generalize("{xyz?pdq}")); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#specialize(java.lang.String, java.lang.String)}. - */ - @Test - public void testSpecialize() { - final String spec = "get.spec"; - final String abcDef = "abc.def"; - - // no spec - assertEquals(abcDef, specialize(abcDef, spec)); - - // spec type 1 - assertEquals("abc.get.spec.def", specialize("abc.{$}.def", spec)); - - // spec type 2 - assertEquals("abc.xxx.get.spec.yyy.def", specialize("abc.{xxx.?.yyy.}def", spec)); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#specializeType1(java.lang.String, java.lang.String, java.util.regex.Matcher)}. - */ - @Test - public void testSpecializeType1() { - final String spec = "spec1"; - - // no prefix - assertEquals("spec1.def", specialize("{$}.def", spec)); - - // no suffix - assertEquals("abc.spec1", specialize("abc.{$}", spec)); - - // with both prefix and suffix - assertEquals("abc.spec1.def", specialize("abc.{$}.def", spec)); - } - - /** - * Test method for {@link org.onap.policy.common.utils.properties.SpecPropertyConfiguration#specializeType2(java.lang.String, java.lang.String, java.util.regex.Matcher)}. - */ - @Test - public void testSpecializeType2() { - final String spec = "spec2"; - - // no prefix - assertEquals("xxx.spec2.yyy.def", specialize("{xxx.?.yyy.}def", spec)); - - // no suffix - assertEquals("abc.xxx.spec2.yyy", specialize("abc{.xxx.?.yyy}", spec)); - - // no spec prefix - assertEquals("abc.spec2.yyy.def", specialize("abc.{?.yyy.}def", spec)); - - // no spec suffix - assertEquals("abc.xxx.spec2.def", specialize("abc.{xxx.?}.def", spec)); - - // no components - assertEquals(spec, specialize("{?}", spec)); - - // all components - assertEquals("abc.xxx.spec2.yyy.def", specialize("abc.{xxx.?.yyy.}def", spec)); - } - -} diff --git a/utils/src/test/java/org/onap/policy/common/utils/time/CurrentTimeTest.java b/utils/src/test/java/org/onap/policy/common/utils/time/CurrentTimeTest.java new file mode 100644 index 00000000..694a3d21 --- /dev/null +++ b/utils/src/test/java/org/onap/policy/common/utils/time/CurrentTimeTest.java @@ -0,0 +1,59 @@ +/* + * ============LICENSE_START======================================================= + * Common Utils + * ================================================================================ + * Copyright (C) 2018 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.common.utils.time; + +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +public class CurrentTimeTest { + + @Test + public void testGetMillis() { + long tcur = System.currentTimeMillis(); + long tval = new CurrentTime().getMillis(); + long tval2 = new CurrentTime().getMillis(); + long tend = System.currentTimeMillis(); + + assertTrue(tval >= tcur && tval <= tend); + assertTrue(tval2 >= tcur && tval2 <= tend); + } + + @Test + public void testGetDate() { + long tcur = System.currentTimeMillis(); + long tval = new CurrentTime().getDate().getTime(); + long tval2 = new CurrentTime().getDate().getTime(); + long tend = System.currentTimeMillis(); + + assertTrue(tval >= tcur && tval <= tend); + assertTrue(tval2 >= tcur && tval2 <= tend); + } + + @Test + public void testSleep() throws Exception { + long tcur = System.currentTimeMillis(); + new CurrentTime().sleep(10); + long tend = System.currentTimeMillis(); + + assertTrue(tend >= tcur + 10 - 1); + } + +} |